diff --git a/Fk/Pages/Room.qml b/Fk/Pages/Room.qml index eee7812b..4e2bbf15 100644 --- a/Fk/Pages/Room.qml +++ b/Fk/Pages/Room.qml @@ -42,6 +42,7 @@ Item { property bool respond_play: false property bool autoPending: false property var extra_data: ({}) + property var skippedUseEventId: [] Image { source: config.roomBg @@ -571,6 +572,16 @@ Item { spacing: 20 visible: false + Button { + id: skipNullificationButton + text: Backend.translate("SkipNullification") + visible: !!extra_data.useEventId && !skippedUseEventId.find(id => id === extra_data.useEventId) + onClicked: { + skippedUseEventId.push(extra_data.useEventId); + Logic.doCancelButton(); + } + } + Button { id: okButton text: Backend.translate("OK") diff --git a/Fk/Pages/RoomLogic.js b/Fk/Pages/RoomLogic.js index b7f84964..62d460e7 100644 --- a/Fk/Pages/RoomLogic.js +++ b/Fk/Pages/RoomLogic.js @@ -999,20 +999,22 @@ callbacks["AskForCardsChosen"] = (jsonData) => { callbacks["AskForMoveCardInBoard"] = (jsonData) => { const data = JSON.parse(jsonData); - const { cards, cardsPosition, generalNames } = data; + const { cards, cardsPosition, generalNames, playerIds } = data; roomScene.state = "replying"; roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/MoveCardInBoardBox.qml"); const boxCards = []; cards.forEach(id => { - const d = Backend.callLuaFunction("GetCardData", [id]); + const cardPos = cardsPosition[cards.findIndex(cid => cid === id)]; + const d = Backend.callLuaFunction("GetCardData", [id, playerIds[cardPos]]); boxCards.push(JSON.parse(d)); }); const box = roomScene.popupBox.item; box.cards = boxCards; box.cardsPosition = cardsPosition; + box.playerIds = playerIds; box.generalNames = generalNames.map(name => { const namesSplited = name.split('/'); return namesSplited.length > 1 ? namesSplited.map(nameSplited => Backend.translate(nameSplited)).join('/') : Backend.translate(name) @@ -1127,7 +1129,13 @@ callbacks["AskForUseCard"] = (jsonData) => { const prompt = data[2]; const extra_data = data[4]; if (extra_data != null) { - roomScene.extra_data = extra_data; + if (extra_data.effectTo !== Self.id && roomScene.skippedUseEventId.find(id => id === extra_data.useEventId)) { + doCancelButton(); + return; + } else { + console.log(extra_data); + roomScene.extra_data = extra_data; + } } if (prompt === "") { diff --git a/Fk/RoomElement/MoveCardInBoardBox.qml b/Fk/RoomElement/MoveCardInBoardBox.qml index dac68c97..f88f3773 100644 --- a/Fk/RoomElement/MoveCardInBoardBox.qml +++ b/Fk/RoomElement/MoveCardInBoardBox.qml @@ -9,6 +9,7 @@ GraphicsBox { property var cards: [] property var cardsPosition: [] property var generalNames: [] + property var playerIds: [] property var result property int padding: 25 @@ -64,7 +65,7 @@ GraphicsBox { Text { horizontalAlignment: Text.AlignHCenter anchors.centerIn: parent - text: Backend.translate(JSON.parse(Backend.callLuaFunction("GetCardData", [modelData.cid])).subtype) + text: Backend.translate(modelData.subtype) color: "#90765F" font.family: fontLibian.name font.pixelSize: 16 @@ -100,6 +101,7 @@ GraphicsBox { name: modelData.name suit: modelData.suit number: modelData.number + virt_name: modelData.virt_name || '' selectable: !result || result.item === this onClicked: { diff --git a/lua/client/client_util.lua b/lua/client/client_util.lua index 5ca9063d..db5446a8 100644 --- a/lua/client/client_util.lua +++ b/lua/client/client_util.lua @@ -76,7 +76,7 @@ local cardSubtypeStrings = { [Card.SubtypeTreasure] = "treasure", } -function GetCardData(id) +function GetCardData(id, virtualCardForm) local card = Fk:getCardById(id) if card == nil then return json.encode{ cid = id, @@ -106,6 +106,13 @@ function GetCardData(id) ret.name = orig.name ret.virt_name = card.name end + if virtualCardForm then + local virtualCard = ClientInstance:getPlayerById(virtualCardForm):getVirualEquip(id) + if virtualCard then + ret.virt_name = virtualCard.name + ret.subtype = cardSubtypeStrings[virtualCard.sub_type] + end + end return json.encode(ret) end diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index e2667000..8d04fb5a 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -137,6 +137,7 @@ FreeKill使用的是libgit2的C API,与此同时使用Git完成拓展包的下 ["Exit Lobby"] = "退出大厅", + ["SkipNullification"] = "本轮忽略", ["OK"] = "确定", ["Cancel"] = "取消", ["End"] = "结束", diff --git a/lua/core/player.lua b/lua/core/player.lua index e6bf3398..61257d1a 100644 --- a/lua/core/player.lua +++ b/lua/core/player.lua @@ -856,7 +856,7 @@ function Player:canMoveCardInBoardTo(to, id) return false end - local card = Fk:getCardById(id) + local card = self:getVirualEquip(id) or Fk:getCardById(id) assert(card.type == Card.TypeEquip or card.sub_type == Card.SubtypeDelayedTrick) if card.type == Card.TypeEquip then @@ -868,12 +868,13 @@ function Player:canMoveCardInBoardTo(to, id) end end -function Player:canMoveCardsInBoardTo(to, flag) +function Player:canMoveCardsInBoardTo(to, flag, excludeIds) if self == to then return false end assert(flag == nil or flag == "e" or flag == "j") + excludeIds = type(excludeIds) == "table" and excludeIds or {} local areas = {} if flag == "e" then @@ -885,7 +886,7 @@ function Player:canMoveCardsInBoardTo(to, flag) end for _, cardId in ipairs(self:getCardIds(areas)) do - if self:canMoveCardInBoardTo(to, cardId) then + if not table.contains(excludeIds, cardId) and self:canMoveCardInBoardTo(to, cardId) then return true end end diff --git a/lua/server/events/hp.lua b/lua/server/events/hp.lua index ce417a70..d647d622 100644 --- a/lua/server/events/hp.lua +++ b/lua/server/events/hp.lua @@ -191,7 +191,7 @@ GameEvent.exit_funcs[GameEvent.Damage] = function(self) room.logic:trigger(fk.DamageFinished, damageStruct.to, damageStruct) if damageStruct.beginnerOfTheDamage and not damageStruct.chain then - local targets = table.filter(room:getAlivePlayers(), function(p) + local targets = table.filter(room:getOtherPlayers(damageStruct.to), function(p) return p.chained end) for _, p in ipairs(targets) do @@ -225,7 +225,7 @@ GameEvent.functions[GameEvent.LoseHp] = function(self) self.logic:breakEvent(false) end - if not self:changeHp(player, -num, "loseHp", skillName) then + if not self:changeHp(player, -data.num, "loseHp", skillName) then self.logic:breakEvent(false) end diff --git a/lua/server/events/movecard.lua b/lua/server/events/movecard.lua index 25bf9c7b..99a38102 100644 --- a/lua/server/events/movecard.lua +++ b/lua/server/events/movecard.lua @@ -62,12 +62,10 @@ GameEvent.functions[GameEvent.MoveCards] = function(self) for _, info in ipairs(data.moveInfo) do local realFromArea = self:getCardArea(info.cardId) local playerAreas = { Player.Hand, Player.Equip, Player.Judge, Player.Special } - local virtualEquip if table.contains(playerAreas, realFromArea) and data.from then local from = self:getPlayerById(data.from) from:removeCards(realFromArea, { info.cardId }, info.fromSpecialName) - virtualEquip = from:getVirualEquip(info.cardId) elseif realFromArea ~= Card.Unknown then local fromAreaIds = {} @@ -86,7 +84,6 @@ GameEvent.functions[GameEvent.MoveCards] = function(self) if table.contains(playerAreas, data.toArea) and data.to then local to = self:getPlayerById(data.to) - if virtualEquip then to:addVirtualEquip(virtualEquip) end to:addCards(data.toArea, { info.cardId }, data.specialName) else diff --git a/lua/server/room.lua b/lua/server/room.lua index d6da4487..076728b1 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -790,6 +790,20 @@ function Room:notifyMoveCards(players, card_moves, forceVisible) return false end + for _, info in ipairs(move.moveInfo) do + local realFromArea = self:getCardArea(info.cardId) + local playerAreas = { Player.Hand, Player.Equip, Player.Judge, Player.Special } + local virtualEquip + + if table.contains(playerAreas, realFromArea) and move.from then + virtualEquip = self:getPlayerById(move.from):getVirualEquip(info.cardId) + end + + if table.contains(playerAreas, move.toArea) and move.to and virtualEquip then + self:getPlayerById(move.to):addVirtualEquip(virtualEquip) + end + end + -- forceVisible make the move visible -- FIXME: move.moveInfo is an array, fix this move.moveVisible = move.moveVisible or (forceVisible) @@ -1840,26 +1854,29 @@ end ---@param skillName string @ 技能名 ---@param flag string|null @ 限定可移动的区域,值为nil(装备区和判定区)、‘e’或‘j’ ---@param moveFrom ServerPlayer|null @ 是否只是目标1移动给目标2 ----@return table<"card"|"from"|"to"> @ 选择的卡牌、起点玩家id和终点玩家id列表 -function Room:askForMoveCardInBoard(player, targetOne, targetTwo, skillName, flag, moveFrom) +---@param excludeIds CardId[]|null @ 本次不可移动的卡牌id +---@return table<"card"|"from"|"to">|null @ 选择的卡牌、起点玩家id和终点玩家id列表 +function Room:askForMoveCardInBoard(player, targetOne, targetTwo, skillName, flag, moveFrom, excludeIds) if flag then assert(flag == "e" or flag == "j") end + excludeIds = type(excludeIds) == "table" and excludeIds or {} + local cards = {} local cardsPosition = {} if not flag or flag == "e" then if not moveFrom or moveFrom == targetOne then for _, equipId in ipairs(targetOne:getCardIds(Player.Equip)) do - if targetOne:canMoveCardInBoardTo(targetTwo, equipId) then + if not table.contains(excludeIds, equipId) and targetOne:canMoveCardInBoardTo(targetTwo, equipId) then table.insert(cards, equipId) end end end if not moveFrom or moveFrom == targetTwo then for _, equipId in ipairs(targetTwo:getCardIds(Player.Equip)) do - if targetTwo:canMoveCardInBoardTo(targetOne, equipId) then + if not table.contains(excludeIds, equipId) and targetTwo:canMoveCardInBoardTo(targetOne, equipId) then table.insert(cards, equipId) end end @@ -1882,7 +1899,7 @@ function Room:askForMoveCardInBoard(player, targetOne, targetTwo, skillName, fla if not flag or flag == "j" then if not moveFrom or moveFrom == targetOne then for _, trickId in ipairs(targetOne:getCardIds(Player.Judge)) do - if targetOne:canMoveCardInBoardTo(targetTwo, trickId) then + if not table.contains(excludeIds, trickId) and targetOne:canMoveCardInBoardTo(targetTwo, trickId) then table.insert(cards, trickId) table.insert(cardsPosition, 0) end @@ -1890,7 +1907,7 @@ function Room:askForMoveCardInBoard(player, targetOne, targetTwo, skillName, fla end if not moveFrom or moveFrom == targetTwo then for _, trickId in ipairs(targetTwo:getCardIds(Player.Judge)) do - if targetTwo:canMoveCardInBoardTo(targetOne, trickId) then + if not table.contains(excludeIds, trickId) and targetTwo:canMoveCardInBoardTo(targetOne, trickId) then table.insert(cards, trickId) table.insert(cardsPosition, 1) end @@ -1905,7 +1922,12 @@ function Room:askForMoveCardInBoard(player, targetOne, targetTwo, skillName, fla local firstGeneralName = targetOne.general + (targetOne.deputyGeneral ~= "" and ("/" .. targetOne.deputyGeneral) or "") local secGeneralName = targetTwo.general + (targetTwo.deputyGeneral ~= "" and ("/" .. targetTwo.deputyGeneral) or "") - local data = { cards = cards, cardsPosition = cardsPosition, generalNames = { firstGeneralName, secGeneralName } } + local data = { + cards = cards, + cardsPosition = cardsPosition, + generalNames = { firstGeneralName, secGeneralName }, + playerIds = { targetOne.id, targetTwo.id } + } local command = "AskForMoveCardInBoard" self:notifyMoveFocus(player, command) local result = self:doRequest(player, command, json.encode(data)) @@ -1917,8 +1939,8 @@ function Room:askForMoveCardInBoard(player, targetOne, targetTwo, skillName, fla result = json.decode(result) end - local cardToMove = Fk:getCardById(result.cardId) local from, to = result.pos == 0 and targetOne, targetTwo or targetTwo, targetOne + local cardToMove = self:getCardOwner(result.cardId):getVirualEquip(result.cardId) or Fk:getCardById(result.cardId) self:moveCardTo( cardToMove, cardToMove.type == Card.TypeEquip and Player.Equip or Player.Judge, @@ -1940,16 +1962,18 @@ end ---@param flag string|null @ 限定可移动的区域,值为nil(装备区和判定区)、‘e’或‘j’ ---@param no_indicate boolean|nil @ 是否不显示指示线 ---@return integer[] @ 选择的玩家id列表,可能为空 -function Room:askForChooseToMoveCardInBoard(player, prompt, skillName, cancelable, flag, no_indicate) +function Room:askForChooseToMoveCardInBoard(player, prompt, skillName, cancelable, flag, no_indicate, excludeIds) if flag then assert(flag == "e" or flag == "j") end cancelable = (cancelable == nil) and true or cancelable no_indicate = (no_indicate == nil) and true or no_indicate + excludeIds = type(excludeIds) == "table" and excludeIds or {} local data = { flag = flag, skillName = skillName, + excludeIds = excludeIds, } local _, ret = self:askForUseActiveSkill( player, @@ -1966,7 +1990,7 @@ function Room:askForChooseToMoveCardInBoard(player, prompt, skillName, cancelabl if cancelable then return {} else - return self:canMoveCardInBoard(flag) + return self:canMoveCardInBoard(flag, excludeIds) end end end @@ -2371,7 +2395,15 @@ function Room:handleCardEffect(event, cardEffectEvent) elseif cardEffectEvent.from then prompt = "#AskForNullificationWithoutTo:" .. cardEffectEvent.from .. "::" .. cardEffectEvent.card.name end - local use = self:askForNullification(players, nil, nil, prompt) + + local extra_data + if #TargetGroup:getRealTargets(cardEffectEvent.tos) > 1 then + local parentUseEvent = self.logic:getCurrentEvent():findParent(GameEvent.UseCard) + if parentUseEvent then + extra_data = { useEventId = parentUseEvent.id, effectTo = cardEffectEvent.to } + end + end + local use = self:askForNullification(players, nil, nil, prompt, true, extra_data) if use then use.toCard = cardEffectEvent.card use.responseToEvent = cardEffectEvent @@ -3062,18 +3094,20 @@ end ---@param flag string|null ---@param players ServerPlayer[]|null +---@param excludeIds CardId[]|null ---@return PlayerId[] @ 可能为空 -function Room:canMoveCardInBoard(flag, players) +function Room:canMoveCardInBoard(flag, players, excludeIds) if flag then assert(flag == "e" or flag == "j") end players = players or self.alive_players + excludeIds = type(excludeIds) == "table" and excludeIds or {} local targets = {} table.find(players, function(p) local canMoveTo = table.find(players, function(another) - return p ~= another and p:canMoveCardsInBoardTo(another, flag) + return p ~= another and p:canMoveCardsInBoardTo(another, flag, excludeIds) end) if canMoveTo then diff --git a/packages/standard/aux_skills.lua b/packages/standard/aux_skills.lua index 587a3041..a85d6592 100644 --- a/packages/standard/aux_skills.lua +++ b/packages/standard/aux_skills.lua @@ -99,10 +99,19 @@ local choosePlayersToMoveCardInBoardSkill = fk.CreateActiveSkill{ target_filter = function(self, to_select, selected, cards) local target = Fk:currentRoom():getPlayerById(to_select) if #selected > 0 then - return Fk:currentRoom():getPlayerById(selected[1]):canMoveCardsInBoardTo(target, self.flag) + return Fk:currentRoom():getPlayerById(selected[1]):canMoveCardsInBoardTo(target, self.flag, self.excludeIds) end - return #target:getCardIds({ Player.Equip, Player.Judge }) > 0 + local fromAreas = { Player.Equip, Player.Judge } + if self.flag == "e" then + fromAreas = { Player.Equip } + elseif self.flag == "j" then + fromAreas = { Player.Judge } + end + + return #table.filter(target:getCardIds(fromAreas), function(id) + return not table.contains((type(self.excludeIds) == "table" and self.excludeIds or {}), id) + end) > 0 end, } diff --git a/packages/standard/init.lua b/packages/standard/init.lua index f0086786..6dfecee3 100644 --- a/packages/standard/init.lua +++ b/packages/standard/init.lua @@ -1092,8 +1092,22 @@ local role_mode = fk.CreateGameMode{ roleTable = roleTable[#Fk:currentRoom().players] if Self.role == "renegade" then - roleCheck = #Fk:currentRoom().alive_players == 2 - roleText = "only you and me" + local rebelNum = #table.filter(roleTable, function(role) + return role == "rebel" + end) + + for _, p in ipairs(Fk:currentRoom().players) do + if p.role == "rebel" then + if not p.dead then + break + else + rebelNum = rebelNum - 1 + end + end + end + + roleCheck = rebelNum == 0 + roleText = "left lord and loyalist alive" elseif Self.role == "rebel" then local rebelNum = #table.filter(roleTable, function(role) return role == "rebel" @@ -1162,7 +1176,7 @@ local role_mode = fk.CreateGameMode{ extension:addGameMode(role_mode) Fk:loadTranslationTable{ ["time limitation: 5 sec"] = "游戏时长达到5秒(测试用)", - ["only you and me"] = "仅剩你和主公存活", + ["left lord and loyalist alive"] = "仅剩你和主忠方存活", ["left one rebel alive"] = "反贼仅剩你存活且不存在存活内奸", ["left you alive"] = "主忠方仅剩你存活且其他阵营仅剩一方", ["loyalist never surrender"] = "忠臣永不投降!", diff --git a/packages/test/image/generals/blank_nvshibing.jpg b/packages/test/image/generals/blank_nvshibing.jpg new file mode 100644 index 00000000..cfe78c15 Binary files /dev/null and b/packages/test/image/generals/blank_nvshibing.jpg differ diff --git a/packages/test/image/generals/blank_shibing.jpg b/packages/test/image/generals/blank_shibing.jpg new file mode 100644 index 00000000..03253f3d Binary files /dev/null and b/packages/test/image/generals/blank_shibing.jpg differ diff --git a/packages/test/init.lua b/packages/test/init.lua index 9f1dcfec..8b53d67a 100644 --- a/packages/test/init.lua +++ b/packages/test/init.lua @@ -1,6 +1,7 @@ -- SPDX-License-Identifier: GPL-3.0-or-later local extension = Package("test_p_0") +extension.extensionName = "test" local cheat = fk.CreateActiveSkill{ name = "cheat", @@ -270,6 +271,18 @@ test2:addSkill(damage_maker) test2:addSkill(change_hero) test2:addSkill(test_zhenggong) +local shibing = General(extension, "blank_shibing", "qun", 5) +shibing.hidden = true +Fk:loadTranslationTable{ + ["blank_shibing"] = "男士兵", +} + +local nvshibing = General(extension, "blank_nvshibing", "qun", 5, 5, General.Female) +Fk:loadTranslationTable{ + ["blank_nvshibing"] = "女士兵", +} +nvshibing.hidden = true + Fk:loadTranslationTable{ ["test_p_0"] = "测试包", ["test"] = "测试",