diff --git a/lua/core/player.lua b/lua/core/player.lua index fff16fc1..e6366a3c 100644 --- a/lua/core/player.lua +++ b/lua/core/player.lua @@ -219,22 +219,8 @@ function Player:getMaxCards() return baseValue end ----@param subtype CardSubtype ----@return integer|null -function Player:getEquipBySubtype(subtype) - local equipId = nil - for _, id in ipairs(self.player_cards[Player.Equip]) do - if Fk:getCardById(id).sub_type == subtype then - equipId = id - break - end - end - - return equipId -end - function Player:getAttackRange() - local weapon = Fk:getCardById(self:getEquipBySubtype(Card.SubtypeWeapon)) + local weapon = Fk:getCardById(self:getEquipment(Card.SubtypeWeapon)) local baseAttackRange = math.max(weapon and weapon.attack_range or 1, 0) return math.max(baseAttackRange, 0) diff --git a/lua/core/util.lua b/lua/core/util.lua index d0d08997..16049c85 100644 --- a/lua/core/util.lua +++ b/lua/core/util.lua @@ -263,6 +263,14 @@ end function AimGroup.static:addTargets(room, aimEvent, playerIds) local playerId = type(playerIds) == "table" and playerIds[1] or playerIds table.insert(aimEvent.tos[AimGroup.Undone], playerId) + + if type(playerIds) == "table" then + for i = 2, #playerIds do + aimEvent.subTargets = aimEvent.subTargets or {} + table.insert(aimEvent.subTargets, playerIds[i]) + end + end + room:sortPlayersByAction(aimEvent.tos[AimGroup.Undone]) if aimEvent.targetGroup then TargetGroup:pushTargets(aimEvent.targetGroup, playerIds) diff --git a/lua/server/room.lua b/lua/server/room.lua index 3d0335ba..06e83706 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -736,6 +736,20 @@ local onAim = function(room, cardUseEvent, aimEventCollaborators) additionalDamage = cardUseEvent.addtionalDamage } + local index = 1 + for _, targets in ipairs(cardUseEvent.tos) do + if index > collaboratorsIndex[toId] then + break + end + + if #targets > 1 then + for i = 2, #targets do + aimStruct.subTargets = {} + table.insert(aimStruct.subTargets, targets[i]) + end + end + end + collaboratorsIndex[toId] = 1 initialEvent = true else @@ -799,7 +813,7 @@ end ---@param cardUseEvent CardUseStruct ---@return boolean function Room:useCard(cardUseEvent) - local from = cardUseEvent.customFrom or cardUseEvent.from + local from = cardUseEvent.from self:moveCards({ ids = { cardUseEvent.cardId }, from = from, @@ -807,6 +821,10 @@ function Room:useCard(cardUseEvent) moveReason = fk.ReasonUse, }) + if Fk:getCardById(cardUseEvent.cardId).skill then + Fk:getCardById(cardUseEvent.cardId).skill:onUse(self, cardUseEvent) + end + self:setEmotion(self:getPlayerById(from), Fk:getCardById(cardUseEvent.cardId).name) self:doAnimate("Indicate", { from = from, @@ -850,9 +868,6 @@ function Room:useCard(cardUseEvent) } end - if Fk:getCardById(cardUseEvent.cardId).skill then - Fk:getCardById(cardUseEvent.cardId).skill:onUse(self, cardUseEvent) - end if self.logic:trigger(fk.PreCardUse, self:getPlayerById(cardUseEvent.from), cardUseEvent) then return false end @@ -980,6 +995,7 @@ function Room:useCard(cardUseEvent) collaboratorsIndex[toId] = collaboratorsIndex[toId] or 1 local curAimEvent = aimEventCollaborators[toId][collaboratorsIndex[toId]] + cardEffectEvent.subTargets = curAimEvent.subTargets cardEffectEvent.addtionalDamage = curAimEvent.additionalDamage if curAimEvent.disresponsiveList then @@ -1089,6 +1105,30 @@ function Room:doCardEffect(cardEffectEvent) end end +---@param cardResponseEvent CardResponseEvent +function Room:responseCard(cardResponseEvent) + local from = cardResponseEvent.customFrom or cardResponseEvent.from + self:moveCards({ + ids = { cardResponseEvent.cardId }, + from = from, + toArea = Card.Processing, + moveReason = fk.ReasonResonpse, + }) + + self:setEmotion(self:getPlayerById(from), Fk:getCardById(cardResponseEvent.cardId).name) + + for _, event in ipairs({ fk.PreCardRespond, fk.CardResponding, fk.CardRespondFinished }) do + self.logic:trigger(event, self:getPlayerById(cardResponseEvent.from), cardResponseEvent) + end + + if self:getCardArea(cardResponseEvent.cardId) == Card.Processing or cardResponseEvent.skipDrop then + self:moveCards({ + ids = { cardResponseEvent.cardId }, + toArea = Card.DiscardPile, + moveReason = fk.ReasonPutIntoDiscardPile, + }) + end +end ------------------------------------------------------------------------ -- move cards, and wrappers ------------------------------------------------------------------------ @@ -1421,6 +1461,10 @@ function Room:damage(damageStruct) return false end + if damageStruct.from and not self:getPlayerById(damageStruct.from):isAlive() then + damageStruct.from = nil + end + assert(type(damageStruct.to) == "number") local stages = { diff --git a/lua/server/system_enum.lua b/lua/server/system_enum.lua index 1e87744c..cd0a06c9 100644 --- a/lua/server/system_enum.lua +++ b/lua/server/system_enum.lua @@ -11,11 +11,12 @@ ---@alias DeathStruct { who: integer, damage: DamageStruct } ---@alias CardUseStruct { from: integer, tos: TargetGroup, cardId: integer, toCardId: integer|null, responseToEvent: CardUseStruct|null, nullifiedTargets: interger[]|null, extraUse: boolean|null, disresponsiveList: integer[]|null, unoffsetableList: integer[]|null, addtionalDamage: integer|null, customFrom: integer|null, cardIdsResponded: integer[]|null } ----@alias AimStruct { from: integer, cardId: integer, tos: AimGroup, to: integer, targetGroup: TargetGroup|null, nullifiedTargets: integer[]|null, firstTarget: boolean, additionalDamage: integer|null, disresponsive: boolean|null, unoffsetableList: boolean|null } ----@alias CardEffectEvent { from: integer, tos: TargetGroup, cardId: integer, toCardId: integer|null, responseToEvent: CardUseStruct|null, nullifiedTargets: interger[]|null, extraUse: boolean|null, disresponsiveList: integer[]|null, unoffsetableList: integer[]|null, addtionalDamage: integer|null, customFrom: integer|null, cardIdsResponded: integer[]|null } +---@alias AimStruct { from: integer, cardId: integer, tos: AimGroup, to: integer, subTargets: integer[]|null, targetGroup: TargetGroup|null, nullifiedTargets: integer[]|null, firstTarget: boolean, additionalDamage: integer|null, disresponsive: boolean|null, unoffsetableList: boolean|null } +---@alias CardEffectEvent { from: integer, to: integer, subTargets: integer[]|null, tos: TargetGroup, cardId: integer, toCardId: integer|null, responseToEvent: CardUseStruct|null, nullifiedTargets: interger[]|null, extraUse: boolean|null, disresponsiveList: integer[]|null, unoffsetableList: integer[]|null, addtionalDamage: integer|null, customFrom: integer|null, cardIdsResponded: integer[]|null, disresponsive: boolean|null, unoffsetable: boolean|null } ---@alias SkillEffectEvent { from: integer, tos: integer[], cards: integer[] } ---@alias JudgeStruct { who: ServerPlayer, card: Card, reason: string } +---@alias CardResponseEvent { from: integer, cardId: integer, responseToEvent: CardEffectEvent|null, skipDrop: boolean|null, customFrom: integer|null } ---@alias CardMoveReason integer diff --git a/packages/standard_cards/init.lua b/packages/standard_cards/init.lua index 75a8e594..cdc11179 100644 --- a/packages/standard_cards/init.lua +++ b/packages/standard_cards/init.lua @@ -28,7 +28,7 @@ local slashSkill = fk.CreateActiveSkill{ room:damage({ from = from, to = to, - damage = 1, + damage = 1 + (effect.addtionalDamage or 0), damageType = fk.NormalDamage, skillName = self.name }) @@ -134,12 +134,12 @@ local peachSkill = fk.CreateActiveSkill{ local to = effect.to local from = effect.from - room:recover{ + room:recover({ who = to, num = 1, recoverBy = from, skillName = self.name - } + }) end } local peach = fk.CreateBasicCard{ @@ -163,10 +163,35 @@ extension:addCards({ peach:clone(Card.Heart, 12), }) +local dismantlementSkill = fk.CreateActiveSkill{ + name = "dismantlement_skill", + target_filter = function(self, to_select, selected) + if #selected == 0 then + local player = Fk:currentRoom():getPlayerById(to_select) + return Self ~= player and not player:isAllNude() + end + end, + feasible = function(self, selected) + return #selected == 1 + end, + on_effect = function(self, room, effect) + local to = room:getPlayerById(effect.to) + local from = room:getPlayerById(effect.from) + local cid = room:askForCardChosen( + from, + to, + "hej", + self.name + ) + + room:throwCard(cid, self.name, to, from) + end +} local dismantlement = fk.CreateTrickCard{ name = "dismantlement", suit = Card.Spade, number = 3, + skill = dismantlementSkill, } Fk:loadTranslationTable{ ["dismantlement"] = "过河拆桥", @@ -202,7 +227,7 @@ local snatchSkill = fk.CreateActiveSkill{ room:getPlayerById(from), room:getPlayerById(to), "hej", - "snatch" + self.name ) room:obtainCard(from, cid) @@ -227,10 +252,60 @@ extension:addCards({ snatch:clone(Card.Diamond, 4), }) +local duelSkill = fk.CreateActiveSkill{ + name = "duel_skill", + target_filter = function(self, to_select, selected) + if #selected == 0 then + local player = Fk:currentRoom():getPlayerById(to_select) + return Self ~= player + end + end, + feasible = function(self, selected) + return #selected == 1 + end, + on_effect = function(self, room, effect) + local to = room:getPlayerById(effect.to) + local from = room:getPlayerById(effect.from) + local responsers = { to, from } + local currentTurn = 1 + local currentResponser = to + + while currentResponser:isAlive() do + if effect.disresponsive or table.contains(effect.disresponsiveList or {}, currentResponser.id) then + break + end + + local cardIdResponded = room:askForResponse(currentResponser, 'slash') + if cardIdResponded then + room:responseCard({ + from = currentResponser.id, + cardId = cardIdResponded, + responseToEvent = effect, + }) + else + break + end + + currentTurn = currentTurn % 2 + 1 + currentResponser = responsers[currentTurn] + end + + if currentResponser:isAlive() then + room:damage({ + from = responsers[currentTurn % 2 + 1].id, + to = currentResponser.id, + damage = 1 + (effect.addtionalDamage or 0), + damageType = fk.NormalDamage, + skillName = self.name, + }) + end + end +} local duel = fk.CreateTrickCard{ name = "duel", suit = Card.Spade, number = 1, + skill = duelSkill, } Fk:loadTranslationTable{ ["duel"] = "决斗", @@ -244,10 +319,44 @@ extension:addCards({ duel:clone(Card.Diamond, 1), }) +local collateralSkill = fk.CreateActiveSkill{ + name = "collateral_skill", + target_filter = function(self, to_select, selected) + local player = Fk:currentRoom():getPlayerById(to_select) + if #selected == 0 then + return Self ~= player and player:getEquipment(Card.SubtypeWeapon) + elseif #selected == 1 then + return Fk:currentRoom():getPlayerById(selected[1]):inMyAttackRange(player) + end + end, + feasible = function(self, selected) + return #selected == 2 + end, + on_use = function(self, room, cardUseEvent) + cardUseEvent.tos = { { cardUseEvent.tos[1][1], cardUseEvent.tos[2][1] } } + end, + on_effect = function(self, room, effect) + local cardIdResponded = nil + if not (effect.disresponsive or table.contains(effect.disresponsiveList or {}, effect.to)) then + cardIdResponded = room:askForResponse(room:getPlayerById(effect.to), 'slash') + end + + if cardIdResponded then + room:useCard({ + from = effect.to, + tos = { { effect.subTargets[1] } }, + cardId = cardIdResponded, + }) + else + room:obtainCard(effect.from, room:getPlayerById(effect.to):getEquipment(Card.SubtypeWeapon), true, fk.ReasonGive) + end + end +} local collateral = fk.CreateTrickCard{ name = "collateral", suit = Card.Club, number = 12, + skill = collateralSkill, } Fk:loadTranslationTable{ ["collateral"] = "借刀杀人", @@ -269,7 +378,6 @@ local exNihiloSkill = fk.CreateActiveSkill{ room:drawCards(room:getPlayerById(cardEffectEvent.to), 2, "ex_nihilo") end } - local exNihilo = fk.CreateTrickCard{ name = "ex_nihilo", suit = Card.Heart, @@ -317,10 +425,44 @@ extension:addCards({ nullification:clone(Card.Diamond, 12), }) +local savageAssaultSkill = fk.CreateActiveSkill{ + name = "savage_assault_skill", + on_use = function(self, room, cardUseEvent) + if not cardUseEvent.tos or #TargetGroup:getRealTargets(cardUseEvent.tos) == 0 then + cardUseEvent.tos = {} + for _, player in ipairs(room:getOtherPlayers(room:getPlayerById(cardUseEvent.from))) do + TargetGroup:pushTargets(cardUseEvent.tos, player.id) + end + end + end, + on_effect = function(self, room, effect) + local cardIdResponded = nil + if not (effect.disresponsive or table.contains(effect.disresponsiveList or {}, effect.to)) then + cardIdResponded = room:askForResponse(room:getPlayerById(effect.to), 'slash') + end + + if cardIdResponded then + room:responseCard({ + from = effect.to, + cardId = cardIdResponded, + responseToEvent = effect, + }) + else + room:damage({ + from = effect.from, + to = effect.to, + damage = 1 + (effect.addtionalDamage or 0), + damageType = fk.NormalDamage, + skillName = self.name, + }) + end + end +} local savageAssault = fk.CreateTrickCard{ name = "savage_assault", suit = Card.Spade, number = 7, + skill = savageAssaultSkill, } Fk:loadTranslationTable{ ["savage_assault"] = "南蛮入侵", @@ -332,10 +474,44 @@ extension:addCards({ savageAssault:clone(Card.Club, 7), }) +local archeryAttackSkill = fk.CreateActiveSkill{ + name = "archery_attack_skill", + on_use = function(self, room, cardUseEvent) + if not cardUseEvent.tos or #TargetGroup:getRealTargets(cardUseEvent.tos) == 0 then + cardUseEvent.tos = {} + for _, player in ipairs(room:getOtherPlayers(room:getPlayerById(cardUseEvent.from))) do + TargetGroup:pushTargets(cardUseEvent.tos, player.id) + end + end + end, + on_effect = function(self, room, effect) + local cardIdResponded = nil + if not (effect.disresponsive or table.contains(effect.disresponsiveList or {}, effect.to)) then + cardIdResponded = room:askForResponse(room:getPlayerById(effect.to), 'jink') + end + + if cardIdResponded then + room:responseCard({ + from = effect.to, + cardId = cardIdResponded, + responseToEvent = effect, + }) + else + room:damage({ + from = effect.from, + to = effect.to, + damage = 1 + (effect.addtionalDamage or 0), + damageType = fk.NormalDamage, + skillName = self.name, + }) + end + end +} local archeryAttack = fk.CreateTrickCard{ name = "archery_attack", suit = Card.Heart, number = 1, + skill = archeryAttackSkill, } Fk:loadTranslationTable{ ["archery_attack"] = "万箭齐发", @@ -345,10 +521,29 @@ extension:addCards({ archeryAttack, }) +local godSalvationSkill = fk.CreateActiveSkill{ + name = "god_salvation_skill", + on_use = function(self, room, cardUseEvent) + if not cardUseEvent.tos or #TargetGroup:getRealTargets(cardUseEvent.tos) == 0 then + cardUseEvent.tos = {} + for _, player in ipairs(room:getAlivePlayers()) do + TargetGroup:pushTargets(cardUseEvent.tos, player.id) + end + end + end, + on_effect = function(self, room, cardEffectEvent) + room:recover({ + who = cardEffectEvent.to, + num = 1, + skillName = self.name, + }) + end +} local godSalvation = fk.CreateTrickCard{ name = "god_salvation", suit = Card.Heart, number = 1, + skill = godSalvationSkill, } Fk:loadTranslationTable{ ["god_salvation"] = "桃园结义", @@ -358,10 +553,25 @@ extension:addCards({ godSalvation, }) +local amazingGraceSkill = fk.CreateActiveSkill{ + name = "amazing_grace_skill", + on_use = function(self, room, cardUseEvent) + if not cardUseEvent.tos or #TargetGroup:getRealTargets(cardUseEvent.tos) == 0 then + cardUseEvent.tos = {} + for _, player in ipairs(room:getAlivePlayers()) do + TargetGroup:pushTargets(cardUseEvent.tos, player.id) + end + end + end, + on_effect = function(self, room, cardEffectEvent) + room:getPlayerById(cardEffectEvent.to):drawCards(1, 'god_salvation') + end +} local amazingGrace = fk.CreateTrickCard{ name = "amazing_grace", suit = Card.Heart, number = 3, + skill = amazingGraceSkill, } Fk:loadTranslationTable{ ["amazing_grace"] = "五谷丰登", diff --git a/qml/Pages/Room.qml b/qml/Pages/Room.qml index ea6037d7..a8bae4ce 100644 --- a/qml/Pages/Room.qml +++ b/qml/Pages/Room.qml @@ -77,6 +77,7 @@ Item { progress.visible = false; okCancel.visible = false; endPhaseButton.visible = false; + respond_play = false; if (dashboard.pending_skill !== "") dashboard.stopPending(); @@ -100,6 +101,7 @@ Item { progress.visible = true; okCancel.visible = true; endPhaseButton.visible = true; + respond_play = false; } } }, @@ -123,6 +125,7 @@ Item { dashboard.disableAllCards(); dashboard.disableSkills(); progress.visible = true; + respond_play = false; } } }