- 15秒后其他人可以将房主踢出
- event中的从room.lua复制过来的self都规范成room
- 删了feasible的deprecate警告
- 虚空印卡

关于虚空印卡的说明:
* 印的卡id为负数,但依然属于实体卡。
* 这也就是说今后判断虚拟牌的依据是id == 0而不是 <= 0。
* 不过其实虚拟牌的id自古以来就固定是0啦,所以不用担心。
* 虚空印的卡自然只和当前运行的房间有关。
* 虚空印卡的id从-2开始,每印一张其id便减少1。
* 之所以不从-1开始是因为UI把-1认定为未知牌。Bot的玩家id也从-2开始,这是一个道理。
* 除此之外,印出的卡就如同一张普通的实体卡一样,洗入牌堆啥的都没问题,用来作其他虚拟卡的子卡也没啥问题。
* 坐等后面测试出bug吧,当然我希望直接不出bug。
This commit is contained in:
notify 2023-08-11 03:19:59 +08:00 committed by GitHub
parent 0745863863
commit a82b8c1b0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 271 additions and 178 deletions

View File

@ -9,6 +9,7 @@ Flickable {
flickableDirection: Flickable.AutoFlickIfNeeded flickableDirection: Flickable.AutoFlickIfNeeded
clip: true clip: true
contentHeight: layout.height contentHeight: layout.height
property bool loading: false
ScrollBar.vertical: ScrollBar { ScrollBar.vertical: ScrollBar {
parent: root.parent parent: root.parent
anchors.top: root.top anchors.top: root.top
@ -65,7 +66,9 @@ Flickable {
enabled: orig_name !== "test_p_0" enabled: orig_name !== "test_p_0"
onCheckedChanged: { onCheckedChanged: {
checkPackage(orig_name, checked); if (!loading) {
checkPackage(orig_name, checked);
}
} }
} }
} }
@ -130,6 +133,7 @@ Flickable {
} }
Component.onCompleted: { Component.onCompleted: {
loading = true;
const g = JSON.parse(Backend.callLuaFunction("GetAllGeneralPack", [])); const g = JSON.parse(Backend.callLuaFunction("GetAllGeneralPack", []));
for (let orig of g) { for (let orig of g) {
if (config.serverHiddenPacks.includes(orig)) { if (config.serverHiddenPacks.includes(orig)) {
@ -153,5 +157,6 @@ Flickable {
pkg_enabled: !config.disabledPack.includes(orig), pkg_enabled: !config.disabledPack.includes(orig),
}); });
} }
loading = false;
} }
} }

View File

@ -21,6 +21,7 @@ Item {
property bool isFull: false property bool isFull: false
property bool isAllReady: false property bool isAllReady: false
property bool isReady: false property bool isReady: false
property bool canKickOwner: false
property alias popupBox: popupBox property alias popupBox: popupBox
property alias manualBox: manualBox property alias manualBox: manualBox
@ -71,6 +72,8 @@ Item {
onIsStartedChanged: { onIsStartedChanged: {
if (isStarted) { if (isStarted) {
bgm.play(); bgm.play();
canKickOwner = false;
kickOwnerTimer.stop();
} else { } else {
// bgm.stop(); // bgm.stop();
} }
@ -209,6 +212,40 @@ Item {
ClientInstance.notifyServer("Ready", ""); ClientInstance.notifyServer("Ready", "");
} }
} }
Button {
id: kickOwner
anchors.horizontalCenter: parent.horizontalCenter
y: parent.height / 2 + 30
text: "踢出房主"
visible: canKickOwner && !isStarted && isFull && !isOwner
onClicked: {
for (let i = 0; i < photoModel.count; i++) {
let item = photoModel.get(i);
if (item.isOwner) {
ClientInstance.notifyServer("KickPlayer", item.id.toString());
}
}
}
}
Timer {
id: kickOwnerTimer
interval: 15000
onTriggered: {
canKickOwner = true;
}
}
onIsAllReadyChanged: {
if (!isAllReady) {
canKickOwner = false;
kickOwnerTimer.stop();
} else {
kickOwnerTimer.start();
}
}
Rectangle { Rectangle {
x: parent.width / 2 + 60 x: parent.width / 2 + 60
y: parent.height / 2 - 30 y: parent.height / 2 - 30

View File

@ -60,6 +60,7 @@ function Client:initialize()
self.skill_costs = {} self.skill_costs = {}
self.card_marks = {} self.card_marks = {}
self.filtered_cards = {} self.filtered_cards = {}
self.printed_cards = {}
self.disabled_packs = {} self.disabled_packs = {}
self.disabled_generals = {} self.disabled_generals = {}
@ -932,6 +933,13 @@ fk.client_callback["EnterLobby"] = function(jsonData)
c:notifyUI("EnterLobby", jsonData) c:notifyUI("EnterLobby", jsonData)
end end
fk.client_callback["PrintCard"] = function(j)
local data = json.decode(j)
local n, s, num = table.unpack(data)
local cd = Fk:cloneCard(n, s, num)
Fk:_addPrintedCard(cd)
end
-- Create ClientInstance (used by Lua) -- Create ClientInstance (used by Lua)
ClientInstance = Client:new() ClientInstance = Client:new()
dofile "lua/client/client_util.lua" dofile "lua/client/client_util.lua"

View File

@ -153,7 +153,7 @@ end
--- 检测是否为虚拟卡牌如果其ID为0及以下则为虚拟卡牌。 --- 检测是否为虚拟卡牌如果其ID为0及以下则为虚拟卡牌。
function Card:isVirtual() function Card:isVirtual()
return self.id <= 0 return self.id == 0
end end
--- 获取卡牌的ID。 --- 获取卡牌的ID。

View File

@ -23,6 +23,7 @@
---@field public currentResponsePattern string @ 要求用牌的种类(如要求用特定花色的桃···) ---@field public currentResponsePattern string @ 要求用牌的种类(如要求用特定花色的桃···)
---@field public currentResponseReason string @ 要求用牌的原因(如濒死,被特定牌指定,使用特定技能···) ---@field public currentResponseReason string @ 要求用牌的原因(如濒死,被特定牌指定,使用特定技能···)
---@field public filtered_cards table<integer, Card> @ 被锁视技影响的卡牌 ---@field public filtered_cards table<integer, Card> @ 被锁视技影响的卡牌
---@field public printed_cards table<integer, Card> @ 被某些房间现场打印的卡牌id都是负数且从-2开始
local Engine = class("Engine") local Engine = class("Engine")
--- Engine的构造函数。 --- Engine的构造函数。
@ -62,6 +63,7 @@ local _foreign_keys = {
"currentResponsePattern", "currentResponsePattern",
"currentResponseReason", "currentResponseReason",
"filtered_cards", "filtered_cards",
"printed_cards",
} }
function Engine:__index(k) function Engine:__index(k)
@ -398,9 +400,11 @@ end
---@param ignoreFilter boolean|nil @ 是否要无视掉锁定视为技,直接获得真牌 ---@param ignoreFilter boolean|nil @ 是否要无视掉锁定视为技,直接获得真牌
---@return Card @ 这个id对应的卡牌 ---@return Card @ 这个id对应的卡牌
function Engine:getCardById(id, ignoreFilter) function Engine:getCardById(id, ignoreFilter)
local ret = self.cards[id] if id == nil then return nil end
local card_tab = (id >= -1) and self.cards or self.printed_cards
local ret = card_tab[id]
if not ignoreFilter then if not ignoreFilter then
ret = self.filtered_cards[id] or self.cards[id] ret = self.filtered_cards[id] or card_tab[id]
end end
return ret return ret
end end
@ -466,6 +470,18 @@ function Engine:filterCard(id, player, data)
end end
end end
--- 添加一张现场打印的牌到游戏中。
---
--- 这张牌必须是clone出来的虚拟牌不能有子卡因为他接下来就要变成实体卡了
---@param card Card
function Engine:_addPrintedCard(card)
assert(card:isVirtual() and #card.subcards == 0)
table.insert(self.printed_cards, card)
local id = -#self.printed_cards - 1
card.id = id
self.printed_cards[id] = card
end
--- 获知当前的Engine是跑在服务端还是客户端并返回相应的实例。 --- 获知当前的Engine是跑在服务端还是客户端并返回相应的实例。
---@return Room | Client ---@return Room | Client
function Engine:currentRoom() function Engine:currentRoom()

View File

@ -191,7 +191,7 @@ function fk.CreateActiveSkill(spec)
if spec.target_filter then skill.targetFilter = spec.target_filter end if spec.target_filter then skill.targetFilter = spec.target_filter end
if spec.mod_target_filter then skill.modTargetFilter = spec.mod_target_filter end if spec.mod_target_filter then skill.modTargetFilter = spec.mod_target_filter end
if spec.feasible then if spec.feasible then
print(spec.name .. ": feasible is deprecated. Use target_num and card_num instead.") -- print(spec.name .. ": feasible is deprecated. Use target_num and card_num instead.")
skill.feasible = spec.feasible skill.feasible = spec.feasible
end end
if spec.on_use then skill.onUse = spec.on_use end if spec.on_use then skill.onUse = spec.on_use end

View File

@ -2,67 +2,68 @@
GameEvent.functions[GameEvent.Dying] = function(self) GameEvent.functions[GameEvent.Dying] = function(self)
local dyingStruct = table.unpack(self.data) local dyingStruct = table.unpack(self.data)
local self = self.room local room = self.room
local dyingPlayer = self:getPlayerById(dyingStruct.who) local logic = room.logic
local dyingPlayer = room:getPlayerById(dyingStruct.who)
dyingPlayer.dying = true dyingPlayer.dying = true
self:broadcastProperty(dyingPlayer, "dying") room:broadcastProperty(dyingPlayer, "dying")
self:sendLog{ room:sendLog{
type = "#EnterDying", type = "#EnterDying",
from = dyingPlayer.id, from = dyingPlayer.id,
} }
self.logic:trigger(fk.EnterDying, dyingPlayer, dyingStruct) logic:trigger(fk.EnterDying, dyingPlayer, dyingStruct)
if dyingPlayer.hp < 1 then if dyingPlayer.hp < 1 then
-- self.logic:trigger(fk.Dying, dyingPlayer, dyingStruct) -- room.logic:trigger(fk.Dying, dyingPlayer, dyingStruct)
local savers = self:getAlivePlayers() local savers = room:getAlivePlayers()
for _, p in ipairs(savers) do for _, p in ipairs(savers) do
if dyingPlayer.hp > 0 or dyingPlayer.dead or self.logic:trigger(fk.AskForPeaches, p, dyingStruct) then if dyingPlayer.hp > 0 or dyingPlayer.dead or logic:trigger(fk.AskForPeaches, p, dyingStruct) then
break break
end end
end end
self.logic:trigger(fk.AskForPeachesDone, dyingPlayer, dyingStruct) logic:trigger(fk.AskForPeachesDone, dyingPlayer, dyingStruct)
end end
if not dyingPlayer.dead and dyingPlayer.dying then if not dyingPlayer.dead and dyingPlayer.dying then
dyingPlayer.dying = false dyingPlayer.dying = false
self:broadcastProperty(dyingPlayer, "dying") room:broadcastProperty(dyingPlayer, "dying")
end end
self.logic:trigger(fk.AfterDying, dyingPlayer, dyingStruct) logic:trigger(fk.AfterDying, dyingPlayer, dyingStruct)
end end
GameEvent.functions[GameEvent.Death] = function(self) GameEvent.functions[GameEvent.Death] = function(self)
local deathStruct = table.unpack(self.data) local deathStruct = table.unpack(self.data)
local self = self.room local room = self.room
local victim = self:getPlayerById(deathStruct.who) local victim = room:getPlayerById(deathStruct.who)
victim.dead = true victim.dead = true
victim._splayer:setDied(true) victim._splayer:setDied(true)
table.removeOne(self.alive_players, victim) table.removeOne(room.alive_players, victim)
local logic = self.logic local logic = room.logic
logic:trigger(fk.BeforeGameOverJudge, victim, deathStruct) logic:trigger(fk.BeforeGameOverJudge, victim, deathStruct)
local killer = deathStruct.damage and deathStruct.damage.from or nil local killer = deathStruct.damage and deathStruct.damage.from or nil
if killer then if killer then
self:sendLog{ room:sendLog{
type = "#KillPlayer", type = "#KillPlayer",
to = {killer.id}, to = {killer.id},
from = victim.id, from = victim.id,
arg = victim.role, arg = victim.role,
} }
else else
self:sendLog{ room:sendLog{
type = "#KillPlayerWithNoKiller", type = "#KillPlayerWithNoKiller",
from = victim.id, from = victim.id,
arg = victim.role, arg = victim.role,
} }
end end
self:sendLogEvent("Death", {to = victim.id}) room:sendLogEvent("Death", {to = victim.id})
self:broadcastProperty(victim, "role") room:broadcastProperty(victim, "role")
self:broadcastProperty(victim, "dead") room:broadcastProperty(victim, "dead")
victim.drank = 0 victim.drank = 0
self:broadcastProperty(victim, "drank") room:broadcastProperty(victim, "drank")
logic:trigger(fk.GameOverJudge, victim, deathStruct) logic:trigger(fk.GameOverJudge, victim, deathStruct)
logic:trigger(fk.Death, victim, deathStruct) logic:trigger(fk.Death, victim, deathStruct)

View File

@ -33,7 +33,8 @@ end
GameEvent.functions[GameEvent.ChangeHp] = function(self) GameEvent.functions[GameEvent.ChangeHp] = function(self)
local player, num, reason, skillName, damageStruct = table.unpack(self.data) local player, num, reason, skillName, damageStruct = table.unpack(self.data)
local self = self.room local room = self.room
local logic = room.logic
if num == 0 then if num == 0 then
return false return false
end end
@ -47,39 +48,39 @@ GameEvent.functions[GameEvent.ChangeHp] = function(self)
damageEvent = damageStruct, damageEvent = damageStruct,
} }
if self.logic:trigger(fk.BeforeHpChanged, player, data) then if logic:trigger(fk.BeforeHpChanged, player, data) then
self.logic:breakEvent(false) logic:breakEvent(false)
end end
assert(not (data.reason == "recover" and data.num < 0)) assert(not (data.reason == "recover" and data.num < 0))
player.hp = math.min(player.hp + data.num, player.maxHp) player.hp = math.min(player.hp + data.num, player.maxHp)
self:broadcastProperty(player, "hp") room:broadcastProperty(player, "hp")
if reason == "damage" then if reason == "damage" then
sendDamageLog(self, damageStruct) sendDamageLog(room, damageStruct)
elseif reason == "loseHp" then elseif reason == "loseHp" then
self:sendLog{ room:sendLog{
type = "#LoseHP", type = "#LoseHP",
from = player.id, from = player.id,
arg = 0 - num, arg = 0 - num,
} }
self:sendLogEvent("LoseHP", {}) room:sendLogEvent("LoseHP", {})
elseif reason == "recover" then elseif reason == "recover" then
self:sendLog{ room:sendLog{
type = "#HealHP", type = "#HealHP",
from = player.id, from = player.id,
arg = num, arg = num,
} }
end end
self:sendLog{ room:sendLog{
type = "#ShowHPAndMaxHP", type = "#ShowHPAndMaxHP",
from = player.id, from = player.id,
arg = player.hp, arg = player.hp,
arg2 = player.maxHp, arg2 = player.maxHp,
} }
self.logic:trigger(fk.HpChanged, player, data) logic:trigger(fk.HpChanged, player, data)
if player.hp < 1 then if player.hp < 1 then
if num < 0 and not data.preventDying then if num < 0 and not data.preventDying then
@ -88,11 +89,11 @@ GameEvent.functions[GameEvent.ChangeHp] = function(self)
who = player.id, who = player.id,
damage = damageStruct, damage = damageStruct,
} }
self:enterDying(dyingStruct) room:enterDying(dyingStruct)
end end
elseif player.dying then elseif player.dying then
player.dying = false player.dying = false
self:broadcastProperty(player, "dying") room:broadcastProperty(player, "dying")
end end
return true return true
@ -100,9 +101,10 @@ end
GameEvent.functions[GameEvent.Damage] = function(self) GameEvent.functions[GameEvent.Damage] = function(self)
local damageStruct = table.unpack(self.data) local damageStruct = table.unpack(self.data)
local self = self.room local room = self.room
local logic = room.logic
if damageStruct.card and damageStruct.skillName == damageStruct.card.name .. "_skill" and not damageStruct.chain then if damageStruct.card and damageStruct.skillName == damageStruct.card.name .. "_skill" and not damageStruct.chain then
local cardEffectData = self.logic:getCurrentEvent():findParent(GameEvent.CardEffect) local cardEffectData = logic:getCurrentEvent():findParent(GameEvent.CardEffect)
if cardEffectData then if cardEffectData then
local cardEffectEvent = cardEffectData.data[1] local cardEffectEvent = cardEffectData.data[1]
damageStruct.damage = damageStruct.damage + (cardEffectEvent.additionalDamage or 0) damageStruct.damage = damageStruct.damage + (cardEffectEvent.additionalDamage or 0)
@ -128,8 +130,8 @@ GameEvent.functions[GameEvent.Damage] = function(self)
for _, struct in ipairs(stages) do for _, struct in ipairs(stages) do
local event, player = table.unpack(struct) local event, player = table.unpack(struct)
if self.logic:trigger(event, player, damageStruct) or damageStruct.damage < 1 then if logic:trigger(event, player, damageStruct) or damageStruct.damage < 1 then
self.logic:breakEvent(false) logic:breakEvent(false)
end end
assert(damageStruct.to:isInstanceOf(ServerPlayer)) assert(damageStruct.to:isInstanceOf(ServerPlayer))
@ -140,7 +142,7 @@ GameEvent.functions[GameEvent.Damage] = function(self)
end end
if damageStruct.card and damageStruct.damage > 0 then if damageStruct.card and damageStruct.damage > 0 then
local parentUseData = self.logic:getCurrentEvent():findParent(GameEvent.UseCard) local parentUseData = logic:getCurrentEvent():findParent(GameEvent.UseCard)
if parentUseData then if parentUseData then
local cardUseEvent = parentUseData.data[1] local cardUseEvent = parentUseData.data[1]
cardUseEvent.damageDealt = cardUseEvent.damageDealt or {} cardUseEvent.damageDealt = cardUseEvent.damageDealt or {}
@ -155,19 +157,19 @@ GameEvent.functions[GameEvent.Damage] = function(self)
-- 先扣减护甲,再扣体力值 -- 先扣减护甲,再扣体力值
local shield_to_lose = math.min(damageStruct.damage, damageStruct.to.shield) local shield_to_lose = math.min(damageStruct.damage, damageStruct.to.shield)
self:changeShield(damageStruct.to, -shield_to_lose) room:changeShield(damageStruct.to, -shield_to_lose)
if shield_to_lose < damageStruct.damage then if shield_to_lose < damageStruct.damage then
if not self:changeHp( if not room:changeHp(
damageStruct.to, damageStruct.to,
shield_to_lose - damageStruct.damage, shield_to_lose - damageStruct.damage,
"damage", "damage",
damageStruct.skillName, damageStruct.skillName,
damageStruct) then damageStruct) then
self.logic:breakEvent(false) logic:breakEvent(false)
end end
else else
sendDamageLog(self, damageStruct) sendDamageLog(room, damageStruct)
end end
stages = { stages = {
@ -178,7 +180,7 @@ GameEvent.functions[GameEvent.Damage] = function(self)
for _, struct in ipairs(stages) do for _, struct in ipairs(stages) do
local event, player = table.unpack(struct) local event, player = table.unpack(struct)
self.logic:trigger(event, player, damageStruct) logic:trigger(event, player, damageStruct)
end end
return true return true
@ -186,9 +188,10 @@ end
GameEvent.exit_funcs[GameEvent.Damage] = function(self) GameEvent.exit_funcs[GameEvent.Damage] = function(self)
local room = self.room local room = self.room
local logic = room.logic
local damageStruct = self.data[1] local damageStruct = self.data[1]
room.logic:trigger(fk.DamageFinished, damageStruct.to, damageStruct) logic:trigger(fk.DamageFinished, damageStruct.to, damageStruct)
if damageStruct.beginnerOfTheDamage and not damageStruct.chain then if damageStruct.beginnerOfTheDamage and not damageStruct.chain then
local targets = table.filter(room:getOtherPlayers(damageStruct.to), function(p) local targets = table.filter(room:getOtherPlayers(damageStruct.to), function(p)
@ -209,7 +212,9 @@ end
GameEvent.functions[GameEvent.LoseHp] = function(self) GameEvent.functions[GameEvent.LoseHp] = function(self)
local player, num, skillName = table.unpack(self.data) local player, num, skillName = table.unpack(self.data)
local self = self.room local room = self.room
local logic = room.logic
if num == nil then if num == nil then
num = 1 num = 1
elseif num < 1 then elseif num < 1 then
@ -221,23 +226,25 @@ GameEvent.functions[GameEvent.LoseHp] = function(self)
num = num, num = num,
skillName = skillName, skillName = skillName,
} }
if self.logic:trigger(fk.PreHpLost, player, data) or data.num < 1 then if logic:trigger(fk.PreHpLost, player, data) or data.num < 1 then
self.logic:breakEvent(false) logic:breakEvent(false)
end end
if not self:changeHp(player, -data.num, "loseHp", skillName) then if not room:changeHp(player, -data.num, "loseHp", skillName) then
self.logic:breakEvent(false) logic:breakEvent(false)
end end
self.logic:trigger(fk.HpLost, player, data) logic:trigger(fk.HpLost, player, data)
return true return true
end end
GameEvent.functions[GameEvent.Recover] = function(self) GameEvent.functions[GameEvent.Recover] = function(self)
local recoverStruct = table.unpack(self.data) local recoverStruct = table.unpack(self.data)
local self = self.room local room = self.room
local logic = room.logic
if recoverStruct.card then if recoverStruct.card then
local cardEffectData = self.logic:getCurrentEvent():findParent(GameEvent.CardEffect) local cardEffectData = logic:getCurrentEvent():findParent(GameEvent.CardEffect)
if cardEffectData then if cardEffectData then
local cardEffectEvent = cardEffectData.data[1] local cardEffectEvent = cardEffectData.data[1]
recoverStruct.num = recoverStruct.num + (cardEffectEvent.additionalRecover or 0) recoverStruct.num = recoverStruct.num + (cardEffectEvent.additionalRecover or 0)
@ -250,63 +257,63 @@ GameEvent.functions[GameEvent.Recover] = function(self)
local who = recoverStruct.who local who = recoverStruct.who
if self.logic:trigger(fk.PreHpRecover, who, recoverStruct) or recoverStruct.num < 1 then if logic:trigger(fk.PreHpRecover, who, recoverStruct) or recoverStruct.num < 1 then
self.logic:breakEvent(false) logic:breakEvent(false)
end end
if not self:changeHp(who, recoverStruct.num, "recover", recoverStruct.skillName) then if not room:changeHp(who, recoverStruct.num, "recover", recoverStruct.skillName) then
self.logic:breakEvent(false) logic:breakEvent(false)
end end
self.logic:trigger(fk.HpRecover, who, recoverStruct) logic:trigger(fk.HpRecover, who, recoverStruct)
return true return true
end end
GameEvent.functions[GameEvent.ChangeMaxHp] = function(self) GameEvent.functions[GameEvent.ChangeMaxHp] = function(self)
local player, num = table.unpack(self.data) local player, num = table.unpack(self.data)
local self = self.room local room = self.room
if num == 0 then if num == 0 then
return false return false
end end
player.maxHp = math.max(player.maxHp + num, 0) player.maxHp = math.max(player.maxHp + num, 0)
self:broadcastProperty(player, "maxHp") room:broadcastProperty(player, "maxHp")
self:sendLogEvent("ChangeMaxHp", { room:sendLogEvent("ChangeMaxHp", {
player = player.id, player = player.id,
num = num, num = num,
}) })
self:sendLog{ room:sendLog{
type = num > 0 and "#HealMaxHP" or "#LoseMaxHP", type = num > 0 and "#HealMaxHP" or "#LoseMaxHP",
from = player.id, from = player.id,
arg = num > 0 and num or - num, arg = num > 0 and num or - num,
} }
if player.maxHp == 0 then if player.maxHp == 0 then
player.hp = 0 player.hp = 0
self:broadcastProperty(player, "hp") room:broadcastProperty(player, "hp")
self:sendLog{ room:sendLog{
type = "#ShowHPAndMaxHP", type = "#ShowHPAndMaxHP",
from = player.id, from = player.id,
arg = 0, arg = 0,
arg2 = 0, arg2 = 0,
} }
self:killPlayer({ who = player.id }) room:killPlayer({ who = player.id })
return false return false
end end
local diff = player.hp - player.maxHp local diff = player.hp - player.maxHp
if diff > 0 then if diff > 0 then
if not self:changeHp(player, -diff) then if not room:changeHp(player, -diff) then
player.hp = player.hp - diff player.hp = player.hp - diff
end end
end end
self:sendLog{ room:sendLog{
type = "#ShowHPAndMaxHP", type = "#ShowHPAndMaxHP",
from = player.id, from = player.id,
arg = player.hp, arg = player.hp,
arg2 = player.maxHp, arg2 = player.maxHp,
} }
self.logic:trigger(fk.MaxHpChanged, player, { num = num }) room.logic:trigger(fk.MaxHpChanged, player, { num = num })
return true return true
end end

View File

@ -2,69 +2,70 @@
GameEvent.functions[GameEvent.Judge] = function(self) GameEvent.functions[GameEvent.Judge] = function(self)
local data = table.unpack(self.data) local data = table.unpack(self.data)
local self = self.room local room = self.room
local logic = room.logic
local who = data.who local who = data.who
self.logic:trigger(fk.StartJudge, who, data) logic:trigger(fk.StartJudge, who, data)
data.card = data.card or Fk:getCardById(self:getNCards(1)[1]) data.card = data.card or Fk:getCardById(room:getNCards(1)[1])
if data.reason ~= "" then if data.reason ~= "" then
self:sendLog{ room:sendLog{
type = "#StartJudgeReason", type = "#StartJudgeReason",
from = who.id, from = who.id,
arg = data.reason, arg = data.reason,
} }
end end
self:sendLog{ room:sendLog{
type = "#InitialJudge", type = "#InitialJudge",
from = who.id, from = who.id,
card = {data.card.id}, card = {data.card.id},
} }
self:moveCardTo(data.card, Card.Processing, nil, fk.ReasonJudge) room:moveCardTo(data.card, Card.Processing, nil, fk.ReasonJudge)
self:sendFootnote({ data.card.id }, { room:sendFootnote({ data.card.id }, {
type = "##JudgeCard", type = "##JudgeCard",
arg = data.reason, arg = data.reason,
}) })
self.logic:trigger(fk.AskForRetrial, who, data) logic:trigger(fk.AskForRetrial, who, data)
self.logic:trigger(fk.FinishRetrial, who, data) logic:trigger(fk.FinishRetrial, who, data)
Fk:filterCard(data.card.id, who, data) Fk:filterCard(data.card.id, who, data)
self:sendLog{ room:sendLog{
type = "#JudgeResult", type = "#JudgeResult",
from = who.id, from = who.id,
card = {data.card.id}, card = {data.card.id},
} }
self:sendFootnote({ data.card.id }, { room:sendFootnote({ data.card.id }, {
type = "##JudgeCard", type = "##JudgeCard",
arg = data.reason, arg = data.reason,
}) })
if data.pattern then if data.pattern then
self:delay(400); room:delay(400);
self:setCardEmotion(data.card.id, data.card:matchPattern(data.pattern) and "judgegood" or "judgebad") room:setCardEmotion(data.card.id, data.card:matchPattern(data.pattern) and "judgegood" or "judgebad")
self:delay(900); room:delay(900);
end end
if self.logic:trigger(fk.FinishJudge, who, data) then if logic:trigger(fk.FinishJudge, who, data) then
self.logic:breakEvent() logic:breakEvent()
end 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 room = self.room
if (self.interrupted or not data.skipDrop) and self:getCardArea(data.card.id) == Card.Processing then if (self.interrupted or not data.skipDrop) and room:getCardArea(data.card.id) == Card.Processing then
self:moveCardTo(data.card, Card.DiscardPile, nil, fk.ReasonJudge) room:moveCardTo(data.card, Card.DiscardPile, nil, fk.ReasonJudge)
end end
if not self.interrupted then return end if not self.interrupted then return end
-- prohibit access to judge.card -- prohibit access to judge.card
setmetatable(data, { setmetatable(data, {
__index = function(self, key) __index = function(s, key)
if key == "card" then if key == "card" then
error("__manuallyBreak") error("__manuallyBreak")
end end
return rawget(self, key) return rawget(s, key)
end end
}) })
end end

View File

@ -2,7 +2,7 @@
GameEvent.functions[GameEvent.MoveCards] = function(self) GameEvent.functions[GameEvent.MoveCards] = function(self)
local args = self.data local args = self.data
local self = self.room local room = self.room
---@type CardsMoveStruct[] ---@type CardsMoveStruct[]
local cardsMoveStructs = {} local cardsMoveStructs = {}
local infoCheck = function(info) local infoCheck = function(info)
@ -20,8 +20,8 @@ GameEvent.functions[GameEvent.MoveCards] = function(self)
for _, id in ipairs(cardsMoveInfo.ids) do for _, id in ipairs(cardsMoveInfo.ids) do
table.insert(infos, { table.insert(infos, {
cardId = id, cardId = id,
fromArea = self:getCardArea(id), fromArea = room:getCardArea(id),
fromSpecialName = cardsMoveInfo.from and self:getPlayerById(cardsMoveInfo.from):getPileNameOfId(id), fromSpecialName = cardsMoveInfo.from and room:getPlayerById(cardsMoveInfo.from):getPileNameOfId(id),
}) })
end end
@ -48,11 +48,11 @@ GameEvent.functions[GameEvent.MoveCards] = function(self)
return false return false
end end
if self.logic:trigger(fk.BeforeCardsMove, nil, cardsMoveStructs) then if room.logic:trigger(fk.BeforeCardsMove, nil, cardsMoveStructs) then
self.logic:breakEvent(false) room.logic:breakEvent(false)
end end
self:notifyMoveCards(nil, cardsMoveStructs) room:notifyMoveCards(nil, cardsMoveStructs)
for _, data in ipairs(cardsMoveStructs) do for _, data in ipairs(cardsMoveStructs) do
if #data.moveInfo > 0 then if #data.moveInfo > 0 then
@ -60,49 +60,49 @@ GameEvent.functions[GameEvent.MoveCards] = function(self)
---@param info MoveInfo ---@param info MoveInfo
for _, info in ipairs(data.moveInfo) do for _, info in ipairs(data.moveInfo) do
local realFromArea = self:getCardArea(info.cardId) local realFromArea = room:getCardArea(info.cardId)
local playerAreas = { Player.Hand, Player.Equip, Player.Judge, Player.Special } local playerAreas = { Player.Hand, Player.Equip, Player.Judge, Player.Special }
if table.contains(playerAreas, realFromArea) and data.from then if table.contains(playerAreas, realFromArea) and data.from then
local from = self:getPlayerById(data.from) local from = room:getPlayerById(data.from)
from:removeCards(realFromArea, { info.cardId }, info.fromSpecialName) from:removeCards(realFromArea, { info.cardId }, info.fromSpecialName)
elseif realFromArea ~= Card.Unknown then elseif realFromArea ~= Card.Unknown then
local fromAreaIds = {} local fromAreaIds = {}
if realFromArea == Card.Processing then if realFromArea == Card.Processing then
fromAreaIds = self.processing_area fromAreaIds = room.processing_area
elseif realFromArea == Card.DrawPile then elseif realFromArea == Card.DrawPile then
fromAreaIds = self.draw_pile fromAreaIds = room.draw_pile
elseif realFromArea == Card.DiscardPile then elseif realFromArea == Card.DiscardPile then
fromAreaIds = self.discard_pile fromAreaIds = room.discard_pile
elseif realFromArea == Card.Void then elseif realFromArea == Card.Void then
fromAreaIds = self.void fromAreaIds = room.void
end end
table.removeOne(fromAreaIds, info.cardId) table.removeOne(fromAreaIds, info.cardId)
end end
if table.contains(playerAreas, data.toArea) and data.to then if table.contains(playerAreas, data.toArea) and data.to then
local to = self:getPlayerById(data.to) local to = room:getPlayerById(data.to)
to:addCards(data.toArea, { info.cardId }, data.specialName) to:addCards(data.toArea, { info.cardId }, data.specialName)
else else
local toAreaIds = {} local toAreaIds = {}
if data.toArea == Card.Processing then if data.toArea == Card.Processing then
toAreaIds = self.processing_area toAreaIds = room.processing_area
elseif data.toArea == Card.DrawPile then elseif data.toArea == Card.DrawPile then
toAreaIds = self.draw_pile toAreaIds = room.draw_pile
elseif data.toArea == Card.DiscardPile then elseif data.toArea == Card.DiscardPile then
toAreaIds = self.discard_pile toAreaIds = room.discard_pile
elseif data.toArea == Card.Void then elseif data.toArea == Card.Void then
toAreaIds = self.void toAreaIds = room.void
end end
if data.toArea == Card.DrawPile then if data.toArea == Card.DrawPile then
local putIndex = data.drawPilePosition or 1 local putIndex = data.drawPilePosition or 1
if putIndex == -1 then if putIndex == -1 then
putIndex = #self.draw_pile + 1 putIndex = #room.draw_pile + 1
elseif putIndex < 1 or putIndex > #self.draw_pile + 1 then elseif putIndex < 1 or putIndex > #room.draw_pile + 1 then
putIndex = 1 putIndex = 1
end end
@ -111,13 +111,13 @@ GameEvent.functions[GameEvent.MoveCards] = function(self)
table.insert(toAreaIds, info.cardId) table.insert(toAreaIds, info.cardId)
end end
end end
self:setCardArea(info.cardId, data.toArea, data.to) room: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
self:doBroadcastNotify("UpdateDrawPile", #self.draw_pile) room:doBroadcastNotify("UpdateDrawPile", #room.draw_pile)
end end
if not (data.to and data.toArea ~= Card.PlayerHand) then if not (data.to and data.toArea ~= Card.PlayerHand) then
Fk:filterCard(info.cardId, self:getPlayerById(data.to)) Fk:filterCard(info.cardId, room:getPlayerById(data.to))
end end
local currentCard = Fk:getCardById(info.cardId) local currentCard = Fk:getCardById(info.cardId)
@ -126,17 +126,17 @@ GameEvent.functions[GameEvent.MoveCards] = function(self)
realFromArea == Player.Hand and realFromArea == Player.Hand and
data.from data.from
then then
self:setCardMark(currentCard, name, 0) room:setCardMark(currentCard, name, 0)
end end
end end
if if
data.toArea == Player.Equip and data.toArea == Player.Equip and
currentCard.type == Card.TypeEquip and currentCard.type == Card.TypeEquip and
data.to ~= nil and data.to ~= nil and
self:getPlayerById(data.to):isAlive() and room:getPlayerById(data.to):isAlive() and
currentCard.equip_skill currentCard.equip_skill
then then
currentCard:onInstall(self, self:getPlayerById(data.to)) currentCard:onInstall(room, room:getPlayerById(data.to))
end end
if if
@ -145,12 +145,12 @@ GameEvent.functions[GameEvent.MoveCards] = function(self)
data.from ~= nil and data.from ~= nil and
currentCard.equip_skill currentCard.equip_skill
then then
currentCard:onUninstall(self, self:getPlayerById(data.from)) currentCard:onUninstall(room, room:getPlayerById(data.from))
end end
end end
end end
end end
self.logic:trigger(fk.AfterCardsMove, nil, cardsMoveStructs) room.logic:trigger(fk.AfterCardsMove, nil, cardsMoveStructs)
return true return true
end end

View File

@ -180,30 +180,31 @@ local sendCardEmotionAndLog = function(room, cardUseEvent)
end end
end end
---@param self GameEvent
GameEvent.functions[GameEvent.UseCard] = function(self) GameEvent.functions[GameEvent.UseCard] = function(self)
local cardUseEvent = table.unpack(self.data) local cardUseEvent = table.unpack(self.data)
local self = self.room local room = self.room
local logic = room.logic
local from = cardUseEvent.from local from = cardUseEvent.from
self:moveCards({ room:moveCards({
ids = self:getSubcardsByRule(cardUseEvent.card), ids = room:getSubcardsByRule(cardUseEvent.card),
from = from, from = from,
toArea = Card.Processing, toArea = Card.Processing,
moveReason = fk.ReasonUse, moveReason = fk.ReasonUse,
}) })
if cardUseEvent.card.skill then if cardUseEvent.card.skill then
cardUseEvent.card.skill:onUse(self, cardUseEvent) cardUseEvent.card.skill:onUse(room, cardUseEvent)
end end
if self.logic:trigger(fk.PreCardUse, self:getPlayerById(cardUseEvent.from), cardUseEvent) then if logic:trigger(fk.PreCardUse, room:getPlayerById(cardUseEvent.from), cardUseEvent) then
self.logic:breakEvent() logic:breakEvent()
end end
sendCardEmotionAndLog(self, cardUseEvent) sendCardEmotionAndLog(room, cardUseEvent)
if not cardUseEvent.extraUse then if not cardUseEvent.extraUse then
self:getPlayerById(cardUseEvent.from):addCardUseHistory(cardUseEvent.card.trueName, 1) room:getPlayerById(cardUseEvent.from):addCardUseHistory(cardUseEvent.card.trueName, 1)
end end
if cardUseEvent.responseToEvent then if cardUseEvent.responseToEvent then
@ -216,22 +217,22 @@ GameEvent.functions[GameEvent.UseCard] = function(self)
break break
end end
self.logic:trigger(event, self:getPlayerById(cardUseEvent.from), cardUseEvent) logic:trigger(event, room:getPlayerById(cardUseEvent.from), cardUseEvent)
if event == fk.CardUsing then if event == fk.CardUsing then
self:doCardUseEffect(cardUseEvent) room:doCardUseEffect(cardUseEvent)
end end
end end
end end
GameEvent.cleaners[GameEvent.UseCard] = function(self) GameEvent.cleaners[GameEvent.UseCard] = function(self)
local cardUseEvent = table.unpack(self.data) local cardUseEvent = table.unpack(self.data)
local self = self.room local room = self.room
self.logic:trigger(fk.CardUseFinished, self:getPlayerById(cardUseEvent.from), cardUseEvent) room.logic:trigger(fk.CardUseFinished, room:getPlayerById(cardUseEvent.from), cardUseEvent)
local leftRealCardIds = self:getSubcardsByRule(cardUseEvent.card, { Card.Processing }) local leftRealCardIds = room:getSubcardsByRule(cardUseEvent.card, { Card.Processing })
if #leftRealCardIds > 0 then if #leftRealCardIds > 0 then
self:moveCards({ room:moveCards({
ids = leftRealCardIds, ids = leftRealCardIds,
toArea = Card.DiscardPile, toArea = Card.DiscardPile,
moveReason = fk.ReasonUse, moveReason = fk.ReasonUse,
@ -241,20 +242,21 @@ end
GameEvent.functions[GameEvent.RespondCard] = function(self) GameEvent.functions[GameEvent.RespondCard] = function(self)
local cardResponseEvent = table.unpack(self.data) local cardResponseEvent = table.unpack(self.data)
local self = self.room local room = self.room
local logic = room.logic
local from = cardResponseEvent.customFrom or cardResponseEvent.from local from = cardResponseEvent.customFrom or cardResponseEvent.from
local card = cardResponseEvent.card local card = cardResponseEvent.card
local cardIds = self:getSubcardsByRule(card) local cardIds = room:getSubcardsByRule(card)
if card:isVirtual() then if card:isVirtual() then
if #cardIds == 0 then if #cardIds == 0 then
self:sendLog{ room:sendLog{
type = "#ResponsePlayV0Card", type = "#ResponsePlayV0Card",
from = from, from = from,
arg = card:toLogString(), arg = card:toLogString(),
} }
else else
self:sendLog{ room:sendLog{
type = "#ResponsePlayVCard", type = "#ResponsePlayVCard",
from = from, from = from,
card = cardIds, card = cardIds,
@ -262,46 +264,46 @@ GameEvent.functions[GameEvent.RespondCard] = function(self)
} }
end end
else else
self:sendLog{ room:sendLog{
type = "#ResponsePlayCard", type = "#ResponsePlayCard",
from = from, from = from,
card = cardIds, card = cardIds,
} }
end end
self:moveCards({ room:moveCards({
ids = cardIds, ids = cardIds,
from = from, from = from,
toArea = Card.Processing, toArea = Card.Processing,
moveReason = fk.ReasonResonpse, moveReason = fk.ReasonResonpse,
}) })
if #cardIds > 0 then if #cardIds > 0 then
self:sendFootnote(cardIds, { room:sendFootnote(cardIds, {
type = "##ResponsePlayCard", type = "##ResponsePlayCard",
from = from, from = from,
}) })
if card:isVirtual() then if card:isVirtual() then
self:sendCardVirtName(cardIds, card.name) room:sendCardVirtName(cardIds, card.name)
end end
end end
if self.logic:trigger(fk.PreCardRespond, self:getPlayerById(cardResponseEvent.from), cardResponseEvent) then if logic:trigger(fk.PreCardRespond, room:getPlayerById(cardResponseEvent.from), cardResponseEvent) then
self.logic:breakEvent() logic:breakEvent()
end end
playCardEmotionAndSound(self, self:getPlayerById(from), card) playCardEmotionAndSound(room, room:getPlayerById(from), card)
self.logic:trigger(fk.CardResponding, self:getPlayerById(cardResponseEvent.from), cardResponseEvent) logic:trigger(fk.CardResponding, room:getPlayerById(cardResponseEvent.from), cardResponseEvent)
end end
GameEvent.cleaners[GameEvent.RespondCard] = function(self) GameEvent.cleaners[GameEvent.RespondCard] = function(self)
local cardResponseEvent = table.unpack(self.data) local cardResponseEvent = table.unpack(self.data)
local self = self.room local room = self.room
self.logic:trigger(fk.CardRespondFinished, self:getPlayerById(cardResponseEvent.from), cardResponseEvent) room.logic:trigger(fk.CardRespondFinished, room:getPlayerById(cardResponseEvent.from), cardResponseEvent)
local realCardIds = self:getSubcardsByRule(cardResponseEvent.card, { Card.Processing }) local realCardIds = room:getSubcardsByRule(cardResponseEvent.card, { Card.Processing })
if #realCardIds > 0 and not cardResponseEvent.skipDrop then if #realCardIds > 0 and not cardResponseEvent.skipDrop then
self:moveCards({ room:moveCards({
ids = realCardIds, ids = realCardIds,
toArea = Card.DiscardPile, toArea = Card.DiscardPile,
moveReason = fk.ReasonResonpse, moveReason = fk.ReasonResonpse,
@ -311,40 +313,41 @@ end
GameEvent.functions[GameEvent.CardEffect] = function(self) GameEvent.functions[GameEvent.CardEffect] = function(self)
local cardEffectEvent = table.unpack(self.data) local cardEffectEvent = table.unpack(self.data)
local self = self.room local room = self.room
local logic = room.logic
for _, event in ipairs({ fk.PreCardEffect, fk.BeforeCardEffect, fk.CardEffecting, fk.CardEffectFinished }) do for _, event in ipairs({ fk.PreCardEffect, fk.BeforeCardEffect, fk.CardEffecting, fk.CardEffectFinished }) do
local user = cardEffectEvent.from and self:getPlayerById(cardEffectEvent.from) or nil local user = cardEffectEvent.from and room:getPlayerById(cardEffectEvent.from) or nil
if cardEffectEvent.isCancellOut then if cardEffectEvent.isCancellOut then
if self.logic:trigger(fk.CardEffectCancelledOut, user, cardEffectEvent) then if logic:trigger(fk.CardEffectCancelledOut, user, cardEffectEvent) then
cardEffectEvent.isCancellOut = false cardEffectEvent.isCancellOut = false
else else
self.logic:breakEvent() logic:breakEvent()
end end
end end
if if
not cardEffectEvent.toCard and not cardEffectEvent.toCard and
( (
not (self:getPlayerById(cardEffectEvent.to):isAlive() and cardEffectEvent.to) not (room:getPlayerById(cardEffectEvent.to):isAlive() and cardEffectEvent.to)
or #self:deadPlayerFilter(TargetGroup:getRealTargets(cardEffectEvent.tos)) == 0 or #room:deadPlayerFilter(TargetGroup:getRealTargets(cardEffectEvent.tos)) == 0
) )
then then
self.logic:breakEvent() logic:breakEvent()
end end
if table.contains((cardEffectEvent.nullifiedTargets or Util.DummyTable), cardEffectEvent.to) then if table.contains((cardEffectEvent.nullifiedTargets or Util.DummyTable), cardEffectEvent.to) then
self.logic:breakEvent() logic:breakEvent()
end end
if event == fk.PreCardEffect then if event == fk.PreCardEffect then
if cardEffectEvent.from and self.logic:trigger(event, self:getPlayerById(cardEffectEvent.from), cardEffectEvent) then if cardEffectEvent.from and logic:trigger(event, room:getPlayerById(cardEffectEvent.from), cardEffectEvent) then
self.logic:breakEvent() logic:breakEvent()
end end
elseif cardEffectEvent.to and self.logic:trigger(event, self:getPlayerById(cardEffectEvent.to), cardEffectEvent) then elseif cardEffectEvent.to and logic:trigger(event, room:getPlayerById(cardEffectEvent.to), cardEffectEvent) then
self.logic:breakEvent() logic:breakEvent()
end end
self:handleCardEffect(event, cardEffectEvent) room:handleCardEffect(event, cardEffectEvent)
end end
end end

View File

@ -91,6 +91,7 @@ function Room:initialize(_room)
self.skill_costs = {} self.skill_costs = {}
self.card_marks = {} self.card_marks = {}
self.filtered_cards = {} self.filtered_cards = {}
self.printed_cards = {}
self.settings = json.decode(self.room:settings()) self.settings = json.decode(self.room:settings())
self.disabled_packs = self.settings.disabledPack self.disabled_packs = self.settings.disabledPack
@ -3182,6 +3183,20 @@ function Room:canMoveCardInBoard(flag, players, excludeIds)
return targets return targets
end end
--- 现场印卡。当然了,这个卡只和这个房间有关。
---@param name string @ 牌名
---@param suit Suit|nil @ 花色
---@param number integer|nil @ 点数
---@return Card
function Room:printCard(name, suit, number)
local cd = Fk:cloneCard(name, suit, number)
Fk:_addPrintedCard(cd)
table.insert(self.void, cd.id)
self:setCardArea(cd.id, Card.Void, nil)
self:doBroadcastNotify("PrintCard", json.encode{ name, suit, number })
return cd
end
function Room:updateQuestSkillState(player, skillName, failed) function Room:updateQuestSkillState(player, skillName, failed)
assert(Fk.skills[skillName].frequency == Skill.Quest) assert(Fk.skills[skillName].frequency == Skill.Quest)

View File

@ -192,12 +192,12 @@ fk.ReasonJudge = 11
---@class LogMessage ---@class LogMessage
---@field public type string ---@field public type string
---@field public from integer ---@field public from integer | nil
---@field public to integer[] ---@field public to integer[] | nil
---@field public card integer[] ---@field public card integer[] | nil
---@field public arg any ---@field public arg any | nil
---@field public arg2 any ---@field public arg2 any | nil
---@field public arg3 any ---@field public arg3 any | nil
---@class SkillUseStruct ---@class SkillUseStruct
---@field public skill Skill ---@field public skill Skill

View File

@ -41,12 +41,12 @@ local cheat = fk.CreateActiveSkill{
end end
local cardName = room:askForChoice(from, allCardNames, "cheat") local cardName = room:askForChoice(from, allCardNames, "cheat")
local toGain = nil local toGain = room:printCard(cardName, Card.Heart, 1)
if #allCardMapper[cardName] > 0 then -- if #allCardMapper[cardName] > 0 then
toGain = allCardMapper[cardName][math.random(1, #allCardMapper[cardName])] -- toGain = allCardMapper[cardName][math.random(1, #allCardMapper[cardName])]
end -- end
from:addToPile(self.name, toGain, true, self.name) -- from:addToPile(self.name, toGain, true, self.name)
-- room:setCardMark(Fk:getCardById(toGain), "@@test_cheat-phase", 1) -- room:setCardMark(Fk:getCardById(toGain), "@@test_cheat-phase", 1)
-- room:setCardMark(Fk:getCardById(toGain), "@@test_cheat-inhand", 1) -- room:setCardMark(Fk:getCardById(toGain), "@@test_cheat-inhand", 1)
room:obtainCard(effect.from, toGain, true, fk.ReasonPrey) room:obtainCard(effect.from, toGain, true, fk.ReasonPrey)
@ -323,7 +323,7 @@ Fk:loadTranslationTable{
[":test_filter"] = "你的点数大于11的牌视为无中生有。", [":test_filter"] = "你的点数大于11的牌视为无中生有。",
["mouxusheng"] = "谋徐盛", ["mouxusheng"] = "谋徐盛",
-- ["cheat"] = "小开", -- ["cheat"] = "小开",
[":cheat"] = "出牌阶段,你可以获得一张想要的牌", [":cheat"] = "出牌阶段,你可以以红桃A打印一张想要的牌并获得之",
["#cheat"] = "cheat你可以获得一张想要的牌", ["#cheat"] = "cheat你可以获得一张想要的牌",
-- ["@@test_cheat-phase"] = "苦肉", -- ["@@test_cheat-phase"] = "苦肉",
-- ["@@test_cheat-inhand"] = "连营", -- ["@@test_cheat-inhand"] = "连营",

View File

@ -284,7 +284,7 @@ void Router::handlePacket(const QByteArray &rawPacket) {
} else if (command == "KickPlayer") { } else if (command == "KickPlayer") {
int i = jsonData.toInt(); int i = jsonData.toInt();
auto p = room->findPlayer(i); auto p = room->findPlayer(i);
if (p) room->removePlayer(p); if (p && !room->isStarted()) room->removePlayer(p);
} else if (command == "Ready") { } else if (command == "Ready") {
player->setReady(!player->isReady()); player->setReady(!player->isReady());
room->doBroadcastNotify(room->getPlayers(), "ReadyChanged", room->doBroadcastNotify(room->getPlayers(), "ReadyChanged",

View File

@ -541,7 +541,7 @@ void Room::gameOver() {
if (p->getState() == Player::Offline) { if (p->getState() == Player::Offline) {
auto pid = p->getId(); auto pid = p->getId();
addRunRate(pid, mode); addRunRate(pid, mode);
addRunRate(pid, mode); // addRunRate(pid, mode);
server->temporarilyBan(pid); server->temporarilyBan(pid);
} }
p->deleteLater(); p->deleteLater();