- 复活角色
- 将cancelable全改为默认true
- move私有牌堆的未知牌时不再显示错误
- 处理区牌增加大多数脚注
- 装备栏有宝物时压缩间距
- 使用虚拟牌时处理区有虚拟名字
- 带详细描述的选择框
- 武将一览界面显示技能语音、胜利语音、死亡语音
This commit is contained in:
notify 2023-06-10 02:18:51 +08:00 committed by GitHub
parent 04f1009075
commit 7f718503bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 390 additions and 57 deletions

View File

@ -17,19 +17,19 @@ ColumnLayout {
font.pixelSize: 20
font.bold: true
Layout.fillWidth: true
wrapMode: Text.WrapAnywhere
wrapMode: Text.WordWrap
}
Text {
text: qsTr(hint)
Layout.fillWidth: true
wrapMode: Text.WrapAnywhere
wrapMode: Text.WordWrap
}
Text {
text: qsTr("validator_hint")
Layout.fillWidth: true
wrapMode: Text.WrapAnywhere
wrapMode: Text.WordWrap
}
TextField {

View File

@ -12,7 +12,7 @@ ColumnLayout {
Text {
text: qsTr("help_text")
Layout.fillWidth: true
wrapMode: Text.WrapAnywhere
wrapMode: Text.WordWrap
}
RowLayout {
@ -54,7 +54,7 @@ ColumnLayout {
Text {
text: qsTr("key_help_text")
Layout.fillWidth: true
wrapMode: Text.WrapAnywhere
wrapMode: Text.WordWrap
textFormat: Text.RichText
onLinkActivated: Qt.openUrlExternally(link);
}

View File

@ -148,16 +148,42 @@ Item {
radius: 8
property string general: "caocao"
function addSkillAudio(skill) {
const skilldata = JSON.parse(Backend.callLuaFunction("GetSkillData", [skill]));
if (!skilldata) return;
const extension = skilldata.extension;
for (let i = 0; i < 999; i++) {
let fname = AppPath + "/packages/" + extension + "/audio/skill/" +
skill + (i !== 0 ? i.toString() : "") + ".mp3";
if (Backend.exists(fname)) {
audioModel.append({ name: skill, idx: i });
} else {
if (i > 0) break;
}
}
}
function updateGeneral() {
detailGeneralCard.name = general;
const data = JSON.parse(Backend.callLuaFunction("GetGeneralDetail", [general]));
generalText.clear();
audioModel.clear();
data.skill.forEach(t => {
generalText.append("<b>" + Backend.translate(t.name) + "</b>: " + t.description)
generalText.append("<b>" + Backend.translate(t.name) +
"</b>: " + t.description);
addSkillAudio(t.name);
});
data.related_skill.forEach(t => {
generalText.append("<font color=\"purple\"><b>" + Backend.translate(t.name) + "</b>: " + t.description + "</font>")
generalText.append("<font color=\"purple\"><b>" + Backend.translate(t.name) +
"</b>: " + t.description + "</font>");
addSkillAudio(t.name);
});
addSkillAudio(general + "_win_audio");
}
Flickable {
@ -190,6 +216,58 @@ Item {
textFormat: TextEdit.RichText
font.pixelSize: 16
}
Repeater {
model: ListModel {
id: audioModel
}
Button {
Layout.fillWidth: true
contentItem: ColumnLayout {
Text {
Layout.fillWidth: true
text: Backend.translate(name) + (idx ? " (" + idx.toString() + ")" : "")
font.bold: true
font.pixelSize: 14
}
Text {
Layout.fillWidth: true
text: Backend.translate("$" + name + (idx ? idx.toString() : ""))
wrapMode: Text.WordWrap
}
}
onClicked: {
const skilldata = JSON.parse(Backend.callLuaFunction("GetSkillData", [name]));
const extension = skilldata.extension;
Backend.playSound("./packages/" + extension +
"/audio/skill/" + name, idx);
}
}
}
Button {
Layout.fillWidth: true
contentItem: ColumnLayout {
Text {
Layout.fillWidth: true
text: Backend.translate("Death audio")
font.bold: true
font.pixelSize: 14
}
Text {
Layout.fillWidth: true
text: Backend.translate("~" + generalDetail.general)
wrapMode: Text.WordWrap
}
}
onClicked: {
const general = generalDetail.general
const extension = JSON.parse(Backend.callLuaFunction("GetGeneralData", [general])).extension;
Backend.playSound("./packages/" + extension + "/audio/death/" + general);
}
}
}
}
}

View File

@ -39,7 +39,7 @@ Item {
Text {
id: bulletin_info
width: parent.width
wrapMode: TextEdit.WrapAnywhere
wrapMode: TextEdit.WordWrap
textFormat: Text.MarkdownText
text: Backend.translate('Bulletin Info')
}

View File

@ -263,6 +263,53 @@ function setEmotion(id, emotion, isCardId) {
animation.start();
}
function setCardFootnote(id, footnote) {
let card;
roomScene.tableCards.forEach((v) => {
if (v.cid === id) {
card = v;
return;
}
});
if (!card) {
return;
}
card.footnote = footnote;
card.footnoteVisible = true;
}
callbacks["SetCardFootnote"] = (j) => {
const data = JSON.parse(j);
const id = data[0];
const note = data[1];
setCardFootnote(id, note);
}
function setCardVirtName(id, name) {
let card;
roomScene.tableCards.forEach((v) => {
if (v.cid === id) {
card = v;
return;
}
});
if (!card) {
return;
}
card.virt_name = name;
}
callbacks["SetCardVirtName"] = (j) => {
const data = JSON.parse(j);
const ids = data[0];
const note = data[1];
ids.forEach(id => setCardVirtName(id, note));
}
function changeHp(id, delta, losthp) {
const photo = getPhoto(id);
if (!photo) {
@ -754,6 +801,7 @@ callbacks["AskForChoice"] = (jsonData) => {
const choices = data[0];
const skill_name = data[1];
const prompt = data[2];
const detailed = data[3];
if (prompt === "") {
roomScene.promptText = Backend.translate("#AskForChoice")
.arg(Backend.translate(skill_name));
@ -761,7 +809,13 @@ callbacks["AskForChoice"] = (jsonData) => {
roomScene.promptText = processPrompt(prompt);
}
roomScene.state = "replying";
roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/ChoiceBox.qml");
let qmlSrc;
if (!detailed) {
qmlSrc = "../RoomElement/ChoiceBox.qml";
} else {
qmlSrc = "../RoomElement/DetailedChoiceBox.qml";
}
roomScene.popupBox.sourceComponent = Qt.createComponent(qmlSrc);
const box = roomScene.popupBox.item;
box.options = choices;
box.skill_name = skill_name;

View File

@ -13,9 +13,9 @@ import Fk.RoomElement
*/
Column {
height: 88
height: 70
width: 138
property int itemHeight: Math.floor(height / 4)
property int itemHeight: treasureItem.name === "" ? height / 3 : height / 4
property var items: [treasureItem, weaponItem, armorItem, defensiveHorseItem, offensiveHorseItem]
property var subtypes: ["treasure", "weapon", "armor", "defensive_horse", "offensive_horse"]
property int length: area.length
@ -29,7 +29,7 @@ Column {
EquipItem {
id: treasureItem
width: parent.width
height: itemHeight
height: name === "" ? 0 : itemHeight
opacity: 0
}

View File

@ -120,31 +120,44 @@ Item {
}
Rectangle {
id: virt_rect
visible: root.virt_name !== ""
width: parent.width
height: 14
anchors.verticalCenter: parent.verticalCenter
Text {
anchors.centerIn: parent
text: Backend.translate(root.virt_name)
}
height: 20
y: 40
color: "snow"
opacity: 0.8
radius: 4
border.color: "black"
border.width: 1
}
GlowText {
Text {
visible: virt_rect.visible
anchors.centerIn: virt_rect
font.pixelSize: 16
font.family: fontLibian.name
font.letterSpacing: -0.6
text: Backend.translate(root.virt_name)
}
Text {
id: footnoteItem
text: footnote
x: 6
y: parent.height - height - 6
x: 0
y: parent.height - height - 10
width: root.width - x * 2
color: "#E4D5A0"
// color: "white"
visible: footnoteVisible
style: Text.Outline
wrapMode: Text.WrapAnywhere
horizontalAlignment: Text.AlignHCenter
font.family: fontLibian.name
font.pixelSize: 14
glow.color: "black"
glow.spread: 1
glow.radius: 1
// glow.color: "black"
// glow.spread: 1
// glow.radius: 1
//glow.samples: 12
}

View File

@ -0,0 +1,67 @@
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Fk.Pages
GraphicsBox {
property var options: []
property string skill_name: ""
property int result
id: root
title.text: Backend.translate("$Choice").arg(Backend.translate(skill_name))
width: Math.max(140, body.width + 20)
height: body.height + title.height + 20
ListView {
id: body
x: 10
y: title.height + 5
width: Math.min(700, 220 * model.length)
height: 300
orientation: ListView.Horizontal
clip: true
spacing: 20
model: options
delegate: Item {
width: 200
height: 290
MetroButton {
id: choicetitle
width: parent.width
text: Backend.translate(modelData)
textFont.pixelSize: 24
anchors.top: choiceDetail.bottom
anchors.topMargin: 8
onClicked: {
result = index;
root.close();
}
}
Flickable {
id: choiceDetail
x: 4
height: parent.height - choicetitle.height
contentHeight: detail.height
width: parent.width
clip: true
Text {
id: detail
width: parent.width
text: Backend.translate(":" + modelData)
color: "white"
wrapMode: Text.WordWrap
font.pixelSize: 16
textFormat: TextEdit.RichText
}
}
}
}
}

View File

@ -45,7 +45,7 @@ GraphicsBox {
font.family: fontLibian.name
font.pixelSize: 18
style: Text.Outline
wrapMode: Text.WrapAnywhere
wrapMode: Text.WordWrap
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}

View File

@ -130,7 +130,7 @@ Item {
lineHeightMode: Text.FixedHeight
color: "white"
width: 24
wrapMode: Text.WordWrap
wrapMode: Text.WrapAnywhere
text: ""
}
@ -205,7 +205,7 @@ Item {
lineHeightMode: Text.FixedHeight
color: "white"
width: 24
wrapMode: Text.WordWrap
wrapMode: Text.WrapAnywhere
text: Backend.translate(deputyGeneral)
style: Text.Outline
}
@ -278,7 +278,7 @@ Item {
id: equipAreaItem
x: 31
y: 139
y: 157
}
Item {

View File

@ -66,7 +66,7 @@ Item {
c.selectable = true;
c.height = c.height * 0.8;
c.width = c.width * 0.8;
c.rotation = (Math.random() - 0.5) * 5;
// c.rotation = (Math.random() - 0.5) * 5;
}
}
@ -81,7 +81,7 @@ Item {
c.selectable = false;
c.height = c.height / 0.8;
c.width = c.width / 0.8;
c.rotation = 0;
// c.rotation = 0;
}
const vanished = [];
if (result.length < outputs.length) {

View File

@ -117,9 +117,11 @@ function Client:moveCards(moves)
end
---@param msg LogMessage
function Client:appendLog(msg)
local function parseMsg(msg, nocolor)
local self = ClientInstance
local data = msg
local function getPlayerStr(pid, color)
if nocolor then color = "white" end
if not pid then
return ""
end
@ -177,7 +179,7 @@ function Client:appendLog(msg)
local function parseArg(arg)
arg = arg or ""
arg = Fk:translate(arg)
arg = string.format('<font color="#0598BC"><b>%s</b></font>', arg)
arg = string.format('<font color="%s"><b>%s</b></font>', nocolor and "white" or "#0598BC", arg)
return arg
end
@ -192,7 +194,26 @@ function Client:appendLog(msg)
log = string.gsub(log, "%%arg2", arg2)
log = string.gsub(log, "%%arg3", arg3)
log = string.gsub(log, "%%arg", arg)
self:notifyUI("GameLog", log)
return log
end
---@param msg LogMessage
function Client:appendLog(msg)
self:notifyUI("GameLog", parseMsg(msg))
end
---@param msg LogMessage
function Client:setCardNote(ids, msg)
for _, id in ipairs(ids) do
if id ~= -1 then
self:notifyUI("SetCardFootnote", json.encode{ id, parseMsg(msg, true) })
end
end
end
fk.client_callback["SetCardFootnote"] = function(jsonData)
local data = json.decode(jsonData)
ClientInstance:setCardNote(data[1], data[2]);
end
fk.client_callback["Setup"] = function(jsonData)
@ -412,28 +433,33 @@ local function mergeMoves(moves)
end
local function sendMoveCardLog(move)
local client = ClientInstance
if #move.ids == 0 then return end
local hidden = table.contains(move.ids, -1)
local msgtype
if move.from and move.toArea == Card.DrawPile then
msgtype = hidden and "$PutCard" or "$PutKnownCard"
ClientInstance:appendLog{
client:appendLog{
type = msgtype,
from = move.from,
card = move.ids,
arg = #move.ids,
}
client:setCardNote(move.ids, {
type = "$$PutCard",
from = move.from,
})
elseif move.toArea == Card.PlayerSpecial then
msgtype = hidden and "$RemoveCardFromGame" or "$AddToPile"
ClientInstance:appendLog{
client:appendLog{
type = msgtype,
arg = move.specialName,
arg2 = #move.ids,
card = move.ids,
}
elseif move.fromArea == Card.PlayerSpecial and move.to then
ClientInstance:appendLog{
client:appendLog{
type = "$GetCardsFromPile",
from = move.to,
arg = move.fromSpecialName,
@ -441,7 +467,7 @@ local function sendMoveCardLog(move)
card = move.ids,
}
elseif move.moveReason == fk.ReasonDraw then
ClientInstance:appendLog{
client:appendLog{
type = "$DrawCards",
from = move.to,
card = move.ids,
@ -449,7 +475,7 @@ local function sendMoveCardLog(move)
}
elseif (move.fromArea == Card.DrawPile or move.fromArea == Card.DiscardPile)
and move.moveReason == fk.ReasonPrey then
ClientInstance:appendLog{
client:appendLog{
type = "$PreyCardsFromPile",
from = move.to,
card = move.ids,
@ -457,14 +483,14 @@ local function sendMoveCardLog(move)
}
elseif (move.fromArea == Card.Processing or move.fromArea == Card.PlayerJudge)
and move.toArea == Card.PlayerHand then
ClientInstance:appendLog{
client:appendLog{
type = "$GotCardBack",
from = move.to,
card = move.ids,
arg = #move.ids,
}
elseif move.fromArea == Card.DiscardPile and move.toArea == Card.PlayerHand then
ClientInstance:appendLog{
client:appendLog{
type = "$RecycleCard",
from = move.to,
card = move.ids,
@ -472,7 +498,7 @@ local function sendMoveCardLog(move)
}
elseif move.from and move.fromArea ~= Card.PlayerJudge and
move.toArea ~= Card.PlayerJudge and move.to and move.from ~= move.to then
ClientInstance:appendLog{
client:appendLog{
type = "$MoveCards",
from = move.from,
to = { move.to },
@ -486,7 +512,7 @@ local function sendMoveCardLog(move)
msgtype = "$PasteCard"
end
if msgtype then
ClientInstance:appendLog{
client:appendLog{
type = msgtype,
from = move.from,
to = { move.to },
@ -497,12 +523,16 @@ local function sendMoveCardLog(move)
-- TODO ...
if move.moveReason == fk.ReasonDiscard then
ClientInstance:appendLog{
client:appendLog{
type = "$DiscardCards",
from = move.from,
card = move.ids,
arg = #move.ids,
}
client:setCardNote(move.ids, {
type = "$$DiscardCards",
from = move.from
})
end
end
@ -512,10 +542,10 @@ fk.client_callback["MoveCards"] = function(jsonData)
local separated = separateMoves(raw_moves)
ClientInstance:moveCards(separated)
local merged = mergeMoves(separated)
ClientInstance:notifyUI("MoveCards", json.encode(merged))
for _, move in ipairs(merged) do
sendMoveCardLog(move)
end
ClientInstance:notifyUI("MoveCards", json.encode(merged))
end
fk.client_callback["ShowCard"] = function(jsonData)

View File

@ -130,6 +130,7 @@ FreeKill使用的是libgit2的C API与此同时使用Git完成拓展包的下
["Quit"] = "退出",
["BanGeneral"] = "禁将",
["ResumeGeneral"] = "解禁",
["Death audio"] = "阵亡",
["$WelcomeToLobby"] = "欢迎进入新月杀游戏大厅!",
@ -350,9 +351,22 @@ Fk:loadTranslationTable{
["#EnterDying"] = "%from 进入了濒死阶段",
["#KillPlayer"] = "%from [%arg] 阵亡,凶手是 %to",
["#KillPlayerWithNoKiller"] = "%from [%arg] 阵亡,无伤害来源",
["#Revive"] = "%from 竟然复活了",
-- misc
["#GuanxingResult"] = "%from 的观星结果为 %arg 上 %arg2 下",
["#ChainStateChange"] = "%from %arg 了武将牌",
["#ChainDamage"] = "%from 处于连环状态,将受到传导的伤害",
}
-- card footnote
Fk:loadTranslationTable{
["$$DiscardCards"] = "%from弃置",
["$$PutCard"] = "%from置于",
["##UseCard"] = "%from使用",
["##UseCardTo"] = "%from对%to",
["##ResponsePlayCard"] = "%from打出",
["##ShowCard"] = "%from展示",
["##JudgeCard"] = "%arg判定",
}

View File

@ -244,7 +244,14 @@ function Player:removeCards(playerArea, cardIds, specialName)
break
end
if table.contains(fromAreaIds, id) then
table.removeOne(fromAreaIds, id)
-- FIXME: 为客户端移动id为-1的牌考虑但总感觉有地方需要商讨啊
elseif table.every(fromAreaIds, function(e) return e == -1 end) then
table.remove(fromAreaIds, 1)
elseif id == -1 then
table.remove(fromAreaIds, 1)
end
end
end
end

View File

@ -21,6 +21,10 @@ GameEvent.functions[GameEvent.Judge] = function(self)
card = {data.card.id},
}
self:moveCardTo(data.card, Card.Processing, nil, fk.ReasonJudge)
self:sendFootnote({ data.card.id }, {
type = "##JudgeCard",
arg = data.reason,
})
self.logic:trigger(fk.AskForRetrial, who, data)
self.logic:trigger(fk.FinishRetrial, who, data)
@ -30,6 +34,10 @@ GameEvent.functions[GameEvent.Judge] = function(self)
from = who.id,
card = {data.card.id},
}
self:sendFootnote({ data.card.id }, {
type = "##JudgeCard",
arg = data.reason,
})
if data.pattern then
self:delay(400);

View File

@ -154,6 +154,27 @@ local sendCardEmotionAndLog = function(room, cardUseEvent)
}
end
end
if #useCardIds == 0 then return end
if cardUseEvent.tos and #cardUseEvent.tos > 0 and #cardUseEvent.tos <= 2 then
local tos = table.map(cardUseEvent.tos, function(e) return e[1] end)
room:sendFootnote(useCardIds, {
type = "##UseCardTo",
from = from,
to = tos,
})
if card:isVirtual() then
room:sendCardVirtName(useCardIds, card.name)
end
else
room:sendFootnote(useCardIds, {
type = "##UseCard",
from = from,
})
if card:isVirtual() then
room:sendCardVirtName(useCardIds, card.name)
end
end
end
---@param self GameEvent
@ -250,6 +271,15 @@ GameEvent.functions[GameEvent.RespondCard] = function(self)
toArea = Card.Processing,
moveReason = fk.ReasonResonpse,
})
if #cardIds > 0 then
self:sendFootnote(cardIds, {
type = "##ResponsePlayCard",
from = from,
})
if card:isVirtual() then
self:sendCardVirtName(cardIds, card.name)
end
end
if self.logic:trigger(fk.PreCardRespond, self:getPlayerById(cardResponseEvent.from), cardResponseEvent) then
self.logic:breakEvent()

View File

@ -419,6 +419,11 @@ function Room:removeCardMark(card, mark, count)
self:setCardMark(card, mark, math.max(num - count, 0))
end
---@param player ServerPlayer
function Room:setPlayerProperty(player, property, value)
player[property] = value
self:broadcastProperty(player, property)
end
--- 将房间中某个tag设为特定值。
---
@ -742,6 +747,14 @@ function Room:sendLog(log)
self:doBroadcastNotify("GameLog", json.encode(log))
end
function Room:sendFootnote(ids, log)
self:doBroadcastNotify("SetCardFootnote", json.encode{ ids, log })
end
function Room:sendCardVirtName(ids, name)
self:doBroadcastNotify("SetCardVirtName", json.encode{ ids, name })
end
--- 播放某种动画效果给players看。
---@param type string @ 动画名字
---@param data any @ 这个动画附加的额外信息在这个函数将会被转成json字符串
@ -868,7 +881,7 @@ end
---@return boolean, table
function Room:askForUseActiveSkill(player, skill_name, prompt, cancelable, extra_data)
prompt = prompt or ""
cancelable = cancelable or false
cancelable = (cancelable == nil) and true or cancelable
extra_data = extra_data or {}
local skill = Fk.skills[skill_name]
if not (skill and (skill:isInstanceOf(ActiveSkill) or skill:isInstanceOf(ViewAsSkill))) then
@ -929,7 +942,7 @@ Room.askForUseViewAsSkill = Room.askForUseActiveSkill
---@param skipDiscard boolean @ 是否跳过弃牌(即只询问选择可以弃置的牌)
---@return integer[] @ 弃掉的牌的id列表可能是空的
function Room:askForDiscard(player, minNum, maxNum, includeEquip, skillName, cancelable, pattern, prompt, skipDiscard)
cancelable = cancelable or false
cancelable = (cancelable == nil) and true or cancelable
pattern = pattern or ""
local canDiscards = table.filter(
@ -1004,7 +1017,7 @@ function Room:askForChoosePlayers(player, targets, minNum, maxNum, prompt, skill
if maxNum < 1 then
return {}
end
cancelable = cancelable or false
cancelable = (cancelable == nil) and true or cancelable
local data = {
targets = targets,
@ -1042,7 +1055,7 @@ function Room:askForCard(player, minNum, maxNum, includeEquip, skillName, cancel
if minNum < 1 then
return nil
end
cancelable = cancelable or false
cancelable = (cancelable == nil) and true or cancelable
pattern = pattern or ""
local chosenCards = {}
@ -1089,7 +1102,7 @@ function Room:askForChooseCardAndPlayers(player, targets, minNum, maxNum, patter
if maxNum < 1 then
return {}
end
cancelable = cancelable or false
cancelable = (cancelable == nil) and true or cancelable
pattern = pattern or "."
local pcards = table.filter(player:getCardIds({ Player.Hand, Player.Equip }), function(id)
@ -1224,15 +1237,15 @@ end
---@param choices string[] @ 可选选项列表
---@param skill_name string @ 技能名
---@param prompt string @ 提示信息
---@param data any @ 暂未使用
---@param detailed boolean @ 暂未使用
---@return string @ 选择的选项
function Room:askForChoice(player, choices, skill_name, prompt, data)
function Room:askForChoice(player, choices, skill_name, prompt, detailed)
if #choices == 1 then return choices[1] end
local command = "AskForChoice"
prompt = prompt or ""
self:notifyMoveFocus(player, skill_name)
local result = self:doRequest(player, command, json.encode{
choices, skill_name, prompt
choices, skill_name, prompt, detailed
})
if result == "" then result = choices[1] end
return result
@ -1459,7 +1472,7 @@ function Room:askForUseCard(player, card_name, pattern, prompt, cancelable, extr
local command = "AskForUseCard"
self:notifyMoveFocus(player, card_name)
cancelable = cancelable or false
cancelable = (cancelable == nil) and true or cancelable
extra_data = extra_data or {}
pattern = pattern or card_name
prompt = prompt or ""
@ -1505,7 +1518,7 @@ function Room:askForResponse(player, card_name, pattern, prompt, cancelable, ext
local command = "AskForResponseCard"
self:notifyMoveFocus(player, card_name)
cancelable = cancelable or false
cancelable = (cancelable == nil) and true or cancelable
extra_data = extra_data or {}
pattern = pattern or card_name
prompt = prompt or ""
@ -1554,7 +1567,7 @@ function Room:askForNullification(players, card_name, pattern, prompt, cancelabl
local command = "AskForUseCard"
card_name = card_name or "nullification"
cancelable = cancelable or false
cancelable = (cancelable == nil) and true or cancelable
extra_data = extra_data or {}
prompt = prompt or ""
pattern = pattern or card_name
@ -1750,7 +1763,7 @@ function Room:askForChooseToMoveCardInBoard(player, prompt, skillName, cancelabl
if flag then
assert(flag == "e" or flag == "j")
end
cancelable = (not cancelable) and false or true
cancelable = (cancelable == nil) and true or cancelable
local data = {
flag = flag,
@ -2694,6 +2707,21 @@ function Room:useSkill(player, skill, effect_cb)
end
end
---@param player ServerPlayer
---@param sendLog boolean|nil
function Room:revivePlayer(player, sendLog)
if not player.dead then return end
self:setPlayerProperty(player, "dead", false)
self:setPlayerProperty(player, "dying", false)
self:setPlayerProperty(player, "hp", player.maxHp)
table.insertIfNeed(self.alive_players, player)
sendLog = (sendLog == nil) and true or sendLog
if sendLog then
self:sendLog { type = "#Revive", from = player.id }
end
end
---@param room Room
local function shouldUpdateWinRate(room)
if room.settings.gameMode == "heg_mode" then return false end

View File

@ -351,6 +351,10 @@ function ServerPlayer:showCards(cards)
from = self.id,
cards = cards,
})
room:sendFootnote(cards, {
type = "##ShowCard",
from = self.id,
})
room.logic:trigger(fk.CardShown, self, { cardIds = cards })
end