Deputy general (#127)

增加双将机制
This commit is contained in:
notify 2023-04-19 14:07:16 +08:00 committed by GitHub
parent 1ad352521f
commit 85923c8a71
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 124 additions and 29 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -25,6 +25,7 @@ Fk:loadTranslationTable{
["Select general num"] = "选将数目", ["Select general num"] = "选将数目",
["Game Mode"] = "游戏模式", ["Game Mode"] = "游戏模式",
["Enable free assign"] = "自由选将", ["Enable free assign"] = "自由选将",
["Enable deputy general"] = "启用副将机制",
["General Settings"] = "通常设置", ["General Settings"] = "通常设置",
["Package Settings"] = "拓展包设置", ["Package Settings"] = "拓展包设置",
["General Packages"] = "武将拓展包", ["General Packages"] = "武将拓展包",

View File

@ -14,6 +14,7 @@
---@field public kingdom string @ 势力 ---@field public kingdom string @ 势力
---@field public role string @ 身份 ---@field public role string @ 身份
---@field public general string @ 武将 ---@field public general string @ 武将
---@field public deputyGeneral string @ 副将
---@field public gender integer @ 性别 ---@field public gender integer @ 性别
---@field public seat integer @ 座位号 ---@field public seat integer @ 座位号
---@field public next Player @ 下家 ---@field public next Player @ 下家
@ -67,6 +68,7 @@ function Player:initialize()
self.kingdom = "qun" self.kingdom = "qun"
self.role = "" self.role = ""
self.general = "" self.general = ""
self.deputyGeneral = ""
self.gender = General.Male self.gender = General.Male
self.seat = 0 self.seat = 0
self.next = nil self.next = nil

View File

@ -70,6 +70,7 @@ end
function GameLogic:chooseGenerals() function GameLogic:chooseGenerals()
local room = self.room local room = self.room
local generalNum = room.settings.generalNum local generalNum = room.settings.generalNum
local n = room.settings.enableDeputy and 2 or 1
local lord = room:getLord() local lord = room:getLord()
local lord_general = nil local lord_general = nil
if lord ~= nil then if lord ~= nil then
@ -78,9 +79,17 @@ function GameLogic:chooseGenerals()
for i = 1, #generals do for i = 1, #generals do
generals[i] = generals[i].name generals[i] = generals[i].name
end end
lord_general = room:askForGeneral(lord, generals) lord_general = room:askForGeneral(lord, generals, n)
local deputy
if type(lord_general) == "table" then
deputy = lord_general[2]
lord_general = lord_general[1]
end
room:setPlayerGeneral(lord, lord_general, true) room:setPlayerGeneral(lord, lord_general, true)
room:broadcastProperty(lord, "general") room:broadcastProperty(lord, "general")
room:setDeputyGeneral(lord, deputy)
room:broadcastProperty(lord, "deputyGeneral")
end end
local nonlord = room:getOtherPlayers(lord, true) local nonlord = room:getOtherPlayers(lord, true)
@ -91,18 +100,22 @@ function GameLogic:chooseGenerals()
for i = 1, generalNum do for i = 1, generalNum do
table.insert(arg, table.remove(generals, 1).name) table.insert(arg, table.remove(generals, 1).name)
end end
p.request_data = json.encode(arg) p.request_data = json.encode{ arg, n }
p.default_reply = arg[1] p.default_reply = table.random(arg, n)
end end
room:notifyMoveFocus(nonlord, "AskForGeneral") room:notifyMoveFocus(nonlord, "AskForGeneral")
room:doBroadcastRequest("AskForGeneral", nonlord) room:doBroadcastRequest("AskForGeneral", nonlord)
for _, p in ipairs(nonlord) do for _, p in ipairs(nonlord) do
if p.general == "" and p.reply_ready then if p.general == "" and p.reply_ready then
local general = json.decode(p.client_reply)[1] local generals = json.decode(p.client_reply)
local general = generals[1]
local deputy = generals[2]
room:setPlayerGeneral(p, general, true) room:setPlayerGeneral(p, general, true)
room:setDeputyGeneral(p, deputy)
else else
room:setPlayerGeneral(p, p.default_reply, true) room:setPlayerGeneral(p, p.default_reply[1], true)
room:setDeputyGeneral(p, p.default_reply[2])
end end
p.default_reply = "" p.default_reply = ""
end end
@ -120,13 +133,16 @@ function GameLogic:prepareForStart()
for _, p in ipairs(players) do for _, p in ipairs(players) do
assert(p.general ~= "") assert(p.general ~= "")
local general = Fk.generals[p.general] local general = Fk.generals[p.general]
p.maxHp = general.maxHp local deputy = Fk.generals[p.deputyGeneral]
p.hp = general.hp p.maxHp = deputy and math.floor((deputy.maxHp + general.maxHp) / 2)
p.shield = general.shield or general.maxHp
p.hp = deputy and math.floor((deputy.hp + general.hp) / 2) or general.hp
p.shield = math.min(general.shield + (deputy and deputy.shield or 0), 5)
-- TODO: setup AI here -- TODO: setup AI here
if p.role ~= "lord" then if p.role ~= "lord" then
room:broadcastProperty(p, "general") room:broadcastProperty(p, "general")
room:broadcastProperty(p, "deputyGeneral")
elseif #players >= 5 then elseif #players >= 5 then
p.maxHp = p.maxHp + 1 p.maxHp = p.maxHp + 1
p.hp = p.hp + 1 p.hp = p.hp + 1
@ -159,6 +175,17 @@ function GameLogic:prepareForStart()
for _, sname in ipairs(Fk.generals[p.general].other_skills) do for _, sname in ipairs(Fk.generals[p.general].other_skills) do
addRoleModSkills(p, sname) addRoleModSkills(p, sname)
end end
local deputy = Fk.generals[p.deputyGeneral]
if deputy then
skills = deputy.skills
for _, s in ipairs(skills) do
addRoleModSkills(p, s.name)
end
for _, sname in ipairs(deputy.other_skills) do
addRoleModSkills(p, sname)
end
end
end end
self:addTriggerSkill(GameRule) self:addTriggerSkill(GameRule)

View File

@ -393,6 +393,14 @@ function Room:setPlayerGeneral(player, general, changeKingdom)
end end
end end
---@param player ServerPlayer
---@param general string
function Room:setDeputyGeneral(player, general)
if Fk.generals[general] == nil then return end
player.deputyGeneral = general
self:notifyProperty(player, player, "deputyGeneral")
end
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- 网络通信有关 -- 网络通信有关
------------------------------------------------------------------------ ------------------------------------------------------------------------
@ -1048,22 +1056,24 @@ end
---@param player ServerPlayer @ 询问目标 ---@param player ServerPlayer @ 询问目标
---@param generals string[] @ 可选武将 ---@param generals string[] @ 可选武将
---@return string @ 选择的武将 ---@return string @ 选择的武将
function Room:askForGeneral(player, generals) function Room:askForGeneral(player, generals, n)
local command = "AskForGeneral" local command = "AskForGeneral"
self:notifyMoveFocus(player, command) self:notifyMoveFocus(player, command)
if #generals == 1 then return generals[1] end n = n or 1
local defaultChoice = generals[1] if #generals == n then return n == 1 and generals[1] or generals end
local defaultChoice = table.random(generals, n)
if (player.state == "online") then if (player.state == "online") then
local result = self:doRequest(player, command, json.encode(generals)) local result = self:doRequest(player, command, json.encode{ generals, n })
local choices
if result == "" then if result == "" then
return defaultChoice choices = defaultChoice
else else
-- TODO: result is a JSON array choices = json.decode(result)
-- update here when choose multiple generals
return json.decode(result)[1]
end end
if #choices == 1 then return choices[1] end
return choices
end end
return defaultChoice return defaultChoice

View File

@ -85,6 +85,12 @@ ColumnLayout {
text: Backend.translate("Enable free assign") text: Backend.translate("Enable free assign")
} }
CheckBox {
id: deputyCheck
checked: Debugging ? true : false
text: Backend.translate("Enable deputy general")
}
RowLayout { RowLayout {
anchors.rightMargin: 8 anchors.rightMargin: 8
spacing: 16 spacing: 16
@ -97,6 +103,7 @@ ColumnLayout {
"CreateRoom", "CreateRoom",
JSON.stringify([roomName.text, playerNum.value, { JSON.stringify([roomName.text, playerNum.value, {
enableFreeAssign: freeAssignCheck.checked, enableFreeAssign: freeAssignCheck.checked,
enableDeputy: deputyCheck.checked,
gameMode: config.preferedMode, gameMode: config.preferedMode,
disabledPack: config.disabledPack, disabledPack: config.disabledPack,
generalNum: config.preferredGeneralNum, generalNum: config.preferredGeneralNum,

View File

@ -186,6 +186,7 @@ Item {
Photo { Photo {
playerid: model.id playerid: model.id
general: model.general general: model.general
deputyGeneral: model.deputyGeneral
screenName: model.screenName screenName: model.screenName
role: model.role role: model.role
kingdom: model.kingdom kingdom: model.kingdom
@ -253,6 +254,7 @@ Item {
self.playerid: dashboardModel.id self.playerid: dashboardModel.id
self.general: dashboardModel.general self.general: dashboardModel.general
self.screenName: dashboardModel.screenName self.screenName: dashboardModel.screenName
self.deputyGeneral: dashboardModel.deputyGeneral
self.role: dashboardModel.role self.role: dashboardModel.role
self.kingdom: dashboardModel.kingdom self.kingdom: dashboardModel.kingdom
self.netstate: dashboardModel.netstate self.netstate: dashboardModel.netstate
@ -798,6 +800,7 @@ Item {
dashboardModel = { dashboardModel = {
id: Self.id, id: Self.id,
general: Self.avatar, general: Self.avatar,
deputyGeneral: "",
screenName: Self.screenName, screenName: Self.screenName,
role: "unknown", role: "unknown",
kingdom: "qun", kingdom: "qun",
@ -822,6 +825,7 @@ Item {
id: -1, id: -1,
index: i - 1, // For animating seat swap index: i - 1, // For animating seat swap
general: "", general: "",
deputyGeneral: "",
screenName: "", screenName: "",
role: "unknown", role: "unknown",
kingdom: "qun", kingdom: "qun",

View File

@ -13,6 +13,7 @@ Item {
scale: 0.75 scale: 0.75
property int playerid: 0 property int playerid: 0
property string general: "" property string general: ""
property string deputyGeneral: ""
property string screenName: "" property string screenName: ""
property string role: "unknown" property string role: "unknown"
property string kingdom: "qun" property string kingdom: "qun"
@ -120,7 +121,7 @@ Item {
y: 28 y: 28
font.family: fontLibian.name font.family: fontLibian.name
font.pixelSize: 22 font.pixelSize: 22
opacity: 0.7 opacity: 0.9
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
lineHeight: 18 lineHeight: 18
lineHeightMode: Text.FixedHeight lineHeightMode: Text.FixedHeight
@ -138,7 +139,7 @@ Item {
font.pixelSize: 22 font.pixelSize: 22
rotation: 90 rotation: 90
transformOrigin: Item.BottomLeft transformOrigin: Item.BottomLeft
opacity: 0.7 opacity: 0.9
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
lineHeight: 18 lineHeight: 18
lineHeightMode: Text.FixedHeight lineHeightMode: Text.FixedHeight
@ -156,15 +157,55 @@ Item {
anchors.bottomMargin: 36 anchors.bottomMargin: 36
} }
Image { Item {
id: generalImage
width: 138 width: 138
height: 222 height: 222
smooth: true
visible: false visible: false
fillMode: Image.PreserveAspectCrop id: generalImgItem
source: (general != "") ? SkinBank.getGeneralPicture(general) : ""
Image {
id: generalImage
width: deputyGeneral ? parent.width / 2 : parent.width
height: parent.height
smooth: true
fillMode: Image.PreserveAspectCrop
source: (general != "") ? SkinBank.getGeneralPicture(general) : ""
}
Image {
id: deputyGeneralImage
anchors.left: generalImage.right
width: parent.width / 2
height: parent.height
smooth: true
fillMode: Image.PreserveAspectCrop
source: (deputyGeneral != "") ?
SkinBank.getGeneralPicture(deputyGeneral) : ""
}
Image {
id: deputySplit
source: SkinBank.PHOTO_DIR + "deputy-split"
opacity: deputyGeneral ? 1 : 0
}
Text {
id: deputyGeneralName
anchors.left: generalImage.right
anchors.leftMargin: -14
y: 23
font.family: fontLibian.name
font.pixelSize: 22
opacity: 0.9
horizontalAlignment: Text.AlignHCenter
lineHeight: 18
lineHeightMode: Text.FixedHeight
color: "white"
width: 24
wrapMode: Text.WordWrap
text: Backend.translate(deputyGeneral)
style: Text.Outline
}
} }
Rectangle { Rectangle {
@ -179,13 +220,13 @@ Item {
OpacityMask { OpacityMask {
anchors.fill: photoMask anchors.fill: photoMask
source: generalImage source: generalImgItem
maskSource: photoMask maskSource: photoMask
} }
Colorize { Colorize {
anchors.fill: photoMask anchors.fill: photoMask
source: generalImage source: generalImgItem
saturation: 0 saturation: 0
visible: root.dead visible: root.dead
} }

View File

@ -564,18 +564,20 @@ callbacks["PlayerRunned"] = function(jsonData) {
callbacks["AskForGeneral"] = function(jsonData) { callbacks["AskForGeneral"] = function(jsonData) {
// jsonData: string[] Generals // jsonData: string[] Generals
// TODO: choose multiple generals
let data = JSON.parse(jsonData); let data = JSON.parse(jsonData);
let generals = data[0];
let n = data[1];
roomScene.promptText = Backend.translate("#AskForGeneral"); roomScene.promptText = Backend.translate("#AskForGeneral");
roomScene.state = "replying"; roomScene.state = "replying";
roomScene.popupBox.source = "RoomElement/ChooseGeneralBox.qml"; roomScene.popupBox.source = "RoomElement/ChooseGeneralBox.qml";
let box = roomScene.popupBox.item; let box = roomScene.popupBox.item;
box.choiceNum = 1; box.choiceNum = 1;
box.accepted.connect(() => { box.accepted.connect(() => {
replyToServer(JSON.stringify([box.choices[0]])); replyToServer(JSON.stringify(box.choices));
}); });
for (let i = 0; i < data.length; i++) box.choiceNum = n;
box.generalList.append({ "name": data[i] }); for (let i = 0; i < generals.length; i++)
box.generalList.append({ "name": generals[i] });
box.updatePosition(); box.updatePosition();
} }

View File

@ -73,6 +73,7 @@ Server::Server(QObject *parent) : QObject(parent) {
} }
Server::~Server() { Server::~Server() {
isListening = false;
ServerInstance = nullptr; ServerInstance = nullptr;
m_lobby->deleteLater(); m_lobby->deleteLater();
sqlite3_close(db); sqlite3_close(db);