Switch skill (#148)
- 实现转换技; - 将特殊的标记名称注册在mark_enum.lua文件; - 标记值在UI的显示支持解析数组; - 将觉醒技的觉醒条件分离至canWake函数; - 修复一系列bug; - 在Room类新增从牌堆、弃牌堆中随机获取牌的方法。
After Width: | Height: | Size: 70 B |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 9.4 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.4 KiB |
|
@ -525,7 +525,10 @@ end
|
||||||
---@param times integer
|
---@param times integer
|
||||||
local function updateLimitSkill(pid, skill, times)
|
local function updateLimitSkill(pid, skill, times)
|
||||||
if not skill.visible then return end
|
if not skill.visible then return end
|
||||||
if skill.frequency == Skill.Limited or skill.frequency == Skill.Wake then
|
if skill:isSwitchSkill() then
|
||||||
|
times = ClientInstance:getPlayerById(pid):getSwitchSkillState(skill.switchSkillName) == fk.SwitchYang and 0 or 1
|
||||||
|
ClientInstance:notifyUI("UpdateLimitSkill", json.encode{ pid, skill.switchSkillName, times })
|
||||||
|
elseif skill.frequency == Skill.Limited or skill.frequency == Skill.Wake then
|
||||||
ClientInstance:notifyUI("UpdateLimitSkill", json.encode{ pid, skill.name, times })
|
ClientInstance:notifyUI("UpdateLimitSkill", json.encode{ pid, skill.name, times })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -274,6 +274,7 @@ function GetSkillData(skill_name)
|
||||||
extension = skill.package.extensionName,
|
extension = skill.package.extensionName,
|
||||||
freq = freq,
|
freq = freq,
|
||||||
frequency = frequency,
|
frequency = frequency,
|
||||||
|
switchSkillName = skill.switchSkillName,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -193,6 +193,10 @@ FreeKill使用的是libgit2的C API,与此同时使用Git完成拓展包的下
|
||||||
["$Winner"] = "%1 获胜",
|
["$Winner"] = "%1 获胜",
|
||||||
["$NoWinner"] = "平局!",
|
["$NoWinner"] = "平局!",
|
||||||
["Back To Lobby"] = "返回大厅",
|
["Back To Lobby"] = "返回大厅",
|
||||||
|
|
||||||
|
["basic_char"] = "基",
|
||||||
|
["trick_char"] = "锦",
|
||||||
|
["equip_char"] = "装",
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Game concepts
|
-- Game concepts
|
||||||
|
|
|
@ -284,6 +284,12 @@ local function getNumberStr(num)
|
||||||
return tostring(num)
|
return tostring(num)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- 判断卡牌是否为普通锦囊牌
|
||||||
|
---@return boolean
|
||||||
|
function Card:isCommonTrick()
|
||||||
|
return self.type == Card.TypeTrick and self.sub_type ~= Card.SubtypeDelayedTrick
|
||||||
|
end
|
||||||
|
|
||||||
--- 为卡牌赋予Mark。
|
--- 为卡牌赋予Mark。
|
||||||
---@param mark string @ 标记
|
---@param mark string @ 标记
|
||||||
---@param count integer @ 为标记赋予的数量
|
---@param count integer @ 为标记赋予的数量
|
||||||
|
@ -333,6 +339,51 @@ function Card:getMarkNames()
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param anotherCard Card
|
||||||
|
---@param diff boolean
|
||||||
|
---@return boolean
|
||||||
|
function Card:compareSuitWith(anotherCard, diff)
|
||||||
|
if table.contains({ self.suit, anotherCard.suit }, Card.NoSuit) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if diff then
|
||||||
|
return self.suit ~= anotherCard.suit
|
||||||
|
else
|
||||||
|
return self.suit == anotherCard.suit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param anotherCard Card
|
||||||
|
---@param diff boolean
|
||||||
|
---@return boolean
|
||||||
|
function Card:compareColorWith(anotherCard, diff)
|
||||||
|
if table.contains({ self.color, anotherCard.color }, Card.NoColor) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if diff then
|
||||||
|
return self.color ~= anotherCard.color
|
||||||
|
else
|
||||||
|
return self.color == anotherCard.color
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param anotherCard Card
|
||||||
|
---@param diff boolean
|
||||||
|
---@return boolean
|
||||||
|
function Card:compareNumberWith(anotherCard, diff)
|
||||||
|
if self.number < 1 or anotherCard.number < 1 then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if diff then
|
||||||
|
return self.number ~= anotherCard.number
|
||||||
|
else
|
||||||
|
return self.number == anotherCard.number
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- for sendLog
|
-- for sendLog
|
||||||
--- 获取卡牌的文字信息并准备作为log发送。
|
--- 获取卡牌的文字信息并准备作为log发送。
|
||||||
function Card:toLogString()
|
function Card:toLogString()
|
||||||
|
|
|
@ -119,8 +119,8 @@ function Player:setGeneral(general, setHp, addSkills)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Player:getGeneralMaxHp()
|
function Player:getGeneralMaxHp()
|
||||||
local general = Fk.generals[self:getMark("__heg_general") or self.general]
|
local general = Fk.generals[type(self:getMark("__heg_general")) == "string" and self:getMark("__heg_general") or self.general]
|
||||||
local deputy = Fk.generals[self:getMark("__heg_deputy") or self.deputy]
|
local deputy = Fk.generals[type(self:getMark("__heg_deputy")) == "string" and self:getMark("__heg_deputy") or self.deputy]
|
||||||
|
|
||||||
if not deputy then
|
if not deputy then
|
||||||
return general.maxHp
|
return general.maxHp
|
||||||
|
@ -727,4 +727,19 @@ function Player:prohibitDiscard(card)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@field SwitchYang number @ 转换技状态阳
|
||||||
|
fk.SwitchYang = 0
|
||||||
|
---@field SwitchYin number @ 转换技状态阴
|
||||||
|
fk.SwitchYin = 1
|
||||||
|
|
||||||
|
---@param skillName string
|
||||||
|
---@return number
|
||||||
|
function Player:getSwitchSkillState(skillName, afterUse)
|
||||||
|
if afterUse then
|
||||||
|
return self:getMark(MarkEnum.SwithSkillPreName .. skillName) < 1 and fk.SwitchYin or fk.SwitchYang
|
||||||
|
else
|
||||||
|
return self:getMark(MarkEnum.SwithSkillPreName .. skillName) < 1 and fk.SwitchYang or fk.SwitchYin
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return Player
|
return Player
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
---@field public anim_type string @ 技能类型定义
|
---@field public anim_type string @ 技能类型定义
|
||||||
---@field public related_skills Skill[] @ 和本技能相关的其他技能,有时候一个技能实际上是通过好几个技能拼接而实现的。
|
---@field public related_skills Skill[] @ 和本技能相关的其他技能,有时候一个技能实际上是通过好几个技能拼接而实现的。
|
||||||
---@field public attached_equip string @ 属于什么装备的技能?
|
---@field public attached_equip string @ 属于什么装备的技能?
|
||||||
|
---@field public switchSkillName string
|
||||||
local Skill = class("Skill")
|
local Skill = class("Skill")
|
||||||
|
|
||||||
---@alias Frequency integer
|
---@alias Frequency integer
|
||||||
|
@ -90,4 +91,8 @@ function Skill:addAttachedKingdom(kingdom)
|
||||||
table.insertIfNeed(self.attachedKingdom, kingdom)
|
table.insertIfNeed(self.attachedKingdom, kingdom)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Skill:isSwitchSkill()
|
||||||
|
return self.switchSkillName and type(self.switchSkillName) == 'string' and self.switchSkillName ~= ""
|
||||||
|
end
|
||||||
|
|
||||||
return Skill
|
return Skill
|
||||||
|
|
|
@ -57,8 +57,14 @@ end
|
||||||
-- DO NOT modify this function
|
-- DO NOT modify this function
|
||||||
function TriggerSkill:doCost(event, target, player, data)
|
function TriggerSkill:doCost(event, target, player, data)
|
||||||
local ret = self:cost(event, target, player, data)
|
local ret = self:cost(event, target, player, data)
|
||||||
|
local room = player.room
|
||||||
|
|
||||||
|
local cost_data_bak = self.cost_data
|
||||||
|
room.logic:trigger(fk.BeforeTriggerSkillUse, player, { skill = self, willUse = ret })
|
||||||
|
self.cost_data = cost_data_bak
|
||||||
|
|
||||||
if ret then
|
if ret then
|
||||||
return player.room:useSkill(player, self, function()
|
return room:useSkill(player, self, function()
|
||||||
return self:use(event, target, player, data)
|
return self:use(event, target, player, data)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
@ -90,4 +96,22 @@ end
|
||||||
---@return boolean
|
---@return boolean
|
||||||
function TriggerSkill:use(event, target, player, data) end
|
function TriggerSkill:use(event, target, player, data) end
|
||||||
|
|
||||||
|
function TriggerSkill:canWake(event, target, player, data)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param event Event @ TriggerEvent
|
||||||
|
---@param target ServerPlayer @ Player who triggered this event
|
||||||
|
---@param player ServerPlayer @ Player who is operating
|
||||||
|
---@param data any @ useful data of the event
|
||||||
|
---@return boolean
|
||||||
|
function TriggerSkill:enableToWake(event, target, player, data)
|
||||||
|
return
|
||||||
|
type(player:getMark(MarkEnum.StraightToWake)) == "table" and
|
||||||
|
table.find(player:getMark(MarkEnum.StraightToWake), function(skillName)
|
||||||
|
return self.name == skillName
|
||||||
|
end) or
|
||||||
|
self:canWake(event, target, player, data)
|
||||||
|
end
|
||||||
|
|
||||||
return TriggerSkill
|
return TriggerSkill
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
-- 首先加载所有详细的技能类型、卡牌类型等等,以及时机列表
|
-- 首先加载所有详细的技能类型、卡牌类型等等,以及时机列表
|
||||||
dofile "lua/server/event.lua"
|
dofile "lua/server/event.lua"
|
||||||
dofile "lua/server/system_enum.lua"
|
dofile "lua/server/system_enum.lua"
|
||||||
|
dofile "lua/server/mark_enum.lua"
|
||||||
TriggerSkill = require "core.skill_type.trigger"
|
TriggerSkill = require "core.skill_type.trigger"
|
||||||
ActiveSkill = require "core.skill_type.active"
|
ActiveSkill = require "core.skill_type.active"
|
||||||
ViewAsSkill = require "core.skill_type.view_as"
|
ViewAsSkill = require "core.skill_type.view_as"
|
||||||
|
@ -32,6 +33,11 @@ local function readCommonSpecToSkill(skill, spec)
|
||||||
assert(type(spec.attached_equip) == "string")
|
assert(type(spec.attached_equip) == "string")
|
||||||
skill.attached_equip = spec.attached_equip
|
skill.attached_equip = spec.attached_equip
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if spec.switch_skill_name then
|
||||||
|
assert(type(spec.switch_skill_name) == "string")
|
||||||
|
skill.switchSkillName = spec.switch_skill_name
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function readUsableSpecToSkill(skill, spec)
|
local function readUsableSpecToSkill(skill, spec)
|
||||||
|
@ -110,7 +116,18 @@ function fk.CreateTriggerSkill(spec)
|
||||||
if spec.on_trigger then skill.trigger = spec.on_trigger end
|
if spec.on_trigger then skill.trigger = spec.on_trigger end
|
||||||
|
|
||||||
if spec.can_trigger then
|
if spec.can_trigger then
|
||||||
skill.triggerable = spec.can_trigger
|
if spec.frequency == Skill.Wake then
|
||||||
|
skill.triggerable = function(self, event, target, player, data)
|
||||||
|
return spec.can_trigger(self, event, target, player, data) and
|
||||||
|
skill:enableToWake(event, target, player, data)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
skill.triggerable = spec.can_trigger
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if skill.frequency == Skill.Wake and spec.can_wake then
|
||||||
|
skill.canWake = spec.can_wake
|
||||||
end
|
end
|
||||||
|
|
||||||
if spec.on_cost then skill.cost = spec.on_cost end
|
if spec.on_cost then skill.cost = spec.on_cost end
|
||||||
|
@ -197,7 +214,7 @@ end
|
||||||
---@field public pattern string
|
---@field public pattern string
|
||||||
---@field public enabled_at_play fun(self: ViewAsSkill, player: Player): boolean
|
---@field public enabled_at_play fun(self: ViewAsSkill, player: Player): boolean
|
||||||
---@field public enabled_at_response fun(self: ViewAsSkill, player: Player): boolean
|
---@field public enabled_at_response fun(self: ViewAsSkill, player: Player): boolean
|
||||||
---@field public before_use fun(self: ViewAsSkill, player: Player)
|
---@field public before_use fun(self: ViewAsSkill, player: ServerPlayer)
|
||||||
|
|
||||||
---@param spec ViewAsSkillSpec
|
---@param spec ViewAsSkillSpec
|
||||||
---@return ViewAsSkill
|
---@return ViewAsSkill
|
||||||
|
|
|
@ -9,6 +9,10 @@
|
||||||
---FreeKill's lua API
|
---FreeKill's lua API
|
||||||
fk = {}
|
fk = {}
|
||||||
|
|
||||||
|
---@class MarkEnum
|
||||||
|
---Special marks
|
||||||
|
MarkEnum = {}
|
||||||
|
|
||||||
---@class fk.SPlayerList
|
---@class fk.SPlayerList
|
||||||
SPlayerList = {}
|
SPlayerList = {}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ local function useActiveSkill(self, skill, card)
|
||||||
filter_func = function() return false end
|
filter_func = function() return false end
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.command == "PlayCard" and not skill:canUse(player) then
|
if self.command == "PlayCard" and not skill:canUse(player, card) then
|
||||||
return ""
|
return ""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -95,5 +95,10 @@ fk.PindianResultConfirmed = 71
|
||||||
fk.PindianFinished = 72
|
fk.PindianFinished = 72
|
||||||
|
|
||||||
-- 73 = TurnEnd
|
-- 73 = TurnEnd
|
||||||
|
fk.AfterDrawPileShuffle = 74
|
||||||
|
|
||||||
fk.NumOfEvents = 74
|
fk.BeforeTriggerSkillUse = 75
|
||||||
|
|
||||||
|
fk.BeforeDrawCard = 76
|
||||||
|
|
||||||
|
fk.NumOfEvents = 77
|
||||||
|
|
|
@ -40,15 +40,12 @@ GameEvent.functions[GameEvent.Judge] = function(self)
|
||||||
if self.logic:trigger(fk.FinishJudge, who, data) then
|
if self.logic:trigger(fk.FinishJudge, who, data) then
|
||||||
self.logic:breakEvent()
|
self.logic:breakEvent()
|
||||||
end
|
end
|
||||||
if self:getCardArea(data.card.id) == Card.Processing then
|
|
||||||
self:moveCardTo(data.card, Card.DiscardPile, nil, fk.ReasonPutIntoDiscardPile)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
GameEvent.cleaners[GameEvent.Judge] = function(self)
|
GameEvent.cleaners[GameEvent.Judge] = function(self)
|
||||||
local data = table.unpack(self.data)
|
local data = table.unpack(self.data)
|
||||||
local self = self.room
|
local self = self.room
|
||||||
if self:getCardArea(data.card.id) == Card.Processing then
|
if (self.interrupted or not data.skipDrop) and self:getCardArea(data.card.id) == Card.Processing then
|
||||||
self:moveCardTo(data.card, Card.DiscardPile, nil, fk.ReasonPutIntoDiscardPile)
|
self:moveCardTo(data.card, Card.DiscardPile, nil, fk.ReasonPutIntoDiscardPile)
|
||||||
end
|
end
|
||||||
if not self.interrupted then return end
|
if not self.interrupted then return end
|
||||||
|
|
|
@ -100,7 +100,7 @@ GameEvent.functions[GameEvent.MoveCards] = function(self)
|
||||||
toAreaIds = self.void
|
toAreaIds = self.void
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(toAreaIds, toAreaIds == Card.DrawPile and 1 or #toAreaIds + 1, info.cardId)
|
table.insert(toAreaIds, data.toArea == Card.DrawPile and 1 or #toAreaIds + 1, info.cardId)
|
||||||
end
|
end
|
||||||
self:setCardArea(info.cardId, data.toArea, data.to)
|
self:setCardArea(info.cardId, data.toArea, data.to)
|
||||||
if data.toArea == Card.DrawPile or realFromArea == Card.DrawPile then
|
if data.toArea == Card.DrawPile or realFromArea == Card.DrawPile then
|
||||||
|
|
|
@ -414,8 +414,15 @@ function GameLogic:trigger(event, target, data, refresh_only)
|
||||||
|
|
||||||
local len = #skills
|
local len = #skills
|
||||||
broken = skill:trigger(event, target, player, data)
|
broken = skill:trigger(event, target, player, data)
|
||||||
table.insertTable(skill_names, table.map(
|
|
||||||
table.slice(skills, len - #skills), function(s) return s.name end))
|
table.insertTable(
|
||||||
|
skill_names,
|
||||||
|
table.map(table.filter(table.slice(skills, len - #skills), function(s)
|
||||||
|
return
|
||||||
|
s.priority_table[event] == prio and
|
||||||
|
s:triggerable(event, target, player, data)
|
||||||
|
end), function(s) return s.name end)
|
||||||
|
)
|
||||||
|
|
||||||
broken = broken or (event == fk.AskForPeaches
|
broken = broken or (event == fk.AskForPeaches
|
||||||
and room:getPlayerById(data.who).hp > 0)
|
and room:getPlayerById(data.who).hp > 0)
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
-- SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
MarkEnum = {}
|
||||||
|
|
||||||
|
---@field StraightToWake string @ 跳过觉醒标记(值为技能名通过+连接)
|
||||||
|
MarkEnum.StraightToWake = "_straight_to_wake"
|
||||||
|
|
||||||
|
MarkEnum.SwithSkillPreName = "__switcher_"
|
|
@ -195,8 +195,25 @@ end
|
||||||
|
|
||||||
--- 将房间中的玩家按照座位顺序重新排序。
|
--- 将房间中的玩家按照座位顺序重新排序。
|
||||||
---@param playerIds integer[] @ 玩家id列表,这个数组会被这个函数排序
|
---@param playerIds integer[] @ 玩家id列表,这个数组会被这个函数排序
|
||||||
function Room:sortPlayersByAction(playerIds)
|
function Room:sortPlayersByAction(playerIds, isTargetGroup)
|
||||||
|
table.sort(playerIds, function(prev, next)
|
||||||
|
local prevSeat = self:getPlayerById(isTargetGroup and prev[1] or prev).seat
|
||||||
|
local nextSeat = self:getPlayerById(isTargetGroup and next[1] or next).seat
|
||||||
|
|
||||||
|
return prevSeat < nextSeat
|
||||||
|
end)
|
||||||
|
|
||||||
|
if
|
||||||
|
self.current and
|
||||||
|
table.find(isTargetGroup and TargetGroup:getRealTargets(playerIds) or playerIds, function(id)
|
||||||
|
return self:getPlayerById(id).seat >= self.current.seat
|
||||||
|
end)
|
||||||
|
then
|
||||||
|
while self:getPlayerById(isTargetGroup and playerIds[1][1] or playerIds[1]).seat < self.current.seat do
|
||||||
|
local toPlayerId = table.remove(playerIds, 1)
|
||||||
|
table.insert(playerIds, toPlayerId)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Room:deadPlayerFilter(playerIds)
|
function Room:deadPlayerFilter(playerIds)
|
||||||
|
@ -487,10 +504,10 @@ function Room:changeHero(player, new_general, full, isDeputy, sendLog)
|
||||||
end
|
end
|
||||||
|
|
||||||
player.maxHp = player:getGeneralMaxHp()
|
player.maxHp = player:getGeneralMaxHp()
|
||||||
self:broadcastProperty(player, "hp")
|
self:broadcastProperty(player, "maxHp")
|
||||||
if full then
|
if full then
|
||||||
player.hp = player.maxHp
|
player.hp = player.maxHp
|
||||||
self:broadcastProperty(player, "maxHp")
|
self:broadcastProperty(player, "hp")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1220,7 +1237,10 @@ end
|
||||||
---@param cards integer[] @ 可以被观星的卡牌id列表
|
---@param cards integer[] @ 可以被观星的卡牌id列表
|
||||||
---@param top_limit integer[] @ 置于牌堆顶的牌的限制(下限,上限),不填写则不限
|
---@param top_limit integer[] @ 置于牌堆顶的牌的限制(下限,上限),不填写则不限
|
||||||
---@param bottom_limit integer[] @ 置于牌堆顶的牌的限制(下限,上限),不填写则不限
|
---@param bottom_limit integer[] @ 置于牌堆顶的牌的限制(下限,上限),不填写则不限
|
||||||
function Room:askForGuanxing(player, cards, top_limit, bottom_limit)
|
---@param customNotify string|null @ 自定义读条操作提示
|
||||||
|
---@param noPut boolean|null @ 是否进行放置牌操作
|
||||||
|
---@return table<top|bottom, cardId[]>
|
||||||
|
function Room:askForGuanxing(player, cards, top_limit, bottom_limit, customNotify, noPut)
|
||||||
-- 这一大堆都是来提前报错的
|
-- 这一大堆都是来提前报错的
|
||||||
top_limit = top_limit or {}
|
top_limit = top_limit or {}
|
||||||
bottom_limit = bottom_limit or {}
|
bottom_limit = bottom_limit or {}
|
||||||
|
@ -1236,7 +1256,7 @@ function Room:askForGuanxing(player, cards, top_limit, bottom_limit)
|
||||||
assert(#cards >= top_limit[1] + bottom_limit[1] and #cards <= top_limit[2] + bottom_limit[2], "限定区间设置错误:可用空间不能容纳所有牌。")
|
assert(#cards >= top_limit[1] + bottom_limit[1] and #cards <= top_limit[2] + bottom_limit[2], "限定区间设置错误:可用空间不能容纳所有牌。")
|
||||||
end
|
end
|
||||||
local command = "AskForGuanxing"
|
local command = "AskForGuanxing"
|
||||||
self:notifyMoveFocus(player, command)
|
self:notifyMoveFocus(player, customNotify or command)
|
||||||
local data = {
|
local data = {
|
||||||
cards = cards,
|
cards = cards,
|
||||||
min_top_cards = top_limit and top_limit[1] or 0,
|
min_top_cards = top_limit and top_limit[1] or 0,
|
||||||
|
@ -1261,19 +1281,23 @@ function Room:askForGuanxing(player, cards, top_limit, bottom_limit)
|
||||||
bottom = {}
|
bottom = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
for i = #top, 1, -1 do
|
if not noPut then
|
||||||
table.insert(self.draw_pile, 1, top[i])
|
for i = #top, 1, -1 do
|
||||||
end
|
table.insert(self.draw_pile, 1, top[i])
|
||||||
for _, id in ipairs(bottom) do
|
end
|
||||||
table.insert(self.draw_pile, id)
|
for _, id in ipairs(bottom) do
|
||||||
|
table.insert(self.draw_pile, id)
|
||||||
|
end
|
||||||
|
|
||||||
|
self:sendLog{
|
||||||
|
type = "#GuanxingResult",
|
||||||
|
from = player.id,
|
||||||
|
arg = #top,
|
||||||
|
arg2 = #bottom,
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
self:sendLog{
|
return { top = top, bottom = bottom }
|
||||||
type = "#GuanxingResult",
|
|
||||||
from = player.id,
|
|
||||||
arg = #top,
|
|
||||||
arg2 = #bottom,
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- 平时写DIY用不到的函数。
|
--- 平时写DIY用不到的函数。
|
||||||
|
@ -1567,7 +1591,7 @@ local onAim = function(room, cardUseEvent, aimEventCollaborators)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
room:sortPlayersByAction(cardUseEvent.tos)
|
room:sortPlayersByAction(cardUseEvent.tos, true)
|
||||||
local aimGroup = AimGroup:initAimGroup(TargetGroup:getRealTargets(cardUseEvent.tos))
|
local aimGroup = AimGroup:initAimGroup(TargetGroup:getRealTargets(cardUseEvent.tos))
|
||||||
|
|
||||||
local collaboratorsIndex = {}
|
local collaboratorsIndex = {}
|
||||||
|
@ -1628,7 +1652,7 @@ local onAim = function(room, cardUseEvent, aimEventCollaborators)
|
||||||
|
|
||||||
local aimEventTargetGroup = aimStruct.targetGroup
|
local aimEventTargetGroup = aimStruct.targetGroup
|
||||||
if aimEventTargetGroup then
|
if aimEventTargetGroup then
|
||||||
room:sortPlayersByAction(aimEventTargetGroup)
|
room:sortPlayersByAction(aimEventTargetGroup, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
cardUseEvent.from = aimStruct.from
|
cardUseEvent.from = aimStruct.from
|
||||||
|
@ -2030,6 +2054,17 @@ end
|
||||||
---@param fromPlace string @ 摸牌的位置,"top" 或者 "bottom"
|
---@param fromPlace string @ 摸牌的位置,"top" 或者 "bottom"
|
||||||
---@return integer[] @ 摸到的牌
|
---@return integer[] @ 摸到的牌
|
||||||
function Room:drawCards(player, num, skillName, fromPlace)
|
function Room:drawCards(player, num, skillName, fromPlace)
|
||||||
|
local drawData = {
|
||||||
|
who = player,
|
||||||
|
num = num,
|
||||||
|
skillName = skillName,
|
||||||
|
fromPlace = fromPlace,
|
||||||
|
}
|
||||||
|
self.logic:trigger(fk.BeforeDrawCard, player, drawData)
|
||||||
|
|
||||||
|
num = drawData.num
|
||||||
|
fromPlace = drawData.fromPlace
|
||||||
|
|
||||||
local topCards = self:getNCards(num, fromPlace)
|
local topCards = self:getNCards(num, fromPlace)
|
||||||
self:moveCards({
|
self:moveCards({
|
||||||
ids = topCards,
|
ids = topCards,
|
||||||
|
@ -2408,6 +2443,8 @@ function Room:shuffleDrawPile()
|
||||||
end
|
end
|
||||||
self.discard_pile = {}
|
self.discard_pile = {}
|
||||||
table.shuffle(self.draw_pile)
|
table.shuffle(self.draw_pile)
|
||||||
|
|
||||||
|
self.logic:trigger(fk.AfterDrawPileShuffle, nil, {})
|
||||||
end
|
end
|
||||||
|
|
||||||
--- 使用技能。先增加技能发动次数,再执行相应的函数。
|
--- 使用技能。先增加技能发动次数,再执行相应的函数。
|
||||||
|
@ -2428,6 +2465,15 @@ function Room:useSkill(player, skill, effect_cb)
|
||||||
self:notifySkillInvoked(player, skill.name)
|
self:notifySkillInvoked(player, skill.name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if skill:isSwitchSkill() then
|
||||||
|
local switchSkillName = skill.switchSkillName
|
||||||
|
self:setPlayerMark(
|
||||||
|
player,
|
||||||
|
MarkEnum.SwithSkillPreName .. switchSkillName,
|
||||||
|
player:getSwitchSkillState(switchSkillName, true)
|
||||||
|
)
|
||||||
|
end
|
||||||
player:addSkillUseHistory(skill.name)
|
player:addSkillUseHistory(skill.name)
|
||||||
|
|
||||||
if effect_cb then
|
if effect_cb then
|
||||||
|
@ -2470,6 +2516,55 @@ function Room:getSubcardsByRule(card, fromAreas)
|
||||||
return cardIds
|
return cardIds
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param pattern string
|
||||||
|
---@param num number|null
|
||||||
|
---@param fromPile number|null
|
||||||
|
---@return cardId[]
|
||||||
|
function Room:getCardFromPileByRule(pattern, num, fromPile)
|
||||||
|
num = num or 1
|
||||||
|
local pileToSearch = fromPile == Card.DiscardPile and self.discard_pile or self.draw_pile
|
||||||
|
|
||||||
|
local cardPack = {}
|
||||||
|
if num < 3 then
|
||||||
|
for i = 1, num do
|
||||||
|
local randomIndex = math.random(1, #pileToSearch)
|
||||||
|
local curIndex = randomIndex
|
||||||
|
repeat
|
||||||
|
local curCardId = pileToSearch[curIndex]
|
||||||
|
if Fk:getCardById(curCardId):matchPattern(pattern) and not table.contains(cardPack, curCardId) then
|
||||||
|
table.insert(cardPack, pileToSearch[curIndex])
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
curIndex = curIndex + 1
|
||||||
|
if curIndex > #pileToSearch then
|
||||||
|
curIndex = 1
|
||||||
|
end
|
||||||
|
until curIndex == randomIndex
|
||||||
|
|
||||||
|
if #cardPack < num then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local matchedIds = {}
|
||||||
|
for _, id in ipairs(pileToSearch) do
|
||||||
|
if Fk:getCardById(id):matchPattern(pattern) then
|
||||||
|
table.insert(matchedIds, id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local loopTimes = math.min(num, #matchedIds)
|
||||||
|
for i = 1, loopTimes do
|
||||||
|
local randomCardId = matchedIds[math.random(1, #matchedIds)]
|
||||||
|
table.insert(cardPack, randomCardId)
|
||||||
|
table.removeOne(matchedIds, randomCardId)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return cardPack
|
||||||
|
end
|
||||||
|
|
||||||
function CreateRoom(_room)
|
function CreateRoom(_room)
|
||||||
RoomInstance = Room:new(_room)
|
RoomInstance = Room:new(_room)
|
||||||
end
|
end
|
||||||
|
|
|
@ -141,6 +141,7 @@ fk.IceDamage = 4
|
||||||
---@field public card Card
|
---@field public card Card
|
||||||
---@field public reason string
|
---@field public reason string
|
||||||
---@field public pattern string
|
---@field public pattern string
|
||||||
|
---@field public skipDrop boolean|null
|
||||||
|
|
||||||
---@class CardResponseEvent
|
---@class CardResponseEvent
|
||||||
---@field public from integer
|
---@field public from integer
|
||||||
|
@ -189,3 +190,13 @@ fk.ReasonResonpse = 10
|
||||||
---@field public arg any
|
---@field public arg any
|
||||||
---@field public arg2 any
|
---@field public arg2 any
|
||||||
---@field public arg3 any
|
---@field public arg3 any
|
||||||
|
|
||||||
|
---@class SkillUseStruct
|
||||||
|
---@field public skill Skill
|
||||||
|
---@field public willUse boolean
|
||||||
|
|
||||||
|
---@class DrawCardStruct
|
||||||
|
---@field public who ServerPlayer
|
||||||
|
---@field public num number
|
||||||
|
---@field public skillName string
|
||||||
|
---@field public fromPlace "top"|"bottom"
|
||||||
|
|
|
@ -79,8 +79,8 @@ extension:addCards{
|
||||||
local analepticSkill = fk.CreateActiveSkill{
|
local analepticSkill = fk.CreateActiveSkill{
|
||||||
name = "analeptic_skill",
|
name = "analeptic_skill",
|
||||||
max_turn_use_time = 1,
|
max_turn_use_time = 1,
|
||||||
can_use = function(self, player)
|
can_use = function(self, player, card)
|
||||||
return player:usedCardTimes("analeptic", Player.HistoryTurn) < self:getMaxUseTime(Self, Player.HistoryTurn)
|
return player:usedCardTimes("analeptic", Player.HistoryTurn) < self:getMaxUseTime(Self, Player.HistoryTurn, card)
|
||||||
end,
|
end,
|
||||||
on_use = function(self, room, use)
|
on_use = function(self, room, use)
|
||||||
if not use.tos or #TargetGroup:getRealTargets(use.tos) == 0 then
|
if not use.tos or #TargetGroup:getRealTargets(use.tos) == 0 then
|
||||||
|
@ -120,21 +120,22 @@ local analepticEffect = fk.CreateTriggerSkill{
|
||||||
if event == fk.PreCardUse then
|
if event == fk.PreCardUse then
|
||||||
return data.card.trueName == "slash" and player.drank > 0
|
return data.card.trueName == "slash" and player.drank > 0
|
||||||
else
|
else
|
||||||
return player.phase == Player.NotActive
|
return target.phase == Player.NotActive
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
on_trigger = function(self, event, target, player, data)
|
on_trigger = function(self, event, target, player, data)
|
||||||
|
local room = player.room
|
||||||
if event == fk.PreCardUse then
|
if event == fk.PreCardUse then
|
||||||
data.additionalDamage = (data.additionalDamage or 0) + player.drank
|
data.additionalDamage = (data.additionalDamage or 0) + player.drank
|
||||||
data.extra_data = data.extra_data or {}
|
data.extra_data = data.extra_data or {}
|
||||||
data.extra_data.drankBuff = player.drank
|
data.extra_data.drankBuff = player.drank
|
||||||
player.drank = 0
|
player.drank = 0
|
||||||
player.room:broadcastProperty(player, "drank")
|
room:broadcastProperty(player, "drank")
|
||||||
else
|
else
|
||||||
for _, p in ipairs(player.room:getAlivePlayers(true)) do
|
for _, p in ipairs(room:getAlivePlayers(true)) do
|
||||||
if p.drank > 0 then
|
if p.drank > 0 then
|
||||||
p.drank = 0
|
p.drank = 0
|
||||||
p.room:broadcastProperty(player, "drank")
|
room:broadcastProperty(p, "drank")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -270,12 +271,12 @@ extension:addCards{
|
||||||
local supplyShortageSkill = fk.CreateActiveSkill{
|
local supplyShortageSkill = fk.CreateActiveSkill{
|
||||||
name = "supply_shortage_skill",
|
name = "supply_shortage_skill",
|
||||||
distance_limit = 1,
|
distance_limit = 1,
|
||||||
target_filter = function(self, to_select, selected)
|
target_filter = function(self, to_select, selected, _, card)
|
||||||
if #selected == 0 then
|
if #selected == 0 then
|
||||||
local player = Fk:currentRoom():getPlayerById(to_select)
|
local player = Fk:currentRoom():getPlayerById(to_select)
|
||||||
if Self ~= player then
|
if Self ~= player then
|
||||||
return not player:hasDelayedTrick("supply_shortage") and
|
return not player:hasDelayedTrick("supply_shortage") and
|
||||||
Self:distanceTo(player) <= self:getDistanceLimit(Self)
|
Self:distanceTo(player) <= self:getDistanceLimit(Self, card)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -41,8 +41,8 @@ Item {
|
||||||
onSkillnameChanged: {
|
onSkillnameChanged: {
|
||||||
let data = Backend.callLuaFunction("GetSkillData", [skillname]);
|
let data = Backend.callLuaFunction("GetSkillData", [skillname]);
|
||||||
data = JSON.parse(data);
|
data = JSON.parse(data);
|
||||||
if (data.frequency) {
|
if (data.frequency || data.switchSkillName) {
|
||||||
skilltype = data.frequency;
|
skilltype = data.switchSkillName ? 'switch' : data.frequency;
|
||||||
visible = true;
|
visible = true;
|
||||||
} else {
|
} else {
|
||||||
visible = false;
|
visible = false;
|
||||||
|
@ -59,6 +59,9 @@ Item {
|
||||||
x.visible = true;
|
x.visible = true;
|
||||||
bg.source = SkinBank.LIMIT_SKILL_DIR + "limit-used";
|
bg.source = SkinBank.LIMIT_SKILL_DIR + "limit-used";
|
||||||
}
|
}
|
||||||
|
} else if (skilltype === 'switch') {
|
||||||
|
visible = true;
|
||||||
|
bg.source = SkinBank.LIMIT_SKILL_DIR + (usedtimes < 1 ? 'switch' : 'switch-yin');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ Item {
|
||||||
width: childrenRect.width
|
width: childrenRect.width
|
||||||
height: 22
|
height: 22
|
||||||
Text {
|
Text {
|
||||||
text: Backend.translate(mark_name) + ' ' + Backend.translate(mark_extra)
|
text: Backend.translate(mark_name) + ' ' + mark_extra
|
||||||
font.family: fontLibian.name
|
font.family: fontLibian.name
|
||||||
font.pixelSize: 22
|
font.pixelSize: 22
|
||||||
font.letterSpacing: -0.6
|
font.letterSpacing: -0.6
|
||||||
|
@ -82,6 +82,8 @@ Item {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data = (data instanceof Array ? data.map((markText) => Backend.translate(markText)).join(' ') : Backend.translate(data));
|
||||||
if (modelItem)
|
if (modelItem)
|
||||||
modelItem.mark_extra = data;
|
modelItem.mark_extra = data;
|
||||||
else
|
else
|
||||||
|
|
|
@ -878,7 +878,7 @@ callbacks["SetPlayerMark"] = function(jsonData) {
|
||||||
let data = JSON.parse(jsonData);
|
let data = JSON.parse(jsonData);
|
||||||
let player = getPhoto(data[0]);
|
let player = getPhoto(data[0]);
|
||||||
let mark = data[1];
|
let mark = data[1];
|
||||||
let value = data[2].toString();
|
let value = data[2] instanceof Array ? data[2] : data[2].toString();
|
||||||
if (value == 0) {
|
if (value == 0) {
|
||||||
player.markArea.removeMark(mark);
|
player.markArea.removeMark(mark);
|
||||||
} else {
|
} else {
|
||||||
|
|