From 59c25c583c2375a608142acce0c7f3888fb6a03a Mon Sep 17 00:00:00 2001 From: notify Date: Tue, 1 Aug 2023 21:01:01 +0800 Subject: [PATCH] Replay (#231) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 录像保存. 重放. --- Fk/Config.qml | 1 + Fk/Logic.js | 4 +- Fk/Pages/Lobby.qml | 5 + Fk/Pages/Replay.qml | 155 +++++++++++++++++++++++++++++ Fk/Pages/Room.qml | 74 +++++++++++++- Fk/Pages/RoomLogic.js | 13 +++ Fk/Pages/qmldir | 1 + Fk/RoomElement/GameOverBox.qml | 20 +++- Fk/main.qml | 6 +- lua/client/client.lua | 28 ++++-- lua/client/client_util.lua | 5 + lua/client/i18n/zh_CN.lua | 6 ++ src/CMakeLists.txt | 1 + src/client/client.cpp | 4 + src/client/client.h | 4 + src/client/replayer.cpp | 175 +++++++++++++++++++++++++++++++++ src/client/replayer.h | 52 ++++++++++ src/core/util.cpp | 2 +- src/ui/qmlbackend.cpp | 60 +++++++++++ src/ui/qmlbackend.h | 15 +++ 20 files changed, 617 insertions(+), 14 deletions(-) create mode 100644 Fk/Pages/Replay.qml create mode 100644 src/client/replayer.cpp create mode 100644 src/client/replayer.h diff --git a/Fk/Config.qml b/Fk/Config.qml index 8f544b53..8609754d 100644 --- a/Fk/Config.qml +++ b/Fk/Config.qml @@ -40,6 +40,7 @@ QtObject { property int roomTimeout: 0 property bool enableFreeAssign: false property bool observing: false + property bool replaying: false property var blockedUsers: [] function loadConf() { diff --git a/Fk/Logic.js b/Fk/Logic.js index 3d15eb4b..2639edc1 100644 --- a/Fk/Logic.js +++ b/Fk/Logic.js @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later +/* var generalsOverviewPage, cardsOverviewPage; var clientPageCreated = false; function createClientPages() { @@ -13,6 +14,7 @@ function createClientPages() { mainWindow.cardsOverviewPage = cardsOverviewPage; } } +*/ var callbacks = {}; let sheduled_download = ""; @@ -98,7 +100,7 @@ callbacks["BackToStart"] = (jsonData) => { callbacks["EnterLobby"] = (jsonData) => { // depth == 1 means the lobby page is not present in mainStack - createClientPages(); + // createClientPages(); if (mainStack.depth === 1) { // we enter the lobby successfully, so save password now. config.lastLoginServer = config.serverAddr; diff --git a/Fk/Pages/Lobby.qml b/Fk/Pages/Lobby.qml index 75303d62..1cf33337 100644 --- a/Fk/Pages/Lobby.qml +++ b/Fk/Pages/Lobby.qml @@ -168,6 +168,7 @@ Item { lobby_dialog.sourceComponent = Qt.createComponent("../LobbyElement/CreateRoom.qml"); lobby_drawer.open(); config.observing = false; + config.replaying = false; } } @@ -197,6 +198,9 @@ Item { } Button { text: Backend.translate("Replay") + onClicked: { + mainStack.push(mainWindow.replayPage); + } } Button { text: Backend.translate("About") @@ -279,6 +283,7 @@ Item { } function enterRoom(roomId, playerNum, capacity, pw) { + config.replaying = false; if (playerNum < capacity) { config.observing = false; Backend.callLuaFunction("SetObserving", [false]); diff --git a/Fk/Pages/Replay.qml b/Fk/Pages/Replay.qml new file mode 100644 index 00000000..81b461b0 --- /dev/null +++ b/Fk/Pages/Replay.qml @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Fk + +Item { + id: root + + ToolBar { + id: bar + width: parent.width + RowLayout { + anchors.fill: parent + ToolButton { + icon.source: AppPath + "/image/modmaker/back" + onClicked: mainStack.pop(); + } + Label { + text: Backend.translate("Replay Manager") + horizontalAlignment: Qt.AlignHCenter + Layout.fillWidth: true + } + ToolButton { + icon.source: AppPath + "/image/modmaker/menu" + onClicked: menu.open() + + Menu { + id: menu + y: bar.height + } + } + } + } + + Rectangle { + width: parent.width + height: parent.height - bar.height + anchors.top: bar.bottom + color: "snow" + opacity: 0.75 + clip: true + + ListView { + id: list + clip: true + anchors.fill: parent + model: ListModel { + id: model + } + delegate: Item { + width: root.width + height: 64 + + Image { + id: generalPic + anchors.top: parent.top + anchors.left: parent.left + anchors.margins: 8 + width: 48 + height: 48 + source: SkinBank.getGeneralExtraPic(general, "avatar/") ?? SkinBank.getGeneralPicture(general) + sourceClipRect: sourceSize.width > 200 ? Qt.rect(61, 0, 128, 128) : undefined + + Rectangle { + anchors.fill: parent + color: "transparent" + border.width: 1 + } + } + + ColumnLayout { + anchors.left: generalPic.right + anchors.margins: 8 + Text { + text: { + const win = winner.split("+").indexOf(role) !== -1; + const winStr = win ? Backend.translate("Game Win") : Backend.translate("Game Lose"); + return "" + Backend.translate(general) + " " + Backend.translate(role) + " " + winStr; + } + font.pixelSize: 20 + textFormat: Text.RichText + } + Text { + text: { + const y = repDate.slice(0,4); + const month = repDate.slice(4,6); + const d = repDate.slice(6,8); + const h = repDate.slice(8,10); + const m = repDate.slice(10,12); + const s = repDate.slice(12,14); + const dateStr = y + "-" + month + "-" + d + " " + h + ":" + m + ":" + s; + + return playerName + " " + Backend.translate(gameMode) + " " + dateStr + } + } + } + + Button { + id: replayBtn + text: Backend.translate("Play the Replay") + anchors.right: delBtn.left + anchors.rightMargin: 8 + onClicked: { + config.observing = true; + config.replaying = true; + Backend.playRecord(fileName); + } + } + + Button { + id: delBtn + text: Backend.translate("Delete Replay") + anchors.right: parent.right + anchors.rightMargin: 8 + onClicked: { + Backend.removeRecord(fileName); + removeModel(index); + } + } + } + } + } + + function updateList() { + model.clear(); + const data = Backend.ls("recording"); + data.reverse(); + data.forEach(s => { + const d = s.split("."); + if (d.length !== 8) return; + // s: