diff --git a/Fk/Pages/Room.qml b/Fk/Pages/Room.qml index 501e23bc..c4844cc8 100644 --- a/Fk/Pages/Room.qml +++ b/Fk/Pages/Room.qml @@ -483,7 +483,7 @@ Item { && JSON.parse(Backend.callLuaFunction("GetPlayerHandcards", [Self.id])).includes(card)) { const skills = JSON.parse(Backend.callLuaFunction("GetCardSpecialSkills", [card])); - if (JSON.parse(Backend.callLuaFunction("CanUseCard", [card, Self.id]))) { + if (JSON.parse(Backend.callLuaFunction("CanUseCard", [card, Self.id, JSON.stringify(roomScene.extra_data)]))) { skills.unshift("_normal_use"); } specialCardSkills.model = skills; diff --git a/Fk/Pages/RoomLogic.js b/Fk/Pages/RoomLogic.js index 3f7a789e..faa991df 100644 --- a/Fk/Pages/RoomLogic.js +++ b/Fk/Pages/RoomLogic.js @@ -570,7 +570,7 @@ function enableTargets(card) { // card: int | { skill: string, subcards: int[] } const id = photo.playerid; const ret = JSON.parse(Backend.callLuaFunction( "CanUseCardToTarget", - [card, id, selected_targets] + [card, id, selected_targets, JSON.stringify(roomScene.extra_data)] )); photo.selectable = ret; if (roomScene.extra_data instanceof Object) { @@ -603,7 +603,7 @@ function enableTargets(card) { // card: int | { skill: string, subcards: int[] } )) && (roomScene.autoPending || !JSON.parse(Backend.callLuaFunction( "CardProhibitedUse", [card]))); } else if (okButton.enabled && roomScene.state === "playing") { - okButton.enabled = JSON.parse(Backend.callLuaFunction("CanUseCard", [card, Self.id])); + okButton.enabled = JSON.parse(Backend.callLuaFunction("CanUseCard", [card, Self.id, JSON.stringify(roomScene.extra_data)])); } if (okButton.enabled) { if (roomScene.extra_data instanceof Object) { @@ -648,7 +648,7 @@ function updateSelectedTargets(playerid, selected) { const id = photo.playerid; const ret = JSON.parse(Backend.callLuaFunction( "CanUseCardToTarget", - [card, id, selected_targets] + [card, id, selected_targets, JSON.stringify(roomScene.extra_data)] )); photo.selectable = ret; if (roomScene.extra_data instanceof Object) { @@ -681,7 +681,7 @@ function updateSelectedTargets(playerid, selected) { )) && (roomScene.autoPending || !JSON.parse(Backend.callLuaFunction( "CardProhibitedUse", [card]))); } else if (okButton.enabled && roomScene.state === "playing") { - okButton.enabled = JSON.parse(Backend.callLuaFunction("CanUseCard", [card, Self.id])); + okButton.enabled = JSON.parse(Backend.callLuaFunction("CanUseCard", [card, Self.id, JSON.stringify(roomScene.extra_data)])); } if (okButton.enabled) { if (roomScene.extra_data instanceof Object) { diff --git a/Fk/RoomElement/Dashboard.qml b/Fk/RoomElement/Dashboard.qml index 53986a8f..61dea611 100644 --- a/Fk/RoomElement/Dashboard.qml +++ b/Fk/RoomElement/Dashboard.qml @@ -223,14 +223,14 @@ RowLayout { const ids = [], cards = handcardAreaItem.cards; for (let i = 0; i < cards.length; i++) { cards[i].prohibitReason = ""; - if (JSON.parse(Backend.callLuaFunction("CanUseCard", [cards[i].cid, Self.id]))) { + if (JSON.parse(Backend.callLuaFunction("CanUseCard", [cards[i].cid, Self.id, JSON.stringify(roomScene.extra_data)]))) { ids.push(cards[i].cid); } else { // cannot use? considering special_skills const skills = JSON.parse(Backend.callLuaFunction("GetCardSpecialSkills", [cards[i].cid])); for (let j = 0; j < skills.length; j++) { const s = skills[j]; - if (JSON.parse(Backend.callLuaFunction("ActiveCanUse", [s]))) { + if (JSON.parse(Backend.callLuaFunction("ActiveCanUse", [s, JSON.stringify(roomScene.extra_data)]))) { ids.push(cards[i].cid); break; } @@ -475,7 +475,7 @@ RowLayout { continue; } - item.enabled = JSON.parse(Backend.callLuaFunction("ActiveCanUse", [item.orig])); + item.enabled = JSON.parse(Backend.callLuaFunction("ActiveCanUse", [item.orig, JSON.stringify(roomScene.extra_data)])); } } diff --git a/Fk/RoomElement/Photo.qml b/Fk/RoomElement/Photo.qml index 7c111b94..869d0e60 100644 --- a/Fk/RoomElement/Photo.qml +++ b/Fk/RoomElement/Photo.qml @@ -247,14 +247,15 @@ Item { } OpacityMask { + id: photoMaskEffect anchors.fill: photoMask source: generalImgItem maskSource: photoMask } Colorize { - anchors.fill: photoMask - source: generalImgItem + anchors.fill: photoMaskEffect + source: photoMaskEffect saturation: 0 visible: root.dead || root.surrendered } diff --git a/audio/system/ice_damage.mp3 b/audio/system/ice_damage.mp3 new file mode 100644 index 00000000..bb69be22 Binary files /dev/null and b/audio/system/ice_damage.mp3 differ diff --git a/audio/system/ice_damage2.mp3 b/audio/system/ice_damage2.mp3 new file mode 100644 index 00000000..049ebf35 Binary files /dev/null and b/audio/system/ice_damage2.mp3 differ diff --git a/lua/client/client_util.lua b/lua/client/client_util.lua index 07809821..55ae80dc 100644 --- a/lua/client/client_util.lua +++ b/lua/client/client_util.lua @@ -251,8 +251,10 @@ end ---@param card string | integer ---@param player integer -function CanUseCard(card, player) +---@param extra_data_str string +function CanUseCard(card, player, extra_data_str) local c ---@type Card + local extra_data = extra_data_str == "" and nil or json.decode(extra_data_str) if type(card) == "number" then c = Fk:getCardById(card) else @@ -271,13 +273,13 @@ function CanUseCard(card, player) end player = ClientInstance:getPlayerById(player) - local ret = c.skill:canUse(player, c) + local ret = c.skill:canUse(player, c, extra_data) 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 + if c.skill:targetFilter(p.id, {}, {}, c, extra_data) then return "true" end end @@ -311,7 +313,9 @@ 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) +---@param extra_data_str string @ extra data +function CanUseCardToTarget(card, to_select, selected, extra_data_str) + local extra_data = extra_data_str == "" and nil or json.decode(extra_data_str) if ClientInstance:getPlayerById(to_select).dead then return "false" end @@ -322,10 +326,10 @@ function CanUseCardToTarget(card, to_select, selected) selected_cards = {card} else local t = json.decode(card) - return ActiveTargetFilter(t.skill, to_select, selected, t.subcards) + return ActiveTargetFilter(t.skill, to_select, selected, t.subcards, extra_data) end - local ret = c.skill:targetFilter(to_select, selected, selected_cards, c) + local ret = c.skill:targetFilter(to_select, selected, selected_cards, c, extra_data) ret = ret and not Self:isProhibited(Fk:currentRoom():getPlayerById(to_select), c) return json.encode(ret) end @@ -392,12 +396,13 @@ function GetSkillData(skill_name) } end -function ActiveCanUse(skill_name) +function ActiveCanUse(skill_name, extra_data_str) + local extra_data = extra_data_str == "" and nil or json.decode(extra_data_str) local skill = Fk.skills[skill_name] local ret = false if skill then if skill:isInstanceOf(ActiveSkill) then - ret = skill:canUse(Self) + ret = skill:canUse(Self, extra_data) elseif skill:isInstanceOf(ViewAsSkill) then ret = skill:enabledAtPlay(Self) if ret then @@ -414,7 +419,7 @@ function ActiveCanUse(skill_name) for _, n in ipairs(cnames) do local c = Fk:cloneCard(n) c.skillName = skill_name - ret = c.skill:canUse(Self, c) + ret = c.skill:canUse(Self, c, extra_data) if ret then break end end end @@ -449,7 +454,7 @@ function ActiveCardFilter(skill_name, to_select, selected, selected_targets) return json.encode(ret) end -function ActiveTargetFilter(skill_name, to_select, selected, selected_cards) +function ActiveTargetFilter(skill_name, to_select, selected, selected_cards, extra_data) local skill = Fk.skills[skill_name] local ret = false if skill then @@ -458,7 +463,7 @@ function ActiveTargetFilter(skill_name, 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 = card.skill:targetFilter(to_select, selected, selected_cards, card, extra_data) ret = ret and not Self:isProhibited(Fk:currentRoom():getPlayerById(to_select), card) end end @@ -722,7 +727,7 @@ function PoxiPrompt(poxi_type, data, extra_data) local poxi = Fk.poxi_methods[poxi_type] if not poxi or not poxi.prompt then return "" end if type(poxi.prompt) == "string" then return Fk:translate(poxi.prompt) end - return poxi.prompt(data, extra_data) + return Fk:translate(poxi.prompt(data, extra_data)) end function PoxiFilter(poxi_type, to_select, selected, data, extra_data) diff --git a/lua/client/i18n/en_US.lua b/lua/client/i18n/en_US.lua index 1da0ad40..29156e18 100644 --- a/lua/client/i18n/en_US.lua +++ b/lua/client/i18n/en_US.lua @@ -268,6 +268,7 @@ Fk:loadTranslationTable({ ["thunder_damage"] = "Thunder", ["ice_damage"] = "Ice", ["hp_lost"] = "HP lost", + ["lose_hp"] = "lose HP", ["phase_start"] = "Prepare phase", ["phase_judge"] = "Judge phase", diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index 804a9edf..9ae9ca97 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -324,6 +324,7 @@ Fk:loadTranslationTable{ ["thunder_damage"] = "雷属性", ["ice_damage"] = "冰属性", ["hp_lost"] = "体力流失", + ["lose_hp"] = "失去体力", ["phase_start"] = "准备阶段", ["phase_judge"] = "判定阶段", diff --git a/lua/core/card.lua b/lua/core/card.lua index 6cb51c34..71ec5e15 100644 --- a/lua/core/card.lua +++ b/lua/core/card.lua @@ -406,6 +406,9 @@ function Card:getMark(mark) if (not self:isVirtual()) and next(self.mark) == nil then self.mark = nil end + if type(ret) == "table" then + ret = table.simpleClone(ret) + end return ret end diff --git a/lua/core/engine.lua b/lua/core/engine.lua index f451c208..c8d2a4f2 100644 --- a/lua/core/engine.lua +++ b/lua/core/engine.lua @@ -275,8 +275,8 @@ end ---@param name string @ 要查询的武将名字 ---@return string[] @ 这个武将对应的同名武将列表 function Engine:getSameGenerals(name) - local tmp = name:split("__") - local tName = tmp[#tmp] + if not self.generals[name] then return {} end + local tName = self.generals[name].trueName local ret = self.same_generals[tName] or {} return table.filter(ret, function(g) return g ~= name and self.generals[g] ~= nil and self:canUseGeneral(g) diff --git a/lua/core/exppattern.lua b/lua/core/exppattern.lua index 1cf1f9fd..1a959bd1 100644 --- a/lua/core/exppattern.lua +++ b/lua/core/exppattern.lua @@ -10,7 +10,7 @@ 2. 对于 Matcher 字符串,它是用 ('|') 分割的 3. 然后在 Matcher 的每一个细分中,又可以用 ',' 来进行更进一步的分割 - 其中 Matcher 的格式为 牌名|花色|点数|位置|详细牌名|类型|牌的id + 其中 Matcher 的格式为 牌名|点数|花色|区域|完整牌名|牌类型|牌id 更进一步,“点数” 可以用 '~' 符号表示数字的范围,并且可以用 AJQK 表示对应点数 例如: diff --git a/lua/core/general.lua b/lua/core/general.lua index 3ab607d3..33e220b4 100644 --- a/lua/core/general.lua +++ b/lua/core/general.lua @@ -94,12 +94,18 @@ function General:addRelatedSkill(skill) end --- 获取武将所有技能。 +---@param include_lord bool function General:getSkillNameList(include_lord) - local ret = table.map(self.skills, Util.NameMapper) - table.insertTable(ret, self.other_skills) - - if not include_lord then + local ret = {} + local other_skills = table.map(self.other_skills, Util.Name2SkillMapper) + local skills = table.connect(self.skills, other_skills) + for _, skill in ipairs(skills) do + if include_lord or not skill.lordSkill then + table.insert(ret, skill.name) + end end + + -- table.insertTable(ret, self.other_skills) return ret end diff --git a/lua/core/player.lua b/lua/core/player.lua index 4e86703b..e54dcfef 100644 --- a/lua/core/player.lua +++ b/lua/core/player.lua @@ -215,7 +215,10 @@ end ---@param mark string @ 标记 ---@return any function Player:getMark(mark) - return (self.mark[mark] or 0) + local mark = self.mark[mark] + if not mark then return 0 end + if type(mark) == "table" then return table.simpleClone(mark) end + return mark end --- 判定角色是否拥有对应的Mark。 @@ -363,13 +366,13 @@ function Player:getCardIds(playerAreas, specialName) return cardIds end ---- 通过名字检索获取玩家是否存在对应私人牌堆。 +--- 通过名字检索获取玩家对应的私人牌堆。 ---@param name string @ 私人牌堆名 function Player:getPile(name) - return self.special_cards[name] or {} + return table.simpleClone(self.special_cards[name] or {}) end ---- 通过ID检索获取玩家是否存在对应私人牌堆。 +--- 通过ID检索获取玩家对应的私人牌堆。 ---@param id integer @ 私人牌堆ID ---@return string? function Player:getPileNameOfId(id) diff --git a/lua/core/skill_type/active.lua b/lua/core/skill_type/active.lua index 643a8e47..874299ec 100644 --- a/lua/core/skill_type/active.lua +++ b/lua/core/skill_type/active.lua @@ -27,7 +27,7 @@ end --- Determine whether the skill can be used in playing phase ---@param player Player ---@param card Card @ helper -function ActiveSkill:canUse(player, card) +function ActiveSkill:canUse(player, card, extra_data) return self:isEffectable(player) end @@ -45,8 +45,9 @@ end ---@param to_select integer @ id of the target ---@param selected integer[] @ ids of selected targets ---@param selected_cards integer[] @ ids of selected cards +---@param extra_data any @ extra_data ---@param card Card @ helper -function ActiveSkill:targetFilter(to_select, selected, selected_cards, card) +function ActiveSkill:targetFilter(to_select, selected, selected_cards, card, extra_data) return false end @@ -142,6 +143,35 @@ function ActiveSkill:getDistanceLimit(player, card, to) return ret end +function ActiveSkill:withinDistanceLimit(player, isattack, card, to) + if to and to.dead then return false end + local status_skills = Fk:currentRoom().status_skills[TargetModSkill] or Util.DummyTable + if not card and self.name:endsWith("_skill") then + card = Fk:cloneCard(self.name:sub(1, #self.name - 6)) + end + for _, skill in ipairs(status_skills) do + if skill:bypassDistancesCheck(player, self, card, to) then return true end + end + + local temp_suf = table.simpleClone(MarkEnum.TempMarkSuffix) + local card_temp_suf = table.simpleClone(MarkEnum.CardTempMarkSuffix) + table.insert(temp_suf, 1, "") + table.insert(temp_suf, "-tmp") + table.insert(card_temp_suf, 1, "") + + return (isattack and player:inMyAttackRange(to)) or + (player:distanceTo(to) > 0 and player:distanceTo(to) <= self:getDistanceLimit(player, card, to)) or + (card and table.find(card_temp_suf, function(s) + return card:getMark(MarkEnum.BypassDistancesLimit .. s) ~= 0 + end)) or + (table.find(temp_suf, function(s) + return player:getMark(MarkEnum.BypassDistancesLimit .. s) ~= 0 + end)) or + (to and (table.find(temp_suf, function(s) + return to:getMark(MarkEnum.BypassDistancesLimitTo .. s) ~= 0 + end))) +end + --- Determine if selected cards and targets are valid for this skill --- If returns true, the OK button should be enabled --- only used in skill of players diff --git a/lua/core/skill_type/usable_skill.lua b/lua/core/skill_type/usable_skill.lua index f3429296..70e00244 100644 --- a/lua/core/skill_type/usable_skill.lua +++ b/lua/core/skill_type/usable_skill.lua @@ -35,41 +35,24 @@ function UsableSkill:withinTimesLimit(player, scope, card, card_name, to) for _, skill in ipairs(status_skills) do if skill:bypassTimesCheck(player, self, scope, card, to) then return true end end + card_name = card_name or card.trueName local temp_suf = table.simpleClone(MarkEnum.TempMarkSuffix) + local card_temp_suf = table.simpleClone(MarkEnum.CardTempMarkSuffix) + table.insert(temp_suf, 1, "") table.insert(temp_suf, "-tmp") + table.insert(card_temp_suf, 1, "") + return player:usedCardTimes(card_name, scope) < self:getMaxUseTime(player, scope, card, to) or - (player:getMark(MarkEnum.BypassTimesLimit) ~= 0 or - table.find(temp_suf, function(s) + (card and table.find(card_temp_suf, function(s) + return card:getMark(MarkEnum.BypassTimesLimit .. s) ~= 0 + end)) or + (table.find(temp_suf, function(s) return player:getMark(MarkEnum.BypassTimesLimit .. s) ~= 0 end)) or - (to and (to:getMark(MarkEnum.BypassTimesLimitTo) ~= 0 or - table.find(temp_suf, function(s) + (to and (table.find(temp_suf, function(s) return to:getMark(MarkEnum.BypassTimesLimitTo .. s) ~= 0 end))) end -function UsableSkill:withinDistanceLimit(player, isattack, card, to) - if to and to.dead then return false end - local status_skills = Fk:currentRoom().status_skills[TargetModSkill] or Util.DummyTable - if not card and self.name:endsWith("_skill") then - card = Fk:cloneCard(self.name:sub(1, #self.name - 6)) - end - for _, skill in ipairs(status_skills) do - if skill:bypassDistancesCheck(player, self, card, to) then return true end - end - local temp_suf = table.simpleClone(MarkEnum.TempMarkSuffix) - table.insert(temp_suf, "-tmp") - return (isattack and player:inMyAttackRange(to)) or - (player:distanceTo(to) > 0 and player:distanceTo(to) <= self:getDistanceLimit(player, card, to)) or - (player:getMark(MarkEnum.BypassDistancesLimit) ~= 0 or - table.find(temp_suf, function(s) - return player:getMark(MarkEnum.BypassDistancesLimit .. s) ~= 0 - end)) or - (to and (to:getMark(MarkEnum.BypassDistancesLimitTo) ~= 0 or - table.find(temp_suf, function(s) - return to:getMark(MarkEnum.BypassDistancesLimitTo .. s) ~= 0 - end))) -end - return UsableSkill diff --git a/lua/fk_ex.lua b/lua/fk_ex.lua index b352f004..1cf0d062 100644 --- a/lua/fk_ex.lua +++ b/lua/fk_ex.lua @@ -170,9 +170,9 @@ function fk.CreateTriggerSkill(spec) end ---@class ActiveSkillSpec: UsableSkillSpec ----@field public can_use? fun(self: ActiveSkill, player: Player, card: Card): boolean? +---@field public can_use? fun(self: ActiveSkill, player: Player, card: Card, extra_data: any): boolean? ---@field public card_filter? fun(self: ActiveSkill, to_select: integer, selected: integer[], selected_targets: integer[]): boolean? ----@field public target_filter? fun(self: ActiveSkill, to_select: integer, selected: integer[], selected_cards: integer[], card: Card): boolean? +---@field public target_filter? fun(self: ActiveSkill, to_select: integer, selected: integer[], selected_cards: integer[], card: Card, extra_data: any): boolean? ---@field public feasible? fun(self: ActiveSkill, selected: integer[], selected_cards: integer[]): boolean? ---@field public on_use? fun(self: ActiveSkill, room: Room, cardUseEvent: CardUseStruct): boolean? ---@field public about_to_effect? fun(self: ActiveSkill, room: Room, cardEffectEvent: CardEffectEvent): boolean? diff --git a/lua/server/ai/random_ai.lua b/lua/server/ai/random_ai.lua index 1108b17b..cfb12ac9 100644 --- a/lua/server/ai/random_ai.lua +++ b/lua/server/ai/random_ai.lua @@ -27,6 +27,7 @@ function RandomAI:useActiveSkill(skill, card) -- local max = skill:getMaxTargetNum(player, card) -- local min_card = skill:getMinCardNum() -- local max_card = skill:getMaxCardNum() + -- 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) @@ -121,6 +122,9 @@ random_cb["AskForUseActiveSkill"] = function(self, jsonData) for k, v in pairs(extra_data) do skill[k] = v end + if skill:isInstanceOf(ViewAsSkill) then + return RandomAI.useVSSkill(skill) + end return RandomAI.useActiveSkill(self, skill) end diff --git a/lua/server/events/misc.lua b/lua/server/events/misc.lua index 6b73e3e4..36128b8b 100644 --- a/lua/server/events/misc.lua +++ b/lua/server/events/misc.lua @@ -19,12 +19,12 @@ GameEvent.functions[GameEvent.ChangeProperty] = function(self) if data.general and data.general ~= "" and data.general ~= player.general then local originalGeneral = Fk.generals[player.general] or Fk.generals["blank_shibing"] - local originalSkills = originalGeneral and originalGeneral:getSkillNameList() or Util.DummyTable + local originalSkills = originalGeneral and originalGeneral:getSkillNameList(true) or Util.DummyTable table.insertTableIfNeed(skills, table.map(originalSkills, function(e) return "-" .. e end)) local newGeneral = Fk.generals[data.general] or Fk.generals["blank_shibing"] - for _, name in ipairs(newGeneral:getSkillNameList()) do + for _, name in ipairs(newGeneral:getSkillNameList(data.isLord)) do local s = Fk.skills[name] if not s.relate_to_place or s.relate_to_place == "m" then table.insertIfNeed(skills, name) @@ -45,14 +45,14 @@ GameEvent.functions[GameEvent.ChangeProperty] = function(self) if data.deputyGeneral and data.deputyGeneral ~= player.deputyGeneral then local originalDeputy = Fk.generals[player.deputyGeneral] or Fk.generals["blank_shibing"] - local originalSkills = originalDeputy and originalDeputy:getSkillNameList() or Util.DummyTable + local originalSkills = originalDeputy and originalDeputy:getSkillNameList(true) or Util.DummyTable table.insertTableIfNeed(skills, table.map(originalSkills, function(e) return "-" .. e end)) if data.deputyGeneral ~= "" then local newDeputy = Fk.generals[data.deputyGeneral] or Fk.generals["blank_shibing"] - for _, name in ipairs(newDeputy:getSkillNameList()) do + for _, name in ipairs(newDeputy:getSkillNameList(data.isLord)) do local s = Fk.skills[name] if not s.relate_to_place or s.relate_to_place == "d" then table.insertIfNeed(skills, name) diff --git a/lua/server/gameevent.lua b/lua/server/gameevent.lua index 1a1e36b6..e89012b6 100644 --- a/lua/server/gameevent.lua +++ b/lua/server/gameevent.lua @@ -85,10 +85,10 @@ end function GameEvent:findParent(eventType, includeSelf) if includeSelf and self.event == eventType then return self end local e = self.parent - repeat + while e do if e.event == eventType then return e end e = e.parent - until not e + end return nil end diff --git a/lua/server/gamelogic.lua b/lua/server/gamelogic.lua index 54fdc624..ed174f29 100644 --- a/lua/server/gamelogic.lua +++ b/lua/server/gamelogic.lua @@ -218,6 +218,10 @@ function GameLogic:attachSkillToPlayers() local addRoleModSkills = function(player, skillName) local skill = Fk.skills[skillName] + if not skill then + fk.qCritical("Skill: "..skillName.." doesn't exist!") + return + end if skill.lordSkill and (player.role ~= "lord" or #room.players < 5) then return end diff --git a/lua/server/room.lua b/lua/server/room.lua index 6a4fdf94..571859ec 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -1035,7 +1035,7 @@ function Room:notifySkillInvoked(player, skill_name, skill_type) self:doAnimate("InvokeUltSkill", { name = skill_name, player = player.id, - deputy = player.deputyGeneral and player.deputyGeneral ~= "" and table.contains(Fk.generals[player.deputyGeneral]:getSkillNameList(), skill_name), + deputy = player.deputyGeneral and player.deputyGeneral ~= "" and table.contains(Fk.generals[player.deputyGeneral]:getSkillNameList(true), skill_name), }) self:delay(2000) end @@ -1978,36 +1978,26 @@ end ---@param event_data? CardEffectEvent @ 事件信息 ---@return CardUseStruct? @ 返回关于本次使用牌的数据,以便后续处理 function Room:askForUseCard(player, card_name, pattern, prompt, cancelable, extra_data, event_data) + pattern = pattern or card_name if event_data and (event_data.disresponsive or table.contains(event_data.disresponsiveList or Util.DummyTable, player.id)) then return nil end - if event_data and event_data.prohibitedCardNames and card_name then - local splitedCardNames = card_name:split(",") - splitedCardNames = table.filter(splitedCardNames, function(name) - return not table.contains(event_data.prohibitedCardNames, name) - end) - - if #splitedCardNames == 0 then - return nil + if event_data and event_data.prohibitedCardNames then + local exp = Exppattern:Parse(pattern) + for _, matcher in ipairs(exp.matchers) do + matcher.name = table.filter(matcher.name, function(name) + return not table.contains(event_data.prohibitedCardNames, name) + end) + if #matcher.name == 0 then return nil end end - - card_name = table.concat(splitedCardNames, ",") + pattern = tostring(exp) end - if extra_data then - if extra_data.bypass_distances then - player.room:setPlayerMark(player, MarkEnum.BypassDistancesLimit .. "-tmp", 1) -- FIXME: 缺少直接传入无限制的手段 - end - if extra_data.bypass_times == nil or extra_data.bypass_times then - player.room:setPlayerMark(player, MarkEnum.BypassTimesLimit .. "-tmp", 1) -- FIXME: 缺少直接传入无限制的手段 - end - end local command = "AskForUseCard" self:notifyMoveFocus(player, card_name) cancelable = (cancelable == nil) and true or cancelable extra_data = extra_data or Util.DummyTable - pattern = pattern or card_name prompt = prompt or "" local askForUseCardData = { @@ -2020,8 +2010,6 @@ function Room:askForUseCard(player, card_name, pattern, prompt, cancelable, extr self.logic:trigger(fk.AskForCardUse, player, askForUseCardData) if askForUseCardData.result and type(askForUseCardData.result) == 'table' then - player.room:setPlayerMark(player, MarkEnum.BypassDistancesLimit .. "-tmp", 0) -- FIXME: 缺少直接传入无限制的手段 - player.room:setPlayerMark(player, MarkEnum.BypassTimesLimit .. "-tmp", 0) -- FIXME: 缺少直接传入无限制的手段 return askForUseCardData.result else local useResult @@ -2036,8 +2024,6 @@ function Room:askForUseCard(player, card_name, pattern, prompt, cancelable, extr Fk.currentResponsePattern = nil if result ~= "" then - player.room:setPlayerMark(player, MarkEnum.BypassDistancesLimit .. "-tmp", 0) -- FIXME: 缺少直接传入无限制的手段 - player.room:setPlayerMark(player, MarkEnum.BypassTimesLimit .. "-tmp", 0) -- FIXME: 缺少直接传入无限制的手段 useResult = self:handleUseCardReply(player, result) if type(useResult) == "string" and useResult ~= "" then @@ -2045,12 +2031,8 @@ function Room:askForUseCard(player, card_name, pattern, prompt, cancelable, extr end end until type(useResult) ~= "string" - player.room:setPlayerMark(player, MarkEnum.BypassDistancesLimit .. "-tmp", 0) -- FIXME: 缺少直接传入无限制的手段 - player.room:setPlayerMark(player, MarkEnum.BypassTimesLimit .. "-tmp", 0) -- FIXME: 缺少直接传入无限制的手段 return useResult end - player.room:setPlayerMark(player, MarkEnum.BypassDistancesLimit .. "-tmp", 0) -- FIXME: 缺少直接传入无限制的手段 - player.room:setPlayerMark(player, MarkEnum.BypassTimesLimit .. "-tmp", 0) -- FIXME: 缺少直接传入无限制的手段 return nil end @@ -3337,6 +3319,8 @@ function Room:shuffleDrawPile() self.discard_pile = {} table.shuffle(self.draw_pile) + self:doBroadcastNotify("UpdateDrawPile", #self.draw_pile) + self.logic:trigger(fk.AfterDrawPileShuffle, nil, {}) end diff --git a/lua/server/serverplayer.lua b/lua/server/serverplayer.lua index 75a26cae..754c5ced 100644 --- a/lua/server/serverplayer.lua +++ b/lua/server/serverplayer.lua @@ -923,7 +923,7 @@ function ServerPlayer:revealGeneral(isDeputy, no_trigger) end local general = Fk.generals[generalName] or Fk.generals["blank_shibing"] - for _, s in ipairs(general:getSkillNameList()) do + for _, s in ipairs(general:getSkillNameList(true)) do local skill = Fk.skills[s] self:loseFakeSkill(skill) end @@ -931,7 +931,7 @@ function ServerPlayer:revealGeneral(isDeputy, no_trigger) local ret = true if not ((isDeputy and self.general ~= "anjiang") or (not isDeputy and self.deputyGeneral ~= "anjiang")) then local other = Fk.generals[self:getMark(isDeputy and "__heg_general" or "__heg_deputy")] or Fk.generals["blank_shibing"] - for _, sname in ipairs(other:getSkillNameList()) do + for _, sname in ipairs(other:getSkillNameList(true)) do local s = Fk.skills[sname] if s.frequency == Skill.Compulsory and s.relate_to_place ~= (isDeputy and "m" or "d") then ret = false @@ -1017,7 +1017,7 @@ function ServerPlayer:revealBySkillName(skill_name) if main then if table.contains(Fk.generals[self:getMark("__heg_general")] - :getSkillNameList(), skill_name) then + :getSkillNameList(true), skill_name) then self:revealGeneral(false) return end @@ -1025,7 +1025,7 @@ function ServerPlayer:revealBySkillName(skill_name) if deputy then if table.contains(Fk.generals[self:getMark("__heg_deputy")] - :getSkillNameList(), skill_name) then + :getSkillNameList(true), skill_name) then self:revealGeneral(true) return end diff --git a/packages/maneuvering/init.lua b/packages/maneuvering/init.lua index a9e08227..cdcf2f20 100644 --- a/packages/maneuvering/init.lua +++ b/packages/maneuvering/init.lua @@ -109,13 +109,13 @@ local analepticSkill = fk.CreateActiveSkill{ prompt = "#analeptic_skill", max_turn_use_time = 1, mod_target_filter = function(self, to_select, _, _, card, _) - return self:withinTimesLimit(Fk:currentRoom():getPlayerById(to_select), Player.HistoryTurn, card, "analeptic", Fk:currentRoom():getPlayerById(to_select)) and - not table.find(Fk:currentRoom().alive_players, function(p) - return p.dying - end) + return not table.find(Fk:currentRoom().alive_players, function(p) + return p.dying + end) end, - can_use = function(self, player, card) - return self:withinTimesLimit(player, Player.HistoryTurn, card, "analeptic", player) + can_use = function(self, player, card, extra_data) + return ((extra_data and (extra_data.bypass_times or extra_data.analepticRecover)) or + self:withinTimesLimit(player, Player.HistoryTurn, card, "analeptic", player)) end, on_use = function(_, _, use) if not use.tos or #TargetGroup:getRealTargets(use.tos) == 0 then @@ -291,8 +291,9 @@ local supplyShortageSkill = fk.CreateActiveSkill{ local from = Fk:currentRoom():getPlayerById(user) return from ~= player and not (distance_limited and not self:withinDistanceLimit(from, false, card, player)) end, - target_filter = function(self, to_select, selected, _, card) - return #selected == 0 and self:modTargetFilter(to_select, selected, Self.id, card, true) + target_filter = function(self, to_select, selected, _, card, extra_data) + local count_distances = not (extra_data and extra_data.bypass_distances) + return #selected == 0 and self:modTargetFilter(to_select, selected, Self.id, card, count_distances) end, target_num = 1, on_effect = function(self, room, effect) diff --git a/packages/standard/aux_skills.lua b/packages/standard/aux_skills.lua index 310a0b0c..3cc3a116 100644 --- a/packages/standard/aux_skills.lua +++ b/packages/standard/aux_skills.lua @@ -207,7 +207,7 @@ local revealProhibited = fk.CreateInvaliditySkill { end local generalName = g == "m" and from:getMark("__heg_general") or from:getMark("__heg_deputy") local general = Fk.generals[generalName] - if table.contains(general:getSkillNameList(), sname) then + if table.contains(general:getSkillNameList(true), sname) then return true end end @@ -223,7 +223,7 @@ local revealSkill = fk.CreateActiveSkill{ local choiceList = {} if (Self.general == "anjiang" and not Self:prohibitReveal()) then local general = Fk.generals[Self:getMark("__heg_general")] - for _, sname in ipairs(general:getSkillNameList()) do + for _, sname in ipairs(general:getSkillNameList(true)) do local s = Fk.skills[sname] if s.frequency == Skill.Compulsory and s.relate_to_place ~= "m" then table.insert(choiceList, "revealMain") @@ -233,7 +233,7 @@ local revealSkill = fk.CreateActiveSkill{ end if (Self.deputyGeneral == "anjiang" and not Self:prohibitReveal(true)) then local general = Fk.generals[Self:getMark("__heg_deputy")] - for _, sname in ipairs(general:getSkillNameList()) do + for _, sname in ipairs(general:getSkillNameList(true)) do local s = Fk.skills[sname] if s.frequency == Skill.Compulsory and s.relate_to_place ~= "d" then table.insert(choiceList, "revealDeputy") diff --git a/packages/standard/game_rule.lua b/packages/standard/game_rule.lua index 72bb0ec8..3cc23b40 100644 --- a/packages/standard/game_rule.lua +++ b/packages/standard/game_rule.lua @@ -53,7 +53,7 @@ GameRule = fk.CreateTriggerSkill{ end) if #cardNames == 0 then return end - local peach_use = room:askForUseCard(player, "peach", table.concat(cardNames, ",") , prompt) + local peach_use = room:askForUseCard(player, "peach", table.concat(cardNames, ",") , prompt, true, {analepticRecover = true}) if not peach_use then break end peach_use.tos = { {dyingPlayer.id} } if peach_use.card.trueName == "analeptic" then diff --git a/packages/standard/init.lua b/packages/standard/init.lua index a1fbb3fb..aaea8820 100644 --- a/packages/standard/init.lua +++ b/packages/standard/init.lua @@ -1172,6 +1172,56 @@ local role_getlogic = function() room:broadcastProperty(lord, "kingdom") room:setDeputyGeneral(lord, deputy) room:broadcastProperty(lord, "deputyGeneral") + + -- 显示技能 + local canAttachSkill = function(player, skillName) + local skill = Fk.skills[skillName] + if not skill then + fk.qCritical("Skill: "..skillName.." doesn't exist!") + return false + end + if skill.lordSkill and (player.role ~= "lord" or #room.players < 5) then + return false + end + + if #skill.attachedKingdom > 0 and not table.contains(skill.attachedKingdom, player.kingdom) then + return false + end + + return true + end + + local lord_skills = {} + for _, s in ipairs(Fk.generals[lord.general].skills) do + if canAttachSkill(lord, s.name) then + table.insertIfNeed(lord_skills, s.name) + end + end + for _, sname in ipairs(Fk.generals[lord.general].other_skills) do + if canAttachSkill(lord, sname) then + table.insertIfNeed(lord_skills, sname) + end + end + + local deputyGeneral = Fk.generals[lord.deputyGeneral] + if deputyGeneral then + for _, s in ipairs(deputyGeneral.skills) do + if canAttachSkill(lord, s.name) then + table.insertIfNeed(lord_skills, s.name) + end + end + for _, sname in ipairs(deputyGeneral.other_skills) do + if canAttachSkill(lord, sname) then + table.insertIfNeed(lord_skills, sname) + end + end + end + for _, skill in ipairs(lord_skills) do + room:doBroadcastNotify("AddSkill", json.encode{ + lord.id, + skill + }) + end end local nonlord = room:getOtherPlayers(lord, true) diff --git a/packages/standard_cards/init.lua b/packages/standard_cards/init.lua index 26b674b0..abfacd02 100644 --- a/packages/standard_cards/init.lua +++ b/packages/standard_cards/init.lua @@ -20,8 +20,8 @@ local slashSkill = fk.CreateActiveSkill{ end, max_phase_use_time = 1, target_num = 1, - can_use = function(self, player, card) - return + can_use = function(self, player, card, extra_data) + return (extra_data and extra_data.bypass_times) or table.find(Fk:currentRoom().alive_players, function(p) return self:withinTimesLimit(player, Player.HistoryPhase, card, "slash", p) end) @@ -31,11 +31,12 @@ local slashSkill = fk.CreateActiveSkill{ local from = Fk:currentRoom():getPlayerById(user) return from ~= player and not (distance_limited and not self:withinDistanceLimit(from, true, card, player)) end, - target_filter = function(self, to_select, selected, _, card) + target_filter = function(self, to_select, selected, _, card, extra_data) + local count_distances = not (extra_data and extra_data.bypass_distances) if #selected < self:getMaxTargetNum(Self, card) then local player = Fk:currentRoom():getPlayerById(to_select) - return self:modTargetFilter(to_select, selected, Self.id, card, true) and - (#selected > 0 or self:withinTimesLimit(Self, Player.HistoryPhase, card, "slash", player)) + return self:modTargetFilter(to_select, selected, Self.id, card, count_distances) and + (#selected > 0 or (extra_data and extra_data.bypass_times) or self:withinTimesLimit(Self, Player.HistoryPhase, card, "slash", player)) end end, on_effect = function(self, room, effect) @@ -229,9 +230,10 @@ local snatchSkill = fk.CreateActiveSkill{ local from = Fk:currentRoom():getPlayerById(user) return from ~= player and not (player:isAllNude() or (distance_limited and not self:withinDistanceLimit(from, false, card, player))) end, - target_filter = function(self, to_select, selected, _, card) + target_filter = function(self, to_select, selected, _, card, extra_data) + local count_distances = not (extra_data and extra_data.bypass_distances) if #selected < self:getMaxTargetNum(Self, card) then - return self:modTargetFilter(to_select, selected, Self.id, card, true) + return self:modTargetFilter(to_select, selected, Self.id, card, count_distances) end end, target_num = 1, diff --git a/packages/test/init.lua b/packages/test/init.lua index 32d4d995..3ae6a71a 100644 --- a/packages/test/init.lua +++ b/packages/test/init.lua @@ -90,6 +90,8 @@ local control = fk.CreateActiveSkill{ -- room:setPlayerMark(from, "@$b", {'slash','duel','axe'}) --room:askForMiniGame({from}, "test", "test", { [from.id] = {"Helloworld"} }) --print(from.client_reply) + -- p(Fk.generals[to.general]:getSkillNameList()) + -- p(Fk.generals[to.general]:getSkillNameList(true)) if to:getMark("mouxushengcontrolled") == 0 then room:addPlayerMark(to, "mouxushengcontrolled") from:control(to) @@ -412,6 +414,11 @@ Fk:loadTranslationTable{ ["$change_hero"] = "敌军色厉内荏,可筑假城以退敌!", ["~mouxusheng"] = "来世,愿再为我江东之臣……", + + ["heal_hp"] = "回复体力", + ["lose_max_hp"] = "减少体力上限", + ["heal_max_hp"] = "增加体力上限", + ["revive"] = "复活", } return { extension }