diff --git a/lua/client/client.lua b/lua/client/client.lua index 6741b167..7b289e8f 100644 --- a/lua/client/client.lua +++ b/lua/client/client.lua @@ -184,6 +184,9 @@ fk.client_callback["EnterRoom"] = function(jsonData) ClientInstance.players = {Self} ClientInstance.alive_players = {Self} ClientInstance.discard_pile = {} + + local data = json.decode(jsonData)[3] + Fk.disabled_packs = data.disabledPack ClientInstance:notifyUI("EnterRoom", jsonData) end diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index 2bbbef54..1d659369 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -21,6 +21,10 @@ Fk:loadTranslationTable{ ["Player num"] = "玩家数目", ["Game Mode"] = "游戏模式", ["Enable free assign"] = "自由选将", + ["General Settings"] = "通常设置", + ["Package Settings"] = "拓展包设置", + ["General Packages"] = "武将拓展包", + ["Card Packages"] = "卡牌拓展包", ["Generals Overview"] = "武将一览", ["Cards Overview"] = "卡牌一览", diff --git a/lua/core/engine.lua b/lua/core/engine.lua index 59d1c06c..23cb05b9 100644 --- a/lua/core/engine.lua +++ b/lua/core/engine.lua @@ -10,6 +10,7 @@ ---@field cards Card[] ---@field translations table> ---@field game_modes table +---@field disabled_packs string[] local Engine = class("Engine") function Engine:initialize() @@ -32,6 +33,7 @@ function Engine:initialize() self.cards = {} -- Card[] self.translations = {} -- srcText --> translated self.game_modes = {} + self.disabled_packs = {} self:loadPackages() self:addSkills(AuxSkills) @@ -207,7 +209,7 @@ function Engine:getGeneralsRandomly(num, generalPool, except, filter) assert(type(filter) == "function") end - generalPool = generalPool or self.generals + generalPool = generalPool or self:getAllGenerals() except = except or {} for _, g in ipairs(self.packages["test_p_0"].generals) do table.insert(except, g.name) @@ -242,9 +244,11 @@ end ---@return General[] function Engine:getAllGenerals(except) local result = {} - for _, general in ipairs(self.generals) do + for _, general in pairs(self.generals) do if not (except and table.contains(except, general)) then - table.insert(result, general) + if not table.contains(self.disabled_packs, general.package.name) then + table.insert(result, general) + end end end @@ -257,7 +261,9 @@ function Engine:getAllCardIds(except) local result = {} for _, card in ipairs(self.cards) do if not (except and table.contains(except, card.id)) then - table.insert(result, card.id) + if not table.contains(self.disabled_packs, card.package.name) then + table.insert(result, card.id) + end end end diff --git a/lua/core/util.lua b/lua/core/util.lua index 4d99321a..c5b5377a 100644 --- a/lua/core/util.lua +++ b/lua/core/util.lua @@ -49,6 +49,11 @@ function table.map(self, func) return ret end +-- frequenly used filter & map functions +IdMapper = function(e) return e.id end +Id2CardMapper = function(id) return Fk:getCardById(id) end +Id2PlayerMapper = function(id) return Fk:currentRoom():getPlayerById(id) end + ---@generic T ---@param self T[] ---@return T[] @@ -151,6 +156,41 @@ function table.random(tab, n) return n0 == nil and ret[1] or ret end +-- allow a = "Hello"; a[1] == "H" +local str_mt = getmetatable("") +str_mt.__index = function(str, k) + if type(k) == "number" then + if math.abs(k) > str:len() then + error("string index out of range") + end + local start, _end + if k > 0 then + start, _end = utf8.offset(str, k), utf8.offset(str, k + 1) + elseif k < 0 then + local len = str:len() + start, _end = utf8.offset(str, len + k + 1), utf8.offset(str, len + k + 2) + else + error("str[0] is undefined behavior") + end + return str:sub(start, _end - 1) + end + return string[k] +end + +str_mt.__add = function(a, b) + return a .. b +end + +str_mt.__mul = function(a, b) + return a:rep(b) +end + +-- override default string.len +string.rawlen = string.len +function string:len() + return utf8.len(self) +end + ---@param delimiter string ---@return string[] function string:split(delimiter) diff --git a/lua/server/gamelogic.lua b/lua/server/gamelogic.lua index c6b91aff..a3da424e 100644 --- a/lua/server/gamelogic.lua +++ b/lua/server/gamelogic.lua @@ -88,7 +88,7 @@ function GameLogic:chooseGenerals() end local nonlord = room:getOtherPlayers(lord, true) - local generals = Fk:getGeneralsRandomly(#nonlord * 3, Fk.generals, {lord_general}) + local generals = Fk:getGeneralsRandomly(#nonlord * 3, nil, {lord_general}) table.shuffle(generals) for _, p in ipairs(nonlord) do local arg = { diff --git a/lua/server/room.lua b/lua/server/room.lua index 3561e655..451ab392 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -60,6 +60,7 @@ function Room:initialize(_room) self.room.startGame = function(_self) Room.initialize(self, _room) -- clear old data self.settings = json.decode(_room:settings()) + Fk.disabled_packs = self.settings.disabledPack local main_co = coroutine.create(function() self:run() end) diff --git a/packages/maneuvering/init.lua b/packages/maneuvering/init.lua index 901b7304..b0d7cddd 100644 --- a/packages/maneuvering/init.lua +++ b/packages/maneuvering/init.lua @@ -303,6 +303,8 @@ extension:addCards{ } Fk:loadTranslationTable{ + ["maneuvering"] = "军争", + ["thunder__slash"] = "雷杀", ["fire__slash"] = "火杀", ["iron_chain"] = "铁锁连环", diff --git a/qml/Config.qml b/qml/Config.qml index 4e34469e..85e2be20 100644 --- a/qml/Config.qml +++ b/qml/Config.qml @@ -13,6 +13,9 @@ QtObject { property string roomBg property string bgmFile property string language + property var disabledPack: [] + property string preferedMode + property int preferedPlayerNum // Player property of client property string serverAddr @@ -38,6 +41,9 @@ QtObject { roomBg = conf.roomBg || AppPath + "/image/gamebg"; bgmFile = conf.bgmFile || AppPath + "/audio/system/bgm.mp3"; language = conf.language || "zh_CN"; + disabledPack = conf.disabledPack || [ "test_p_0" ]; + preferedMode = conf.preferedMode || "aaa_role_mode"; + preferedPlayerNum = conf.preferedPlayerNum || 2; } function saveConf() { @@ -51,6 +57,9 @@ QtObject { conf.roomBg = roomBg; conf.bgmFile = bgmFile; conf.language = language; + conf.disabledPack = disabledPack; + conf.preferedMode = preferedMode; + conf.preferedPlayerNum = preferedPlayerNum; Backend.saveConf(JSON.stringify(conf, undefined, 2)); } diff --git a/qml/Pages/Lobby.qml b/qml/Pages/Lobby.qml index 7cd28f01..36919748 100644 --- a/qml/Pages/Lobby.qml +++ b/qml/Pages/Lobby.qml @@ -179,7 +179,7 @@ Item { Loader { id: lobby_dialog - anchors.centerIn: parent + anchors.fill: parent onSourceChanged: { if (item === null) return; diff --git a/qml/Pages/LobbyElement/CreateRoom.qml b/qml/Pages/LobbyElement/CreateRoom.qml index 3c4ca690..04da9483 100644 --- a/qml/Pages/LobbyElement/CreateRoom.qml +++ b/qml/Pages/LobbyElement/CreateRoom.qml @@ -1,102 +1,39 @@ -import QtQuick 2.15 -import QtQuick.Controls 2.0 -import QtQuick.Layouts 1.15 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts Item { id: root - - width: childrenRect.width - height: childrenRect.height + anchors.fill: parent signal finished() - ColumnLayout { - spacing: 20 - - RowLayout { - anchors.rightMargin: 8 - spacing: 16 - Text { - text: Backend.translate("Room Name") - } - TextField { - id: roomName - font.pixelSize: 18 - text: Backend.translate("$RoomName").arg(Self.screenName) - } + TabBar { + id: bar + y: -height + transformOrigin: Item.BottomLeft + rotation: 90 + width: root.height + TabButton { + text: Backend.translate("General Settings") } - - RowLayout { - anchors.rightMargin: 8 - spacing: 16 - Text { - text: Backend.translate("Player num") - } - SpinBox { - id: playerNum - from: 2 - to: 8 - } - } - - RowLayout { - anchors.rightMargin: 8 - spacing: 16 - Text { - text: Backend.translate("Game Mode") - } - ComboBox { - id: gameModeCombo - textRole: "name" - model: ListModel { - id: gameModeList - } - - onCurrentIndexChanged: { - let data = gameModeList.get(currentIndex); - playerNum.from = data.minPlayer; - playerNum.to = data.maxPlayer; - } - } - } - - CheckBox { - id: freeAssignCheck - checked: Debugging ? true : false - text: Backend.translate("Enable free assign") - } - - RowLayout { - anchors.rightMargin: 8 - spacing: 16 - Button { - text: Backend.translate("OK") - onClicked: { - root.finished(); - mainWindow.busy = true; - ClientInstance.notifyServer( - "CreateRoom", - JSON.stringify([roomName.text, playerNum.value, { - enableFreeAssign: freeAssignCheck.checked, - gameMode: gameModeList.get(gameModeCombo.currentIndex).orig_name, - }]) - ); - } - } - Button { - text: Backend.translate("Cancel") - onClicked: { - root.finished(); - } - } + TabButton { + text: Backend.translate("Package Settings") } } - Component.onCompleted: { - let mode_data = JSON.parse(Backend.callLuaFunction("GetGameModes", [])); - for (let d of mode_data) { - gameModeList.append(d); + SwipeView { + width: root.width - bar.height - 16 + x: bar.height + 16 + height: root.height + interactive: false + orientation: Qt.Vertical + currentIndex: bar.currentIndex + RoomGeneralSettings {} + Item { + RoomPackageSettings { + anchors.fill: parent + } } - gameModeCombo.currentIndex = 0; } } diff --git a/qml/Pages/LobbyElement/EditProfile.qml b/qml/Pages/LobbyElement/EditProfile.qml index 3679cf50..2cc4a875 100644 --- a/qml/Pages/LobbyElement/EditProfile.qml +++ b/qml/Pages/LobbyElement/EditProfile.qml @@ -6,13 +6,11 @@ import QtQuick.Dialogs Item { id: root - width: childrenRect.width - height: childrenRect.height - signal finished() ColumnLayout { spacing: 20 + anchors.centerIn: parent RowLayout { anchors.rightMargin: 8 diff --git a/qml/Pages/LobbyElement/RoomGeneralSettings.qml b/qml/Pages/LobbyElement/RoomGeneralSettings.qml new file mode 100644 index 00000000..5af70467 --- /dev/null +++ b/qml/Pages/LobbyElement/RoomGeneralSettings.qml @@ -0,0 +1,106 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +ColumnLayout { + spacing: 20 + + RowLayout { + anchors.rightMargin: 8 + spacing: 16 + Text { + text: Backend.translate("Room Name") + } + TextField { + id: roomName + font.pixelSize: 18 + text: Backend.translate("$RoomName").arg(Self.screenName) + } + } + + RowLayout { + anchors.rightMargin: 8 + spacing: 16 + Text { + text: Backend.translate("Player num") + } + SpinBox { + id: playerNum + from: 2 + to: 8 + + onValueChanged: { + config.preferedPlayerNum = value; + } + } + } + + RowLayout { + anchors.rightMargin: 8 + spacing: 16 + Text { + text: Backend.translate("Game Mode") + } + ComboBox { + id: gameModeCombo + textRole: "name" + model: ListModel { + id: gameModeList + } + + onCurrentIndexChanged: { + let data = gameModeList.get(currentIndex); + playerNum.from = data.minPlayer; + playerNum.to = data.maxPlayer; + + config.preferedMode = data.orig_name; + } + } + } + + CheckBox { + id: freeAssignCheck + checked: Debugging ? true : false + text: Backend.translate("Enable free assign") + } + + RowLayout { + anchors.rightMargin: 8 + spacing: 16 + Button { + text: Backend.translate("OK") + onClicked: { + root.finished(); + mainWindow.busy = true; + ClientInstance.notifyServer( + "CreateRoom", + JSON.stringify([roomName.text, playerNum.value, { + enableFreeAssign: freeAssignCheck.checked, + gameMode: config.preferedMode, + disabledPack: config.disabledPack, + }]) + ); + } + } + Button { + text: Backend.translate("Cancel") + onClicked: { + root.finished(); + } + } + } + + Component.onCompleted: { + let mode_data = JSON.parse(Backend.callLuaFunction("GetGameModes", [])); + let i = 0; + for (let d of mode_data) { + gameModeList.append(d); + if (d.orig_name == config.preferedMode) { + gameModeCombo.currentIndex = i; + } + i += 1; + } + + playerNum.value = config.preferedPlayerNum; + } +} diff --git a/qml/Pages/LobbyElement/RoomPackageSettings.qml b/qml/Pages/LobbyElement/RoomPackageSettings.qml new file mode 100644 index 00000000..9cce00fa --- /dev/null +++ b/qml/Pages/LobbyElement/RoomPackageSettings.qml @@ -0,0 +1,109 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +Flickable { + id: root + flickableDirection: Flickable.AutoFlickIfNeeded + clip: true + contentHeight: layout.height + ScrollBar.vertical: ScrollBar { + parent: root.parent + anchors.top: root.top + anchors.right: root.right + anchors.bottom: root.bottom + } + + ColumnLayout { + id: layout + anchors.top: parent.top + anchors.topMargin: 8 + + CheckBox { + text: "禁用Lua拓展 (重启后生效)" + } + + Text { + text: Backend.translate("General Packages") + font.bold: true + } + + GridLayout { + id: gpacks + columns: 2 + + Repeater { + model: ListModel { + id: gpacklist + } + + CheckBox { + text: name + checked: pkg_enabled + enabled: orig_name !== "test_p_0" + + onCheckedChanged: { + let packs = config.disabledPack; + if (checked) { + let idx = packs.indexOf(orig_name); + if (idx !== -1) packs.splice(idx, 1); + } else { + packs.push(orig_name); + } + } + } + } + } + + Text { + text: Backend.translate("Card Packages") + font.bold: true + } + + GridLayout { + id: cpacks + columns: 2 + + Repeater { + model: ListModel { + id: cpacklist + } + + CheckBox { + text: name + checked: pkg_enabled + + onCheckedChanged: { + let packs = config.disabledPack; + if (checked) { + let idx = packs.indexOf(orig_name); + if (idx !== -1) packs.splice(idx, 1); + } else { + packs.push(orig_name); + } + } + } + } + } + } + + Component.onCompleted: { + let g = JSON.parse(Backend.callLuaFunction("GetAllGeneralPack", [])); + for (let orig of g) { + gpacklist.append({ + name: Backend.translate(orig), + orig_name: orig, + pkg_enabled: config.disabledPack.indexOf(orig) === -1, + }); + } + + let c = JSON.parse(Backend.callLuaFunction("GetAllCardPack", [])); + for (let orig of c) { + cpacklist.append({ + name: Backend.translate(orig), + orig_name: orig, + pkg_enabled: config.disabledPack.indexOf(orig) === -1, + }); + } + } +}