diff --git a/lua/client/client_util.lua b/lua/client/client_util.lua index 52591d31..3c6795e3 100644 --- a/lua/client/client_util.lua +++ b/lua/client/client_util.lua @@ -1,702 +1,697 @@ -- SPDX-License-Identifier: GPL-3.0-or-later -- All functions in this file are used by Qml function Translate(src) - return Fk:translate(src) + return Fk:translate(src) end function GetGeneralData(name) - local general = Fk.generals[name] - if general == nil then - general = Fk.generals["diaochan"] - end - return json.encode { - package = general.package.name, - extension = general.package.extensionName, - kingdom = general.kingdom, - subkingdom = general.subkingdom, - hp = general.hp, - maxHp = general.maxHp, - shield = general.shield, - hidden = general.hidden, - total_hidden = general.total_hidden - } + local general = Fk.generals[name] + if general == nil then + general = Fk.generals["diaochan"] + end + return json.encode { + package = general.package.name, + extension = general.package.extensionName, + kingdom = general.kingdom, + subkingdom = general.subkingdom, + hp = general.hp, + maxHp = general.maxHp, + shield = general.shield, + hidden = general.hidden, + total_hidden = general.total_hidden + } end function GetGeneralDetail(name) - local general = Fk.generals[name] - if general == nil then - general = Fk.generals["diaochan"] + local general = Fk.generals[name] + if general == nil then + general = Fk.generals["diaochan"] + end + local ret = { + package = general.package.name, + extension = general.package.extensionName, + kingdom = general.kingdom, + hp = general.hp, + maxHp = general.maxHp, + gender = general.gender, + skill = {}, + related_skill = {}, + companions = general.companions + } + for _, s in ipairs(general.skills) do + table.insert(ret.skill, { + name = s.name, + description = Fk:getDescription(s.name) + }) + end + for _, s in ipairs(general.other_skills) do + table.insert(ret.skill, { + name = s, + description = Fk:getDescription(s) + }) + end + for _, s in ipairs(general.related_skills) do + table.insert(ret.related_skill, { + name = s.name, + description = Fk:getDescription(s.name) + }) + end + for _, s in ipairs(general.related_other_skills) do + table.insert(ret.related_skill, { + name = s, + description = Fk:getDescription(s) + }) + end + for _, g in pairs(Fk.generals) do + if table.contains(g.companions, general.name) then + table.insertIfNeed(ret.companions, g.name) end - local ret = { - package = general.package.name, - extension = general.package.extensionName, - kingdom = general.kingdom, - hp = general.hp, - maxHp = general.maxHp, - gender = general.gender, - skill = {}, - related_skill = {}, - companions = general.companions - } - for _, s in ipairs(general.skills) do - table.insert(ret.skill, { - name = s.name, - description = Fk:getDescription(s.name) - }) - end - for _, s in ipairs(general.other_skills) do - table.insert(ret.skill, { - name = s, - description = Fk:getDescription(s) - }) - end - for _, s in ipairs(general.related_skills) do - table.insert(ret.related_skill, { - name = s.name, - description = Fk:getDescription(s.name) - }) - end - for _, s in ipairs(general.related_other_skills) do - table.insert(ret.related_skill, { - name = s, - description = Fk:getDescription(s) - }) - end - for _, g in pairs(Fk.generals) do - if table.contains(g.companions, general.name) then - table.insertIfNeed(ret.companions, g.name) - end - end - return json.encode(ret) + end + return json.encode(ret) end function GetSameGenerals(name) - return json.encode(Fk:getSameGenerals(name)) + return json.encode(Fk:getSameGenerals(name)) end local cardSubtypeStrings = { - [Card.SubtypeNone] = "none", - [Card.SubtypeDelayedTrick] = "delayed_trick", - [Card.SubtypeWeapon] = "weapon", - [Card.SubtypeArmor] = "armor", - [Card.SubtypeDefensiveRide] = "defensive_horse", - [Card.SubtypeOffensiveRide] = "offensive_horse", - [Card.SubtypeTreasure] = "treasure" + [Card.SubtypeNone] = "none", + [Card.SubtypeDelayedTrick] = "delayed_trick", + [Card.SubtypeWeapon] = "weapon", + [Card.SubtypeArmor] = "armor", + [Card.SubtypeDefensiveRide] = "defensive_horse", + [Card.SubtypeOffensiveRide] = "offensive_horse", + [Card.SubtypeTreasure] = "treasure" } function GetCardData(id, virtualCardForm) - local card = Fk:getCardById(id) - if card == nil then - return json.encode { - cid = id, - known = false - } - end - local mark = {} - for k, v in pairs(card.mark) do - if k and k:startsWith("@") and v and v ~= 0 then - table.insert(mark, { - k = k, - v = v - }) - end - end - local ret = { - cid = id, - name = card.name, - extension = card.package.extensionName, - number = card.number, - suit = card:getSuitString(), - color = card:getColorString(), - mark = mark, - type = card.type, - subtype = cardSubtypeStrings[card.sub_type] + local card = Fk:getCardById(id) + if card == nil then + return json.encode { + cid = id, + known = false } - if card.skillName ~= "" then - local orig = Fk:getCardById(id, true) - ret.name = orig.name - ret.virt_name = card.name + end + local mark = {} + for k, v in pairs(card.mark) do + if k and k:startsWith("@") and v and v ~= 0 then + table.insert(mark, { + k = k, + v = v + }) end - if virtualCardForm then - local virtualCard = ClientInstance:getPlayerById(virtualCardForm):getVirualEquip(id) - if virtualCard then - ret.virt_name = virtualCard.name - ret.subtype = cardSubtypeStrings[virtualCard.sub_type] - end + end + local ret = { + cid = id, + name = card.name, + extension = card.package.extensionName, + number = card.number, + suit = card:getSuitString(), + color = card:getColorString(), + mark = mark, + type = card.type, + subtype = cardSubtypeStrings[card.sub_type] + } + if card.skillName ~= "" then + local orig = Fk:getCardById(id, true) + ret.name = orig.name + ret.virt_name = card.name + end + if virtualCardForm then + local virtualCard = ClientInstance:getPlayerById(virtualCardForm):getVirualEquip(id) + if virtualCard then + ret.virt_name = virtualCard.name + ret.subtype = cardSubtypeStrings[virtualCard.sub_type] end - return json.encode(ret) + end + return json.encode(ret) end function GetCardExtensionByName(cardName) - local card = table.find(Fk.cards, function(card) - return card.name == cardName - end) + local card = table.find(Fk.cards, function(card) + return card.name == cardName + end) - return card and card.package.extensionName or "" + return card and card.package.extensionName or "" end function GetAllGeneralPack() - local ret = {} - for _, name in ipairs(Fk.package_names) do - if Fk.packages[name].type == Package.GeneralPack then - table.insert(ret, name) - end + local ret = {} + for _, name in ipairs(Fk.package_names) do + if Fk.packages[name].type == Package.GeneralPack then + table.insert(ret, name) end - return json.encode(ret) + end + return json.encode(ret) end function GetGenerals(pack_name) - local ret = {} - for _, g in ipairs(Fk.packages[pack_name].generals) do - if not g.total_hidden then - table.insert(ret, g.name) - end + local ret = {} + for _, g in ipairs(Fk.packages[pack_name].generals) do + if not g.total_hidden then + table.insert(ret, g.name) end - return json.encode(ret) + end + return json.encode(ret) end function SearchAllGenerals(word) - local ret = {} - for _, name in ipairs(Fk.package_names) do - if Fk.packages[name].type == Package.GeneralPack then - table.insertTable(ret, json.decode(SearchGenerals(name, word))) - end + local ret = {} + for _, name in ipairs(Fk.package_names) do + if Fk.packages[name].type == Package.GeneralPack then + table.insertTable(ret, json.decode(SearchGenerals(name, word))) end - return json.encode(ret) + end + return json.encode(ret) end function SearchGenerals(pack_name, word) - local ret = {} - if word == "" then - return GetGenerals(pack_name) + local ret = {} + if word == "" then + return GetGenerals(pack_name) + end + for _, g in ipairs(Fk.packages[pack_name].generals) do + if not g.total_hidden and string.find(Fk:translate(g.name), word) then + table.insert(ret, g.name) end - for _, g in ipairs(Fk.packages[pack_name].generals) do - if not g.total_hidden and string.find(Fk:translate(g.name), word) then - table.insert(ret, g.name) - end - end - return json.encode(ret) + end + return json.encode(ret) end function UpdatePackageEnable(pkg, enabled) - if enabled then - table.removeOne(ClientInstance.disabled_packs, pkg) - else - table.insertIfNeed(ClientInstance.disabled_packs, pkg) - end + if enabled then + table.removeOne(ClientInstance.disabled_packs, pkg) + else + table.insertIfNeed(ClientInstance.disabled_packs, pkg) + end end function GetAvailableGeneralsNum() - local generalPool = Fk:getAllGenerals() - local except = {} - local ret = 0 - for _, g in ipairs(Fk.packages["test_p_0"].generals) do - table.insert(except, g.name) - end + local generalPool = Fk:getAllGenerals() + local except = {} + local ret = 0 + for _, g in ipairs(Fk.packages["test_p_0"].generals) do + table.insert(except, g.name) + end - local availableGenerals = {} - for _, general in pairs(generalPool) do - if not table.contains(except, general.name) then - if (not general.hidden and not general.total_hidden) and #table.filter(availableGenerals, function(g) - return g.trueName == general.trueName - end) == 0 then - ret = ret + 1 - end - end + local availableGenerals = {} + for _, general in pairs(generalPool) do + if not table.contains(except, general.name) then + if (not general.hidden and not general.total_hidden) and #table.filter(availableGenerals, function(g) + return g.trueName == general.trueName + end) == 0 then + ret = ret + 1 + end end + end - return ret + return ret end function GetAllCardPack() - local ret = {} - for _, name in ipairs(Fk.package_names) do - if Fk.packages[name].type == Package.CardPack then - table.insert(ret, name) - end + local ret = {} + for _, name in ipairs(Fk.package_names) do + if Fk.packages[name].type == Package.CardPack then + table.insert(ret, name) end - return json.encode(ret) + end + return json.encode(ret) end function GetCards(pack_name) - local ret = {} - for _, c in ipairs(Fk.packages[pack_name].cards) do - table.insert(ret, c.id) - end - return json.encode(ret) + local ret = {} + for _, c in ipairs(Fk.packages[pack_name].cards) do + table.insert(ret, c.id) + end + return json.encode(ret) end function GetCardSpecialSkills(cid) - return json.encode(Fk:getCardById(cid).special_skills or Util.DummyTable) + return json.encode(Fk:getCardById(cid).special_skills or Util.DummyTable) end function DistanceTo(from, to) - local a = ClientInstance:getPlayerById(from) - local b = ClientInstance:getPlayerById(to) - return a:distanceTo(b) + local a = ClientInstance:getPlayerById(from) + local b = ClientInstance:getPlayerById(to) + return a:distanceTo(b) end function GetPile(id, name) - return json.encode(ClientInstance:getPlayerById(id):getPile(name) or {}) + return json.encode(ClientInstance:getPlayerById(id):getPile(name) or {}) end function GetAllPiles(id) - return json.encode(ClientInstance:getPlayerById(id).special_cards or Util.DummyTable) + return json.encode(ClientInstance:getPlayerById(id).special_cards or Util.DummyTable) end function GetPlayerSkills(id) - local p = ClientInstance:getPlayerById(id) - return json.encode(table.map(p.player_skills, function(s) - return s.visible and { - name = s.name, - description = Fk:getDescription(s.name) - } or nil - end)) + local p = ClientInstance:getPlayerById(id) + return json.encode(table.map(p.player_skills, function(s) + return s.visible and { + name = s.name, + description = Fk:getDescription(s.name) + } or nil + end)) end ---@param card string | integer ---@param player integer function CanUseCard(card, player) - local c ---@type Card - if type(card) == "number" then - c = Fk:getCardById(card) + local c ---@type Card + if type(card) == "number" then + c = Fk:getCardById(card) + else + local data = json.decode(card) + local skill = Fk.skills[data.skill] + local selected_cards = data.subcards + if skill:isInstanceOf(ViewAsSkill) then + c = skill:viewAs(selected_cards) + if not c then + return "false" + end else - local data = json.decode(card) - local skill = Fk.skills[data.skill] - local selected_cards = data.subcards - if skill:isInstanceOf(ViewAsSkill) then - c = skill:viewAs(selected_cards) - if not c then - return "false" - end - else - -- ActiveSkill should return true here - return "true" - end + -- ActiveSkill should return true here + return "true" end + end - player = ClientInstance:getPlayerById(player) - local ret = c.skill:canUse(player, c) - ret = ret and not player:prohibitUse(c) - if ret then - local min_target = c.skill:getMinTargetNum() - if min_target > 0 then - for _, p in ipairs(ClientInstance.players) do - if c.skill:targetFilter(p.id, {}, {}, c) then - return "true" - end - end - return "false" + player = ClientInstance:getPlayerById(player) + local ret = c.skill:canUse(player, c) + ret = ret and not player:prohibitUse(c) + if ret then + local min_target = c.skill:getMinTargetNum() + if min_target > 0 then + for _, p in ipairs(ClientInstance.players) do + if c.skill:targetFilter(p.id, {}, {}, c) then + return "true" end + end + return "false" end - return json.encode(ret) + end + return json.encode(ret) end function CardProhibitedUse(card) - local c ---@type Card - local ret = false - if type(card) == "number" then - c = Fk:getCardById(card) - else - local data = json.decode(card) - local skill = Fk.skills[data.skill] - local selected_cards = data.subcards - if skill:isInstanceOf(ViewAsSkill) then - c = skill:viewAs(selected_cards) - end + local c ---@type Card + local ret = false + if type(card) == "number" then + c = Fk:getCardById(card) + else + local data = json.decode(card) + local skill = Fk.skills[data.skill] + local selected_cards = data.subcards + if skill:isInstanceOf(ViewAsSkill) then + c = skill:viewAs(selected_cards) end - if c == nil then - return "true" - else - ret = Self:prohibitUse(c) - end - return json.encode(ret) + end + if c == nil then + return "true" + else + ret = Self:prohibitUse(c) + end + return json.encode(ret) end ---@param card string | integer ---@param to_select integer @ id of the target ---@param selected integer[] @ ids of selected targets function CanUseCardToTarget(card, to_select, selected) - if ClientInstance:getPlayerById(to_select).dead then - return "false" - end - local c ---@type Card - local selected_cards - if type(card) == "number" then - c = Fk:getCardById(card) - selected_cards = {card} - else - local t = json.decode(card) - return ActiveTargetFilter(t.skill, to_select, selected, t.subcards) - end + if ClientInstance:getPlayerById(to_select).dead then + return "false" + end + local c ---@type Card + local selected_cards + if type(card) == "number" then + c = Fk:getCardById(card) + selected_cards = { card } + else + local t = json.decode(card) + return ActiveTargetFilter(t.skill, to_select, selected, t.subcards) + end - local ret = c.skill:targetFilter(to_select, selected, selected_cards, c) - ret = ret and not Self:isProhibited(Fk:currentRoom():getPlayerById(to_select), c) - return json.encode(ret) + local ret = c.skill:targetFilter(to_select, selected, selected_cards, c) + ret = ret and not Self:isProhibited(Fk:currentRoom():getPlayerById(to_select), c) + return json.encode(ret) end ---@param card string | integer ---@param to_select integer @ id of a card not selected ---@param selected_targets integer[] @ ids of selected players function CanSelectCardForSkill(card, to_select, selected_targets) - local c ---@type Card - local selected_cards - if type(card) == "number" then - c = Fk:getCardById(card) - selected_cards = {card} - else - error() - end + local c ---@type Card + local selected_cards + if type(card) == "number" then + c = Fk:getCardById(card) + selected_cards = { card } + else + error() + end - local ret = c.skill:cardFilter(to_select, selected_cards, selected_targets) - return json.encode(ret) + local ret = c.skill:cardFilter(to_select, selected_cards, selected_targets) + return json.encode(ret) end ---@param card string | integer ---@param selected_targets integer[] @ ids of selected players function CardFeasible(card, selected_targets) - local c ---@type Card - local selected_cards - if type(card) == "number" then - c = Fk:getCardById(card) - selected_cards = {card} - else - local t = json.decode(card) - return ActiveFeasible(t.skill, selected_targets, t.subcards) - end + local c ---@type Card + local selected_cards + if type(card) == "number" then + c = Fk:getCardById(card) + selected_cards = { card } + else + local t = json.decode(card) + return ActiveFeasible(t.skill, selected_targets, t.subcards) + end - local ret = c.skill:feasible(selected_targets, selected_cards, Self, c) - return json.encode(ret) + local ret = c.skill:feasible(selected_targets, selected_cards, Self, c) + return json.encode(ret) end -- Handle skills function GetSkillData(skill_name) - local skill = Fk.skills[skill_name] - if not skill then - return "null" - end - local freq = "notactive" - if skill:isInstanceOf(ActiveSkill) or skill:isInstanceOf(ViewAsSkill) then - freq = "active" - end - local frequency - if skill.frequency == Skill.Limited then - frequency = "limit" - elseif skill.frequency == Skill.Wake then - frequency = "wake" - elseif skill.frequency == Skill.Quest then - frequency = "quest" - end - return json.encode { - skill = Fk:translate(skill_name), - orig_skill = skill_name, - extension = skill.package.extensionName, - freq = freq, - frequency = frequency, - switchSkillName = skill.switchSkillName, - isViewAsSkill = skill:isInstanceOf(ViewAsSkill) - } + local skill = Fk.skills[skill_name] + if not skill then + return "null" + end + local freq = "notactive" + if skill:isInstanceOf(ActiveSkill) or skill:isInstanceOf(ViewAsSkill) then + freq = "active" + end + local frequency + if skill.frequency == Skill.Limited then + frequency = "limit" + elseif skill.frequency == Skill.Wake then + frequency = "wake" + elseif skill.frequency == Skill.Quest then + frequency = "quest" + end + return json.encode { + skill = Fk:translate(skill_name), + orig_skill = skill_name, + extension = skill.package.extensionName, + freq = freq, + frequency = frequency, + switchSkillName = skill.switchSkillName, + isViewAsSkill = skill:isInstanceOf(ViewAsSkill) + } end function ActiveCanUse(skill_name) - local skill = Fk.skills[skill_name] - local ret = false - if skill then - if skill:isInstanceOf(ActiveSkill) then - ret = skill:canUse(Self) - elseif skill:isInstanceOf(ViewAsSkill) then - ret = skill:enabledAtPlay(Self) - if ret then - 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 - if m.trueName then - table.insertTable(cnames, m.trueName) - end - end - for _, n in ipairs(cnames) do - local c = Fk:cloneCard(n) - c.skillName = skill_name - ret = c.skill:canUse(Self, c) - if ret then - break - end - end - end + local skill = Fk.skills[skill_name] + local ret = false + if skill then + if skill:isInstanceOf(ActiveSkill) then + ret = skill:canUse(Self) + elseif skill:isInstanceOf(ViewAsSkill) then + ret = skill:enabledAtPlay(Self) + if ret then + 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 + if m.trueName then + table.insertTable(cnames, m.trueName) + end end + for _, n in ipairs(cnames) do + local c = Fk:cloneCard(n) + c.skillName = skill_name + ret = c.skill:canUse(Self, c) + if ret then + break + end + end + end end - return json.encode(ret) + end + return json.encode(ret) end function ActiveSkillPrompt(skill_name, selected, selected_targets) - local skill = Fk.skills[skill_name] - local ret = false - if skill then - if type(skill.prompt) == "function" then - ret = skill:prompt(selected, selected_targets) - else - ret = skill.prompt - end + local skill = Fk.skills[skill_name] + local ret = false + if skill then + if type(skill.prompt) == "function" then + ret = skill:prompt(selected, selected_targets) + else + ret = skill.prompt end - return json.encode(ret or "") + end + return json.encode(ret or "") end function ActiveCardFilter(skill_name, to_select, selected, selected_targets) - local skill = Fk.skills[skill_name] - local ret = false - if skill then - if skill:isInstanceOf(ActiveSkill) then - ret = skill:cardFilter(to_select, selected, selected_targets) - elseif skill:isInstanceOf(ViewAsSkill) then - ret = skill:cardFilter(to_select, selected) - end + local skill = Fk.skills[skill_name] + local ret = false + if skill then + if skill:isInstanceOf(ActiveSkill) then + ret = skill:cardFilter(to_select, selected, selected_targets) + elseif skill:isInstanceOf(ViewAsSkill) then + ret = skill:cardFilter(to_select, selected) end - return json.encode(ret) + end + return json.encode(ret) end function ActiveTargetFilter(skill_name, to_select, selected, selected_cards) - local skill = Fk.skills[skill_name] - local ret = false - if skill then - if skill:isInstanceOf(ActiveSkill) then - ret = skill:targetFilter(to_select, selected, selected_cards) - elseif skill:isInstanceOf(ViewAsSkill) then - local card = skill:viewAs(selected_cards) - if card then - ret = card.skill:targetFilter(to_select, selected, selected_cards, card) - ret = ret and not Self:isProhibited(Fk:currentRoom():getPlayerById(to_select), card) - end - end + local skill = Fk.skills[skill_name] + local ret = false + if skill then + if skill:isInstanceOf(ActiveSkill) then + ret = skill:targetFilter(to_select, selected, selected_cards) + elseif skill:isInstanceOf(ViewAsSkill) then + local card = skill:viewAs(selected_cards) + if card then + ret = card.skill:targetFilter(to_select, selected, selected_cards, card) + ret = ret and not Self:isProhibited(Fk:currentRoom():getPlayerById(to_select), card) + end end - return json.encode(ret) + end + return json.encode(ret) end function ActiveFeasible(skill_name, selected, selected_cards) - local skill = Fk.skills[skill_name] - local ret = false - if skill then - if skill:isInstanceOf(ActiveSkill) then - ret = skill:feasible(selected, selected_cards, Self, nil) - elseif skill:isInstanceOf(ViewAsSkill) then - local card = skill:viewAs(selected_cards) - if card then - ret = card.skill:feasible(selected, selected_cards, Self, card) - end - end + local skill = Fk.skills[skill_name] + local ret = false + if skill then + if skill:isInstanceOf(ActiveSkill) then + ret = skill:feasible(selected, selected_cards, Self, nil) + elseif skill:isInstanceOf(ViewAsSkill) then + local card = skill:viewAs(selected_cards) + if card then + ret = card.skill:feasible(selected, selected_cards, Self, card) + end end - return json.encode(ret) + end + return json.encode(ret) end function CanViewAs(skill_name, card_ids) - local skill = Fk.skills[skill_name] - local ret = false - if skill then - if skill:isInstanceOf(ViewAsSkill) then - ret = skill:viewAs(card_ids) ~= nil - elseif skill:isInstanceOf(ActiveSkill) then - ret = true - end + local skill = Fk.skills[skill_name] + local ret = false + if skill then + if skill:isInstanceOf(ViewAsSkill) then + ret = skill:viewAs(card_ids) ~= nil + elseif skill:isInstanceOf(ActiveSkill) then + ret = true end - return json.encode(ret) + end + return json.encode(ret) end -- card_name may be id, name of card, or json string function CardFitPattern(card_name, pattern) - local exp = Exppattern:Parse(pattern) - local c - local ret = false - if type(card_name) == "number" then - c = Fk:getCardById(card_name) + local exp = Exppattern:Parse(pattern) + local c + local ret = false + if type(card_name) == "number" then + c = Fk:getCardById(card_name) + ret = exp:match(c) + elseif string.sub(card_name, 1, 1) == "{" then + local data = json.decode(card_name) + local skill = Fk.skills[data.skill] + local selected_cards = data.subcards + if skill:isInstanceOf(ViewAsSkill) then + c = skill:viewAs(selected_cards) + if c then ret = exp:match(c) - elseif string.sub(card_name, 1, 1) == "{" then - local data = json.decode(card_name) - local skill = Fk.skills[data.skill] - local selected_cards = data.subcards - if skill:isInstanceOf(ViewAsSkill) then - c = skill:viewAs(selected_cards) - if c then - ret = exp:match(c) - end - else - return "true" - end + end else - ret = exp:matchExp(card_name) + return "true" end - return json.encode(ret) + else + ret = exp:matchExp(card_name) + end + return json.encode(ret) end function SkillFitPattern(skill_name, pattern) - local skill = Fk.skills[skill_name] - local ret = false - if skill and skill.pattern then - local exp = Exppattern:Parse(pattern) - ret = exp:matchExp(skill.pattern) - end - return json.encode(ret) + local skill = Fk.skills[skill_name] + local ret = false + if skill and skill.pattern then + local exp = Exppattern:Parse(pattern) + ret = exp:matchExp(skill.pattern) + end + return json.encode(ret) end function CardProhibitedResponse(card) - local c ---@type Card - local ret = false - if type(card) == "number" then - c = Fk:getCardById(card) - else - local data = json.decode(card) - local skill = Fk.skills[data.skill] - local selected_cards = data.subcards - if skill:isInstanceOf(ViewAsSkill) then - c = skill:viewAs(selected_cards) - end + local c ---@type Card + local ret = false + if type(card) == "number" then + c = Fk:getCardById(card) + else + local data = json.decode(card) + local skill = Fk.skills[data.skill] + local selected_cards = data.subcards + if skill:isInstanceOf(ViewAsSkill) then + c = skill:viewAs(selected_cards) end - if c == nil then - return "true" - else - ret = Self:prohibitUse(c) - end - return json.encode(ret) + end + if c == nil then + return "true" + else + ret = Self:prohibitUse(c) + end + return json.encode(ret) end function SkillCanResponse(skill_name, cardResponsing) - local skill = Fk.skills[skill_name] - local ret = false - if skill and skill:isInstanceOf(ViewAsSkill) then - ret = skill:enabledAtResponse(Self, cardResponsing) - end - return json.encode(ret) + local skill = Fk.skills[skill_name] + local ret = false + if skill and skill:isInstanceOf(ViewAsSkill) then + ret = skill:enabledAtResponse(Self, cardResponsing) + end + return json.encode(ret) end function GetVirtualEquip(player, cid) - local c = ClientInstance:getPlayerById(player):getVirualEquip(cid) - if not c then - return "null" - end - return json.encode { - name = c.name, - cid = c.subcards[1] - } + local c = ClientInstance:getPlayerById(player):getVirualEquip(cid) + if not c then + return "null" + end + return json.encode { + name = c.name, + cid = c.subcards[1] + } end function GetExpandPileOfSkill(skillName) - local skill = Fk.skills[skillName] - return skill and (skill.expand_pile or "") or "" + local skill = Fk.skills[skillName] + return skill and (skill.expand_pile or "") or "" end function GetGameModes() - local ret = {} - for k, v in pairs(Fk.game_modes) do - table.insert(ret, { - name = Fk:translate(v.name), - orig_name = v.name, - minPlayer = v.minPlayer, - maxPlayer = v.maxPlayer - }) - end - table.sort(ret, function(a, b) - return a.name > b.name - end) - return json.encode(ret) + local ret = {} + for k, v in pairs(Fk.game_modes) do + table.insert(ret, { + name = Fk:translate(v.name), + orig_name = v.name, + minPlayer = v.minPlayer, + maxPlayer = v.maxPlayer + }) + end + table.sort(ret, function(a, b) + return a.name > b.name + end) + return json.encode(ret) end function GetInteractionOfSkill(skill_name) - local skill = Fk.skills[skill_name] - if skill and skill.interaction then - return json.encode(skill:interaction()) - end - return "null" + local skill = Fk.skills[skill_name] + if skill and skill.interaction then + return json.encode(skill:interaction()) + end + return "null" end function SetInteractionDataOfSkill(skill_name, data) - local skill = Fk.skills[skill_name] - if skill and skill.interaction then - skill.interaction.data = json.decode(data) - end + local skill = Fk.skills[skill_name] + if skill and skill.interaction then + skill.interaction.data = json.decode(data) + end end function ChangeSelf(pid) - ClientInstance.client:changeSelf(pid) -- for qml - Self = ClientInstance:getPlayerById(pid) + ClientInstance.client:changeSelf(pid) -- for qml + Self = ClientInstance:getPlayerById(pid) end function GetPlayerHandcards(pid) - local p = ClientInstance:getPlayerById(pid) - return json.encode(p.player_cards[Player.Hand]) + local p = ClientInstance:getPlayerById(pid) + return json.encode(p.player_cards[Player.Hand]) end function GetPlayerEquips(pid) - local p = ClientInstance:getPlayerById(pid) - return json.encode(p.player_cards[Player.Equip]) + local p = ClientInstance:getPlayerById(pid) + return json.encode(p.player_cards[Player.Equip]) end function ResetClientLua() - local _data = ClientInstance.enter_room_data; - local data = ClientInstance.room_settings - Self = ClientPlayer:new(fk.Self) - ClientInstance = Client:new() -- clear old client data - ClientInstance.players = {Self} - ClientInstance.alive_players = {Self} - ClientInstance.discard_pile = {} + local _data = ClientInstance.enter_room_data; + local data = ClientInstance.room_settings + Self = ClientPlayer:new(fk.Self) + ClientInstance = Client:new() -- clear old client data + ClientInstance.players = { Self } + ClientInstance.alive_players = { Self } + ClientInstance.discard_pile = {} - ClientInstance.enter_room_data = _data; - ClientInstance.room_settings = data + ClientInstance.enter_room_data = _data; + ClientInstance.room_settings = data - ClientInstance.disabled_packs = data.disabledPack - ClientInstance.disabled_generals = data.disabledGenerals - -- ClientInstance:notifyUI("EnterRoom", jsonData) + ClientInstance.disabled_packs = data.disabledPack + ClientInstance.disabled_generals = data.disabledGenerals + -- ClientInstance:notifyUI("EnterRoom", jsonData) end function ResetAddPlayer(j) - fk.client_callback["AddPlayer"](j) + fk.client_callback["AddPlayer"](j) end function GetRoomConfig() - return json.encode(ClientInstance.room_settings) + return json.encode(ClientInstance.room_settings) end function GetPlayerGameData(pid) - local p = ClientInstance:getPlayerById(pid) - if not p then - return "[0, 0, 0]" - end - local raw = p.player:getGameData() - local ret = {} - for _, i in fk.qlist(raw) do - table.insert(ret, i) - end - return json.encode(ret) + local p = ClientInstance:getPlayerById(pid) + if not p then + return "[0, 0, 0]" + end + local raw = p.player:getGameData() + local ret = {} + for _, i in fk.qlist(raw) do + table.insert(ret, i) + end + return json.encode(ret) end function SetPlayerGameData(pid, data) - local p = ClientInstance:getPlayerById(pid) - p.player:setGameData(table.unpack(data)) - table.insert(data, 1, pid) - ClientInstance:notifyUI("UpdateGameData", json.encode(data)) + local p = ClientInstance:getPlayerById(pid) + p.player:setGameData(table.unpack(data)) + table.insert(data, 1, pid) + ClientInstance:notifyUI("UpdateGameData", json.encode(data)) end function FilterMyHandcards() - Self:filterHandcards() + Self:filterHandcards() end function SetObserving(o) - ClientInstance.observing = o + ClientInstance.observing = o end function CheckSurrenderAvailable(playedTime) - local curMode = ClientInstance.room_settings.gameMode - return json.encode(Fk.game_modes[curMode]:surrenderFunc(playedTime)) + local curMode = ClientInstance.room_settings.gameMode + return json.encode(Fk.game_modes[curMode]:surrenderFunc(playedTime)) end function SaveRecord() -<<<<<<< HEAD - local c = ClientInstance - c.client:saveRecord(json.encode(c.record), c.record[2]) -======= - local c = ClientInstance - c.client:saveRecord(json.encode(c.record), c.record[2]) + ClientInstance.client:saveRecord(json.encode(ClientInstance.record), ClientInstance.record[2]) end function GetCardProhibitReason(cid, method, pattern) @@ -747,7 +742,6 @@ function PoxiFeasible(poxi_type, selected, data) local poxi = Fk.poxi_methods[poxi_type] if not poxi then return "false" end return json.encode(poxi.feasible(selected, data)) ->>>>>>> 30b363ef775598c6c6510ceb17e70ca01bd2f677 end dofile "lua/client/i18n/init.lua" diff --git a/lua/server/ai/random_ai.lua b/lua/server/ai/random_ai.lua index 7e7e037e..61042410 100644 --- a/lua/server/ai/random_ai.lua +++ b/lua/server/ai/random_ai.lua @@ -6,273 +6,273 @@ local RandomAI = AI:subclass("RandomAI") ---@param skill ActiveSkill ---@param card Card | nil local function useActiveSkill(self, skill, card) - local room = self.room - local player = self.player + local room = self.room + local player = self.player - local filter_func = skill.cardFilter - if card then - filter_func = function() - return false - end + local filter_func = skill.cardFilter + if card then + filter_func = function() + return false end + end - 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 + 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 "" end ---@param self RandomAI ---@param skill ViewAsSkill local function useVSSkill(self, skill, pattern, cancelable, extra_data) - local player = self.player - local room = self.room - local precondition + local player = self.player + local room = self.room + local precondition - if self.command == "PlayCard" then - precondition = skill:enabledAtPlay(player) - if not precondition then - 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) + if self.command == "PlayCard" then + precondition = skill:enabledAtPlay(player) + if not precondition then + return nil end - - if (not precondition) or math.random() < 0.2 then - return nil + 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 - - 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 + 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 + + if (not precondition) or math.random() < 0.2 then 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 local random_cb = {} random_cb.AskForUseActiveSkill = function(self, jsonData) - local data = json.decode(jsonData) - local skill = Fk.skills[data[1]] - local cancelable = data[3] - if cancelable and math.random() < 0.25 then - return "" - end - local extra_data = json.decode(data[4]) - for k, v in pairs(extra_data) do - skill[k] = v - end - return useActiveSkill(self, skill) + local data = json.decode(jsonData) + local skill = Fk.skills[data[1]] + local cancelable = data[3] + if cancelable and math.random() < 0.25 then + return "" + end + local extra_data = json.decode(data[4]) + for k, v in pairs(extra_data) do + skill[k] = v + end + return useActiveSkill(self, skill) end random_cb.AskForSkillInvoke = function(self, jsonData) - return table.random {"1", ""} + return table.random { "1", "" } end random_cb.AskForUseCard = function(self, jsonData) - local player = self.player - local data = json.decode(jsonData) - local card_name = data[1] - local pattern = data[2] or card_name - local cancelable = data[4] or true - local exp = Exppattern:Parse(pattern) + local player = self.player + local data = json.decode(jsonData) + local card_name = data[1] + local pattern = data[2] or card_name + local cancelable = data[4] or true + local exp = Exppattern:Parse(pattern) - local avail_cards = table.filter(player:getCardIds("he"), function(id) - return exp:match(Fk:getCardById(id)) and not player:prohibitUse(Fk:getCardById(id)) - end) - if #avail_cards > 0 then - if math.random() < 0.25 then - 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 + local avail_cards = table.filter(player:getCardIds("he"), function(id) + return exp:match(Fk:getCardById(id)) and not player:prohibitUse(Fk:getCardById(id)) + end) + if #avail_cards > 0 then + if math.random() < 0.25 then + return "" 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 ---@param self RandomAI random_cb.AskForResponseCard = function(self, jsonData) - local data = json.decode(jsonData) - local pattern = data[2] - local cancelable = true - local exp = Exppattern:Parse(pattern) - local avail_cards = table.filter(self.player:getCardIds{Player.Hand, Player.Equip}, function(id) - return exp:match(Fk:getCardById(id)) - end) - if #avail_cards > 0 then - return json.encode { - card = table.random(avail_cards), - targets = {} - } - end - -- TODO: vs skill - return "" + local data = json.decode(jsonData) + local pattern = data[2] + local cancelable = true + local exp = Exppattern:Parse(pattern) + local avail_cards = table.filter(self.player:getCardIds { Player.Hand, Player.Equip }, function(id) + return exp:match(Fk:getCardById(id)) + end) + if #avail_cards > 0 then + return json.encode { + card = table.random(avail_cards), + targets = {} + } + end + -- TODO: vs skill + return "" end ---@param self RandomAI random_cb.PlayCard = function(self, jsonData) - local cards = table.map(self.player:getCardIds(Player.Hand), function(id) - return Fk:getCardById(id) - end) - local actives = table.filter(self.player:getAllSkills(), function(s) - return s:isInstanceOf(ActiveSkill) - end) - local vss = table.filter(self.player:getAllSkills(), function(s) - return s:isInstanceOf(ViewAsSkill) - end) - table.insertTable(cards, actives) - table.insertTable(cards, vss) + local cards = table.map(self.player:getCardIds(Player.Hand), function(id) + return Fk:getCardById(id) + end) + local actives = table.filter(self.player:getAllSkills(), function(s) + return s:isInstanceOf(ActiveSkill) + end) + local vss = table.filter(self.player:getAllSkills(), function(s) + return s:isInstanceOf(ViewAsSkill) + end) + table.insertTable(cards, actives) + table.insertTable(cards, vss) - while #cards > 0 do - local sth = table.random(cards) - if sth:isInstanceOf(Card) then - local card = sth - local skill = card.skill ---@type ActiveSkill - if math.random() > 0.15 then - local ret = useActiveSkill(self, skill, card) - if ret ~= "" then - 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) + while #cards > 0 do + local sth = table.random(cards) + if sth:isInstanceOf(Card) then + local card = sth + local skill = card.skill ---@type ActiveSkill + if math.random() > 0.15 then + local ret = useActiveSkill(self, skill, card) + if ret ~= "" then + 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 - return "" + return "" end function RandomAI:initialize(player) - AI.initialize(self, player) - self.cb_table = random_cb + AI.initialize(self, player) + self.cb_table = random_cb end return RandomAI diff --git a/lua/server/ai/trust_ai.lua b/lua/server/ai/trust_ai.lua index ed577ea4..153eca26 100644 --- a/lua/server/ai/trust_ai.lua +++ b/lua/server/ai/trust_ai.lua @@ -8,328 +8,6 @@ local trust_cb = {} function TrustAI:initialize(player) AI.initialize(self, player) 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) 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 return TrustAI diff --git a/lua/server/event.lua b/lua/server/event.lua index 09f91e0c..ce2db8c8 100644 --- a/lua/server/event.lua +++ b/lua/server/event.lua @@ -129,3 +129,121 @@ fk.GeneralRevealed = 89 fk.GeneralHidden = 90 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 --]] diff --git a/lua/server/events/usecard.lua b/lua/server/events/usecard.lua index 32613bcd..19b99c01 100644 --- a/lua/server/events/usecard.lua +++ b/lua/server/events/usecard.lua @@ -194,16 +194,15 @@ end GameEvent.functions[GameEvent.UseCard] = function(self) local cardUseEvent = table.unpack(self.data) local room = self.room - local logic = room.logic if cardUseEvent.card.skill then cardUseEvent.card.skill:onUse(room, cardUseEvent) 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 self.data = { cardUseEvent } - logic:breakEvent() + room.logic:breakEvent() end room:moveCardTo(cardUseEvent.card, Card.Processing, nil, fk.ReasonUse) @@ -223,7 +222,7 @@ GameEvent.functions[GameEvent.UseCard] = function(self) break end - logic:trigger(event, room:getPlayerById(cardUseEvent.from), cardUseEvent) + room.logic:trigger(event, room:getPlayerById(cardUseEvent.from), cardUseEvent) if event == fk.CardUsing then room:doCardUseEffect(cardUseEvent) end @@ -232,13 +231,12 @@ end GameEvent.cleaners[GameEvent.UseCard] = function(self) 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 - room:moveCards({ + self.room:moveCards({ ids = leftRealCardIds, toArea = Card.DiscardPile, moveReason = fk.ReasonUse @@ -249,12 +247,11 @@ end GameEvent.functions[GameEvent.RespondCard] = function(self) local cardResponseEvent = table.unpack(self.data) 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 self.data = { cardResponseEvent } - logic:breakEvent() + room.logic:breakEvent() end local from = cardResponseEvent.customFrom or cardResponseEvent.from @@ -296,19 +293,18 @@ GameEvent.functions[GameEvent.RespondCard] = function(self) if cardResponseEvent.retrial ~= true then playCardEmotionAndSound(room, room:getPlayerById(from), card) end - logic:trigger(fk.CardResponding, room:getPlayerById(cardResponseEvent.from), cardResponseEvent) + room.logic:trigger(fk.CardResponding, room:getPlayerById(cardResponseEvent.from), cardResponseEvent) self.data = { cardResponseEvent } end GameEvent.cleaners[GameEvent.RespondCard] = function(self) 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 - room:moveCards({ + self.room:moveCards({ ids = realCardIds, toArea = Card.DiscardPile, moveReason = fk.ReasonResonpse @@ -319,34 +315,33 @@ end GameEvent.functions[GameEvent.CardEffect] = function(self) local cardEffectEvent = table.unpack(self.data) local room = self.room - local logic = room.logic for _, event in ipairs({ fk.PreCardEffect, fk.BeforeCardEffect, fk.CardEffecting, fk.CardEffectFinished }) do local user = cardEffectEvent.from and room:getPlayerById(cardEffectEvent.from) or nil if cardEffectEvent.isCancellOut then - if logic:trigger(fk.CardEffectCancelledOut, user, cardEffectEvent) then + if room.logic:trigger(fk.CardEffectCancelledOut, user, cardEffectEvent) then cardEffectEvent.isCancellOut = false else - logic:breakEvent() + room.logic:breakEvent() end end if not cardEffectEvent.toCard and (not (room:getPlayerById(cardEffectEvent.to):isAlive() and cardEffectEvent.to) or #room:deadPlayerFilter(TargetGroup:getRealTargets(cardEffectEvent.tos)) == 0) then - logic:breakEvent() + room.logic:breakEvent() end if table.contains((cardEffectEvent.nullifiedTargets or Util.DummyTable), cardEffectEvent.to) then - logic:breakEvent() + room.logic:breakEvent() end if event == fk.PreCardEffect then - if cardEffectEvent.from and logic:trigger(event, room:getPlayerById(cardEffectEvent.from), cardEffectEvent) then - logic:breakEvent() + if cardEffectEvent.from and room.logic:trigger(event, room:getPlayerById(cardEffectEvent.from), cardEffectEvent) then + room.logic:breakEvent() end - elseif cardEffectEvent.to and logic:trigger(event, room:getPlayerById(cardEffectEvent.to), cardEffectEvent) then - logic:breakEvent() + elseif cardEffectEvent.to and room.logic:trigger(event, room:getPlayerById(cardEffectEvent.to), cardEffectEvent) then + room.logic:breakEvent() end room:handleCardEffect(event, cardEffectEvent) diff --git a/lua/server/gamelogic.lua b/lua/server/gamelogic.lua index 429c4ca9..eea38604 100644 --- a/lua/server/gamelogic.lua +++ b/lua/server/gamelogic.lua @@ -13,424 +13,423 @@ 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.game_event_stack = Stack:new() - self.all_game_events = {} - self.event_recorder = {} - self.current_event_id = 0 + self.room = room + self.skill_table = {} -- TriggerEvent --> TriggerSkill[] + self.skill_priority_table = {} + self.refresh_skill_table = {} + self.skills = {} -- skillName[] + self.game_event_stack = Stack:new() + self.all_game_events = {} + self.event_recorder = {} + self.current_event_id = 0 - self.role_table = {{"lord"}, {"lord", "rebel"}, {"lord", "rebel", "renegade"}, - {"lord", "loyalist", "rebel", "renegade"}, {"lord", "loyalist", "rebel", "rebel", "renegade"}, - {"lord", "loyalist", "rebel", "rebel", "rebel", "renegade"}, - {"lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "renegade"}, - {"lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "rebel", "renegade"}} + self.role_table = { { "lord" }, { "lord", "rebel" }, { "lord", "rebel", "renegade" }, + { "lord", "loyalist", "rebel", "renegade" }, { "lord", "loyalist", "rebel", "rebel", "renegade" }, + { "lord", "loyalist", "rebel", "rebel", "rebel", "renegade" }, + { "lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "renegade" }, + { "lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "rebel", "renegade" } } end function GameLogic:run() - -- default logic - local room = self.room - table.shuffle(self.room.players) - self:assignRoles() - self.room.game_started = true - room:doBroadcastNotify("StartGame", "") - room:adjustSeats() + -- default logic + local room = self.room + table.shuffle(self.room.players) + self:assignRoles() + self.room.game_started = true + room:doBroadcastNotify("StartGame", "") + room:adjustSeats() - self:chooseGenerals() + self:chooseGenerals() - self:buildPlayerCircle() - self:broadcastGeneral() - self:prepareDrawPile() - self:attachSkillToPlayers() - self:prepareForStart() + self:buildPlayerCircle() + self:broadcastGeneral() + self:prepareDrawPile() + self:attachSkillToPlayers() + self:prepareForStart() - self:action() + self:action() end local function execGameEvent(type, ...) - local event = GameEvent:new(type, ...) - local _, ret = event:exec() - return ret + local event = GameEvent:new(type, ...) + local _, ret = event:exec() + return ret end function GameLogic:assignRoles() - local room = self.room - local n = #room.players - local roles = self.role_table[n] - table.shuffle(roles) + local room = self.room + local n = #room.players + local roles = self.role_table[n] + table.shuffle(roles) - for i = 1, n do - local p = room.players[i] - p.role = roles[i] - if p.role == "lord" then - p.role_shown = true - room:broadcastProperty(p, "role") - else - room:notifyProperty(p, p, "role") - end + for i = 1, n do + local p = room.players[i] + p.role = roles[i] + if p.role == "lord" then + p.role_shown = true + room:broadcastProperty(p, "role") + else + room:notifyProperty(p, p, "role") end + end end function GameLogic:chooseGenerals() - local room = self.room - local generalNum = room.settings.generalNum - local n = room.settings.enableDeputy and 2 or 1 - local lord = room:getLord() - local lord_generals = {} + local room = self.room + local generalNum = room.settings.generalNum + local n = room.settings.enableDeputy and 2 or 1 + local lord = room:getLord() + local lord_generals = {} - if lord ~= nil then - room.current = lord - local generals = {} - local lordlist = {} - local lordpools = {} - if room.settings.gameMode == "aaa_role_mode" then - for _, general in pairs(Fk:getAllGenerals()) do - if (not general.hidden and not general.total_hidden) and table.find(general.skills, function(s) - return s.lordSkill - end) and not table.find(lordlist, function(g) - return g.trueName == general.trueName - end) then - table.insert(lordlist, general) - end - end - lordlist = table.random(lordlist, 3) or {} + if lord ~= nil then + room.current = lord + local generals = {} + local lordlist = {} + local lordpools = {} + if room.settings.gameMode == "aaa_role_mode" then + for _, general in pairs(Fk:getAllGenerals()) do + if (not general.hidden and not general.total_hidden) and table.find(general.skills, function(s) + return s.lordSkill + end) and not table.find(lordlist, function(g) + return g.trueName == general.trueName + end) then + table.insert(lordlist, general) end - table.insertTable(generals, Fk:getGeneralsRandomly(generalNum, Fk:getAllGenerals(), nil, function(g) - return table.contains(table.map(lordlist, function(g) - return g.trueName - end), g.trueName) - end)) - for i = 1, #generals do - generals[i] = generals[i].name - end - lordpools = table.simpleClone(generals) - table.insertTable(lordpools, table.map(lordlist, function(g) - return g.name - end)) - lord_generals = room:askForGeneral(lord, lordpools, n) - local lord_general, deputy - if type(lord_generals) == "table" then - deputy = lord_generals[2] - lord_general = lord_generals[1] - else - lord_general = lord_generals - lord_generals = {lord_general} - end - - room:setPlayerGeneral(lord, lord_general, true) - room:askForChooseKingdom({lord}) - room:broadcastProperty(lord, "general") - room:broadcastProperty(lord, "kingdom") - room:setDeputyGeneral(lord, deputy) - room:broadcastProperty(lord, "deputyGeneral") + end + lordlist = table.random(lordlist, 3) or {} + end + table.insertTable(generals, Fk:getGeneralsRandomly(generalNum, Fk:getAllGenerals(), nil, function(g) + return table.contains(table.map(lordlist, function(g) + return g.trueName + end), g.trueName) + end)) + for i = 1, #generals do + generals[i] = generals[i].name + end + lordpools = table.simpleClone(generals) + table.insertTable(lordpools, table.map(lordlist, function(g) + return g.name + end)) + lord_generals = room:askForGeneral(lord, lordpools, n) + local lord_general, deputy + if type(lord_generals) == "table" then + deputy = lord_generals[2] + lord_general = lord_generals[1] + else + lord_general = lord_generals + lord_generals = { lord_general } end - local nonlord = room:getOtherPlayers(lord, true) - 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 - p.request_data = json.encode {arg, n} - p.default_reply = table.random(arg, n) + room:setPlayerGeneral(lord, lord_general, true) + room:askForChooseKingdom({ lord }) + room:broadcastProperty(lord, "general") + room:broadcastProperty(lord, "kingdom") + room:setDeputyGeneral(lord, deputy) + room:broadcastProperty(lord, "deputyGeneral") + end + + local nonlord = room:getOtherPlayers(lord, true) + 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 + p.request_data = json.encode { arg, n } + p.default_reply = table.random(arg, n) + end - room:notifyMoveFocus(nonlord, "AskForGeneral") - room:doBroadcastRequest("AskForGeneral", nonlord) + room:notifyMoveFocus(nonlord, "AskForGeneral") + room:doBroadcastRequest("AskForGeneral", nonlord) - for _, p in ipairs(nonlord) do - if p.general == "" and p.reply_ready then - local generals = json.decode(p.client_reply) - local general = generals[1] - local deputy = generals[2] - room:setPlayerGeneral(p, general, true, true) - room:setDeputyGeneral(p, deputy) - else - room:setPlayerGeneral(p, p.default_reply[1], true, true) - room:setDeputyGeneral(p, p.default_reply[2]) - end - p.default_reply = "" + for _, p in ipairs(nonlord) do + if p.general == "" and p.reply_ready then + local generals = json.decode(p.client_reply) + local general = generals[1] + local deputy = generals[2] + room:setPlayerGeneral(p, general, true, true) + room:setDeputyGeneral(p, deputy) + else + room:setPlayerGeneral(p, p.default_reply[1], true, true) + room:setDeputyGeneral(p, p.default_reply[2]) end + p.default_reply = "" + end - room:askForChooseKingdom(nonlord) + room:askForChooseKingdom(nonlord) end function GameLogic:buildPlayerCircle() - local room = self.room - local players = room.players - room.alive_players = {table.unpack(players)} - for i = 1, #players - 1 do - players[i].next = players[i + 1] - end - players[#players].next = players[1] + local room = self.room + local players = room.players + room.alive_players = { table.unpack(players) } + for i = 1, #players - 1 do + players[i].next = players[i + 1] + end + players[#players].next = players[1] end function GameLogic:broadcastGeneral() - local room = self.room - local players = room.players + local room = self.room + local players = room.players - for _, p in ipairs(players) do - assert(p.general ~= "") - local general = Fk.generals[p.general] - local deputy = Fk.generals[p.deputyGeneral] - p.maxHp = p:getGeneralMaxHp() - 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) - -- TODO: setup AI here + for _, p in ipairs(players) do + assert(p.general ~= "") + local general = Fk.generals[p.general] + local deputy = Fk.generals[p.deputyGeneral] + p.maxHp = p:getGeneralMaxHp() + 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) + -- TODO: setup AI here - if p.role ~= "lord" then - room:broadcastProperty(p, "general") - room:broadcastProperty(p, "kingdom") - room:broadcastProperty(p, "deputyGeneral") - elseif #players >= 5 then - p.maxHp = p.maxHp + 1 - p.hp = p.hp + 1 - end - room:broadcastProperty(p, "maxHp") - room:broadcastProperty(p, "hp") - room:broadcastProperty(p, "shield") + if p.role ~= "lord" then + room:broadcastProperty(p, "general") + room:broadcastProperty(p, "kingdom") + room:broadcastProperty(p, "deputyGeneral") + elseif #players >= 5 then + p.maxHp = p.maxHp + 1 + p.hp = p.hp + 1 end + room:broadcastProperty(p, "maxHp") + room:broadcastProperty(p, "hp") + room:broadcastProperty(p, "shield") + end end function GameLogic:prepareDrawPile() - local room = self.room - local allCardIds = Fk:getAllCardIds() + local room = self.room + local allCardIds = Fk:getAllCardIds() - for i = #allCardIds, 1, -1 do - if Fk:getCardById(allCardIds[i]).is_derived then - local id = allCardIds[i] - table.removeOne(allCardIds, id) - table.insert(room.void, id) - room:setCardArea(id, Card.Void, nil) - end + for i = #allCardIds, 1, -1 do + if Fk:getCardById(allCardIds[i]).is_derived then + local id = allCardIds[i] + table.removeOne(allCardIds, id) + table.insert(room.void, id) + room:setCardArea(id, Card.Void, nil) end + end - table.shuffle(allCardIds) - room.draw_pile = allCardIds - for _, id in ipairs(room.draw_pile) do - self.room:setCardArea(id, Card.DrawPile, nil) - end + table.shuffle(allCardIds) + room.draw_pile = allCardIds + for _, id in ipairs(room.draw_pile) do + self.room:setCardArea(id, Card.DrawPile, nil) + end end function GameLogic:attachSkillToPlayers() - local room = self.room - local players = room.players + local room = self.room + local players = room.players - local addRoleModSkills = function(player, skillName) - local skill = Fk.skills[skillName] - if skill.lordSkill and (player.role ~= "lord" or #room.players < 5) then - return - end - - if #skill.attachedKingdom > 0 and not table.contains(skill.attachedKingdom, player.kingdom) then - return - end - - room:handleAddLoseSkills(player, skillName, nil, false) + local addRoleModSkills = function(player, skillName) + local skill = Fk.skills[skillName] + if skill.lordSkill and (player.role ~= "lord" or #room.players < 5) then + return 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 + if #skill.attachedKingdom > 0 and not table.contains(skill.attachedKingdom, player.kingdom) then + return 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 function GameLogic:prepareForStart() - local room = self.room - local players = room.players + local room = self.room + local players = room.players - self:addTriggerSkill(GameRule) - for _, trig in ipairs(Fk.global_trigger) do - self:addTriggerSkill(trig) - end + self:addTriggerSkill(GameRule) + for _, trig in ipairs(Fk.global_trigger) do + self:addTriggerSkill(trig) + end - self.room:sendLog{ - type = "$GameStart" - } + self.room:sendLog { + type = "$GameStart" + } end function GameLogic:action() - self:trigger(fk.GamePrepared) - local room = self.room + self:trigger(fk.GamePrepared) + local room = self.room - execGameEvent(GameEvent.DrawInitial) + execGameEvent(GameEvent.DrawInitial) - while true do - execGameEvent(GameEvent.Round) - if room.game_finished then - break - end + while true do + execGameEvent(GameEvent.Round) + if room.game_finished then + break end + end end ---@param skill TriggerSkill function GameLogic:addTriggerSkill(skill) - if skill == nil or table.contains(self.skills, skill.name) then - return + if skill == nil or table.contains(self.skills, skill.name) then + 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 - 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] = {} + 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 - table.insert(self.refresh_skill_table[event], skill) + end + + if not table.contains(priority_tab, prio) then + table.insert(priority_tab, prio) + end 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 - - 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 + 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 - if (Fk.related_skills[skill.name] == nil) then - return - end - for _, s in ipairs(Fk.related_skills[skill.name]) do - if (s.class == TriggerSkill) then - self:addTriggerSkill(s) - end - end + if skill.visible then + if (Fk.related_skills[skill.name] == nil) then + return end + for _, s in ipairs(Fk.related_skills[skill.name]) do + if (s.class == TriggerSkill) then + self:addTriggerSkill(s) + end + end + end end ---@param event Event ---@param target ServerPlayer|nil ---@param data any|nil function GameLogic:trigger(event, target, data, refresh_only) - local room = self.room - local broken = false - local skills = self.skill_table[event] or {} - local skills_to_refresh = self.refresh_skill_table[event] or Util.DummyTable - local _target = room.current -- for iteration - local player = _target - 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 - skill:refresh(event, target, player, data) - end - end - player = player.next - end - until player == _target + local room = self.room + local broken = false + local skills = self.skill_table[event] or {} + local skills_to_refresh = self.refresh_skill_table[event] or Util.DummyTable + local _target = room.current -- for iteration + local player = _target + 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 + skill:refresh(event, target, player, data) + end + end + player = player.next + end + 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 - if #skills == 0 or refresh_only then - return - 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 prio_tab = self.skill_priority_table[event] - local prev_prio = math.huge + local skill_names = table.map(triggerables, function(skill) + return skill.name + end) - for _, prio in ipairs(prio_tab) do - if broken then + 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 prio >= prev_prio then - -- continue - goto trigger_loop_continue + if broken then + break end + player = player.next + end + until player == _target - 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 = 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 + prev_prio = prio + ::trigger_loop_continue:: + end + _target.ai:filterEvent(event, target, data) + return broken end ---@return GameEvent 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 -- 在指定历史范围中找至多n个符合条件的事件 @@ -440,90 +439,89 @@ end ---@param scope integer @ 查询历史范围,只能是当前阶段/回合/轮次 ---@return GameEvent[] @ 找到的符合条件的所有事件,最多n个但不保证有n个 function GameLogic:getEventsOfScope(eventType, n, func, scope) - scope = scope or Player.HistoryTurn - local event = self:getCurrentEvent() - local start_event ---@type GameEvent - if scope == Player.HistoryGame then - start_event = self.all_game_events[1] - elseif scope == Player.HistoryRound then - start_event = event:findParent(GameEvent.Round, true) - elseif scope == Player.HistoryTurn then - start_event = event:findParent(GameEvent.Turn, true) - elseif scope == Player.HistoryPhase then - start_event = event:findParent(GameEvent.Phase, true) - end + scope = scope or Player.HistoryTurn + local event = self:getCurrentEvent() + local start_event ---@type GameEvent + if scope == Player.HistoryGame then + start_event = self.all_game_events[1] + elseif scope == Player.HistoryRound then + start_event = event:findParent(GameEvent.Round, true) + elseif scope == Player.HistoryTurn then + start_event = event:findParent(GameEvent.Turn, true) + elseif scope == Player.HistoryPhase then + start_event = event:findParent(GameEvent.Phase, true) + end - return start_event:searchEvents(eventType, n, func) + return start_event:searchEvents(eventType, n, func) end function GameLogic:dumpEventStack(detailed) - local top = self:getCurrentEvent() - local i = self.game_event_stack.p - local inspect = p - if not top then - return + local top = self:getCurrentEvent() + local i = self.game_event_stack.p + local inspect = p + if not top then + 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 - print("===== Start of event stack dump =====") 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 - repeat - local printable_data - if type(top.data) ~= "table" then - printable_data = top.data - else - printable_data = table.cloneWithoutClass(top.data) - end + top = top.parent + i = i - 1 + until not top - if not detailed then - 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 =====") + print("\n===== End of event stack dump =====") end function GameLogic:dumpAllEvents(from, to) - from = from or 1 - to = to or #self.all_game_events - assert(from <= to) - - local indent = 0 - local tab = " " - for i = from, to, 1 do - local v = self.all_game_events[i] - if type(v) ~= "table" then - indent = math.max(indent - 1, 0) - -- v = "End" - -- print(tab:rep(indent) .. string.format("#%d: %s", i, v)) - else - print(tab:rep(indent) .. string.format("%s", tostring(v))) - if v.id ~= v.end_id then - indent = indent + 1 - end - end + from = from or 1 + to = to or #self.all_game_events + assert(from <= to) + local indent = 0 + local tab = " " + for i = from, to, 1 do + local v = self.all_game_events[i] + if type(v) ~= "table" then + indent = math.max(indent - 1, 0) + -- v = "End" + -- print(tab:rep(indent) .. string.format("#%d: %s", i, v)) + else + print(tab:rep(indent) .. string.format("%s", tostring(v))) + if v.id ~= v.end_id then + indent = indent + 1 + end end + end end function GameLogic:breakEvent(ret) - self.room.breakEvent = true - coroutine.yield("__breakEvent", ret) + self.room.breakEvent = true + coroutine.yield("__breakEvent", ret) end function GameLogic:breakTurn() - local event = self:getCurrentEvent():findParent(GameEvent.Turn) - event:shutdown() + local event = self:getCurrentEvent():findParent(GameEvent.Turn) + event:shutdown() end return GameLogic