diff --git a/Fk/Pages/RoomLogic.js b/Fk/Pages/RoomLogic.js index 39be0dbe..25dedbe4 100644 --- a/Fk/Pages/RoomLogic.js +++ b/Fk/Pages/RoomLogic.js @@ -954,7 +954,7 @@ callbacks["AskForGeneral"] = (jsonData) => { }); box.choiceNum = n; box.convertDisabled = !!convert; - box.needSameKingdom = !!heg; + box.hegemony = !!heg; for (let i = 0; i < generals.length; i++) box.generalList.append({ "name": generals[i] }); box.updatePosition(); diff --git a/Fk/RoomElement/ChooseGeneralBox.qml b/Fk/RoomElement/ChooseGeneralBox.qml index 50c6adf8..1278a28c 100644 --- a/Fk/RoomElement/ChooseGeneralBox.qml +++ b/Fk/RoomElement/ChooseGeneralBox.qml @@ -12,7 +12,7 @@ GraphicsBox { property var selectedItem: [] property bool loaded: false property bool convertDisabled: false - property bool needSameKingdom: false + property bool hegemony: false ListModel { id: generalList @@ -230,6 +230,14 @@ GraphicsBox { return false; } + function updateCompanion(gcard1, gcard2, overwrite) { + if (lcall("IsCompanionWith", gcard1.name, gcard2.name)) { + gcard1.hasCompanions = true; + } else if (overwrite) { + gcard1.hasCompanions = false; + } + } + function updatePosition() { choices = []; @@ -248,12 +256,37 @@ GraphicsBox { root.choicesChanged(); fightButton.enabled = (choices.length == choiceNum) && - (needSameKingdom ? isHegPair(selectedItem[0], selectedItem[1]) : true); + (hegemony ? isHegPair(selectedItem[0], selectedItem[1]) : true); for (i = 0; i < generalCardList.count; i++) { item = generalCardList.itemAt(i); - item.selectable = needSameKingdom ? isHegPair(selectedItem[0], item) + item.selectable = hegemony ? isHegPair(selectedItem[0], item) : true; + if (hegemony) { + if (selectedItem[0]) { + if (selectedItem[1]) { + if (selectedItem[0] === item) { + updateCompanion(item, selectedItem[1], true); + } else if (selectedItem[1] === item) { + updateCompanion(item, selectedItem[0], true); + } else { + item.hasCompanions = false; + } + } else { + if (selectedItem[0] !== item) { + updateCompanion(item, selectedItem[0], true); + } else { + for (let j = 0; j < generalList.count; j++) { + updateCompanion(item, generalList.get(j), false); + } + } + } + } else { + for (let j = 0; j < generalList.count; j++) { + updateCompanion(item, generalList.get(j), false); + } + } + } if (selectedItem.indexOf(item) != -1) continue; diff --git a/Fk/RoomElement/GeneralCardItem.qml b/Fk/RoomElement/GeneralCardItem.qml index 87332f2a..979d4a8e 100644 --- a/Fk/RoomElement/GeneralCardItem.qml +++ b/Fk/RoomElement/GeneralCardItem.qml @@ -24,6 +24,7 @@ CardItem { property int shieldNum property string pkgName: "" property bool detailed: true + property alias hasCompanions: companions.visible name: "" // description: Sanguosha.getGeneralDescription(name) suit: "" @@ -157,6 +158,7 @@ CardItem { height: 80 x: 2 y: lineCount > 6 ? 30 : 34 + z: 999 text: name !== "" ? luatr(name) : "nil" visible: luatr(name).length <= 6 && detailed && known color: "white" @@ -170,6 +172,7 @@ CardItem { Text { x: 0 y: 12 + z: 999 rotation: 90 transformOrigin: Item.BottomLeft text: luatr(name) @@ -184,6 +187,7 @@ CardItem { visible: pkgName !== "" && detailed && known height: 16 width: childrenRect.width + 4 + z: 100 anchors.bottom: parent.bottom anchors.bottomMargin: 4 anchors.right: parent.right @@ -205,6 +209,17 @@ CardItem { } } + Image { + id: companions + width: parent.width + fillMode: Image.PreserveAspectFit + visible: false + source: SkinBank.getGeneralCardDir(kingdom) + kingdom + "-companions" + anchors.horizontalCenter: parent.horizontalCenter + y: 80 + z: 1 + } + onNameChanged: { const data = lcall("GetGeneralData", name); kingdom = data.kingdom; diff --git a/image/card/general/qun-companions.png b/image/card/general/qun-companions.png new file mode 100644 index 00000000..efb117cd Binary files /dev/null and b/image/card/general/qun-companions.png differ diff --git a/image/card/general/shu-companions.png b/image/card/general/shu-companions.png new file mode 100644 index 00000000..2f06b40c Binary files /dev/null and b/image/card/general/shu-companions.png differ diff --git a/image/card/general/wei-companions.png b/image/card/general/wei-companions.png new file mode 100644 index 00000000..28b31b6b Binary files /dev/null and b/image/card/general/wei-companions.png differ diff --git a/image/card/general/wild-companions.png b/image/card/general/wild-companions.png new file mode 100644 index 00000000..3c1c1d14 Binary files /dev/null and b/image/card/general/wild-companions.png differ diff --git a/image/card/general/wu-companions.png b/image/card/general/wu-companions.png new file mode 100644 index 00000000..c8a9c730 Binary files /dev/null and b/image/card/general/wu-companions.png differ diff --git a/lua/client/client_util.lua b/lua/client/client_util.lua index 27cbc345..3ec77179 100644 --- a/lua/client/client_util.lua +++ b/lua/client/client_util.lua @@ -72,6 +72,11 @@ function GetSameGenerals(name) return json.encode(Fk:getSameGenerals(name)) end +function IsCompanionWith(general, general2) + local _general, _general2 = Fk.generals[general], Fk.generals[general2] + return json.encode(_general:isCompanionWith(_general2)) +end + local cardSubtypeStrings = { [Card.SubtypeNone] = "none", [Card.SubtypeDelayedTrick] = "delayed_trick", diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index c0315d17..4ab2ac0b 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -431,9 +431,12 @@ Fk:loadTranslationTable{ ["$DiscardOther"] = "%to 弃置了 %from 的 %arg 张牌 %card", ["$PutToDiscard"] = "%arg 张牌 %card 被置入弃牌堆", + ["#AbortArea"] = "%from 的 %arg 被废除", + ["#ResumeArea"] = "%from 的 %arg 被恢复", + ["#ShowCard"] = "%from 展示了牌 %card", ["#Recast"] = "%from 重铸了 %card", - ["#RecastBySkill"] = "%from 发动了 “%arg” 重铸了 %card", + ["#RecastBySkill"] = "%from 因 “%arg” 重铸了 %card", -- phase ["#PhaseSkipped"] = "%from 跳过了 %arg", diff --git a/lua/core/general.lua b/lua/core/general.lua index 24008eee..8bd5d046 100644 --- a/lua/core/general.lua +++ b/lua/core/general.lua @@ -110,8 +110,8 @@ function General:getSkillNameList(include_lord) return ret end ---- 为武将增加珠联璧合关系武将(1个或多个),只需写trueName。 ----@param name string[] @ 武将真名(表) +--- 为武将增加珠联璧合关系武将(1个或多个)。 +---@param name string|string[] @ 武将名(表) function General:addCompanions(name) if type(name) == "table" then table.insertTable(self.companions, name) @@ -120,4 +120,12 @@ function General:addCompanions(name) end end +--- 是否与另一武将构成珠联璧合关系。 +---@param other General @ 另一武将 +function General:isCompanionWith(other) + return table.contains(self.companions, other.name) or table.contains(other.companions, self.name) + or (string.find(self.name, "lord") and (other.kingdom == self.kingdom or other.subkingdom == self.kingdom)) + or (string.find(other.name, "lord") and (self.kingdom == other.kingdom or self.subkingdom == other.kingdom)) +end + return General diff --git a/lua/core/player.lua b/lua/core/player.lua index db78d0e2..33d674e0 100644 --- a/lua/core/player.lua +++ b/lua/core/player.lua @@ -560,6 +560,30 @@ function Player:distanceTo(other, mode, ignore_dead) return math.max(ret, 1) end +--- 比较距离 +---@param other Player @ 终点角色 +---@param num integer @ 比较基准 +---@param operator string @ 运算符,有 ``"<"`` ``">"`` ``"<="`` ``">="`` ``"=="`` ``"~="`` +---@return boolean @ 返回比较结果,不计入距离结果永远为false +function Player:compareDistance(other, num, operator) + local distance = self:distanceTo(other) + if distance < 0 or num < 0 then return false end + if operator == ">" then + return distance > num + elseif operator == "<" then + return distance < num + elseif operator == "==" then + return distance == num + elseif operator == ">=" then + return distance >= num + elseif operator == "<=" then + return distance <= num + elseif operator == "~=" then + return distance ~= num + end + return false +end + --- 获取其他玩家是否在玩家的攻击范围内。 ---@param other Player @ 其他玩家 ---@param fixLimit? integer @ 卡牌距离限制增加专用 @@ -762,7 +786,12 @@ function Player:hasSkill(skill, ignoreNullified, ignoreAlive) end if table.contains(self.player_skills, skill) then - return true + if not skill:isInstanceOf(StatusSkill) then return true end + if self:isInstanceOf(ServerPlayer) then + return not self:isFakeSkill(skill) + else + return table.contains(self.player_skills, skill) + end end if self:isInstanceOf(ServerPlayer) and -- isInstanceOf(nil) will return false @@ -781,6 +810,20 @@ function Player:hasSkill(skill, ignoreNullified, ignoreAlive) return false end +--- 技能是否亮出 +---@param skill string | Skill +---@return boolean +function Player:hasShownSkill(skill, ignoreNullified, ignoreAlive) + if not self:hasSkill(skill, ignoreNullified, ignoreAlive) then return false end + + if self:isInstanceOf(ServerPlayer) then + return not self:isFakeSkill(skill) + else + if type(skill) == "string" then skill = Fk.skills[skill] end + return table.contains(self.player_skills, skill) + end +end + --- 为玩家增加对应技能。 ---@param skill string | Skill @ 技能名 ---@param source_skill? string | Skill @ 本有技能(和衍生技能相对) @@ -1137,4 +1180,14 @@ function Player:compareGenderWith(other, diff) end end +--- 是否为男性(包括双性)。 +function Player:isMale() + return self.gender == General.Male or self.gender == General.Bigender +end + +--- 是否为女性(包括双性)。 +function Player:isFemale() + return self.gender == General.Female or self.gender == General.Bigender +end + return Player diff --git a/lua/core/skill_type/active.lua b/lua/core/skill_type/active.lua index 3f13b76f..9c5d13c9 100644 --- a/lua/core/skill_type/active.lua +++ b/lua/core/skill_type/active.lua @@ -209,7 +209,7 @@ function ActiveSkill:onUse(room, cardUseEvent) end ---@param room Room ---@param cardUseEvent CardUseStruct ----@param isEnding? bool +---@param finished? bool function ActiveSkill:onAction(room, cardUseEvent, finished) end ---@param room Room diff --git a/lua/server/events/pindian.lua b/lua/server/events/pindian.lua index c1c593a4..0982a373 100644 --- a/lua/server/events/pindian.lua +++ b/lua/server/events/pindian.lua @@ -38,7 +38,7 @@ GameEvent.functions[GameEvent.Pindian] = function(self) table.insert(moveInfos, { ids = { _pindianCard.id }, - from = pindianData.from.id, + from = room.owner_map[_pindianCard.id], fromArea = room:getCardArea(_pindianCard.id), toArea = Card.Processing, moveReason = fk.ReasonPut, @@ -56,7 +56,7 @@ GameEvent.functions[GameEvent.Pindian] = function(self) table.insert(moveInfos, { ids = { _pindianCard.id }, - from = to.id, + from = room.owner_map[_pindianCard.id], fromArea = room:getCardArea(_pindianCard.id), toArea = Card.Processing, moveReason = fk.ReasonPut, diff --git a/lua/server/room.lua b/lua/server/room.lua index 600937cb..789b4bf4 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -3040,6 +3040,7 @@ end ---@param tos ServerPlayer | ServerPlayer[] @ 目标角色(列表) ---@param skillName? string @ 技能名 ---@param extra? boolean @ 是否不计入次数 +---@return CardUseStruct function Room:useVirtualCard(card_name, subcards, from, tos, skillName, extra) local card = Fk:cloneCard(card_name) card.skillName = skillName @@ -3064,7 +3065,7 @@ function Room:useVirtualCard(card_name, subcards, from, tos, skillName, extra) use.extraUse = extra self:useCard(use) - return true + return use end ------------------------------------------------------------------------ @@ -3936,6 +3937,13 @@ function Room:abortPlayerArea(player, playerSlots) table.insertTable(player.sealedSlots, slotsToSeal) self:broadcastProperty(player, "sealedSlots") + for _, s in ipairs(slotsToSeal) do + self:sendLog{ + type = "#AbortArea", + from = player.id, + arg = s, + } + end self.logic:trigger(fk.AreaAborted, player, { slots = slotsSealed }) end @@ -3961,6 +3969,13 @@ function Room:resumePlayerArea(player, playerSlots) if #slotsToResume > 0 then self:broadcastProperty(player, "sealedSlots") + for _, s in ipairs(slotsToResume) do + self:sendLog{ + type = "#ResumeArea", + from = player.id, + arg = s, + } + end self.logic:trigger(fk.AreaResumed, player, { slots = slotsToResume }) end end diff --git a/lua/server/serverplayer.lua b/lua/server/serverplayer.lua index e76e88fe..be723d0d 100644 --- a/lua/server/serverplayer.lua +++ b/lua/server/serverplayer.lua @@ -943,7 +943,7 @@ function ServerPlayer:revealGeneral(isDeputy, no_trigger) end end if ret then - self:loseFakeSkill("reveal_skill") + self:loseFakeSkill("reveal_skill&") end local oldKingdom = self.kingdom @@ -1058,7 +1058,7 @@ function ServerPlayer:hideGeneral(isDeputy) local s = Fk.skills[sname] if s.relate_to_place ~= place then if s.frequency == Skill.Compulsory then - self:addFakeSkill("reveal_skill") + self:addFakeSkill("reveal_skill&") end self:addFakeSkill(s) end diff --git a/packages/standard/aux_skills.lua b/packages/standard/aux_skills.lua index 408c3521..42b31090 100644 --- a/packages/standard/aux_skills.lua +++ b/packages/standard/aux_skills.lua @@ -271,8 +271,8 @@ local revealProhibited = fk.CreateInvaliditySkill { -- 亮将 local revealSkill = fk.CreateActiveSkill{ - name = "reveal_skill", - prompt = "#reveal_skill", + name = "reveal_skill&", + prompt = "#reveal_skill&", interaction = function(self) local choiceList = {} if (Self.general == "anjiang" and not Self:prohibitReveal()) then @@ -280,7 +280,7 @@ local revealSkill = fk.CreateActiveSkill{ 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") + table.insert(choiceList, "revealMain:::" .. general.name) break end end @@ -290,13 +290,13 @@ local revealSkill = fk.CreateActiveSkill{ 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") + table.insert(choiceList, "revealDeputy:::" .. general.name) break end end end if #choiceList == 0 then return false end - return UI.ComboBox { choices = choiceList} + return UI.ComboBox { choices = choiceList } end, target_num = 0, card_num = 0, @@ -305,18 +305,16 @@ local revealSkill = fk.CreateActiveSkill{ local player = room:getPlayerById(effect.from) local choice = self.interaction.data if not choice then return false - elseif choice == "revealMain" then player:revealGeneral(false) - elseif choice == "revealDeputy" then player:revealGeneral(true) end + elseif choice:startsWith("revealMain") then player:revealGeneral(false) + elseif choice:startsWith("revealDeputy") then player:revealGeneral(true) end end, can_use = function(self, player) - local choiceList = {} if (player.general == "anjiang" and not player:prohibitReveal()) then local general = Fk.generals[player:getMark("__heg_general")] 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") - break + return true end end end @@ -325,12 +323,11 @@ local revealSkill = fk.CreateActiveSkill{ 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") - break + return true end end end - return #choiceList > 0 + return false end } diff --git a/packages/standard/i18n/en_US.lua b/packages/standard/i18n/en_US.lua index 173de1fb..bef7f82d 100644 --- a/packages/standard/i18n/en_US.lua +++ b/packages/standard/i18n/en_US.lua @@ -195,9 +195,11 @@ Fk:loadTranslationTable({ ["ex__choose_skill"] = "Choose", ["distribution_select_skill"] = "Distribute", ["choose_players_to_move_card_in_board"] = "Choose players", - ["reveal_skill"] = "Reveal", - ["#reveal_skill"] = "Choose a character to reveal", - [":reveal_skill"] = "In action phase, you can reveal a character who has Forced skills.", + ["reveal_skill&"] = "Reveal", + ["#reveal_skill&"] = "Choose a character to reveal", + [":reveal_skill&"] = "In action phase, you can reveal a character who has Forced skills.", + ["revealMain"] = "Reveal main character %arg", + ["revealDeputy"] = "Reveal deputy character %arg", ["game_rule"] = "Discard", }, "en_US") diff --git a/packages/standard/i18n/zh_CN.lua b/packages/standard/i18n/zh_CN.lua index acfd0c31..b633733c 100644 --- a/packages/standard/i18n/zh_CN.lua +++ b/packages/standard/i18n/zh_CN.lua @@ -522,9 +522,11 @@ Fk:loadTranslationTable{ ["ex__choose_skill"] = "选择", ["distribution_select_skill"] = "分配", ["choose_players_to_move_card_in_board"] = "选择角色", - ["reveal_skill"] = "亮将", - ["#reveal_skill"] = "选择一个武将亮将(点击左侧选择框展开)", - [":reveal_skill"] = "出牌阶段,你可亮出一张有锁定技的武将。", + ["reveal_skill&"] = "亮将", + ["#reveal_skill&"] = "选择一个武将亮将(点击左侧选择框展开)", + [":reveal_skill&"] = "出牌阶段,你可明置一张有锁定技的武将。", + ["revealMain"] = "明置主将 %arg", + ["revealDeputy"] = "明置副将 %arg", ["game_rule"] = "弃牌阶段", } diff --git a/packages/standard/init.lua b/packages/standard/init.lua index 90d4c728..d1bc5d2a 100644 --- a/packages/standard/init.lua +++ b/packages/standard/init.lua @@ -170,10 +170,11 @@ local tuxi = fk.CreateTriggerSkill{ 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 local p = room:getPlayerById(id) - if not p.dead then + if not p.dead and not p:isKongcheng() then local c = room:askForCardChosen(player, p, "h", self.name) room:obtainCard(player.id, c, false, fk.ReasonPrey) end @@ -689,6 +690,9 @@ local qixi = fk.CreateViewAsSkill{ c:addSubcard(cards[1]) return c end, + enabled_at_response = function (self, player, response) + return not response + end } local ganning = General:new(extension, "ganning", "wu", 4) ganning:addSkill(qixi) @@ -759,7 +763,7 @@ local fanjian = fk.CreateActiveSkill{ can_use = function(self, player) return player:usedSkillTimes(self.name, Player.HistoryPhase) == 0 end, - card_filter = function() return false end, + card_filter = Util.FalseFunc, target_filter = function(self, to_select, selected) return #selected == 0 and to_select ~= Self.id end, @@ -801,6 +805,9 @@ local guose = fk.CreateViewAsSkill{ c:addSubcard(cards[1]) return c end, + enabled_at_response = function (self, player, response) + return not response + end } local liuli = fk.CreateTriggerSkill{ name = "liuli", @@ -937,9 +944,7 @@ local jieyin = fk.CreateActiveSkill{ end, target_filter = function(self, to_select, selected) local target = Fk:currentRoom():getPlayerById(to_select) - return target:isWounded() and - (target.gender == General.Male or target.gender == General.Bigender) - and #selected < 1 and to_select ~= Self.id + return target:isWounded() and target:isMale() and #selected < 1 and to_select ~= Self.id end, target_num = 1, card_num = 2, @@ -1015,11 +1020,9 @@ local jijiu = fk.CreateViewAsSkill{ c:addSubcard(cards[1]) return c end, - enabled_at_play = function(self, player) - return false - end, - enabled_at_response = function(self, player) - return player.phase == Player.NotActive + enabled_at_play = Util.FalseFunc, + enabled_at_response = function(self, player, res) + return player.phase == Player.NotActive and not res end, } local huatuo = General:new(extension, "huatuo", "qun", 3) @@ -1067,8 +1070,7 @@ local lijian = fk.CreateActiveSkill{ end, target_filter = function(self, to_select, selected) if #selected < 2 and to_select ~= Self.id then - local target = Fk:currentRoom():getPlayerById(to_select) - return target.gender == General.Male or target.gender == General.Bigender + return Fk:currentRoom():getPlayerById(to_select):isMale() end end, target_num = 2, diff --git a/packages/standard_cards/init.lua b/packages/standard_cards/init.lua index b43b9580..d4827b6a 100644 --- a/packages/standard_cards/init.lua +++ b/packages/standard_cards/init.lua @@ -1157,7 +1157,8 @@ local eightDiagramSkill = fk.CreateTriggerSkill{ events = {fk.AskForCardUse, fk.AskForCardResponse}, can_trigger = function(self, event, target, player, data) return target == player and player:hasSkill(self) and - (data.cardName == "jink" or (data.pattern and Exppattern:Parse(data.pattern):matchExp("jink|0|nosuit|none"))) + (data.cardName == "jink" or (data.pattern and Exppattern:Parse(data.pattern):matchExp("jink|0|nosuit|none"))) and + (event == fk.AskForCardUse and not player:prohibitUse(Fk:cloneCard("jink")) or not player:prohibitResponse(Fk:cloneCard("jink"))) end, on_use = function(self, event, target, player, data) local room = player.room