杀光侧栏 只留战报一个
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.Controls
import QtQuick.Layouts
import Fk.RoomElement
Flickable {
id: root
@ -14,16 +15,25 @@ Flickable {
contentHeight: details.height
ScrollBar.vertical: ScrollBar {}
ColumnLayout {
RowLayout {
id: details
width: parent.width - 40
x: 20
spacing: 20
// TODO: player details
CardItem {
id: cardPic
Layout.alignment: Qt.AlignTop
Layout.topMargin: 10
cid: 0
}
ColumnLayout {
Text {
id: screenName
Layout.fillWidth: true
font.pixelSize: 18
color: "#E4D5A0"
}
TextEdit {
@ -31,6 +41,7 @@ Flickable {
Layout.fillWidth: true
font.pixelSize: 18
color: "#E4D5A0"
readOnly: true
selectByKeyboard: true
@ -39,10 +50,12 @@ Flickable {
textFormat: TextEdit.RichText
}
}
}
onExtra_dataChanged: {
const card = extra_data.card;
if (!card) return;
cardPic.setData(card.toData());
const name = card.virt_name ? card.virt_name : card.name;
screenName.text = Backend.translate(name);
skillDesc.text = Backend.translate(":" + name);

View File

@ -24,6 +24,7 @@ Item {
text: Backend.translate("Back")
onClicked: stack.pop()
}
Label {
text: Backend.translate("Enable free assign")
elide: Label.ElideRight
@ -31,8 +32,29 @@ Item {
verticalAlignment: Qt.AlignVCenter
Layout.fillWidth: true
}
TextField {
id: word
placeholderText: "Search..."
clip: true
verticalAlignment: Qt.AlignVCenter
background: Rectangle {
implicitHeight: 16
implicitWidth: 120
color: "transparent"
}
}
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 {}
model: packages
clip: true
cellWidth: width / 3
cellWidth: width / 5
cellHeight: 40
delegate: ItemDelegate {
width: listView.width / 3
width: listView.width / 5
height: 40
Text {
text: Backend.translate(name)
color: "#E4D5A0"
anchors.centerIn: parent
}

View File

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

View File

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

View File

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

View File

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

View File

@ -15,6 +15,7 @@ Item {
transformOrigin: Item.BottomLeft
rotation: 90
width: root.height
background: Rectangle { color: "#EEEEEEEE" }
TabButton {
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 {
anchors.rightMargin: 8
spacing: 16
@ -69,12 +51,33 @@ Flickable {
}
}
RowLayout {
GridLayout {
anchors.rightMargin: 8
spacing: 16
rowSpacing: 20
columnSpacing: 20
columns: 4
Text {
text: Backend.translate("Player num")
}
Text {
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 {
id: generalNum
from: 3
@ -85,6 +88,25 @@ Flickable {
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 {
@ -100,41 +122,6 @@ Flickable {
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 {
anchors.rightMargin: 8
spacing: 16
@ -150,6 +137,9 @@ Flickable {
}
}
RowLayout {
anchors.rightMargin: 8
spacing: 16
Switch {
id: freeAssignCheck
checked: Debugging ? true : false
@ -161,6 +151,7 @@ Flickable {
checked: Debugging ? true : false
text: Backend.translate("Enable deputy general")
}
}
RowLayout {
anchors.rightMargin: 8

View File

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

View File

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

View File

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

View File

@ -8,6 +8,7 @@ import QtMultimedia
import Fk
import Fk.Common
import Fk.RoomElement
import Fk.PhotoElement as PhotoElement
import "RoomLogic.js" as Logic
Item {
@ -37,6 +38,7 @@ Item {
property alias drawPile: drawPile
property alias skillInteraction: skillInteraction
property alias miscStatus: miscStatus
property alias banner: banner
property var selected_targets: []
property string responding_card
@ -55,7 +57,6 @@ Item {
fillMode: Image.PreserveAspectCrop
}
/*
MediaPlayer {
id: bgm
source: config.bgmFile
@ -69,15 +70,14 @@ Item {
volume: config.bgmVolume / 100
}
}
*/
onIsStartedChanged: {
if (isStarted) {
// bgm.play();
bgm.play();
canKickOwner = false;
kickOwnerTimer.stop();
} 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 {
id: replayControls
visible: config.replaying
@ -855,7 +847,7 @@ Item {
Drawer {
id: roomDrawer
width: parent.width * 0.3 / mainWindow.scale
width: parent.width * 0.36 / mainWindow.scale
height: parent.height / mainWindow.scale
dim: false
clip: true
@ -901,20 +893,25 @@ Item {
}
}
Drawer {
Popup {
id: cheatDrawer
edge: Qt.RightEdge
width: parent.width * 0.4 / mainWindow.scale
height: parent.height / mainWindow.scale
dim: false
clip: true
dragMargin: 0
scale: mainWindow.scale
transformOrigin: Item.TopRight
width: realMainWin.width * 0.60
height: realMainWin.height * 0.8
anchors.centerIn: parent
background: Rectangle {
color: "#CC2E2C27"
radius: 5
border.color: "#A6967A"
border.width: 1
}
Loader {
id: cheatLoader
anchors.fill: parent
anchors.centerIn: parent
width: parent.width / mainWindow.scale
height: parent.height / mainWindow.scale
scale: mainWindow.scale
clip: true
onSourceChanged: {
if (item === null)
return;
@ -931,6 +928,20 @@ Item {
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 {
id: easyChat
width: parent.width
@ -967,6 +978,16 @@ Item {
}
}
Shortcut {
sequence: "T"
onActivated: {
easyChat.visible = true;
easyChatEdit.enabled = true;
easyChatEdit.forceActiveFocus();
}
}
*/
MiscStatus {
id: miscStatus
anchors.right: menuButton.left
@ -975,20 +996,20 @@ Item {
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 {
id: danmaku
width: parent.width
}
Shortcut {
sequence: "T"
onActivated: {
easyChat.visible = true;
easyChatEdit.enabled = true;
easyChatEdit.forceActiveFocus();
}
}
Shortcut {
sequence: "D"
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 ]
// TODO: multiple choices, e.g. benxi_ol
const data = JSON.parse(jsonData);
@ -1025,7 +1025,7 @@ callbacks["AskForCheck"] = (jsonData) => {
const prompt = data[5];
const detailed = data[6];
if (prompt === "") {
roomScene.promptText = Backend.translate("#AskForCheck")
roomScene.promptText = Backend.translate("#AskForChoices")
.arg(Backend.translate(skill_name));
} else {
roomScene.setPrompt(processPrompt(prompt), true);
@ -1335,7 +1335,7 @@ callbacks["SetPlayerMark"] = (jsonData) => {
const data = JSON.parse(jsonData);
const player = getPhoto(data[0]);
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;
if (data[2] === 0) {
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) => {
// jsonData: [Object object]
const data = JSON.parse(jsonData);
@ -1581,7 +1593,7 @@ callbacks["ChangeSelf"] = (j) => {
callbacks["AskForLuckCard"] = (j) => {
// jsonData: int time
if (config.replaying) return;
if (config.observing || config.replaying) return;
const time = parseInt(j);
roomScene.setPrompt(Backend.translate("#AskForLuckCard").arg(time), true);
roomScene.state = "replying";

View File

@ -6,6 +6,7 @@ import QtQuick.Layouts
Item {
id: root
width: 138
property var bgColor: "#3C3229"
ListModel {
id: markList
@ -15,7 +16,7 @@ Item {
anchors.bottom: parent.bottom
width: parent.width
height: parent.height
color: "#3C3229"
color: bgColor
opacity: 0.8
radius: 4
border.color: "white"
@ -68,7 +69,21 @@ Item {
} else {
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 {
if (!root.parent.playerid) return;
let data = JSON.parse(Backend.callLuaFunction("GetPile", [root.parent.playerid, mark_name]));
data = data.filter((e) => e !== -1);
if (data.length === 0)
@ -103,6 +118,15 @@ Item {
if (mark.startsWith('@$') || mark.startsWith('@&')) {
special_value += data.length;
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 {
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 Qt5Compat.GraphicalEffects
Item {
ColumnLayout {
id: root
anchors.fill: parent
property var extra_data: ({}) // unused
@ -14,33 +14,24 @@ Item {
Text {
text: Backend.translate("Handcard selector")
width: parent.width
anchors.topMargin: 6
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 16
font.pixelSize: 18
color: "#E4D5A0"
}
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
GridView {
id: cardsList
cellWidth: 93 * 0.9 + 4
cellHeight: 130 * 0.9 + 4
Layout.preferredWidth: root.width - root.width % 88
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter
clip: true
GridLayout {
id: cardsList
columns: Math.floor(flickableContainer.width / 90)
Repeater {
model: cards
CardItem {
delegate: CardItem {
width: 93 * 0.9
height: 130 * 0.9
chosenInBox: modelData.chosen
@ -59,8 +50,6 @@ Item {
}
}
}
}
}
Component.onCompleted: {
cards = roomScene.dashboard.handcardArea.cards

View File

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

View File

@ -2,87 +2,34 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Qt5Compat.GraphicalEffects
Item {
ColumnLayout {
id: root
anchors.fill: parent
property var extra_data: ({})
signal finish()
Rectangle {
anchors.fill: parent
color: "black"
BigGlowText {
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height + 4
GlowText {
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 {
anchors.fill: pileName
source: pileName
gradient: Gradient {
GradientStop {
position: 0
color: "#FEF7C2"
}
GradientStop {
position: 0.5
color: "#D2AD4A"
}
GradientStop {
position: 1
color: "#BE9878"
}
}
}
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
GridView {
cellWidth: 93 + 4
cellHeight: 130 + 4
Layout.preferredWidth: root.width - root.width % 97
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter
clip: true
ColumnLayout {
id: cardsList
GridLayout {
columns: Math.floor(flickableContainer.width / 100)
Repeater {
model: extra_data.ids || extra_data.cardNames
GeneralCardItem {
delegate: GeneralCardItem {
id: cardItem
// width: (flickableContainer.width - 15) / 4
// height: cardItem.width * 1.4
autoBack: false
name: modelData
}
}
}
}
}
}
}

View File

@ -2,81 +2,32 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Qt5Compat.GraphicalEffects
Item {
ColumnLayout {
id: root
anchors.fill: parent
property var extra_data: ({})
signal finish()
Rectangle {
anchors.fill: parent
color: "black"
BigGlowText {
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height + 4
GlowText {
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 {
anchors.fill: pileName
source: pileName
gradient: Gradient {
GradientStop {
position: 0
color: "#FEF7C2"
}
GradientStop {
position: 0.5
color: "#D2AD4A"
}
GradientStop {
position: 1
color: "#BE9878"
}
}
}
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
GridView {
cellWidth: 93 + 4
cellHeight: 130 + 4
Layout.preferredWidth: root.width - root.width % 97
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter
clip: true
ColumnLayout {
id: cardsList
GridLayout {
columns: 4
Repeater {
model: extra_data.ids || extra_data.cardNames
CardItem {
delegate: CardItem {
id: cardItem
width: (flickableContainer.width - 15) / 4
height: cardItem.width * 1.4
autoBack: false
Component.onCompleted: {
let data = {}
@ -93,8 +44,4 @@ Item {
}
}
}
}
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -27,6 +27,7 @@
---@field public printed_cards table<integer, Card> @ 被某些房间现场打印的卡牌id都是负数且从-2开始
---@field private _custom_events any[] @ 自定义事件列表
---@field public poxi_methods table<string, PoxiSpec> @ “魄袭”框操作方法表
---@field public qml_marks table<string, QmlMarkSpec> @ 自定义Qml标记的表
local Engine = class("Engine")
--- Engine的构造函数。
@ -59,6 +60,7 @@ function Engine:initialize()
self.kingdoms = {}
self._custom_events = {}
self.poxi_methods = {}
self.qml_marks = {}
self:loadPackages()
self:loadDisabled()
@ -345,11 +347,22 @@ function Engine:addPoxiMethod(spec)
assert(type(spec.name) == "string")
assert(type(spec.card_filter) == "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
spec.default_choice = spec.default_choice or function() return {} end
spec.post_select = spec.post_select or function(s) return s 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 default_choice? fun(data: any, extra_data: any): int[]
---@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
-- 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
self:notifyProperty(player, p, "general")
self:notifyProperty(player, p, "deputyGeneral")

View File

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

View File

@ -362,6 +362,11 @@ function ServerPlayer:reconnect()
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
room:notifyProperty(self, p, "general")
room:notifyProperty(self, p, "deputyGeneral")

View File

@ -87,6 +87,10 @@ local control = fk.CreateActiveSkill{
on_use = function(self, room, effect)
--room:doSuperLightBox("packages/test/qml/Test.qml")
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)
for _, pid in ipairs(effect.tos) do
local to = room:getPlayerById(pid)
@ -145,6 +149,27 @@ Fk:addPoxiMethod{
end,
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{
name = "test_vs",

View File

@ -1,38 +1,57 @@
//
import QtQuick
import "../../../qml/Pages/RoomElement"
import "../../../qml/Pages"
GraphicsBox {
property string custom_string: ""
import QtQuick.Layouts
import Fk.RoomElement
ColumnLayout {
id: root
title.text: Backend.translate("Test")
width: Math.max(140, body.width + 20)
height: body.height + title.height + 20
anchors.fill: parent
property var extra_data: ({ name: "", data: {
all: [1, 2, 4, 6],
ok: [1, 4],
} })
signal finish()
Column {
id: body
x: 10
y: title.height + 5
spacing: 10
BigGlowText {
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height + 4
text: Backend.translate(extra_data.name)
}
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 {
text: root.custom_string
color: "#E4D5A0"
}
MetroButton {
text: Backend.translate("OKOK")
anchors.horizontalCenter: parent.horizontalCenter
onClicked: {
close();
ClientInstance.replyToServer("", "Hello from test dialog");
anchors.centerIn: parent
text: modelData
font.pixelSize: 24
}
}
path: Path {
//
startX: pathView.width / 2
startY: 40
PathArc {
x: pathView.width / 2
y: pathView.height - 40
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") {
int i = jsonData.toInt();
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") {
player->setReady(!player->isReady());
room->doBroadcastNotify(room->getPlayers(), "ReadyChanged",

View File

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