272 lines
7.7 KiB
Lua
272 lines
7.7 KiB
Lua
-- SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
---@param victim ServerPlayer
|
|
local function getWinner(victim)
|
|
local room = victim.room
|
|
local winner = ""
|
|
local alive = room.alive_players
|
|
|
|
if victim.role == "lord" then
|
|
if #alive == 1 and alive[1].role == "renegade" then
|
|
winner = "renegade"
|
|
else
|
|
winner = "rebel"
|
|
end
|
|
elseif victim.role ~= "loyalist" then
|
|
local lord_win = true
|
|
for _, p in ipairs(alive) do
|
|
if p.role == "rebel" or p.role == "renegade" then
|
|
lord_win = false
|
|
break
|
|
end
|
|
end
|
|
if lord_win then
|
|
winner = "lord+loyalist"
|
|
end
|
|
end
|
|
|
|
return winner
|
|
end
|
|
|
|
---@param killer ServerPlayer
|
|
local function rewardAndPunish(killer, victim)
|
|
if killer.dead then return end
|
|
if victim.role == "rebel" then
|
|
killer:drawCards(3, "kill")
|
|
elseif victim.role == "loyalist" and killer.role == "lord" then
|
|
killer:throwAllCards("he")
|
|
end
|
|
end
|
|
|
|
GameRule = fk.CreateTriggerSkill{
|
|
name = "game_rule",
|
|
refresh_events = {
|
|
fk.GameStart, fk.DrawInitialCards, fk.TurnStart,
|
|
fk.EventPhaseProceeding, fk.EventPhaseEnd, fk.EventPhaseChanging,
|
|
fk.RoundStart,
|
|
fk.AskForPeaches, fk.AskForPeachesDone,
|
|
fk.GameOverJudge, fk.BuryVictim,
|
|
},
|
|
priority = 0,
|
|
|
|
can_refresh = function(self, event, target, player, data)
|
|
return (target == player) or (target == nil)
|
|
end,
|
|
|
|
on_refresh = function(self, event, target, player, data)
|
|
local room = player.room
|
|
if room:getTag("SkipGameRule") then
|
|
room:setTag("SkipGameRule", false)
|
|
return false
|
|
end
|
|
|
|
if event == fk.GameStart then
|
|
room:setTag("FirstRound", true)
|
|
room:setTag("RoundCount", 0)
|
|
return false
|
|
end
|
|
|
|
switch(event, {
|
|
[fk.DrawInitialCards] = function()
|
|
if data.num > 0 then
|
|
-- TODO: need a new function to call the UI
|
|
local cardIds = room:getNCards(data.num)
|
|
player:addCards(Player.Hand, cardIds)
|
|
for _, id in ipairs(cardIds) do
|
|
Fk:filterCard(id, player)
|
|
end
|
|
local move_to_notify = {} ---@type CardsMoveStruct
|
|
move_to_notify.toArea = Card.PlayerHand
|
|
move_to_notify.to = player.id
|
|
move_to_notify.moveInfo = {}
|
|
move_to_notify.moveReason = fk.ReasonDraw
|
|
for _, id in ipairs(cardIds) do
|
|
table.insert(move_to_notify.moveInfo,
|
|
{ cardId = id, fromArea = Card.DrawPile })
|
|
end
|
|
room:notifyMoveCards(nil, {move_to_notify})
|
|
|
|
for _, id in ipairs(cardIds) do
|
|
room:setCardArea(id, Card.PlayerHand, player.id)
|
|
end
|
|
|
|
room.logic:trigger(fk.AfterDrawInitialCards, player, data)
|
|
end
|
|
end,
|
|
[fk.RoundStart] = function()
|
|
if room:getTag("FirstRound") then
|
|
room:setTag("FirstRound", false)
|
|
end
|
|
|
|
room:setTag("RoundCount", room:getTag("RoundCount") + 1)
|
|
room:doBroadcastNotify("UpdateRoundNum", room:getTag("RoundCount"))
|
|
|
|
for _, p in ipairs(room.players) do
|
|
p:setCardUseHistory("", 0, Player.HistoryRound)
|
|
p:setSkillUseHistory("", 0, Player.HistoryRound)
|
|
for name, _ in pairs(p.mark) do
|
|
if name:endsWith("-round") then
|
|
room:setPlayerMark(p, name, 0)
|
|
end
|
|
end
|
|
end
|
|
|
|
room:sendLog{ type = "$AppendSeparator" }
|
|
end,
|
|
[fk.TurnStart] = function()
|
|
if not player.faceup then
|
|
player:turnOver()
|
|
elseif not player.dead then
|
|
player:play()
|
|
end
|
|
end,
|
|
[fk.EventPhaseProceeding] = function()
|
|
switch(player.phase, {
|
|
[Player.PhaseNone] = function()
|
|
error("You should never proceed PhaseNone")
|
|
end,
|
|
[Player.RoundStart] = function()
|
|
|
|
end,
|
|
[Player.Start] = function()
|
|
|
|
end,
|
|
[Player.Judge] = function()
|
|
local cards = player:getCardIds(Player.Judge)
|
|
for i = #cards, 1, -1 do
|
|
local card
|
|
card = player:removeVirtualEquip(cards[i])
|
|
if not card then
|
|
card = Fk:getCardById(cards[i])
|
|
end
|
|
room:moveCardTo(card, Card.Processing, nil, fk.ReasonPut, self.name)
|
|
|
|
---@type CardEffectEvent
|
|
local effect_data = {
|
|
card = card,
|
|
to = player.id,
|
|
tos = { {player.id} },
|
|
}
|
|
room:doCardEffect(effect_data)
|
|
if effect_data.isCancellOut and card.skill then
|
|
card.skill:onNullified(room, effect_data)
|
|
end
|
|
end
|
|
end,
|
|
[Player.Draw] = function()
|
|
local data = {
|
|
n = 2
|
|
}
|
|
room.logic:trigger(fk.DrawNCards, player, data)
|
|
room:drawCards(player, data.n, self.name)
|
|
room.logic:trigger(fk.AfterDrawNCards, player, data)
|
|
end,
|
|
[Player.Play] = function()
|
|
while not player.dead do
|
|
room:notifyMoveFocus(player, "PlayCard")
|
|
local result = room:doRequest(player, "PlayCard", player.id)
|
|
if result == "" then break end
|
|
|
|
local use = room:handleUseCardReply(player, result)
|
|
if use then
|
|
room:useCard(use)
|
|
end
|
|
end
|
|
end,
|
|
[Player.Discard] = function()
|
|
local discardNum = #player:getCardIds(Player.Hand) - player:getMaxCards()
|
|
if discardNum > 0 then
|
|
room:askForDiscard(player, discardNum, discardNum, false, self.name)
|
|
end
|
|
end,
|
|
[Player.Finish] = function()
|
|
|
|
end,
|
|
[Player.NotActive] = function()
|
|
|
|
end,
|
|
})
|
|
end,
|
|
[fk.EventPhaseEnd] = function()
|
|
if player.phase == Player.Play then
|
|
for _, p in ipairs(room.players) do
|
|
p:setCardUseHistory("", 0, Player.HistoryPhase)
|
|
p:setSkillUseHistory("", 0, Player.HistoryPhase)
|
|
for name, _ in pairs(p.mark) do
|
|
if name:endsWith("-phase") then
|
|
room:setPlayerMark(p, name, 0)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end,
|
|
[fk.EventPhaseChanging] = function()
|
|
-- TODO: copy but dont copy all
|
|
if data.to == Player.NotActive then
|
|
for _, p in ipairs(room.players) do
|
|
p:setCardUseHistory("", 0, Player.HistoryTurn)
|
|
p:setSkillUseHistory("", 0, Player.HistoryTurn)
|
|
for name, _ in pairs(p.mark) do
|
|
if name:endsWith("-turn") then
|
|
room:setPlayerMark(p, name, 0)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end,
|
|
[fk.AskForPeaches] = function()
|
|
local dyingPlayer = room:getPlayerById(data.who)
|
|
while dyingPlayer.hp < 1 do
|
|
local pattern = "peach"
|
|
if player == dyingPlayer then
|
|
pattern = pattern .. ",analeptic"
|
|
end
|
|
|
|
local peach_use = room:askForUseCard(player, "peach", pattern)
|
|
if not peach_use then break end
|
|
peach_use.tos = { {dyingPlayer.id} }
|
|
if peach_use.card.trueName == "analeptic" then
|
|
peach_use.extra_data = peach_use.extra_data or {}
|
|
peach_use.extra_data.analepticRecover = true
|
|
end
|
|
room:useCard(peach_use)
|
|
end
|
|
end,
|
|
[fk.AskForPeachesDone] = function()
|
|
if player.hp < 1 and not data.ignoreDeath then
|
|
---@type DeathStruct
|
|
local deathData = {
|
|
who = player.id,
|
|
damage = data.damage,
|
|
}
|
|
room:killPlayer(deathData)
|
|
end
|
|
end,
|
|
[fk.GameOverJudge] = function()
|
|
local winner = getWinner(player)
|
|
if winner ~= "" then
|
|
room:gameOver(winner)
|
|
return true
|
|
end
|
|
end,
|
|
[fk.BuryVictim] = function()
|
|
player:bury()
|
|
if room.tag["SkipNormalDeathProcess"] then
|
|
return false
|
|
end
|
|
local damage = data.damage
|
|
if damage and damage.from then
|
|
local killer = damage.from
|
|
rewardAndPunish(killer, player);
|
|
end
|
|
end,
|
|
default = function()
|
|
print("game_rule: Event=" .. event)
|
|
room:askForSkillInvoke(player, "rule")
|
|
end,
|
|
})
|
|
return false
|
|
end,
|
|
|
|
}
|