diff --git a/Fk/Pages/Room.qml b/Fk/Pages/Room.qml index b35e462e..caf6b187 100644 --- a/Fk/Pages/Room.qml +++ b/Fk/Pages/Room.qml @@ -121,9 +121,9 @@ Item { enabled: !config.observing && !config.replaying text: luatr("Surrender") onClicked: { - if (isStarted && !getPhoto(Self.id).dead) { - const surrenderCheck = - lcall('CheckSurrenderAvailable', miscStatus.playedTime); + const photo = getPhoto(Self.id); + if (isStarted && !(photo.dead && photo.rest <= 0)) { + const surrenderCheck = lcall('CheckSurrenderAvailable', miscStatus.playedTime); if (!surrenderCheck.length) { surrenderDialog.informativeText = luatr('Surrender is disabled in this mode'); diff --git a/lua/core/player.lua b/lua/core/player.lua index 8a9fe445..cf608f55 100644 --- a/lua/core/player.lua +++ b/lua/core/player.lua @@ -1006,7 +1006,7 @@ function Player:canMoveCardInBoardTo(to, id) return not ( table.find(to:getCardIds(Player.Judge), function(cardId) - return Fk:getCardById(cardId).name == card.name + return (to:getVirualEquip(cardId) or Fk:getCardById(cardId)).name == card.name end) or table.contains(to.sealedSlots, Player.JudgeSlot) ) diff --git a/lua/core/skill_type/active.lua b/lua/core/skill_type/active.lua index 874299ec..20760a04 100644 --- a/lua/core/skill_type/active.lua +++ b/lua/core/skill_type/active.lua @@ -190,6 +190,11 @@ end ---@param cardUseEvent CardUseStruct function ActiveSkill:onUse(room, cardUseEvent) end +---@param room Room +---@param cardUseEvent CardUseStruct +---@param isEnding? bool +function ActiveSkill:onAction(room, cardUseEvent, finished) end + ---@param room Room ---@param cardEffectEvent CardEffectEvent | SkillEffectEvent function ActiveSkill:aboutToEffect(room, cardEffectEvent) end diff --git a/lua/core/skill_type/view_as.lua b/lua/core/skill_type/view_as.lua index b8e695e9..b3798753 100644 --- a/lua/core/skill_type/view_as.lua +++ b/lua/core/skill_type/view_as.lua @@ -39,6 +39,10 @@ end ---@param cardUseStruct CardUseStruct function ViewAsSkill:beforeUse(player, cardUseStruct) end +---@param player Player +---@param cardUseStruct CardUseStruct +function ViewAsSkill:afterUse(player, cardUseStruct) end + ---@param selected integer[] @ ids of selected players ---@param selected_cards integer[] @ ids of selected cards function ViewAsSkill:prompt(selected, selected_cards) return "" end diff --git a/lua/fk_ex.lua b/lua/fk_ex.lua index f0a9eb0e..aaca1ea8 100644 --- a/lua/fk_ex.lua +++ b/lua/fk_ex.lua @@ -175,6 +175,7 @@ end ---@field public target_filter? fun(self: ActiveSkill, to_select: integer, selected: integer[], selected_cards: integer[], card: Card, extra_data: any): boolean? ---@field public feasible? fun(self: ActiveSkill, selected: integer[], selected_cards: integer[]): boolean? ---@field public on_use? fun(self: ActiveSkill, room: Room, cardUseEvent: CardUseStruct): boolean? +---@field public on_action? fun(self: ActiveSkill, room: Room, cardUseEvent: CardUseStruct, finished: boolean): boolean? ---@field public about_to_effect? fun(self: ActiveSkill, room: Room, cardEffectEvent: CardEffectEvent): boolean? ---@field public on_effect? fun(self: ActiveSkill, room: Room, cardEffectEvent: CardEffectEvent): boolean? ---@field public on_nullified? fun(self: ActiveSkill, room: Room, cardEffectEvent: CardEffectEvent): boolean? @@ -202,6 +203,7 @@ function fk.CreateActiveSkill(spec) skill.feasible = spec.feasible end if spec.on_use then skill.onUse = spec.on_use end + if spec.on_action then skill.onAction = spec.on_action end if spec.about_to_effect then skill.aboutToEffect = spec.about_to_effect end if spec.on_effect then skill.onEffect = spec.on_effect end if spec.on_nullified then skill.onNullified = spec.on_nullified end @@ -274,6 +276,10 @@ function fk.CreateViewAsSkill(spec) skill.beforeUse = spec.before_use end + if spec.after_use and type(spec.after_use) == "function" then + skill.afterUse = spec.after_use + end + return skill end diff --git a/lua/server/events/hp.lua b/lua/server/events/hp.lua index 44d4d34a..06ef099f 100644 --- a/lua/server/events/hp.lua +++ b/lua/server/events/hp.lua @@ -136,16 +136,16 @@ GameEvent.functions[GameEvent.Damage] = function(self) assert(damageStruct.to:isInstanceOf(ServerPlayer)) local stages = { - {fk.PreDamage, damageStruct.from}, + {fk.PreDamage, "from"}, } if not damageStruct.isVirtualDMG then - table.insertTable(stages, { { fk.DamageCaused, damageStruct.from }, { fk.DamageInflicted, damageStruct.to } }) + table.insertTable(stages, { { fk.DamageCaused, "from" }, { fk.DamageInflicted, "to" } }) end for _, struct in ipairs(stages) do local event, player = table.unpack(struct) - if logic:trigger(event, player, damageStruct) or damageStruct.damage < 1 then + if logic:trigger(event, damageStruct[player], damageStruct) or damageStruct.damage < 1 then logic:breakEvent(false) end @@ -184,13 +184,13 @@ GameEvent.functions[GameEvent.Damage] = function(self) stages = { - {fk.Damage, damageStruct.from}, - {fk.Damaged, damageStruct.to}, + {fk.Damage, "from"}, + {fk.Damaged, "to"}, } for _, struct in ipairs(stages) do local event, player = table.unpack(struct) - logic:trigger(event, player, damageStruct) + logic:trigger(event, damageStruct[player], damageStruct) end return true diff --git a/lua/server/events/usecard.lua b/lua/server/events/usecard.lua index e0300b92..d73f93d0 100644 --- a/lua/server/events/usecard.lua +++ b/lua/server/events/usecard.lua @@ -335,9 +335,16 @@ GameEvent.functions[GameEvent.CardEffect] = function(self) if event == fk.PreCardEffect then if cardEffectEvent.from and logic:trigger(event, room:getPlayerById(cardEffectEvent.from), cardEffectEvent) then + if cardEffectEvent.to then + cardEffectEvent.nullifiedTargets = cardEffectEvent.nullifiedTargets or {} + table.insert(cardEffectEvent.nullifiedTargets, cardEffectEvent.to) + end logic:breakEvent() end elseif cardEffectEvent.to and logic:trigger(event, room:getPlayerById(cardEffectEvent.to), cardEffectEvent) then + cardEffectEvent.nullifiedTargets = cardEffectEvent.nullifiedTargets or {} + table.insert(cardEffectEvent.nullifiedTargets, cardEffectEvent.to) + logic:breakEvent() end diff --git a/lua/server/gamelogic.lua b/lua/server/gamelogic.lua index e1c72130..db8470a1 100644 --- a/lua/server/gamelogic.lua +++ b/lua/server/gamelogic.lua @@ -26,7 +26,7 @@ function GameLogic:initialize(room) self.event_recorder = {} self.current_event_id = 0 self.specific_events_id = { - [GameEvent.Damage] = 0, + [GameEvent.Damage] = 1, } self.role_table = { diff --git a/lua/server/room.lua b/lua/server/room.lua index f28fd8f7..98aceb58 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -1927,6 +1927,7 @@ function Room:handleUseCardReply(player, data) use.card = c self:useSkill(player, skill, Util.DummyFunc) + use.attachedSkillAndUser = { skillName = skill.name, user = player.id } local rejectSkillName = skill:beforeUse(player, use) if type(rejectSkillName) == "string" then @@ -2402,7 +2403,23 @@ end ---@param cardUseEvent CardUseStruct @ 使用数据 ---@return boolean function Room:useCard(cardUseEvent) - return execGameEvent(GameEvent.UseCard, cardUseEvent) + local attachedSkillAndUser + if type(cardUseEvent.attachedSkillAndUser) == "table" then + attachedSkillAndUser = table.simpleClone(cardUseEvent.attachedSkillAndUser) + cardUseEvent.attachedSkillAndUser = nil + end + + local ret = execGameEvent(GameEvent.UseCard, cardUseEvent) + + if + type(attachedSkillAndUser) == "table" and + Fk.skills[attachedSkillAndUser.skillName] and + Fk.skills[attachedSkillAndUser.skillName].afterUse + then + Fk.skills[attachedSkillAndUser.skillName]:afterUse(self:getPlayerById(attachedSkillAndUser.user), cardUseEvent) + end + + return ret end ---@param room Room @@ -2439,6 +2456,7 @@ local onAim = function(room, cardUseEvent, aimEventCollaborators) firstTarget = firstTarget, additionalDamage = cardUseEvent.additionalDamage, additionalRecover = cardUseEvent.additionalRecover, + additionalEffect = cardUseEvent.additionalEffect, extra_data = cardUseEvent.extra_data, } @@ -2466,6 +2484,7 @@ local onAim = function(room, cardUseEvent, aimEventCollaborators) aimStruct.targetGroup = cardUseEvent.tos aimStruct.nullifiedTargets = cardUseEvent.nullifiedTargets or {} aimStruct.firstTarget = firstTarget + aimStruct.additionalEffect = cardUseEvent.additionalEffect aimStruct.extra_data = cardUseEvent.extra_data end @@ -2483,6 +2502,7 @@ local onAim = function(room, cardUseEvent, aimEventCollaborators) cardUseEvent.from = aimStruct.from cardUseEvent.tos = aimEventTargetGroup cardUseEvent.nullifiedTargets = aimStruct.nullifiedTargets + cardUseEvent.additionalEffect = aimStruct.additionalEffect cardUseEvent.extra_data = aimStruct.extra_data if #AimGroup:getAllTargets(aimStruct.tos) == 0 then @@ -2643,57 +2663,71 @@ function Room:doCardUseEffect(cardUseEvent) return end - -- Else: do effect to all targets - local collaboratorsIndex = {} - for _, toId in ipairs(TargetGroup:getRealTargets(cardUseEvent.tos)) do - if not table.contains(cardUseEvent.nullifiedTargets, toId) and self:getPlayerById(toId):isAlive() then - if aimEventCollaborators[toId] then - cardEffectEvent.to = toId - collaboratorsIndex[toId] = collaboratorsIndex[toId] or 1 - local curAimEvent = aimEventCollaborators[toId][collaboratorsIndex[toId]] + for i = 1, (cardUseEvent.additionalEffect or 0) + 1 do + if #TargetGroup:getRealTargets(cardUseEvent.tos) > 0 and cardUseEvent.card.skill.onAction then + cardUseEvent.card.skill:onAction(self, cardUseEvent) + end - cardEffectEvent.subTargets = curAimEvent.subTargets - cardEffectEvent.additionalDamage = curAimEvent.additionalDamage - cardEffectEvent.additionalRecover = curAimEvent.additionalRecover + -- Else: do effect to all targets + local collaboratorsIndex = {} + for _, toId in ipairs(TargetGroup:getRealTargets(cardUseEvent.tos)) do + if not table.contains(cardUseEvent.nullifiedTargets, toId) and self:getPlayerById(toId):isAlive() then + if aimEventCollaborators[toId] then + cardEffectEvent.to = toId + collaboratorsIndex[toId] = collaboratorsIndex[toId] or 1 + local curAimEvent = aimEventCollaborators[toId][collaboratorsIndex[toId]] - if curAimEvent.disresponsiveList then - cardEffectEvent.disresponsiveList = cardEffectEvent.disresponsiveList or {} + cardEffectEvent.subTargets = curAimEvent.subTargets + cardEffectEvent.additionalDamage = curAimEvent.additionalDamage + cardEffectEvent.additionalRecover = curAimEvent.additionalRecover - for _, disresponsivePlayer in ipairs(curAimEvent.disresponsiveList) do - if not table.contains(cardEffectEvent.disresponsiveList, disresponsivePlayer) then - table.insert(cardEffectEvent.disresponsiveList, disresponsivePlayer) + if curAimEvent.disresponsiveList then + cardEffectEvent.disresponsiveList = cardEffectEvent.disresponsiveList or {} + + for _, disresponsivePlayer in ipairs(curAimEvent.disresponsiveList) do + if not table.contains(cardEffectEvent.disresponsiveList, disresponsivePlayer) then + table.insert(cardEffectEvent.disresponsiveList, disresponsivePlayer) + end end end - end - if curAimEvent.unoffsetableList then - cardEffectEvent.unoffsetableList = cardEffectEvent.unoffsetableList or {} + if curAimEvent.unoffsetableList then + cardEffectEvent.unoffsetableList = cardEffectEvent.unoffsetableList or {} - for _, unoffsetablePlayer in ipairs(curAimEvent.unoffsetableList) do - if not table.contains(cardEffectEvent.unoffsetableList, unoffsetablePlayer) then - table.insert(cardEffectEvent.unoffsetableList, unoffsetablePlayer) + for _, unoffsetablePlayer in ipairs(curAimEvent.unoffsetableList) do + if not table.contains(cardEffectEvent.unoffsetableList, unoffsetablePlayer) then + table.insert(cardEffectEvent.unoffsetableList, unoffsetablePlayer) + end end end - end - cardEffectEvent.disresponsive = curAimEvent.disresponsive - cardEffectEvent.unoffsetable = curAimEvent.unoffsetable - cardEffectEvent.fixedResponseTimes = curAimEvent.fixedResponseTimes - cardEffectEvent.fixedAddTimesResponsors = curAimEvent.fixedAddTimesResponsors + cardEffectEvent.disresponsive = curAimEvent.disresponsive + cardEffectEvent.unoffsetable = curAimEvent.unoffsetable + cardEffectEvent.fixedResponseTimes = curAimEvent.fixedResponseTimes + cardEffectEvent.fixedAddTimesResponsors = curAimEvent.fixedAddTimesResponsors - collaboratorsIndex[toId] = collaboratorsIndex[toId] + 1 + collaboratorsIndex[toId] = collaboratorsIndex[toId] + 1 - local curCardEffectEvent = table.simpleClone(cardEffectEvent) - self:doCardEffect(curCardEffectEvent) + local curCardEffectEvent = table.simpleClone(cardEffectEvent) + self:doCardEffect(curCardEffectEvent) - if curCardEffectEvent.cardsResponded then - cardUseEvent.cardsResponded = cardUseEvent.cardsResponded or {} - for _, card in ipairs(curCardEffectEvent.cardsResponded) do - table.insertIfNeed(cardUseEvent.cardsResponded, card) + if curCardEffectEvent.cardsResponded then + cardUseEvent.cardsResponded = cardUseEvent.cardsResponded or {} + for _, card in ipairs(curCardEffectEvent.cardsResponded) do + table.insertIfNeed(cardUseEvent.cardsResponded, card) + end + end + + if type(curCardEffectEvent.nullifiedTargets) == 'table' then + table.insertTableIfNeed(cardUseEvent.nullifiedTargets, curCardEffectEvent.nullifiedTargets) end end end end + + if #TargetGroup:getRealTargets(cardUseEvent.tos) > 0 and cardUseEvent.card.skill.onAction then + cardUseEvent.card.skill:onAction(self, cardUseEvent, true) + end end end diff --git a/lua/server/serverplayer.lua b/lua/server/serverplayer.lua index 754c5ced..fb6e76ce 100644 --- a/lua/server/serverplayer.lua +++ b/lua/server/serverplayer.lua @@ -215,7 +215,7 @@ function ServerPlayer:marshal(player, observe) if self.dead then room:notifyProperty(player, self, "dead") - room:notifyProperty(player, self, "role") + room:notifyProperty(player, self, self.rest > 0 and "rest" or "role") else room:notifyProperty(player, self, "seat") room:notifyProperty(player, self, "phase") diff --git a/lua/server/system_enum.lua b/lua/server/system_enum.lua index 9f0b0b88..5c8dd9bd 100644 --- a/lua/server/system_enum.lua +++ b/lua/server/system_enum.lua @@ -110,6 +110,7 @@ fk.IceDamage = 4 ---@field public cardsResponded? Card[] ---@field public prohibitedCardNames? string[] ---@field public damageDealt? table +---@field public additionalEffect? integer ---@class AimStruct ---@field public from integer @@ -126,6 +127,7 @@ fk.IceDamage = 4 ---@field public unoffsetableList? boolean ---@field public additionalResponseTimes? table|integer ---@field public fixedAddTimesResponsors? integer[] +---@field public additionalEffect? integer ---@class CardEffectEvent ---@field public from integer diff --git a/packages/standard_cards/init.lua b/packages/standard_cards/init.lua index 8038ae1a..136dd20a 100644 --- a/packages/standard_cards/init.lua +++ b/packages/standard_cards/init.lua @@ -608,6 +608,43 @@ local amazingGraceSkill = fk.CreateActiveSkill{ can_use = Util.GlobalCanUse, on_use = Util.GlobalOnUse, mod_target_filter = Util.TrueFunc, + on_action = function(self, room, use, finished) + if not finished then + local toDisplay = room:getNCards(#TargetGroup:getRealTargets(use.tos)) + room:moveCards({ + ids = toDisplay, + toArea = Card.Processing, + moveReason = fk.ReasonPut, + }) + + table.forEach(room.players, function(p) + room:fillAG(p, toDisplay) + end) + + use.extra_data = use.extra_data or {} + use.extra_data.AGFilled = toDisplay + else + if use.extra_data and use.extra_data.AGFilled then + table.forEach(room.players, function(p) + room:closeAG(p) + end) + + local toDiscard = table.filter(use.extra_data.AGFilled, function(id) + return room:getCardArea(id) == Card.Processing + end) + + if #toDiscard > 0 then + room:moveCards({ + ids = toDiscard, + toArea = Card.DiscardPile, + moveReason = fk.ReasonPutIntoDiscardPile, + }) + end + end + + use.extra_data.AGFilled = nil + end + end, on_effect = function(self, room, effect) local to = room:getPlayerById(effect.to) if not (effect.extra_data and effect.extra_data.AGFilled) then