parent
d763929544
commit
7e8b798c05
|
@ -30,6 +30,16 @@ function table:every(func)
|
||||||
return true
|
return true
|
||||||
end
|
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
|
---@generic T
|
||||||
---@param self T[]
|
---@param self T[]
|
||||||
---@param func fun(element, index, array)
|
---@param func fun(element, index, array)
|
||||||
|
@ -179,6 +189,22 @@ function table:random(n)
|
||||||
return n0 == nil and ret[1] or ret
|
return n0 == nil and ret[1] or ret
|
||||||
end
|
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"
|
-- allow a = "Hello"; a[1] == "H"
|
||||||
local str_mt = getmetatable("")
|
local str_mt = getmetatable("")
|
||||||
str_mt.__index = function(str, k)
|
str_mt.__index = function(str, k)
|
||||||
|
|
|
@ -27,4 +27,11 @@ end
|
||||||
GameEvent.functions[GameEvent.Turn] = function(self)
|
GameEvent.functions[GameEvent.Turn] = function(self)
|
||||||
local room = self.room
|
local room = self.room
|
||||||
room.logic:trigger(fk.TurnStart, room.current)
|
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
|
end
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
---@class GameLogic: Object
|
---@class GameLogic: Object
|
||||||
---@field public room Room
|
---@field public room Room
|
||||||
---@field public skill_table table<Event, TriggerSkill[]>
|
---@field public skill_table table<Event, TriggerSkill[]>
|
||||||
|
---@field public skill_priority_table<Event, number[]>
|
||||||
---@field public refresh_skill_table table<Event, TriggerSkill[]>
|
---@field public refresh_skill_table table<Event, TriggerSkill[]>
|
||||||
---@field public skills string[]
|
---@field public skills string[]
|
||||||
---@field public event_stack Stack
|
---@field public event_stack Stack
|
||||||
|
@ -13,6 +14,7 @@ local GameLogic = class("GameLogic")
|
||||||
function GameLogic:initialize(room)
|
function GameLogic:initialize(room)
|
||||||
self.room = room
|
self.room = room
|
||||||
self.skill_table = {} -- TriggerEvent --> TriggerSkill[]
|
self.skill_table = {} -- TriggerEvent --> TriggerSkill[]
|
||||||
|
self.skill_priority_table = {}
|
||||||
self.refresh_skill_table = {}
|
self.refresh_skill_table = {}
|
||||||
self.skills = {} -- skillName[]
|
self.skills = {} -- skillName[]
|
||||||
self.event_stack = Stack:new()
|
self.event_stack = Stack:new()
|
||||||
|
@ -228,6 +230,32 @@ function GameLogic:addTriggerSkill(skill)
|
||||||
self.skill_table[event] = {}
|
self.skill_table[event] = {}
|
||||||
end
|
end
|
||||||
table.insert(self.skill_table[event], skill)
|
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
|
end
|
||||||
|
|
||||||
if skill.visible then
|
if skill.visible then
|
||||||
|
@ -248,12 +276,12 @@ function GameLogic:trigger(event, target, data)
|
||||||
local broken = false
|
local broken = false
|
||||||
local skills = self.skill_table[event] or {}
|
local skills = self.skill_table[event] or {}
|
||||||
local skills_to_refresh = self.refresh_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
|
local player = _target
|
||||||
|
|
||||||
self.event_stack:push({event, target, data})
|
self.event_stack:push({event, target, data})
|
||||||
|
|
||||||
repeat do
|
if #skills_to_refresh > 0 then repeat do
|
||||||
-- refresh skills. This should not be broken
|
-- refresh skills. This should not be broken
|
||||||
for _, skill in ipairs(skills_to_refresh) do
|
for _, skill in ipairs(skills_to_refresh) do
|
||||||
if skill:canRefresh(event, target, player, data) then
|
if skill:canRefresh(event, target, player, data) then
|
||||||
|
@ -261,53 +289,57 @@ function GameLogic:trigger(event, target, data)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
player = player.next
|
player = player.next
|
||||||
end until player == _target
|
end until player == _target end
|
||||||
|
|
||||||
---@param a TriggerSkill
|
if #skills == 0 then return end
|
||||||
---@param b TriggerSkill
|
|
||||||
local compare_func = function (a, b)
|
|
||||||
return a.priority_table[event] > b.priority_table[event]
|
|
||||||
end
|
|
||||||
table.sort(skills, compare_func)
|
|
||||||
|
|
||||||
repeat do
|
local prio_tab = self.skill_priority_table[event]
|
||||||
local triggerable_skills = {} ---@type table<number, TriggerSkill[]>
|
local prev_prio = math.huge
|
||||||
local priority_table = {} ---@type number[]
|
|
||||||
for _, skill in ipairs(skills) do
|
for _, prio in ipairs(prio_tab) do
|
||||||
if skill:triggerable(event, target, player, data) then
|
if broken then break end
|
||||||
local priority = skill.priority_table[event]
|
if prio >= prev_prio then
|
||||||
if triggerable_skills[priority] == nil then
|
-- continue
|
||||||
triggerable_skills[priority] = {}
|
goto trigger_loop_continue
|
||||||
end
|
|
||||||
table.insert(triggerable_skills[priority], skill)
|
|
||||||
if not table.contains(priority_table, priority) then
|
|
||||||
table.insert(priority_table, priority)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, priority in ipairs(priority_table) do
|
repeat do
|
||||||
local triggerables = triggerable_skills[priority]
|
local triggerables = table.filter(skills, function(skill)
|
||||||
local skill_names = {} ---@type string[]
|
return skill.priority_table[event] == prio and
|
||||||
for _, skill in ipairs(triggerables) do
|
skill:triggerable(event, target, player, data)
|
||||||
table.insert(skill_names, skill.name)
|
end)
|
||||||
end
|
|
||||||
|
local skill_names = table.map(triggerables, function(skill)
|
||||||
|
return skill.name
|
||||||
|
end)
|
||||||
|
|
||||||
while #skill_names > 0 do
|
while #skill_names > 0 do
|
||||||
local skill_name = room:askForChoice(player, skill_names, "trigger", "#choose-trigger")
|
local skill_name = prio <= 0 and table.random(skill_names) or
|
||||||
local skill = triggerables[table.indexOf(skill_names, skill_name)]
|
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 = 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
|
if broken then break end
|
||||||
table.removeOne(skill_names, skill_name)
|
table.removeOne(skill_names, skill_name)
|
||||||
table.removeOne(triggerables, skill)
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
if broken then break end
|
if broken then break end
|
||||||
|
|
||||||
player = player.next
|
player = player.next
|
||||||
end until player == _target
|
end until player == _target
|
||||||
|
|
||||||
|
prev_prio = prio
|
||||||
|
::trigger_loop_continue::
|
||||||
|
end
|
||||||
|
|
||||||
self.event_stack:pop()
|
self.event_stack:pop()
|
||||||
return broken
|
return broken
|
||||||
|
|
|
@ -111,8 +111,8 @@ local analepticEffect = fk.CreateTriggerSkill{
|
||||||
name = "analeptic_effect",
|
name = "analeptic_effect",
|
||||||
global = true,
|
global = true,
|
||||||
priority = 0, -- game rule
|
priority = 0, -- game rule
|
||||||
refresh_events = { fk.PreCardUse, fk.EventPhaseStart },
|
events = { fk.PreCardUse, fk.EventPhaseStart },
|
||||||
can_refresh = function(self, event, target, player, data)
|
can_trigger = function(self, event, target, player, data)
|
||||||
if target ~= player then
|
if target ~= player then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
@ -123,7 +123,7 @@ local analepticEffect = fk.CreateTriggerSkill{
|
||||||
return player.phase == Player.NotActive
|
return player.phase == Player.NotActive
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
on_refresh = function(self, event, target, player, data)
|
on_trigger = function(self, event, target, player, data)
|
||||||
if event == fk.PreCardUse then
|
if event == fk.PreCardUse then
|
||||||
data.additionalDamage = (data.additionalDamage or 0) + player.drank
|
data.additionalDamage = (data.additionalDamage or 0) + player.drank
|
||||||
data.extra_data = data.extra_data or {}
|
data.extra_data = data.extra_data or {}
|
||||||
|
@ -161,15 +161,15 @@ local ironChainEffect = fk.CreateTriggerSkill{
|
||||||
name = "iron_chain_effect",
|
name = "iron_chain_effect",
|
||||||
global = true,
|
global = true,
|
||||||
priority = { [fk.BeforeHpChanged] = 10, [fk.DamageFinished] = 0 }, -- game rule
|
priority = { [fk.BeforeHpChanged] = 10, [fk.DamageFinished] = 0 }, -- game rule
|
||||||
refresh_events = { fk.BeforeHpChanged, fk.DamageFinished },
|
events = { fk.BeforeHpChanged, fk.DamageFinished },
|
||||||
can_refresh = function(self, event, target, player, data)
|
can_trigger = function(self, event, target, player, data)
|
||||||
if event == fk.BeforeHpChanged then
|
if event == fk.BeforeHpChanged then
|
||||||
return target == player and data.damageEvent and data.damageEvent.damageType ~= fk.NormalDamage and player.chained
|
return target == player and data.damageEvent and data.damageEvent.damageType ~= fk.NormalDamage and player.chained
|
||||||
else
|
else
|
||||||
return target == player and data.beginnerOfTheDamage and not data.chain
|
return target == player and data.beginnerOfTheDamage and not data.chain
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
on_refresh = function(self, event, target, player, data)
|
on_trigger = function(self, event, target, player, data)
|
||||||
local room = player.room
|
local room = player.room
|
||||||
if event == fk.BeforeHpChanged then
|
if event == fk.BeforeHpChanged then
|
||||||
data.damageEvent.beginnerOfTheDamage = true
|
data.damageEvent.beginnerOfTheDamage = true
|
||||||
|
|
|
@ -40,8 +40,8 @@ end
|
||||||
|
|
||||||
GameRule = fk.CreateTriggerSkill{
|
GameRule = fk.CreateTriggerSkill{
|
||||||
name = "game_rule",
|
name = "game_rule",
|
||||||
refresh_events = {
|
events = {
|
||||||
fk.GameStart, fk.DrawInitialCards, fk.TurnStart,
|
fk.GameStart, fk.DrawInitialCards,
|
||||||
fk.EventPhaseProceeding, fk.EventPhaseEnd, fk.EventPhaseChanging,
|
fk.EventPhaseProceeding, fk.EventPhaseEnd, fk.EventPhaseChanging,
|
||||||
fk.RoundStart,
|
fk.RoundStart,
|
||||||
fk.AskForPeaches, fk.AskForPeachesDone,
|
fk.AskForPeaches, fk.AskForPeachesDone,
|
||||||
|
@ -49,11 +49,11 @@ GameRule = fk.CreateTriggerSkill{
|
||||||
},
|
},
|
||||||
priority = 0,
|
priority = 0,
|
||||||
|
|
||||||
can_refresh = function(self, event, target, player, data)
|
can_trigger = function(self, event, target, player, data)
|
||||||
return (target == player) or (target == nil)
|
return (target == player) or (target == nil)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
on_refresh = function(self, event, target, player, data)
|
on_trigger = function(self, event, target, player, data)
|
||||||
local room = player.room
|
local room = player.room
|
||||||
if room:getTag("SkipGameRule") then
|
if room:getTag("SkipGameRule") then
|
||||||
room:setTag("SkipGameRule", false)
|
room:setTag("SkipGameRule", false)
|
||||||
|
@ -113,13 +113,6 @@ GameRule = fk.CreateTriggerSkill{
|
||||||
|
|
||||||
room:sendLog{ type = "$AppendSeparator" }
|
room:sendLog{ type = "$AppendSeparator" }
|
||||||
end,
|
end,
|
||||||
[fk.TurnStart] = function()
|
|
||||||
if not player.faceup then
|
|
||||||
player:turnOver()
|
|
||||||
elseif not player.dead then
|
|
||||||
player:play()
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
[fk.EventPhaseProceeding] = function()
|
[fk.EventPhaseProceeding] = function()
|
||||||
switch(player.phase, {
|
switch(player.phase, {
|
||||||
[Player.PhaseNone] = function()
|
[Player.PhaseNone] = function()
|
||||||
|
|
|
@ -566,8 +566,8 @@ local amazingGraceAction = fk.CreateTriggerSkill{
|
||||||
name = "amazing_grace_action",
|
name = "amazing_grace_action",
|
||||||
global = true,
|
global = true,
|
||||||
priority = { [fk.BeforeCardUseEffect] = 0, [fk.CardUseFinished] = 10 }, -- game rule
|
priority = { [fk.BeforeCardUseEffect] = 0, [fk.CardUseFinished] = 10 }, -- game rule
|
||||||
refresh_events = { fk.BeforeCardUseEffect, fk.CardUseFinished },
|
events = { fk.BeforeCardUseEffect, fk.CardUseFinished },
|
||||||
can_refresh = function(self, event, target, player, data)
|
can_trigger = function(self, event, target, player, data)
|
||||||
local frameFilled = data.extra_data and data.extra_data.AGFilled
|
local frameFilled = data.extra_data and data.extra_data.AGFilled
|
||||||
if event == fk.BeforeCardUseEffect then
|
if event == fk.BeforeCardUseEffect then
|
||||||
return data.card.trueName == 'amazing_grace' and not frameFilled
|
return data.card.trueName == 'amazing_grace' and not frameFilled
|
||||||
|
@ -575,7 +575,7 @@ local amazingGraceAction = fk.CreateTriggerSkill{
|
||||||
return frameFilled
|
return frameFilled
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
on_refresh = function(self, event, target, player, data)
|
on_trigger = function(self, event, target, player, data)
|
||||||
local room = player.room
|
local room = player.room
|
||||||
if event == fk.BeforeCardUseEffect then
|
if event == fk.BeforeCardUseEffect then
|
||||||
local toDisplay = room:getNCards(#TargetGroup:getRealTargets(data.tos))
|
local toDisplay = room:getNCards(#TargetGroup:getRealTargets(data.tos))
|
||||||
|
|
|
@ -123,12 +123,18 @@ local test_vs = fk.CreateViewAsSkill{
|
||||||
return c
|
return c
|
||||||
end,
|
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)
|
local test2 = General(extension, "mouxusheng", "wu", 4, 4, General.Female)
|
||||||
test2.shield = 4
|
test2.shield = 4
|
||||||
test2:addSkill("rende")
|
test2:addSkill("rende")
|
||||||
test2:addSkill(cheat)
|
test2:addSkill(cheat)
|
||||||
test2:addSkill(test_active)
|
test2:addSkill(test_active)
|
||||||
test2:addSkill(test_vs)
|
test2:addSkill(test_vs)
|
||||||
|
test2:addSkill(test_trig)
|
||||||
|
|
||||||
Fk:loadTranslationTable{
|
Fk:loadTranslationTable{
|
||||||
["test"] = "测试",
|
["test"] = "测试",
|
||||||
|
|
Loading…
Reference in New Issue