From 7e8b798c05059191b594bf9079eda4c1d513ba2d Mon Sep 17 00:00:00 2001 From: notify Date: Sat, 22 Apr 2023 14:10:06 +0800 Subject: [PATCH] Gameflow (#132) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复trigger的优先级bug 修复回合事件 --- lua/core/util.lua | 26 ++++++++ lua/server/events/gameflow.lua | 7 ++ lua/server/gamelogic.lua | 106 ++++++++++++++++++++----------- packages/maneuvering/init.lua | 12 ++-- packages/standard/game_rule.lua | 15 ++--- packages/standard_cards/init.lua | 6 +- packages/test/init.lua | 6 ++ 7 files changed, 121 insertions(+), 57 deletions(-) diff --git a/lua/core/util.lua b/lua/core/util.lua index 2f0df7fd..04f13b2c 100644 --- a/lua/core/util.lua +++ b/lua/core/util.lua @@ -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) diff --git a/lua/server/events/gameflow.lua b/lua/server/events/gameflow.lua index 8168b020..e5ca889d 100644 --- a/lua/server/events/gameflow.lua +++ b/lua/server/events/gameflow.lua @@ -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 diff --git a/lua/server/gamelogic.lua b/lua/server/gamelogic.lua index 3f791f24..3018b7c8 100644 --- a/lua/server/gamelogic.lua +++ b/lua/server/gamelogic.lua @@ -3,6 +3,7 @@ ---@class GameLogic: Object ---@field public room Room ---@field public skill_table table +---@field public skill_priority_table ---@field public refresh_skill_table table ---@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 - 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 diff --git a/packages/maneuvering/init.lua b/packages/maneuvering/init.lua index 5655cab1..22b64dfa 100644 --- a/packages/maneuvering/init.lua +++ b/packages/maneuvering/init.lua @@ -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 diff --git a/packages/standard/game_rule.lua b/packages/standard/game_rule.lua index 0d25d4dd..e7f22cbd 100644 --- a/packages/standard/game_rule.lua +++ b/packages/standard/game_rule.lua @@ -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() diff --git a/packages/standard_cards/init.lua b/packages/standard_cards/init.lua index 44193154..6ec4738d 100644 --- a/packages/standard_cards/init.lua +++ b/packages/standard_cards/init.lua @@ -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)) diff --git a/packages/test/init.lua b/packages/test/init.lua index 4760e313..5038cb24 100644 --- a/packages/test/init.lua +++ b/packages/test/init.lua @@ -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"] = "测试",