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

View File

@ -21,6 +21,7 @@ Item {
property bool isFull: false
property bool isAllReady: false
property bool isReady: false
property bool canKickOwner: false
property alias popupBox: popupBox
property alias manualBox: manualBox
@ -71,6 +72,8 @@ Item {
onIsStartedChanged: {
if (isStarted) {
bgm.play();
canKickOwner = false;
kickOwnerTimer.stop();
} else {
// bgm.stop();
}
@ -209,6 +212,40 @@ Item {
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 {
x: parent.width / 2 + 60
y: parent.height / 2 - 30

View File

@ -60,6 +60,7 @@ function Client:initialize()
self.skill_costs = {}
self.card_marks = {}
self.filtered_cards = {}
self.printed_cards = {}
self.disabled_packs = {}
self.disabled_generals = {}
@ -932,6 +933,13 @@ fk.client_callback["EnterLobby"] = function(jsonData)
c:notifyUI("EnterLobby", jsonData)
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)
ClientInstance = Client:new()
dofile "lua/client/client_util.lua"

View File

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

View File

@ -23,6 +23,7 @@
---@field public currentResponsePattern string @ 要求用牌的种类(如要求用特定花色的桃···)
---@field public currentResponseReason string @ 要求用牌的原因(如濒死,被特定牌指定,使用特定技能···)
---@field public filtered_cards table<integer, Card> @ 被锁视技影响的卡牌
---@field public printed_cards table<integer, Card> @ 被某些房间现场打印的卡牌id都是负数且从-2开始
local Engine = class("Engine")
--- Engine的构造函数。
@ -62,6 +63,7 @@ local _foreign_keys = {
"currentResponsePattern",
"currentResponseReason",
"filtered_cards",
"printed_cards",
}
function Engine:__index(k)
@ -398,9 +400,11 @@ end
---@param ignoreFilter boolean|nil @ 是否要无视掉锁定视为技,直接获得真牌
---@return Card @ 这个id对应的卡牌
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
ret = self.filtered_cards[id] or self.cards[id]
ret = self.filtered_cards[id] or card_tab[id]
end
return ret
end
@ -466,6 +470,18 @@ function Engine:filterCard(id, player, data)
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是跑在服务端还是客户端并返回相应的实例。
---@return Room | Client
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.mod_target_filter then skill.modTargetFilter = spec.mod_target_filter end
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
end
if spec.on_use then skill.onUse = spec.on_use end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -91,6 +91,7 @@ function Room:initialize(_room)
self.skill_costs = {}
self.card_marks = {}
self.filtered_cards = {}
self.printed_cards = {}
self.settings = json.decode(self.room:settings())
self.disabled_packs = self.settings.disabledPack
@ -3182,6 +3183,20 @@ function Room:canMoveCardInBoard(flag, players, excludeIds)
return targets
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)
assert(Fk.skills[skillName].frequency == Skill.Quest)

View File

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

View File

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

View File

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

View File

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