This commit is contained in:
DESKTOP-C7UTUBQ\32064 2023-09-23 14:09:09 +08:00
commit c99e200301
9 changed files with 1318 additions and 1479 deletions

View File

@ -747,6 +747,26 @@ Item {
} }
} }
// manualBox: same as popupBox, but must be closed manually
Loader {
id: manualBox
z: 999
onSourceChanged: {
if (item === null)
return;
item.finished.connect(() => sourceComponent = undefined);
item.widthChanged.connect(() => manualBox.moveToCenter());
item.heightChanged.connect(() => manualBox.moveToCenter());
moveToCenter();
}
onSourceComponentChanged: sourceChanged();
function moveToCenter() {
item.x = Math.round((roomArea.width - item.width) / 2);
item.y = Math.round(roomArea.height * 0.67 - item.height / 2);
}
}
Loader { Loader {
id: popupBox id: popupBox
z: 999 z: 999
@ -772,27 +792,6 @@ Item {
} }
} }
// manualBox: same as popupBox, but must be closed manually
Loader {
id: manualBox
z: 999
onSourceChanged: {
if (item === null)
return;
item.finished.connect(() => sourceComponent = undefined);
item.widthChanged.connect(() => manualBox.moveToCenter());
item.heightChanged.connect(() => manualBox.moveToCenter());
moveToCenter();
}
onSourceComponentChanged: sourceChanged();
function moveToCenter()
{
item.x = Math.round((roomArea.width - item.width) / 2);
item.y = Math.round(roomArea.height * 0.67 - item.height / 2);
}
}
Loader { Loader {
id: bigAnim id: bigAnim
anchors.fill: parent anchors.fill: parent

File diff suppressed because it is too large Load Diff

View File

@ -6,273 +6,273 @@ local RandomAI = AI:subclass("RandomAI")
---@param skill ActiveSkill ---@param skill ActiveSkill
---@param card Card | nil ---@param card Card | nil
local function useActiveSkill(self, skill, card) local function useActiveSkill(self, skill, card)
local room = self.room local room = self.room
local player = self.player local player = self.player
local filter_func = skill.cardFilter local filter_func = skill.cardFilter
if card then if card then
filter_func = function() filter_func = function()
return false return false
end
end end
end
if self.command == "PlayCard" and (not skill:canUse(player, card) or player:prohibitUse(card)) then if self.command == "PlayCard" and (not skill:canUse(player, card) or player:prohibitUse(card)) then
return ""
end
local max_try_times = 100
local selected_targets = {}
local selected_cards = {}
local min = skill:getMinTargetNum()
local max = skill:getMaxTargetNum(player, card)
local min_card = skill:getMinCardNum()
local max_card = skill:getMaxCardNum()
for _ = 0, max_try_times do
if skill:feasible(selected_targets, selected_cards, self.player, card) then
break
end
local avail_targets = table.filter(self.room:getAlivePlayers(), function(p)
local ret = skill:targetFilter(p.id, selected_targets, selected_cards, card or Fk:cloneCard 'zixing')
if ret and card then
if player:prohibitUse(card) then
ret = false
end
end
return ret
end)
avail_targets = table.map(avail_targets, function(p)
return p.id
end)
local avail_cards = table.filter(player:getCardIds{Player.Hand, Player.Equip}, function(id)
return filter_func(skill, id, selected_cards, selected_targets)
end)
if #avail_targets == 0 and #avail_cards == 0 then
break
end
table.insertIfNeed(selected_targets, table.random(avail_targets))
table.insertIfNeed(selected_cards, table.random(avail_cards))
end
if skill:feasible(selected_targets, selected_cards, self.player, card) then
local ret = json.encode {
card = card and card.id or json.encode {
skill = skill.name,
subcards = selected_cards
},
targets = selected_targets
}
-- print(ret)
return ret
end
return "" return ""
end
local max_try_times = 100
local selected_targets = {}
local selected_cards = {}
local min = skill:getMinTargetNum()
local max = skill:getMaxTargetNum(player, card)
local min_card = skill:getMinCardNum()
local max_card = skill:getMaxCardNum()
for _ = 0, max_try_times do
if skill:feasible(selected_targets, selected_cards, self.player, card) then
break
end
local avail_targets = table.filter(self.room:getAlivePlayers(), function(p)
local ret = skill:targetFilter(p.id, selected_targets, selected_cards, card or Fk:cloneCard 'zixing')
if ret and card then
if player:prohibitUse(card) then
ret = false
end
end
return ret
end)
avail_targets = table.map(avail_targets, function(p)
return p.id
end)
local avail_cards = table.filter(player:getCardIds { Player.Hand, Player.Equip }, function(id)
return filter_func(skill, id, selected_cards, selected_targets)
end)
if #avail_targets == 0 and #avail_cards == 0 then
break
end
table.insertIfNeed(selected_targets, table.random(avail_targets))
table.insertIfNeed(selected_cards, table.random(avail_cards))
end
if skill:feasible(selected_targets, selected_cards, self.player, card) then
local ret = json.encode {
card = card and card.id or json.encode {
skill = skill.name,
subcards = selected_cards
},
targets = selected_targets
}
-- print(ret)
return ret
end
return ""
end end
---@param self RandomAI ---@param self RandomAI
---@param skill ViewAsSkill ---@param skill ViewAsSkill
local function useVSSkill(self, skill, pattern, cancelable, extra_data) local function useVSSkill(self, skill, pattern, cancelable, extra_data)
local player = self.player local player = self.player
local room = self.room local room = self.room
local precondition local precondition
if self.command == "PlayCard" then if self.command == "PlayCard" then
precondition = skill:enabledAtPlay(player) precondition = skill:enabledAtPlay(player)
if not precondition then if not precondition then
return nil return nil
end
local exp = Exppattern:Parse(skill.pattern)
local cnames = {}
for _, m in ipairs(exp.matchers) do
if m.name then
table.insertTable(cnames, m.name)
end
end
for _, n in ipairs(cnames) do
local c = Fk:cloneCard(n)
precondition = c.skill:canUse(Self, c)
if precondition then
break
end
end
else
precondition = skill:enabledAtResponse(player)
if not precondition then
return nil
end
local exp = Exppattern:Parse(pattern)
precondition = exp:matchExp(skill.pattern)
end end
local exp = Exppattern:Parse(skill.pattern)
if (not precondition) or math.random() < 0.2 then local cnames = {}
return nil for _, m in ipairs(exp.matchers) do
if m.name then
table.insertTable(cnames, m.name)
end
end end
for _, n in ipairs(cnames) do
local selected_cards = {} local c = Fk:cloneCard(n)
local max_try_time = 100 precondition = c.skill:canUse(Self, c)
if precondition then
for _ = 0, max_try_time do break
local avail_cards = table.filter(player:getCardIds{Player.Hand, Player.Equip}, function(id) end
return skill:cardFilter(id, selected_cards)
end)
if #avail_cards == 0 then
break
end
table.insert(selected_cards, table.random(avail_cards))
if skill:viewAs(selected_cards) then
return {
skill = skill.name,
subcards = selected_cards
}
end
end end
else
precondition = skill:enabledAtResponse(player)
if not precondition then
return nil
end
local exp = Exppattern:Parse(pattern)
precondition = exp:matchExp(skill.pattern)
end
if (not precondition) or math.random() < 0.2 then
return nil return nil
end
local selected_cards = {}
local max_try_time = 100
for _ = 0, max_try_time do
local avail_cards = table.filter(player:getCardIds { Player.Hand, Player.Equip }, function(id)
return skill:cardFilter(id, selected_cards)
end)
if #avail_cards == 0 then
break
end
table.insert(selected_cards, table.random(avail_cards))
if skill:viewAs(selected_cards) then
return {
skill = skill.name,
subcards = selected_cards
}
end
end
return nil
end end
local random_cb = {} local random_cb = {}
random_cb.AskForUseActiveSkill = function(self, jsonData) random_cb.AskForUseActiveSkill = function(self, jsonData)
local data = json.decode(jsonData) local data = json.decode(jsonData)
local skill = Fk.skills[data[1]] local skill = Fk.skills[data[1]]
local cancelable = data[3] local cancelable = data[3]
if cancelable and math.random() < 0.25 then if cancelable and math.random() < 0.25 then
return "" return ""
end end
local extra_data = json.decode(data[4]) local extra_data = json.decode(data[4])
for k, v in pairs(extra_data) do for k, v in pairs(extra_data) do
skill[k] = v skill[k] = v
end end
return useActiveSkill(self, skill) return useActiveSkill(self, skill)
end end
random_cb.AskForSkillInvoke = function(self, jsonData) random_cb.AskForSkillInvoke = function(self, jsonData)
return table.random {"1", ""} return table.random { "1", "" }
end end
random_cb.AskForUseCard = function(self, jsonData) random_cb.AskForUseCard = function(self, jsonData)
local player = self.player local player = self.player
local data = json.decode(jsonData) local data = json.decode(jsonData)
local card_name = data[1] local card_name = data[1]
local pattern = data[2] or card_name local pattern = data[2] or card_name
local cancelable = data[4] or true local cancelable = data[4] or true
local exp = Exppattern:Parse(pattern) local exp = Exppattern:Parse(pattern)
local avail_cards = table.filter(player:getCardIds("he"), function(id) local avail_cards = table.filter(player:getCardIds("he"), function(id)
return exp:match(Fk:getCardById(id)) and not player:prohibitUse(Fk:getCardById(id)) return exp:match(Fk:getCardById(id)) and not player:prohibitUse(Fk:getCardById(id))
end) end)
if #avail_cards > 0 then if #avail_cards > 0 then
if math.random() < 0.25 then if math.random() < 0.25 then
return "" return ""
end
avail_cards = table.map(avail_cards, function(id)
return Fk:getCardById(id)
end)
for _, card in ipairs(avail_cards) do
local skill = card.skill
local max_try_times = 100
local selected_targets = {}
local min = skill:getMinTargetNum()
local max = skill:getMaxTargetNum(player, card)
local min_card = skill:getMinCardNum()
local max_card = skill:getMaxCardNum()
for _ = 0, max_try_times do
if skill:feasible(selected_targets, {card.id}, self.player, card) then
break
end
local avail_targets = table.filter(self.room:getAlivePlayers(), function(p)
local ret = skill:targetFilter(p.id, selected_targets, {card}, card or Fk:cloneCard 'zixing')
return ret
end)
avail_targets = table.map(avail_targets, function(p)
return p.id
end)
if #avail_targets + #avail_cards < 1 then
break
end
table.insertIfNeed(selected_targets, table.random(avail_targets))
end
if skill:feasible(selected_targets, {card.id}, self.player, card) then
return json.encode {
card = table.random(avail_cards),
targets = selected_targets
}
end
end
end end
return "" avail_cards = table.map(avail_cards, function(id)
return Fk:getCardById(id)
end)
for _, card in ipairs(avail_cards) do
local skill = card.skill
local max_try_times = 100
local selected_targets = {}
local min = skill:getMinTargetNum()
local max = skill:getMaxTargetNum(player, card)
local min_card = skill:getMinCardNum()
local max_card = skill:getMaxCardNum()
for _ = 0, max_try_times do
if skill:feasible(selected_targets, { card.id }, self.player, card) then
break
end
local avail_targets = table.filter(self.room:getAlivePlayers(), function(p)
local ret = skill:targetFilter(p.id, selected_targets, { card }, card or Fk:cloneCard 'zixing')
return ret
end)
avail_targets = table.map(avail_targets, function(p)
return p.id
end)
if #avail_targets + #avail_cards < 1 then
break
end
table.insertIfNeed(selected_targets, table.random(avail_targets))
end
if skill:feasible(selected_targets, { card.id }, self.player, card) then
return json.encode {
card = table.random(avail_cards),
targets = selected_targets
}
end
end
end
return ""
end end
---@param self RandomAI ---@param self RandomAI
random_cb.AskForResponseCard = function(self, jsonData) random_cb.AskForResponseCard = function(self, jsonData)
local data = json.decode(jsonData) local data = json.decode(jsonData)
local pattern = data[2] local pattern = data[2]
local cancelable = true local cancelable = true
local exp = Exppattern:Parse(pattern) local exp = Exppattern:Parse(pattern)
local avail_cards = table.filter(self.player:getCardIds{Player.Hand, Player.Equip}, function(id) local avail_cards = table.filter(self.player:getCardIds { Player.Hand, Player.Equip }, function(id)
return exp:match(Fk:getCardById(id)) return exp:match(Fk:getCardById(id))
end) end)
if #avail_cards > 0 then if #avail_cards > 0 then
return json.encode { return json.encode {
card = table.random(avail_cards), card = table.random(avail_cards),
targets = {} targets = {}
} }
end end
-- TODO: vs skill -- TODO: vs skill
return "" return ""
end end
---@param self RandomAI ---@param self RandomAI
random_cb.PlayCard = function(self, jsonData) random_cb.PlayCard = function(self, jsonData)
local cards = table.map(self.player:getCardIds(Player.Hand), function(id) local cards = table.map(self.player:getCardIds(Player.Hand), function(id)
return Fk:getCardById(id) return Fk:getCardById(id)
end) end)
local actives = table.filter(self.player:getAllSkills(), function(s) local actives = table.filter(self.player:getAllSkills(), function(s)
return s:isInstanceOf(ActiveSkill) return s:isInstanceOf(ActiveSkill)
end) end)
local vss = table.filter(self.player:getAllSkills(), function(s) local vss = table.filter(self.player:getAllSkills(), function(s)
return s:isInstanceOf(ViewAsSkill) return s:isInstanceOf(ViewAsSkill)
end) end)
table.insertTable(cards, actives) table.insertTable(cards, actives)
table.insertTable(cards, vss) table.insertTable(cards, vss)
while #cards > 0 do while #cards > 0 do
local sth = table.random(cards) local sth = table.random(cards)
if sth:isInstanceOf(Card) then if sth:isInstanceOf(Card) then
local card = sth local card = sth
local skill = card.skill ---@type ActiveSkill local skill = card.skill ---@type ActiveSkill
if math.random() > 0.15 then if math.random() > 0.15 then
local ret = useActiveSkill(self, skill, card) local ret = useActiveSkill(self, skill, card)
if ret ~= "" then if ret ~= "" then
return ret return ret
end
table.removeOne(cards, card)
else
table.removeOne(cards, card)
end
elseif sth:isInstanceOf(ActiveSkill) then
local active = sth
if math.random() > 0.30 then
local ret = useActiveSkill(self, active, nil)
if ret ~= "" then
return ret
end
end
table.removeOne(cards, active)
else
local vs = sth
if math.random() > 0.20 then
local ret = useVSSkill(self, vs)
-- TODO: handle vs result
end
table.removeOne(cards, vs)
end end
table.removeOne(cards, card)
else
table.removeOne(cards, card)
end
elseif sth:isInstanceOf(ActiveSkill) then
local active = sth
if math.random() > 0.30 then
local ret = useActiveSkill(self, active, nil)
if ret ~= "" then
return ret
end
end
table.removeOne(cards, active)
else
local vs = sth
if math.random() > 0.20 then
local ret = useVSSkill(self, vs)
-- TODO: handle vs result
end
table.removeOne(cards, vs)
end end
end
return "" return ""
end end
function RandomAI:initialize(player) function RandomAI:initialize(player)
AI.initialize(self, player) AI.initialize(self, player)
self.cb_table = random_cb self.cb_table = random_cb
end end
return RandomAI return RandomAI

View File

@ -8,328 +8,6 @@ local trust_cb = {}
function TrustAI:initialize(player) function TrustAI:initialize(player)
AI.initialize(self, player) AI.initialize(self, player)
self.cb_table = trust_cb self.cb_table = trust_cb
<<<<<<< HEAD
=======
self.player = player
self.room = RoomInstance or ClientInstance
fk.ai_role[player.id] = "neutral"
fk.roleValue[player.id] = {
lord = 0,
loyalist = 0,
rebel = 0,
renegade = 0
}
self:updatePlayers()
end
function TrustAI:isRolePredictable()
return self.room.settings.gameMode ~= "aaa_role_mode"
end
local function aliveRoles(room)
fk.alive_roles = {
lord = 0,
loyalist = 0,
rebel = 0,
renegade = 0
}
for _, ap in ipairs(room:getAllPlayers(false)) do
fk.alive_roles[ap.role] = 0
end
for _, ap in ipairs(room:getAlivePlayers(false)) do
fk.alive_roles[ap.role] = fk.alive_roles[ap.role] + 1
end
return fk.alive_roles
end
function TrustAI:objectiveLevel(to)
if self.player.id == to.id then
return -2
elseif #self.room:getAlivePlayers(false) < 3 then
return 5
end
local ars = aliveRoles(self.room)
if self:isRolePredictable() then
fk.ai_role[self.player.id] = self.role
fk.roleValue[self.player.id][self.role] = 666
if self.role == "renegade" then
fk.explicit_renegade = true
end
for _, p in ipairs(self.room:getAlivePlayers()) do
if
p.role == self.role or p.role == "lord" and self.role == "loyalist" or
p.role == "loyalist" and self.role == "lord"
then
table.insert(self.friends, p)
if p.id ~= self.player.id then
table.insert(self.friends_noself, p)
end
else
table.insert(self.enemies, p)
end
end
elseif self.role == "renegade" then
if to.role == "lord" then
return -1
elseif ars.rebel < 1 then
return 4
elseif fk.ai_role[to.id] == "loyalist" then
return ars.lord + ars.loyalist - ars.rebel
elseif fk.ai_role[to.id] == "rebel" then
local r = ars.rebel - ars.lord + ars.loyalist
if r >= 0 then
return 3
else
return r
end
end
elseif self.role == "lord" or self.role == "loyalist" then
if fk.ai_role[to.id] == "rebel" then
return 5
elseif to.role == "lord" then
return -2
elseif ars.rebel < 1 then
if self.role == "lord" then
return fk.explicit_renegade and fk.ai_role[to.id] == "renegade" and 4 or to.hp > 1 and 2 or 0
elseif fk.explicit_renegade then
return fk.ai_role[to.id] == "renegade" and 4 or -1
else
return 3
end
elseif fk.ai_role[to.id] == "loyalist" then
return -2
elseif fk.ai_role[to.id] == "renegade" then
local r = ars.lord + ars.loyalist - ars.rebel
if r <= 0 then
return r
else
return 3
end
end
elseif self.role == "rebel" then
if to.role == "lord" then
return 5
elseif fk.ai_role[to.id] == "loyalist" then
return 4
elseif fk.ai_role[to.id] == "rebel" then
return -2
elseif fk.ai_role[to.id] == "renegade" then
local r = ars.rebel - ars.lord + ars.loyalist
if r > 0 then
return 1
else
return r
end
end
end
return 0
end
function TrustAI:updatePlayers(update)
self.role = self.player.role
local neutrality = {}
self.enemies = {}
self.friends = {}
self.friends_noself = {}
local aps = self.room:getAlivePlayers()
local function compare_func(a, b)
local v1 = fk.roleValue[a.id].rebel
local v2 = fk.roleValue[b.id].rebel
if v1 == v2 then
v1 = fk.roleValue[a.id].renegade
v2 = fk.roleValue[b.id].renegade
end
return v1 > v2
end
table.sort(aps, compare_func)
fk.explicit_renegade = false
local ars = aliveRoles(self.room)
local rebel, renegade, loyalist = 0, 0, 0
for _, ap in ipairs(aps) do
if ap.role == "lord" then
fk.ai_role[ap.id] = "loyalist"
elseif fk.roleValue[ap.id].rebel > 50 and ars.rebel > rebel then
rebel = rebel + 1
fk.ai_role[ap.id] = "rebel"
elseif fk.roleValue[ap.id].renegade > 50 and ars.renegade > renegade then
renegade = renegade + 1
fk.ai_role[ap.id] = "renegade"
fk.explicit_renegade = fk.roleValue[ap.id].renegade > 100
elseif fk.roleValue[ap.id].rebel < -50 and ars.loyalist > loyalist then
loyalist = loyalist + 1
fk.ai_role[ap.id] = "loyalist"
else
fk.ai_role[ap.id] = "neutral"
end
end
for n, p in ipairs(self.room:getAlivePlayers(false)) do
n = self:objectiveLevel(p)
if n < 0 then
table.insert(self.friends, p)
if p.id ~= self.player.id then
table.insert(self.friends_noself, p)
end
elseif n > 0 then
table.insert(self.enemies, p)
else
table.insert(neutrality, p)
end
end
self:assignValue()
--[[
if self.enemies<1 and #neutrality>0
and#self.toUse<3 and self:getOverflow()>0
then
function compare_func(a,b)
return sgs.getDefense(a)<sgs.getDefense(b)
end
table.sort(neutrality,compare_func)
table.insert(self.enemies,neutrality[1])
end-]]
end
local function updateIntention(player, to, intention)
if player.id == to.id then
return
elseif player.role == "lord" then
fk.roleValue[to.id].rebel = fk.roleValue[to.id].rebel + intention * (200 - fk.roleValue[to.id].rebel) / 200
else
if to.role == "lord" or fk.ai_role[to.id] == "loyalist" then
fk.roleValue[player.id].rebel = fk.roleValue[player.id].rebel +
intention * (200 - fk.roleValue[player.id].rebel) / 200
elseif fk.ai_role[to.id] == "rebel" then
fk.roleValue[player.id].rebel = fk.roleValue[player.id].rebel -
intention * (fk.roleValue[player.id].rebel + 200) / 200
end
if fk.roleValue[player.id].rebel < 0 and intention > 0 or fk.roleValue[player.id].rebel > 0 and intention < 0 then
fk.roleValue[player.id].renegade = fk.roleValue[player.id].renegade +
intention * (100 - fk.roleValue[player.id].renegade) / 200
end
local aps = player.room:getAlivePlayers()
local function compare_func(a, b)
local v1 = fk.roleValue[a.id].rebel
local v2 = fk.roleValue[b.id].rebel
if v1 == v2 then
v1 = fk.roleValue[a.id].renegade
v2 = fk.roleValue[b.id].renegade
end
return v1 > v2
end
table.sort(aps, compare_func)
fk.explicit_renegade = false
local ars = aliveRoles(player.room)
local rebel, renegade, loyalist = 0, 0, 0
for _, ap in ipairs(aps) do
if ap.role == "lord" then
fk.ai_role[ap.id] = "loyalist"
elseif fk.roleValue[ap.id].rebel > 50 and ars.rebel > rebel then
rebel = rebel + 1
fk.ai_role[ap.id] = "rebel"
elseif fk.roleValue[ap.id].renegade > 50 and ars.renegade > renegade then
renegade = renegade + 1
fk.ai_role[ap.id] = "renegade"
fk.explicit_renegade = fk.roleValue[ap.id].renegade > 100
elseif fk.roleValue[ap.id].rebel < -50 and ars.loyalist > loyalist then
loyalist = loyalist + 1
fk.ai_role[ap.id] = "loyalist"
else
fk.ai_role[ap.id] = "neutral"
end
end
fk.qWarning(
player.general ..
" " ..
intention ..
" " ..
fk.ai_role[player.id] ..
" rebelValue:" .. fk.roleValue[player.id].rebel .. " renegadeValue:" .. fk.roleValue[player.id].renegade
) --]]
end
end
function TrustAI:filterEvent(event, player, data)
if event == fk.TargetSpecified then
local callback = fk.ai_card[data.card.name]
callback = callback and callback.intention
if type(callback) == "function" then
for _, p in ipairs(TargetGroup:getRealTargets(data.tos)) do
p = self.room:getPlayerById(p)
local intention = callback(p.ai, data.card, self.room:getPlayerById(data.from))
if type(intention) == "number" then
updateIntention(self.room:getPlayerById(data.from), p, intention)
end
end
elseif type(callback) == "number" then
for _, p in ipairs(TargetGroup:getRealTargets(data.tos)) do
p = self.room:getPlayerById(p)
updateIntention(self.room:getPlayerById(data.from), p, callback)
end
end
elseif event == fk.StartJudge then
fk.trick_judge[data.reason] = data.pattern
elseif event == fk.AfterCardsMove then
end
end
function TrustAI:isWeak(player, getAP)
player = player or self.player
if type(player) == "number" then
player = self.room:getPlayerById(player)
end
return player.hp < 2 or player.hp <= 2 and #player:getCardIds("&h") <= 2
end
function TrustAI:isFriend(pid, tid)
if tid then
local bt = self:isFriend(pid)
return bt ~= nil and bt == self:isFriend(tid)
end
if type(pid) == "number" then
pid = self.room:getPlayerById(pid)
end
local ve = self:objectiveLevel(pid)
if ve < 0 then
return true
elseif ve > 0 then
return false
end
end
function TrustAI:isEnemie(pid, tid)
if tid then
local bt = self:isFriend(pid)
return bt ~= nil and bt ~= self:isFriend(tid)
end
if type(pid) == "number" then
pid = self.room:getPlayerById(pid)
end
local ve = self:objectiveLevel(pid)
if ve > 0 then
return true
elseif ve < 0 then
return false
end
end
function TrustAI:eventData(game_event)
local event = self.room.logic:getCurrentEvent():findParent(GameEvent[game_event], true)
return event and event.data[1]
end
for _, n in ipairs(FileIO.ls("packages")) do
if FileIO.isDir("packages/" .. n) and FileIO.exists("packages/" .. n .. "/" .. n .. "_ai.lua") then
dofile("packages/" .. n .. "/" .. n .. "_ai.lua")
end
end
-- 加载两次拓展是为了能够引用,例如属性杀的使用直接套入普通杀的使用
for _, n in ipairs(FileIO.ls("packages")) do
if FileIO.isDir("packages/" .. n) and FileIO.exists("packages/" .. n .. "/" .. n .. "_ai.lua") then
dofile("packages/" .. n .. "/" .. n .. "_ai.lua")
end
>>>>>>> 79d3213cc0aa996c9072ae5696e387a9bda210d8
end end
return TrustAI return TrustAI

View File

@ -129,3 +129,121 @@ fk.GeneralRevealed = 89
fk.GeneralHidden = 90 fk.GeneralHidden = 90
fk.NumOfEvents = 91 fk.NumOfEvents = 91
local events = {
"NonTrigger",
"GamePrepared",
"GameStart",
"BeforeTurnStart",
"TurnStart",
"TurnEnd",
"AfterTurnEnd",
"EventPhaseStart",
"EventPhaseProceeding",
"EventPhaseEnd",
"AfterPhaseEnd",
"EventPhaseChanging",
"EventPhaseSkipping",
"BeforeCardsMove",
"AfterCardsMove",
"DrawNCards",
"AfterDrawNCards",
"DrawInitialCards",
"AfterDrawInitialCards",
"PreHpRecover",
"HpRecover",
"PreHpLost",
"HpLost",
"BeforeHpChanged",
"HpChanged",
"MaxHpChanged",
"EventLoseSkill",
"EventAcquireSkill",
"StartJudge",
"AskForRetrial",
"FinishRetrial",
"FinishJudge",
"RoundStart",
"RoundEnd",
"BeforeTurnOver",
"TurnedOver",
"BeforeChainStateChange",
"ChainStateChanged",
"PreDamage",
"DamageCaused",
"DamageInflicted",
"Damage",
"Damaged",
"DamageFinished",
"EnterDying",
"Dying",
"AfterDying",
"PreCardUse",
"AfterCardUseDeclared",
"AfterCardTargetDeclared",
"CardUsing",
"BeforeCardUseEffect",
"TargetSpecifying",
"TargetConfirming",
"TargetSpecified",
"TargetConfirmed",
"CardUseFinished",
"PreCardRespond",
"CardResponding",
"CardRespondFinished",
"PreCardEffect",
"BeforeCardEffect",
"CardEffecting",
"CardEffectFinished",
"CardEffectCancelledOut",
"AskForPeaches",
"AskForPeachesDone",
"Death",
"BuryVictim",
"Deathed",
"BeforeGameOverJudge",
"GameOverJudge",
"GameFinished",
"AskForCardUse",
"AskForCardResponse",
"StartPindian",
"PindianCardsDisplayed",
"PindianResultConfirmed",
"PindianFinished",
"AfterDrawPileShuffle",
"BeforeTriggerSkillUse",
"BeforeDrawCard",
"CardShown",
"SkillEffect",
"AfterSkillEffect",
"AreaAborted",
"AreaResumed",
"GeneralRevealed",
"GeneralHidden",
"NumOfEvents"
}
for i, event in ipairs(events) do
fk[event] = i
end --]]

View File

@ -194,16 +194,15 @@ end
GameEvent.functions[GameEvent.UseCard] = function(self) GameEvent.functions[GameEvent.UseCard] = function(self)
local cardUseEvent = table.unpack(self.data) local cardUseEvent = table.unpack(self.data)
local room = self.room local room = self.room
local logic = room.logic
if cardUseEvent.card.skill then if cardUseEvent.card.skill then
cardUseEvent.card.skill:onUse(room, cardUseEvent) cardUseEvent.card.skill:onUse(room, cardUseEvent)
end end
if logic:trigger(fk.PreCardUse, room:getPlayerById(cardUseEvent.from), cardUseEvent) then if room.logic:trigger(fk.PreCardUse, room:getPlayerById(cardUseEvent.from), cardUseEvent) then
cardUseEvent.breakEvent = true cardUseEvent.breakEvent = true
self.data = { cardUseEvent } self.data = { cardUseEvent }
logic:breakEvent() room.logic:breakEvent()
end end
room:moveCardTo(cardUseEvent.card, Card.Processing, nil, fk.ReasonUse) room:moveCardTo(cardUseEvent.card, Card.Processing, nil, fk.ReasonUse)
@ -223,7 +222,7 @@ GameEvent.functions[GameEvent.UseCard] = function(self)
break break
end end
logic:trigger(event, room:getPlayerById(cardUseEvent.from), cardUseEvent) room.logic:trigger(event, room:getPlayerById(cardUseEvent.from), cardUseEvent)
if event == fk.CardUsing then if event == fk.CardUsing then
room:doCardUseEffect(cardUseEvent) room:doCardUseEffect(cardUseEvent)
end end
@ -232,13 +231,12 @@ end
GameEvent.cleaners[GameEvent.UseCard] = function(self) GameEvent.cleaners[GameEvent.UseCard] = function(self)
local cardUseEvent = table.unpack(self.data) local cardUseEvent = table.unpack(self.data)
local room = self.room
room.logic:trigger(fk.CardUseFinished, room:getPlayerById(cardUseEvent.from), cardUseEvent) self.room.logic:trigger(fk.CardUseFinished, self.room:getPlayerById(cardUseEvent.from), cardUseEvent)
local leftRealCardIds = room:getSubcardsByRule(cardUseEvent.card, { Card.Processing }) local leftRealCardIds = self.room:getSubcardsByRule(cardUseEvent.card, { Card.Processing })
if #leftRealCardIds > 0 then if #leftRealCardIds > 0 then
room:moveCards({ self.room:moveCards({
ids = leftRealCardIds, ids = leftRealCardIds,
toArea = Card.DiscardPile, toArea = Card.DiscardPile,
moveReason = fk.ReasonUse moveReason = fk.ReasonUse
@ -249,12 +247,11 @@ end
GameEvent.functions[GameEvent.RespondCard] = function(self) GameEvent.functions[GameEvent.RespondCard] = function(self)
local cardResponseEvent = table.unpack(self.data) local cardResponseEvent = table.unpack(self.data)
local room = self.room local room = self.room
local logic = room.logic
if logic:trigger(fk.PreCardRespond, room:getPlayerById(cardResponseEvent.from), cardResponseEvent) then if room.logic:trigger(fk.PreCardRespond, room:getPlayerById(cardResponseEvent.from), cardResponseEvent) then
cardResponseEvent.breakEvent = true cardResponseEvent.breakEvent = true
self.data = { cardResponseEvent } self.data = { cardResponseEvent }
logic:breakEvent() room.logic:breakEvent()
end end
local from = cardResponseEvent.customFrom or cardResponseEvent.from local from = cardResponseEvent.customFrom or cardResponseEvent.from
@ -296,19 +293,18 @@ GameEvent.functions[GameEvent.RespondCard] = function(self)
if cardResponseEvent.retrial ~= true then if cardResponseEvent.retrial ~= true then
playCardEmotionAndSound(room, room:getPlayerById(from), card) playCardEmotionAndSound(room, room:getPlayerById(from), card)
end end
logic:trigger(fk.CardResponding, room:getPlayerById(cardResponseEvent.from), cardResponseEvent) room.logic:trigger(fk.CardResponding, room:getPlayerById(cardResponseEvent.from), cardResponseEvent)
self.data = { cardResponseEvent } self.data = { cardResponseEvent }
end end
GameEvent.cleaners[GameEvent.RespondCard] = function(self) GameEvent.cleaners[GameEvent.RespondCard] = function(self)
local cardResponseEvent = table.unpack(self.data) local cardResponseEvent = table.unpack(self.data)
local room = self.room
room.logic:trigger(fk.CardRespondFinished, room:getPlayerById(cardResponseEvent.from), cardResponseEvent) self.room.logic:trigger(fk.CardRespondFinished, self.room:getPlayerById(cardResponseEvent.from), cardResponseEvent)
local realCardIds = room:getSubcardsByRule(cardResponseEvent.card, { Card.Processing }) local realCardIds = self.room:getSubcardsByRule(cardResponseEvent.card, { Card.Processing })
if #realCardIds > 0 and not cardResponseEvent.skipDrop then if #realCardIds > 0 and not cardResponseEvent.skipDrop then
room:moveCards({ self.room:moveCards({
ids = realCardIds, ids = realCardIds,
toArea = Card.DiscardPile, toArea = Card.DiscardPile,
moveReason = fk.ReasonResonpse moveReason = fk.ReasonResonpse
@ -319,34 +315,33 @@ end
GameEvent.functions[GameEvent.CardEffect] = function(self) GameEvent.functions[GameEvent.CardEffect] = function(self)
local cardEffectEvent = table.unpack(self.data) local cardEffectEvent = table.unpack(self.data)
local room = self.room local room = self.room
local logic = room.logic
for _, event in ipairs({ fk.PreCardEffect, fk.BeforeCardEffect, fk.CardEffecting, fk.CardEffectFinished }) do for _, event in ipairs({ fk.PreCardEffect, fk.BeforeCardEffect, fk.CardEffecting, fk.CardEffectFinished }) do
local user = cardEffectEvent.from and room:getPlayerById(cardEffectEvent.from) or nil local user = cardEffectEvent.from and room:getPlayerById(cardEffectEvent.from) or nil
if cardEffectEvent.isCancellOut then if cardEffectEvent.isCancellOut then
if logic:trigger(fk.CardEffectCancelledOut, user, cardEffectEvent) then if room.logic:trigger(fk.CardEffectCancelledOut, user, cardEffectEvent) then
cardEffectEvent.isCancellOut = false cardEffectEvent.isCancellOut = false
else else
logic:breakEvent() room.logic:breakEvent()
end end
end end
if not cardEffectEvent.toCard and if not cardEffectEvent.toCard and
(not (room:getPlayerById(cardEffectEvent.to):isAlive() and cardEffectEvent.to) or (not (room:getPlayerById(cardEffectEvent.to):isAlive() and cardEffectEvent.to) or
#room:deadPlayerFilter(TargetGroup:getRealTargets(cardEffectEvent.tos)) == 0) then #room:deadPlayerFilter(TargetGroup:getRealTargets(cardEffectEvent.tos)) == 0) then
logic:breakEvent() room.logic:breakEvent()
end end
if table.contains((cardEffectEvent.nullifiedTargets or Util.DummyTable), cardEffectEvent.to) then if table.contains((cardEffectEvent.nullifiedTargets or Util.DummyTable), cardEffectEvent.to) then
logic:breakEvent() room.logic:breakEvent()
end end
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 room.logic:trigger(event, room:getPlayerById(cardEffectEvent.from), cardEffectEvent) then
logic:breakEvent() room.logic:breakEvent()
end end
elseif cardEffectEvent.to and logic:trigger(event, room:getPlayerById(cardEffectEvent.to), cardEffectEvent) then elseif cardEffectEvent.to and room.logic:trigger(event, room:getPlayerById(cardEffectEvent.to), cardEffectEvent) then
logic:breakEvent() room.logic:breakEvent()
end end
room:handleCardEffect(event, cardEffectEvent) room:handleCardEffect(event, cardEffectEvent)

View File

@ -13,424 +13,423 @@
local GameLogic = class("GameLogic") 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.skill_priority_table = {}
self.refresh_skill_table = {} self.refresh_skill_table = {}
self.skills = {} -- skillName[] self.skills = {} -- skillName[]
self.game_event_stack = Stack:new() self.game_event_stack = Stack:new()
self.all_game_events = {} self.all_game_events = {}
self.event_recorder = {} self.event_recorder = {}
self.current_event_id = 0 self.current_event_id = 0
self.role_table = {{"lord"}, {"lord", "rebel"}, {"lord", "rebel", "renegade"}, self.role_table = { { "lord" }, { "lord", "rebel" }, { "lord", "rebel", "renegade" },
{"lord", "loyalist", "rebel", "renegade"}, {"lord", "loyalist", "rebel", "rebel", "renegade"}, { "lord", "loyalist", "rebel", "renegade" }, { "lord", "loyalist", "rebel", "rebel", "renegade" },
{"lord", "loyalist", "rebel", "rebel", "rebel", "renegade"}, { "lord", "loyalist", "rebel", "rebel", "rebel", "renegade" },
{"lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "renegade"}, { "lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "renegade" },
{"lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "rebel", "renegade"}} { "lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "rebel", "renegade" } }
end end
function GameLogic:run() function GameLogic:run()
-- default logic -- default logic
local room = self.room local room = self.room
table.shuffle(self.room.players) table.shuffle(self.room.players)
self:assignRoles() self:assignRoles()
self.room.game_started = true self.room.game_started = true
room:doBroadcastNotify("StartGame", "") room:doBroadcastNotify("StartGame", "")
room:adjustSeats() room:adjustSeats()
self:chooseGenerals() self:chooseGenerals()
self:buildPlayerCircle() self:buildPlayerCircle()
self:broadcastGeneral() self:broadcastGeneral()
self:prepareDrawPile() self:prepareDrawPile()
self:attachSkillToPlayers() self:attachSkillToPlayers()
self:prepareForStart() self:prepareForStart()
self:action() self:action()
end end
local function execGameEvent(type, ...) local function execGameEvent(type, ...)
local event = GameEvent:new(type, ...) local event = GameEvent:new(type, ...)
local _, ret = event:exec() local _, ret = event:exec()
return ret return ret
end end
function GameLogic:assignRoles() function GameLogic:assignRoles()
local room = self.room local room = self.room
local n = #room.players local n = #room.players
local roles = self.role_table[n] local roles = self.role_table[n]
table.shuffle(roles) table.shuffle(roles)
for i = 1, n do for i = 1, n do
local p = room.players[i] local p = room.players[i]
p.role = roles[i] p.role = roles[i]
if p.role == "lord" then if p.role == "lord" then
p.role_shown = true p.role_shown = true
room:broadcastProperty(p, "role") room:broadcastProperty(p, "role")
else else
room:notifyProperty(p, p, "role") room:notifyProperty(p, p, "role")
end
end end
end
end end
function GameLogic:chooseGenerals() function GameLogic:chooseGenerals()
local room = self.room local room = self.room
local generalNum = room.settings.generalNum local generalNum = room.settings.generalNum
local n = room.settings.enableDeputy and 2 or 1 local n = room.settings.enableDeputy and 2 or 1
local lord = room:getLord() local lord = room:getLord()
local lord_generals = {} local lord_generals = {}
if lord ~= nil then if lord ~= nil then
room.current = lord room.current = lord
local generals = {} local generals = {}
local lordlist = {} local lordlist = {}
local lordpools = {} local lordpools = {}
if room.settings.gameMode == "aaa_role_mode" then if room.settings.gameMode == "aaa_role_mode" then
for _, general in pairs(Fk:getAllGenerals()) do for _, general in pairs(Fk:getAllGenerals()) do
if (not general.hidden and not general.total_hidden) and table.find(general.skills, function(s) if (not general.hidden and not general.total_hidden) and table.find(general.skills, function(s)
return s.lordSkill return s.lordSkill
end) and not table.find(lordlist, function(g) end) and not table.find(lordlist, function(g)
return g.trueName == general.trueName return g.trueName == general.trueName
end) then end) then
table.insert(lordlist, general) table.insert(lordlist, general)
end
end
lordlist = table.random(lordlist, 3) or {}
end end
table.insertTable(generals, Fk:getGeneralsRandomly(generalNum, Fk:getAllGenerals(), nil, function(g) end
return table.contains(table.map(lordlist, function(g) lordlist = table.random(lordlist, 3) or {}
return g.trueName end
end), g.trueName) table.insertTable(generals, Fk:getGeneralsRandomly(generalNum, Fk:getAllGenerals(), nil, function(g)
end)) return table.contains(table.map(lordlist, function(g)
for i = 1, #generals do return g.trueName
generals[i] = generals[i].name end), g.trueName)
end end))
lordpools = table.simpleClone(generals) for i = 1, #generals do
table.insertTable(lordpools, table.map(lordlist, function(g) generals[i] = generals[i].name
return g.name end
end)) lordpools = table.simpleClone(generals)
lord_generals = room:askForGeneral(lord, lordpools, n) table.insertTable(lordpools, table.map(lordlist, function(g)
local lord_general, deputy return g.name
if type(lord_generals) == "table" then end))
deputy = lord_generals[2] lord_generals = room:askForGeneral(lord, lordpools, n)
lord_general = lord_generals[1] local lord_general, deputy
else if type(lord_generals) == "table" then
lord_general = lord_generals deputy = lord_generals[2]
lord_generals = {lord_general} lord_general = lord_generals[1]
end else
lord_general = lord_generals
room:setPlayerGeneral(lord, lord_general, true) lord_generals = { lord_general }
room:askForChooseKingdom({lord})
room:broadcastProperty(lord, "general")
room:broadcastProperty(lord, "kingdom")
room:setDeputyGeneral(lord, deputy)
room:broadcastProperty(lord, "deputyGeneral")
end end
local nonlord = room:getOtherPlayers(lord, true) room:setPlayerGeneral(lord, lord_general, true)
local generals = Fk:getGeneralsRandomly(#nonlord * generalNum, nil, lord_generals) room:askForChooseKingdom({ lord })
table.shuffle(generals) room:broadcastProperty(lord, "general")
for _, p in ipairs(nonlord) do room:broadcastProperty(lord, "kingdom")
local arg = {} room:setDeputyGeneral(lord, deputy)
for i = 1, generalNum do room:broadcastProperty(lord, "deputyGeneral")
table.insert(arg, table.remove(generals, 1).name) end
end
p.request_data = json.encode {arg, n} local nonlord = room:getOtherPlayers(lord, true)
p.default_reply = table.random(arg, n) local generals = Fk:getGeneralsRandomly(#nonlord * generalNum, nil, lord_generals)
table.shuffle(generals)
for _, p in ipairs(nonlord) do
local arg = {}
for i = 1, generalNum do
table.insert(arg, table.remove(generals, 1).name)
end end
p.request_data = json.encode { arg, n }
p.default_reply = table.random(arg, n)
end
room:notifyMoveFocus(nonlord, "AskForGeneral") room:notifyMoveFocus(nonlord, "AskForGeneral")
room:doBroadcastRequest("AskForGeneral", nonlord) room:doBroadcastRequest("AskForGeneral", nonlord)
for _, p in ipairs(nonlord) do for _, p in ipairs(nonlord) do
if p.general == "" and p.reply_ready then if p.general == "" and p.reply_ready then
local generals = json.decode(p.client_reply) local generals = json.decode(p.client_reply)
local general = generals[1] local general = generals[1]
local deputy = generals[2] local deputy = generals[2]
room:setPlayerGeneral(p, general, true, true) room:setPlayerGeneral(p, general, true, true)
room:setDeputyGeneral(p, deputy) room:setDeputyGeneral(p, deputy)
else else
room:setPlayerGeneral(p, p.default_reply[1], true, true) room:setPlayerGeneral(p, p.default_reply[1], true, true)
room:setDeputyGeneral(p, p.default_reply[2]) room:setDeputyGeneral(p, p.default_reply[2])
end
p.default_reply = ""
end end
p.default_reply = ""
end
room:askForChooseKingdom(nonlord) room:askForChooseKingdom(nonlord)
end end
function GameLogic:buildPlayerCircle() function GameLogic:buildPlayerCircle()
local room = self.room local room = self.room
local players = room.players local players = room.players
room.alive_players = {table.unpack(players)} room.alive_players = { table.unpack(players) }
for i = 1, #players - 1 do for i = 1, #players - 1 do
players[i].next = players[i + 1] players[i].next = players[i + 1]
end end
players[#players].next = players[1] players[#players].next = players[1]
end end
function GameLogic:broadcastGeneral() function GameLogic:broadcastGeneral()
local room = self.room local room = self.room
local players = room.players local players = room.players
for _, p in ipairs(players) do for _, p in ipairs(players) do
assert(p.general ~= "") assert(p.general ~= "")
local general = Fk.generals[p.general] local general = Fk.generals[p.general]
local deputy = Fk.generals[p.deputyGeneral] local deputy = Fk.generals[p.deputyGeneral]
p.maxHp = p:getGeneralMaxHp() p.maxHp = p:getGeneralMaxHp()
p.hp = deputy and math.floor((deputy.hp + general.hp) / 2) or general.hp p.hp = deputy and math.floor((deputy.hp + general.hp) / 2) or general.hp
p.shield = math.min(general.shield + (deputy and deputy.shield or 0), 5) p.shield = math.min(general.shield + (deputy and deputy.shield or 0), 5)
-- TODO: setup AI here -- TODO: setup AI here
if p.role ~= "lord" then if p.role ~= "lord" then
room:broadcastProperty(p, "general") room:broadcastProperty(p, "general")
room:broadcastProperty(p, "kingdom") room:broadcastProperty(p, "kingdom")
room:broadcastProperty(p, "deputyGeneral") room:broadcastProperty(p, "deputyGeneral")
elseif #players >= 5 then elseif #players >= 5 then
p.maxHp = p.maxHp + 1 p.maxHp = p.maxHp + 1
p.hp = p.hp + 1 p.hp = p.hp + 1
end
room:broadcastProperty(p, "maxHp")
room:broadcastProperty(p, "hp")
room:broadcastProperty(p, "shield")
end end
room:broadcastProperty(p, "maxHp")
room:broadcastProperty(p, "hp")
room:broadcastProperty(p, "shield")
end
end end
function GameLogic:prepareDrawPile() function GameLogic:prepareDrawPile()
local room = self.room local room = self.room
local allCardIds = Fk:getAllCardIds() local allCardIds = Fk:getAllCardIds()
for i = #allCardIds, 1, -1 do for i = #allCardIds, 1, -1 do
if Fk:getCardById(allCardIds[i]).is_derived then if Fk:getCardById(allCardIds[i]).is_derived then
local id = allCardIds[i] local id = allCardIds[i]
table.removeOne(allCardIds, id) table.removeOne(allCardIds, id)
table.insert(room.void, id) table.insert(room.void, id)
room:setCardArea(id, Card.Void, nil) room:setCardArea(id, Card.Void, nil)
end
end end
end
table.shuffle(allCardIds) table.shuffle(allCardIds)
room.draw_pile = allCardIds room.draw_pile = allCardIds
for _, id in ipairs(room.draw_pile) do for _, id in ipairs(room.draw_pile) do
self.room:setCardArea(id, Card.DrawPile, nil) self.room:setCardArea(id, Card.DrawPile, nil)
end end
end end
function GameLogic:attachSkillToPlayers() function GameLogic:attachSkillToPlayers()
local room = self.room local room = self.room
local players = room.players local players = room.players
local addRoleModSkills = function(player, skillName) local addRoleModSkills = function(player, skillName)
local skill = Fk.skills[skillName] local skill = Fk.skills[skillName]
if skill.lordSkill and (player.role ~= "lord" or #room.players < 5) then if skill.lordSkill and (player.role ~= "lord" or #room.players < 5) then
return return
end
if #skill.attachedKingdom > 0 and not table.contains(skill.attachedKingdom, player.kingdom) then
return
end
room:handleAddLoseSkills(player, skillName, nil, false)
end end
for _, p in ipairs(room.alive_players) do
local skills = Fk.generals[p.general].skills
for _, s in ipairs(skills) do
addRoleModSkills(p, s.name)
end
for _, sname in ipairs(Fk.generals[p.general].other_skills) do
addRoleModSkills(p, sname)
end
local deputy = Fk.generals[p.deputyGeneral] if #skill.attachedKingdom > 0 and not table.contains(skill.attachedKingdom, player.kingdom) then
if deputy then return
skills = deputy.skills
for _, s in ipairs(skills) do
addRoleModSkills(p, s.name)
end
for _, sname in ipairs(deputy.other_skills) do
addRoleModSkills(p, sname)
end
end
end end
room:handleAddLoseSkills(player, skillName, nil, false)
end
for _, p in ipairs(room.alive_players) do
local skills = Fk.generals[p.general].skills
for _, s in ipairs(skills) do
addRoleModSkills(p, s.name)
end
for _, sname in ipairs(Fk.generals[p.general].other_skills) do
addRoleModSkills(p, sname)
end
local deputy = Fk.generals[p.deputyGeneral]
if deputy then
skills = deputy.skills
for _, s in ipairs(skills) do
addRoleModSkills(p, s.name)
end
for _, sname in ipairs(deputy.other_skills) do
addRoleModSkills(p, sname)
end
end
end
end end
function GameLogic:prepareForStart() function GameLogic:prepareForStart()
local room = self.room local room = self.room
local players = room.players local players = room.players
self:addTriggerSkill(GameRule) self:addTriggerSkill(GameRule)
for _, trig in ipairs(Fk.global_trigger) do for _, trig in ipairs(Fk.global_trigger) do
self:addTriggerSkill(trig) self:addTriggerSkill(trig)
end end
self.room:sendLog{ self.room:sendLog {
type = "$GameStart" type = "$GameStart"
} }
end end
function GameLogic:action() function GameLogic:action()
self:trigger(fk.GamePrepared) self:trigger(fk.GamePrepared)
local room = self.room local room = self.room
execGameEvent(GameEvent.DrawInitial) execGameEvent(GameEvent.DrawInitial)
while true do while true do
execGameEvent(GameEvent.Round) execGameEvent(GameEvent.Round)
if room.game_finished then if room.game_finished then
break break
end
end end
end
end end
---@param skill TriggerSkill ---@param skill TriggerSkill
function GameLogic:addTriggerSkill(skill) function GameLogic:addTriggerSkill(skill)
if skill == nil or table.contains(self.skills, skill.name) then if skill == nil or table.contains(self.skills, skill.name) then
return return
end
table.insert(self.skills, skill.name)
for _, event in ipairs(skill.refresh_events) do
if self.refresh_skill_table[event] == nil then
self.refresh_skill_table[event] = {}
end
table.insert(self.refresh_skill_table[event], skill)
end
for _, event in ipairs(skill.events) do
if self.skill_table[event] == nil then
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 end
table.insert(self.skills, skill.name) local priority_tab = self.skill_priority_table[event]
local prio = skill.priority_table[event]
for _, event in ipairs(skill.refresh_events) do if not table.contains(priority_tab, prio) then
if self.refresh_skill_table[event] == nil then for i, v in ipairs(priority_tab) do
self.refresh_skill_table[event] = {} if v < prio then
table.insert(priority_tab, i, prio)
break
end end
table.insert(self.refresh_skill_table[event], skill) end
if not table.contains(priority_tab, prio) then
table.insert(priority_tab, prio)
end
end end
for _, event in ipairs(skill.events) do if not table.contains(self.skill_priority_table[event], skill.priority_table[event]) then
if self.skill_table[event] == nil then table.insert(self.skill_priority_table[event], skill.priority_table[event])
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 end
end
if skill.visible then if skill.visible then
if (Fk.related_skills[skill.name] == nil) then if (Fk.related_skills[skill.name] == nil) then
return return
end
for _, s in ipairs(Fk.related_skills[skill.name]) do
if (s.class == TriggerSkill) then
self:addTriggerSkill(s)
end
end
end end
for _, s in ipairs(Fk.related_skills[skill.name]) do
if (s.class == TriggerSkill) then
self:addTriggerSkill(s)
end
end
end
end end
---@param event Event ---@param event Event
---@param target ServerPlayer|nil ---@param target ServerPlayer|nil
---@param data any|nil ---@param data any|nil
function GameLogic:trigger(event, target, data, refresh_only) function GameLogic:trigger(event, target, data, refresh_only)
local room = self.room local room = self.room
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 Util.DummyTable local skills_to_refresh = self.refresh_skill_table[event] or Util.DummyTable
local _target = room.current -- for iteration local _target = room.current -- for iteration
local player = _target local player = _target
if #skills_to_refresh > 0 then if #skills_to_refresh > 0 then
repeat repeat
do 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
skill:refresh(event, target, player, data) skill:refresh(event, target, player, data)
end end
end end
player = player.next player = player.next
end end
until player == _target until player == _target
end
if #skills == 0 or refresh_only then
return
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 end
if #skills == 0 or refresh_only then repeat
return do
end local triggerables = table.filter(skills, function(skill)
return skill.priority_table[event] == prio and skill:triggerable(event, target, player, data)
end)
local prio_tab = self.skill_priority_table[event] local skill_names = table.map(triggerables, function(skill)
local prev_prio = math.huge return skill.name
end)
for _, prio in ipairs(prio_tab) do while #skill_names > 0 do
if broken then 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)
table.insertTable(skill_names,
table.map(table.filter(table.slice(skills, len - #skills), function(s)
return s.priority_table[event] == prio and s:triggerable(event, target, player, data)
end), function(s)
return s.name
end))
broken = broken or (event == fk.AskForPeaches and room:getPlayerById(data.who).hp > 0)
if broken then
break break
end
table.removeOne(skill_names, skill_name)
end end
if prio >= prev_prio then if broken then
-- continue break
goto trigger_loop_continue
end end
player = player.next
end
until player == _target
repeat prev_prio = prio
do ::trigger_loop_continue::
local triggerables = table.filter(skills, function(skill) end
return skill.priority_table[event] == prio and skill:triggerable(event, target, player, data) _target.ai:filterEvent(event, target, data)
end) return broken
local skill_names = table.map(triggerables, function(skill)
return skill.name
end)
while #skill_names > 0 do
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)
table.insertTable(skill_names,
table.map(table.filter(table.slice(skills, len - #skills), function(s)
return s.priority_table[event] == prio and s:triggerable(event, target, player, data)
end), 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)
end
if broken then
break
end
player = player.next
end
until player == _target
prev_prio = prio
::trigger_loop_continue::
end
_target.ai:filterEvent(event, target, data)
return broken
end end
---@return GameEvent ---@return GameEvent
function GameLogic:getCurrentEvent() function GameLogic:getCurrentEvent()
return self.game_event_stack.t[self.game_event_stack.p] return self.game_event_stack.t[self.game_event_stack.p]
end end
-- 在指定历史范围中找至多n个符合条件的事件 -- 在指定历史范围中找至多n个符合条件的事件
@ -440,90 +439,89 @@ end
---@param scope integer @ 查询历史范围,只能是当前阶段/回合/轮次 ---@param scope integer @ 查询历史范围,只能是当前阶段/回合/轮次
---@return GameEvent[] @ 找到的符合条件的所有事件最多n个但不保证有n个 ---@return GameEvent[] @ 找到的符合条件的所有事件最多n个但不保证有n个
function GameLogic:getEventsOfScope(eventType, n, func, scope) function GameLogic:getEventsOfScope(eventType, n, func, scope)
scope = scope or Player.HistoryTurn scope = scope or Player.HistoryTurn
local event = self:getCurrentEvent() local event = self:getCurrentEvent()
local start_event ---@type GameEvent local start_event ---@type GameEvent
if scope == Player.HistoryGame then if scope == Player.HistoryGame then
start_event = self.all_game_events[1] start_event = self.all_game_events[1]
elseif scope == Player.HistoryRound then elseif scope == Player.HistoryRound then
start_event = event:findParent(GameEvent.Round, true) start_event = event:findParent(GameEvent.Round, true)
elseif scope == Player.HistoryTurn then elseif scope == Player.HistoryTurn then
start_event = event:findParent(GameEvent.Turn, true) start_event = event:findParent(GameEvent.Turn, true)
elseif scope == Player.HistoryPhase then elseif scope == Player.HistoryPhase then
start_event = event:findParent(GameEvent.Phase, true) start_event = event:findParent(GameEvent.Phase, true)
end end
return start_event:searchEvents(eventType, n, func) return start_event:searchEvents(eventType, n, func)
end end
function GameLogic:dumpEventStack(detailed) function GameLogic:dumpEventStack(detailed)
local top = self:getCurrentEvent() local top = self:getCurrentEvent()
local i = self.game_event_stack.p local i = self.game_event_stack.p
local inspect = p local inspect = p
if not top then if not top then
return return
end
print("===== Start of event stack dump =====")
if not detailed then
print("")
end
repeat
local printable_data
if type(top.data) ~= "table" then
printable_data = top.data
else
printable_data = table.cloneWithoutClass(top.data)
end end
print("===== Start of event stack dump =====")
if not detailed then if not detailed then
print("") print("Stack level #" .. i .. ": " .. tostring(top))
else
print("\nStack level #" .. i .. ":")
inspect {
eventId = GameEvent:translate(top.event),
data = printable_data or "nil"
}
end end
repeat top = top.parent
local printable_data i = i - 1
if type(top.data) ~= "table" then until not top
printable_data = top.data
else
printable_data = table.cloneWithoutClass(top.data)
end
if not detailed then print("\n===== End of event stack dump =====")
print("Stack level #" .. i .. ": " .. tostring(top))
else
print("\nStack level #" .. i .. ":")
inspect {
eventId = GameEvent:translate(top.event),
data = printable_data or "nil"
}
end
top = top.parent
i = i - 1
until not top
print("\n===== End of event stack dump =====")
end end
function GameLogic:dumpAllEvents(from, to) function GameLogic:dumpAllEvents(from, to)
from = from or 1 from = from or 1
to = to or #self.all_game_events to = to or #self.all_game_events
assert(from <= to) assert(from <= to)
local indent = 0
local indent = 0 local tab = " "
local tab = " " for i = from, to, 1 do
for i = from, to, 1 do local v = self.all_game_events[i]
local v = self.all_game_events[i] if type(v) ~= "table" then
if type(v) ~= "table" then indent = math.max(indent - 1, 0)
indent = math.max(indent - 1, 0) -- v = "End"
-- v = "End" -- print(tab:rep(indent) .. string.format("#%d: %s", i, v))
-- print(tab:rep(indent) .. string.format("#%d: %s", i, v)) else
else print(tab:rep(indent) .. string.format("%s", tostring(v)))
print(tab:rep(indent) .. string.format("%s", tostring(v))) if v.id ~= v.end_id then
if v.id ~= v.end_id then indent = indent + 1
indent = indent + 1 end
end
end
end end
end
end end
function GameLogic:breakEvent(ret) function GameLogic:breakEvent(ret)
self.room.breakEvent = true self.room.breakEvent = true
coroutine.yield("__breakEvent", ret) coroutine.yield("__breakEvent", ret)
end end
function GameLogic:breakTurn() function GameLogic:breakTurn()
local event = self:getCurrentEvent():findParent(GameEvent.Turn) local event = self:getCurrentEvent():findParent(GameEvent.Turn)
event:shutdown() event:shutdown()
end end
return GameLogic return GameLogic

View File

@ -3,7 +3,7 @@
dir=FreeKill-${FK_VER} dir=FreeKill-${FK_VER}
mkdir $dir mkdir $dir
echo Copying echo Copying
cp -r ./Freekill/.git $dir cp -r ./FreeKill/.git $dir
cd $dir cd $dir
git restore . git restore .

View File

@ -8,10 +8,10 @@
给FreeKill的GitHub仓库点一个star吧 给FreeKill的GitHub仓库点一个star吧
你可以在武将一览界面禁将,在你创建的房间中禁用武将不会出现在选将框。 你可以在武将一览界面禁将,在你创建的房间中禁用武将不会出现在选将框。
如果你遇到了bug请截图并反馈给开发者 如果你遇到了bug请截图并反馈给开发者
如果你想要制作FK的mod需要先学习Lua语言 如果你想要制作新月杀的mod需要先学习Lua语言
目光所及短寸吾才之间满腹狭目之阿瞒有我见袁本良计初只能窥取冀州便是底竟不从易成略在胸如反掌之良计速出吾才满目光所及腹袁本初竟短寸之间不从之 目光所及短寸吾才之间满腹狭目之阿瞒有我见袁本良计初只能窥取冀州便是底竟不从易成略在胸如反掌之良计速出吾才满目光所及腹袁本初竟短寸之间不从之
吔! 吔!
想要参与到FK的开发工作中那么就从制作自己的Mod开始吧 想要参与到新月杀的开发工作中那么就从制作自己的Mod开始吧
在游戏的一些地方,长按可以显示该元素的详情。如果你用的是电脑版的话可以用鼠标右键代替长按。 在游戏的一些地方,长按可以显示该元素的详情。如果你用的是电脑版的话可以用鼠标右键代替长按。
注意:官方不支持任何形式的账号交易行为,请妥善保管好您的账号密码。请勿与他人共享账号。因账号交易、共享引起的账号争议官方均不受理。 注意:官方不支持任何形式的账号交易行为,请妥善保管好您的账号密码。请勿与他人共享账号。因账号交易、共享引起的账号争议官方均不受理。
如果牌不好的话就请辱骂GK的发牌员吧 如果牌不好的话就请辱骂GK的发牌员吧
@ -19,8 +19,9 @@
神曰“MBKS” 神曰“MBKS”
正在匹配势均力敌的对手…… 匹配到界徐盛 正在匹配势均力敌的对手…… 匹配到界徐盛
先喝酒还是先上刀,这是个值得考虑的问题 先喝酒还是先上刀,这是个值得考虑的问题
在抱怨FK只有标准包?那么去试着参与联机吧 在抱怨新月杀只有标准包?那么去试着参与联机吧
如果在牌局内想查看技能,可以长按想要看技能的角色 如果在牌局内想查看技能,可以长按想要看技能的角色
FK的开服很简单,只要单机启动就可以当做服务器使用了 新月杀的开服很简单,只要单机启动就可以当做服务器使用了
用Linux也能搭建FK服务器起始页推荐的联机IP就是跑在Linux服务器上的 用Linux也能搭建新月杀服务器起始页推荐的联机IP就是跑在Linux服务器上的
我们的游戏正在蒸蒸日上哦~ 我们的游戏正在蒸蒸日上哦~
请素质游戏!不要做出烧条之类的举动哦~