杀光侧栏 只留战报一个
Qml标记,以及一个割圆demo
自由选将增加搜索功能
room:setBanner,相当于公共标记了,客户端可以Fk:currentRoom():getBanner拿(当然服务端也可)
改掉两个很蠢的命名
This commit is contained in:
notify 2023-12-06 21:07:35 +08:00 committed by GitHub
parent 8afe5122d7
commit 9a9fc9c105
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 584 additions and 393 deletions

View File

@ -3,6 +3,7 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Fk.RoomElement
Flickable { Flickable {
id: root id: root
@ -14,35 +15,47 @@ Flickable {
contentHeight: details.height contentHeight: details.height
ScrollBar.vertical: ScrollBar {} ScrollBar.vertical: ScrollBar {}
ColumnLayout { RowLayout {
id: details id: details
width: parent.width - 40 width: parent.width - 40
x: 20 x: 20
spacing: 20
// TODO: player details CardItem {
Text { id: cardPic
id: screenName Layout.alignment: Qt.AlignTop
Layout.fillWidth: true Layout.topMargin: 10
font.pixelSize: 18 cid: 0
} }
TextEdit { ColumnLayout {
id: skillDesc Text {
id: screenName
Layout.fillWidth: true
font.pixelSize: 18
color: "#E4D5A0"
}
Layout.fillWidth: true TextEdit {
font.pixelSize: 18 id: skillDesc
readOnly: true Layout.fillWidth: true
selectByKeyboard: true font.pixelSize: 18
selectByMouse: false color: "#E4D5A0"
wrapMode: TextEdit.WordWrap
textFormat: TextEdit.RichText readOnly: true
selectByKeyboard: true
selectByMouse: false
wrapMode: TextEdit.WordWrap
textFormat: TextEdit.RichText
}
} }
} }
onExtra_dataChanged: { onExtra_dataChanged: {
const card = extra_data.card; const card = extra_data.card;
if (!card) return; if (!card) return;
cardPic.setData(card.toData());
const name = card.virt_name ? card.virt_name : card.name; const name = card.virt_name ? card.virt_name : card.name;
screenName.text = Backend.translate(name); screenName.text = Backend.translate(name);
skillDesc.text = Backend.translate(":" + name); skillDesc.text = Backend.translate(":" + name);

View File

@ -24,6 +24,7 @@ Item {
text: Backend.translate("Back") text: Backend.translate("Back")
onClicked: stack.pop() onClicked: stack.pop()
} }
Label { Label {
text: Backend.translate("Enable free assign") text: Backend.translate("Enable free assign")
elide: Label.ElideRight elide: Label.ElideRight
@ -31,8 +32,29 @@ Item {
verticalAlignment: Qt.AlignVCenter verticalAlignment: Qt.AlignVCenter
Layout.fillWidth: true Layout.fillWidth: true
} }
TextField {
id: word
placeholderText: "Search..."
clip: true
verticalAlignment: Qt.AlignVCenter
background: Rectangle {
implicitHeight: 16
implicitWidth: 120
color: "transparent"
}
}
ToolButton { ToolButton {
opacity: 0 text: Backend.translate("Search")
enabled: word.text !== ""
onClicked: {
if (stack.depth > 1) stack.pop();
generalModel = JSON.parse(Backend.callLuaFunction("SearchAllGenerals",
[word.text]));
stack.push(generalList);
word.text = "";
}
} }
} }
} }
@ -58,15 +80,16 @@ Item {
ScrollBar.vertical: ScrollBar {} ScrollBar.vertical: ScrollBar {}
model: packages model: packages
clip: true clip: true
cellWidth: width / 3 cellWidth: width / 5
cellHeight: 40 cellHeight: 40
delegate: ItemDelegate { delegate: ItemDelegate {
width: listView.width / 3 width: listView.width / 5
height: 40 height: 40
Text { Text {
text: Backend.translate(name) text: Backend.translate(name)
color: "#E4D5A0"
anchors.centerIn: parent anchors.centerIn: parent
} }

View File

@ -3,6 +3,8 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Fk.Pages
import Fk.RoomElement
Flickable { Flickable {
id: root id: root
@ -24,29 +26,18 @@ Flickable {
Text { Text {
id: screenName id: screenName
font.pixelSize: 18 font.pixelSize: 18
color: "#E4D5A0"
} }
Text { Text {
id: playerGameData id: playerGameData
Layout.fillWidth: true Layout.fillWidth: true
font.pixelSize: 18 font.pixelSize: 18
} color: "#E4D5A0"
TextEdit {
id: skillDesc
Layout.fillWidth: true
font.pixelSize: 18
readOnly: true
selectByKeyboard: true
selectByMouse: false
wrapMode: TextEdit.WordWrap
textFormat: TextEdit.RichText
} }
RowLayout { RowLayout {
Button { MetroButton {
text: Backend.translate("Give Flower") text: Backend.translate("Give Flower")
onClicked: { onClicked: {
enabled = false; enabled = false;
@ -55,7 +46,7 @@ Flickable {
} }
} }
Button { MetroButton {
text: Backend.translate("Give Egg") text: Backend.translate("Give Egg")
onClicked: { onClicked: {
enabled = false; enabled = false;
@ -68,7 +59,7 @@ Flickable {
} }
} }
Button { MetroButton {
text: Backend.translate("Give Wine") text: Backend.translate("Give Wine")
enabled: Math.random() < 0.3 enabled: Math.random() < 0.3
onClicked: { onClicked: {
@ -78,7 +69,7 @@ Flickable {
} }
} }
Button { MetroButton {
text: Backend.translate("Give Shoe") text: Backend.translate("Give Shoe")
enabled: Math.random() < 0.3 enabled: Math.random() < 0.3
onClicked: { onClicked: {
@ -87,10 +78,8 @@ Flickable {
root.finish(); root.finish();
} }
} }
}
RowLayout { MetroButton {
Button {
text: config.blockedUsers.indexOf(screenName.text) === -1 ? Backend.translate("Block Chatter") : Backend.translate("Unblock Chatter") text: config.blockedUsers.indexOf(screenName.text) === -1 ? Backend.translate("Block Chatter") : Backend.translate("Unblock Chatter")
enabled: pid !== Self.id && pid > 0 enabled: pid !== Self.id && pid > 0
onClicked: { onClicked: {
@ -103,7 +92,7 @@ Flickable {
config.blockedUsersChanged(); config.blockedUsersChanged();
} }
} }
Button { MetroButton {
text: Backend.translate("Kick From Room") text: Backend.translate("Kick From Room")
visible: !roomScene.isStarted && roomScene.isOwner visible: !roomScene.isStarted && roomScene.isOwner
enabled: pid !== Self.id enabled: pid !== Self.id
@ -113,6 +102,41 @@ Flickable {
} }
} }
} }
RowLayout {
spacing: 20
ColumnLayout {
Layout.alignment: Qt.AlignTop
Layout.topMargin: 16
GeneralCardItem {
id: mainChara
name: "caocao"
visible: name !== ""
}
GeneralCardItem {
id: deputyChara
name: "caocao"
visible: name !== ""
}
}
TextEdit {
id: skillDesc
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
Layout.topMargin: 10
font.pixelSize: 18
color: "#E4D5A0"
readOnly: true
selectByKeyboard: true
selectByMouse: false
wrapMode: TextEdit.WordWrap
textFormat: TextEdit.RichText
}
}
} }
function givePresent(p) { function givePresent(p) {
@ -136,6 +160,8 @@ Flickable {
root.pid = id; root.pid = id;
screenName.text = extra_data.photo.screenName; screenName.text = extra_data.photo.screenName;
mainChara.name = extra_data.photo.general;
deputyChara.name = extra_data.photo.deputyGeneral;
if (!config.observing) { if (!config.observing) {
const gamedata = JSON.parse(Backend.callLuaFunction("GetPlayerGameData", [id])); const gamedata = JSON.parse(Backend.callLuaFunction("GetPlayerGameData", [id]));

View File

@ -28,7 +28,7 @@ Item {
ColumnLayout { ColumnLayout {
Text { text: Backend.translate(gname) } Text { text: Backend.translate(gname) }
GridLayout { GridLayout {
columns: 3 columns: 6
Repeater { Repeater {
model: JSON.parse(Backend.callLuaFunction("GetSameGenerals", [gname])) model: JSON.parse(Backend.callLuaFunction("GetSameGenerals", [gname]))

View File

@ -46,16 +46,7 @@ Item {
config.disabledGenerals = []; config.disabledGenerals = [];
} }
} }
}
Text {
Layout.fillWidth: true
Layout.margins: 8
wrapMode: Text.WrapAnywhere
text: Backend.translate("Help_Ban_List")
}
RowLayout {
Button { Button {
text: Backend.translate("Export") text: Backend.translate("Export")
onClicked: { onClicked: {
@ -91,12 +82,19 @@ Item {
} }
} }
Text {
Layout.fillWidth: true
Layout.margins: 8
wrapMode: Text.WrapAnywhere
text: Backend.translate("Help_Ban_List")
}
GridView { GridView {
id: listView id: listView
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
clip: true clip: true
cellWidth: width / 2 cellWidth: width / 4
cellHeight: 24 cellHeight: 24
model: config.disabledGenerals model: config.disabledGenerals
delegate: Text { delegate: Text {

View File

@ -16,6 +16,7 @@ Item {
transformOrigin: Item.BottomLeft transformOrigin: Item.BottomLeft
rotation: 90 rotation: 90
width: root.height width: root.height
background: Rectangle { color: "#EEEEEEEE" }
TabButton { TabButton {
text: Backend.translate("General Settings") text: Backend.translate("General Settings")
} }

View File

@ -15,6 +15,7 @@ Item {
transformOrigin: Item.BottomLeft transformOrigin: Item.BottomLeft
rotation: 90 rotation: 90
width: root.height width: root.height
background: Rectangle { color: "#EEEEEEEE" }
TabButton { TabButton {
text: Backend.translate("Userinfo Settings") text: Backend.translate("Userinfo Settings")
} }

View File

@ -28,24 +28,6 @@ Flickable {
} }
} }
RowLayout {
anchors.rightMargin: 8
spacing: 16
Text {
text: Backend.translate("Player num")
}
SpinBox {
id: playerNum
from: 2
to: 12
value: config.preferedPlayerNum
onValueChanged: {
config.preferedPlayerNum = value;
}
}
}
RowLayout { RowLayout {
anchors.rightMargin: 8 anchors.rightMargin: 8
spacing: 16 spacing: 16
@ -69,12 +51,33 @@ Flickable {
} }
} }
RowLayout { GridLayout {
anchors.rightMargin: 8 anchors.rightMargin: 8
spacing: 16 rowSpacing: 20
columnSpacing: 20
columns: 4
Text {
text: Backend.translate("Player num")
}
Text { Text {
text: Backend.translate("Select generals num") text: Backend.translate("Select generals num")
} }
Text {
text: Backend.translate("Operation timeout")
}
Text {
text: Backend.translate("Luck Card Times")
}
SpinBox {
id: playerNum
from: 2
to: 12
value: config.preferedPlayerNum
onValueChanged: {
config.preferedPlayerNum = value;
}
}
SpinBox { SpinBox {
id: generalNum id: generalNum
from: 3 from: 3
@ -85,6 +88,25 @@ Flickable {
config.preferredGeneralNum = value; config.preferredGeneralNum = value;
} }
} }
SpinBox {
from: 10
to: 60
editable: true
value: config.preferredTimeout
onValueChanged: {
config.preferredTimeout = value;
}
}
SpinBox {
from: 0
to: 8
value: config.preferredLuckTime
onValueChanged: {
config.preferredLuckTime = value;
}
}
} }
Text { Text {
@ -100,41 +122,6 @@ Flickable {
color: "red" color: "red"
} }
RowLayout {
anchors.rightMargin: 8
spacing: 16
Text {
text: Backend.translate("Operation timeout")
}
SpinBox {
from: 10
to: 60
editable: true
value: config.preferredTimeout
onValueChanged: {
config.preferredTimeout = value;
}
}
}
RowLayout {
anchors.rightMargin: 8
spacing: 16
Text {
text: Backend.translate("Luck Card Times")
}
SpinBox {
from: 0
to: 8
value: config.preferredLuckTime
onValueChanged: {
config.preferredLuckTime = value;
}
}
}
RowLayout { RowLayout {
anchors.rightMargin: 8 anchors.rightMargin: 8
spacing: 16 spacing: 16
@ -150,16 +137,20 @@ Flickable {
} }
} }
Switch { RowLayout {
id: freeAssignCheck anchors.rightMargin: 8
checked: Debugging ? true : false spacing: 16
text: Backend.translate("Enable free assign") Switch {
} id: freeAssignCheck
checked: Debugging ? true : false
text: Backend.translate("Enable free assign")
}
Switch { Switch {
id: deputyCheck id: deputyCheck
checked: Debugging ? true : false checked: Debugging ? true : false
text: Backend.translate("Enable deputy general") text: Backend.translate("Enable deputy general")
}
} }
RowLayout { RowLayout {

View File

@ -52,7 +52,7 @@ Flickable {
} }
GridLayout { GridLayout {
columns: 2 columns: 4
Repeater { Repeater {
id: gpacks id: gpacks
@ -100,7 +100,7 @@ Flickable {
} }
GridLayout { GridLayout {
columns: 2 columns: 4
Repeater { Repeater {
id: cpacks id: cpacks

View File

@ -224,19 +224,25 @@ Item {
} }
} }
Drawer { Popup {
id: lobby_drawer id: lobby_drawer
width: parent.width * 0.4 / mainWindow.scale width: realMainWin.width * 0.8
height: parent.height / mainWindow.scale height: realMainWin.height * 0.8
dim: false anchors.centerIn: parent
clip: true background: Rectangle {
dragMargin: 0 color: "#EEEEEEEE"
scale: mainWindow.scale radius: 5
transformOrigin: Item.TopLeft border.color: "#A6967A"
border.width: 1
}
Loader { Loader {
id: lobby_dialog id: lobby_dialog
anchors.fill: parent anchors.centerIn: parent
width: parent.width / mainWindow.scale
height: parent.height / mainWindow.scale
scale: mainWindow.scale
clip: true
onSourceChanged: { onSourceChanged: {
if (item === null) if (item === null)
return; return;

View File

@ -41,6 +41,9 @@ Item {
TapHandler { TapHandler {
id: mouse id: mouse
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.NoButton
gesturePolicy: TapHandler.WithinBounds
onTapped: if (parent.enabled) parent.clicked() onTapped: if (parent.enabled) parent.clicked()
} }

View File

@ -8,6 +8,7 @@ import QtMultimedia
import Fk import Fk
import Fk.Common import Fk.Common
import Fk.RoomElement import Fk.RoomElement
import Fk.PhotoElement as PhotoElement
import "RoomLogic.js" as Logic import "RoomLogic.js" as Logic
Item { Item {
@ -37,6 +38,7 @@ Item {
property alias drawPile: drawPile property alias drawPile: drawPile
property alias skillInteraction: skillInteraction property alias skillInteraction: skillInteraction
property alias miscStatus: miscStatus property alias miscStatus: miscStatus
property alias banner: banner
property var selected_targets: [] property var selected_targets: []
property string responding_card property string responding_card
@ -55,7 +57,6 @@ Item {
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
} }
/*
MediaPlayer { MediaPlayer {
id: bgm id: bgm
source: config.bgmFile source: config.bgmFile
@ -69,15 +70,14 @@ Item {
volume: config.bgmVolume / 100 volume: config.bgmVolume / 100
} }
} }
*/
onIsStartedChanged: { onIsStartedChanged: {
if (isStarted) { if (isStarted) {
// bgm.play(); bgm.play();
canKickOwner = false; canKickOwner = false;
kickOwnerTimer.stop(); kickOwnerTimer.stop();
} else { } else {
// bgm.stop(); bgm.stop();
} }
} }
@ -533,14 +533,6 @@ Item {
} }
} }
GlowText {
text: Backend.translate("Observing ...")
visible: config.observing && !config.replaying
color: "#4B83CD"
font.family: fontLi2.name
font.pixelSize: 48
}
Rectangle { Rectangle {
id: replayControls id: replayControls
visible: config.replaying visible: config.replaying
@ -855,7 +847,7 @@ Item {
Drawer { Drawer {
id: roomDrawer id: roomDrawer
width: parent.width * 0.3 / mainWindow.scale width: parent.width * 0.36 / mainWindow.scale
height: parent.height / mainWindow.scale height: parent.height / mainWindow.scale
dim: false dim: false
clip: true clip: true
@ -901,20 +893,25 @@ Item {
} }
} }
Drawer { Popup {
id: cheatDrawer id: cheatDrawer
edge: Qt.RightEdge width: realMainWin.width * 0.60
width: parent.width * 0.4 / mainWindow.scale height: realMainWin.height * 0.8
height: parent.height / mainWindow.scale anchors.centerIn: parent
dim: false background: Rectangle {
clip: true color: "#CC2E2C27"
dragMargin: 0 radius: 5
scale: mainWindow.scale border.color: "#A6967A"
transformOrigin: Item.TopRight border.width: 1
}
Loader { Loader {
id: cheatLoader id: cheatLoader
anchors.fill: parent anchors.centerIn: parent
width: parent.width / mainWindow.scale
height: parent.height / mainWindow.scale
scale: mainWindow.scale
clip: true
onSourceChanged: { onSourceChanged: {
if (item === null) if (item === null)
return; return;
@ -931,6 +928,20 @@ Item {
anchors.fill: parent anchors.fill: parent
} }
Rectangle {
anchors.fill: dashboard
visible: config.observing && !config.replaying
color: "transparent"
GlowText {
anchors.centerIn: parent
text: Backend.translate("Observing ...")
color: "#4B83CD"
font.family: fontLi2.name
font.pixelSize: 48
}
}
/* 西使
Rectangle { Rectangle {
id: easyChat id: easyChat
width: parent.width width: parent.width
@ -967,6 +978,16 @@ Item {
} }
} }
Shortcut {
sequence: "T"
onActivated: {
easyChat.visible = true;
easyChatEdit.enabled = true;
easyChatEdit.forceActiveFocus();
}
}
*/
MiscStatus { MiscStatus {
id: miscStatus id: miscStatus
anchors.right: menuButton.left anchors.right: menuButton.left
@ -975,20 +996,20 @@ Item {
anchors.topMargin: 8 anchors.topMargin: 8
} }
PhotoElement.MarkArea {
id: banner
x: 12; y: 12
width: (roomScene.width - 175 * 0.75 * 7) / 4 + 175 - 16
transformOrigin: Item.TopLeft
scale: 0.75
bgColor: "#BB838AEA"
}
Danmaku { Danmaku {
id: danmaku id: danmaku
width: parent.width width: parent.width
} }
Shortcut {
sequence: "T"
onActivated: {
easyChat.visible = true;
easyChatEdit.enabled = true;
easyChatEdit.forceActiveFocus();
}
}
Shortcut { Shortcut {
sequence: "D" sequence: "D"
property bool show_distance: false property bool show_distance: false

View File

@ -1012,7 +1012,7 @@ callbacks["AskForChoice"] = (jsonData) => {
}); });
} }
callbacks["AskForCheck"] = (jsonData) => { callbacks["AskForChoices"] = (jsonData) => {
// jsonData: [ string[] choices, string skill ] // jsonData: [ string[] choices, string skill ]
// TODO: multiple choices, e.g. benxi_ol // TODO: multiple choices, e.g. benxi_ol
const data = JSON.parse(jsonData); const data = JSON.parse(jsonData);
@ -1025,7 +1025,7 @@ callbacks["AskForCheck"] = (jsonData) => {
const prompt = data[5]; const prompt = data[5];
const detailed = data[6]; const detailed = data[6];
if (prompt === "") { if (prompt === "") {
roomScene.promptText = Backend.translate("#AskForCheck") roomScene.promptText = Backend.translate("#AskForChoices")
.arg(Backend.translate(skill_name)); .arg(Backend.translate(skill_name));
} else { } else {
roomScene.setPrompt(processPrompt(prompt), true); roomScene.setPrompt(processPrompt(prompt), true);
@ -1335,7 +1335,7 @@ callbacks["SetPlayerMark"] = (jsonData) => {
const data = JSON.parse(jsonData); const data = JSON.parse(jsonData);
const player = getPhoto(data[0]); const player = getPhoto(data[0]);
const mark = data[1]; const mark = data[1];
const value = data[2] instanceof Array ? data[2] : data[2].toString(); const value = data[2] instanceof Object ? data[2] : data[2].toString();
let area = mark.startsWith("@!") ? player.picMarkArea : player.markArea; let area = mark.startsWith("@!") ? player.picMarkArea : player.markArea;
if (data[2] === 0) { if (data[2] === 0) {
area.removeMark(mark); area.removeMark(mark);
@ -1344,6 +1344,18 @@ callbacks["SetPlayerMark"] = (jsonData) => {
} }
} }
callbacks["SetBanner"] = (jsonData) => {
const data = JSON.parse(jsonData);
const mark = data[0];
const value = data[1] instanceof Object ? data[1] : data[1].toString();
let area = roomScene.banner;
if (data[1] === 0) {
area.removeMark(mark);
} else {
area.setMark(mark, mark.startsWith("@@") ? "" : value);
}
}
callbacks["Animate"] = (jsonData) => { callbacks["Animate"] = (jsonData) => {
// jsonData: [Object object] // jsonData: [Object object]
const data = JSON.parse(jsonData); const data = JSON.parse(jsonData);
@ -1581,7 +1593,7 @@ callbacks["ChangeSelf"] = (j) => {
callbacks["AskForLuckCard"] = (j) => { callbacks["AskForLuckCard"] = (j) => {
// jsonData: int time // jsonData: int time
if (config.replaying) return; if (config.observing || config.replaying) return;
const time = parseInt(j); const time = parseInt(j);
roomScene.setPrompt(Backend.translate("#AskForLuckCard").arg(time), true); roomScene.setPrompt(Backend.translate("#AskForLuckCard").arg(time), true);
roomScene.state = "replying"; roomScene.state = "replying";

View File

@ -6,6 +6,7 @@ import QtQuick.Layouts
Item { Item {
id: root id: root
width: 138 width: 138
property var bgColor: "#3C3229"
ListModel { ListModel {
id: markList id: markList
@ -15,7 +16,7 @@ Item {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
width: parent.width width: parent.width
height: parent.height height: parent.height
color: "#3C3229" color: bgColor
opacity: 0.8 opacity: 0.8
radius: 4 radius: 4
border.color: "white" border.color: "white"
@ -68,7 +69,21 @@ Item {
} else { } else {
params.cardNames = data; params.cardNames = data;
} }
} else if (mark_name.startsWith('@[')) {
// @[xxx]yyy 怀qml
const close_br = mark_name.indexOf(']');
if (close_br === -1) return;
const mark_type = mark_name.slice(2, close_br);
const _data = (mark_extra);
let data = JSON.parse(Backend.callLuaFunction("GetQmlMark", [mark_type, mark_name, JSON.stringify(_data)]));
if (data && data.qml_path) {
params.data = _data;
roomScene.startCheat("../../" + data.qml_path, params);
}
return;
} else { } else {
if (!root.parent.playerid) return;
let data = JSON.parse(Backend.callLuaFunction("GetPile", [root.parent.playerid, mark_name])); let data = JSON.parse(Backend.callLuaFunction("GetPile", [root.parent.playerid, mark_name]));
data = data.filter((e) => e !== -1); data = data.filter((e) => e !== -1);
if (data.length === 0) if (data.length === 0)
@ -103,6 +118,15 @@ Item {
if (mark.startsWith('@$') || mark.startsWith('@&')) { if (mark.startsWith('@$') || mark.startsWith('@&')) {
special_value += data.length; special_value += data.length;
data = data.join(','); data = data.join(',');
} else if (mark.startsWith('@[')) {
const close_br = mark.indexOf(']');
if (close_br !== -1) {
const mark_type = mark.slice(2, close_br);
const _data = JSON.parse(Backend.callLuaFunction("GetQmlMark", [mark_type, mark, JSON.stringify(data)]));
if (_data && _data.text) {
special_value = _data.text;
}
}
} else { } else {
data = data instanceof Array ? data.map((markText) => Backend.translate(markText)).join(' ') : Backend.translate(data); data = data instanceof Array ? data.map((markText) => Backend.translate(markText)).join(' ') : Backend.translate(data);
} }

View File

@ -0,0 +1,39 @@
import QtQuick
import Qt5Compat.GraphicalEffects
Item {
property alias text: pileName.text
GlowText {
id: pileName
horizontalAlignment: Text.AlignHCenter
width: parent.width
font.family: fontLibian.name
color: "#E4D5A0"
font.pixelSize: 30
font.weight: Font.Medium
glow.color: "black"
glow.spread: 0.3
glow.radius: 5
}
LinearGradient {
anchors.fill: pileName
source: pileName
gradient: Gradient {
GradientStop {
position: 0
color: "#FEF7C2"
}
GradientStop {
position: 0.5
color: "#D2AD4A"
}
GradientStop {
position: 1
color: "#BE9878"
}
}
}
}

View File

@ -5,7 +5,7 @@ import QtQuick.Layouts
import QtQuick.Controls import QtQuick.Controls
import Qt5Compat.GraphicalEffects import Qt5Compat.GraphicalEffects
Item { ColumnLayout {
id: root id: root
anchors.fill: parent anchors.fill: parent
property var extra_data: ({}) // unused property var extra_data: ({}) // unused
@ -14,51 +14,40 @@ Item {
Text { Text {
text: Backend.translate("Handcard selector") text: Backend.translate("Handcard selector")
width: parent.width Layout.fillWidth: true
anchors.topMargin: 6
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
font.pixelSize: 16 font.pixelSize: 18
color: "#E4D5A0"
} }
Flickable { GridView {
id: flickableContainer id: cardsList
ScrollBar.vertical: ScrollBar {} cellWidth: 93 * 0.9 + 4
anchors.horizontalCenter: parent.horizontalCenter cellHeight: 130 * 0.9 + 4
anchors.top: parent.top Layout.preferredWidth: root.width - root.width % 88
anchors.topMargin: 40 Layout.fillHeight: true
flickableDirection: Flickable.VerticalFlick Layout.alignment: Qt.AlignHCenter
width: parent.width - 20
height: parent.height - 40
contentWidth: cardsList.width
contentHeight: cardsList.height
clip: true clip: true
GridLayout { model: cards
id: cardsList
columns: Math.floor(flickableContainer.width / 90)
Repeater { delegate: CardItem {
model: cards width: 93 * 0.9
height: 130 * 0.9
CardItem { chosenInBox: modelData.chosen
width: 93 * 0.9 onClicked: {
height: 130 * 0.9 const clist = roomScene.dashboard.handcardArea.cards;
chosenInBox: modelData.chosen for (let cd of clist) {
onClicked: { if (cd.cid == cid) {
const clist = roomScene.dashboard.handcardArea.cards; cd.selected = !cd.selected;
for (let cd of clist) { cd.clicked();
if (cd.cid == cid) { finish();
cd.selected = !cd.selected;
cd.clicked();
finish();
}
}
}
Component.onCompleted: {
setData(JSON.parse(Backend.callLuaFunction("GetCardData", [modelData.cid])));
} }
} }
} }
Component.onCompleted: {
setData(JSON.parse(Backend.callLuaFunction("GetCardData", [modelData.cid])));
}
} }
} }

View File

@ -153,7 +153,7 @@ CardItem {
height: 80 height: 80
x: 2 x: 2
y: lineCount > 6 ? 30 : 34 y: lineCount > 6 ? 30 : 34
text: Backend.translate(name) text: name !== "" ? Backend.translate(name) : "nil"
visible: Backend.translate(name).length <= 6 && detailed visible: Backend.translate(name).length <= 6 && detailed
color: "white" color: "white"
font.family: fontLibian.name font.family: fontLibian.name

View File

@ -2,87 +2,34 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Controls
import Qt5Compat.GraphicalEffects
Item { ColumnLayout {
id: root id: root
anchors.fill: parent anchors.fill: parent
property var extra_data: ({}) property var extra_data: ({})
signal finish() signal finish()
Rectangle { BigGlowText {
anchors.fill: parent Layout.fillWidth: true
color: "black" Layout.preferredHeight: childrenRect.height + 4
GlowText { text: Backend.translate(extra_data.name)
id: pileName }
text: Backend.translate(extra_data.name)
width: parent.width
anchors.topMargin: 10
horizontalAlignment: Text.AlignHCenter
font.family: fontLibian.name
color: "#E4D5A0"
font.pixelSize: 30
font.weight: Font.Medium
glow.color: "black"
glow.spread: 0.3
glow.radius: 5
}
LinearGradient { GridView {
anchors.fill: pileName cellWidth: 93 + 4
source: pileName cellHeight: 130 + 4
gradient: Gradient { Layout.preferredWidth: root.width - root.width % 97
GradientStop { Layout.fillHeight: true
position: 0 Layout.alignment: Qt.AlignHCenter
color: "#FEF7C2" clip: true
}
GradientStop { model: extra_data.ids || extra_data.cardNames
position: 0.5
color: "#D2AD4A"
}
GradientStop { delegate: GeneralCardItem {
position: 1 id: cardItem
color: "#BE9878" autoBack: false
} name: modelData
}
}
Flickable {
id: flickableContainer
ScrollBar.vertical: ScrollBar {}
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 40
flickableDirection: Flickable.VerticalFlick
width: parent.width - 20
height: parent.height - 40
contentWidth: cardsList.width
contentHeight: cardsList.height
clip: true
ColumnLayout {
id: cardsList
GridLayout {
columns: Math.floor(flickableContainer.width / 100)
Repeater {
model: extra_data.ids || extra_data.cardNames
GeneralCardItem {
id: cardItem
// width: (flickableContainer.width - 15) / 4
// height: cardItem.width * 1.4
autoBack: false
name: modelData
}
}
}
}
} }
} }
} }

View File

@ -2,98 +2,45 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Controls
import Qt5Compat.GraphicalEffects
Item { ColumnLayout {
id: root id: root
anchors.fill: parent anchors.fill: parent
property var extra_data: ({}) property var extra_data: ({})
signal finish() signal finish()
Rectangle { BigGlowText {
anchors.fill: parent Layout.fillWidth: true
color: "black" Layout.preferredHeight: childrenRect.height + 4
GlowText { text: Backend.translate(extra_data.name)
id: pileName }
text: Backend.translate(extra_data.name)
width: parent.width
anchors.topMargin: 10
horizontalAlignment: Text.AlignHCenter
font.family: fontLibian.name
color: "#E4D5A0"
font.pixelSize: 30
font.weight: Font.Medium
glow.color: "black"
glow.spread: 0.3
glow.radius: 5
}
LinearGradient { GridView {
anchors.fill: pileName cellWidth: 93 + 4
source: pileName cellHeight: 130 + 4
gradient: Gradient { Layout.preferredWidth: root.width - root.width % 97
GradientStop { Layout.fillHeight: true
position: 0 Layout.alignment: Qt.AlignHCenter
color: "#FEF7C2" clip: true
}
model: extra_data.ids || extra_data.cardNames
GradientStop {
position: 0.5 delegate: CardItem {
color: "#D2AD4A" id: cardItem
} autoBack: false
Component.onCompleted: {
GradientStop { let data = {}
position: 1 if (extra_data.ids) {
color: "#BE9878" data = JSON.parse(Backend.callLuaFunction("GetCardData", [modelData]));
} } else {
} data.cid = 0;
} data.name = modelData;
data.suit = '';
Flickable { data.number = 0;
id: flickableContainer data.color = '';
ScrollBar.vertical: ScrollBar {}
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 40
flickableDirection: Flickable.VerticalFlick
width: parent.width - 20
height: parent.height - 40
contentWidth: cardsList.width
contentHeight: cardsList.height
clip: true
ColumnLayout {
id: cardsList
GridLayout {
columns: 4
Repeater {
model: extra_data.ids || extra_data.cardNames
CardItem {
id: cardItem
width: (flickableContainer.width - 15) / 4
height: cardItem.width * 1.4
autoBack: false
Component.onCompleted: {
let data = {}
if (extra_data.ids) {
data = JSON.parse(Backend.callLuaFunction("GetCardData", [modelData]));
} else {
data.cid = 0;
data.name = modelData;
data.suit = '';
data.number = 0;
data.color = '';
}
setData(data);
}
}
}
} }
setData(data);
} }
} }
} }

View File

@ -8,6 +8,7 @@ Dashboard 1.0 Dashboard.qml
GameOverBox 1.0 GameOverBox.qml GameOverBox 1.0 GameOverBox.qml
GeneralCardItem 1.0 GeneralCardItem.qml GeneralCardItem 1.0 GeneralCardItem.qml
GlowText 1.0 GlowText.qml GlowText 1.0 GlowText.qml
BigGlowText 1.0 BigGlowText.qml
GraphicsBox 1.0 GraphicsBox.qml GraphicsBox 1.0 GraphicsBox.qml
GuanxingBox 1.0 GuanxingBox.qml GuanxingBox 1.0 GuanxingBox.qml
HandcardArea 1.0 HandcardArea.qml HandcardArea 1.0 HandcardArea.qml

View File

@ -334,6 +334,10 @@
<source>Room is full or already started!</source> <source>Room is full or already started!</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>rejected your demand of joining room</source>
<translation></translation>
</message>
<message> <message>
<source>server is full!</source> <source>server is full!</source>
<translation></translation> <translation></translation>

View File

@ -8,6 +8,7 @@
---@field public current ClientPlayer @ 当前回合玩家 ---@field public current ClientPlayer @ 当前回合玩家
---@field public discard_pile integer[] @ 弃牌堆 ---@field public discard_pile integer[] @ 弃牌堆
---@field public status_skills Skill[] @ 状态技总和 ---@field public status_skills Skill[] @ 状态技总和
---@field public banners table<string, any> @ 左上角显示的东西
---@field public observing boolean ---@field public observing boolean
Client = class('Client') Client = class('Client')
@ -57,6 +58,8 @@ function Client:initialize()
self.status_skills[class] = {table.unpack(skills)} self.status_skills[class] = {table.unpack(skills)}
end end
self.banners = {}
self.skill_costs = {} self.skill_costs = {}
self.card_marks = {} self.card_marks = {}
self.filtered_cards = {} self.filtered_cards = {}
@ -234,6 +237,15 @@ function Client:setCardNote(ids, msg)
end end
end end
function Client:setBanner(name, value)
if value == 0 then value = nil end
self.banners[name] = value
end
function Client:getBanner(name)
return self.banners[name]
end
fk.client_callback["SetCardFootnote"] = function(jsonData) fk.client_callback["SetCardFootnote"] = function(jsonData)
local data = json.decode(jsonData) local data = json.decode(jsonData)
ClientInstance:setCardNote(data[1], data[2]); ClientInstance:setCardNote(data[1], data[2]);
@ -769,6 +781,17 @@ fk.client_callback["SetPlayerMark"] = function(jsonData)
end end
end end
fk.client_callback["SetBanner"] = function(jsonData)
-- jsonData: [ int id, string mark, int value ]
local data = json.decode(jsonData)
local mark, value = data[1], data[2]
ClientInstance:setBanner(mark, value)
if string.sub(mark, 1, 1) == "@" then
ClientInstance:notifyUI("SetBanner", jsonData)
end
end
fk.client_callback["SetCardMark"] = function(jsonData) fk.client_callback["SetCardMark"] = function(jsonData)
-- jsonData: [ int id, string mark, int value ] -- jsonData: [ int id, string mark, int value ]
local data = json.decode(jsonData) local data = json.decode(jsonData)

View File

@ -734,4 +734,14 @@ function PoxiFeasible(poxi_type, selected, data, extra_data)
return json.encode(poxi.feasible(selected, data, extra_data)) return json.encode(poxi.feasible(selected, data, extra_data))
end end
function GetQmlMark(mtype, name, value)
local spec = Fk.qml_marks[mtype]
if not spec then return "{}" end
value = json.decode(value)
return json.encode {
qml_path = spec.qml_path,
text = spec.how_to_show(name, value)
}
end
dofile "lua/client/i18n/init.lua" dofile "lua/client/i18n/init.lua"

View File

@ -159,7 +159,7 @@ Fk:loadTranslationTable({
["#AskForLuckCard"] = "Do you want to use luck card (%1 times left)?", ["#AskForLuckCard"] = "Do you want to use luck card (%1 times left)?",
["AskForLuckCard"] = "Luck card", ["AskForLuckCard"] = "Luck card",
["#AskForChoice"] = "%1: Please choose", ["#AskForChoice"] = "%1: Please choose",
["#AskForCheck"] = "%1: Please choose", ["#AskForChoices"] = "%1: Please choose",
["#choose-trigger"] = "Please choose the skill to use", ["#choose-trigger"] = "Please choose the skill to use",
["trigger"] = "Trigger skill", ["trigger"] = "Trigger skill",
-- ["Please arrange cards"] = "请拖拽移动卡牌", -- ["Please arrange cards"] = "请拖拽移动卡牌",
@ -170,7 +170,7 @@ Fk:loadTranslationTable({
["AskForGuanxing"] = "Stargazing", ["AskForGuanxing"] = "Stargazing",
["AskForExchange"] = "Exchaging", ["AskForExchange"] = "Exchaging",
["AskForChoice"] = "Making choice", ["AskForChoice"] = "Making choice",
["AskForCheck"] = "Making choice", ["AskForChoices"] = "Making choice",
["AskForKingdom"] = "Choosing kingdom", ["AskForKingdom"] = "Choosing kingdom",
["AskForPindian"] = "Point fight", ["AskForPindian"] = "Point fight",
["AskForMoveCardInBoard"] = "Moving cards", ["AskForMoveCardInBoard"] = "Moving cards",

View File

@ -206,7 +206,7 @@ FreeKill使用的是libgit2的C API与此同时使用Git完成拓展包的下
["#AskForLuckCard"] = "你想使用手气卡吗?还可以使用 %1 次,剩余手气卡∞张", ["#AskForLuckCard"] = "你想使用手气卡吗?还可以使用 %1 次,剩余手气卡∞张",
["AskForLuckCard"] = "手气卡", ["AskForLuckCard"] = "手气卡",
["#AskForChoice"] = "%1请选择", ["#AskForChoice"] = "%1请选择",
["#AskForCheck"] = "%1请选择", ["#AskForChoices"] = "%1请选择",
["#choose-trigger"] = "请选择一项技能发动", ["#choose-trigger"] = "请选择一项技能发动",
["trigger"] = "选择技能", ["trigger"] = "选择技能",
["Please arrange cards"] = "请拖拽移动卡牌", ["Please arrange cards"] = "请拖拽移动卡牌",
@ -217,7 +217,7 @@ FreeKill使用的是libgit2的C API与此同时使用Git完成拓展包的下
["AskForGuanxing"] = "观星", ["AskForGuanxing"] = "观星",
["AskForExchange"] = "换牌", ["AskForExchange"] = "换牌",
["AskForChoice"] = "选择", ["AskForChoice"] = "选择",
["AskForCheck"] = "选择", ["AskForChoices"] = "选择",
["AskForKingdom"] = "选择势力", ["AskForKingdom"] = "选择势力",
["AskForPindian"] = "拼点", ["AskForPindian"] = "拼点",
["AskForMoveCardInBoard"] = "移动卡牌", ["AskForMoveCardInBoard"] = "移动卡牌",

View File

@ -27,6 +27,7 @@
---@field public printed_cards table<integer, Card> @ 被某些房间现场打印的卡牌id都是负数且从-2开始 ---@field public printed_cards table<integer, Card> @ 被某些房间现场打印的卡牌id都是负数且从-2开始
---@field private _custom_events any[] @ 自定义事件列表 ---@field private _custom_events any[] @ 自定义事件列表
---@field public poxi_methods table<string, PoxiSpec> @ “魄袭”框操作方法表 ---@field public poxi_methods table<string, PoxiSpec> @ “魄袭”框操作方法表
---@field public qml_marks table<string, QmlMarkSpec> @ 自定义Qml标记的表
local Engine = class("Engine") local Engine = class("Engine")
--- Engine的构造函数。 --- Engine的构造函数。
@ -59,6 +60,7 @@ function Engine:initialize()
self.kingdoms = {} self.kingdoms = {}
self._custom_events = {} self._custom_events = {}
self.poxi_methods = {} self.poxi_methods = {}
self.qml_marks = {}
self:loadPackages() self:loadPackages()
self:loadDisabled() self:loadDisabled()
@ -345,11 +347,22 @@ function Engine:addPoxiMethod(spec)
assert(type(spec.name) == "string") assert(type(spec.name) == "string")
assert(type(spec.card_filter) == "function") assert(type(spec.card_filter) == "function")
assert(type(spec.feasible) == "function") assert(type(spec.feasible) == "function")
if self.poxi_methods[spec.name] then
fk.qCritical("Warning: duplicated poxi_method " .. spec.name)
end
self.poxi_methods[spec.name] = spec self.poxi_methods[spec.name] = spec
spec.default_choice = spec.default_choice or function() return {} end spec.default_choice = spec.default_choice or function() return {} end
spec.post_select = spec.post_select or function(s) return s end spec.post_select = spec.post_select or function(s) return s end
end end
function Engine:addQmlMark(spec)
assert(type(spec.name) == "string")
if self.qml_marks[spec.name] then
fk.qCritical("Warning: duplicated qml mark type " .. spec.name)
end
self.qml_marks[spec.name] = spec
end
--- 从已经开启的拓展包中,随机选出若干名武将。 --- 从已经开启的拓展包中,随机选出若干名武将。
--- ---
--- 对于同名武将不会重复选取。 --- 对于同名武将不会重复选取。

View File

@ -598,3 +598,8 @@ end
---@field post_select? fun(selected: int[], data: any, extra_data: any): int[] ---@field post_select? fun(selected: int[], data: any, extra_data: any): int[]
---@field default_choice? fun(data: any, extra_data: any): int[] ---@field default_choice? fun(data: any, extra_data: any): int[]
---@field prompt? string | fun(data: any, extra_data: any): string ---@field prompt? string | fun(data: any, extra_data: any): string
---@class QmlMarkSpec
---@field name string
---@field qml_path string
---@field how_to_show function(name: string, value?: any): string?

View File

@ -41,6 +41,11 @@ local function tellRoomToObserver(self, player)
end end
end end
-- send banners
for k, v in pairs(self.banners) do
player:doNotify("SetBanner", json.encode{ k, v })
end
for _, p in ipairs(self.players) do for _, p in ipairs(self.players) do
self:notifyProperty(player, p, "general") self:notifyProperty(player, p, "general")
self:notifyProperty(player, p, "deputyGeneral") self:notifyProperty(player, p, "deputyGeneral")

View File

@ -15,6 +15,7 @@
---@field public game_finished boolean @ 游戏是否已经结束 ---@field public game_finished boolean @ 游戏是否已经结束
---@field public timeout integer @ 出牌时长上限 ---@field public timeout integer @ 出牌时长上限
---@field public tag table<string, any> @ Tag清单其实跟Player的标记是差不多的东西 ---@field public tag table<string, any> @ Tag清单其实跟Player的标记是差不多的东西
---@field public banners table<string, any> @ 左上角显示点啥好呢?
---@field public general_pile string[] @ 武将牌堆,这是可用武将名的数组 ---@field public general_pile string[] @ 武将牌堆,这是可用武将名的数组
---@field public draw_pile integer[] @ 摸牌堆这是卡牌id的数组 ---@field public draw_pile integer[] @ 摸牌堆这是卡牌id的数组
---@field public discard_pile integer[] @ 弃牌堆也是卡牌id的数组 ---@field public discard_pile integer[] @ 弃牌堆也是卡牌id的数组
@ -77,6 +78,7 @@ function Room:initialize(_room)
self.game_finished = false self.game_finished = false
self.timeout = _room:getTimeout() self.timeout = _room:getTimeout()
self.tag = {} self.tag = {}
self.banners = {}
self.general_pile = {} self.general_pile = {}
self.draw_pile = {} self.draw_pile = {}
self.discard_pile = {} self.discard_pile = {}
@ -563,6 +565,16 @@ function Room:removeTag(tag_name)
self.tag[tag_name] = nil self.tag[tag_name] = nil
end end
function Room:setBanner(name, value)
if value == 0 then value = nil end
self.banners[name] = value
self:doBroadcastNotify("SetBanner", json.encode{ name, value })
end
function Room:getBanner(name)
return self.banners[name]
end
---@return boolean ---@return boolean
local function execGameEvent(type, ...) local function execGameEvent(type, ...)
local event = GameEvent:new(type, ...) local event = GameEvent:new(type, ...)
@ -1338,9 +1350,9 @@ end
---@param cancelable? boolean @ 能否点取消 ---@param cancelable? boolean @ 能否点取消
---@param no_indicate? boolean @ 是否不显示指示线 ---@param no_indicate? boolean @ 是否不显示指示线
---@return integer[], integer[] ---@return integer[], integer[]
function Room:askForChooseBoth(player, minCardNum, maxCardNum, targets, minTargetNum, maxTargetNum, pattern, prompt, skillName, cancelable, no_indicate) function Room:askForChooseCardsAndPlayers(player, minCardNum, maxCardNum, targets, minTargetNum, maxTargetNum, pattern, prompt, skillName, cancelable, no_indicate)
if minCardNum < 1 or minTargetNum < 1 then if minCardNum < 1 or minTargetNum < 1 then
return table.unpack({}, {}) return {}, {}
end end
cancelable = (cancelable == nil) and true or cancelable cancelable = (cancelable == nil) and true or cancelable
no_indicate = no_indicate or false no_indicate = no_indicate or false
@ -1366,7 +1378,7 @@ function Room:askForChooseBoth(player, minCardNum, maxCardNum, targets, minTarge
return ret.targets, ret.cards return ret.targets, ret.cards
else else
if cancelable then if cancelable then
return table.unpack({}, {}) return {}, {}
else else
return table.random(targets, minTargetNum), table.random(pcards, minCardNum) return table.random(targets, minTargetNum), table.random(pcards, minCardNum)
end end
@ -1675,12 +1687,12 @@ end
---@param detailed? boolean @ 选项详细描述 ---@param detailed? boolean @ 选项详细描述
---@param all_choices? string[] @ 所有选项(不可选变灰) ---@param all_choices? string[] @ 所有选项(不可选变灰)
---@return string[] @ 选择的选项 ---@return string[] @ 选择的选项
function Room:askForCheck(player, choices, minNum, maxNum, skill_name, prompt, cancelable, detailed, all_choices) function Room:askForChoices(player, choices, minNum, maxNum, skill_name, prompt, cancelable, detailed, all_choices)
cancelable = (cancelable == nil) and true or cancelable cancelable = (cancelable == nil) and true or cancelable
if #choices <= minNum and not all_choices then return choices end if #choices <= minNum and not all_choices then return choices end
assert(minNum <= maxNum) assert(minNum <= maxNum)
assert(not all_choices or table.every(choices, function(c) return table.contains(all_choices, c) end)) assert(not all_choices or table.every(choices, function(c) return table.contains(all_choices, c) end))
local command = "AskForCheck" local command = "AskForChoices"
skill_name = skill_name or "" skill_name = skill_name or ""
prompt = prompt or "" prompt = prompt or ""
all_choices = all_choices or choices all_choices = all_choices or choices

View File

@ -362,6 +362,11 @@ function ServerPlayer:reconnect()
end end
end end
-- send banners
for k, v in pairs(room.banners) do
self:doNotify("SetBanner", json.encode{ k, v })
end
for _, p in ipairs(room.players) do for _, p in ipairs(room.players) do
room:notifyProperty(self, p, "general") room:notifyProperty(self, p, "general")
room:notifyProperty(self, p, "deputyGeneral") room:notifyProperty(self, p, "deputyGeneral")

View File

@ -87,6 +87,10 @@ local control = fk.CreateActiveSkill{
on_use = function(self, room, effect) on_use = function(self, room, effect)
--room:doSuperLightBox("packages/test/qml/Test.qml") --room:doSuperLightBox("packages/test/qml/Test.qml")
local from = room:getPlayerById(effect.from) local from = room:getPlayerById(effect.from)
-- room:setPlayerMark(from, "@[test]test", {
-- all = {3, 1, 6, 9, 5, 11, 10, 2, 8, 7, 12, 4, 13},
-- ok = {10, 2},
-- })
-- room:swapSeat(from, to) -- room:swapSeat(from, to)
for _, pid in ipairs(effect.tos) do for _, pid in ipairs(effect.tos) do
local to = room:getPlayerById(pid) local to = room:getPlayerById(pid)
@ -145,6 +149,27 @@ Fk:addPoxiMethod{
end, end,
prompt = "魄袭:选你们俩手牌总共四个花色,或者不选直接按确定按钮" prompt = "魄袭:选你们俩手牌总共四个花色,或者不选直接按确定按钮"
} }
Fk:loadTranslationTable{['@[test]test']='割圆'}
Fk:addQmlMark{
name = "test",
how_to_show = function(name, value)
local all_points = value.all
local ok_points = value.ok
-- 若没有点亮的就不显示
if #ok_points == 0 then return "" end
-- 否则,显示相邻的,逻辑上要构成循环
local start_idx = table.indexOf(all_points, ok_points[1]) - 1
local end_idx = table.indexOf(all_points, ok_points[#ok_points]) + 1
if start_idx == 0 then start_idx = #all_points end
if end_idx == #all_points + 1 then end_idx = 1 end
if start_idx == end_idx then
return Card:getNumberStr(all_points[start_idx])
else
return Card:getNumberStr(all_points[start_idx]) .. Card:getNumberStr(all_points[end_idx])
end
end,
qml_path = "packages/test/qml/TestDialog"
}
--]] --]]
local test_vs = fk.CreateViewAsSkill{ local test_vs = fk.CreateViewAsSkill{
name = "test_vs", name = "test_vs",

View File

@ -1,38 +1,57 @@
//
import QtQuick import QtQuick
import "../../../qml/Pages/RoomElement" import QtQuick.Layouts
import "../../../qml/Pages" import Fk.RoomElement
GraphicsBox {
property string custom_string: ""
ColumnLayout {
id: root id: root
title.text: Backend.translate("Test") anchors.fill: parent
width: Math.max(140, body.width + 20) property var extra_data: ({ name: "", data: {
height: body.height + title.height + 20 all: [1, 2, 4, 6],
ok: [1, 4],
} })
signal finish()
Column { BigGlowText {
id: body Layout.fillWidth: true
x: 10 Layout.preferredHeight: childrenRect.height + 4
y: title.height + 5
spacing: 10
Text { text: Backend.translate(extra_data.name)
text: root.custom_string }
color: "#E4D5A0"
PathView {
id: pathView
Layout.fillWidth: true
Layout.fillHeight: true
model: extra_data.data.all
delegate: Rectangle{
width: 42; height: 42
color: extra_data.data.ok.includes(modelData) ? "yellow" : "#CCEEEEEE"
radius: 2
Text {
anchors.centerIn: parent
text: modelData
font.pixelSize: 24
}
} }
path: Path {
MetroButton { //
text: Backend.translate("OKOK") startX: pathView.width / 2
anchors.horizontalCenter: parent.horizontalCenter startY: 40
PathArc {
onClicked: { x: pathView.width / 2
close(); y: pathView.height - 40
ClientInstance.replyToServer("", "Hello from test dialog"); radiusX: (pathView.height - 80) / 2
radiusY: (pathView.height - 80) / 2
direction: PathArc.Clockwise
}
PathArc {
x: pathView.width / 2
y: 40
radiusX: (pathView.height - 80) / 2
radiusY: (pathView.height - 80) / 2
direction: PathArc.Clockwise
} }
} }
} }
function loadData(data) {
custom_string = data;
}
} }

View File

@ -284,7 +284,13 @@ void Router::handlePacket(const QByteArray &rawPacket) {
} else if (command == "KickPlayer") { } else if (command == "KickPlayer") {
int i = jsonData.toInt(); int i = jsonData.toInt();
auto p = room->findPlayer(i); auto p = room->findPlayer(i);
if (p && !room->isStarted()) room->removePlayer(p); if (p && !room->isStarted()) {
room->removePlayer(p);
room->addRejectId(i);
QTimer::singleShot(30000, this, [=]() {
room->removeRejectId(i);
});
}
} else if (command == "Ready") { } else if (command == "Ready") {
player->setReady(!player->isReady()); player->setReady(!player->isReady());
room->doBroadcastNotify(room->getPlayers(), "ReadyChanged", room->doBroadcastNotify(room->getPlayers(), "ReadyChanged",

View File

@ -125,6 +125,11 @@ void Room::addPlayer(ServerPlayer *player) {
if (!player) if (!player)
return; return;
if (rejected_players.contains(player->getId())) {
player->doNotify("ErrorMsg", "rejected your demand of joining room");
return;
}
// 如果要加入的房间满员了,或者已经开战了,就不能再加入 // 如果要加入的房间满员了,或者已经开战了,就不能再加入
if (isFull() || gameStarted) { if (isFull() || gameStarted) {
player->doNotify("ErrorMsg", "Room is full or already started!"); player->doNotify("ErrorMsg", "Room is full or already started!");
@ -322,6 +327,11 @@ void Room::addObserver(ServerPlayer *player) {
return; return;
} }
if (rejected_players.contains(player->getId())) {
player->doNotify("ErrorMsg", "rejected your demand of joining room");
return;
}
// 向observers中追加player并从大厅移除player然后将player的room设为this // 向observers中追加player并从大厅移除player然后将player的room设为this
observers.append(player); observers.append(player);
player->setRoom(this); player->setRoom(this);
@ -573,3 +583,12 @@ void Room::pushRequest(const QString &req) {
m_thread->pushRequest(QString("%1,%2").arg(QString::number(id), req)); m_thread->pushRequest(QString("%1,%2").arg(QString::number(id), req));
} }
} }
void Room::addRejectId(int id) {
if (isLobby()) return;
rejected_players << id;
}
void Room::removeRejectId(int id) {
rejected_players.removeOne(id);
}

View File

@ -63,6 +63,8 @@ class Room : public QObject {
void manuallyStart(); void manuallyStart();
void pushRequest(const QString &req); void pushRequest(const QString &req);
void addRejectId(int id);
void removeRejectId(int id);
signals: signals:
void abandoned(); void abandoned();
@ -82,6 +84,7 @@ class Room : public QObject {
QList<ServerPlayer *> players; QList<ServerPlayer *> players;
QList<ServerPlayer *> observers; QList<ServerPlayer *> observers;
QList<int> runned_players; QList<int> runned_players;
QList<int> rejected_players;
int robot_id; int robot_id;
bool gameStarted; bool gameStarted;
bool m_ready; bool m_ready;

View File

@ -60,7 +60,7 @@ Server::Server(QObject *parent) : QObject(parent) {
} }
} }
for (int i = 0; i < 20; i++) { for (int i = 0; i < 30; i++) {
if (!this->isListening) { if (!this->isListening) {
return; return;
} }