From fd072ca0b4581d3ca1b019f341c944e564ea5374 Mon Sep 17 00:00:00 2001 From: notify Date: Sun, 22 Jan 2023 00:49:11 +0800 Subject: [PATCH] Status skill and some skills (#47) * prototype for 3 status skills * maxcards skill * attack range skill * prohibit skill * kongcheng * use general's skill instead of default * skills --- lua/client/client_util.lua | 10 ++ lua/core/player.lua | 29 ++++- lua/core/skill_type/attack_range.lua | 18 +++ lua/core/skill_type/max_cards.lua | 25 ++++ lua/core/skill_type/prohibit.lua | 19 +++ lua/fk_ex.lua | 69 +++++++++++ lua/server/gamelogic.lua | 5 +- lua/server/room.lua | 20 ++- lua/server/system_enum.lua | 2 +- packages/standard/game_rule.lua | 7 +- packages/standard/init.lua | 175 ++++++++++++++++++++++++++- packages/standard_cards/init.lua | 5 + 12 files changed, 373 insertions(+), 11 deletions(-) diff --git a/lua/client/client_util.lua b/lua/client/client_util.lua index 082239cc..f3b13231 100644 --- a/lua/client/client_util.lua +++ b/lua/client/client_util.lua @@ -116,6 +116,16 @@ function CanUseCardToTarget(card, to_select, selected) end local ret = c.skill:targetFilter(to_select, selected, selected_cards) + 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 return json.encode(ret) end diff --git a/lua/core/player.lua b/lua/core/player.lua index f4162c3b..c5977a81 100644 --- a/lua/core/player.lua +++ b/lua/core/player.lua @@ -218,7 +218,23 @@ end function Player:getMaxCards() local baseValue = math.max(self.hp, 0) - return baseValue + local status_skills = Fk:currentRoom().status_skills[MaxCardsSkill] or {} + local max_fixed = nil + for _, skill in ipairs(status_skills) do + local f = skill:getFixed(self) + if f ~= nil then + max_fixed = max_fixed and math.max(max_fixed, f) or f + end + end + + if max_fixed then baseValue = math.max(max_fixed, 0) end + + for _, skill in ipairs(status_skills) do + local c = skill:getCorrect(self) + baseValue = baseValue + c + end + + return math.max(baseValue, 0) end function Player:getAttackRange() @@ -268,7 +284,16 @@ end ---@param other Player function Player:inMyAttackRange(other) - return self ~= other and self:distanceTo(other) <= self:getAttackRange() + if self == other then + return false + end + local baseAttackRange = self:getAttackRange() + local status_skills = Fk:currentRoom().status_skills[AttackRangeSkill] or {} + for _, skill in ipairs(status_skills) do + local correct = skill:getCorrect(self, other) + baseAttackRange = baseAttackRange + correct + end + return self:distanceTo(other) <= baseAttackRange end function Player:addCardUseHistory(cardName, num) diff --git a/lua/core/skill_type/attack_range.lua b/lua/core/skill_type/attack_range.lua index e69de29b..50624a7f 100644 --- a/lua/core/skill_type/attack_range.lua +++ b/lua/core/skill_type/attack_range.lua @@ -0,0 +1,18 @@ +---@class AttackRangeSkill : Skill +---@field global boolean +local AttackRangeSkill = Skill:subclass("AttackRangeSkill") + +function AttackRangeSkill:initialize(name) + Skill.initialize(self, name, Skill.NotFrequent) + + self.global = false +end + +---@param from Player +---@param to Player +---@return integer +function AttackRangeSkill:getCorrect(from, to) + return 0 +end + +return AttackRangeSkill diff --git a/lua/core/skill_type/max_cards.lua b/lua/core/skill_type/max_cards.lua index e69de29b..738497c0 100644 --- a/lua/core/skill_type/max_cards.lua +++ b/lua/core/skill_type/max_cards.lua @@ -0,0 +1,25 @@ +---@class MaxCardsSkill : Skill +---@field global boolean +local MaxCardsSkill = Skill:subclass("MaxCardsSkill") + +function MaxCardsSkill:initialize(name) + Skill.initialize(self, name, Skill.NotFrequent) + + self.global = false +end + +---@param from Player +---@param to Player +---@return integer +function MaxCardsSkill:getFixed(player) + return nil +end + +---@param from Player +---@param to Player +---@return integer +function MaxCardsSkill:getCorrect(player) + return 0 +end + +return MaxCardsSkill diff --git a/lua/core/skill_type/prohibit.lua b/lua/core/skill_type/prohibit.lua index e69de29b..e4610f9d 100644 --- a/lua/core/skill_type/prohibit.lua +++ b/lua/core/skill_type/prohibit.lua @@ -0,0 +1,19 @@ +---@class ProhibitSkill : Skill +---@field global boolean +local ProhibitSkill = Skill:subclass("ProhibitSkill") + +function ProhibitSkill:initialize(name) + Skill.initialize(self, name, Skill.NotFrequent) + + self.global = false +end + +---@param from Player +---@param to Player +---@param card Card +---@return integer +function ProhibitSkill:isProhibited(from, to, card) + return 0 +end + +return ProhibitSkill diff --git a/lua/fk_ex.lua b/lua/fk_ex.lua index 5f65a765..06f9deed 100644 --- a/lua/fk_ex.lua +++ b/lua/fk_ex.lua @@ -6,8 +6,14 @@ TriggerSkill = require "core.skill_type.trigger" ActiveSkill = require "core.skill_type.active_skill" ViewAsSkill = require "core.skill_type.view_as" DistanceSkill = require "core.skill_type.distance" +ProhibitSkill = require "core.skill_type.prohibit" +AttackRangeSkill = require "core.skill_type.attack_range" +MaxCardsSkill = require "core.skill_type.max_cards" StatusSkills = { DistanceSkill, + ProhibitSkill, + AttackRangeSkill, + MaxCardsSkill, } BasicCard = require "core.card_type.basic" @@ -167,6 +173,69 @@ function fk.CreateDistanceSkill(spec) return skill end +---@class ProhibitSpec: SkillSpec +---@field is_prohibited fun(self: ProhibitSkill, from: Player, to: Player, card: Card) +---@field global boolean + +---@param spec ProhibitSpec +---@return ProhibitSkill +function fk.CreateProhibitSkill(spec) + assert(type(spec.name) == "string") + assert(type(spec.is_prohibited) == "function") + + local skill = ProhibitSkill:new(spec.name) + skill.isProhibited = spec.is_prohibited + if spec.global then + skill.global = spec.global + end + + return skill +end + +---@class AttackRangeSpec: SkillSpec +---@field correct_func fun(self: AttackRangeSkill, from: Player, to: Player) +---@field global boolean + +---@param spec AttackRangeSpec +---@return AttackRangeSkill +function fk.CreateAttackRangeSkill(spec) + assert(type(spec.name) == "string") + assert(type(spec.correct_func) == "function") + + local skill = AttackRangeSkill:new(spec.name) + skill.getCorrect = spec.correct_func + if spec.global then + skill.global = spec.global + end + + return skill +end + +---@class MaxCardsSpec: SkillSpec +---@field correct_func fun(self: MaxCardsSkill, player: Player) +---@field fixed_func fun(self: MaxCardsSkill, from: Player) +---@field global boolean + +---@param spec MaxCardsSpec +---@return MaxCardsSkill +function fk.CreateMaxCardsSkill(spec) + assert(type(spec.name) == "string") + assert(type(spec.correct_func) == "function" or type(spec.fixed_func) == "function") + + local skill = MaxCardsSkill:new(spec.name) + if spec.correct_func then + skill.getCorrect = spec.correct_func + end + if spec.fixed_func then + skill.getFixed = spec.fixed_func + end + if spec.global then + skill.global = spec.global + end + + return skill +end + ---@class CardSpec: Card ---@field skill Skill diff --git a/lua/server/gamelogic.lua b/lua/server/gamelogic.lua index 62af5956..1c616c24 100644 --- a/lua/server/gamelogic.lua +++ b/lua/server/gamelogic.lua @@ -136,7 +136,10 @@ function GameLogic:prepareForStart() end for _, p in ipairs(room.alive_players) do - room:handleAddLoseSkills(p, "zhiheng|mashu|fankui|kurou|jieyin", nil, false) + local skills = Fk.generals[p.general].skills + for _, s in ipairs(skills) do + room:handleAddLoseSkills(p, s.name, nil, false) + end end self:addTriggerSkill(GameRule) diff --git a/lua/server/room.lua b/lua/server/room.lua index 5b49e872..1b730b22 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -117,9 +117,13 @@ function Room:setCardArea(cardId, cardArea, owner) self.owner_map[cardId] = owner end ----@param cardId integer +---@param cardId integer | card ---@return CardArea function Room:getCardArea(cardId) + if type(cardId) ~= "number" then + assert(cardId and cardId:isInstanceOf(Card)) + cardId = cardId:getEffectiveId() + end return self.card_place[cardId] or Card.Unknown end @@ -585,6 +589,7 @@ function Room:askForDiscard(player, minNum, maxNum, includeEquip, skillName) end self:throwCard(toDiscard, skillName, player, player) + return toDiscard end ---@param player ServerPlayer @@ -1438,12 +1443,19 @@ function Room:moveCards(...) end ---@param player integer ----@param cid integer +---@param cid integer|Card ---@param unhide boolean ---@param reason CardMoveReason function Room:obtainCard(player, cid, unhide, reason) + if type(cid) ~= "number" then + assert(cid and cid:isInstanceOf(Card)) + cid = cid:isVirtual() and {cid.id} or cid.subcards + else + cid = {cid} + end + if #cid == 0 then return end self:moveCards({ - ids = {cid}, + ids = cid, from = self.owner_map[cid], to = player, toArea = Card.PlayerHand, @@ -1868,7 +1880,7 @@ end -- judge ----@param data JudgeData +---@param data JudgeStruct ---@return Card function Room:judge(data) local who = data.who diff --git a/lua/server/system_enum.lua b/lua/server/system_enum.lua index 0a95196c..9b755e36 100644 --- a/lua/server/system_enum.lua +++ b/lua/server/system_enum.lua @@ -4,7 +4,7 @@ ---@alias HpChangedData { num: integer, reason: string, skillName: string } ---@alias HpLostData { num: integer, skillName: string } ----@alias DamageStruct { from: integer|null, to: integer, damage: integer, damageType: DamageType, skillName: string } +---@alias DamageStruct { from: integer|null, to: integer, damage: integer, card: Card, damageType: DamageType, skillName: string } ---@alias RecoverStruct { who: integer, num: integer, recoverBy: integer|null, skillName: string|null } ---@alias DyingStruct { who: integer, damage: DamageStruct } diff --git a/packages/standard/game_rule.lua b/packages/standard/game_rule.lua index 5b061f0c..0184043f 100644 --- a/packages/standard/game_rule.lua +++ b/packages/standard/game_rule.lua @@ -136,7 +136,12 @@ GameRule = fk.CreateTriggerSkill{ end end, [Player.Draw] = function() - room:drawCards(player, 2, self.name) + local data = { + n = 2 + } + room.logic:trigger(fk.DrawNCards, player, data) + room:drawCards(player, data.n, self.name) + room.logic:trigger(fk.AfterDrawNCards, player, data) end, [Player.Play] = function() while not player.dead do diff --git a/packages/standard/init.lua b/packages/standard/init.lua index 800f97e9..cd0904a2 100644 --- a/packages/standard/init.lua +++ b/packages/standard/init.lua @@ -17,9 +17,27 @@ Fk:loadTranslationTable{ ["nocolor"] = '无色', } +local jianxiong = fk.CreateTriggerSkill{ + name = "jianxiong", + events = {fk.Damaged}, + can_trigger = function(self, event, target, player, data) + local room = target.room + return data.card ~= nil and + target == player and + target:hasSkill(self.name) and + room:getCardArea(data.card) == Card.Processing and + not target.dead + end, + on_use = function(self, event, target, player, data) + local room = player.room + room:obtainCard(player.id, data.card, false) + end, +} local caocao = General:new(extension, "caocao", "wei", 4) +caocao:addSkill(jianxiong) Fk:loadTranslationTable{ ["caocao"] = "曹操", + ["jianxiong"] = "奸雄", } local fankui = fk.CreateTriggerSkill{ @@ -48,9 +66,42 @@ Fk:loadTranslationTable{ ["fankui"] = "反馈", } +local ganglie = fk.CreateTriggerSkill{ + name = "ganglie", + events = {fk.Damaged}, + can_trigger = function(self, event, target, player, data) + local room = target.room + return data.from ~= nil and + target == player and + target:hasSkill(self.name) and + not target.dead + end, + on_use = function(self, event, target, player, data) + local room = player.room + local from = room:getPlayerById(data.from) + local judge = { + who = from, + reason = self.name, + } + room:judge(judge) + if judge.card.suit ~= Card.Heart then + local discards = room:askForDiscard(from, 2, 2, false, self.name) + if #discards == 0 then + room:damage{ + from = player.id, + to = from.id, + damage = 1, + skillName = self.name, + } + end + end + end, +} local xiahoudun = General:new(extension, "xiahoudun", "wei", 4) +xiahoudun:addSkill(ganglie) Fk:loadTranslationTable{ ["xiahoudun"] = "夏侯惇", + ["ganglie"] = "刚烈", } local zhangliao = General:new(extension, "zhangliao", "wei", 4) @@ -106,14 +157,49 @@ Fk:loadTranslationTable{ ["zhangfei"] = "张飞", } +local kongcheng = fk.CreateProhibitSkill{ + name = "kongcheng", + is_prohibited = function(self, from, to, card) + if to:hasSkill(self.name) and to:isKongcheng() then + return card.name == "slash" or card.name == "duel" + end + end, +} local zhugeliang = General:new(extension, "zhugeliang", "shu", 3) +zhugeliang:addSkill(kongcheng) Fk:loadTranslationTable{ ["zhugeliang"] = "诸葛亮", + ["kongcheng"] = "空城", } +local longdan = fk.CreateViewAsSkill{ + name = "longdan", + pattern = "slash,jink", + card_filter = function(self, to_select, selected) + if #selected == 1 then return false end + local c = Fk:getCardById(to_select) + return c.name == "slash" or c.name == "jink" + end, + view_as = function(self, cards) + if #cards ~= 1 then + return nil + end + local _c = Fk:getCardById(cards[1]) + local c + if _c.name == "slash" then + c = Fk:cloneCard("jink") + elseif _c.name == "jink" then + c = Fk:cloneCard("slash") + end + c:addSubcard(cards[1]) + return c + end, +} local zhaoyun = General:new(extension, "zhaoyun", "shu", 4) +zhaoyun:addSkill(longdan) Fk:loadTranslationTable{ ["zhaoyun"] = "赵云", + ["longdan"] = "龙胆", } local mashu = fk.CreateDistanceSkill{ @@ -131,9 +217,23 @@ Fk:loadTranslationTable{ ["mashu"] = "马术", } +local jizhi = fk.CreateTriggerSkill{ + name = "jizhi", + events = {fk.CardUsing}, + can_trigger = function(self, event, target, player, data) + return target == player and player:hasSkill(self.name) and + data.card.type == Card.TypeTrick and + data.card.sub_type ~= Card.SubtypeDelayedTrick + end, + on_use = function(self, event, target, player, data) + player:drawCards(1, self.name) + end, +} local huangyueying = General:new(extension, "huangyueying", "shu", 3, 3, General.Female) +huangyueying:addSkill(jizhi) Fk:loadTranslationTable{ ["huangyueying"] = "黄月英", + ["jizhi"] = "集智", } local zhiheng = fk.CreateActiveSkill{ @@ -154,14 +254,67 @@ Fk:loadTranslationTable{ ["zhiheng"] = "制衡", } +local qixi = fk.CreateViewAsSkill{ + name = "qixi", + pattern = "dismantlement", + card_filter = function(self, to_select, selected) + if #selected == 1 then return false end + return Fk:getCardById(to_select).color == Card.Black + end, + view_as = function(self, cards) + if #cards ~= 1 then + return nil + end + local c = Fk:cloneCard("dismantlement") + c:addSubcard(cards[1]) + return c + end, +} local ganning = General:new(extension, "ganning", "wu", 4) +ganning:addSkill(qixi) Fk:loadTranslationTable{ ["ganning"] = "甘宁", + ["qixi"] = "奇袭", } +local keji = fk.CreateTriggerSkill{ + name = "keji", + events = {fk.EventPhaseChanging}, + can_trigger = function(self, event, target, player, data) + return target == player and player:hasSkill(self.name) and + data.to == Player.Discard and + player:usedTimes("slash") < 1 and + player:getMark("_keji_played_slash") == 0 + end, + on_use = function(self, event, target, player, data) + return true + end, + + refresh_events = {fk.CardResponding, fk.EventPhaseStart}, + can_refresh = function(self, event, target, player, data) + if not (target == player and player:hasSkill(self.name)) then + return false + end + if event == fk.CardResponding then + return data.card.name == "slash" + elseif event == fk.EventPhaseStart then + return player.phase == player.NotActive + end + end, + on_refresh = function(self, event, target, player, data) + local room = player.room + if event == fk.CardResponding then + room:addPlayerMark(player, "_keji_played_slash", 1) + elseif event == fk.EventPhaseStart then + room:setPlayerMark(player, "_keji_played_slash", 0) + end + end +} local lvmeng = General:new(extension, "lvmeng", "wu", 4) +lvmeng:addSkill(keji) Fk:loadTranslationTable{ ["lvmeng"] = "吕蒙", + ["keji"] = "克己", } local kurou = fk.CreateActiveSkill{ @@ -184,9 +337,18 @@ Fk:loadTranslationTable{ ["kurou"] = "苦肉", } +local yingzi = fk.CreateTriggerSkill{ + name = "yingzi", + events = {fk.DrawNCards}, + on_use = function(self, event, target, player, data) + data.n = data.n + 1 + end, +} local zhouyu = General:new(extension, "zhouyu", "wu", 3) +zhouyu:addSkill(yingzi) Fk:loadTranslationTable{ ["zhouyu"] = "周瑜", + ["yingzi"] = "英姿", } local daqiao = General:new(extension, "daqiao", "wu", 3, 3, General.Female) @@ -194,15 +356,25 @@ Fk:loadTranslationTable{ ["daqiao"] = "大乔", } +local qianxun = fk.CreateProhibitSkill{ + name = "qianxun", + is_prohibited = function(self, from, to, card) + if to:hasSkill(self.name) then + return card.name == "indulgence" or card.name == "snatch" + end + end, +} local luxun = General:new(extension, "luxun", "wu", 3) +luxun:addSkill(qianxun) Fk:loadTranslationTable{ ["luxun"] = "陆逊", + ["qianxun"] = "谦逊", } local jieyin = fk.CreateActiveSkill{ name = "jieyin", card_filter = function(self, to_select, selected) - return #selected < 2 -- TODO:choose equip + return #selected < 2 end, target_filter = function(self, to_select, selected) local target = Fk:currentRoom():getPlayerById(to_select) @@ -210,7 +382,6 @@ local jieyin = fk.CreateActiveSkill{ return target:isWounded() and Fk.generals[name].gender == General.Male and #selected < 1 - -- and not target:hasSkill(self.name) end, feasible = function(self, selected, selected_cards) return #selected == 1 and #selected_cards == 2 diff --git a/packages/standard_cards/init.lua b/packages/standard_cards/init.lua index ee45383a..fa6492d0 100644 --- a/packages/standard_cards/init.lua +++ b/packages/standard_cards/init.lua @@ -28,6 +28,7 @@ local slashSkill = fk.CreateActiveSkill{ room:damage({ from = from, to = to, + card = effect.card, damage = 1 + (effect.addtionalDamage or 0), damageType = fk.NormalDamage, skillName = self.name @@ -296,6 +297,7 @@ local duelSkill = fk.CreateActiveSkill{ room:damage({ from = responsers[currentTurn % 2 + 1].id, to = currentResponser.id, + card = effect.card, damage = 1 + (effect.addtionalDamage or 0), damageType = fk.NormalDamage, skillName = self.name, @@ -449,6 +451,7 @@ local savageAssaultSkill = fk.CreateActiveSkill{ room:damage({ from = effect.from, to = effect.to, + card = effect.card, damage = 1 + (effect.addtionalDamage or 0), damageType = fk.NormalDamage, skillName = self.name, @@ -498,6 +501,7 @@ local archeryAttackSkill = fk.CreateActiveSkill{ room:damage({ from = effect.from, to = effect.to, + card = effect.card, damage = 1 + (effect.addtionalDamage or 0), damageType = fk.NormalDamage, skillName = self.name, @@ -609,6 +613,7 @@ local lightningSkill = fk.CreateActiveSkill{ room:damage{ to = to.id, damage = 3, + card = effect.card, damageType = fk.ThunderDamage, skillName = self.name, }