diff --git a/lua/core/card.lua b/lua/core/card.lua index bbb40cdd..dfdf5f92 100644 --- a/lua/core/card.lua +++ b/lua/core/card.lua @@ -191,7 +191,7 @@ function Card:getTypeString() elseif t == Card.TypeEquip then return "equip" end - return "nocolor" + return "notype" end local function getNumberStr(num) diff --git a/lua/core/player.lua b/lua/core/player.lua index 522e6080..63862431 100644 --- a/lua/core/player.lua +++ b/lua/core/player.lua @@ -479,9 +479,17 @@ local function getActualSkill(skill) end ---@param skill string | Skill -function Player:hasSkill(skill) +function Player:hasSkill(skill, ignoreNullified, ignoreAlive) + if not ignoreAlive and self.dead then + return false + end + skill = getActualSkill(skill) + if not (ignoreNullified or skill:isEffectable(self)) then + return false + end + if table.contains(self.player_skills, skill) then return true end @@ -507,7 +515,7 @@ function Player:addSkill(skill, source_skill) local room = Fk:currentRoom() local ret = {} for _, s in ipairs(toget) do - if not self:hasSkill(s) then + if not self:hasSkill(s, true, true) then table.insert(ret, s) if s:isInstanceOf(TriggerSkill) and RoomInstance then room.logic:addTriggerSkill(s) @@ -563,7 +571,7 @@ function Player:loseSkill(skill, source_skill) local ret = {} ---@type Skill[] for _, s in ipairs(tolose) do - if not self:hasSkill(s) then + if not self:hasSkill(s, true, true) then table.insert(ret, s) end end diff --git a/lua/core/skill.lua b/lua/core/skill.lua index d6f49dc5..494429f0 100644 --- a/lua/core/skill.lua +++ b/lua/core/skill.lua @@ -51,4 +51,17 @@ function Skill:isEquipmentSkill() return self.attached_equip and type(self.attached_equip) == 'string' and self.attached_equip ~= "" end +---@param player Player +---@return boolean +function Skill:isEffectable(player) + local nullifySkills = Fk:currentRoom().status_skills[InvaliditySkill] or {} + for _, nullifySkill in ipairs(nullifySkills) do + if self.name ~= nullifySkill.name and nullifySkill:getInvalidity(player, self) then + return false + end + end + + return true +end + return Skill diff --git a/lua/core/skill_type/invalidity.lua b/lua/core/skill_type/invalidity.lua index e69de29b..0751ce2e 100644 --- a/lua/core/skill_type/invalidity.lua +++ b/lua/core/skill_type/invalidity.lua @@ -0,0 +1,11 @@ +---@class InvaliditySkill : StatusSkill +local InvaliditySkill = StatusSkill:subclass("InvaliditySkill") + +---@param from Player +---@param skill Skill +---@return boolean +function InvaliditySkill:getInvalidity(from, skill) + return false +end + +return InvaliditySkill diff --git a/lua/core/skill_type/trigger.lua b/lua/core/skill_type/trigger.lua index e475df6a..19e78d6d 100644 --- a/lua/core/skill_type/trigger.lua +++ b/lua/core/skill_type/trigger.lua @@ -38,7 +38,7 @@ function TriggerSkill:refresh(event, target, player, data) end ---@return boolean function TriggerSkill:triggerable(event, target, player, data) return target and (target == player) - and (self.global or (target:isAlive() and target:hasSkill(self))) + and (self.global or target:hasSkill(self)) end -- Determine how to cost this skill. diff --git a/lua/fk_ex.lua b/lua/fk_ex.lua index f9af2cbc..b52fc2ab 100644 --- a/lua/fk_ex.lua +++ b/lua/fk_ex.lua @@ -11,6 +11,7 @@ AttackRangeSkill = require "core.skill_type.attack_range" MaxCardsSkill = require "core.skill_type.max_cards" TargetModSkill = require "core.skill_type.target_mod" FilterSkill = require "core.skill_type.filter" +InvaliditySkill = require "lua.core.skill_type.invalidity" BasicCard = require "core.card_type.basic" local Trick = require "core.card_type.trick" @@ -320,6 +321,21 @@ function fk.CreateFilterSkill(spec) return skill end +---@class InvaliditySpec: StatusSkillSpec +---@field invalidity_func fun(self: InvaliditySkill, from: Player, skill: Skill) + +---@param spec InvaliditySpec +---@return InvaliditySkill +function fk.CreateInvaliditySkill(spec) + assert(type(spec.name) == "string") + + local skill = InvaliditySkill:new(spec.name) + readStatusSpecToSkill(skill, spec) + skill.getInvalidity = spec.invalidity_func + + return skill +end + ---@class CardSpec: Card ---@field skill Skill ---@field equip_skill Skill diff --git a/lua/server/events/hp.lua b/lua/server/events/hp.lua index 9002bb9b..b566b969 100644 --- a/lua/server/events/hp.lua +++ b/lua/server/events/hp.lua @@ -11,6 +11,7 @@ GameEvent.functions[GameEvent.ChangeHp] = function(self) num = num, reason = reason, skillName = skillName, + damageEvent = damageStruct, } if self.logic:trigger(fk.BeforeHpChanged, player, data) then diff --git a/lua/server/room.lua b/lua/server/room.lua index 57110675..78493b0e 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -1317,7 +1317,8 @@ local onAim = function(room, cardUseEvent, aimEventCollaborators) nullifiedTargets = cardUseEvent.nullifiedTargets or {}, tos = aimGroup, firstTarget = firstTarget, - additionalDamage = cardUseEvent.additionalDamage + additionalDamage = cardUseEvent.additionalDamage, + extra_data = cardUseEvent.extra_data, } local index = 1 @@ -1344,6 +1345,7 @@ local onAim = function(room, cardUseEvent, aimEventCollaborators) aimStruct.targetGroup = cardUseEvent.tos aimStruct.nullifiedTargets = cardUseEvent.nullifiedTargets or {} aimStruct.firstTarget = firstTarget + aimStruct.extra_data = cardUseEvent.extra_data end firstTarget = false @@ -1361,6 +1363,7 @@ local onAim = function(room, cardUseEvent, aimEventCollaborators) cardUseEvent.from = aimStruct.from cardUseEvent.tos = aimEventTargetGroup cardUseEvent.nullifiedTargets = aimStruct.nullifiedTargets + cardUseEvent.extra_data = aimStruct.extra_data if #AimGroup:getAllTargets(aimStruct.tos) == 0 then return false @@ -1824,7 +1827,7 @@ function Room:handleAddLoseSkills(player, skill_names, source_skill, sendlog, no for _, skill in ipairs(skill_names) do if string.sub(skill, 1, 1) == "-" then local actual_skill = string.sub(skill, 2, #skill) - if player:hasSkill(actual_skill) then + if player:hasSkill(actual_skill, true, true) then local lost_skills = player:loseSkill(actual_skill, source_skill) for _, s in ipairs(lost_skills) do self:doBroadcastNotify("LoseSkill", json.encode{ @@ -1846,7 +1849,7 @@ function Room:handleAddLoseSkills(player, skill_names, source_skill, sendlog, no end else local sk = Fk.skills[skill] - if sk and not player:hasSkill(sk) then + if sk and not player:hasSkill(sk, true, true) then local got_skills = player:addSkill(sk) for _, s in ipairs(got_skills) do diff --git a/lua/server/system_enum.lua b/lua/server/system_enum.lua index 34b7c778..86531b0c 100644 --- a/lua/server/system_enum.lua +++ b/lua/server/system_enum.lua @@ -35,6 +35,7 @@ ---@field num integer ---@field reason string ---@field skillName string +---@field damageEvent DamageStruct|null ---@class HpLostData ---@field num integer @@ -54,6 +55,7 @@ fk.FireDamage = 3 ---@field chain boolean ---@field damageType DamageType ---@field skillName string +---@field beginnerOfTheDamage boolean|null ---@class RecoverStruct ---@field who ServerPlayer diff --git a/packages/maneuvering/init.lua b/packages/maneuvering/init.lua index 99712dde..fd0f282d 100644 --- a/packages/maneuvering/init.lua +++ b/packages/maneuvering/init.lua @@ -156,32 +156,34 @@ extension:addCards({ local ironChainEffect = fk.CreateTriggerSkill{ name = "iron_chain_effect", global = true, - priority = 0, -- game rule - refresh_events = {fk.DamageFinished}, + priority = { [fk.BeforeHpChanged] = 10, [fk.DamageFinished] = 0 }, -- game rule + refresh_events = { fk.BeforeHpChanged, fk.DamageFinished }, can_refresh = function(self, event, target, player, data) - return target == player and data.damageType ~= fk.NormalDamage + if event == fk.BeforeHpChanged then + return target == player and data.damageEvent and data.damageEvent.damageType ~= fk.NormalDamage and player.chained + else + return target == player and data.beginnerOfTheDamage and not data.chain + end end, on_refresh = function(self, event, target, player, data) local room = player.room - if data.to.chained then - data.to:setChainState(false) + if event == fk.BeforeHpChanged then + data.damageEvent.beginnerOfTheDamage = true + player:setChainState(false) else - return - end - if data.chain then return end - - local targets = table.filter(room:getAlivePlayers(), function(p) - return p.chained - end) - for _, p in ipairs(targets) do - room:sendLog{ - type = "#ChainDamage", - from = p.id - } - local dmg = table.simpleClone(data) - dmg.to = p - dmg.chain = true - room:damage(dmg) + local targets = table.filter(room:getAlivePlayers(), function(p) + return p.chained + end) + for _, p in ipairs(targets) do + room:sendLog{ + type = "#ChainDamage", + from = p.id + } + local dmg = table.simpleClone(data) + dmg.to = p + dmg.chain = true + room:damage(dmg) + end end end, } @@ -320,6 +322,30 @@ local gudingBlade = fk.CreateWeapon{ extension:addCard(gudingBlade) +local fanSkill = fk.CreateTriggerSkill{ + name = "#fan_skill", + attached_equip = "fan", + events = { fk.AfterCardUseDeclared }, + can_trigger = function(self, event, target, player, data) + return target == player and player:hasSkill(self.name) and data.card.name == "slash" + end, + on_use = function(_, _, _, _, data) + local fireSlash = Fk:cloneCard("fire__slash") + fireSlash:addSubcard(data.card) + data.card = fireSlash + end, +} +Fk:addSkill(fanSkill) +local fan = fk.CreateWeapon{ + name = "fan", + suit = Card.Diamond, + number = 1, + attack_range = 4, + equip_skill = fanSkill, +} + +extension:addCard(fan) + local vineSkill = fk.CreateTriggerSkill{ name = "#vine_skill", attached_equip = "vine", @@ -380,7 +406,7 @@ local silverLion = fk.CreateArmor{ equip_skill = silverLionSkill, on_uninstall = function(self, room, player) Armor.onUninstall(self, room, player) - if player:isWounded() then + if player:isWounded() and self.equip_skill:isEffectable(player) then room:broadcastPlaySound("./packages/maneuvering/audio/card/silver_lion") room:setEmotion(player, "./packages/maneuvering/image/anim/silver_lion") room:recover{ @@ -436,6 +462,8 @@ Fk:loadTranslationTable{ ["fire_attack"] = "火攻", ["supply_shortage"] = "兵粮寸断", ["guding_blade"] = "古锭刀", + ["fan"] = "朱雀羽扇", + ["#fan_skill"] = "朱雀羽扇", ["vine"] = "藤甲", ["silver_lion"] = "白银狮子", ["hualiu"] = "骅骝", diff --git a/packages/standard_cards/init.lua b/packages/standard_cards/init.lua index 7792ace3..5eca7dda 100644 --- a/packages/standard_cards/init.lua +++ b/packages/standard_cards/init.lua @@ -773,11 +773,62 @@ extension:addCards({ crossbow:clone(Card.Diamond, 1), }) +fk.MarkArmorNullified = "mark__armor_nullified" + +local armorInvalidity = fk.CreateInvaliditySkill { + name = "armor_invalidity_skill", + global = true, + invalidity_func = function(self, from, skill) + return + from:getMark(fk.MarkArmorNullified) > 0 and + from:getEquipment(Card.SubtypeArmor) ~= nil and + skill.attached_equip == Fk:getCardById(from:getEquipment(Card.SubtypeArmor)).name + end +} +Fk:addSkill(armorInvalidity) + +local qingGangSkill = fk.CreateTriggerSkill{ + name = "#qinggang_sword_skill", + attached_equip = "qinggang_sword", + frequency = Skill.Compulsory, + events = { fk.TargetSpecified }, + can_trigger = function(self, event, target, player, data) + return target == player and player:hasSkill(self.name) and + data.card and data.card.trueName == "slash" + end, + on_use = function(self, event, target, player, data) + local room = player.room + room:addPlayerMark(room:getPlayerById(data.to), fk.MarkArmorNullified) + + data.extra_data = data.extra_data or {} + data.extra_data.qinggangNullified = data.extra_data.qinggangNullified or {} + data.extra_data.qinggangNullified[tostring(data.to)] = (data.extra_data.qinggangNullified[tostring(data.to)] or 0) + 1 + end, + + refresh_events = { fk.CardUseFinished }, + can_refresh = function(self, event, target, player, data) + return data.extra_data and data.extra_data.qinggangNullified + end, + on_refresh = function(self, event, target, player, data) + local room = player.room + for key, num in pairs(data.extra_data.qinggangNullified) do + local p = room:getPlayerById(tonumber(key)) + if p:getMark(fk.MarkArmorNullified) > 0 then + room:removePlayerMark(p, fk.MarkArmorNullified, num) + end + end + + data.qinggangNullified = nil + end, +} +Fk:addSkill(qingGangSkill) + local qingGang = fk.CreateWeapon{ name = "qinggang_sword", suit = Card.Spade, number = 6, attack_range = 2, + equip_skill = qingGangSkill, } extension:addCards({ @@ -1035,7 +1086,7 @@ local eightDiagramSkill = fk.CreateTriggerSkill{ events = {fk.AskForCardUse, fk.AskForCardResponse}, can_trigger = function(self, event, target, player, data) return target == player and player:hasSkill(self.name) and - (data.cardName == "jink" or (data.pattern and Exppattern:Parse(data.pattern):matchExp("jink"))) + (data.cardName == "jink" or (data.pattern and Exppattern:Parse(data.pattern):matchExp("jink|0|nosuit|none"))) end, on_use = function(self, event, target, player, data) local room = player.room