diff --git a/Fk/Pages/RoomLogic.js b/Fk/Pages/RoomLogic.js index aaebc51b..3f7a789e 100644 --- a/Fk/Pages/RoomLogic.js +++ b/Fk/Pages/RoomLogic.js @@ -1575,6 +1575,25 @@ callbacks["CustomDialog"] = (j) => { } } +callbacks["MiniGame"] = (j) => { + const data = JSON.parse(j); + const game = data.type; + const dat = data.data; + const gdata = JSON.parse(Backend.callLuaFunction("GetMiniGame", [game, Self.id, JSON.stringify(dat)])); + roomScene.state = "replying"; + roomScene.popupBox.source = AppPath + "/" + gdata.qml_path + ".qml"; + if (dat) { + roomScene.popupBox.item.loadData(dat); + } +} + +callbacks["UpdateMiniGame"] = (j) => { + const data = JSON.parse(j); + if (roomScene.popupBox.item) { + roomScene.popupBox.item.updateData(data); + } +} + callbacks["UpdateLimitSkill"] = (j) => { const data = JSON.parse(j); const id = data[0]; diff --git a/lua/client/client.lua b/lua/client/client.lua index c4352732..b057794e 100644 --- a/lua/client/client.lua +++ b/lua/client/client.lua @@ -509,7 +509,91 @@ local function sendMoveCardLog(move) local hidden = table.contains(move.ids, -1) local msgtype - if move.from and move.toArea == Card.DrawPile then + if move.toArea == Card.PlayerHand then + if move.fromArea == Card.PlayerSpecial then + client:appendLog{ + type = "$GetCardsFromPile", + from = move.to, + arg = move.fromSpecialName, + arg2 = #move.ids, + card = move.ids, + } + elseif move.fromArea == Card.DrawPile then + client:appendLog{ + type = "$DrawCards", + from = move.to, + card = move.ids, + arg = #move.ids, + } + elseif move.fromArea == Card.Processing then + client:appendLog{ + type = "$GotCardBack", + from = move.to, + card = move.ids, + arg = #move.ids, + } + elseif move.fromArea == Card.DiscardPile then + client:appendLog{ + type = "$RecycleCard", + from = move.to, + card = move.ids, + arg = #move.ids, + } + elseif move.from then + client:appendLog{ + type = "$MoveCards", + from = move.from, + to = { move.to }, + arg = #move.ids, + card = move.ids, + } + else + client:appendLog{ + type = "$PreyCardsFromPile", + from = move.to, + card = move.ids, + arg = #move.ids, + } + end + elseif move.toArea == Card.PlayerEquip then + client:appendLog{ + type = "$InstallEquip", + from = move.to, + card = move.ids, + } + elseif move.toArea == Card.PlayerJudge then + if move.from ~= move.to and move.fromArea == Card.PlayerJudge then + client:appendLog{ + type = "$LightningMove", + from = move.from, + to = { move.to }, + card = move.ids, + } + elseif move.from then + client:appendLog{ + type = "$PasteCard", + from = move.from, + to = { move.to }, + card = move.ids, + } + end + elseif move.toArea == Card.PlayerSpecial then + client:appendLog{ + type = "$AddToPile", + arg = move.specialName, + arg2 = #move.ids, + from = move.to, + card = move.ids, + } + elseif move.fromArea == Card.PlayerEquip then + client:appendLog{ + type = "$UninstallEquip", + from = move.from, + card = move.ids, + } + -- elseif move.toArea == Card.Processing then + -- nop + elseif move.from and move.toArea == Card.DrawPile then msgtype = hidden and "$PutCard" or "$PutKnownCard" client:appendLog{ type = msgtype, @@ -521,85 +605,27 @@ local function sendMoveCardLog(move) type = "$$PutCard", from = move.from, }) - elseif move.toArea == Card.PlayerSpecial then - msgtype = hidden and "$RemoveCardFromGame" or "$AddToPile" - client:appendLog{ - type = msgtype, - arg = move.specialName, - arg2 = #move.ids, - card = move.ids, - } - elseif move.fromArea == Card.PlayerSpecial and move.to then - client:appendLog{ - type = "$GetCardsFromPile", - from = move.to, - arg = move.fromSpecialName, - arg2 = #move.ids, - card = move.ids, - } - elseif move.moveReason == fk.ReasonDraw then - client:appendLog{ - type = "$DrawCards", - from = move.to, - card = move.ids, - arg = #move.ids, - } - elseif (move.fromArea == Card.DrawPile or move.fromArea == Card.DiscardPile) - and move.moveReason == fk.ReasonPrey then - client:appendLog{ - type = "$PreyCardsFromPile", - from = move.to, - card = move.ids, - arg = #move.ids, - } - elseif (move.fromArea == Card.Processing or move.fromArea == Card.PlayerJudge) - and move.toArea == Card.PlayerHand then - client:appendLog{ - type = "$GotCardBack", - from = move.to, - card = move.ids, - arg = #move.ids, - } - elseif move.fromArea == Card.DiscardPile and move.toArea == Card.PlayerHand then - client:appendLog{ - type = "$RecycleCard", - from = move.to, - card = move.ids, - arg = #move.ids, - } - elseif move.from and move.fromArea ~= Card.PlayerJudge and - move.toArea ~= Card.PlayerJudge and move.to and move.from ~= move.to then - client:appendLog{ - type = "$MoveCards", - from = move.from, - to = { move.to }, - arg = #move.ids, - card = move.ids, - } - elseif move.from and move.to and move.toArea == Card.PlayerJudge then - if move.fromArea == Card.PlayerJudge and move.from ~= move.to then - msgtype = "$LightningMove" - elseif move.fromArea ~= Card.PlayerJudge then - msgtype = "$PasteCard" - end - if msgtype then + elseif move.toArea == Card.DiscardPile then + if move.moveReason == fk.ReasonDiscard then client:appendLog{ - type = msgtype, + type = "$DiscardCards", from = move.from, - to = { move.to }, card = move.ids, + arg = #move.ids, + } + elseif move.moveReason == fk.ReasonPutIntoDiscardPile then + client:appendLog{ + type = "$PutToDiscard", + card = move.ids, + arg = #move.ids, } end + -- elseif move.toArea == Card.Void then + -- nop end - -- TODO ... + -- TODO: footnote if move.moveReason == fk.ReasonDiscard then - client:appendLog{ - type = "$DiscardCards", - from = move.from, - card = move.ids, - arg = #move.ids, - } client:setCardNote(move.ids, { type = "$$DiscardCards", from = move.from diff --git a/lua/client/client_util.lua b/lua/client/client_util.lua index 7b0bd104..07809821 100644 --- a/lua/client/client_util.lua +++ b/lua/client/client_util.lua @@ -748,4 +748,13 @@ function GetQmlMark(mtype, name, value, p) } end +function GetMiniGame(gtype, p, data) + local spec = Fk.mini_games[gtype] + p = ClientInstance:getPlayerById(p) + data = json.decode(data) + return json.encode { + qml_path = type(spec.qml_path) == "function" and spec.qml_path(p, data) or spec.qml_path, + } +end + dofile "lua/client/i18n/init.lua" diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index 02ef90f3..804a9edf 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -386,22 +386,26 @@ Fk:loadTranslationTable{ ["#LoseSkill"] = "%from 失去了技能 “%arg”", -- moveCards (they are sent by notifyMoveCards) - ["$PutCard"] = "%from 的 %arg 张牌被置于牌堆", - ["$PutKnownCard"] = "%from 的牌 %card 被置于牌堆", - ["$RemoveCardFromGame"] = "%arg2 张牌被作为 %arg 移出游戏", - ["$AddToPile"] = "%card 被作为 %arg 移出游戏", ["$GetCardsFromPile"] = "%from 从 %arg 中获得了 %arg2 张牌 %card", ["$DrawCards"] = "%from 摸了 %arg 张牌 %card", + ["$MoveCards"] = "%to 从 %from 处获得了 %arg 张牌 %card", ["$PreyCardsFromPile"] = "%from 获得了 %arg 张牌 %card", ["$GotCardBack"] = "%from 收回了 %arg 张牌 %card", ["$RecycleCard"] = "%from 从弃牌堆回收了 %arg 张牌 %card", - ["$MoveCards"] = "%to 从 %from 处获得了 %arg 张牌 %card", - ["$LightningMove"] = "%card 从 %from 转移到了 %to", - ["$PasteCard"] = "%from 给 %to 贴了张 %card", - ["$DiscardCards"] = "%from 弃置了 %arg 张牌 %card", + ["$InstallEquip"] = "%from 装备了 %card", ["$UninstallEquip"] = "%from 卸载了 %card", + ["$LightningMove"] = "%card 从 %from 转移到了 %to", + ["$PasteCard"] = "%from 给 %to 贴了张 %card", + + ["$AddToPile"] = "%arg2 张牌 %card 被作为 %from 的 %arg 移出游戏", + + ["$PutCard"] = "%from 的 %arg 张牌被置于牌堆", + ["$PutKnownCard"] = "%from 的牌 %card 被置于牌堆", + ["$DiscardCards"] = "%from 弃置了 %arg 张牌 %card", + ["$PutToDiscard"] = "%arg 张牌 %card 被置入弃牌堆", + ["#ShowCard"] = "%from 展示了牌 %card", ["#Recast"] = "%from 重铸了 %card", ["#RecastBySkill"] = "%from 发动了 “%arg” 重铸了 %card", diff --git a/lua/core/engine.lua b/lua/core/engine.lua index 00d3bf93..f451c208 100644 --- a/lua/core/engine.lua +++ b/lua/core/engine.lua @@ -28,6 +28,7 @@ ---@field private _custom_events any[] @ 自定义事件列表 ---@field public poxi_methods table @ “魄袭”框操作方法表 ---@field public qml_marks table @ 自定义Qml标记的表 +---@field public mini_games table @ 自定义多人交互表 local Engine = class("Engine") --- Engine的构造函数。 @@ -61,6 +62,7 @@ function Engine:initialize() self._custom_events = {} self.poxi_methods = {} self.qml_marks = {} + self.mini_games = {} self:loadPackages() self:loadDisabled() @@ -356,6 +358,7 @@ function Engine:addPoxiMethod(spec) spec.post_select = spec.post_select or function(s) return s end end +---@param spec QmlMarkSpec function Engine:addQmlMark(spec) assert(type(spec.name) == "string") if self.qml_marks[spec.name] then @@ -364,6 +367,15 @@ function Engine:addQmlMark(spec) self.qml_marks[spec.name] = spec end +---@param spec MiniGameSpec +function Engine:addMiniGame(spec) + assert(type(spec.name) == "string") + if self.mini_games[spec.name] then + fk.qCritical("Warning: duplicated mini game type " .. spec.name) + end + self.mini_games[spec.name] = spec +end + --- 从已经开启的拓展包中,随机选出若干名武将。 --- --- 对于同名武将不会重复选取。 diff --git a/lua/fk_ex.lua b/lua/fk_ex.lua index 8d94de46..b352f004 100644 --- a/lua/fk_ex.lua +++ b/lua/fk_ex.lua @@ -608,3 +608,10 @@ end ---@field name string ---@field qml_path string | fun(name: string, value?: any, player?: Player): string ---@field how_to_show fun(name: string, value?: any, player?: Player): string? + +-- TODO: 断连 不操作的人观看 现在只做了专为22设计的框 +---@class MiniGameSpec +---@field name string +---@field qml_path string | fun(player: Player, data: any): string +---@field update_func? fun(player: ServerPlayer, data: any) +---@field default_choice? fun(player: ServerPlayer, data: any): any diff --git a/lua/server/gamelogic.lua b/lua/server/gamelogic.lua index 0f8f184c..54fdc624 100644 --- a/lua/server/gamelogic.lua +++ b/lua/server/gamelogic.lua @@ -439,6 +439,10 @@ function GameLogic:start() e = self:getCurrentCleaner() end + if not e then -- 没有事件,按理说不应该,平局处理 + self.room:gameOver("") + end + -- ret, evt解释: -- * true, nil: 中止 -- * false, nil: 正常结束 diff --git a/lua/server/request.lua b/lua/server/request.lua index 329aa148..79a0a70e 100644 --- a/lua/server/request.lua +++ b/lua/server/request.lua @@ -182,6 +182,18 @@ request_handlers["surrender"] = function(room, id, reqlist) end end +request_handlers["updatemini"] = function(room, pid, reqlist) + local player = room:getPlayerById(pid) + local data = player.mini_game_data + if not data then return end + local game = Fk.mini_games[data.type] + if not (game and game.update_func) then return end + local dat = table.simpleClone(reqlist) + table.remove(dat, 1) + table.remove(dat, 1) + game.update_func(player, dat) +end + request_handlers["newroom"] = function(s, id) s:registerRoom(id) end diff --git a/lua/server/room.lua b/lua/server/room.lua index 9ca4496c..6a4fdf94 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -2214,6 +2214,34 @@ function Room:closeAG(player) else self:doBroadcastNotify("CloseAG", "") end end +-- TODO: 重构request机制,不然这个还得手动拿client_reply +---@param players ServerPlayer[] +---@param focus string +---@param game_type string +---@param data_table table @ 对应每个player +function Room:askForMiniGame(players, focus, game_type, data_table) + local command = "MiniGame" + local game = Fk.mini_games[game_type] + if #players == 0 or not game then return end + for _, p in ipairs(players) do + local data = data_table[p.id] + p.mini_game_data = { type = game_type, data = data } + p.request_data = json.encode(p.mini_game_data) + p.default_reply = game.default_choice and json.encode(game.default_choice(p, data)) or "" + end + + self:notifyMoveFocus(players, focus) + self:doBroadcastRequest(command, players) + + for _, p in ipairs(players) do + p.mini_game_data = nil + if not p.reply_ready then + p.client_reply = p.default_reply + p.reply_ready = true + end + end +end + -- Show a qml dialog and return qml's ClientInstance.replyToServer -- Do anything you like through this function diff --git a/lua/server/serverplayer.lua b/lua/server/serverplayer.lua index 9e16670b..75a26cae 100644 --- a/lua/server/serverplayer.lua +++ b/lua/server/serverplayer.lua @@ -5,6 +5,7 @@ ---@field public room Room ---@field public next ServerPlayer ---@field public request_data string +---@field public mini_game_data any ---@field public client_reply string ---@field public default_reply string ---@field public reply_ready boolean diff --git a/packages/test/init.lua b/packages/test/init.lua index 10c163fc..32d4d995 100644 --- a/packages/test/init.lua +++ b/packages/test/init.lua @@ -88,6 +88,8 @@ local control = fk.CreateActiveSkill{ -- }, from.hp, false)) -- room:setPlayerMark(from, "@$a", {1,2,3}) -- room:setPlayerMark(from, "@$b", {'slash','duel','axe'}) + --room:askForMiniGame({from}, "test", "test", { [from.id] = {"Helloworld"} }) + --print(from.client_reply) if to:getMark("mouxushengcontrolled") == 0 then room:addPlayerMark(to, "mouxushengcontrolled") from:control(to) @@ -126,6 +128,13 @@ local control = fk.CreateActiveSkill{ end, } --[[ +Fk:addMiniGame{ + name = "test", + qml_path = "packages/test/qml/TestMini", + update_func = function(player, data) + player:doNotify("UpdateMiniGame", json.encode(data)) + end +} Fk:addPoxiMethod{ name = "test", card_filter = function(to_select, selected, data, extra_data) diff --git a/packages/test/qml/TestMini.qml b/packages/test/qml/TestMini.qml new file mode 100644 index 00000000..84a532ca --- /dev/null +++ b/packages/test/qml/TestMini.qml @@ -0,0 +1,47 @@ +// 割圆的例子 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Fk.RoomElement + +GraphicsBox { + id: root + height: 200; width: 300 +ColumnLayout { + Text { + id: txt + color: "white" + } + + Button { + text: "Btn 1" + onClicked: { + ClientInstance.notifyServer("PushRequest", "updatemini,B1") + } + } + + Button { + text: "Btn 2" + onClicked: { + ClientInstance.notifyServer("PushRequest", "updatemini,B2") + } + } + + Button { + text: "Reply" + onClicked: { + close(); + roomScene.state = "notactive"; + ClientInstance.replyToServer("", JSON.stringify("Hello")); + } + } +} + + function loadData(data) { + txt.text = data[0] + } + + function updateData(data) { + txt.text = JSON.stringify(data) + " updated" + } +}