From 6ae86a1e3ece56b3ae8d37a6da60d50f11fdcb36 Mon Sep 17 00:00:00 2001 From: notify Date: Tue, 14 Mar 2023 00:12:02 +0800 Subject: [PATCH] Gamemode (#75) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 提供拓展游戏模式的接口 --- doc/dev/scenario.md | 27 ++++++++++++++++++++++ lua/client/client_util.lua | 14 ++++++++++++ lua/client/i18n/zh_CN.lua | 1 + lua/core/engine.lua | 19 ++++++++++++++++ lua/core/game_mode.lua | 15 +++++++++++++ lua/core/package.lua | 6 +++++ lua/fk_ex.lua | 12 ++++++++++ lua/freekill.lua | 1 + lua/server/room.lua | 21 +++++++++++++++++- packages/standard/i18n/zh_CN.lua | 2 ++ packages/standard/init.lua | 7 ++++++ qml/Pages/LobbyElement/CreateRoom.qml | 32 ++++++++++++++++++++++++++- src/swig/server.i | 6 +++++ src/ui/qmlbackend.cpp | 1 + 14 files changed, 162 insertions(+), 2 deletions(-) create mode 100644 doc/dev/scenario.md create mode 100644 lua/core/game_mode.lua diff --git a/doc/dev/scenario.md b/doc/dev/scenario.md new file mode 100644 index 00000000..badfd943 --- /dev/null +++ b/doc/dev/scenario.md @@ -0,0 +1,27 @@ +# 关于扩展FreeKill玩法的思考 + +___ + +要扩展玩法,大概就这些: + +1. 扩展新规则,覆盖本来的身份版规则 +2. 直接大改gamelogic把流程都改了 + +要将扩展的玩法放进游戏: + +1. 首先创房间的时候有下拉菜单给人选模式 +2. Room正式开始之后根据模式加载相应的Logic +3. 加载GameRule后根据模式加载特殊规则 +4. 开始玩 + +___ + +## 拓展新规 + +首先就是如何覆盖老规则,这个可以通过设置一个特殊tag + +___ + +## 拓展logic + +从GameLogic继承然后重写有关函数就行 diff --git a/lua/client/client_util.lua b/lua/client/client_util.lua index 46d0c86b..04df46b5 100644 --- a/lua/client/client_util.lua +++ b/lua/client/client_util.lua @@ -376,4 +376,18 @@ function GetExpandPileOfSkill(skillName) return skill and (skill.expand_pile or "") or "" end +function GetGameModes() + local ret = {} + for k, v in pairs(Fk.game_modes) do + table.insert(ret, { + name = Fk:translate(v.name), + orig_name = v.name, + minPlayer = v.minPlayer, + maxPlayer = v.maxPlayer, + }) + end + table.sort(ret, function(a, b) return a.name > b.name end) + return json.encode(ret) +end + dofile "lua/client/i18n/init.lua" diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index 63993f9c..2bbbef54 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -19,6 +19,7 @@ Fk:loadTranslationTable{ ["Room Name"] = "房间名字", ["$RoomName"] = "%1的房间", ["Player num"] = "玩家数目", + ["Game Mode"] = "游戏模式", ["Enable free assign"] = "自由选将", ["Generals Overview"] = "武将一览", diff --git a/lua/core/engine.lua b/lua/core/engine.lua index 4201633c..59d1c06c 100644 --- a/lua/core/engine.lua +++ b/lua/core/engine.lua @@ -9,6 +9,7 @@ ---@field lords string[] ---@field cards Card[] ---@field translations table> +---@field game_modes table local Engine = class("Engine") function Engine:initialize() @@ -30,6 +31,7 @@ function Engine:initialize() self.lords = {} -- lordName[] self.cards = {} -- Card[] self.translations = {} -- srcText --> translated + self.game_modes = {} self:loadPackages() self:addSkills(AuxSkills) @@ -51,6 +53,7 @@ function Engine:loadPackage(pack) self:addGenerals(pack.generals) end self:addSkills(pack:getSkills()) + self:addGameModes(pack.game_modes) end function Engine:loadPackages() @@ -178,6 +181,22 @@ function Engine:cloneCard(name, suit, number) return ret end +---@param game_modes GameMode[] +function Engine:addGameModes(game_modes) + for _, s in ipairs(game_modes) do + self:addGameMode(s) + end +end + +---@param game_mode GameMode +function Engine:addGameMode(game_mode) + assert(game_mode:isInstanceOf(GameMode)) + if self.game_modes[game_mode.name] ~= nil then + error(string.format("Duplicate game_mode %s detected", game_mode.name)) + end + self.game_modes[game_mode.name] = game_mode +end + ---@param num integer ---@param generalPool General[] ---@param except string[] diff --git a/lua/core/game_mode.lua b/lua/core/game_mode.lua new file mode 100644 index 00000000..6dc2792b --- /dev/null +++ b/lua/core/game_mode.lua @@ -0,0 +1,15 @@ +---@class GameMode: Object +---@field name string +---@field minPlayer integer +---@field maxPlayer integer +---@field rule TriggerSkill +---@field logic GameLogic +local GameMode = class("GameMode") + +function GameMode:initialize(name, min, max) + self.name = name + self.minPlayer = math.max(min, 2) + self.maxPlayer = math.min(max, 8) +end + +return GameMode diff --git a/lua/core/package.lua b/lua/core/package.lua index edb8c066..97ff11b8 100644 --- a/lua/core/package.lua +++ b/lua/core/package.lua @@ -6,6 +6,7 @@ ---@field extra_skills Skill[] ---@field related_skills table ---@field cards Card[] +---@field game_modes GameMode[] local Package = class("Package") ---@alias PackageType integer @@ -25,6 +26,7 @@ function Package:initialize(name, _type) self.extra_skills = {} -- skill not belongs to any generals, like "jixi" self.related_skills = {} self.cards = {} + self.game_modes = {} end ---@return Skill[] @@ -60,4 +62,8 @@ function Package:addCards(cards) end end +function Package:addGameMode(game_mode) + table.insert(self.game_modes, game_mode) +end + return Package diff --git a/lua/fk_ex.lua b/lua/fk_ex.lua index ac051d39..c7c0c3c3 100644 --- a/lua/fk_ex.lua +++ b/lua/fk_ex.lua @@ -465,3 +465,15 @@ function fk.CreateTreasure(spec) if spec.on_uninstall then card.onUninstall = spec.on_uninstall end return card end + +---@param spec GameMode +---@return GameMode +function fk.CreateGameMode(spec) + assert(type(spec.name) == "string") + assert(type(spec.minPlayer) == "number") + assert(type(spec.maxPlayer) == "number") + local ret = GameMode:new(spec.name, spec.minPlayer, spec.maxPlayer) + ret.rule = spec.rule + ret.logic = spec.logic + return ret +end diff --git a/lua/freekill.lua b/lua/freekill.lua index cd88c281..8c437cf6 100644 --- a/lua/freekill.lua +++ b/lua/freekill.lua @@ -25,6 +25,7 @@ Skill = require "core.skill" UsableSkill = require "core.skill_type.usable_skill" StatusSkill = require "core.skill_type.status_skill" Player = require "core.player" +GameMode = require "core.game_mode" -- load config local function loadConf() diff --git a/lua/server/room.lua b/lua/server/room.lua index 2d2ff6c9..471ba4c7 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -15,6 +15,7 @@ ---@field card_place table ---@field owner_map table ---@field status_skills Skill[] +---@field settings table local Room = class("Room") -- load classes used by the game @@ -58,6 +59,7 @@ function Room:initialize(_room) self.room.startGame = function(_self) Room.initialize(self, _room) -- clear old data + self.settings = json.decode(_room:settings()) local main_co = coroutine.create(function() self:run() end) @@ -114,7 +116,9 @@ function Room:run() table.insert(self.players, player) end - self.logic = GameLogic:new(self) + local mode = Fk.game_modes[self.settings.gameMode] + self.logic = (mode.logic or GameLogic):new(self) + if mode.rule then self.logic:addTriggerSkill(mode.rule) end self.logic:run() end @@ -307,6 +311,21 @@ function Room:removePlayerMark(player, mark, count) self:setPlayerMark(player, mark, math.max(num - count, 0)) end +---@param tag_name string +function Room:setTag(tag_name, value) + self.tag[tag_name] = value +end + +---@param tag_name string +function Room:getTag(tag_name) + return self.tag[tag_name] +end + +---@param tag_name string +function Room:removeTag(tag_name) + self.tag[tag_name] = nil +end + ------------------------------------------------------------------------ -- network functions, notify function ------------------------------------------------------------------------ diff --git a/packages/standard/i18n/zh_CN.lua b/packages/standard/i18n/zh_CN.lua index 38a59c2d..d4a55896 100644 --- a/packages/standard/i18n/zh_CN.lua +++ b/packages/standard/i18n/zh_CN.lua @@ -135,6 +135,8 @@ Fk:loadTranslationTable{ [":lijian"] = "阶段技,你可以弃置一张牌并选择两名其他男性角色,后选择的角色视为对先选择的角色使用了一张不能被无懈可击的决斗。", ["biyue"] = "闭月", [":biyue"] = "结束阶段开始时,你可以摸一张牌。", + + ["aaa_role_mode"] = "身份模式", } -- aux skills diff --git a/packages/standard/init.lua b/packages/standard/init.lua index 3f8217dc..a9e81dc8 100644 --- a/packages/standard/init.lua +++ b/packages/standard/init.lua @@ -921,6 +921,13 @@ local diaochan = General:new(extension, "diaochan", "qun", 3, 3, General.Female) diaochan:addSkill(lijian) diaochan:addSkill(biyue) +local role_mode = fk.CreateGameMode{ + name = "aaa_role_mode", -- just to let it at the top of list + minPlayer = 2, + maxPlayer = 8, +} +extension:addGameMode(role_mode) + -- load translations of this package dofile "packages/standard/i18n/init.lua" diff --git a/qml/Pages/LobbyElement/CreateRoom.qml b/qml/Pages/LobbyElement/CreateRoom.qml index 486588aa..3c4ca690 100644 --- a/qml/Pages/LobbyElement/CreateRoom.qml +++ b/qml/Pages/LobbyElement/CreateRoom.qml @@ -39,6 +39,27 @@ Item { } } + 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 @@ -56,7 +77,8 @@ Item { ClientInstance.notifyServer( "CreateRoom", JSON.stringify([roomName.text, playerNum.value, { - enableFreeAssign: freeAssignCheck.checked + enableFreeAssign: freeAssignCheck.checked, + gameMode: gameModeList.get(gameModeCombo.currentIndex).orig_name, }]) ); } @@ -69,4 +91,12 @@ Item { } } } + + Component.onCompleted: { + let mode_data = JSON.parse(Backend.callLuaFunction("GetGameModes", [])); + for (let d of mode_data) { + gameModeList.append(d); + } + gameModeCombo.currentIndex = 0; + } } diff --git a/src/swig/server.i b/src/swig/server.i index e7519b3d..01472d12 100644 --- a/src/swig/server.i +++ b/src/swig/server.i @@ -56,6 +56,12 @@ public: bool hasRequest() const; }; +%extend Room { + QString settings() { + return $self->getSettings(); + } +} + %{ void Room::initLua() { diff --git a/src/ui/qmlbackend.cpp b/src/ui/qmlbackend.cpp index 70ee2a20..f48eb89f 100644 --- a/src/ui/qmlbackend.cpp +++ b/src/ui/qmlbackend.cpp @@ -116,6 +116,7 @@ bool QmlBackend::isDir(const QString &file) { } QString QmlBackend::translate(const QString &src) { + if (!ClientInstance) return src; lua_State *L = ClientInstance->getLuaState(); lua_getglobal(L, "Translate"); auto bytes = src.toUtf8();