Modify Use-Event (#314)

- 为使用流程和Aim流程增加属性additionalEffect,用于指定额外结算次数(OL版),顺带移动【五谷丰登】开启和关闭AG的位置;
- 为视为技新增after_use方法处理转化牌后的后续操作;
- 修复伤害流程时机触发者不变问题;
- 修复旁观休整的问题;
- 修复可移动场上牌判断函数未判断虚拟牌名的问题。
This commit is contained in:
Ho-spair 2024-02-04 15:29:39 +08:00 committed by GitHub
parent 3b9dd89b10
commit 58e76c1b9c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 142 additions and 47 deletions

View File

@ -121,9 +121,9 @@ Item {
enabled: !config.observing && !config.replaying enabled: !config.observing && !config.replaying
text: luatr("Surrender") text: luatr("Surrender")
onClicked: { onClicked: {
if (isStarted && !getPhoto(Self.id).dead) { const photo = getPhoto(Self.id);
const surrenderCheck = if (isStarted && !(photo.dead && photo.rest <= 0)) {
lcall('CheckSurrenderAvailable', miscStatus.playedTime); const surrenderCheck = lcall('CheckSurrenderAvailable', miscStatus.playedTime);
if (!surrenderCheck.length) { if (!surrenderCheck.length) {
surrenderDialog.informativeText = surrenderDialog.informativeText =
luatr('Surrender is disabled in this mode'); luatr('Surrender is disabled in this mode');

View File

@ -1006,7 +1006,7 @@ function Player:canMoveCardInBoardTo(to, id)
return return
not ( not (
table.find(to:getCardIds(Player.Judge), function(cardId) 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 end) or
table.contains(to.sealedSlots, Player.JudgeSlot) table.contains(to.sealedSlots, Player.JudgeSlot)
) )

View File

@ -190,6 +190,11 @@ end
---@param cardUseEvent CardUseStruct ---@param cardUseEvent CardUseStruct
function ActiveSkill:onUse(room, cardUseEvent) end 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 room Room
---@param cardEffectEvent CardEffectEvent | SkillEffectEvent ---@param cardEffectEvent CardEffectEvent | SkillEffectEvent
function ActiveSkill:aboutToEffect(room, cardEffectEvent) end function ActiveSkill:aboutToEffect(room, cardEffectEvent) end

View File

@ -39,6 +39,10 @@ end
---@param cardUseStruct CardUseStruct ---@param cardUseStruct CardUseStruct
function ViewAsSkill:beforeUse(player, cardUseStruct) end 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 integer[] @ ids of selected players
---@param selected_cards integer[] @ ids of selected cards ---@param selected_cards integer[] @ ids of selected cards
function ViewAsSkill:prompt(selected, selected_cards) return "" end function ViewAsSkill:prompt(selected, selected_cards) return "" end

View File

@ -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 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 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_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 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_effect? fun(self: ActiveSkill, room: Room, cardEffectEvent: CardEffectEvent): boolean?
---@field public on_nullified? 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 skill.feasible = spec.feasible
end end
if spec.on_use then skill.onUse = spec.on_use 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.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_effect then skill.onEffect = spec.on_effect end
if spec.on_nullified then skill.onNullified = spec.on_nullified 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 skill.beforeUse = spec.before_use
end end
if spec.after_use and type(spec.after_use) == "function" then
skill.afterUse = spec.after_use
end
return skill return skill
end end

View File

@ -136,16 +136,16 @@ GameEvent.functions[GameEvent.Damage] = function(self)
assert(damageStruct.to:isInstanceOf(ServerPlayer)) assert(damageStruct.to:isInstanceOf(ServerPlayer))
local stages = { local stages = {
{fk.PreDamage, damageStruct.from}, {fk.PreDamage, "from"},
} }
if not damageStruct.isVirtualDMG then 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 end
for _, struct in ipairs(stages) do for _, struct in ipairs(stages) do
local event, player = table.unpack(struct) 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) logic:breakEvent(false)
end end
@ -184,13 +184,13 @@ GameEvent.functions[GameEvent.Damage] = function(self)
stages = { stages = {
{fk.Damage, damageStruct.from}, {fk.Damage, "from"},
{fk.Damaged, damageStruct.to}, {fk.Damaged, "to"},
} }
for _, struct in ipairs(stages) do for _, struct in ipairs(stages) do
local event, player = table.unpack(struct) local event, player = table.unpack(struct)
logic:trigger(event, player, damageStruct) logic:trigger(event, damageStruct[player], damageStruct)
end end
return true return true

View File

@ -335,9 +335,16 @@ GameEvent.functions[GameEvent.CardEffect] = function(self)
if event == fk.PreCardEffect then if event == fk.PreCardEffect then
if cardEffectEvent.from and logic:trigger(event, room:getPlayerById(cardEffectEvent.from), cardEffectEvent) 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() logic:breakEvent()
end end
elseif cardEffectEvent.to and logic:trigger(event, room:getPlayerById(cardEffectEvent.to), cardEffectEvent) then 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() logic:breakEvent()
end end

View File

@ -26,7 +26,7 @@ function GameLogic:initialize(room)
self.event_recorder = {} self.event_recorder = {}
self.current_event_id = 0 self.current_event_id = 0
self.specific_events_id = { self.specific_events_id = {
[GameEvent.Damage] = 0, [GameEvent.Damage] = 1,
} }
self.role_table = { self.role_table = {

View File

@ -1927,6 +1927,7 @@ function Room:handleUseCardReply(player, data)
use.card = c use.card = c
self:useSkill(player, skill, Util.DummyFunc) self:useSkill(player, skill, Util.DummyFunc)
use.attachedSkillAndUser = { skillName = skill.name, user = player.id }
local rejectSkillName = skill:beforeUse(player, use) local rejectSkillName = skill:beforeUse(player, use)
if type(rejectSkillName) == "string" then if type(rejectSkillName) == "string" then
@ -2402,7 +2403,23 @@ end
---@param cardUseEvent CardUseStruct @ 使用数据 ---@param cardUseEvent CardUseStruct @ 使用数据
---@return boolean ---@return boolean
function Room:useCard(cardUseEvent) 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 end
---@param room Room ---@param room Room
@ -2439,6 +2456,7 @@ local onAim = function(room, cardUseEvent, aimEventCollaborators)
firstTarget = firstTarget, firstTarget = firstTarget,
additionalDamage = cardUseEvent.additionalDamage, additionalDamage = cardUseEvent.additionalDamage,
additionalRecover = cardUseEvent.additionalRecover, additionalRecover = cardUseEvent.additionalRecover,
additionalEffect = cardUseEvent.additionalEffect,
extra_data = cardUseEvent.extra_data, extra_data = cardUseEvent.extra_data,
} }
@ -2466,6 +2484,7 @@ local onAim = function(room, cardUseEvent, aimEventCollaborators)
aimStruct.targetGroup = cardUseEvent.tos aimStruct.targetGroup = cardUseEvent.tos
aimStruct.nullifiedTargets = cardUseEvent.nullifiedTargets or {} aimStruct.nullifiedTargets = cardUseEvent.nullifiedTargets or {}
aimStruct.firstTarget = firstTarget aimStruct.firstTarget = firstTarget
aimStruct.additionalEffect = cardUseEvent.additionalEffect
aimStruct.extra_data = cardUseEvent.extra_data aimStruct.extra_data = cardUseEvent.extra_data
end end
@ -2483,6 +2502,7 @@ local onAim = function(room, cardUseEvent, aimEventCollaborators)
cardUseEvent.from = aimStruct.from cardUseEvent.from = aimStruct.from
cardUseEvent.tos = aimEventTargetGroup cardUseEvent.tos = aimEventTargetGroup
cardUseEvent.nullifiedTargets = aimStruct.nullifiedTargets cardUseEvent.nullifiedTargets = aimStruct.nullifiedTargets
cardUseEvent.additionalEffect = aimStruct.additionalEffect
cardUseEvent.extra_data = aimStruct.extra_data cardUseEvent.extra_data = aimStruct.extra_data
if #AimGroup:getAllTargets(aimStruct.tos) == 0 then if #AimGroup:getAllTargets(aimStruct.tos) == 0 then
@ -2643,57 +2663,71 @@ function Room:doCardUseEffect(cardUseEvent)
return return
end end
-- Else: do effect to all targets for i = 1, (cardUseEvent.additionalEffect or 0) + 1 do
local collaboratorsIndex = {} if #TargetGroup:getRealTargets(cardUseEvent.tos) > 0 and cardUseEvent.card.skill.onAction then
for _, toId in ipairs(TargetGroup:getRealTargets(cardUseEvent.tos)) do cardUseEvent.card.skill:onAction(self, cardUseEvent)
if not table.contains(cardUseEvent.nullifiedTargets, toId) and self:getPlayerById(toId):isAlive() then end
if aimEventCollaborators[toId] then
cardEffectEvent.to = toId
collaboratorsIndex[toId] = collaboratorsIndex[toId] or 1
local curAimEvent = aimEventCollaborators[toId][collaboratorsIndex[toId]]
cardEffectEvent.subTargets = curAimEvent.subTargets -- Else: do effect to all targets
cardEffectEvent.additionalDamage = curAimEvent.additionalDamage local collaboratorsIndex = {}
cardEffectEvent.additionalRecover = curAimEvent.additionalRecover 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.subTargets = curAimEvent.subTargets
cardEffectEvent.disresponsiveList = cardEffectEvent.disresponsiveList or {} cardEffectEvent.additionalDamage = curAimEvent.additionalDamage
cardEffectEvent.additionalRecover = curAimEvent.additionalRecover
for _, disresponsivePlayer in ipairs(curAimEvent.disresponsiveList) do if curAimEvent.disresponsiveList then
if not table.contains(cardEffectEvent.disresponsiveList, disresponsivePlayer) then cardEffectEvent.disresponsiveList = cardEffectEvent.disresponsiveList or {}
table.insert(cardEffectEvent.disresponsiveList, disresponsivePlayer)
for _, disresponsivePlayer in ipairs(curAimEvent.disresponsiveList) do
if not table.contains(cardEffectEvent.disresponsiveList, disresponsivePlayer) then
table.insert(cardEffectEvent.disresponsiveList, disresponsivePlayer)
end
end end
end end
end
if curAimEvent.unoffsetableList then if curAimEvent.unoffsetableList then
cardEffectEvent.unoffsetableList = cardEffectEvent.unoffsetableList or {} cardEffectEvent.unoffsetableList = cardEffectEvent.unoffsetableList or {}
for _, unoffsetablePlayer in ipairs(curAimEvent.unoffsetableList) do for _, unoffsetablePlayer in ipairs(curAimEvent.unoffsetableList) do
if not table.contains(cardEffectEvent.unoffsetableList, unoffsetablePlayer) then if not table.contains(cardEffectEvent.unoffsetableList, unoffsetablePlayer) then
table.insert(cardEffectEvent.unoffsetableList, unoffsetablePlayer) table.insert(cardEffectEvent.unoffsetableList, unoffsetablePlayer)
end
end end
end end
end
cardEffectEvent.disresponsive = curAimEvent.disresponsive cardEffectEvent.disresponsive = curAimEvent.disresponsive
cardEffectEvent.unoffsetable = curAimEvent.unoffsetable cardEffectEvent.unoffsetable = curAimEvent.unoffsetable
cardEffectEvent.fixedResponseTimes = curAimEvent.fixedResponseTimes cardEffectEvent.fixedResponseTimes = curAimEvent.fixedResponseTimes
cardEffectEvent.fixedAddTimesResponsors = curAimEvent.fixedAddTimesResponsors cardEffectEvent.fixedAddTimesResponsors = curAimEvent.fixedAddTimesResponsors
collaboratorsIndex[toId] = collaboratorsIndex[toId] + 1 collaboratorsIndex[toId] = collaboratorsIndex[toId] + 1
local curCardEffectEvent = table.simpleClone(cardEffectEvent) local curCardEffectEvent = table.simpleClone(cardEffectEvent)
self:doCardEffect(curCardEffectEvent) self:doCardEffect(curCardEffectEvent)
if curCardEffectEvent.cardsResponded then if curCardEffectEvent.cardsResponded then
cardUseEvent.cardsResponded = cardUseEvent.cardsResponded or {} cardUseEvent.cardsResponded = cardUseEvent.cardsResponded or {}
for _, card in ipairs(curCardEffectEvent.cardsResponded) do for _, card in ipairs(curCardEffectEvent.cardsResponded) do
table.insertIfNeed(cardUseEvent.cardsResponded, card) table.insertIfNeed(cardUseEvent.cardsResponded, card)
end
end
if type(curCardEffectEvent.nullifiedTargets) == 'table' then
table.insertTableIfNeed(cardUseEvent.nullifiedTargets, curCardEffectEvent.nullifiedTargets)
end end
end end
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
end end

View File

@ -215,7 +215,7 @@ function ServerPlayer:marshal(player, observe)
if self.dead then if self.dead then
room:notifyProperty(player, self, "dead") room:notifyProperty(player, self, "dead")
room:notifyProperty(player, self, "role") room:notifyProperty(player, self, self.rest > 0 and "rest" or "role")
else else
room:notifyProperty(player, self, "seat") room:notifyProperty(player, self, "seat")
room:notifyProperty(player, self, "phase") room:notifyProperty(player, self, "phase")

View File

@ -110,6 +110,7 @@ fk.IceDamage = 4
---@field public cardsResponded? Card[] ---@field public cardsResponded? Card[]
---@field public prohibitedCardNames? string[] ---@field public prohibitedCardNames? string[]
---@field public damageDealt? table<PlayerId, number> ---@field public damageDealt? table<PlayerId, number>
---@field public additionalEffect? integer
---@class AimStruct ---@class AimStruct
---@field public from integer ---@field public from integer
@ -126,6 +127,7 @@ fk.IceDamage = 4
---@field public unoffsetableList? boolean ---@field public unoffsetableList? boolean
---@field public additionalResponseTimes? table<string, integer>|integer ---@field public additionalResponseTimes? table<string, integer>|integer
---@field public fixedAddTimesResponsors? integer[] ---@field public fixedAddTimesResponsors? integer[]
---@field public additionalEffect? integer
---@class CardEffectEvent ---@class CardEffectEvent
---@field public from integer ---@field public from integer

View File

@ -608,6 +608,43 @@ local amazingGraceSkill = fk.CreateActiveSkill{
can_use = Util.GlobalCanUse, can_use = Util.GlobalCanUse,
on_use = Util.GlobalOnUse, on_use = Util.GlobalOnUse,
mod_target_filter = Util.TrueFunc, 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) on_effect = function(self, room, effect)
local to = room:getPlayerById(effect.to) local to = room:getPlayerById(effect.to)
if not (effect.extra_data and effect.extra_data.AGFilled) then if not (effect.extra_data and effect.extra_data.AGFilled) then