diff --git a/image/photo/magatama/shield.png b/image/photo/magatama/shield.png new file mode 100644 index 00000000..d68ef5ef Binary files /dev/null and b/image/photo/magatama/shield.png differ diff --git a/lua/client/client.lua b/lua/client/client.lua index 1dfde8ab..d121f28f 100644 --- a/lua/client/client.lua +++ b/lua/client/client.lua @@ -82,7 +82,9 @@ function Client:moveCards(moves) table.remove(from.player_cards[Player.Hand]) end else - from:removeCards(move.fromArea, move.ids, move.fromSpecialName) + if table.contains({ Player.Hand, Player.Equip, Player.Judge, Player.Special }, move.fromArea) then + from:removeCards(move.fromArea, move.ids, move.fromSpecialName) + end end elseif move.fromArea == Card.DiscardPile then table.removeOne(self.discard_pile, move.ids[1]) diff --git a/lua/client/client_util.lua b/lua/client/client_util.lua index c3f699da..6c8d5f8e 100644 --- a/lua/client/client_util.lua +++ b/lua/client/client_util.lua @@ -14,7 +14,8 @@ function GetGeneralData(name) extension = general.package.extensionName, kingdom = general.kingdom, hp = general.hp, - maxHp = general.maxHp + maxHp = general.maxHp, + shield = general.shield, } end @@ -168,28 +169,13 @@ function CanUseCard(card, player) player = ClientInstance:getPlayerById(player) local ret = c.skill:canUse(player, c) - if ret then - local status_skills = Fk:currentRoom().status_skills[ProhibitSkill] or {} - for _, skill in ipairs(status_skills) do - if skill:prohibitUse(player, c) then - ret = false - break - end - end - end + ret = ret and not player:prohibitUse(c) return json.encode(ret) end function CardProhibitedUse(cid) local c = Fk:getCardById(cid) - local ret = false - local status_skills = Fk:currentRoom().status_skills[ProhibitSkill] or {} - for _, skill in ipairs(status_skills) do - if skill:prohibitUse(Self, c) then - ret = true - break - end - end + local ret = Self:prohibitUse(c) return json.encode(ret) end @@ -211,16 +197,7 @@ function CanUseCardToTarget(card, to_select, selected) end local ret = c.skill:targetFilter(to_select, selected, selected_cards, c) - if ret then - local r = Fk:currentRoom() - local status_skills = r.status_skills[ProhibitSkill] or {} - for _, skill in ipairs(status_skills) do - if skill:isProhibited(Self, r:getPlayerById(to_select), c) then - ret = false - break - end - end - end + ret = ret and not Self:isProhibited(Fk:currentRoom():getPlayerById(to_select), c) return json.encode(ret) end @@ -403,14 +380,7 @@ end function CardProhibitedResponse(cid) local c = Fk:getCardById(cid) - local ret = false - local status_skills = Fk:currentRoom().status_skills[ProhibitSkill] or {} - for _, skill in ipairs(status_skills) do - if skill:prohibitResponse(Self, c) then - ret = true - break - end - end + local ret = Self:prohibitResponse(c) return json.encode(ret) end @@ -453,12 +423,19 @@ end function GetInteractionOfSkill(skill_name) local skill = Fk.skills[skill_name] - return skill and json.encode(skill.interaction) or "null" + if skill and skill.interaction then + if type(skill.interaction) == "function" then + return json.encode(skill:interaction()) + else + return json.encode(skill.interaction) + end + end + return "null" end function SetInteractionDataOfSkill(skill_name, data) local skill = Fk.skills[skill_name] - if skill and type(skill.interaction) == "table" then + if skill and skill.interaction then skill.interaction.data = json.decode(data) end end diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index 6ca8daa3..7ffa5887 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -171,6 +171,7 @@ Fk:loadTranslationTable{ ["normal_damage"] = "无属性", ["fire_damage"] = "火属性", ["thunder_damage"] = "雷属性", + ["ice_damage"] = "冰属性", ["phase_judge"] = "判定阶段", ["phase_draw"] = "摸牌阶段", diff --git a/lua/core/card.lua b/lua/core/card.lua index 75ef8bc2..ff8b2422 100644 --- a/lua/core/card.lua +++ b/lua/core/card.lua @@ -20,6 +20,7 @@ ---@field public skillNames string[] ---@field public skill Skill ---@field public special_skills string[] | nil +---@field public is_damage_card boolean local Card = class("Card") ---@alias Suit integer @@ -162,7 +163,7 @@ local function updateColorAndNumber(card) number = math.min(number + c.number, 13) if color ~= c.color then if not different_color then - if color ~= Card.NoColor then + if c.color ~= Card.NoColor then different_color = true end color = c.color @@ -183,8 +184,16 @@ function Card:addSubcard(card) table.insert(self.subcards, card) else assert(card:isInstanceOf(Card)) - assert(not card:isVirtual(), "Can not add virtual card as subcard") - table.insert(self.subcards, card.id) + -- assert(not card:isVirtual(), "Can not add virtual card as subcard") + if card:isVirtual() then + table.insertTable(self.subcards, card.subcards) + else + table.insert(self.subcards, card.id) + end + + for _, skill in ipairs(card.skillNames) do + self.skillName = skill + end end updateColorAndNumber(self) diff --git a/lua/core/general.lua b/lua/core/general.lua index 8a53d449..ae98f342 100644 --- a/lua/core/general.lua +++ b/lua/core/general.lua @@ -13,6 +13,7 @@ ---@field public kingdom string @ 武将所属势力 ---@field public hp integer @ 武将初始体力 ---@field public maxHp integer @ 武将初始最大体力 +---@field public shield integer @ 初始护甲 ---@field public gender Gender @ 武将性别 ---@field public skills Skill[] @ 武将技能 ---@field public other_skills string[] @ 武将身上属于其他武将的技能,通过字符串调用 @@ -40,6 +41,7 @@ function General:initialize(package, name, kingdom, hp, maxHp, gender) self.hp = hp self.maxHp = maxHp or hp self.gender = gender or General.Male + self.shield = 0 self.skills = {} -- skills first added to this general self.other_skills = {} -- skill belongs other general, e.g. "mashu" of pangde diff --git a/lua/core/player.lua b/lua/core/player.lua index e5c1b47e..027f0e14 100644 --- a/lua/core/player.lua +++ b/lua/core/player.lua @@ -10,6 +10,7 @@ ---@field public id integer @ 玩家的id,每名玩家的id是唯一的。机器人的id是负数。 ---@field public hp integer @ 体力值 ---@field public maxHp integer @ 体力上限 +---@field public shield integer @ 护甲数 ---@field public kingdom string @ 势力 ---@field public role string @ 身份 ---@field public general string @ 武将 @@ -667,4 +668,50 @@ function Player:getAllSkills() return ret end +---@param to Player +---@param card Card +function Player:isProhibited(to, card) + local r = Fk:currentRoom() + local status_skills = r.status_skills[ProhibitSkill] or {} + for _, skill in ipairs(status_skills) do + if skill:isProhibited(self, to, card) then + return true + end + end + return false +end + +---@param card Card +function Player:prohibitUse(card) + local status_skills = Fk:currentRoom().status_skills[ProhibitSkill] or {} + for _, skill in ipairs(status_skills) do + if skill:prohibitUse(self, card) then + return true + end + end + return false +end + +---@param card Card +function Player:prohibitResponse(card) + local status_skills = Fk:currentRoom().status_skills[ProhibitSkill] or {} + for _, skill in ipairs(status_skills) do + if skill:prohibitResponse(self, card) then + return true + end + end + return false +end + +---@param card Card +function Player:prohibitDiscard(card) + local status_skills = Fk:currentRoom().status_skills[ProhibitSkill] or {} + for _, skill in ipairs(status_skills) do + if skill:prohibitDiscard(self, card) then + return true + end + end + return false +end + return Player diff --git a/lua/core/util.lua b/lua/core/util.lua index 541f53a0..2f0df7fd 100644 --- a/lua/core/util.lua +++ b/lua/core/util.lua @@ -1,5 +1,7 @@ -- SPDX-License-Identifier: GPL-3.0-or-later +local Util = {} + -- the iterator of QList object local qlist_iterator = function(list, n) if n < list:length() - 1 then @@ -52,9 +54,11 @@ function table:map(func) end -- frequenly used filter & map functions -IdMapper = function(e) return e.id end -Id2CardMapper = function(id) return Fk:getCardById(id) end -Id2PlayerMapper = function(id) return Fk:currentRoom():getPlayerById(id) end +Util.IdMapper = function(e) return e.id end +Util.Id2CardMapper = function(id) return Fk:getCardById(id) end +Util.Id2PlayerMapper = function(id) + return Fk:currentRoom():getPlayerById(id) +end ---@generic T ---@param self T[] @@ -467,4 +471,4 @@ function AimGroup:getCancelledTargets(aimGroup) return aimGroup[AimGroup.Cancelled] end -return { TargetGroup, AimGroup } +return { TargetGroup, AimGroup, Util } diff --git a/lua/fk_ex.lua b/lua/fk_ex.lua index d7f5cf95..82989e6d 100644 --- a/lua/fk_ex.lua +++ b/lua/fk_ex.lua @@ -345,6 +345,7 @@ end ---@class CardSpec: Card ---@field public skill Skill ---@field public equip_skill Skill +---@field public is_damage_card boolean local defaultCardSkill = fk.CreateActiveSkill{ name = "default_card_skill", @@ -355,144 +356,105 @@ local defaultCardSkill = fk.CreateActiveSkill{ end } ----@param spec CardSpec ----@return BasicCard -function fk.CreateBasicCard(spec) +local function preprocessCardSpec(spec) assert(type(spec.name) == "string" or type(spec.class_name) == "string") if not spec.name then spec.name = spec.class_name elseif not spec.class_name then spec.class_name = spec.name end if spec.suit then assert(type(spec.suit) == "number") end if spec.number then assert(type(spec.number) == "number") end +end - local card = BasicCard:new(spec.name, spec.suit, spec.number) +local function readCardSpecToCard(card, spec) card.skill = spec.skill or defaultCardSkill card.special_skills = spec.special_skills + card.is_damage_card = spec.is_damage_card +end + +---@param spec CardSpec +---@return BasicCard +function fk.CreateBasicCard(spec) + preprocessCardSpec(spec) + local card = BasicCard:new(spec.name, spec.suit, spec.number) + readCardSpecToCard(card, spec) return card end ---@param spec CardSpec ---@return TrickCard function fk.CreateTrickCard(spec) - assert(type(spec.name) == "string" or type(spec.class_name) == "string") - if not spec.name then spec.name = spec.class_name - elseif not spec.class_name then spec.class_name = spec.name end - if spec.suit then assert(type(spec.suit) == "number") end - if spec.number then assert(type(spec.number) == "number") end - + preprocessCardSpec(spec) local card = TrickCard:new(spec.name, spec.suit, spec.number) - card.skill = spec.skill or defaultCardSkill - card.special_skills = spec.special_skills + readCardSpecToCard(card, spec) return card end ---@param spec CardSpec ---@return DelayedTrickCard function fk.CreateDelayedTrickCard(spec) - assert(type(spec.name) == "string" or type(spec.class_name) == "string") - if not spec.name then spec.name = spec.class_name - elseif not spec.class_name then spec.class_name = spec.name end - if spec.suit then assert(type(spec.suit) == "number") end - if spec.number then assert(type(spec.number) == "number") end - + preprocessCardSpec(spec) local card = DelayedTrickCard:new(spec.name, spec.suit, spec.number) - card.skill = spec.skill or defaultCardSkill - card.special_skills = spec.special_skills + readCardSpecToCard(card, spec) return card end +local function readCardSpecToEquip(card, spec) + 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 +end + ---@param spec CardSpec ---@return Weapon function fk.CreateWeapon(spec) - assert(type(spec.name) == "string" or type(spec.class_name) == "string") - if not spec.name then spec.name = spec.class_name - elseif not spec.class_name then spec.class_name = spec.name end - if spec.suit then assert(type(spec.suit) == "number") end - if spec.number then assert(type(spec.number) == "number") end - if spec.attack_range then assert(type(spec.attack_range) == "number" and spec.attack_range >= 0) end + preprocessCardSpec(spec) + if spec.attack_range then + assert(type(spec.attack_range) == "number" and spec.attack_range >= 0) + end local card = Weapon:new(spec.name, spec.suit, spec.number, spec.attack_range) - card.skill = spec.skill or defaultCardSkill - card.special_skills = spec.special_skills - 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 + readCardSpecToCard(card, spec) + readCardSpecToEquip(card, spec) return card end ---@param spec CardSpec ---@return Armor function fk.CreateArmor(spec) - assert(type(spec.name) == "string" or type(spec.class_name) == "string") - if not spec.name then spec.name = spec.class_name - elseif not spec.class_name then spec.class_name = spec.name end - if spec.suit then assert(type(spec.suit) == "number") end - if spec.number then assert(type(spec.number) == "number") end - + preprocessCardSpec(spec) local card = Armor:new(spec.name, spec.suit, spec.number) - card.skill = spec.skill or defaultCardSkill - card.equip_skill = spec.equip_skill - card.special_skills = spec.special_skills - - if spec.on_install then card.onInstall = spec.on_install end - if spec.on_uninstall then card.onUninstall = spec.on_uninstall end + readCardSpecToCard(card, spec) + readCardSpecToEquip(card, spec) return card end ---@param spec CardSpec ---@return DefensiveRide function fk.CreateDefensiveRide(spec) - assert(type(spec.name) == "string" or type(spec.class_name) == "string") - if not spec.name then spec.name = spec.class_name - elseif not spec.class_name then spec.class_name = spec.name end - if spec.suit then assert(type(spec.suit) == "number") end - if spec.number then assert(type(spec.number) == "number") end - + preprocessCardSpec(spec) local card = DefensiveRide:new(spec.name, spec.suit, spec.number) - card.skill = spec.skill or defaultCardSkill - card.special_skills = spec.special_skills - 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 + readCardSpecToCard(card, spec) + readCardSpecToEquip(card, spec) return card end ---@param spec CardSpec ---@return OffensiveRide function fk.CreateOffensiveRide(spec) - assert(type(spec.name) == "string" or type(spec.class_name) == "string") - if not spec.name then spec.name = spec.class_name - elseif not spec.class_name then spec.class_name = spec.name end - if spec.suit then assert(type(spec.suit) == "number") end - if spec.number then assert(type(spec.number) == "number") end - + preprocessCardSpec(spec) local card = OffensiveRide:new(spec.name, spec.suit, spec.number) - card.skill = spec.skill or defaultCardSkill - card.special_skills = spec.special_skills - 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 + readCardSpecToCard(card, spec) + readCardSpecToEquip(card, spec) return card end ---@param spec CardSpec ---@return Treasure function fk.CreateTreasure(spec) - assert(type(spec.name) == "string" or type(spec.class_name) == "string") - if not spec.name then spec.name = spec.class_name - elseif not spec.class_name then spec.class_name = spec.name end - if spec.suit then assert(type(spec.suit) == "number") end - if spec.number then assert(type(spec.number) == "number") end - + preprocessCardSpec(spec) local card = Treasure:new(spec.name, spec.suit, spec.number) - card.skill = spec.skill or defaultCardSkill - card.special_skills = spec.special_skills - 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 + readCardSpecToCard(card, spec) + readCardSpecToEquip(card, spec) return card end diff --git a/lua/freekill.lua b/lua/freekill.lua index 774303c6..f0800a54 100644 --- a/lua/freekill.lua +++ b/lua/freekill.lua @@ -17,8 +17,8 @@ json = require "json" math.randomseed(os.time()) -- 加载实用类,让Lua编写起来更轻松。 -local GroupUtils = require "core.util" -TargetGroup, AimGroup = table.unpack(GroupUtils) +local Utils = require "core.util" +TargetGroup, AimGroup, Util = table.unpack(Utils) dofile "lua/core/debug.lua" -- 加载游戏核心类 diff --git a/lua/server/events/hp.lua b/lua/server/events/hp.lua index 1643fef3..5014e60c 100644 --- a/lua/server/events/hp.lua +++ b/lua/server/events/hp.lua @@ -1,5 +1,36 @@ -- SPDX-License-Identifier: GPL-3.0-or-later +local damage_nature_table = { + [fk.NormalDamage] = "normal_damage", + [fk.FireDamage] = "fire_damage", + [fk.ThunderDamage] = "thunder_damage", + [fk.IceDamage] = "ice_damage", +} + +local function sendDamageLog(room, damageStruct) + if damageStruct.from then + room:sendLog{ + type = "#Damage", + to = {damageStruct.from.id}, + from = damageStruct.to.id, + arg = damageStruct.damage, + arg2 = damage_nature_table[damageStruct.damageType], + } + else + room:sendLog{ + type = "#DamageWithNoFrom", + from = damageStruct.to.id, + arg = damageStruct.damage, + arg2 = damage_nature_table[damageStruct.damageType], + } + end + room:sendLogEvent("Damage", { + to = damageStruct.to.id, + damageType = damage_nature_table[damageStruct.damageType], + damageNum = damageStruct.damage, + }) +end + GameEvent.functions[GameEvent.ChangeHp] = function(self) local player, num, reason, skillName, damageStruct = table.unpack(self.data) local self = self.room @@ -25,32 +56,7 @@ GameEvent.functions[GameEvent.ChangeHp] = function(self) self:broadcastProperty(player, "hp") if reason == "damage" then - local damage_nature_table = { - [fk.NormalDamage] = "normal_damage", - [fk.FireDamage] = "fire_damage", - [fk.ThunderDamage] = "thunder_damage", - } - if damageStruct.from then - self:sendLog{ - type = "#Damage", - to = {damageStruct.from.id}, - from = player.id, - arg = 0 - num, - arg2 = damage_nature_table[damageStruct.damageType], - } - else - self:sendLog{ - type = "#DamageWithNoFrom", - from = player.id, - arg = 0 - num, - arg2 = damage_nature_table[damageStruct.damageType], - } - end - self:sendLogEvent("Damage", { - to = player.id, - damageType = damage_nature_table[damageStruct.damageType], - damageNum = damageStruct.damage, - }) + sendDamageLog(self, damageStruct) elseif reason == "loseHp" then self:sendLog{ type = "#LoseHP", @@ -125,8 +131,21 @@ GameEvent.functions[GameEvent.Damage] = function(self) return false end - if not self:changeHp(damageStruct.to, -damageStruct.damage, "damage", damageStruct.skillName, damageStruct) then - self.logic:breakEvent(false) + -- 先扣减护甲,再扣体力值 + local shield_to_lose = math.min(damageStruct.damage, damageStruct.to.shield) + self:changeShield(damageStruct.to, -shield_to_lose) + + if shield_to_lose < damageStruct.damage then + if not self:changeHp( + damageStruct.to, + shield_to_lose - damageStruct.damage, + "damage", + damageStruct.skillName, + damageStruct) then + self.logic:breakEvent(false) + end + else + sendDamageLog(self, damageStruct) end stages = { diff --git a/lua/server/gamelogic.lua b/lua/server/gamelogic.lua index ef34baba..d52717a4 100644 --- a/lua/server/gamelogic.lua +++ b/lua/server/gamelogic.lua @@ -122,6 +122,7 @@ function GameLogic:prepareForStart() local general = Fk.generals[p.general] p.maxHp = general.maxHp p.hp = general.hp + p.shield = general.shield -- TODO: setup AI here if p.role ~= "lord" then @@ -132,6 +133,7 @@ function GameLogic:prepareForStart() end room:broadcastProperty(p, "maxHp") room:broadcastProperty(p, "hp") + room:broadcastProperty(p, "shield") end local allCardIds = Fk:getAllCardIds() diff --git a/lua/server/room.lua b/lua/server/room.lua index 01cd7b10..e10aeceb 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -1882,6 +1882,39 @@ function Room:responseCard(cardResponseEvent) return execGameEvent(GameEvent.RespondCard, cardResponseEvent) end +---@param card_name string @ 想要视为使用的牌名 +---@param subcards integer[] @ 子卡,可以留空或者直接nil +---@param from ServerPlayer @ 使用来源 +---@param tos ServerPlayer | ServerPlayer[] @ 目标角色(列表) +---@param skillName string @ 技能名 +---@param extra boolean @ 是否计入次数 +function Room:useVirtualCard(card_name, subcards, from, tos, skillName, extra) + local card = Fk:cloneCard(card_name) + card.skillName = skillName + + if from:prohibitUse(card) then return false end + + if tos.class then tos = { tos } end + for i, p in ipairs(tos) do + if from:isProhibited(p, card) then + table.remove(tos, i) + end + end + + if #tos == 0 then return false end + + if subcards then card:addSubcards(Card:getIdList(subcards)) end + + local use = {} ---@type CardUseStruct + use.from = from.id + use.tos = table.map(tos, function(p) return { p.id } end) + use.card = card + use.extraUse = extra + self:useCard(use) + + return true +end + ------------------------------------------------------------------------ -- 移动牌 ------------------------------------------------------------------------ @@ -1992,6 +2025,16 @@ function Room:changeHp(player, num, reason, skillName, damageStruct) return execGameEvent(GameEvent.ChangeHp, player, num, reason, skillName, damageStruct) end +--- 改变玩家的护甲数 +---@param player ServerPlayer +---@param num integer @ 变化量 +function Room:changeShield(player, num) + if num == 0 then return end + player.shield = math.max(player.shield + num, 0) + player.shield = math.min(player.shield, 5) + self:broadcastProperty(player, "shield") +end + --- 令一名玩家失去体力。 ---@param player ServerPlayer @ 玩家 ---@param num integer @ 失去的数量 diff --git a/lua/server/serverplayer.lua b/lua/server/serverplayer.lua index df1b7383..06e985d6 100644 --- a/lua/server/serverplayer.lua +++ b/lua/server/serverplayer.lua @@ -141,6 +141,7 @@ function ServerPlayer:marshal(player) room:notifyProperty(player, self, "maxHp") room:notifyProperty(player, self, "hp") + room:notifyProperty(player, self, "shield") room:notifyProperty(player, self, "gender") room:notifyProperty(player, self, "kingdom") diff --git a/lua/server/system_enum.lua b/lua/server/system_enum.lua index 2144fc84..bb648f9e 100644 --- a/lua/server/system_enum.lua +++ b/lua/server/system_enum.lua @@ -51,6 +51,7 @@ fk.NormalDamage = 1 fk.ThunderDamage = 2 fk.FireDamage = 3 +fk.IceDamage = 4 --- DamageStruct 用来描述和伤害事件有关的数据。 ---@class DamageStruct diff --git a/packages/maneuvering/init.lua b/packages/maneuvering/init.lua index e9f8167d..0b51bf00 100644 --- a/packages/maneuvering/init.lua +++ b/packages/maneuvering/init.lua @@ -27,6 +27,7 @@ local thunderSlashSkill = fk.CreateActiveSkill{ local thunderSlash = fk.CreateBasicCard{ name = "thunder__slash", skill = thunderSlashSkill, + is_damage_card = true, } extension:addCards{ @@ -64,6 +65,7 @@ local fireSlashSkill = fk.CreateActiveSkill{ local fireSlash = fk.CreateBasicCard{ name = "fire__slash", skill = fireSlashSkill, + is_damage_card = true, } extension:addCards{ @@ -259,6 +261,7 @@ local fireAttackSkill = fk.CreateActiveSkill{ local fireAttack = fk.CreateTrickCard{ name = "fire_attack", skill = fireAttackSkill, + is_damage_card = true, } extension:addCards{ fireAttack:clone(Card.Heart, 2), diff --git a/packages/standard_cards/init.lua b/packages/standard_cards/init.lua index 52e41b70..d8c91040 100644 --- a/packages/standard_cards/init.lua +++ b/packages/standard_cards/init.lua @@ -37,6 +37,7 @@ local slash = fk.CreateBasicCard{ name = "slash", number = 7, suit = Card.Spade, + is_damage_card = true, skill = slashSkill, } @@ -310,6 +311,7 @@ local duel = fk.CreateTrickCard{ name = "duel", suit = Card.Spade, number = 1, + is_damage_card = true, skill = duelSkill, } @@ -452,6 +454,7 @@ local savageAssault = fk.CreateTrickCard{ name = "savage_assault", suit = Card.Spade, number = 7, + is_damage_card = true, skill = savageAssaultSkill, } @@ -499,6 +502,7 @@ local archeryAttack = fk.CreateTrickCard{ name = "archery_attack", suit = Card.Heart, number = 1, + is_damage_card = true, skill = archeryAttackSkill, } diff --git a/packages/test/init.lua b/packages/test/init.lua index c533ad2a..7dce5589 100644 --- a/packages/test/init.lua +++ b/packages/test/init.lua @@ -63,11 +63,11 @@ local test_active = fk.CreateActiveSkill{ return true end, card_filter = function(self, card) - if self.interaction.data == "joy" then + -- if self.interaction.data == "joy" then --local c = Fk:getCardById(card) --return Self:getPileNameOfId(card) == self.name and c.color == Card.Red return true - end + -- end end, card_num = 2, target_filter = function() return true end, @@ -86,11 +86,12 @@ local test_active = fk.CreateActiveSkill{ -- room:takeAG(from, id) -- room:delay(2000) -- room:closeAG(from) - local cards = room:askForCardsChosen(from, from, 2, 3, "hej", "") - from:addToPile(self.name, cards) - from.kingdom = "wei" - room:broadcastProperty(from, "kingdom") + -- local cards = room:askForCardsChosen(from, from, 2, 3, "hej", "") + -- from:addToPile(self.name, cards) + -- from.kingdom = "wei" + -- room:broadcastProperty(from, "kingdom") -- p(cards) + room:useVirtualCard("slash", nil, from, room:getOtherPlayers(from), self.name, true) end, } local test_vs = fk.CreateViewAsSkill{ @@ -98,16 +99,18 @@ local test_vs = fk.CreateViewAsSkill{ card_filter = function(self, to_select, selected) return #selected == 0 end, - interaction = UI.ComboBox { - choices = { - "ex_nihilo", - "duel", - "snatch", - "dismantlement", - "savage_assault", - "archery_attack", + interaction = function(self) + return UI.ComboBox { + choices = { + "ex_nihilo", + "duel", + "snatch", + "dismantlement", + "savage_assault", + "archery_attack", + } } - }, + end, view_as = function(self, cards) if #cards ~= 1 then return nil @@ -120,6 +123,7 @@ local test_vs = fk.CreateViewAsSkill{ end, } local test2 = General(extension, "mouxusheng", "wu", 4, 4, General.Female) +test2.shield = 4 test2:addSkill("rende") test2:addSkill(cheat) test2:addSkill(test_active) diff --git a/qml/Pages/Room.qml b/qml/Pages/Room.qml index cb1c6b34..cd45c9a5 100644 --- a/qml/Pages/Room.qml +++ b/qml/Pages/Room.qml @@ -192,6 +192,7 @@ Item { netstate: model.netstate maxHp: model.maxHp hp: model.hp + shield: model.shield seatNumber: model.seatNumber dead: model.dead dying: model.dying @@ -256,6 +257,7 @@ Item { self.kingdom: dashboardModel.kingdom self.netstate: dashboardModel.netstate self.maxHp: dashboardModel.maxHp + self.shield: dashboardModel.shield self.hp: dashboardModel.hp self.seatNumber: dashboardModel.seatNumber self.dead: dashboardModel.dead @@ -785,6 +787,7 @@ Item { netstate: "online", maxHp: 0, hp: 0, + shield: 0, seatNumber: 1, dead: false, dying: false, @@ -808,6 +811,7 @@ Item { netstate: "online", maxHp: 0, hp: 0, + shield: 0, seatNumber: i + 1, dead: false, dying: false, diff --git a/qml/Pages/RoomElement/GeneralCardItem.qml b/qml/Pages/RoomElement/GeneralCardItem.qml index 6c493c37..b0f64d7b 100644 --- a/qml/Pages/RoomElement/GeneralCardItem.qml +++ b/qml/Pages/RoomElement/GeneralCardItem.qml @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later import QtQuick +import "PhotoElement" import "../skin-bank.js" as SkinBank /* Layout of general card: @@ -18,6 +19,7 @@ CardItem { property string kingdom property int hp property int maxHp + property int shieldNum property string pkgName: "" name: "" // description: Sanguosha.getGeneralDescription(name) @@ -40,6 +42,7 @@ CardItem { y: 4 spacing: 1 Repeater { + id: hpRepeater model: (hp > 5 || hp !== maxHp) ? 1 : hp Image { source: SkinBank.getGeneralCardDir(kingdom) + kingdom + "-magatama" @@ -56,6 +59,14 @@ CardItem { } } + Shield { + anchors.right: parent.right + anchors.top: parent.top + anchors.topMargin: hpRepeater.model > 4 ? 16 : 0 + scale: 0.8 + value: shieldNum + } + Text { width: 20 height: 80 @@ -114,6 +125,7 @@ CardItem { kingdom = data.kingdom; hp = data.hp; maxHp = data.maxHp; + shieldNum = data.shield; let splited = name.split("__"); if (splited.length > 1) { diff --git a/qml/Pages/RoomElement/Photo.qml b/qml/Pages/RoomElement/Photo.qml index 782ca637..f44cd12a 100644 --- a/qml/Pages/RoomElement/Photo.qml +++ b/qml/Pages/RoomElement/Photo.qml @@ -20,6 +20,7 @@ Item { property alias handcards: handcardAreaItem.length property int maxHp: 0 property int hp: 0 + property int shield: 0 property int seatNumber: 1 property bool dead: false property bool dying: false @@ -150,6 +151,7 @@ Item { x: 8 value: root.hp maxValue: root.maxHp + shieldNum: root.shield anchors.bottom: parent.bottom anchors.bottomMargin: 36 } diff --git a/qml/Pages/RoomElement/PhotoElement/HpBar.qml b/qml/Pages/RoomElement/PhotoElement/HpBar.qml index 1a9ec896..e011ea02 100644 --- a/qml/Pages/RoomElement/PhotoElement/HpBar.qml +++ b/qml/Pages/RoomElement/PhotoElement/HpBar.qml @@ -2,23 +2,31 @@ import QtQuick import ".." +import "../../skin-bank.js" as SkinBank Column { id: root property int maxValue: 4 property int value: 4 property var colors: ["#F4180E", "#F4180E", "#E3B006", "#25EC27"] + property int shieldNum: 0 + + Shield { + id: shield + value: shieldNum + } Repeater { id: repeater - model: maxValue <= 4 ? maxValue : 0 + model: column.visible ? 0 : maxValue Magatama { state: (maxValue - 1 - index) >= value ? 0 : (value >= 3 || value >= maxValue ? 3 : (value <= 0 ? 0 : value)) } } Column { - visible: maxValue > 4 + id: column + visible: maxValue > 4 || value > maxValue || (shieldNum > 0 && maxValue > 3) spacing: -4 Magatama { @@ -43,11 +51,15 @@ Column { GlowText { id: splitter + height: 12 width: root.width text: "/" z: -10 + rotation: 40 color: hpItem.color - font: hpItem.font + font.family: fontLibian.name + font.pixelSize: 14 + font.bold: true horizontalAlignment: hpItem.horizontalAlignment glow.color: hpItem.glow.color diff --git a/qml/Pages/RoomElement/PhotoElement/Shield.qml b/qml/Pages/RoomElement/PhotoElement/Shield.qml new file mode 100644 index 00000000..75c2c8c3 --- /dev/null +++ b/qml/Pages/RoomElement/PhotoElement/Shield.qml @@ -0,0 +1,22 @@ +import QtQuick +import "../../skin-bank.js" as SkinBank + +Image { + id: root + property int value: 0 + width: 20 + height: 21 + visible: (value > 0) + source: SkinBank.MAGATAMA_DIR + "shield" + + Text { + text: value + anchors.horizontalCenter: parent.horizontalCenter + y: -2 + font.family: fontLibian.name + font.pixelSize: 20 + font.bold: true + color: "white" + style: Text.Outline + } +}