From 44b43eea8dac8545a76992b7a879f918fff82415 Mon Sep 17 00:00:00 2001 From: seven <786852516@qq.com> Date: Mon, 19 Aug 2024 13:32:43 +0800 Subject: [PATCH 01/12] =?UTF-8?q?=E4=BF=AE=E6=94=B9skilleffect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lua/client/i18n/en_US.lua | 1 + lua/client/i18n/zh_CN.lua | 1 + lua/core/skill_type/trigger.lua | 4 ++- lua/server/events/skill.lua | 61 ++++++++++++++++++++++++++++---- lua/server/room.lua | 62 +++++++++++---------------------- standard/init.lua | 8 ++--- 6 files changed, 83 insertions(+), 54 deletions(-) diff --git a/lua/client/i18n/en_US.lua b/lua/client/i18n/en_US.lua index 070b2ae..5cefe51 100644 --- a/lua/client/i18n/en_US.lua +++ b/lua/client/i18n/en_US.lua @@ -404,6 +404,7 @@ Fk:loadTranslationTable({ -- skill ["#InvokeSkill"] = '%from used skill "%arg"', + ["#InvokeSkillTo"] = '%from used skill "%arg" to %to', -- judge ["#StartJudgeReason"] = "%from started a judgement (%arg)", diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index f6ff38a..c11f078 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -477,6 +477,7 @@ Fk:loadTranslationTable{ -- skill ["#InvokeSkill"] = "%from 发动了〖%arg〗", + ["#InvokeSkillTo"] = "%from 对 %to 发动了〖%arg〗", -- judge ["#StartJudgeReason"] = "%from 开始了 %arg 的判定", diff --git a/lua/core/skill_type/trigger.lua b/lua/core/skill_type/trigger.lua index 83b890f..406712a 100644 --- a/lua/core/skill_type/trigger.lua +++ b/lua/core/skill_type/trigger.lua @@ -72,9 +72,11 @@ function TriggerSkill:doCost(event, target, player, data) self.cost_data = cost_data_bak if ret then + local cost_data = cost_data_bak or Util.DummyTable + local skill_data = {cost_data = cost_data, tos = cost_data.tos or {}, cards = cost_data.cards or {}} return room:useSkill(player, self, function() return self:use(event, target, player, data) - end) + end, skill_data) end end diff --git a/lua/server/events/skill.lua b/lua/server/events/skill.lua index df4ffea..c44cdea 100644 --- a/lua/server/events/skill.lua +++ b/lua/server/events/skill.lua @@ -3,22 +3,69 @@ ---@class GameEvent.SkillEffect : GameEvent local SkillEffect = GameEvent:subclass("GameEvent.SkillEffect") function SkillEffect:main() - local effect_cb, player, _skill = table.unpack(self.data) + local effect_cb, player, skill, skill_data = table.unpack(self.data) local room = self.room local logic = room.logic - local skill = _skill.main_skill and _skill.main_skill or _skill + local main_skill = skill.main_skill and skill.main_skill or skill + skill_data = skill_data or Util.DummyTable - if player then - player:addSkillUseHistory(skill.name) + if player and not skill.cardSkill then + player:revealBySkillName(main_skill.name) + + local tos = skill_data.tos or {} + local mute = (skill_data.mute ~= nil) and skill_data.mute or skill.mute + local no_indicate = (skill_data.no_indicate ~= nil) and skill_data.no_indicate or skill.no_indicate + if not mute then + if skill.attached_equip then + local equip = Fk.all_card_types[skill.attached_equip] + if equip then + local pkgPath = "./packages/" .. equip.package.extensionName + local soundName = pkgPath .. "/audio/card/" .. equip.name + room:broadcastPlaySound(soundName) + if not no_indicate and #tos > 0 then + room:sendLog{ + type = "#InvokeSkillTo", + from = player.id, + arg = skill.name, + to = tos, + } + else + room:sendLog{ + type = "#InvokeSkill", + from = player.id, + arg = skill.name, + } + end + room:setEmotion(player, pkgPath .. "/image/anim/" .. equip.name) + end + else + player:broadcastSkillInvoke(skill.name) + room:notifySkillInvoked(player, skill.name, nil, no_indicate and {} or tos) + end + end + if not no_indicate then + room:doIndicate(player.id, tos) + end + + if skill:isSwitchSkill() then + local switchSkillName = skill.switchSkillName + room:setPlayerMark( + player, + MarkEnum.SwithSkillPreName .. switchSkillName, + player:getSwitchSkillState(switchSkillName, true) + ) + end + + player:addSkillUseHistory(main_skill.name) end local cost_data_bak = skill.cost_data - logic:trigger(fk.SkillEffect, player, skill) + logic:trigger(fk.SkillEffect, player, main_skill, skill_data) skill.cost_data = cost_data_bak - local ret = effect_cb() + local ret = effect_cb and effect_cb() or false - logic:trigger(fk.AfterSkillEffect, player, skill) + logic:trigger(fk.AfterSkillEffect, player, main_skill, skill_data) return ret end diff --git a/lua/server/room.lua b/lua/server/room.lua index d0580f9..99ff891 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -1101,7 +1101,8 @@ end ---@param player ServerPlayer @ 发动技能的那个玩家 ---@param skill_name string @ 技能名 ---@param skill_type? string | AnimationType @ 技能的动画效果,默认是那个技能的anim_type -function Room:notifySkillInvoked(player, skill_name, skill_type) +---@param tos? table @ 技能目标,填空则不声明 +function Room:notifySkillInvoked(player, skill_name, skill_type, tos) local bigAnim = false if not skill_type then local skill = Fk.skills[skill_name] @@ -1116,11 +1117,20 @@ function Room:notifySkillInvoked(player, skill_name, skill_type) if skill_type == "big" then bigAnim = true end - self:sendLog{ - type = "#InvokeSkill", - from = player.id, - arg = skill_name, - } + if tos and #tos > 0 then + self:sendLog{ + type = "#InvokeSkillTo", + from = player.id, + arg = skill_name, + to = tos, + } + else + self:sendLog{ + type = "#InvokeSkill", + from = player.id, + arg = skill_name, + } + end if not bigAnim then self:doAnimate("InvokeSkill", { @@ -2241,15 +2251,12 @@ function Room:handleUseCardReply(player, data) if skill.interaction then skill.interaction.data = data.interaction_data end if skill:isInstanceOf(ActiveSkill) then self:useSkill(player, skill, function() - if not skill.no_indicate then - self:doIndicate(player.id, targets) - end skill:onUse(self, { from = player.id, cards = selected_cards, tos = targets, }) - end) + end, {tos = targets, cards = selected_cards, cost_data = {}}) return nil elseif skill:isInstanceOf(ViewAsSkill) then Self = player @@ -3840,38 +3847,9 @@ end ---@param player ServerPlayer @ 发动技能的玩家 ---@param skill Skill @ 发动的技能 ---@param effect_cb fun() @ 实际要调用的函数 -function Room:useSkill(player, skill, effect_cb) - player:revealBySkillName(skill.name) - if not skill.mute then - if skill.attached_equip then - local equip = Fk.all_card_types[skill.attached_equip] - local pkgPath = "./packages/" .. equip.package.extensionName - local soundName = pkgPath .. "/audio/card/" .. equip.name - self:broadcastPlaySound(soundName) - self:sendLog{ - type = "#InvokeSkill", - from = player.id, - arg = skill.name, - } - self:setEmotion(player, pkgPath .. "/image/anim/" .. equip.name) - else - player:broadcastSkillInvoke(skill.name) - self:notifySkillInvoked(player, skill.name) - end - end - - if skill:isSwitchSkill() then - local switchSkillName = skill.switchSkillName - self:setPlayerMark( - player, - MarkEnum.SwithSkillPreName .. switchSkillName, - player:getSwitchSkillState(switchSkillName, true) - ) - end - - if effect_cb then - return execGameEvent(GameEvent.SkillEffect, effect_cb, player, skill) - end +---@param skill_data? table @ 技能的信息 +function Room:useSkill(player, skill, effect_cb, skill_data) + return execGameEvent(GameEvent.SkillEffect, effect_cb, player, skill, skill_data or Util.DummyFunc) end ---@param player ServerPlayer diff --git a/standard/init.lua b/standard/init.lua index aa189e4..6d48f11 100644 --- a/standard/init.lua +++ b/standard/init.lua @@ -162,15 +162,15 @@ local tuxi = fk.CreateTriggerSkill{ local result = room:askForChoosePlayers(player, targets, 1, 2, "#tuxi-ask", self.name) if #result > 0 then - self.cost_data = result + room:sortPlayersByAction(result) + self.cost_data = {tos = result} return true end end, on_use = function(self, event, target, player, data) local room = player.room - room:sortPlayersByAction(self.cost_data) - for _, id in ipairs(self.cost_data) do - if player.dead then return end + for _, id in ipairs(self.cost_data.tos) do + if player.dead then break end local p = room:getPlayerById(id) if not p.dead and not p:isKongcheng() then local c = room:askForCardChosen(player, p, "h", self.name) -- Gitee From e164e20b2e3b2a4f27cfa84f2bf864f28bb7969c Mon Sep 17 00:00:00 2001 From: seven <786852516@qq.com> Date: Mon, 19 Aug 2024 13:54:57 +0800 Subject: [PATCH 02/12] fix --- lua/core/skill_type/trigger.lua | 7 +++++-- lua/server/events/skill.lua | 8 ++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lua/core/skill_type/trigger.lua b/lua/core/skill_type/trigger.lua index 406712a..3230be7 100644 --- a/lua/core/skill_type/trigger.lua +++ b/lua/core/skill_type/trigger.lua @@ -72,8 +72,11 @@ function TriggerSkill:doCost(event, target, player, data) self.cost_data = cost_data_bak if ret then - local cost_data = cost_data_bak or Util.DummyTable - local skill_data = {cost_data = cost_data, tos = cost_data.tos or {}, cards = cost_data.cards or {}} + local skill_data = {cost_data = cost_data_bak, tos = {}, cards = {}} + if type(cost_data_bak) == "table" then + if type(cost_data_bak.tos) == "table" then skill_data.tos = cost_data_bak.tos end + if type(cost_data_bak.cards) == "table" then skill_data.cards = cost_data_bak.cards end + end return room:useSkill(player, self, function() return self:use(event, target, player, data) end, skill_data) diff --git a/lua/server/events/skill.lua b/lua/server/events/skill.lua index c44cdea..2068a1d 100644 --- a/lua/server/events/skill.lua +++ b/lua/server/events/skill.lua @@ -8,13 +8,17 @@ function SkillEffect:main() local logic = room.logic local main_skill = skill.main_skill and skill.main_skill or skill skill_data = skill_data or Util.DummyTable + local cost_data = skill_data.cost_data or Util.DummyTable if player and not skill.cardSkill then player:revealBySkillName(main_skill.name) local tos = skill_data.tos or {} - local mute = (skill_data.mute ~= nil) and skill_data.mute or skill.mute - local no_indicate = (skill_data.no_indicate ~= nil) and skill_data.no_indicate or skill.no_indicate + local mute, no_indicate = skill.mute, skill.no_indicate + if type(cost_data) == "table" then + if cost_data.mute then mute = cost_data.mute end + if cost_data.no_indicate then no_indicate = cost_data.no_indicate end + end if not mute then if skill.attached_equip then local equip = Fk.all_card_types[skill.attached_equip] -- Gitee From 92bac0cda9b6a6b8ec6a049bee7a3265e965a225 Mon Sep 17 00:00:00 2001 From: seven <786852516@qq.com> Date: Mon, 19 Aug 2024 14:23:17 +0800 Subject: [PATCH 03/12] fix again --- lua/server/events/skill.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lua/server/events/skill.lua b/lua/server/events/skill.lua index 2068a1d..4703cd9 100644 --- a/lua/server/events/skill.lua +++ b/lua/server/events/skill.lua @@ -64,12 +64,11 @@ function SkillEffect:main() end local cost_data_bak = skill.cost_data - logic:trigger(fk.SkillEffect, player, main_skill, skill_data) + logic:trigger(fk.SkillEffect, player, main_skill) skill.cost_data = cost_data_bak local ret = effect_cb and effect_cb() or false - - logic:trigger(fk.AfterSkillEffect, player, main_skill, skill_data) + logic:trigger(fk.AfterSkillEffect, player, main_skill) return ret end -- Gitee From 559ff9785f3bb74f5559e97ae46adedb5487ce43 Mon Sep 17 00:00:00 2001 From: seven <786852516@qq.com> Date: Tue, 20 Aug 2024 14:35:34 +0800 Subject: [PATCH 04/12] =?UTF-8?q?=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lua/core/skill_type/trigger.lua | 5 ++++- lua/server/events/gameflow.lua | 5 +---- lua/server/room.lua | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lua/core/skill_type/trigger.lua b/lua/core/skill_type/trigger.lua index 3230be7..6c94820 100644 --- a/lua/core/skill_type/trigger.lua +++ b/lua/core/skill_type/trigger.lua @@ -74,7 +74,10 @@ function TriggerSkill:doCost(event, target, player, data) if ret then local skill_data = {cost_data = cost_data_bak, tos = {}, cards = {}} if type(cost_data_bak) == "table" then - if type(cost_data_bak.tos) == "table" then skill_data.tos = cost_data_bak.tos end + if type(cost_data_bak.tos) == "table" and #cost_data_bak.tos > 0 and type(cost_data_bak.tos[1]) == "number" and + room:getPlayerById(cost_data_bak.tos[1]) ~= nil then + skill_data.tos = cost_data_bak.tos + end if type(cost_data_bak.cards) == "table" then skill_data.cards = cost_data_bak.cards end end return room:useSkill(player, self, function() diff --git a/lua/server/events/gameflow.lua b/lua/server/events/gameflow.lua index 4e6c2b7..7d38493 100644 --- a/lua/server/events/gameflow.lua +++ b/lua/server/events/gameflow.lua @@ -369,6 +369,7 @@ function Phase:main() end, [Player.Play] = function() while not player.dead do + if player._phase_end then break end logic:trigger(fk.StartPlayCard, player, nil, true) room:notifyMoveFocus(player, "PlayCard") local result = room:doRequest(player, "PlayCard", player.id) @@ -378,10 +379,6 @@ function Phase:main() if type(useResult) == "table" then room:useCard(useResult) end - - if player._phase_end then - break - end end end, [Player.Discard] = function() diff --git a/lua/server/room.lua b/lua/server/room.lua index 99ff891..55453b1 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -3849,7 +3849,7 @@ end ---@param effect_cb fun() @ 实际要调用的函数 ---@param skill_data? table @ 技能的信息 function Room:useSkill(player, skill, effect_cb, skill_data) - return execGameEvent(GameEvent.SkillEffect, effect_cb, player, skill, skill_data or Util.DummyFunc) + return execGameEvent(GameEvent.SkillEffect, effect_cb, player, skill, skill_data or Util.DummyTable) end ---@param player ServerPlayer -- Gitee From c304f828e08c254551837bdb06496931709dbbc1 Mon Sep 17 00:00:00 2001 From: seven <786852516@qq.com> Date: Fri, 23 Aug 2024 22:15:35 +0800 Subject: [PATCH 05/12] AI --- lua/server/ai/random_ai.lua | 3 +-- lua/server/room.lua | 10 ++++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lua/server/ai/random_ai.lua b/lua/server/ai/random_ai.lua index deea7ff..e984db3 100644 --- a/lua/server/ai/random_ai.lua +++ b/lua/server/ai/random_ai.lua @@ -17,8 +17,7 @@ function RandomAI:useActiveSkill(skill, card) filter_func = Util.FalseFunc end - if self.command == "PlayCard" and card and card.class and card:isInstanceOf(Card) - and (not skill:canUse(player, card) or player:prohibitUse(card)) then + if self.command == "PlayCard" and (not skill:canUse(player, card) or (card and player:prohibitUse(card))) then return "" end diff --git a/lua/server/room.lua b/lua/server/room.lua index 55453b1..308078b 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -4216,4 +4216,14 @@ function Room:endTurn() self:setTag("endTurn", true) end +--清理遗留在处理区的卡牌 +---@param cards? integer[] @ 待清理的卡牌。不填则清理处理区所有牌 +---@param skillName? string @ 技能名 +function Room:cleanProcessingArea(cards, skillName) + local throw = cards and table.filter(cards, function(id) return self:getCardArea(id) == Card.Processing end) or self.processing_area + if #throw > 0 then + self:moveCardTo(throw, Card.DiscardPile, nil, fk.ReasonPutIntoDiscardPile, skillName) + end +end + return Room -- Gitee From 7bf3d0201b0514cb4509a7779caaef9aad8b80bf Mon Sep 17 00:00:00 2001 From: seven <786852516@qq.com> Date: Sun, 25 Aug 2024 11:55:54 +0800 Subject: [PATCH 06/12] interaction --- lua/server/ai/random_ai.lua | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lua/server/ai/random_ai.lua b/lua/server/ai/random_ai.lua index e984db3..055461d 100644 --- a/lua/server/ai/random_ai.lua +++ b/lua/server/ai/random_ai.lua @@ -21,6 +21,24 @@ function RandomAI:useActiveSkill(skill, card) return "" end + local interaction_data + if skill and skill.interaction then + skill.interaction.data = nil + interaction_data = skill:interaction() + if type(interaction_data) == "table" then + if interaction_data.type == "spin" then + interaction_data = math.random(interaction_data.from, interaction_data.to) + elseif interaction_data.type == "combo" then + interaction_data = interaction_data.default + else + -- use default data when handling custom interaction + interaction_data = interaction_data.default or interaction_data.default_choice or nil + end + end + if interaction_data == nil then return "" end + skill.interaction.data = interaction_data + end + local max_try_times = 100 local selected_targets = {} local selected_cards = {} @@ -57,6 +75,7 @@ function RandomAI:useActiveSkill(skill, card) subcards = selected_cards, }, targets = selected_targets, + interaction_data = interaction_data, } -- print(ret) return ret -- Gitee From 19a6df4cdf3f8bc45a6cf7548104de79bf69b9fa Mon Sep 17 00:00:00 2001 From: seven <786852516@qq.com> Date: Mon, 26 Aug 2024 12:34:27 +0800 Subject: [PATCH 07/12] fix --- lua/server/gamelogic.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/server/gamelogic.lua b/lua/server/gamelogic.lua index 9388b89..91c44c9 100644 --- a/lua/server/gamelogic.lua +++ b/lua/server/gamelogic.lua @@ -774,7 +774,7 @@ function GameLogic:damageByCardEffect(is_exact) local c_event = d_event:findParent(GameEvent.CardEffect, false, 2) if c_event == nil then return false end return damage.card == c_event.data[1].card and - (not is_exact or d_event.data[1].from.id == c_event.data[1].from) + (not is_exact or (damage.from or {}).id == c_event.data[1].from) end function GameLogic:dumpEventStack(detailed) -- Gitee From 436195ef735cf4283f99feb9198acbae67f8d9aa Mon Sep 17 00:00:00 2001 From: seven <786852516@qq.com> Date: Tue, 27 Aug 2024 15:33:46 +0800 Subject: [PATCH 08/12] =?UTF-8?q?AI=E5=8D=87=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lua/core/skill_type/active.lua | 10 +-- lua/server/ai/random_ai.lua | 158 +++++++++++++++++++++++++-------- 2 files changed, 124 insertions(+), 44 deletions(-) diff --git a/lua/core/skill_type/active.lua b/lua/core/skill_type/active.lua index 0fdc04f..89a3c9e 100644 --- a/lua/core/skill_type/active.lua +++ b/lua/core/skill_type/active.lua @@ -26,8 +26,8 @@ end -- 判断该技能是否可主动发动 ---@param player Player @ 使用者 ----@param card Card @ 牌 ----@param extra_data UseExtraData @ 额外数据 +---@param card? Card @ 牌,若该技能是卡牌的效果技能,需输入此值 +---@param extra_data? UseExtraData @ 额外数据 ---@return bool function ActiveSkill:canUse(player, card, extra_data) return self:isEffectable(player) @@ -46,8 +46,8 @@ end ---@param to_select integer @ 待选目标 ---@param selected integer[] @ 已选目标 ---@param selected_cards integer[] @ 已选牌 ----@param card Card @ 牌 ----@param extra_data UseExtraData @ 额外数据 +---@param card? Card @ 牌 +---@param extra_data? UseExtraData @ 额外数据 ---@return bool function ActiveSkill:targetFilter(to_select, selected, selected_cards, card, extra_data) return false @@ -217,7 +217,7 @@ end ---@param selected integer[] @ 已选目标 ---@param selected_cards integer[] @ 已选牌 ---@param player Player @ 使用者 ----@param card Card @ 牌 +---@param card? Card @ 牌 ---@return bool function ActiveSkill:feasible(selected, selected_cards, player, card) return #selected >= self:getMinTargetNum() and #selected <= self:getMaxTargetNum(player, card) diff --git a/lua/server/ai/random_ai.lua b/lua/server/ai/random_ai.lua index 055461d..4f96ab7 100644 --- a/lua/server/ai/random_ai.lua +++ b/lua/server/ai/random_ai.lua @@ -50,14 +50,9 @@ function RandomAI:useActiveSkill(skill, card) -- FIXME: ViewAsSkill can be buggy here for _ = 0, max_try_times do if skill:feasible(selected_targets, selected_cards, self.player, card) then break end - local avail_targets = table.filter(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:isProhibited(p, card) then - ret = false - end - end - return ret + local avail_targets = table.filter(room.alive_players, function(p) + return skill:targetFilter(p.id, selected_targets, selected_cards, card) + and (not card or not player:isProhibited(p, card)) 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) @@ -83,17 +78,20 @@ function RandomAI:useActiveSkill(skill, card) return "" end ----@param self RandomAI + ---@param skill ViewAsSkill -function RandomAI:useVSSkill(skill, pattern, cancelable, extra_data) +---@param cancelable? bool +---@param extra_data? table +---@param cardResponsing? bool +function RandomAI:useVSSkill(skill, pattern, cancelable, extra_data, cardResponsing) local player = self.player local room = self.room local precondition - if not skill then return nil end + if not skill then return "" end if self.command == "PlayCard" then precondition = skill:enabledAtPlay(player) - if not precondition then return nil end + if not precondition then return "" end local exp = Exppattern:Parse(skill.pattern) local cnames = {} for _, m in ipairs(exp.matchers) do @@ -105,31 +103,91 @@ function RandomAI:useVSSkill(skill, pattern, cancelable, extra_data) 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) + precondition = skill:enabledAtResponse(player, cardResponsing) and Exppattern:Parse(pattern):matchExp(skill.pattern) end - if (not precondition) or math.random() < 0.2 then return nil end + if (not precondition) or math.random() < 0.2 then return "" end + + local interaction_data + if skill.interaction then + skill.interaction.data = nil + interaction_data = skill:interaction() + if type(interaction_data) == "table" then + if interaction_data.type == "spin" then + interaction_data = math.random(interaction_data.from, interaction_data.to) + elseif interaction_data.type == "combo" then + interaction_data = interaction_data.default + else + -- use default data when handling custom interaction + interaction_data = interaction_data.default or interaction_data.default_choice or nil + end + end + if interaction_data == nil then return "" end + skill.interaction.data = interaction_data + end local selected_cards = {} + local selected_targets = {} + local card local max_try_time = 100 for _ = 0, max_try_time do + card = skill:viewAs(selected_cards) + if card then break end 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 + + if not card then return "" end + + if cardResponsing then + if not player:prohibitResponse(card) then + return json.encode{ + card = json.encode{ + skill = skill.name, + subcards = selected_cards, + }, + targets = {}, + interaction_data = interaction_data, } end + return "" end - return nil + + if player:prohibitUse(card) then return "" end + + if self.command == "PlayCard" then + if not player:canUse(card) then return "" end + + for _ = 0, max_try_time do + if card.skill:feasible(selected_targets, selected_cards, player, card) then break end + local avail_targets = table.filter(room.alive_players, function(p) + return not table.contains(selected_targets, p.id) + and card.skill:targetFilter(p.id, selected_targets, selected_cards, card) + and not player:isProhibited(p, card) + end) + if #avail_targets == 0 then break end + table.insert(selected_targets, table.random(avail_targets).id) + end + if card.skill:feasible(selected_targets, selected_cards, player, card) then + local ret = json.encode{ + card = json.encode{ + skill = skill.name, + subcards = selected_cards, + }, + targets = selected_targets, + interaction_data = interaction_data, + } + return ret + end + else + -- TODO: handle vs when at response + end + + return "" end ---@type table @@ -145,7 +203,7 @@ random_cb["AskForUseActiveSkill"] = function(self, jsonData) skill[k] = v end if skill:isInstanceOf(ViewAsSkill) then - return RandomAI.useVSSkill(skill) + return ""--self:useVSSkill(skill, skill.name, cancelable, extra_data) end return RandomAI.useActiveSkill(self, skill) end @@ -160,9 +218,19 @@ random_cb["AskForUseCard"] = function(self, jsonData) local card_name = data[1] local pattern = data[2] or card_name local cancelable = data[4] or true + local extra_data = data[5] or Util.DummyTable local exp = Exppattern:Parse(pattern) + + -- TODO: ng that 'must_targets' & 'exclusive_targets' should be rebuilt later + local limited_targets = {} + if type(extra_data.must_targets) == "table" then + table.insertTableIfNeed(limited_targets, extra_data.must_targets) + end + if type(extra_data.exclusive_targets) == "table" then + table.insertTableIfNeed(limited_targets, extra_data.exclusive_targets) + end - local avail_cards = table.map(player:getCardIds("he"), Util.Id2CardMapper) + local avail_cards = table.map(player:getHandlyIds(), Util.Id2CardMapper) avail_cards = table.filter(avail_cards, function(c) return exp:match(c) and not player:prohibitUse(c) end) @@ -177,43 +245,55 @@ random_cb["AskForUseCard"] = function(self, jsonData) 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 + if skill:feasible(selected_targets, { card.id }, player, card) then return json.encode{ card = table.random(avail_cards).id, targets = selected_targets, } end local avail_targets = table.filter(self.room:getAlivePlayers(), function(p) - return skill:targetFilter(p.id, selected_targets, {card.id}, card or Fk:cloneCard'zixing') + return skill:targetFilter(p.id, selected_targets, {card.id}, card, extra_data) + and (#limited_targets == 0 or table.contains(limited_targets, p.id)) end) - avail_targets = table.map(avail_targets, function(p) return p.id end) - - if #avail_targets == 0 and #avail_cards == 0 then break end - table.insertIfNeed(selected_targets, table.random(avail_targets)) + if #avail_targets == 0 then break end + table.insertIfNeed(selected_targets, table.random(avail_targets).id) end end end + -- TODO: handle vs skill return "" end random_cb["AskForResponseCard"] = function(self, jsonData) local data = json.decode(jsonData) local pattern = data[2] - local cancelable = true + local cancelable = data[4] or true + local extra_data = data[5] or Util.DummyTable + local exp = Exppattern:Parse(pattern) - local avail_cards = table.filter(self.player:getCardIds{ Player.Hand, Player.Equip }, function(id) + local cards = table.filter(self.player:getCardIds("he&"), 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 + + local vss = table.filter(self.player:getAllSkills(), function(s) + return s:isInstanceOf(ViewAsSkill) + end) + table.insertTable(cards, vss) + + while #cards > 0 do + local sth = table.remove(cards, math.random(#cards)) + if type(sth) == "number" then + return json.encode{ card = sth, targets = {} } + else + local ret = self:useVSSkill(sth, pattern, cancelable, extra_data, true) + if ret ~= "" then return ret end + end + end return "" end random_cb["PlayCard"] = function(self, jsonData) - local cards = table.map(self.player:getCardIds(Player.Hand), Util.Id2CardMapper) + local cards = table.map(self.player:getCardIds("h&"), Util.Id2CardMapper) local actives = table.filter(self.player:getAllSkills(), function(s) return s:isInstanceOf(ActiveSkill) end) @@ -246,7 +326,7 @@ random_cb["PlayCard"] = function(self, jsonData) local vs = sth if math.random() > 0.20 then local ret = self:useVSSkill(vs) - -- TODO: handle vs result + if ret ~= "" then return ret end end table.removeOne(cards, vs) end -- Gitee From bbcf462fc0f15dee77cb503475646a686b148f8b Mon Sep 17 00:00:00 2001 From: seven <786852516@qq.com> Date: Tue, 27 Aug 2024 21:55:42 +0800 Subject: [PATCH 09/12] AI --- lua/core/skill_type/active.lua | 16 +-- lua/server/ai/random_ai.lua | 190 +++++++++++++++++++-------------- 2 files changed, 116 insertions(+), 90 deletions(-) diff --git a/lua/core/skill_type/active.lua b/lua/core/skill_type/active.lua index 89a3c9e..fa3e4f6 100644 --- a/lua/core/skill_type/active.lua +++ b/lua/core/skill_type/active.lua @@ -83,8 +83,8 @@ function ActiveSkill:getMinTargetNum() end -- 获得技能的最大目标数 ----@param player Player @ 使用者 ----@param card Card @ 牌 +---@param player? Player @ 使用者 +---@param card? Card @ 牌 ---@return number @ 最大目标数 function ActiveSkill:getMaxTargetNum(player, card) local ret @@ -99,11 +99,13 @@ function ActiveSkill:getMaxTargetNum(player, card) ret = ret[#ret] end - local status_skills = Fk:currentRoom().status_skills[TargetModSkill] or Util.DummyTable - for _, skill in ipairs(status_skills) do - local correct = skill:getExtraTargetNum(player, self, card) - if correct == nil then correct = 0 end - ret = ret + correct + if player and card then + local status_skills = Fk:currentRoom().status_skills[TargetModSkill] or Util.DummyTable + for _, skill in ipairs(status_skills) do + local correct = skill:getExtraTargetNum(player, self, card) + if correct == nil then correct = 0 end + ret = ret + correct + end end return ret end diff --git a/lua/server/ai/random_ai.lua b/lua/server/ai/random_ai.lua index 4f96ab7..7fac2e5 100644 --- a/lua/server/ai/random_ai.lua +++ b/lua/server/ai/random_ai.lua @@ -6,17 +6,14 @@ local RandomAI = AI:subclass("RandomAI") ---@param self RandomAI ---@param skill ActiveSkill ---@param card? Card -function RandomAI:useActiveSkill(skill, card) +---@param extra_data? table +function RandomAI:useActiveSkill(skill, card, extra_data) local room = self.room local player = self.player + extra_data = extra_data or Util.DummyTable if skill:isInstanceOf(ViewAsSkill) then return "" end - local filter_func = skill.cardFilter - if card then - filter_func = Util.FalseFunc - end - if self.command == "PlayCard" and (not skill:canUse(player, card) or (card and player:prohibitUse(card))) then return "" end @@ -42,28 +39,55 @@ function RandomAI:useActiveSkill(skill, card) local max_try_times = 100 local selected_targets = {} local selected_cards = {} - -- FIXME: ... - -- local min = skill:getMinTargetNum() - -- local max = skill:getMaxTargetNum(player, card) - -- local min_card = skill:getMinCardNum() - -- local max_card = skill:getMaxCardNum() - -- FIXME: ViewAsSkill can be buggy here + + -- TODO: ng that 'must_targets' & 'exclusive_targets' should be rebuilt later + local limited_targets = {} + for _, name in ipairs({"must_targets","exclusive_targets","include_targets"}) do + if type(extra_data[name]) == "table" then + table.insertTableIfNeed(limited_targets, extra_data[name]) + end + end + + local all_cards = player:getCardIds{ Player.Hand, Player.Equip } + if skill.expand_pile then + if type(skill.expand_pile) == "string" then + table.insertTableIfNeed(all_cards, player.special_cards[skill.expand_pile] or {}) + elseif type(skill.expand_pile) == "table" then + table.insertTableIfNeed(all_cards, skill.expand_pile) + end + end + + --local max_target_num = skill:getMaxTargetNum(player, card) + local card_filter_func = card and Util.FalseFunc or skill.cardFilter + local firstTry for _ = 0, max_try_times do - if skill:feasible(selected_targets, selected_cards, self.player, card) then break end + if not firstTry and skill:feasible(selected_targets, selected_cards, self.player, card) then + firstTry = {table.simpleClone(selected_targets), table.simpleClone(selected_cards)} + end + if firstTry and math.random() < 0.1 then break end local avail_targets = table.filter(room.alive_players, function(p) - return skill:targetFilter(p.id, selected_targets, selected_cards, card) + return not table.contains(selected_targets, p.id) and (#limited_targets == 0 or table.contains(limited_targets, p.id)) + and skill:targetFilter(p.id, selected_targets, selected_cards, card) and (not card or not player:isProhibited(p, card)) 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) + local avail_cards = table.filter(all_cards, function(id) + return not table.contains(selected_cards, id) and card_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)) + local random_list = table.connect(avail_targets, avail_cards) + if #random_list == 0 then break end + local randomIndex = math.random(#random_list) + if randomIndex <= #avail_targets then + table.insertIfNeed(selected_targets, random_list[randomIndex].id) + else + table.insertIfNeed(selected_cards, random_list[randomIndex]) + end end - if skill:feasible(selected_targets, selected_cards, self.player, card) then + local feasibleCheck = function () return skill:feasible(selected_targets, selected_cards, self.player, card) end + if firstTry and not feasibleCheck() then + selected_targets = firstTry[1] + selected_cards = firstTry[2] + end + if feasibleCheck() then local ret = json.encode{ card = card and card.id or json.encode{ skill = skill.name, @@ -72,7 +96,6 @@ function RandomAI:useActiveSkill(skill, card) targets = selected_targets, interaction_data = interaction_data, } - -- print(ret) return ret end return "" @@ -80,6 +103,7 @@ end ---@param skill ViewAsSkill +---@param pattern? string ---@param cancelable? bool ---@param extra_data? table ---@param cardResponsing? bool @@ -87,9 +111,10 @@ function RandomAI:useVSSkill(skill, pattern, cancelable, extra_data, cardRespons local player = self.player local room = self.room local precondition + cancelable = cancelable or (cancelable == nil) if not skill then return "" end - if self.command == "PlayCard" then + if self.command == "PlayCard" or not pattern then precondition = skill:enabledAtPlay(player) if not precondition then return "" end local exp = Exppattern:Parse(skill.pattern) @@ -99,14 +124,14 @@ function RandomAI:useVSSkill(skill, pattern, cancelable, extra_data, cardRespons end for _, n in ipairs(cnames) do local c = Fk:cloneCard(n) - precondition = c.skill:canUse(Self, c) + precondition = c.skill:canUse(player, c, extra_data) if precondition then break end end else precondition = skill:enabledAtResponse(player, cardResponsing) and Exppattern:Parse(pattern):matchExp(skill.pattern) end - if (not precondition) or math.random() < 0.2 then return "" end + if (not precondition) or (cancelable and math.random() < 0.2) then return "" end local interaction_data if skill.interaction then @@ -130,12 +155,20 @@ function RandomAI:useVSSkill(skill, pattern, cancelable, extra_data, cardRespons local selected_targets = {} local card local max_try_time = 100 + local all_cards = player:getCardIds{ Player.Hand, Player.Equip } + if skill.expand_pile then + if type(skill.expand_pile) == "string" then + table.insertTableIfNeed(all_cards, player.special_cards[skill.expand_pile] or {}) + elseif type(skill.expand_pile) == "table" then + table.insertTableIfNeed(all_cards, skill.expand_pile) + end + end for _ = 0, max_try_time do card = skill:viewAs(selected_cards) if card then break end - local avail_cards = table.filter(player:getCardIds{ Player.Hand, Player.Equip }, function(id) - return skill:cardFilter(id, selected_cards) + local avail_cards = table.filter(all_cards, function(id) + return not table.contains(selected_cards, id) and skill:cardFilter(id, selected_cards) end) if #avail_cards == 0 then break end table.insert(selected_cards, table.random(avail_cards)) @@ -159,20 +192,19 @@ function RandomAI:useVSSkill(skill, pattern, cancelable, extra_data, cardRespons if player:prohibitUse(card) then return "" end - if self.command == "PlayCard" then - if not player:canUse(card) then return "" end + if player:canUse(card, extra_data) or table.contains({"jink", "peach"}, pattern) then for _ = 0, max_try_time do if card.skill:feasible(selected_targets, selected_cards, player, card) then break end local avail_targets = table.filter(room.alive_players, function(p) return not table.contains(selected_targets, p.id) - and card.skill:targetFilter(p.id, selected_targets, selected_cards, card) + and card.skill:targetFilter(p.id, selected_targets, selected_cards, card, extra_data) and not player:isProhibited(p, card) end) if #avail_targets == 0 then break end table.insert(selected_targets, table.random(avail_targets).id) end - if card.skill:feasible(selected_targets, selected_cards, player, card) then + if card.skill:feasible(selected_targets, selected_cards, player, card, extra_data) then local ret = json.encode{ card = json.encode{ skill = skill.name, @@ -183,8 +215,6 @@ function RandomAI:useVSSkill(skill, pattern, cancelable, extra_data, cardRespons } return ret end - else - -- TODO: handle vs when at response end return "" @@ -203,7 +233,7 @@ random_cb["AskForUseActiveSkill"] = function(self, jsonData) skill[k] = v end if skill:isInstanceOf(ViewAsSkill) then - return ""--self:useVSSkill(skill, skill.name, cancelable, extra_data) + return self:useVSSkill(skill, nil, cancelable, extra_data) end return RandomAI.useActiveSkill(self, skill) end @@ -212,55 +242,52 @@ random_cb["AskForSkillInvoke"] = function(self, jsonData) return table.random{"1", ""} end +random_cb["AskForChoice"] = function(self, jsonData) + local data = json.decode(jsonData) + local choices = data[1] + if table.contains(choices, "Cancel") and #choices > 1 and math.random() < 0.6 then + table.removeOne(choices, "Cancel") + end + return table.random(choices) +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 cancelable = data[4] local extra_data = data[5] or Util.DummyTable - local exp = Exppattern:Parse(pattern) - - -- TODO: ng that 'must_targets' & 'exclusive_targets' should be rebuilt later - local limited_targets = {} - if type(extra_data.must_targets) == "table" then - table.insertTableIfNeed(limited_targets, extra_data.must_targets) - end - if type(extra_data.exclusive_targets) == "table" then - table.insertTableIfNeed(limited_targets, extra_data.exclusive_targets) + + if card_name == "nullification" then + if extra_data.effectTo and extra_data.effectTo ~= player.id and math.random() < 0.8 then + return "" + end end - local avail_cards = table.map(player:getHandlyIds(), Util.Id2CardMapper) - avail_cards = table.filter(avail_cards, function(c) + if (cancelable and math.random() < 0.15) then return "" end + + local cards = table.map(self.player:getCardIds("he&"), Util.Id2CardMapper) + local exp = Exppattern:Parse(pattern) + cards = table.filter(cards, function(c) return exp:match(c) and not player:prohibitUse(c) end) - if #avail_cards > 0 then - if math.random() < 0.25 then return "" 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 }, player, card) then - return json.encode{ - card = table.random(avail_cards).id, - targets = selected_targets, - } - end - local avail_targets = table.filter(self.room:getAlivePlayers(), function(p) - return skill:targetFilter(p.id, selected_targets, {card.id}, card, extra_data) - and (#limited_targets == 0 or table.contains(limited_targets, p.id)) - end) - if #avail_targets == 0 then break end - table.insertIfNeed(selected_targets, table.random(avail_targets).id) - end + local vss = table.filter(player:getAllSkills(), function(s) + return s:isInstanceOf(ViewAsSkill) + end) + table.insertTable(cards, vss) + + while #cards > 0 do + local sth = table.remove(cards, math.random(#cards)) + if sth:isInstanceOf(Card) then + local ret = self:useActiveSkill(sth.skill, sth, extra_data) + if ret ~= "" then return ret end + else + local ret = self:useVSSkill(sth, pattern, cancelable, extra_data) + if ret ~= "" then return ret end end end - -- TODO: handle vs skill + return "" end @@ -269,21 +296,23 @@ random_cb["AskForResponseCard"] = function(self, jsonData) local pattern = data[2] local cancelable = data[4] or true local extra_data = data[5] or Util.DummyTable + local player = self.player + local cards = table.map(self.player:getCardIds("he&"), Util.Id2CardMapper) local exp = Exppattern:Parse(pattern) - local cards = table.filter(self.player:getCardIds("he&"), function(id) - return exp:match(Fk:getCardById(id)) + cards = table.filter(cards, function(c) + return exp:match(c) and not player:prohibitResponse(c) end) - local vss = table.filter(self.player:getAllSkills(), function(s) + local vss = table.filter(player:getAllSkills(), function(s) return s:isInstanceOf(ViewAsSkill) end) table.insertTable(cards, vss) while #cards > 0 do local sth = table.remove(cards, math.random(#cards)) - if type(sth) == "number" then - return json.encode{ card = sth, targets = {} } + if sth:isInstanceOf(Card) then + return json.encode{ card = sth.id, targets = {} } else local ret = self:useVSSkill(sth, pattern, cancelable, extra_data, true) if ret ~= "" then return ret end @@ -304,16 +333,13 @@ random_cb["PlayCard"] = function(self, jsonData) table.insertTable(cards, vss) while #cards > 0 do - local sth = table.random(cards) + local sth = table.remove(cards, math.random(#cards)) if sth:isInstanceOf(Card) then local card = sth local skill = card.skill ---@type ActiveSkill if math.random() > 0.15 then local ret = RandomAI.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 @@ -321,14 +347,12 @@ random_cb["PlayCard"] = function(self, jsonData) local ret = RandomAI.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 = self:useVSSkill(vs) if ret ~= "" then return ret end end - table.removeOne(cards, vs) end end -- Gitee From 22bc04da3ccf6ed1deee4d1d05d9a5c8f303ae03 Mon Sep 17 00:00:00 2001 From: seven <786852516@qq.com> Date: Wed, 28 Aug 2024 14:07:26 +0800 Subject: [PATCH 10/12] =?UTF-8?q?=E6=A0=87=E8=AE=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lua/core/card.lua | 8 ++++++++ lua/server/ai/random_ai.lua | 41 +++++++++++++++++++++++++++++++++++-- lua/server/room.lua | 29 ++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/lua/core/card.lua b/lua/core/card.lua index 10faad6..9b86283 100644 --- a/lua/core/card.lua +++ b/lua/core/card.lua @@ -514,4 +514,12 @@ function Card.static:getIdList(c) return ret end +--- 获得卡牌的标记并初始化为表 +---@param mark string @ 标记 +---@return table +function Card:getTableMark(mark) + local ret = self:getMark(mark) + return type(ret) == "table" and ret or {} +end + return Card diff --git a/lua/server/ai/random_ai.lua b/lua/server/ai/random_ai.lua index 7fac2e5..266a772 100644 --- a/lua/server/ai/random_ai.lua +++ b/lua/server/ai/random_ai.lua @@ -226,6 +226,7 @@ local random_cb = {} random_cb["AskForUseActiveSkill"] = function(self, jsonData) local data = json.decode(jsonData) local skill = Fk.skills[data[1]] + if not skill then return "" end local cancelable = data[3] if cancelable and math.random() < 0.25 then return "" end local extra_data = data[4] @@ -235,11 +236,37 @@ random_cb["AskForUseActiveSkill"] = function(self, jsonData) if skill:isInstanceOf(ViewAsSkill) then return self:useVSSkill(skill, nil, cancelable, extra_data) end - return RandomAI.useActiveSkill(self, skill) + local player = self.player + if skill.name == "choose_cards_skill" then + local exp = Exppattern:Parse(extra_data.pattern) + local all_cards = table.filter(player:getCardIds(extra_data.include_equip and "he" or "h"), function(cid) + return exp:match(Fk:getCardById(cid)) + end) + if #all_cards == 0 then return "" end + local maxNum = extra_data.num + local minNum = extra_data.min_num + local cards = table.random(all_cards, math.random(minNum, maxNum)) + return json.encode{ + card = json.encode{ + skill = skill.name, + subcards = cards, + }, + targets = {}, + } + end + return self:useActiveSkill(skill) end random_cb["AskForSkillInvoke"] = function(self, jsonData) - return table.random{"1", ""} + local skill_name, prompt = table.unpack(json.decode(jsonData)) + local chance = 0.55 + if Fk.skills[skill_name] ~= nil and self.player:hasSkill(skill_name) then + chance = 0.8 + end + if math.random() < chance then + return "1" + end + return "" end random_cb["AskForChoice"] = function(self, jsonData) @@ -256,13 +283,23 @@ random_cb["AskForUseCard"] = function(self, jsonData) local data = json.decode(jsonData) local card_name = data[1] local pattern = data[2] or card_name + local prompt = data[3] local cancelable = data[4] local extra_data = data[5] or Util.DummyTable if card_name == "nullification" then + -- This doesn't seem to work if extra_data.effectTo and extra_data.effectTo ~= player.id and math.random() < 0.8 then return "" end + local split = string.split(prompt, ":") + if #split > 1 and split[1] == "#AskForNullificationWithoutTo" and tonumber(split[2]) == player.id then + return "" + end + elseif card_name == "peach" then + if type(extra_data.must_targets) == "table" and extra_data.must_targets[1] ~= player.id and math.random() < 0.8 then + return "" + end end if (cancelable and math.random() < 0.15) then return "" end diff --git a/lua/server/room.lua b/lua/server/room.lua index 308078b..84f95ea 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -4226,4 +4226,33 @@ function Room:cleanProcessingArea(cards, skillName) end end +--- 为角色或牌的表型标记添加值 +---@param sth ServerPlayer|Card @ 更新标记的玩家或卡牌 +---@param mark string @ 标记的名称 +---@param value any @ 要增加的值 +function Room:addTableMark(sth, mark, value) + local t = sth:getTableMark(mark) + table.insert(t, value) + if sth:isInstanceOf(Card) then + self:setCardMark(sth, mark, t) + else + self:setPlayerMark(sth, mark, t) + end +end + +--- 为角色或牌的表型标记移除值 +---@param sth ServerPlayer @ 更新标记的玩家或卡牌 +---@param mark string @ 标记的名称 +---@param value any @ 要移除的值 +function Room:removeTableMark(sth, mark, value) + local t = sth:getTableMark(mark) + table.removeOne(t, value) + if sth:isInstanceOf(Card) then + self:setCardMark(sth, mark, #t > 0 and t or 0) + else + self:setPlayerMark(sth, mark, #t > 0 and t or 0) + end +end + + return Room -- Gitee From ca8866b4701e1915235dc04c2a929785b83f8c55 Mon Sep 17 00:00:00 2001 From: seven <786852516@qq.com> Date: Wed, 28 Aug 2024 15:24:10 +0800 Subject: [PATCH 11/12] AI --- lua/server/ai/random_ai.lua | 32 +++++++++++++++----------------- lua/server/serverplayer.lua | 1 + 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/lua/server/ai/random_ai.lua b/lua/server/ai/random_ai.lua index 266a772..2ef243a 100644 --- a/lua/server/ai/random_ai.lua +++ b/lua/server/ai/random_ai.lua @@ -103,7 +103,7 @@ end ---@param skill ViewAsSkill ----@param pattern? string +---@param pattern? string @ no 'pattern' means it needs to pass the 'canUse' check ---@param cancelable? bool ---@param extra_data? table ---@param cardResponsing? bool @@ -112,9 +112,10 @@ function RandomAI:useVSSkill(skill, pattern, cancelable, extra_data, cardRespons local room = self.room local precondition cancelable = cancelable or (cancelable == nil) + extra_data = extra_data or Util.DummyTable if not skill then return "" end - if self.command == "PlayCard" or not pattern then + if not pattern then precondition = skill:enabledAtPlay(player) if not precondition then return "" end local exp = Exppattern:Parse(skill.pattern) @@ -192,12 +193,19 @@ function RandomAI:useVSSkill(skill, pattern, cancelable, extra_data, cardRespons if player:prohibitUse(card) then return "" end - if player:canUse(card, extra_data) or table.contains({"jink", "peach"}, pattern) then + if pattern or player:canUse(card, extra_data) then + + local limited_targets = {} + for _, name in ipairs({"must_targets","exclusive_targets","include_targets"}) do + if type(extra_data[name]) == "table" then + table.insertTableIfNeed(limited_targets, extra_data[name]) + end + end for _ = 0, max_try_time do if card.skill:feasible(selected_targets, selected_cards, player, card) then break end local avail_targets = table.filter(room.alive_players, function(p) - return not table.contains(selected_targets, p.id) + return not table.contains(selected_targets, p.id) and (#limited_targets == 0 or table.contains(limited_targets, p.id)) and card.skill:targetFilter(p.id, selected_targets, selected_cards, card, extra_data) and not player:isProhibited(p, card) end) @@ -239,13 +247,12 @@ random_cb["AskForUseActiveSkill"] = function(self, jsonData) local player = self.player if skill.name == "choose_cards_skill" then local exp = Exppattern:Parse(extra_data.pattern) - local all_cards = table.filter(player:getCardIds(extra_data.include_equip and "he" or "h"), function(cid) + local cards = table.filter(player:getCardIds(extra_data.include_equip and "he" or "h"), function(cid) return exp:match(Fk:getCardById(cid)) end) - if #all_cards == 0 then return "" end local maxNum = extra_data.num local minNum = extra_data.min_num - local cards = table.random(all_cards, math.random(minNum, maxNum)) + cards = table.random(cards, math.random(minNum, maxNum)) return json.encode{ card = json.encode{ skill = skill.name, @@ -287,16 +294,7 @@ random_cb["AskForUseCard"] = function(self, jsonData) local cancelable = data[4] local extra_data = data[5] or Util.DummyTable - if card_name == "nullification" then - -- This doesn't seem to work - if extra_data.effectTo and extra_data.effectTo ~= player.id and math.random() < 0.8 then - return "" - end - local split = string.split(prompt, ":") - if #split > 1 and split[1] == "#AskForNullificationWithoutTo" and tonumber(split[2]) == player.id then - return "" - end - elseif card_name == "peach" then + if card_name == "peach" then if type(extra_data.must_targets) == "table" and extra_data.must_targets[1] ~= player.id and math.random() < 0.8 then return "" end diff --git a/lua/server/serverplayer.lua b/lua/server/serverplayer.lua index fed1620..c47aa21 100644 --- a/lua/server/serverplayer.lua +++ b/lua/server/serverplayer.lua @@ -331,6 +331,7 @@ function ServerPlayer:turnOver() self.room.logic:trigger(fk.TurnedOver, self) end +---@param cards integer|integer[]|Card|Card[] function ServerPlayer:showCards(cards) cards = Card:getIdList(cards) for _, id in ipairs(cards) do -- Gitee From 004c2a961424c5f9efdc7bb90930d41daeb79b9a Mon Sep 17 00:00:00 2001 From: seven <786852516@qq.com> Date: Fri, 30 Aug 2024 21:58:39 +0800 Subject: [PATCH 12/12] room_settings --- lua/server/room.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lua/server/room.lua b/lua/server/room.lua index 84f95ea..297533c 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -4241,7 +4241,7 @@ function Room:addTableMark(sth, mark, value) end --- 为角色或牌的表型标记移除值 ----@param sth ServerPlayer @ 更新标记的玩家或卡牌 +---@param sth ServerPlayer|Card @ 更新标记的玩家或卡牌 ---@param mark string @ 标记的名称 ---@param value any @ 要移除的值 function Room:removeTableMark(sth, mark, value) @@ -4255,4 +4255,10 @@ function Room:removeTableMark(sth, mark, value) end +function Room:__index(k) + if k == "room_settings" then + return self.settings + end +end + return Room -- Gitee