Enhancement (#116)
给Card堆一个伤害牌属性 能添加虚拟牌为子卡 封装了“视为使用xx牌”的函数 护甲机制 interaction现在可以作为一个函数,以实现动态化 冰属性伤害 使用牌堆中的牌不再报错
This commit is contained in:
parent
a735013411
commit
9ae119028c
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
|
@ -82,8 +82,10 @@ function Client:moveCards(moves)
|
|||
table.remove(from.player_cards[Player.Hand])
|
||||
end
|
||||
else
|
||||
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])
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -171,6 +171,7 @@ Fk:loadTranslationTable{
|
|||
["normal_damage"] = "无属性",
|
||||
["fire_damage"] = "火属性",
|
||||
["thunder_damage"] = "雷属性",
|
||||
["ice_damage"] = "冰属性",
|
||||
|
||||
["phase_judge"] = "判定阶段",
|
||||
["phase_draw"] = "摸牌阶段",
|
||||
|
|
|
@ -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,10 +184,18 @@ 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")
|
||||
-- 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)
|
||||
end
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
122
lua/fk_ex.lua
122
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
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
-- 加载游戏核心类
|
||||
|
|
|
@ -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,9 +131,22 @@ GameEvent.functions[GameEvent.Damage] = function(self)
|
|||
return false
|
||||
end
|
||||
|
||||
if not self:changeHp(damageStruct.to, -damageStruct.damage, "damage", damageStruct.skillName, damageStruct) then
|
||||
-- 先扣减护甲,再扣体力值
|
||||
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 = {
|
||||
{fk.Damage, damageStruct.from},
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 @ 失去的数量
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
fk.NormalDamage = 1
|
||||
fk.ThunderDamage = 2
|
||||
fk.FireDamage = 3
|
||||
fk.IceDamage = 4
|
||||
|
||||
--- DamageStruct 用来描述和伤害事件有关的数据。
|
||||
---@class DamageStruct
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
@ -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,7 +99,8 @@ local test_vs = fk.CreateViewAsSkill{
|
|||
card_filter = function(self, to_select, selected)
|
||||
return #selected == 0
|
||||
end,
|
||||
interaction = UI.ComboBox {
|
||||
interaction = function(self)
|
||||
return UI.ComboBox {
|
||||
choices = {
|
||||
"ex_nihilo",
|
||||
"duel",
|
||||
|
@ -107,7 +109,8 @@ local test_vs = fk.CreateViewAsSkill{
|
|||
"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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue