diff --git a/lua/client/client.lua b/lua/client/client.lua index f850ab04..d71a3303 100644 --- a/lua/client/client.lua +++ b/lua/client/client.lua @@ -83,7 +83,9 @@ function Client:moveCards(moves) table.insert(self.discard_pile, move.ids[1]) end - Fk:filterCard(move.ids[1], move.to == Self.id and Self or nil) + if (move.ids[1] ~= -1) then + Fk:filterCard(move.ids[1], ClientInstance:getPlayerById(move.to)) + end end end diff --git a/lua/core/card.lua b/lua/core/card.lua index 65ba0204..368edc08 100644 --- a/lua/core/card.lua +++ b/lua/core/card.lua @@ -86,6 +86,7 @@ end function Card:clone(suit, number) local newCard = self.class:new(self.name, suit, number) newCard.skill = self.skill + newCard.equip_skill = self.equip_skill return newCard end diff --git a/lua/core/card_type/equip.lua b/lua/core/card_type/equip.lua index 37db029e..ddfa2dd5 100644 --- a/lua/core/card_type/equip.lua +++ b/lua/core/card_type/equip.lua @@ -1,11 +1,27 @@ ---@class EquipCard : Card ----@field equipSkill Skill +---@field equip_skill Skill local EquipCard = Card:subclass("EquipCard") function EquipCard:initialize(name, suit, number) Card.initialize(self, name, suit, number) self.type = Card.TypeEquip - self.equipSkill = nil + self.equip_skill = nil +end + +---@param room Room +---@param player Player +function EquipCard:onInstall(room, player) + if self.equip_skill then + room:handleAddLoseSkills(player, self.equip_skill.name, nil, false, true) + end +end + +---@param room Room +---@param player Player +function EquipCard:onUninstall(room, player) + if self.equip_skill then + room:handleAddLoseSkills(player, "-" .. self.equip_skill.name, nil, false, true) + end end ---@class Weapon : EquipCard diff --git a/lua/core/player.lua b/lua/core/player.lua index 3a10f6ae..66c6932c 100644 --- a/lua/core/player.lua +++ b/lua/core/player.lua @@ -460,27 +460,10 @@ local function getActualSkill(skill) return skill end ----@param skill string | Skill -function Player:hasEquipSkill(skill) - skill = getActualSkill(skill) - local equips = self.player_cards[Player.Equip] - for _, id in ipairs(equips) do - local card = Fk:getCardById(id) - if card.skill == skill then - return true - end - end - return false -end - ---@param skill string | Skill function Player:hasSkill(skill) skill = getActualSkill(skill) - if self:hasEquipSkill(skill) then - return true - end - if table.contains(self.player_skills, skill) then return true end diff --git a/lua/core/skill.lua b/lua/core/skill.lua index 3e8120d4..5299c18f 100644 --- a/lua/core/skill.lua +++ b/lua/core/skill.lua @@ -6,6 +6,7 @@ ---@field mute boolean ---@field anim_type string ---@field related_skills Skill[] +---@field attached_equip string local Skill = class("Skill") ---@alias Frequency integer @@ -32,6 +33,8 @@ function Skill:initialize(name, frequency) if string.sub(name, 1, 1) == "#" then self.visible = false end + + self.attached_equip = nil end ---@param skill Skill @@ -39,4 +42,9 @@ function Skill:addRelatedSkill(skill) table.insert(self.related_skills, skill) end +---@return boolean +function Skill:isEquipmentSkill() + return self.attached_equip and type(self.attached_equip) == 'string' and self.attached_equip ~= "" +end + return Skill diff --git a/lua/fk_ex.lua b/lua/fk_ex.lua index 3cf12c00..ae56fa56 100644 --- a/lua/fk_ex.lua +++ b/lua/fk_ex.lua @@ -90,15 +90,17 @@ function fk.CreateTriggerSkill(spec) skill.refresh = spec.on_refresh end - if not spec.priority then - if frequency == Skill.Wake then - spec.priority = 3 - elseif frequency == Skill.Compulsory then - spec.priority = 2 - else - spec.priority = 1 + if spec.attached_equip then + assert(type(spec.attached_equip) == "string") + skill.attached_equip = spec.attached_equip + + if not spec.priority then + spec.priority = 0.1 end + elseif not spec.priority then + spec.priority = 1 end + if type(spec.priority) == "number" then for _, event in ipairs(skill.events) do skill.priority_table[event] = spec.priority @@ -137,6 +139,11 @@ function fk.CreateActiveSkill(spec) } skill.distance_limit = spec.distance_limit or skill.distance_limit + if spec.attached_equip then + assert(type(spec.attached_equip) == "string") + skill.attached_equip = spec.attached_equip + end + if spec.can_use then skill.canUse = spec.can_use end if spec.card_filter then skill.cardFilter = spec.card_filter end if spec.target_filter then skill.targetFilter = spec.target_filter end @@ -173,6 +180,11 @@ function fk.CreateViewAsSkill(spec) } skill.distance_limit = spec.distance_limit or skill.distance_limit + if spec.attached_equip then + assert(type(spec.attached_equip) == "string") + skill.attached_equip = spec.attached_equip + end + skill.viewAs = spec.view_as if spec.card_filter then skill.cardFilter = spec.card_filter @@ -205,6 +217,11 @@ function fk.CreateDistanceSkill(spec) skill.global = spec.global end + if spec.attached_equip then + assert(type(spec.attached_equip) == "string") + skill.attached_equip = spec.attached_equip + end + return skill end @@ -223,6 +240,11 @@ function fk.CreateProhibitSkill(spec) skill.global = spec.global end + if spec.attached_equip then + assert(type(spec.attached_equip) == "string") + skill.attached_equip = spec.attached_equip + end + return skill end @@ -241,6 +263,11 @@ function fk.CreateAttackRangeSkill(spec) skill.global = spec.global end + if spec.attached_equip then + assert(type(spec.attached_equip) == "string") + skill.attached_equip = spec.attached_equip + end + return skill end @@ -265,6 +292,11 @@ function fk.CreateMaxCardsSkill(spec) skill.global = spec.global end + if spec.attached_equip then + assert(type(spec.attached_equip) == "string") + skill.attached_equip = spec.attached_equip + end + return skill end @@ -292,6 +324,11 @@ function fk.CreateTargetModSkill(spec) skill.global = spec.global end + if spec.attached_equip then + assert(type(spec.attached_equip) == "string") + skill.attached_equip = spec.attached_equip + end + return skill end @@ -382,6 +419,10 @@ function fk.CreateWeapon(spec) local card = Weapon:new(spec.name, spec.suit, spec.number, spec.attack_range) card.skill = spec.skill or defaultCardSkill + card.equip_skill = spec.equip_skill + + if spec.on_install then card.onInstall = spec.on_install end + if spec.on_uninstall then card.onUninstall = spec.on_uninstall end return card end @@ -396,6 +437,10 @@ function fk.CreateArmor(spec) local card = Armor:new(spec.name, spec.suit, spec.number) card.skill = spec.skill or defaultCardSkill + card.equip_skill = spec.equip_skill + + if spec.on_install then card.onInstall = spec.on_install end + if spec.on_uninstall then card.onUninstall = spec.on_uninstall end return card end @@ -410,6 +455,10 @@ function fk.CreateDefensiveRide(spec) local card = DefensiveRide:new(spec.name, spec.suit, spec.number) card.skill = spec.skill or defaultCardSkill + card.equip_skill = spec.equip_skill + + if spec.on_install then card.onInstall = spec.on_install end + if spec.on_uninstall then card.onUninstall = spec.on_uninstall end return card end @@ -424,6 +473,10 @@ function fk.CreateOffensiveRide(spec) local card = OffensiveRide:new(spec.name, spec.suit, spec.number) card.skill = spec.skill or defaultCardSkill + card.equip_skill = spec.equip_skill + + if spec.on_install then card.onInstall = spec.on_install end + if spec.on_uninstall then card.onUninstall = spec.on_uninstall end return card end @@ -438,5 +491,9 @@ function fk.CreateTreasure(spec) local card = Treasure:new(spec.name, spec.suit, spec.number) card.skill = spec.skill or defaultCardSkill + card.equip_skill = spec.equip_skill + + if spec.on_install then card.onInstall = spec.on_install end + if spec.on_uninstall then card.onUninstall = spec.on_uninstall end return card end diff --git a/lua/server/room.lua b/lua/server/room.lua index b5e81dcf..e2e8c782 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -1660,6 +1660,19 @@ function Room:moveCards(...) end self:setCardArea(info.cardId, data.toArea, data.to) Fk:filterCard(info.cardId, self:getPlayerById(data.to)) + + local currentCard = Fk:getCardById(info.cardId) + if + data.toArea == Player.Equip and + currentCard.type == Card.TypeEquip and + data.to ~= nil and + self:getPlayerById(data.to):isAlive() and + currentCard.equip_skill + then + currentCard:onInstall(self, self:getPlayerById(data.to)) + elseif realFromArea == Player.Equip and currentCard.type == Card.TypeEquip and data.from ~= nil and currentCard.equip_skill then + currentCard:onUninstall(self, self:getPlayerById(data.from)) + end end end end @@ -2032,8 +2045,9 @@ end ---@param player ServerPlayer ---@param skill_names string[] | string ----@param source_skill string | Skill | nil -function Room:handleAddLoseSkills(player, skill_names, source_skill, sendlog) +---@param source_skill string | Skill | null +---@param no_trigger boolean | null +function Room:handleAddLoseSkills(player, skill_names, source_skill, sendlog, no_trigger) if type(skill_names) == "string" then skill_names = skill_names:split("|") end @@ -2094,7 +2108,7 @@ function Room:handleAddLoseSkills(player, skill_names, source_skill, sendlog) end end - if #triggers > 0 then + if (not no_trigger) and #triggers > 0 then for i = 1, #triggers do local event = losts[i] and fk.EventLoseSkill or fk.EventAcquireSkill self.logic:trigger(event, player, triggers[i]) diff --git a/packages/standard_cards/init.lua b/packages/standard_cards/init.lua index 43742318..f4751935 100644 --- a/packages/standard_cards/init.lua +++ b/packages/standard_cards/init.lua @@ -722,11 +722,24 @@ extension:addCards({ indulgence:clone(Card.Heart, 6), }) +local crossbowSkill = fk.CreateTargetModSkill{ + name = "#crossbow_skill", + attached_equip = "crossbow", + residue_func = function(self, player, skill, scope) + if player:hasSkill(self.name) and skill.name == "slash_skill" + and scope == Player.HistoryPhase then + return 999 + end + end, +} +Fk:addSkill(crossbowSkill) + local crossbow = fk.CreateWeapon{ name = "crossbow", suit = Card.Club, number = 1, attack_range = 1, + equip_skill = crossbowSkill, } Fk:loadTranslationTable{ ["crossbow"] = "诸葛连弩", diff --git a/packages/test/init.lua b/packages/test/init.lua index 4aab2c44..d72867de 100644 --- a/packages/test/init.lua +++ b/packages/test/init.lua @@ -1,22 +1,73 @@ local fkp_extensions = require "packages.test.test" local extension = fkp_extensions[1] +local cheat = fk.CreateActiveSkill{ + name = "cheat", + anim_type = "drawcard", + can_use = function(self, player) + return true + end, + feasible = function(self, selected, selected_cards) + return #selected == 0 and #selected_cards == 0 + end, + on_use = function(self, room, effect) + local from = room:getPlayerById(effect.from) + local cardTypeName = room:askForChoice(from, { 'BasicCard', 'TrickCard', 'Equip' }, "cheat") + local cardType = Card.TypeBasic + if cardTypeName == 'TrickCard' then + cardType = Card.TypeTrick + elseif cardTypeName == 'Equip' then + cardType = Card.TypeEquip + end + + local allCardIds = Fk:getAllCardIds() + local allCardMapper = {} + local allCardNames = {} + for _, id in ipairs(allCardIds) do + local card = Fk:getCardById(id) + if card.type == cardType then + if allCardMapper[card.name] == nil then + table.insert(allCardNames, card.name) + end + + allCardMapper[card.name] = allCardMapper[card.name] or {} + table.insert(allCardMapper[card.name], id) + end + end + + if #allCardNames == 0 then + return + end + + local cardName = room:askForChoice(from, allCardNames, "cheat") + local toGain = nil + if #allCardMapper[cardName] > 0 then + toGain = allCardMapper[cardName][math.random(1, #allCardMapper[cardName])] + end + + room:obtainCard(effect.from, toGain, true, fk.ReasonPrey) + end +} local test_filter = fk.CreateFilterSkill{ name = "test_filter", card_filter = function(self, card) - return true + return card.number > 11 end, view_as = function(self, card) - return Fk:cloneCard("ex_nihilo", card.suit, card.number) + return Fk:cloneCard("crossbow", card.suit, card.number) end, } local test2 = General(extension, "mouxusheng", "wu", 4) test2:addSkill(test_filter) +test2:addSkill(cheat) Fk:loadTranslationTable{ ["test"] = "测试", ["test_filter"] = "破军", + [":test_filter"] = "你的点数大于11的牌视为无中生有。", ["mouxusheng"] = "谋徐盛", + --["cheat"] = "开挂", + [":cheat"] = "出牌阶段,你可以获得一张想要的牌。", } return fkp_extensions diff --git a/qml/Pages/RoomElement/CardArea.qml b/qml/Pages/RoomElement/CardArea.qml index 00df3311..b005db80 100644 --- a/qml/Pages/RoomElement/CardArea.qml +++ b/qml/Pages/RoomElement/CardArea.qml @@ -24,6 +24,8 @@ Item { for (let i = 0; i < cards.length; i++) { for (let j = 0; j < outputs.length; j++) { if (outputs[j] === cards[i].cid) { + let state = JSON.parse(Backend.callLuaFunction("GetCardData", [cards[i].cid])); + cards[i].setData(state); result.push(cards[i]); cards.splice(i, 1); i--; diff --git a/qml/Pages/RoomElement/CardItem.qml b/qml/Pages/RoomElement/CardItem.qml index 19e74df0..970c029b 100644 --- a/qml/Pages/RoomElement/CardItem.qml +++ b/qml/Pages/RoomElement/CardItem.qml @@ -241,6 +241,8 @@ Item { suit = data.suit; number = data.number; color = data.color; + subtype = data.subtype ? data.subtype : ""; + virt_name = data.virt_name ? data.virt_name : ""; } function toData() @@ -250,7 +252,9 @@ Item { name: name, suit: suit, number: number, - color: color + color: color, + subtype: subtype, + virt_name: virt_name, }; return data; } diff --git a/qml/Pages/RoomElement/PhotoElement/EquipItem.qml b/qml/Pages/RoomElement/PhotoElement/EquipItem.qml index 748684f5..8c29787a 100644 --- a/qml/Pages/RoomElement/PhotoElement/EquipItem.qml +++ b/qml/Pages/RoomElement/PhotoElement/EquipItem.qml @@ -122,7 +122,11 @@ Item { icon = "horse"; } else { text = Backend.translate(name); - icon = name; + if (card.virt_name) { + icon = card.virt_name; + } else { + icon = name; + } } } diff --git a/qml/main.qml b/qml/main.qml index 2a2c016f..451498d8 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -9,6 +9,8 @@ Window { visible: true width: 960 height: 540 + minimumWidth: 160 + minimumHeight: 90 property var callbacks: Logic.callbacks Item { @@ -36,7 +38,8 @@ Item { StackView { id: mainStack visible: !mainWindow.busy - initialItem: OS !== "Web" ? init : webinit + // If error occurs during loading initialItem, the program will fall into "polish()" loop + // initialItem: OS !== "Web" ? init : webinit anchors.fill: parent } @@ -151,7 +154,7 @@ Item { anchors.centerIn: parent width: Math.min(contentWidth + 24, realMainWin.width * 0.9) height: Math.min(contentHeight + 24, realMainWin.height * 0.9) - closePolicy: Popup.CloseOnEscape + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside padding: 12 contentItem: Text { text: errDialog.txt @@ -197,6 +200,11 @@ Item { } Component.onCompleted: { + if (OS !== "Web") { + mainStack.push(init); + } else { + mainStack.push(webinit); + } if (OS !== "Android" && OS !== "Web") { width = config.winWidth; height = config.winHeight; diff --git a/src/ui/qmlbackend.cpp b/src/ui/qmlbackend.cpp index 4219a250..af9beedb 100644 --- a/src/ui/qmlbackend.cpp +++ b/src/ui/qmlbackend.cpp @@ -77,8 +77,8 @@ void QmlBackend::quitLobby() { if (ClientInstance) delete ClientInstance; - if (ServerInstance) - delete ServerInstance; + // if (ServerInstance) + // delete ServerInstance; } void QmlBackend::emitNotifyUI(const QString &command, const QString &jsonData) {