diff --git a/image/button/tileicon/about.png b/image/button/tileicon/about.png
new file mode 100644
index 00000000..630d6479
Binary files /dev/null and b/image/button/tileicon/about.png differ
diff --git a/image/button/tileicon/card_overview.png b/image/button/tileicon/card_overview.png
new file mode 100644
index 00000000..de02a46b
Binary files /dev/null and b/image/button/tileicon/card_overview.png differ
diff --git a/image/button/tileicon/configure.png b/image/button/tileicon/configure.png
new file mode 100644
index 00000000..6262688d
Binary files /dev/null and b/image/button/tileicon/configure.png differ
diff --git a/image/button/tileicon/create_room.png b/image/button/tileicon/create_room.png
new file mode 100644
index 00000000..3c719444
Binary files /dev/null and b/image/button/tileicon/create_room.png differ
diff --git a/image/button/tileicon/general_overview.png b/image/button/tileicon/general_overview.png
new file mode 100644
index 00000000..61c5b881
Binary files /dev/null and b/image/button/tileicon/general_overview.png differ
diff --git a/image/button/tileicon/quit.png b/image/button/tileicon/quit.png
new file mode 100644
index 00000000..3b4f1e3d
Binary files /dev/null and b/image/button/tileicon/quit.png differ
diff --git a/image/button/tileicon/replay.png b/image/button/tileicon/replay.png
new file mode 100644
index 00000000..cc8473aa
Binary files /dev/null and b/image/button/tileicon/replay.png differ
diff --git a/image/button/tileicon/rule_summary.png b/image/button/tileicon/rule_summary.png
new file mode 100644
index 00000000..d088ea41
Binary files /dev/null and b/image/button/tileicon/rule_summary.png differ
diff --git a/image/button/tileicon/start_game.png b/image/button/tileicon/start_game.png
new file mode 100644
index 00000000..7da1fc71
Binary files /dev/null and b/image/button/tileicon/start_game.png differ
diff --git a/image/logo/freekill.png b/image/logo/freekill.png
new file mode 100644
index 00000000..3f40fdf0
Binary files /dev/null and b/image/logo/freekill.png differ
diff --git a/image/logo/gplv3.png b/image/logo/gplv3.png
new file mode 100644
index 00000000..b44f609f
Binary files /dev/null and b/image/logo/gplv3.png differ
diff --git a/image/logo/lua.png b/image/logo/lua.png
new file mode 100644
index 00000000..75234a6f
Binary files /dev/null and b/image/logo/lua.png differ
diff --git a/image/logo/ossl.png b/image/logo/ossl.png
new file mode 100644
index 00000000..3cfe626c
Binary files /dev/null and b/image/logo/ossl.png differ
diff --git a/image/logo/qt.png b/image/logo/qt.png
new file mode 100644
index 00000000..21c38d10
Binary files /dev/null and b/image/logo/qt.png differ
diff --git a/image/logo/sqlite.png b/image/logo/sqlite.png
new file mode 100644
index 00000000..fddedaee
Binary files /dev/null and b/image/logo/sqlite.png differ
diff --git a/lua/client/client_util.lua b/lua/client/client_util.lua
index 96ded9e2..9419edf8 100644
--- a/lua/client/client_util.lua
+++ b/lua/client/client_util.lua
@@ -232,7 +232,32 @@ Fk:loadTranslationTable{
["Generals Overview"] = "武将一览",
["Cards Overview"] = "卡牌一览",
["Scenarios Overview"] = "玩法一览",
+ ["Replay"] = "录像",
["About"] = "关于",
+ ["about_freekill_description"] = "关于FreeKill
" ..
+ "以便于DIY为首要目的的开源三国杀游戏。
" ..
+ "
项目链接: https://github.com/Notify-ctrl/FreeKill",
+ ["about_qt_description"] = "关于Qt
" ..
+ "Qt是一个C++图形界面应用程序开发框架,拥有强大的跨平台能力以及易于使用的API。
" ..
+ "
本程序使用Qt 6.2+,主要利用QtQuick开发UI,同时也使用Qt的网络库开发服务端程序。
" ..
+ "
官网: https://www.qt.io",
+ ["about_lua_description"] = "关于Lua
" ..
+ "Lua是一种小巧、灵活、高效的脚本语言,广泛用于游戏开发中。
" ..
+ "
本程序使用Lua 5.4,利用其完全实现了整个游戏逻辑。
" ..
+ "
官网: https://www.lua.org",
+ ["about_ossl_description"] = "关于OpenSSL
" ..
+ "OpenSSL是一个开源包,用来提供安全通信与各种加密支持。
" ..
+ "
本程序目前用到了crypto库,以获得RSA加密算法支持。
" ..
+ "
官网: https://www.openssl.org",
+ ["about_gplv3_description"] = "关于GPLv3
" ..
+ "GNU通用公共许可协议(简称GPL)是一个广泛使用的自由软件许可证条款,它确保广大用户自由地使用、学习、共享或修改软件。
" ..
+ "
由于Qt是按照GPLv3协议开源的库,与此同时本程序用到的readline库也属于GPLv3库,再加上QSanguosha也是以GPLv3协议开源的软件(从中借鉴了不少代码和思路),因此这个项目也使用GPLv3协议开源。
" ..
+ "
官网: https://gplv3.fsf.org",
+ ["about_sqlite_description"] = "关于SQLite
" ..
+ "SQLite是一个轻量级的数据库,具有占用资源低、运行效率快、嵌入性好等优点。
" ..
+ "
FreeKill使用sqlite3在服务端保存用户的各种信息。
" ..
+ "
官网: https://www.sqlite.org",
+
["Exit Lobby"] = "退出大厅",
["OK"] = "确定",
diff --git a/qml/Pages/About.qml b/qml/Pages/About.qml
new file mode 100644
index 00000000..5b45d9b6
--- /dev/null
+++ b/qml/Pages/About.qml
@@ -0,0 +1,80 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Item {
+ id: root
+
+ ListModel {
+ id: aboutModel
+ ListElement { dest: "freekill" }
+ ListElement { dest: "qt" }
+ ListElement { dest: "lua" }
+ ListElement { dest: "gplv3" }
+ ListElement { dest: "sqlite" }
+ ListElement { dest: "ossl" }
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ SwipeView {
+ id: swipe
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ currentIndex: indicator.currentIndex
+ Repeater {
+ model: aboutModel
+ Item {
+ Rectangle {
+ anchors.centerIn: parent
+ color: "#88888888"
+ radius: 2
+ width: root.width * 0.8
+ height: root.height * 0.8
+
+ Image {
+ id: logo
+ anchors.left: parent.left
+ anchors.leftMargin: 8
+ anchors.verticalCenter: parent.verticalCenter
+ source: AppPath + "/image/logo/" + dest
+ width: parent.width * 0.3
+ fillMode: Image.PreserveAspectFit
+ }
+
+ Text {
+ anchors.left: logo.right
+ anchors.leftMargin: 16
+ width: parent.width * 0.65
+ text: Backend.translate("about_" + dest + "_description")
+ wrapMode: Text.WordWrap
+ textFormat: Text.RichText
+ font.pixelSize: 18
+ }
+ }
+ }
+ }
+ }
+
+ PageIndicator {
+ id: indicator
+
+ count: swipe.count
+ currentIndex: swipe.currentIndex
+ interactive: true
+
+ Layout.alignment: Qt.AlignHCenter
+ }
+ }
+
+ Button {
+ text: Backend.translate("Quit")
+ anchors.right: parent.right
+ onClicked: {
+ swipe.opacity = 0;
+ mainStack.pop();
+ }
+ }
+
+}
diff --git a/qml/Pages/Lobby.qml b/qml/Pages/Lobby.qml
index 9c272d3b..a6002afa 100644
--- a/qml/Pages/Lobby.qml
+++ b/qml/Pages/Lobby.qml
@@ -69,7 +69,7 @@ Item {
RowLayout {
anchors.fill: parent
Item {
- Layout.preferredWidth: root.width * 0.7
+ Layout.preferredWidth: root.width * 0.6
Layout.fillHeight: true
Rectangle {
width: parent.width * 0.8
@@ -95,42 +95,58 @@ Item {
}
}
- ColumnLayout {
- Button {
+ GridLayout {
+ flow: GridLayout.TopToBottom
+ rows: 4
+ TileButton {
+ iconSource: "configure"
text: Backend.translate("Edit Profile")
onClicked: {
globalPopup.source = "EditProfile.qml";
globalPopup.open();
}
}
- Button {
+ TileButton {
+ iconSource: "create_room"
text: Backend.translate("Create Room")
onClicked: {
globalPopup.source = "CreateRoom.qml";
globalPopup.open();
}
}
- Button {
+ TileButton {
+ iconSource: "general_overview"
text: Backend.translate("Generals Overview")
onClicked: {
mainStack.push(mainWindow.generalsOverviewPage);
mainStack.currentItem.loadPackages();
}
}
- Button {
+ TileButton {
+ iconSource: "card_overview"
text: Backend.translate("Cards Overview")
onClicked: {
mainStack.push(mainWindow.cardsOverviewPage);
mainStack.currentItem.loadPackages();
}
}
- Button {
+ TileButton {
+ iconSource: "rule_summary"
text: Backend.translate("Scenarios Overview")
}
- Button {
- text: Backend.translate("About")
+ TileButton {
+ iconSource: "replay"
+ text: Backend.translate("Replay")
}
- Button {
+ TileButton {
+ iconSource: "about"
+ text: Backend.translate("About")
+ onClicked: {
+ mainStack.push(mainWindow.aboutPage);
+ }
+ }
+ TileButton {
+ iconSource: "quit"
text: Backend.translate("Exit Lobby")
onClicked: {
toast.show("Goodbye.");
diff --git a/qml/Pages/TileButton.qml b/qml/Pages/TileButton.qml
new file mode 100644
index 00000000..b38fe053
--- /dev/null
+++ b/qml/Pages/TileButton.qml
@@ -0,0 +1,175 @@
+import QtQuick
+import Qt5Compat.GraphicalEffects
+import "skin-bank.js" as SkinBank
+
+Item {
+ property alias text: labelText.text
+ property alias textColor: labelText.color
+ property alias textFont: labelText.font
+ property string iconSource
+ property alias backgroundColor: rect.color
+ property alias border: rect.border
+ property bool autoHideText: true
+
+ signal clicked
+
+ id: button
+ width: 124
+ height: 124
+ antialiasing: true
+
+ RectangularGlow {
+ anchors.fill: rect
+ glowRadius: 1
+ spread: 1.0
+ visible: mouse.containsMouse || parent.focus
+ antialiasing: true
+ }
+
+ Rectangle {
+ id: rect
+ anchors.fill: parent
+ color: "#78D478"
+ antialiasing: true
+ border.width: 1
+ border.color: "#8CDA8C"
+ }
+
+ transform: [
+ Rotation {
+ id: rotationTransform
+
+ angle: 0
+
+ axis.x: 0
+ axis.y: 0
+ axis.z: 0
+
+ origin.x: button.width / 2.0
+ origin.y: button.height / 2.0
+
+ Behavior on angle {
+ NumberAnimation { duration: 100 }
+ }
+ },
+
+ Scale {
+ id: scaleTransform
+
+ xScale: 1
+ yScale: 1
+
+ origin.x: button.width / 2.0
+ origin.y: button.height / 2.0
+
+ Behavior on xScale {
+ NumberAnimation { duration: 100 }
+ }
+
+ Behavior on yScale {
+ NumberAnimation { duration: 100 }
+ }
+ }
+
+ ]
+
+ Image {
+ id: icon
+ anchors.centerIn: parent
+ source: SkinBank.TILE_ICON_DIR + iconSource
+ scale: 0.8
+ }
+
+ Text {
+ id: labelText
+
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: 3
+ anchors.left: parent.left
+ anchors.leftMargin: 3
+
+ visible: !autoHideText || mouse.containsMouse
+
+ color: "white"
+ font.pixelSize: 16
+ font.family: "WenQuanYi Micro Hei"
+
+ text: "Button"
+ }
+
+ MouseArea {
+ id: mouse
+ anchors.fill: parent
+ hoverEnabled: true
+
+ property bool down: false
+
+ onPressed: {
+ down = true;
+
+ rotationTransform.axis.x = 0;
+ rotationTransform.axis.y = 0;
+ rotationTransform.origin.x = button.width / 2.0
+ rotationTransform.origin.y = button.height / 2.0
+
+ if (mouseX > parent.width - 30)
+ {
+ rotationTransform.origin.x = 0;
+ rotationTransform.axis.y = 1;
+ rotationTransform.angle = 15;
+ return;
+ }
+
+ if (mouseX < 30) {
+ rotationTransform.origin.x = button.width;
+ rotationTransform.axis.y = 1;
+ rotationTransform.angle = -15;
+ return;
+ }
+
+ if (mouseY < 30) {
+ rotationTransform.origin.y = button.height;
+ rotationTransform.axis.x = 1;
+ rotationTransform.angle = 15;
+ return;
+ }
+
+ if (mouseY > parent.height - 30) {
+ rotationTransform.origin.y = 0;
+ rotationTransform.axis.x = 1;
+ rotationTransform.angle = -15;
+ return;
+ }
+
+ scaleTransform.xScale = 0.95;
+ scaleTransform.yScale = 0.95;
+ }
+
+ onCanceled: {
+ reset();
+ down = false;
+ }
+
+ onReleased: {
+ reset();
+ if (down) {
+ button.clicked();
+ }
+ }
+
+ onExited: {
+ reset();
+ down = false;
+ }
+
+ function reset() {
+ scaleTransform.xScale = 1;
+ scaleTransform.yScale = 1;
+ rotationTransform.angle = 0;
+ }
+ }
+
+ Keys.onReturnPressed: {
+ button.clicked();
+ }
+}
diff --git a/qml/Pages/skin-bank.js b/qml/Pages/skin-bank.js
index cd6f7b54..130cfac5 100644
--- a/qml/Pages/skin-bank.js
+++ b/qml/Pages/skin-bank.js
@@ -13,3 +13,4 @@ var CARD_SUIT_DIR = AppPath + "/image/card/suit/";
var DELAYED_TRICK_DIR = AppPath + "/image/card/delayedTrick/";
var EQUIP_ICON_DIR = AppPath + "/image/card/equipIcon/";
var PIXANIM_DIR = AppPath + "/image/anim/"
+var TILE_ICON_DIR = AppPath + "/image/button/tileicon/"
diff --git a/qml/main.qml b/qml/main.qml
index 813618ab..dddb64e4 100644
--- a/qml/main.qml
+++ b/qml/main.qml
@@ -47,9 +47,11 @@ Item {
Component { id: generalsOverview; GeneralsOverview {} }
Component { id: cardsOverview; CardsOverview {} }
Component { id: room; Room {} }
+ Component { id: aboutPage; About {} }
property var generalsOverviewPage
property var cardsOverviewPage
+ property alias aboutPage: aboutPage
property bool busy: false
BusyIndicator {
diff --git a/src/main.cpp b/src/main.cpp
index da7123cd..07865fc2 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -114,6 +114,9 @@ int main(int argc, char *argv[])
#endif
app = new QApplication(argc, argv);
+#ifdef DESKTOP_BUILD
+ ((QApplication *)app)->setWindowIcon(QIcon("image/icon.png"));
+#endif
#define SHOW_SPLASH_MSG(msg) \
splash.showMessage(msg, Qt::AlignHCenter | Qt::AlignBottom);
diff --git a/src/pch.h b/src/pch.h
index d7851be5..bc19afce 100644
--- a/src/pch.h
+++ b/src/pch.h
@@ -18,4 +18,8 @@ typedef int LuaFunction;
#include
#include
+#if !defined (Q_OS_ANDROID) && !defined (Q_OS_WASM)
+#define DESKTOP_BUILD
+#endif
+
#endif // _PCH_H