parent
d763929544
commit
7e8b798c05
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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"] = "测试",
|
||||
|
|
Loading…
Reference in New Issue