parent
694deecdf1
commit
6ae86a1e3e
|
@ -0,0 +1,27 @@
|
|||
# 关于扩展FreeKill玩法的思考
|
||||
|
||||
___
|
||||
|
||||
要扩展玩法,大概就这些:
|
||||
|
||||
1. 扩展新规则,覆盖本来的身份版规则
|
||||
2. 直接大改gamelogic把流程都改了
|
||||
|
||||
要将扩展的玩法放进游戏:
|
||||
|
||||
1. 首先创房间的时候有下拉菜单给人选模式
|
||||
2. Room正式开始之后根据模式加载相应的Logic
|
||||
3. 加载GameRule后根据模式加载特殊规则
|
||||
4. 开始玩
|
||||
|
||||
___
|
||||
|
||||
## 拓展新规
|
||||
|
||||
首先就是如何覆盖老规则,这个可以通过设置一个特殊tag
|
||||
|
||||
___
|
||||
|
||||
## 拓展logic
|
||||
|
||||
从GameLogic继承然后重写有关函数就行
|
|
@ -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"
|
||||
|
|
|
@ -19,6 +19,7 @@ Fk:loadTranslationTable{
|
|||
["Room Name"] = "房间名字",
|
||||
["$RoomName"] = "%1的房间",
|
||||
["Player num"] = "玩家数目",
|
||||
["Game Mode"] = "游戏模式",
|
||||
["Enable free assign"] = "自由选将",
|
||||
|
||||
["Generals Overview"] = "武将一览",
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
---@field lords string[]
|
||||
---@field cards Card[]
|
||||
---@field translations table<string, table<string, string>>
|
||||
---@field game_modes table<string, GameMode>
|
||||
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[]
|
||||
|
|
|
@ -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
|
|
@ -6,6 +6,7 @@
|
|||
---@field extra_skills Skill[]
|
||||
---@field related_skills table<string, string>
|
||||
---@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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
---@field card_place table<integer, CardArea>
|
||||
---@field owner_map table<integer, integer>
|
||||
---@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
|
||||
------------------------------------------------------------------------
|
||||
|
|
|
@ -135,6 +135,8 @@ Fk:loadTranslationTable{
|
|||
[":lijian"] = "阶段技,你可以弃置一张牌并选择两名其他男性角色,后选择的角色视为对先选择的角色使用了一张不能被无懈可击的决斗。",
|
||||
["biyue"] = "闭月",
|
||||
[":biyue"] = "结束阶段开始时,你可以摸一张牌。",
|
||||
|
||||
["aaa_role_mode"] = "身份模式",
|
||||
}
|
||||
|
||||
-- aux skills
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,6 +56,12 @@ public:
|
|||
bool hasRequest() const;
|
||||
};
|
||||
|
||||
%extend Room {
|
||||
QString settings() {
|
||||
return $self->getSettings();
|
||||
}
|
||||
}
|
||||
|
||||
%{
|
||||
void Room::initLua()
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue