diff --git a/Fk/ModMaker/CreateSomething.qml b/Fk/ModMaker/CreateSomething.qml
index ff2fb692..d3cc7485 100644
--- a/Fk/ModMaker/CreateSomething.qml
+++ b/Fk/ModMaker/CreateSomething.qml
@@ -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 {
diff --git a/Fk/ModMaker/UserInfo.qml b/Fk/ModMaker/UserInfo.qml
index 3bc9bb32..2c91b5c0 100644
--- a/Fk/ModMaker/UserInfo.qml
+++ b/Fk/ModMaker/UserInfo.qml
@@ -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);
}
diff --git a/Fk/Pages/GeneralsOverview.qml b/Fk/Pages/GeneralsOverview.qml
index e49b0374..c9596b89 100644
--- a/Fk/Pages/GeneralsOverview.qml
+++ b/Fk/Pages/GeneralsOverview.qml
@@ -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("" + Backend.translate(t.name) + ": " + t.description)
+ generalText.append("" + Backend.translate(t.name) +
+ ": " + t.description);
+
+ addSkillAudio(t.name);
});
data.related_skill.forEach(t => {
- generalText.append("" + Backend.translate(t.name) + ": " + t.description + "")
+ generalText.append("" + Backend.translate(t.name) +
+ ": " + t.description + "");
+
+ 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);
+ }
+ }
}
}
}
diff --git a/Fk/Pages/Lobby.qml b/Fk/Pages/Lobby.qml
index 3769753c..47900f7a 100644
--- a/Fk/Pages/Lobby.qml
+++ b/Fk/Pages/Lobby.qml
@@ -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')
}
diff --git a/Fk/Pages/RoomLogic.js b/Fk/Pages/RoomLogic.js
index 7e46caa6..33407993 100644
--- a/Fk/Pages/RoomLogic.js
+++ b/Fk/Pages/RoomLogic.js
@@ -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;
diff --git a/Fk/PhotoElement/EquipArea.qml b/Fk/PhotoElement/EquipArea.qml
index ed563756..de0bcd6f 100644
--- a/Fk/PhotoElement/EquipArea.qml
+++ b/Fk/PhotoElement/EquipArea.qml
@@ -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
}
diff --git a/Fk/RoomElement/CardItem.qml b/Fk/RoomElement/CardItem.qml
index 2a6fc2a6..0d15f870 100644
--- a/Fk/RoomElement/CardItem.qml
+++ b/Fk/RoomElement/CardItem.qml
@@ -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
}
diff --git a/Fk/RoomElement/DetailedChoiceBox.qml b/Fk/RoomElement/DetailedChoiceBox.qml
new file mode 100644
index 00000000..e2628585
--- /dev/null
+++ b/Fk/RoomElement/DetailedChoiceBox.qml
@@ -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
+ }
+ }
+ }
+ }
+}
diff --git a/Fk/RoomElement/MoveCardInBoardBox.qml b/Fk/RoomElement/MoveCardInBoardBox.qml
index 7b82f423..dac68c97 100644
--- a/Fk/RoomElement/MoveCardInBoardBox.qml
+++ b/Fk/RoomElement/MoveCardInBoardBox.qml
@@ -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
}
diff --git a/Fk/RoomElement/Photo.qml b/Fk/RoomElement/Photo.qml
index 318701bd..c054aedc 100644
--- a/Fk/RoomElement/Photo.qml
+++ b/Fk/RoomElement/Photo.qml
@@ -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 {
diff --git a/Fk/RoomElement/TablePile.qml b/Fk/RoomElement/TablePile.qml
index f7f50e2e..cd5a7a23 100644
--- a/Fk/RoomElement/TablePile.qml
+++ b/Fk/RoomElement/TablePile.qml
@@ -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) {
diff --git a/lua/client/client.lua b/lua/client/client.lua
index 43f0184c..e90f2086 100644
--- a/lua/client/client.lua
+++ b/lua/client/client.lua
@@ -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('%s', arg)
+ arg = string.format('%s', 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)
diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua
index 22959182..c4eabae0 100644
--- a/lua/client/i18n/zh_CN.lua
+++ b/lua/client/i18n/zh_CN.lua
@@ -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判定",
+}
diff --git a/lua/core/player.lua b/lua/core/player.lua
index 73bb9b6c..71127743 100644
--- a/lua/core/player.lua
+++ b/lua/core/player.lua
@@ -244,7 +244,14 @@ function Player:removeCards(playerArea, cardIds, specialName)
break
end
- table.removeOne(fromAreaIds, id)
+ 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
diff --git a/lua/server/events/judge.lua b/lua/server/events/judge.lua
index 4d81130f..d1d49958 100644
--- a/lua/server/events/judge.lua
+++ b/lua/server/events/judge.lua
@@ -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);
diff --git a/lua/server/events/usecard.lua b/lua/server/events/usecard.lua
index d7045f29..a64ded72 100644
--- a/lua/server/events/usecard.lua
+++ b/lua/server/events/usecard.lua
@@ -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()
diff --git a/lua/server/room.lua b/lua/server/room.lua
index 0978ed36..7df048b6 100644
--- a/lua/server/room.lua
+++ b/lua/server/room.lua
@@ -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
diff --git a/lua/server/serverplayer.lua b/lua/server/serverplayer.lua
index 35667d60..b5c74599 100644
--- a/lua/server/serverplayer.lua
+++ b/lua/server/serverplayer.lua
@@ -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