修复trigger的优先级bug
修复回合事件
This commit is contained in:
notify 2023-04-22 14:10:06 +08:00 committed by GitHub
parent d763929544
commit 7e8b798c05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 121 additions and 57 deletions

View File

@ -30,6 +30,16 @@ function table:every(func)
return true
end
---@param func fun(element, index, array)
function table:find(func)
for i, v in ipairs(self) do
if func(v, i, self) then
return v
end
end
return nil
end
---@generic T
---@param self T[]
---@param func fun(element, index, array)
@ -179,6 +189,22 @@ function table:random(n)
return n0 == nil and ret[1] or ret
end
function table:slice(begin, _end)
local len = #self
begin = begin or 1
_end = _end or len + 1
if begin <= 0 then begin = len + 1 + begin end
if _end <= 0 then _end = len + 1 + _end end
if begin >= _end then return {} end
local ret = {}
for i = begin, _end - 1, 1 do
table.insert(ret, self[i])
end
return ret
end
-- allow a = "Hello"; a[1] == "H"
local str_mt = getmetatable("")
str_mt.__index = function(str, k)

View File

@ -27,4 +27,11 @@ end
GameEvent.functions[GameEvent.Turn] = function(self)
local room = self.room
room.logic:trigger(fk.TurnStart, room.current)
local player = room.current
if not player.faceup then
player:turnOver()
elseif not player.dead then
player:play()
end
end

View File

@ -3,6 +3,7 @@
---@class GameLogic: Object
---@field public room Room
---@field public skill_table table<Event, TriggerSkill[]>
---@field public skill_priority_table<Event, number[]>
---@field public refresh_skill_table table<Event, TriggerSkill[]>
---@field public skills string[]
---@field public event_stack Stack
@ -13,6 +14,7 @@ local GameLogic = class("GameLogic")
function GameLogic:initialize(room)
self.room = room
self.skill_table = {} -- TriggerEvent --> TriggerSkill[]
self.skill_priority_table = {}
self.refresh_skill_table = {}
self.skills = {} -- skillName[]
self.event_stack = Stack:new()
@ -228,6 +230,32 @@ function GameLogic:addTriggerSkill(skill)
self.skill_table[event] = {}
end
table.insert(self.skill_table[event], skill)
if self.skill_priority_table[event] == nil then
self.skill_priority_table[event] = {}
end
local priority_tab = self.skill_priority_table[event]
local prio = skill.priority_table[event]
if not table.contains(priority_tab, prio) then
for i, v in ipairs(priority_tab) do
if v < prio then
table.insert(priority_tab, i, prio)
break
end
end
if not table.contains(priority_tab, prio) then
table.insert(priority_tab, prio)
end
end
if not table.contains(self.skill_priority_table[event],
skill.priority_table[event]) then
table.insert(self.skill_priority_table[event],
skill.priority_table[event])
end
end
if skill.visible then
@ -248,12 +276,12 @@ function GameLogic:trigger(event, target, data)
local broken = false
local skills = self.skill_table[event] or {}
local skills_to_refresh = self.refresh_skill_table[event] or {}
local _target = target or room.current -- for iteration
local _target = room.current -- for iteration
local player = _target
self.event_stack:push({event, target, data})
repeat do
if #skills_to_refresh > 0 then repeat do
-- refresh skills. This should not be broken
for _, skill in ipairs(skills_to_refresh) do
if skill:canRefresh(event, target, player, data) then
@ -261,53 +289,57 @@ function GameLogic:trigger(event, target, data)
end
end
player = player.next
end until player == _target
end until player == _target end
---@param a TriggerSkill
---@param b TriggerSkill
local compare_func = function (a, b)
return a.priority_table[event] > b.priority_table[event]
end
table.sort(skills, compare_func)
if #skills == 0 then return end
repeat do
local triggerable_skills = {} ---@type table<number, TriggerSkill[]>
local priority_table = {} ---@type number[]
for _, skill in ipairs(skills) do
if skill:triggerable(event, target, player, data) then
local priority = skill.priority_table[event]
if triggerable_skills[priority] == nil then
triggerable_skills[priority] = {}
end
table.insert(triggerable_skills[priority], skill)
if not table.contains(priority_table, priority) then
table.insert(priority_table, priority)
end
end
local prio_tab = self.skill_priority_table[event]
local prev_prio = math.huge
for _, prio in ipairs(prio_tab) do
if broken then break end
if prio >= prev_prio then
-- continue
goto trigger_loop_continue
end
for _, priority in ipairs(priority_table) do
local triggerables = triggerable_skills[priority]
local skill_names = {} ---@type string[]
for _, skill in ipairs(triggerables) do
table.insert(skill_names, skill.name)
end
repeat do
local triggerables = table.filter(skills, function(skill)
return skill.priority_table[event] == prio and
skill:triggerable(event, target, player, data)
end)
local skill_names = table.map(triggerables, function(skill)
return skill.name
end)
while #skill_names > 0 do
local skill_name = room:askForChoice(player, skill_names, "trigger", "#choose-trigger")
local skill = triggerables[table.indexOf(skill_names, skill_name)]
local skill_name = prio <= 0 and table.random(skill_names) or
room:askForChoice(player, skill_names, "trigger", "#choose-trigger")
local skill = skill_name == "game_rule" and GameRule
or Fk.skills[skill_name]
local len = #skills
broken = skill:trigger(event, target, player, data)
broken = broken or (event == fk.AskForPeaches and room:getPlayerById(data.who).hp > 0)
table.insertTable(skill_names, table.map(
table.slice(skills, len - #skills), function(s) return s.name end))
broken = broken or (event == fk.AskForPeaches
and room:getPlayerById(data.who).hp > 0)
if broken then break end
table.removeOne(skill_names, skill_name)
table.removeOne(triggerables, skill)
end
end
if broken then break end
if broken then break end
player = player.next
end until player == _target
player = player.next
end until player == _target
prev_prio = prio
::trigger_loop_continue::
end
self.event_stack:pop()
return broken

View File

@ -111,8 +111,8 @@ local analepticEffect = fk.CreateTriggerSkill{
name = "analeptic_effect",
global = true,
priority = 0, -- game rule
refresh_events = { fk.PreCardUse, fk.EventPhaseStart },
can_refresh = function(self, event, target, player, data)
events = { fk.PreCardUse, fk.EventPhaseStart },
can_trigger = function(self, event, target, player, data)
if target ~= player then
return false
end
@ -123,7 +123,7 @@ local analepticEffect = fk.CreateTriggerSkill{
return player.phase == Player.NotActive
end
end,
on_refresh = function(self, event, target, player, data)
on_trigger = function(self, event, target, player, data)
if event == fk.PreCardUse then
data.additionalDamage = (data.additionalDamage or 0) + player.drank
data.extra_data = data.extra_data or {}
@ -161,15 +161,15 @@ local ironChainEffect = fk.CreateTriggerSkill{
name = "iron_chain_effect",
global = true,
priority = { [fk.BeforeHpChanged] = 10, [fk.DamageFinished] = 0 }, -- game rule
refresh_events = { fk.BeforeHpChanged, fk.DamageFinished },
can_refresh = function(self, event, target, player, data)
events = { fk.BeforeHpChanged, fk.DamageFinished },
can_trigger = function(self, event, target, player, data)
if event == fk.BeforeHpChanged then
return target == player and data.damageEvent and data.damageEvent.damageType ~= fk.NormalDamage and player.chained
else
return target == player and data.beginnerOfTheDamage and not data.chain
end
end,
on_refresh = function(self, event, target, player, data)
on_trigger = function(self, event, target, player, data)
local room = player.room
if event == fk.BeforeHpChanged then
data.damageEvent.beginnerOfTheDamage = true

View File

@ -40,8 +40,8 @@ end
GameRule = fk.CreateTriggerSkill{
name = "game_rule",
refresh_events = {
fk.GameStart, fk.DrawInitialCards, fk.TurnStart,
events = {
fk.GameStart, fk.DrawInitialCards,
fk.EventPhaseProceeding, fk.EventPhaseEnd, fk.EventPhaseChanging,
fk.RoundStart,
fk.AskForPeaches, fk.AskForPeachesDone,
@ -49,11 +49,11 @@ GameRule = fk.CreateTriggerSkill{
},
priority = 0,
can_refresh = function(self, event, target, player, data)
can_trigger = function(self, event, target, player, data)
return (target == player) or (target == nil)
end,
on_refresh = function(self, event, target, player, data)
on_trigger = function(self, event, target, player, data)
local room = player.room
if room:getTag("SkipGameRule") then
room:setTag("SkipGameRule", false)
@ -113,13 +113,6 @@ GameRule = fk.CreateTriggerSkill{
room:sendLog{ type = "$AppendSeparator" }
end,
[fk.TurnStart] = function()
if not player.faceup then
player:turnOver()
elseif not player.dead then
player:play()
end
end,
[fk.EventPhaseProceeding] = function()
switch(player.phase, {
[Player.PhaseNone] = function()

View File

@ -566,8 +566,8 @@ local amazingGraceAction = fk.CreateTriggerSkill{
name = "amazing_grace_action",
global = true,
priority = { [fk.BeforeCardUseEffect] = 0, [fk.CardUseFinished] = 10 }, -- game rule
refresh_events = { fk.BeforeCardUseEffect, fk.CardUseFinished },
can_refresh = function(self, event, target, player, data)
events = { fk.BeforeCardUseEffect, fk.CardUseFinished },
can_trigger = function(self, event, target, player, data)
local frameFilled = data.extra_data and data.extra_data.AGFilled
if event == fk.BeforeCardUseEffect then
return data.card.trueName == 'amazing_grace' and not frameFilled
@ -575,7 +575,7 @@ local amazingGraceAction = fk.CreateTriggerSkill{
return frameFilled
end
end,
on_refresh = function(self, event, target, player, data)
on_trigger = function(self, event, target, player, data)
local room = player.room
if event == fk.BeforeCardUseEffect then
local toDisplay = room:getNCards(#TargetGroup:getRealTargets(data.tos))

View File

@ -123,12 +123,18 @@ local test_vs = fk.CreateViewAsSkill{
return c
end,
}
local test_trig = fk.CreateTriggerSkill{
name = "test_trig",
events = {fk.TurnStart},
can_trigger = function() p("a") end,
}
local test2 = General(extension, "mouxusheng", "wu", 4, 4, General.Female)
test2.shield = 4
test2:addSkill("rende")
test2:addSkill(cheat)
test2:addSkill(test_active)
test2:addSkill(test_vs)
test2:addSkill(test_trig)
Fk:loadTranslationTable{
["test"] = "测试",