Resort cards & Surrender (#217)
This commit is contained in:
parent
2e8adf1918
commit
fecc96965d
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Dialogs
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtMultimedia
|
import QtMultimedia
|
||||||
import Fk
|
import Fk
|
||||||
|
@ -71,17 +72,98 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
// tmp
|
// tmp
|
||||||
DelayButton {
|
Button {
|
||||||
id: quitButton
|
id: menuButton
|
||||||
text: "quit"
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
delay: Debugging ? 10 : 1000
|
anchors.rightMargin: 10
|
||||||
onActivated: {
|
text: Backend.translate("Menu")
|
||||||
// ClientInstance.clearPlayers();
|
z: 2
|
||||||
ClientInstance.notifyServer("QuitRoom", "[]");
|
onClicked: {
|
||||||
|
menuContainer.visible || menuContainer.open();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Menu {
|
||||||
|
id: menuContainer
|
||||||
|
x: parent.width - menuButton.width - menuContainer.width - 17
|
||||||
|
width: menuRow.width
|
||||||
|
height: menuRow.height
|
||||||
|
verticalPadding: 0
|
||||||
|
spacing: 7
|
||||||
|
z: 2
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: menuRow
|
||||||
|
spacing: 7
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: surrenderButton
|
||||||
|
text: Backend.translate("Surrender")
|
||||||
|
onClicked: {
|
||||||
|
if (isStarted) {
|
||||||
|
const surrenderCheck = JSON.parse(Backend.callLuaFunction('CheckSurrenderAvailable', [miscStatus.playedTime]));
|
||||||
|
if (!surrenderCheck.length) {
|
||||||
|
surrenderDialog.informativeText = 'surrender is disabled in this mode';
|
||||||
|
} else {
|
||||||
|
surrenderDialog.informativeText = surrenderCheck.map(str => `${Backend.translate(str.text)}(${str.passed ? '√' : '×'})`).join('<br>');
|
||||||
|
}
|
||||||
|
surrenderDialog.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageDialog {
|
||||||
|
id: surrenderDialog
|
||||||
|
title: Backend.translate("Surrender")
|
||||||
|
informativeText: ''
|
||||||
|
buttons: MessageDialog.Ok | MessageDialog.Cancel
|
||||||
|
onButtonClicked: function (button, role) {
|
||||||
|
switch (button) {
|
||||||
|
case MessageDialog.Ok: {
|
||||||
|
const surrenderCheck = JSON.parse(Backend.callLuaFunction('CheckSurrenderAvailable', [miscStatus.playedTime]));
|
||||||
|
if (surrenderCheck.length && !surrenderCheck.find(check => !check.passed)) {
|
||||||
|
ClientInstance.notifyServer("PushRequest", [
|
||||||
|
"surrender", true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MessageDialog.Cancel: {
|
||||||
|
surrenderDialog.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: quitButton
|
||||||
|
text: Backend.translate("Quit")
|
||||||
|
onClicked: {
|
||||||
|
quitDialog.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageDialog {
|
||||||
|
id: quitDialog
|
||||||
|
title: Backend.translate("Quit")
|
||||||
|
informativeText: Backend.translate("Are you sure to quit?")
|
||||||
|
buttons: MessageDialog.Ok | MessageDialog.Cancel
|
||||||
|
onButtonClicked: function (button, role) {
|
||||||
|
switch (button) {
|
||||||
|
case MessageDialog.Ok: {
|
||||||
|
ClientInstance.notifyServer("QuitRoom", "[]");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MessageDialog.Cancel: {
|
||||||
|
quitDialog.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: Backend.translate("Add Robot")
|
text: Backend.translate("Add Robot")
|
||||||
visible: isOwner && !isStarted && !isFull
|
visible: isOwner && !isStarted && !isFull
|
||||||
|
@ -264,6 +346,7 @@ Item {
|
||||||
drank: model.drank
|
drank: model.drank
|
||||||
isOwner: model.isOwner
|
isOwner: model.isOwner
|
||||||
ready: model.ready
|
ready: model.ready
|
||||||
|
surrendered: model.surrendered
|
||||||
|
|
||||||
onSelectedChanged: {
|
onSelectedChanged: {
|
||||||
Logic.updateSelectedTargets(playerid, selected);
|
Logic.updateSelectedTargets(playerid, selected);
|
||||||
|
@ -314,6 +397,7 @@ Item {
|
||||||
MetroButton {
|
MetroButton {
|
||||||
text: Backend.translate("Sort Cards")
|
text: Backend.translate("Sort Cards")
|
||||||
textFont.pixelSize: 28
|
textFont.pixelSize: 28
|
||||||
|
onClicked: Logic.resortHandcards();
|
||||||
}
|
}
|
||||||
MetroButton {
|
MetroButton {
|
||||||
text: Backend.translate("Chat")
|
text: Backend.translate("Chat")
|
||||||
|
@ -709,7 +793,7 @@ Item {
|
||||||
|
|
||||||
MiscStatus {
|
MiscStatus {
|
||||||
id: miscStatus
|
id: miscStatus
|
||||||
anchors.right: quitButton.left
|
anchors.right: menuButton.left
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.rightMargin: 16
|
anchors.rightMargin: 16
|
||||||
anchors.topMargin: 8
|
anchors.topMargin: 8
|
||||||
|
@ -986,6 +1070,7 @@ Item {
|
||||||
drank: 0,
|
drank: 0,
|
||||||
isOwner: false,
|
isOwner: false,
|
||||||
ready: false,
|
ready: false,
|
||||||
|
surrendered: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -204,6 +204,47 @@ function moveCards(moves) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resortHandcards() {
|
||||||
|
if (!dashboard.handcardArea.cards.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const subtypeString2Number = {
|
||||||
|
["none"]: Card.SubtypeNone,
|
||||||
|
["delayed_trick"]: Card.SubtypeDelayedTrick,
|
||||||
|
["weapon"]: Card.SubtypeWeapon,
|
||||||
|
["armor"]: Card.SubtypeArmor,
|
||||||
|
["defensive_horse"]: Card.SubtypeDefensiveRide,
|
||||||
|
["offensive_horse"]: Card.SubtypeOffensiveRide,
|
||||||
|
["treasure"]: Card.SubtypeTreasure,
|
||||||
|
}
|
||||||
|
|
||||||
|
dashboard.handcardArea.cards.sort((prev, next) => {
|
||||||
|
if (prev.type === next.type) {
|
||||||
|
const prevSubtypeNumber = subtypeString2Number[prev.subtype];
|
||||||
|
const nextSubtypeNumber = subtypeString2Number[next.subtype];
|
||||||
|
if (prevSubtypeNumber === nextSubtypeNumber) {
|
||||||
|
const splitedPrevName = prev.name.split('__');
|
||||||
|
const prevTrueName = splitedPrevName[splitedPrevName.length - 1];
|
||||||
|
|
||||||
|
const splitedNextName = next.name.split('__');
|
||||||
|
const nextTrueName = splitedNextName[splitedNextName.length - 1];
|
||||||
|
if (prevTrueName === nextTrueName) {
|
||||||
|
return prev.cid - next.cid;
|
||||||
|
} else {
|
||||||
|
return prevTrueName > nextTrueName ? -1 : 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return prevSubtypeNumber - nextSubtypeNumber;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return prev.type - next.type;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dashboard.handcardArea.updateCardPosition(true);
|
||||||
|
}
|
||||||
|
|
||||||
function setEmotion(id, emotion, isCardId) {
|
function setEmotion(id, emotion, isCardId) {
|
||||||
let path;
|
let path;
|
||||||
if (OS === "Win") {
|
if (OS === "Win") {
|
||||||
|
@ -1336,3 +1377,7 @@ callbacks["AskForLuckCard"] = (j) => {
|
||||||
roomScene.okButton.enabled = true;
|
roomScene.okButton.enabled = true;
|
||||||
roomScene.cancelButton.enabled = true;
|
roomScene.cancelButton.enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callbacks["CancelRequest"] = (jsonData) => {
|
||||||
|
ClientInstance.replyToServer("", "__cancel")
|
||||||
|
}
|
||||||
|
|
|
@ -62,6 +62,9 @@ Item {
|
||||||
card = cards[i];
|
card = cards[i];
|
||||||
card.origX = i * spacing;
|
card.origX = i * spacing;
|
||||||
card.origY = 0;
|
card.origY = 0;
|
||||||
|
card.z = i + 1;
|
||||||
|
card.initialZ = i + 1;
|
||||||
|
card.maxZ = cards.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ Item {
|
||||||
property string name: "slash"
|
property string name: "slash"
|
||||||
property string extension: ""
|
property string extension: ""
|
||||||
property string virt_name: ""
|
property string virt_name: ""
|
||||||
|
property int type: 0
|
||||||
property string subtype: ""
|
property string subtype: ""
|
||||||
property string color: "" // only use when suit is empty
|
property string color: "" // only use when suit is empty
|
||||||
property string footnote: "" // footnote, e.g. "A use card to B"
|
property string footnote: "" // footnote, e.g. "A use card to B"
|
||||||
|
@ -51,6 +52,8 @@ Item {
|
||||||
property bool showDetail: false
|
property bool showDetail: false
|
||||||
property int origX: 0
|
property int origX: 0
|
||||||
property int origY: 0
|
property int origY: 0
|
||||||
|
property int initialZ: 0
|
||||||
|
property int maxZ: 0
|
||||||
property real origOpacity: 1
|
property real origOpacity: 1
|
||||||
// property bool isClicked: false
|
// property bool isClicked: false
|
||||||
property bool moveAborted: false
|
property bool moveAborted: false
|
||||||
|
@ -259,10 +262,12 @@ Item {
|
||||||
if (!draggable) return;
|
if (!draggable) return;
|
||||||
if (hovered) {
|
if (hovered) {
|
||||||
glow.visible = true;
|
glow.visible = true;
|
||||||
root.z++;
|
|
||||||
|
root.z = root.maxZ ? root.maxZ + 1 : root.z + 1;
|
||||||
} else {
|
} else {
|
||||||
glow.visible = false;
|
glow.visible = false;
|
||||||
root.z--;
|
|
||||||
|
root.z = root.initialZ ? root.initialZ : root.z - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -317,6 +322,7 @@ Item {
|
||||||
suit = data.suit;
|
suit = data.suit;
|
||||||
number = data.number;
|
number = data.number;
|
||||||
color = data.color;
|
color = data.color;
|
||||||
|
type = data.type ? data.type : 0
|
||||||
subtype = data.subtype ? data.subtype : "";
|
subtype = data.subtype ? data.subtype : "";
|
||||||
virt_name = data.virt_name ? data.virt_name : "";
|
virt_name = data.virt_name ? data.virt_name : "";
|
||||||
mark = data.mark ?? {};
|
mark = data.mark ?? {};
|
||||||
|
@ -330,6 +336,7 @@ Item {
|
||||||
suit: suit,
|
suit: suit,
|
||||||
number: number,
|
number: number,
|
||||||
color: color,
|
color: color,
|
||||||
|
type: type,
|
||||||
subtype: subtype,
|
subtype: subtype,
|
||||||
virt_name: virt_name,
|
virt_name: virt_name,
|
||||||
mark: mark,
|
mark: mark,
|
||||||
|
|
|
@ -9,7 +9,8 @@ Item {
|
||||||
visible: roundNum || pileNum
|
visible: roundNum || pileNum
|
||||||
|
|
||||||
function getTimeString(time) {
|
function getTimeString(time) {
|
||||||
const s = time % 60;
|
let s = time % 60;
|
||||||
|
s < 10 && (s = '0' + s);
|
||||||
const m = (time - s) / 60;
|
const m = (time - s) / 60;
|
||||||
const h = (time - s - m * 60) / 3600;
|
const h = (time - s - m * 60) / 3600;
|
||||||
return h ? `${h}:${m}:${s}` : `${m}:${s}`;
|
return h ? `${h}:${m}:${s}` : `${m}:${s}`;
|
||||||
|
|
|
@ -52,6 +52,7 @@ Item {
|
||||||
property bool selected: false
|
property bool selected: false
|
||||||
|
|
||||||
property bool playing: false
|
property bool playing: false
|
||||||
|
property bool surrendered: false
|
||||||
onPlayingChanged: {
|
onPlayingChanged: {
|
||||||
if (playing) {
|
if (playing) {
|
||||||
animPlaying.start();
|
animPlaying.start();
|
||||||
|
@ -235,7 +236,7 @@ Item {
|
||||||
anchors.fill: photoMask
|
anchors.fill: photoMask
|
||||||
source: generalImgItem
|
source: generalImgItem
|
||||||
saturation: 0
|
saturation: 0
|
||||||
visible: root.dead
|
visible: root.dead || root.surrendered
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
@ -370,8 +371,8 @@ Item {
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
// id: saveme
|
// id: saveme
|
||||||
visible: root.dead || root.dying
|
visible: root.dead || root.dying || root.surrendered
|
||||||
source: SkinBank.DEATH_DIR + (root.dead ? root.role : "saveme")
|
source: SkinBank.DEATH_DIR + (root.dead ? root.role : root.surrendered ? "surrender" : "saveme")
|
||||||
anchors.centerIn: photoMask
|
anchors.centerIn: photoMask
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,7 @@ function GetCardData(id)
|
||||||
suit = card:getSuitString(),
|
suit = card:getSuitString(),
|
||||||
color = card:getColorString(),
|
color = card:getColorString(),
|
||||||
mark = mark,
|
mark = mark,
|
||||||
|
type = card.type,
|
||||||
subtype = cardSubtypeStrings[card.sub_type]
|
subtype = cardSubtypeStrings[card.sub_type]
|
||||||
}
|
}
|
||||||
if card.skillName ~= "" then
|
if card.skillName ~= "" then
|
||||||
|
@ -625,4 +626,9 @@ function SetObserving(o)
|
||||||
ClientInstance.observing = o
|
ClientInstance.observing = o
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function CheckSurrenderAvailable(playedTime)
|
||||||
|
local curMode = ClientInstance.room_settings.gameMode
|
||||||
|
return json.encode(Fk.game_modes[curMode]:surrenderFunc(playedTime))
|
||||||
|
end
|
||||||
|
|
||||||
dofile "lua/client/i18n/init.lua"
|
dofile "lua/client/i18n/init.lua"
|
||||||
|
|
|
@ -215,6 +215,11 @@ FreeKill使用的是libgit2的C API,与此同时使用Git完成拓展包的下
|
||||||
["seat#8"] = "八号位",
|
["seat#8"] = "八号位",
|
||||||
["@ControledBy"] = "控制者",
|
["@ControledBy"] = "控制者",
|
||||||
|
|
||||||
|
["Menu"] = "菜单",
|
||||||
|
["Surrender"] = "投降",
|
||||||
|
["Quit"] = "退出",
|
||||||
|
["Are you sure to quit?"] = "是否确认退出对局(若对局开始则将计入逃跑次数)?",
|
||||||
|
|
||||||
["Trust"] = "托管",
|
["Trust"] = "托管",
|
||||||
["Sort Cards"] = "牌序",
|
["Sort Cards"] = "牌序",
|
||||||
["Chat"] = "聊天",
|
["Chat"] = "聊天",
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
---@field public maxPlayer integer @ 最大玩家数
|
---@field public maxPlayer integer @ 最大玩家数
|
||||||
---@field public rule TriggerSkill @ 规则(通过技能完成,通常用来为特定角色及特定时机提供触发事件)
|
---@field public rule TriggerSkill @ 规则(通过技能完成,通常用来为特定角色及特定时机提供触发事件)
|
||||||
---@field public logic fun() @ 逻辑(通过function完成,通常用来初始化、分配身份及座次)
|
---@field public logic fun() @ 逻辑(通过function完成,通常用来初始化、分配身份及座次)
|
||||||
|
---@field public surrenderFunc fun()
|
||||||
local GameMode = class("GameMode")
|
local GameMode = class("GameMode")
|
||||||
|
|
||||||
--- 构造函数,不可随意调用。
|
--- 构造函数,不可随意调用。
|
||||||
|
@ -22,4 +23,16 @@ function GameMode:initialize(name, min, max)
|
||||||
self.maxPlayer = math.min(max, 8)
|
self.maxPlayer = math.min(max, 8)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param victim ServerPlayer @ 死者
|
||||||
|
---@return string @ 胜者阵营
|
||||||
|
function GameMode:getWinner(victim)
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param playedTime number @ 游戏时长(单位:秒)
|
||||||
|
---@return table
|
||||||
|
function GameMode:surrenderFunc(playedTime)
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
return GameMode
|
return GameMode
|
||||||
|
|
|
@ -540,5 +540,14 @@ function fk.CreateGameMode(spec)
|
||||||
local ret = GameMode:new(spec.name, spec.minPlayer, spec.maxPlayer)
|
local ret = GameMode:new(spec.name, spec.minPlayer, spec.maxPlayer)
|
||||||
ret.rule = spec.rule
|
ret.rule = spec.rule
|
||||||
ret.logic = spec.logic
|
ret.logic = spec.logic
|
||||||
|
|
||||||
|
if spec.winner_getter then
|
||||||
|
assert(type(spec.winner_getter) == "function")
|
||||||
|
ret.getWinner = spec.winner_getter
|
||||||
|
end
|
||||||
|
if spec.surrender_func then
|
||||||
|
assert(type(spec.surrender_func) == "function")
|
||||||
|
ret.surrenderFunc = spec.surrender_func
|
||||||
|
end
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
|
@ -135,6 +135,23 @@ request_handlers["changeself"] = function(room, id, reqlist)
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
request_handlers["surrender"] = function(room, id, reqlist)
|
||||||
|
local logic = room.logic
|
||||||
|
local curEvent = logic:getCurrentEvent()
|
||||||
|
if curEvent then
|
||||||
|
curEvent:addExitFunc(
|
||||||
|
function()
|
||||||
|
local player = room:getPlayerById(id)
|
||||||
|
player.surrendered = true
|
||||||
|
room:broadcastProperty(player, "surrendered")
|
||||||
|
room:gameOver(Fk.game_modes[room.settings.gameMode]:getWinner(player))
|
||||||
|
end
|
||||||
|
)
|
||||||
|
room.hasSurrendered = true
|
||||||
|
room:doBroadcastNotify("CancelRequest", "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
request_handlers["newroom"] = function(s, id)
|
request_handlers["newroom"] = function(s, id)
|
||||||
s:registerRoom(id)
|
s:registerRoom(id)
|
||||||
end
|
end
|
||||||
|
|
|
@ -2950,7 +2950,7 @@ function Room:getCardsFromPileByRule(pattern, num, fromPile)
|
||||||
if fromPile == "discardPile" then
|
if fromPile == "discardPile" then
|
||||||
pileToSearch = self.discard_pile
|
pileToSearch = self.discard_pile
|
||||||
elseif fromPile == "allPiles" then
|
elseif fromPile == "allPiles" then
|
||||||
pileToSearch = table.clone(self.draw_pile)
|
pileToSearch = table.simpleClone(self.draw_pile)
|
||||||
table.insertTable(pileToSearch, self.discard_pile)
|
table.insertTable(pileToSearch, self.discard_pile)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -92,6 +92,10 @@ local function _waitForReply(player, timeout)
|
||||||
player.request_timeout = timeout
|
player.request_timeout = timeout
|
||||||
player.request_start = start
|
player.request_start = start
|
||||||
if state ~= fk.Player_Online then
|
if state ~= fk.Player_Online then
|
||||||
|
if player.room.hasSurrendered then
|
||||||
|
return "__cancel"
|
||||||
|
end
|
||||||
|
|
||||||
if state ~= fk.Player_Robot then
|
if state ~= fk.Player_Robot then
|
||||||
player.room:checkNoHuman()
|
player.room:checkNoHuman()
|
||||||
player.room:delay(500)
|
player.room:delay(500)
|
||||||
|
@ -117,6 +121,12 @@ local function _waitForReply(player, timeout)
|
||||||
player.serverplayer:setThinking(false)
|
player.serverplayer:setThinking(false)
|
||||||
return ""
|
return ""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if player.room.hasSurrendered then
|
||||||
|
player.serverplayer:setThinking(false)
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
|
||||||
coroutine.yield("__handleRequest", rest)
|
coroutine.yield("__handleRequest", rest)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,33 +1,5 @@
|
||||||
-- SPDX-License-Identifier: GPL-3.0-or-later
|
-- SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
---@param victim ServerPlayer
|
|
||||||
local function getWinner(victim)
|
|
||||||
local room = victim.room
|
|
||||||
local winner = ""
|
|
||||||
local alive = room.alive_players
|
|
||||||
|
|
||||||
if victim.role == "lord" then
|
|
||||||
if #alive == 1 and alive[1].role == "renegade" then
|
|
||||||
winner = "renegade"
|
|
||||||
else
|
|
||||||
winner = "rebel"
|
|
||||||
end
|
|
||||||
elseif victim.role ~= "loyalist" then
|
|
||||||
local lord_win = true
|
|
||||||
for _, p in ipairs(alive) do
|
|
||||||
if p.role == "rebel" or p.role == "renegade" then
|
|
||||||
lord_win = false
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if lord_win then
|
|
||||||
winner = "lord+loyalist"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return winner
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param killer ServerPlayer
|
---@param killer ServerPlayer
|
||||||
local function rewardAndPunish(killer, victim)
|
local function rewardAndPunish(killer, victim)
|
||||||
if killer.dead then return end
|
if killer.dead then return end
|
||||||
|
@ -104,7 +76,7 @@ GameRule = fk.CreateTriggerSkill{
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
[fk.GameOverJudge] = function()
|
[fk.GameOverJudge] = function()
|
||||||
local winner = getWinner(player)
|
local winner = Fk.game_modes[room.settings.gameMode]:getWinner(player)
|
||||||
if winner ~= "" then
|
if winner ~= "" then
|
||||||
room:gameOver(winner)
|
room:gameOver(winner)
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -1114,8 +1114,126 @@ local role_mode = fk.CreateGameMode{
|
||||||
name = "aaa_role_mode", -- just to let it at the top of list
|
name = "aaa_role_mode", -- just to let it at the top of list
|
||||||
minPlayer = 2,
|
minPlayer = 2,
|
||||||
maxPlayer = 8,
|
maxPlayer = 8,
|
||||||
|
winner_getter = function(self, victim)
|
||||||
|
local room = victim.room
|
||||||
|
local winner = ""
|
||||||
|
local alive = table.filter(room.alive_players, function(p)
|
||||||
|
return not p.surrendered
|
||||||
|
end)
|
||||||
|
|
||||||
|
if victim.role == "lord" then
|
||||||
|
if #alive == 1 and alive[1].role == "renegade" then
|
||||||
|
winner = "renegade"
|
||||||
|
else
|
||||||
|
winner = "rebel"
|
||||||
|
end
|
||||||
|
elseif victim.role ~= "loyalist" then
|
||||||
|
local lord_win = true
|
||||||
|
for _, p in ipairs(alive) do
|
||||||
|
if p.role == "rebel" or p.role == "renegade" then
|
||||||
|
lord_win = false
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if lord_win then
|
||||||
|
winner = "lord+loyalist"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return winner
|
||||||
|
end,
|
||||||
|
surrender_func = function(self, playedTime)
|
||||||
|
local roleCheck = false
|
||||||
|
local roleText = ""
|
||||||
|
local roleTable = {
|
||||||
|
{ "lord" },
|
||||||
|
{ "lord", "rebel" },
|
||||||
|
{ "lord", "rebel", "renegade" },
|
||||||
|
{ "lord", "loyalist", "rebel", "renegade" },
|
||||||
|
{ "lord", "loyalist", "rebel", "rebel", "renegade" },
|
||||||
|
{ "lord", "loyalist", "rebel", "rebel", "rebel", "renegade" },
|
||||||
|
{ "lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "renegade" },
|
||||||
|
{ "lord", "loyalist", "loyalist", "rebel", "rebel", "rebel", "rebel", "renegade" },
|
||||||
|
}
|
||||||
|
|
||||||
|
roleTable = roleTable[#Fk:currentRoom().players]
|
||||||
|
|
||||||
|
if Self.role == "renegade" then
|
||||||
|
roleCheck = #Fk:currentRoom().alive_players == 2
|
||||||
|
roleText = "only you and me"
|
||||||
|
elseif Self.role == "rebel" then
|
||||||
|
local rebelNum = #table.filter(roleTable, function(role)
|
||||||
|
return role == "rebel"
|
||||||
|
end)
|
||||||
|
|
||||||
|
local renegadeDead = not table.find(roleTable, function(role)
|
||||||
|
return role == "renegade"
|
||||||
|
end)
|
||||||
|
for _, p in ipairs(Fk:currentRoom().players) do
|
||||||
|
if p.role == "renegade" and p.dead then
|
||||||
|
renegadeDead = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if p ~= Self and p.role == "rebel" then
|
||||||
|
if p:isAlive() then
|
||||||
|
break
|
||||||
|
else
|
||||||
|
rebelNum = rebelNum - 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
roleCheck = renegadeDead and rebelNum == 1
|
||||||
|
roleText = "left one rebel alive"
|
||||||
|
else
|
||||||
|
if Self.role == "loyalist" then
|
||||||
|
return { { text = "loyalist never surrender", passed = false } }
|
||||||
|
else
|
||||||
|
if #Fk:currentRoom().alive_players == 2 then
|
||||||
|
roleCheck = true
|
||||||
|
else
|
||||||
|
local lordNum = #table.filter(roleTable, function(role)
|
||||||
|
return role == "lord" or role == "loyalist"
|
||||||
|
end)
|
||||||
|
|
||||||
|
local renegadeDead = not table.find(roleTable, function(role)
|
||||||
|
return role == "renegade"
|
||||||
|
end)
|
||||||
|
for _, p in ipairs(Fk:currentRoom().players) do
|
||||||
|
if p.role == "renegade" and p.dead then
|
||||||
|
renegadeDead = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if p ~= Self and (p.role == "lord" or p.role == "loyalist") then
|
||||||
|
if p:isAlive() then
|
||||||
|
break
|
||||||
|
else
|
||||||
|
lordNum = lordNum - 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
roleCheck = renegadeDead and lordNum == 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
roleText = "left you alive"
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
{ text = "time limitation: 5 min", passed = playedTime >= 300 },
|
||||||
|
{ text = roleText, passed = roleCheck },
|
||||||
|
}
|
||||||
|
end,
|
||||||
}
|
}
|
||||||
extension:addGameMode(role_mode)
|
extension:addGameMode(role_mode)
|
||||||
|
Fk:loadTranslationTable{
|
||||||
|
["time limitation: 5 min"] = "游戏时长达到5分钟",
|
||||||
|
["only you and me"] = "仅剩你和主公存活",
|
||||||
|
["left one rebel alive"] = "反贼仅剩你存活且不存在存活内奸",
|
||||||
|
["left you alive"] = "主忠方仅剩你存活且其他阵营仅剩一方",
|
||||||
|
["loyalist never surrender"] = "忠臣永不投降!",
|
||||||
|
}
|
||||||
|
|
||||||
local anjiang = General(extension, "anjiang", "unknown", 5)
|
local anjiang = General(extension, "anjiang", "unknown", 5)
|
||||||
anjiang.gender = General.Agender
|
anjiang.gender = General.Agender
|
||||||
|
|
Loading…
Reference in New Issue