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