Merge remote-tracking branch 'origin/master' into dev-YuQi

This commit is contained in:
YoumuKon 2024-03-28 20:25:17 +08:00
commit aff3f4e7a9
161 changed files with 3987 additions and 2893 deletions

View File

@ -32,6 +32,7 @@ jobs:
target: 'desktop' target: 'desktop'
arch: 'win64_mingw' arch: 'win64_mingw'
modules: 'qtmultimedia qt5compat qtshadertools' modules: 'qtmultimedia qt5compat qtshadertools'
tools: 'tools_opensslv3_x64'
- name: Disable PCH - name: Disable PCH
shell: bash shell: bash
@ -46,6 +47,9 @@ jobs:
working-directory: ${{github.workspace}} working-directory: ${{github.workspace}}
env: env:
CMAKE_PREFIX_PATH: ${{env.Qt6_Dir}} CMAKE_PREFIX_PATH: ${{env.Qt6_Dir}}
OPENSSL_ROOT_DIR: ${{env.Qt6_Dir}}/../../Tools/OpenSSLv3/Win_x64
OPENSSL_INCLUDE_DIR: ${{env.OPENSSL_ROOT_DIR}}/include
OPENSSL_CRYPTO_LIBRARY: ${{env.OPENSSL_ROOT_DIR}}/bin/libcrypto-3-x64.dll
run: | run: |
cmake -DCMAKE_BUILD_TYPE=MinSizeRel -G "MinGW Makefiles" -B ${{github.workspace}}/build cmake -DCMAKE_BUILD_TYPE=MinSizeRel -G "MinGW Makefiles" -B ${{github.workspace}}/build
@ -74,7 +78,7 @@ jobs:
cp build/zh_CN.qm FreeKill-release cp build/zh_CN.qm FreeKill-release
cp build/en_US.qm FreeKill-release cp build/en_US.qm FreeKill-release
cp ../Qt/6.5.3/mingw_64/bin/li*.dll FreeKill-release cp ../Qt/6.5.3/mingw_64/bin/li*.dll FreeKill-release
cp '/c/Program Files/OpenSSL/bin/libcrypto-1_1-x64.dll' FreeKill-release cp ../Qt/Tools/OpenSSLv3/Win_x64/bin/libcrypto-3-x64.dll FreeKill-release
7z a -t7z FreeKill-release.7z FreeKill-release -r -mx=9 -m0=LZMA2 -ms=10m -mf=on -mhc=on -mmt=on 7z a -t7z FreeKill-release.7z FreeKill-release -r -mx=9 -m0=LZMA2 -ms=10m -mf=on -mhc=on -mmt=on
- name: Upload Release - name: Upload Release

View File

@ -1,5 +1,81 @@
# ChangeLog # ChangeLog
## v0.4.8 & v0.4.9
- Qml: 新增leval函数可获得lua表达式的值
- 新增AbstractRoom类 去除冗余
- 修gameOver相关bug或许
- 从Utility那里搬运了askForYiji和doYiji两个函数负责分配
虽然暂时没实现单烧条,但先这么用着
- 修复了askForCardAndPlayers的选择中可以选择复数张牌的bug
- 为prohibitDiscard添加了输入id选项
- 正式添加对多后缀标记的支持
- 添加了一点注释
- 搬运了moveCardIntoEquip和canMoveCardIntoEquip
- 为选牌的默认prompt添加了目标
- 完善了朱雀羽扇的判定
- 修复了抽选武将牌堆时未删除已选武将的bug
- 修复了maxCard标记不识别“-turn”以外标记的bug
- 修复了obtaincard实际不能接受id数组的bug
- CardItem一律可长按除了卡牌一览
- Qml Mark在QML中可获得主人的id
- Qml Mark可实现某某视角完全不可见
- 隐藏#开头的pile
- 可自定义interaction了
- LogMessage新增toast成员
- 修复投降杀人bug
___
## v0.4.6 & v0.4.7
- 攻击范围状态技类新增基础值修正函数
- 伤害值在一个技能处理后小于1会终止当前事件
- 不向不能使用【无懈可击】的角色询问使用【无懈可击】
- 修正在濒死插结中有人死亡后仍然会向该角色求桃的情况
- 将PreCardUse和PreCardRespond时机移至实体牌移动之前
- 调整改判函数原判定牌置入弃牌堆的原因
- 修正【朱雀羽扇】、【借刀杀人】、【酒】
- 为使用流程和Aim流程增加属性additionalEffect用于指定额外结算次数OL版顺带移动【五谷丰登】开启和关
闭AG的位置
- 为视为技新增after_use方法处理转化牌后的后续操作
- 修复伤害流程时机触发者不变问题;
- 修复旁观休整的问题;
- 修复可移动场上牌判断函数未判断虚拟牌名的问题。
- 修复传入数组的extraPile无法收回
- 被弃置牌的log添加操作者
- beforeMaxHpChanged的num可以被修改
- 额外回合增加skillName
- 修复亮将技能和禁止亮将
- 水一些注释和格式
- git报错优化
- 防止反复shutdown同一事件
- 将Utility如canUseCardTo的一些函数搬运到了本体
- 为技能添加hooked_piles属性当失去技能时自动弃置hooked_piles内的所有私人牌堆
- 修复了添加技能没写source_skill的bug
- 修复了ActiveSkill的interaction不传入Skill本身而是metatable的bug
- 修复了主动询问canUse时没有传入extra_data的bug
- 修复了多选时按钮选项变回空白的bug
- 修复了判定阶段被中途拿走判定牌后报错的bug
___
## v0.4.4 & 0.4.5
禁将增强修复bug
UsableSkill的expand_pile功能加强
___
## v0.4.3
1. 事件栈和实际的函数调用栈分离
2. 2v2选将专用的MiniGame
3. 各种小修小补
___
## v0.4.2 && v0.4.1 ## v0.4.2 && v0.4.1
1. 修复和完善qml mark 1. 修复和完善qml mark

View File

@ -1,8 +1,12 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# ------------------------------------------------------------
# CMake+QT
# 2022-01-24 2023-02-21 v0.0.1
# ------------------------------------------------------------
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
project(FreeKill VERSION 0.4.2) project(FreeKill VERSION 0.4.9)
add_definitions(-DFK_VERSION=\"${CMAKE_PROJECT_VERSION}\") add_definitions(-DFK_VERSION=\"${CMAKE_PROJECT_VERSION}\")
find_package(Qt6 REQUIRED COMPONENTS find_package(Qt6 REQUIRED COMPONENTS

View File

@ -46,7 +46,8 @@ Item {
} }
ScriptAction { ScriptAction {
script: Backend.playSound("./audio/system/fly" + (Math.floor(Math.random() * 2) + 1)); script: Backend.playSound("./audio/system/fly" +
(Math.floor(Math.random() * 2) + 1));
} }
ParallelAnimation { ParallelAnimation {
@ -91,7 +92,8 @@ Item {
} }
ScriptAction { ScriptAction {
script: Backend.playSound("./audio/system/egg" + (Math.floor(Math.random() * 2) + 1)); script: Backend.playSound("./audio/system/egg" +
(Math.floor(Math.random() * 2) + 1));
} }
ParallelAnimation { ParallelAnimation {

View File

@ -43,7 +43,8 @@ Item {
id: pointToAnimation id: pointToAnimation
running: false running: false
ScriptAction { ScriptAction {
script: Backend.playSound("./audio/system/fly" + (Math.floor(Math.random() * 2) + 1)); script: Backend.playSound("./audio/system/fly" +
(Math.floor(Math.random() * 2) + 1));
} }
ParallelAnimation { ParallelAnimation {
@ -80,7 +81,8 @@ Item {
} }
ScriptAction { ScriptAction {
script: Backend.playSound("./audio/system/flower" + (Math.floor(Math.random() * 2) + 1)); script: Backend.playSound("./audio/system/flower" +
(Math.floor(Math.random() * 2) + 1));
} }
ParallelAnimation { ParallelAnimation {

View File

@ -46,7 +46,8 @@ Item {
} }
ScriptAction { ScriptAction {
script: Backend.playSound("./audio/system/fly" + (Math.floor(Math.random() * 2) + 1)); script: Backend.playSound("./audio/system/fly" +
(Math.floor(Math.random() * 2) + 1));
} }
ParallelAnimation { ParallelAnimation {
@ -91,7 +92,8 @@ Item {
} }
ScriptAction { ScriptAction {
script: Backend.playSound("./audio/system/egg" + (Math.floor(Math.random() * 2) + 1)); script: Backend.playSound("./audio/system/egg" +
(Math.floor(Math.random() * 2) + 1));
} }
ParallelAnimation { ParallelAnimation {

View File

@ -64,7 +64,8 @@ Item {
script: { script: {
egg.opacity = 0; egg.opacity = 0;
whip.opacity = 1; whip.opacity = 1;
Backend.playSound("./audio/system/egg" + (Math.floor(Math.random() * 2) + 1)); Backend.playSound("./audio/system/egg" +
(Math.floor(Math.random() * 2) + 1));
} }
} }
PropertyAnimation { PropertyAnimation {

View File

@ -27,7 +27,8 @@ Item {
y: start.y - height / 2 + yOffset y: start.y - height / 2 + yOffset
scale: 0.7 scale: 0.7
opacity: 0 opacity: 0
rotation: (Math.atan(Math.abs(end.y - start.y) / Math.abs(end.x - start.x)) rotation: (Math.atan(Math.abs(end.y - start.y)
/ Math.abs(end.x - start.x))
/ Math.PI * 180 - 90) * (end.x > start.x ? -1 : 1) / Math.PI * 180 - 90) * (end.x > start.x ? -1 : 1)
} }

View File

@ -57,7 +57,7 @@ Flickable {
if (!card) return; if (!card) return;
cardPic.setData(card.toData()); 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 = luatr(name);
skillDesc.text = Backend.translate(":" + name); skillDesc.text = luatr(":" + name);
} }
} }

View File

@ -21,12 +21,12 @@ Item {
ToolButton { ToolButton {
opacity: stack.depth > 1 ? 1 : 0 opacity: stack.depth > 1 ? 1 : 0
Behavior on opacity { NumberAnimation { duration: 100 } } Behavior on opacity { NumberAnimation { duration: 100 } }
text: Backend.translate("Back") text: luatr("Back")
onClicked: stack.pop() onClicked: stack.pop()
} }
Label { Label {
text: Backend.translate("Enable free assign") text: luatr("Enable free assign")
elide: Label.ElideRight elide: Label.ElideRight
horizontalAlignment: Qt.AlignHCenter horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter verticalAlignment: Qt.AlignVCenter
@ -46,12 +46,11 @@ Item {
} }
ToolButton { ToolButton {
text: Backend.translate("Search") text: luatr("Search")
enabled: word.text !== "" enabled: word.text !== ""
onClicked: { onClicked: {
if (stack.depth > 1) stack.pop(); if (stack.depth > 1) stack.pop();
generalModel = JSON.parse(Backend.callLuaFunction("SearchAllGenerals", generalModel = lcall("SearchAllGenerals", word.text);
[word.text]));
stack.push(generalList); stack.push(generalList);
word.text = ""; word.text = "";
} }
@ -88,14 +87,13 @@ Item {
height: 40 height: 40
Text { Text {
text: Backend.translate(name) text: luatr(name)
color: "#E4D5A0" color: "#E4D5A0"
anchors.centerIn: parent anchors.centerIn: parent
} }
onClicked: { onClicked: {
generalModel = JSON.parse(Backend.callLuaFunction("GetGenerals", generalModel = lcall("GetGenerals", packages.get(index).name);
[packages.get(index).name]));
stack.push(generalList); stack.push(generalList);
} }
} }
@ -134,7 +132,7 @@ Item {
} }
function load() { function load() {
const packs = JSON.parse(Backend.callLuaFunction("GetAllGeneralPack", [])); const packs = lcall("GetAllGeneralPack");
packs.forEach((name) => packages.append({ name: name })); packs.forEach((name) => packages.append({ name: name }));
} }

View File

@ -39,21 +39,23 @@ Flickable {
skillDesc.text = ""; skillDesc.text = "";
extra_data.generals.forEach((g) => { extra_data.generals.forEach((g) => {
const data = JSON.parse(Backend.callLuaFunction("GetGeneralDetail", [g])); const data = lcall("GetGeneralDetail", g);
skillDesc.append(Backend.translate(data.kingdom) + " " + Backend.translate(g) + " " + data.hp + "/" + data.maxHp); skillDesc.append(luatr(data.kingdom) + " " + luatr(g) + " " + data.hp +
"/" + data.maxHp);
if (data.companions.length > 0){ if (data.companions.length > 0){
let ret = ''; let ret = '';
ret += "<font color=\"slategrey\"><b>" + Backend.translate("Companions") + "</b>: "; ret +="<font color=\"slategrey\"><b>" + luatr("Companions") + "</b>: ";
data.companions.forEach(t => { data.companions.forEach(t => {
ret += Backend.translate(t) + ' ' ret += luatr(t) + ' '
}); });
skillDesc.append(ret) skillDesc.append(ret)
} }
data.skill.forEach(t => { data.skill.forEach(t => {
skillDesc.append("<b>" + Backend.translate(t.name) + "</b>: " + t.description) skillDesc.append("<b>" + luatr(t.name) + "</b>: " + t.description)
}); });
data.related_skill.forEach(t => { data.related_skill.forEach(t => {
skillDesc.append("<font color=\"purple\"><b>" + Backend.translate(t.name) + "</b>: " + t.description + "</font>") skillDesc.append("<font color=\"purple\"><b>" + luatr(t.name) +
"</b>: " + t.description + "</font>")
}); });
skillDesc.append("\n"); skillDesc.append("\n");
}); });

View File

@ -38,7 +38,7 @@ Flickable {
RowLayout { RowLayout {
MetroButton { MetroButton {
text: Backend.translate("Give Flower") text: luatr("Give Flower")
onClicked: { onClicked: {
enabled = false; enabled = false;
root.givePresent("Flower"); root.givePresent("Flower");
@ -47,7 +47,7 @@ Flickable {
} }
MetroButton { MetroButton {
text: Backend.translate("Give Egg") text: luatr("Give Egg")
onClicked: { onClicked: {
enabled = false; enabled = false;
if (Math.random() < 0.03) { if (Math.random() < 0.03) {
@ -60,7 +60,7 @@ Flickable {
} }
MetroButton { MetroButton {
text: Backend.translate("Give Wine") text: luatr("Give Wine")
enabled: Math.random() < 0.3 enabled: Math.random() < 0.3
onClicked: { onClicked: {
enabled = false; enabled = false;
@ -70,7 +70,7 @@ Flickable {
} }
MetroButton { MetroButton {
text: Backend.translate("Give Shoe") text: luatr("Give Shoe")
enabled: Math.random() < 0.3 enabled: Math.random() < 0.3
onClicked: { onClicked: {
enabled = false; enabled = false;
@ -80,7 +80,10 @@ Flickable {
} }
MetroButton { MetroButton {
text: config.blockedUsers.indexOf(screenName.text) === -1 ? Backend.translate("Block Chatter") : Backend.translate("Unblock Chatter") text: {
const blocked = !config.blockedUsers.includes(screenName.text);
return blocked ? luatr("Block Chatter") : luatr("Unblock Chatter");
}
enabled: pid !== Self.id && pid > 0 enabled: pid !== Self.id && pid > 0
onClicked: { onClicked: {
const idx = config.blockedUsers.indexOf(screenName.text); const idx = config.blockedUsers.indexOf(screenName.text);
@ -94,7 +97,7 @@ Flickable {
} }
MetroButton { MetroButton {
text: Backend.translate("Kick From Room") text: luatr("Kick From Room")
visible: !roomScene.isStarted && roomScene.isOwner visible: !roomScene.isStarted && roomScene.isOwner
enabled: pid !== Self.id enabled: pid !== Self.id
onClicked: { onClicked: {
@ -157,7 +160,7 @@ Flickable {
skillDesc.text = ""; skillDesc.text = "";
const id = extra_data.photo.playerid; const id = extra_data.photo.playerid;
if (id == 0) return; if (id === 0) return;
root.pid = id; root.pid = id;
screenName.text = extra_data.photo.screenName; screenName.text = extra_data.photo.screenName;
@ -165,36 +168,34 @@ Flickable {
deputyChara.name = extra_data.photo.deputyGeneral; deputyChara.name = extra_data.photo.deputyGeneral;
if (!config.observing) { if (!config.observing) {
const gamedata = JSON.parse(Backend.callLuaFunction("GetPlayerGameData", [id])); const gamedata = lcall("GetPlayerGameData", id);
const total = gamedata[0]; const total = gamedata[0];
const win = gamedata[1]; const win = gamedata[1];
const run = gamedata[2]; const run = gamedata[2];
const totalTime = gamedata[3]; const totalTime = gamedata[3];
const winRate = (win / total) * 100; const winRate = (win / total) * 100;
const runRate = (run / total) * 100; const runRate = (run / total) * 100;
playerGameData.text = total === 0 ? Backend.translate("Newbie") : playerGameData.text = total === 0 ? luatr("Newbie") :
Backend.translate("Win=%1 Run=%2 Total=%3").arg(winRate.toFixed(2)) luatr("Win=%1 Run=%2 Total=%3").arg(winRate.toFixed(2))
.arg(runRate.toFixed(2)).arg(total); .arg(runRate.toFixed(2)).arg(total);
const h = (totalTime / 3600).toFixed(2); const h = (totalTime / 3600).toFixed(2);
const m = Math.floor(totalTime / 60); const m = Math.floor(totalTime / 60);
if (m < 100) { if (m < 100) {
playerGameData.text += " " + Backend.translate("TotalGameTime: %1 min").arg(m); playerGameData.text += " " + luatr("TotalGameTime: %1 min").arg(m);
} else { } else {
playerGameData.text += " " + Backend.translate("TotalGameTime: %1 h").arg(h); playerGameData.text += " " + luatr("TotalGameTime: %1 h").arg(h);
} }
} }
const data = JSON.parse(Backend.callLuaFunction("GetPlayerSkills", [id])); lcall("GetPlayerSkills", id).forEach(t => {
data.forEach(t => { skillDesc.append("<b>" + luatr(t.name) + "</b>: " + t.description)
skillDesc.append("<b>" + Backend.translate(t.name) + "</b>: " + t.description)
}); });
const equips = JSON.parse(Backend.callLuaFunction("GetPlayerEquips", [id])); lcall("GetPlayerEquips", id).forEach(cid => {
equips.forEach(cid => { const t = lcall("GetCardData", cid);
const t = JSON.parse(Backend.callLuaFunction("GetCardData", [cid]));
skillDesc.append("--------------------"); skillDesc.append("--------------------");
skillDesc.append("<b>" + Backend.translate(t.name) + "</b>: " + Backend.translate(":" + t.name)); skillDesc.append("<b>" + luatr(t.name) + "</b>: " + luatr(":" + t.name));
}); });
} }
} }

View File

@ -28,13 +28,13 @@ Item {
ColumnLayout { ColumnLayout {
Text { Text {
color: "#E4D5A0" color: "#E4D5A0"
text: Backend.translate(gname) text: luatr(gname)
} }
GridLayout { GridLayout {
columns: 6 columns: 6
Repeater { Repeater {
model: JSON.parse(Backend.callLuaFunction("GetSameGenerals", [gname])) model: lcall("GetSameGenerals", gname)
GeneralCardItem { GeneralCardItem {
name: modelData name: modelData

View File

@ -6,7 +6,8 @@ Image {
width: 64 width: 64
height: 64 height: 64
source: SkinBank.getGeneralExtraPic(general, "avatar/") ?? SkinBank.getGeneralPicture(general) source: SkinBank.getGeneralExtraPic(general, "avatar/")
?? SkinBank.getGeneralPicture(general)
// sourceSize.width: 250 // sourceSize.width: 250
// sourceSize.height: 292 // sourceSize.height: 292
property bool useSmallPic: !!SkinBank.getGeneralExtraPic(general, "avatar/") property bool useSmallPic: !!SkinBank.getGeneralExtraPic(general, "avatar/")

View File

@ -18,7 +18,8 @@ Rectangle {
avatar = "__observer"; avatar = "__observer";
} }
chatLogBox.append({ chatLogBox.append({
avatar: data.general || roomScene.getPhoto(data.sender)?.general || avatar || "unknown", avatar: data.general || roomScene.getPhoto(data.sender)?.general ||
avatar || "unknown",
general: general, general: general,
msg: data.msg, msg: data.msg,
userName: data.userName, userName: data.userName,
@ -28,7 +29,7 @@ Rectangle {
} }
function loadSkills() { function loadSkills() {
for (let i = 1; i <= 16; i++) { for (let i = 1; i <= 23; i++) {
skills.append({ name: "fastchat_m", idx: i }); skills.append({ name: "fastchat_m", idx: i });
} }
} }
@ -60,7 +61,7 @@ Rectangle {
anchors.right: !isSelf ? undefined : avatarPic.left anchors.right: !isSelf ? undefined : avatarPic.left
anchors.margins: 6 anchors.margins: 6
font.pixelSize: 14 font.pixelSize: 14
text: userName + (general ? (" (" + Backend.translate(general) + ")") : "") text: userName + (general ? (" (" + luatr(general) + ")") : "")
+ ' <font color="grey">[' + time + "]</font>" + ' <font color="grey">[' + time + "]</font>"
} }
@ -123,7 +124,8 @@ Rectangle {
anchors.centerIn: parent anchors.centerIn: parent
source: "../../image/emoji/" + index source: "../../image/emoji/" + index
} }
onClicked: chatEdit.insert(chatEdit.cursorPosition, "{emoji" + index + "}"); onClicked: chatEdit.insert(chatEdit.cursorPosition,
"{emoji" + index + "}");
} }
} }
@ -142,14 +144,14 @@ Rectangle {
delegate: ItemDelegate { delegate: ItemDelegate {
width: soundSelector.width width: soundSelector.width
height: 30 height: 30
text: Backend.translate("$" + name + (idx ? idx.toString() : "")) text: luatr("$" + name + (idx ? idx.toString() : ""))
onClicked: { onClicked: {
opTimer.start(); opTimer.start();
const general = roomScene.getPhoto(Self.id).general; const general = roomScene.getPhoto(Self.id).general;
let skill = "fastchat_m"; let skill = "fastchat_m";
if (general !== "") { if (general !== "") {
const data = JSON.parse(Backend.callLuaFunction("GetGeneralDetail", [general])); const data = lcall("GetGeneralDetail", general);
const gender = data.gender; const gender = data.gender;
if (gender !== 1) { if (gender !== 1) {
skill = "fastchat_f"; skill = "fastchat_f";

View File

@ -13,7 +13,7 @@ Rectangle {
} }
function loadSkills() { function loadSkills() {
for (let i = 1; i <= 16; i++) { for (let i = 1; i <= 23; i++) {
skills.append({ name: "fastchat_m", idx: i }); skills.append({ name: "fastchat_m", idx: i });
} }
} }
@ -54,7 +54,8 @@ Rectangle {
anchors.centerIn: parent anchors.centerIn: parent
source: "../../image/emoji/" + index source: "../../image/emoji/" + index
} }
onClicked: chatEdit.insert(chatEdit.cursorPosition, "{emoji" + index + "}"); onClicked: chatEdit.insert(chatEdit.cursorPosition,
"{emoji" + index + "}");
} }
} }
@ -73,14 +74,14 @@ Rectangle {
delegate: ItemDelegate { delegate: ItemDelegate {
width: soundSelector.width width: soundSelector.width
height: 30 height: 30
text: Backend.translate("$" + name + (idx ? idx.toString() : "")) text: luatr("$" + name + (idx ? idx.toString() : ""))
onClicked: { onClicked: {
opTimer.start(); opTimer.start();
const general = roomScene.getPhoto(Self.id).general; const general = roomScene.getPhoto(Self.id).general;
let skill = "fastchat_m"; let skill = "fastchat_m";
if (general !== "") { if (general !== "") {
const data = JSON.parse(Backend.callLuaFunction("GetGeneralDetail", [general])); const data = lcall("GetGeneralDetail", general);
const gender = data.gender; const gender = data.gender;
if (gender !== 1) { if (gender !== 1) {
skill = "fastchat_f"; skill = "fastchat_f";

View File

@ -15,7 +15,7 @@ QtObject {
property string roomBg property string roomBg
property string bgmFile property string bgmFile
property string language property string language
property list<string> disabledPack: [] // property list<string> disabledPack: []
property string preferedMode property string preferedMode
property int preferedPlayerNum property int preferedPlayerNum
property int preferredGeneralNum property int preferredGeneralNum
@ -23,9 +23,12 @@ QtObject {
property real bgmVolume property real bgmVolume
property bool disableMsgAudio property bool disableMsgAudio
property bool hideUseless property bool hideUseless
property list<string> disabledGenerals: [] // property list<string> disabledGenerals: []
property list<var> disableGeneralSchemes: [] // property list<var> disableGeneralSchemes: []
property int disableSchemeIdx: 0 // property int disableSchemeIdx: 0
property list<var> disableSchemes: []
property int currentDisableIdx: 0
property var curScheme
property int preferredTimeout property int preferredTimeout
property int preferredLuckTime property int preferredLuckTime
@ -52,9 +55,9 @@ QtObject {
property list<string> blockedUsers: [] property list<string> blockedUsers: []
property int totalTime: 0 // FIXME: only for notifying property int totalTime: 0 // FIXME: only for notifying
onDisabledGeneralsChanged: { // onDisabledGeneralsChanged: {
disableGeneralSchemes[disableSchemeIdx] = disabledGenerals; // disableGeneralSchemes[disableSchemeIdx] = disabledGenerals;
} // }
function loadConf() { function loadConf() {
conf = JSON.parse(Backend.loadConf()); conf = JSON.parse(Backend.loadConf());
@ -75,7 +78,7 @@ QtObject {
return 'en_US'; return 'en_US';
} }
})(); })();
disabledPack = conf.disabledPack ?? [ "test_p_0" ]; // disabledPack = conf.disabledPack ?? [ "test_p_0" ];
preferedMode = conf.preferedMode ?? "aaa_role_mode"; preferedMode = conf.preferedMode ?? "aaa_role_mode";
preferedPlayerNum = conf.preferedPlayerNum ?? 2; preferedPlayerNum = conf.preferedPlayerNum ?? 2;
preferredGeneralNum = conf.preferredGeneralNum ?? 3; preferredGeneralNum = conf.preferredGeneralNum ?? 3;
@ -87,9 +90,17 @@ QtObject {
preferredTimeout = conf.preferredTimeout ?? 15; preferredTimeout = conf.preferredTimeout ?? 15;
preferredLuckTime = conf.preferredLuckTime ?? 0; preferredLuckTime = conf.preferredLuckTime ?? 0;
firstRun = conf.firstRun ?? true; firstRun = conf.firstRun ?? true;
disabledGenerals = conf.disabledGenerals ?? []; // disabledGenerals = conf.disabledGenerals ?? [];
disableGeneralSchemes = conf.disableGeneralSchemes ?? [ disabledGenerals ]; // disableGeneralSchemes = conf.disableGeneralSchemes ?? [ disabledGenerals ];
disableSchemeIdx = conf.disableSchemeIdx ?? 0; // disableSchemeIdx = conf.disableSchemeIdx ?? 0;
disableSchemes = conf.disableSchemes ?? [{
name: "",
banPkg: {}, // :
normalPkg: {}, // :
banCardPkg: [], //
}];
currentDisableIdx = conf.currentDisableIdx ?? 0;
curScheme = disableSchemes[currentDisableIdx];
blockedUsers = conf.blockedUsers ?? []; blockedUsers = conf.blockedUsers ?? [];
} }
@ -104,7 +115,7 @@ QtObject {
conf.roomBg = roomBg; conf.roomBg = roomBg;
conf.bgmFile = bgmFile; conf.bgmFile = bgmFile;
conf.language = language; conf.language = language;
conf.disabledPack = disabledPack; // conf.disabledPack = disabledPack;
conf.preferedMode = preferedMode; conf.preferedMode = preferedMode;
conf.preferedPlayerNum = preferedPlayerNum; conf.preferedPlayerNum = preferedPlayerNum;
conf.ladyImg = ladyImg; conf.ladyImg = ladyImg;
@ -116,9 +127,12 @@ QtObject {
conf.preferredTimeout = preferredTimeout; conf.preferredTimeout = preferredTimeout;
conf.preferredLuckTime = preferredLuckTime; conf.preferredLuckTime = preferredLuckTime;
conf.firstRun = firstRun; conf.firstRun = firstRun;
conf.disabledGenerals = disabledGenerals; // conf.disabledGenerals = disabledGenerals;
conf.disableGeneralSchemes = disableGeneralSchemes; // conf.disableGeneralSchemes = disableGeneralSchemes;
conf.disableSchemeIdx = disableSchemeIdx; // conf.disableSchemeIdx = disableSchemeIdx;
disableSchemes[currentDisableIdx] = curScheme;
conf.disableSchemes = disableSchemes;
conf.currentDisableIdx = currentDisableIdx;
conf.blockedUsers = blockedUsers; conf.blockedUsers = blockedUsers;
Backend.saveConf(JSON.stringify(conf, undefined, 2)); Backend.saveConf(JSON.stringify(conf, undefined, 2));

View File

@ -9,7 +9,7 @@ ColumnLayout {
anchors.rightMargin: 8 anchors.rightMargin: 8
spacing: 16 spacing: 16
Text { Text {
text: Backend.translate("BGM Volume") text: luatr("BGM Volume")
} }
Slider { Slider {
Layout.rightMargin: 16 Layout.rightMargin: 16
@ -25,7 +25,7 @@ ColumnLayout {
anchors.rightMargin: 8 anchors.rightMargin: 8
spacing: 16 spacing: 16
Text { Text {
text: Backend.translate("Effect Volume") text: luatr("Effect Volume")
} }
Slider { Slider {
Layout.rightMargin: 16 Layout.rightMargin: 16
@ -38,13 +38,13 @@ ColumnLayout {
} }
Switch { Switch {
text: Backend.translate("Disable message audio") text: luatr("Disable message audio")
checked: config.disableMsgAudio checked: config.disableMsgAudio
onCheckedChanged: config.disableMsgAudio = checked; onCheckedChanged: config.disableMsgAudio = checked;
} }
Switch { Switch {
text: Backend.translate("Hide unselectable cards") text: luatr("Hide unselectable cards")
checked: config.hideUseless checked: config.hideUseless
onCheckedChanged: { onCheckedChanged: {
config.hideUseless = checked; config.hideUseless = checked;

View File

@ -10,7 +10,7 @@ ColumnLayout {
anchors.rightMargin: 8 anchors.rightMargin: 8
spacing: 16 spacing: 16
Text { Text {
text: Backend.translate("Lobby BG") text: luatr("Lobby BG")
} }
TextField { TextField {
Layout.fillWidth: true Layout.fillWidth: true
@ -30,7 +30,7 @@ ColumnLayout {
anchors.rightMargin: 8 anchors.rightMargin: 8
spacing: 16 spacing: 16
Text { Text {
text: Backend.translate("Room BG") text: luatr("Room BG")
} }
TextField { TextField {
Layout.fillWidth: true Layout.fillWidth: true
@ -50,7 +50,7 @@ ColumnLayout {
anchors.rightMargin: 8 anchors.rightMargin: 8
spacing: 16 spacing: 16
Text { Text {
text: Backend.translate("Game BGM") text: luatr("Game BGM")
} }
TextField { TextField {
Layout.fillWidth: true Layout.fillWidth: true
@ -70,7 +70,7 @@ ColumnLayout {
anchors.rightMargin: 8 anchors.rightMargin: 8
spacing: 16 spacing: 16
Text { Text {
text: Backend.translate("Poster Girl") text: luatr("Poster Girl")
} }
TextField { TextField {
Layout.fillWidth: true Layout.fillWidth: true

View File

@ -11,73 +11,103 @@ Item {
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
RowLayout { RowLayout {
Layout.fillWidth: true
anchors.rightMargin: 8 anchors.rightMargin: 8
spacing: 16 spacing: 16
Text { Text {
text: Backend.translate("Ban List") text: luatr("Ban List")
} }
ComboBox { ComboBox {
id: banCombo id: banCombo
textRole: "name" textRole: "name"
Layout.fillWidth: true
model: ListModel { model: ListModel {
id: banComboList id: banComboList
} }
onCurrentIndexChanged: { onCurrentIndexChanged: {
config.disableSchemeIdx = currentIndex; word.text = "";
config.disabledGenerals = config.disableGeneralSchemes[currentIndex]; config.disableSchemes[config.currentDisableIdx] = config.curScheme;
config.currentDisableIdx = currentIndex;
config.curScheme = config.disableSchemes[currentIndex];
} }
} }
Button { GridLayout {
text: Backend.translate("New") columns: 2
onClicked: {
const i = config.disableGeneralSchemes.length;
banComboList.append({
name: Backend.translate("List") + (i + 1),
});
config.disableGeneralSchemes.push([]);
}
}
Button { Button {
text: Backend.translate("Clear") text: luatr("New")
onClicked: { onClicked: {
config.disabledGenerals = []; const i = config.disableSchemes.length;
} banComboList.append({
} name: luatr("List") + (i + 1),
});
Button { config.disableSchemes.push({
text: Backend.translate("Export") name: "",
onClicked: { banPkg: {},
Backend.copyToClipboard(JSON.stringify(config.disabledGenerals)); normalPkg: {},
toast.show(Backend.translate("Export Success")); banCardPkg: [],
} });
}
Button {
text: Backend.translate("Import")
onClicked: {
const str = Backend.readClipboard();
let data;
try {
data = JSON.parse(str);
} catch (e) {
toast.show(Backend.translate("Not Legal"));
return;
} }
if (!data instanceof Array) { }
toast.show(Backend.translate("Not JSON"));
return; Button {
text: luatr("Clear")
onClicked: {
config.curScheme.banPkg = {};
config.curScheme.normalPkg = {};
config.curScheme.banCardPkg = [];
config.curSchemeChanged();
} }
let d = []; }
for (let e of data) {
if (typeof e === "string" && Backend.translate(e) !== e) { Button {
d.push(e); text: luatr("Export")
onClicked: {
Backend.copyToClipboard(JSON.stringify(config.curScheme));
toast.show(luatr("Export Success"));
}
}
Button {
text: luatr("Import")
onClicked: {
const str = Backend.readClipboard();
let data;
try {
data = JSON.parse(str);
} catch (e) {
toast.show(luatr("Not Legal"));
return;
}
if (!data instanceof Object || !data.banPkg || !data.normalPkg
|| !data.banCardPkg) {
toast.show(luatr("Not JSON"));
return;
}
config.curScheme = data;
if (data.name) {
banComboList.get(banCombo.currentIndex).name = data.name;
} }
} }
config.disabledGenerals = d; }
toast.show(Backend.translate("Import Success")); }
TextField {
id: word
clip: true
leftPadding: 5
rightPadding: 5
}
Button {
text: luatr("Rename")
enabled: word.text !== ""
onClicked: {
banComboList.get(banCombo.currentIndex).name = word.text;
config.curScheme.name = word.text;
word.text = "";
} }
} }
} }
@ -86,38 +116,122 @@ Item {
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: 8 Layout.margins: 8
wrapMode: Text.WrapAnywhere wrapMode: Text.WrapAnywhere
text: Backend.translate("Help_Ban_List") text: luatr("Help_Ban_List")
} }
GridView { GridLayout {
id: listView id: grid
flow: GridLayout.TopToBottom
rows: 2
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
clip: true
cellWidth: width / 4 Text {
cellHeight: 24 wrapMode: Text.WrapAnywhere
model: config.disabledGenerals text: luatr("Ban_Generals")
delegate: Text { font.pixelSize: 18
width: listView.width font.bold: true
text: {
const prefix = modelData.split("__")[0];
let name = Backend.translate(modelData);
if (prefix !== modelData) {
name += (" (" + Backend.translate(prefix) + ")");
}
return name;
}
font.pixelSize: 16
} }
GridView {
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
cellWidth: width / 2
cellHeight: 24
model: {
let ret = [], k;
const s = config.curScheme;
for (k in s.normalPkg) {
ret.push(...s.normalPkg[k]);
}
return ret;
}
delegate: Text {
//width: banChara.width
text: {
const prefix = modelData.split("__")[0];
let name = luatr(modelData);
if (prefix !== modelData) {
name += (" (" + luatr(prefix) + ")");
}
return name;
}
font.pixelSize: 16
}
}
Text {
wrapMode: Text.WrapAnywhere
text: luatr("Ban_Packages")
font.pixelSize: 18
font.bold: true
}
GridView {
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
cellWidth: width / 2
cellHeight: 24
model: {
let ret = [], k;
const s = config.curScheme;
for (k in s.banPkg) {
ret.push(k);
}
ret.push(...s.banCardPkg)
return ret;
}
delegate: Text {
text: luatr(modelData)
font.pixelSize: 16
}
}
Text {
wrapMode: Text.WrapAnywhere
text: luatr("Whitelist_Generals")
font.pixelSize: 18
font.bold: true
}
GridView {
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
cellWidth: width / 2
cellHeight: 24
model: {
let ret = [], k;
const s = config.curScheme;
for (k in s.banPkg) {
ret.push(...s.banPkg[k]);
}
return ret;
}
delegate: Text {
text: {
const prefix = modelData.split("__")[0];
let name = luatr(modelData);
if (prefix !== modelData) {
name += (" (" + luatr(prefix) + ")");
}
return name;
}
font.pixelSize: 16
}
}
} }
} }
Component.onCompleted: { Component.onCompleted: {
for (let i = 0; i < config.disableGeneralSchemes.length; i++) { for (let i = 0; i < config.disableSchemes.length; i++) {
banComboList.append({ banComboList.append({
name: Backend.translate("List") + (i + 1), name: config.disableSchemes[i]?.name || (luatr("List") + (i + 1)),
}); });
} }
banCombo.currentIndex = config.disableSchemeIdx; banCombo.currentIndex = config.currentDisableIdx;
} }
} }

View File

@ -18,13 +18,13 @@ Item {
width: root.height width: root.height
background: Rectangle { color: "#EEEEEEEE" } background: Rectangle { color: "#EEEEEEEE" }
TabButton { TabButton {
text: Backend.translate("General Settings") text: luatr("General Settings")
} }
TabButton { TabButton {
text: Backend.translate("Package Settings") text: luatr("Package Settings")
} }
TabButton { TabButton {
text: Backend.translate("Ban General Settings") text: luatr("Ban General Settings")
} }
} }

View File

@ -17,13 +17,13 @@ Item {
width: root.height width: root.height
background: Rectangle { color: "#EEEEEEEE" } background: Rectangle { color: "#EEEEEEEE" }
TabButton { TabButton {
text: Backend.translate("Userinfo Settings") text: luatr("Userinfo Settings")
} }
TabButton { TabButton {
text: Backend.translate("BG Settings") text: luatr("BG Settings")
} }
TabButton { TabButton {
text: Backend.translate("Audio Settings") text: luatr("Audio Settings")
} }
} }

View File

@ -23,14 +23,14 @@ Item {
Text { Text {
text: { text: {
config.totalTime; config.totalTime;
const gamedata = JSON.parse(Backend.callLuaFunction("GetPlayerGameData", [Self.id])); const gamedata = lcall("GetPlayerGameData", Self.id);
const totalTime = gamedata[3]; const totalTime = gamedata[3];
const h = (totalTime / 3600).toFixed(2); const h = (totalTime / 3600).toFixed(2);
const m = Math.floor(totalTime / 60); const m = Math.floor(totalTime / 60);
if (m < 100) { if (m < 100) {
return Backend.translate("TotalGameTime: %1 min").arg(m); return luatr("TotalGameTime: %1 min").arg(m);
} else { } else {
return Backend.translate("TotalGameTime: %1 h").arg(h); return luatr("TotalGameTime: %1 h").arg(h);
} }
} }
x: 12; y: 1 x: 12; y: 1

View File

@ -16,7 +16,7 @@ Flickable {
anchors.rightMargin: 8 anchors.rightMargin: 8
spacing: 16 spacing: 16
Text { Text {
text: Backend.translate("Room Name") text: luatr("Room Name")
} }
TextField { TextField {
id: roomName id: roomName
@ -24,7 +24,7 @@ Flickable {
font.pixelSize: 18 font.pixelSize: 18
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.fillWidth: true Layout.fillWidth: true
text: Backend.translate("$RoomName").arg(Self.screenName) text: luatr("$RoomName").arg(Self.screenName)
} }
} }
@ -32,7 +32,7 @@ Flickable {
anchors.rightMargin: 8 anchors.rightMargin: 8
spacing: 16 spacing: 16
Text { Text {
text: Backend.translate("Game Mode") text: luatr("Game Mode")
} }
ComboBox { ComboBox {
id: gameModeCombo id: gameModeCombo
@ -57,16 +57,16 @@ Flickable {
columnSpacing: 20 columnSpacing: 20
columns: 4 columns: 4
Text { Text {
text: Backend.translate("Player num") text: luatr("Player num")
} }
Text { Text {
text: Backend.translate("Select generals num") text: luatr("Select generals num")
} }
Text { Text {
text: Backend.translate("Operation timeout") text: luatr("Operation timeout")
} }
Text { Text {
text: Backend.translate("Luck Card Times") text: luatr("Luck Card Times")
} }
SpinBox { SpinBox {
id: playerNum id: playerNum
@ -109,24 +109,26 @@ Flickable {
} }
} }
/*
Text { Text {
id: warning id: warning
anchors.rightMargin: 8 anchors.rightMargin: 8
visible: { visible: {
//config.disabledPack; // visible const avail = lcall("GetAvailableGeneralsNum");
const avail = JSON.parse(Backend.callLuaFunction("GetAvailableGeneralsNum", [])); const ret = avail <
const ret = avail < config.preferredGeneralNum * config.preferedPlayerNum; config.preferredGeneralNum * config.preferedPlayerNum;
return ret; return ret;
} }
text: Backend.translate("No enough generals") text: luatr("No enough generals")
color: "red" color: "red"
} }
*/
RowLayout { RowLayout {
anchors.rightMargin: 8 anchors.rightMargin: 8
spacing: 16 spacing: 16
Text { Text {
text: Backend.translate("Room Password") text: luatr("Room Password")
} }
TextField { TextField {
id: roomPassword id: roomPassword
@ -143,13 +145,13 @@ Flickable {
Switch { Switch {
id: freeAssignCheck id: freeAssignCheck
checked: Debugging ? true : false checked: Debugging ? true : false
text: Backend.translate("Enable free assign") text: luatr("Enable free assign")
} }
Switch { Switch {
id: deputyCheck id: deputyCheck
checked: Debugging ? true : false checked: Debugging ? true : false
text: Backend.translate("Enable deputy general") text: luatr("Enable deputy general")
} }
} }
@ -157,44 +159,43 @@ Flickable {
anchors.rightMargin: 8 anchors.rightMargin: 8
spacing: 16 spacing: 16
Button { Button {
text: Backend.translate("OK") text: luatr("OK")
enabled: !(warning.visible) // enabled: !(warning.visible)
onClicked: { onClicked: {
config.saveConf(); config.saveConf();
root.finished(); root.finished();
mainWindow.busy = true; mainWindow.busy = true;
let k, arr;
let disabledGenerals = config.disabledGenerals.slice(); let disabledGenerals = [];
if (disabledGenerals.length) { for (k in config.curScheme.banPkg) {
const availablePack = JSON.parse(Backend.callLuaFunction("GetAllGeneralPack", [])). arr = config.curScheme.banPkg[k];
filter((pack) => !config.disabledPack.includes(pack)); if (arr.length !== 0) {
disabledGenerals = disabledGenerals.filter((general) => { const generals = lcall("GetGenerals", k);
return availablePack.find((pack) => JSON.parse(Backend.callLuaFunction("GetGenerals", [pack])).includes(general)); disabledGenerals.push(...generals.filter(g => !arr.includes(g)));
}); }
}
disabledGenerals = Array.from(new Set(disabledGenerals)); for (k in config.curScheme.normalPkg) {
arr = config.curScheme.normalPkg[k] ?? [];
if (arr.length !== 0)
disabledGenerals.push(...arr);
} }
let disabledPack = config.disabledPack.slice(); let disabledPack = config.curScheme.banCardPkg.slice();
for (k in config.curScheme.banPkg) {
if (config.curScheme.banPkg[k].length === 0)
disabledPack.push(k);
}
config.serverHiddenPacks.forEach(p => { config.serverHiddenPacks.forEach(p => {
if (!disabledPack.includes(p)) { if (!disabledPack.includes(p)) {
disabledPack.push(p); disabledPack.push(p);
} }
}); });
const generalPacks = JSON.parse(Backend.callLuaFunction("GetAllGeneralPack", []));
for (let pk of generalPacks) {
if (disabledPack.includes(pk)) continue;
let generals = JSON.parse(Backend.callLuaFunction("GetGenerals", [pk]));
let t = generals.filter(g => !disabledGenerals.includes(g));
if (t.length === 0) {
disabledPack.push(pk);
disabledGenerals = disabledGenerals.filter(g1 => !generals.includes(g1));
}
}
ClientInstance.notifyServer( ClientInstance.notifyServer(
"CreateRoom", "CreateRoom",
JSON.stringify([roomName.text, playerNum.value, config.preferredTimeout, { JSON.stringify([roomName.text, playerNum.value,
config.preferredTimeout, {
enableFreeAssign: freeAssignCheck.checked, enableFreeAssign: freeAssignCheck.checked,
enableDeputy: deputyCheck.checked, enableDeputy: deputyCheck.checked,
gameMode: config.preferedMode, gameMode: config.preferedMode,
@ -208,7 +209,7 @@ Flickable {
} }
} }
Button { Button {
text: Backend.translate("Cancel") text: luatr("Cancel")
onClicked: { onClicked: {
root.finished(); root.finished();
} }
@ -216,11 +217,11 @@ Flickable {
} }
Component.onCompleted: { Component.onCompleted: {
const mode_data = JSON.parse(Backend.callLuaFunction("GetGameModes", [])); const mode_data = lcall("GetGameModes");
let i = 0; let i = 0;
for (let d of mode_data) { for (let d of mode_data) {
gameModeList.append(d); gameModeList.append(d);
if (d.orig_name == config.preferedMode) { if (d.orig_name === config.preferedMode) {
gameModeCombo.currentIndex = i; gameModeCombo.currentIndex = i;
} }
i += 1; i += 1;
@ -228,10 +229,12 @@ Flickable {
playerNum.value = config.preferedPlayerNum; playerNum.value = config.preferedPlayerNum;
config.disabledPack.forEach(p => { for (let k in config.curScheme.banPkg) {
Backend.callLuaFunction("UpdatePackageEnable", [p, false]); lcall("UpdatePackageEnable", k, false);
}); }
config.disabledPackChanged(); config.curScheme.banCardPkg.forEach(p =>
lcall("UpdatePackageEnable", p, false));
config.curSchemeChanged();
} }
} }
} }

View File

@ -23,16 +23,16 @@ Flickable {
anchors.topMargin: 8 anchors.topMargin: 8
Switch { Switch {
text: Backend.translate("Disable Extension") text: luatr("Disable Extension")
} }
RowLayout { RowLayout {
Text { Text {
text: Backend.translate("General Packages") text: luatr("General Packages")
font.bold: true font.bold: true
} }
Button { Button {
text: Backend.translate("Select All") text: luatr("Select All")
onClicked: { onClicked: {
for (let i = 0; i < gpacks.count; i++) { for (let i = 0; i < gpacks.count; i++) {
const item = gpacks.itemAt(i); const item = gpacks.itemAt(i);
@ -41,7 +41,7 @@ Flickable {
} }
} }
Button { Button {
text: Backend.translate("Revert Selection") text: luatr("Revert Selection")
onClicked: { onClicked: {
for (let i = 0; i < gpacks.count; i++) { for (let i = 0; i < gpacks.count; i++) {
const item = gpacks.itemAt(i); const item = gpacks.itemAt(i);
@ -76,11 +76,11 @@ Flickable {
RowLayout { RowLayout {
Text { Text {
text: Backend.translate("Card Packages") text: luatr("Card Packages")
font.bold: true font.bold: true
} }
Button { Button {
text: Backend.translate("Select All") text: luatr("Select All")
onClicked: { onClicked: {
for (let i = 0; i < cpacks.count; i++) { for (let i = 0; i < cpacks.count; i++) {
const item = cpacks.itemAt(i); const item = cpacks.itemAt(i);
@ -89,7 +89,7 @@ Flickable {
} }
} }
Button { Button {
text: Backend.translate("Revert Selection") text: luatr("Revert Selection")
onClicked: { onClicked: {
for (let i = 0; i < cpacks.count; i++) { for (let i = 0; i < cpacks.count; i++) {
const item = cpacks.itemAt(i); const item = cpacks.itemAt(i);
@ -113,7 +113,15 @@ Flickable {
checked: pkg_enabled checked: pkg_enabled
onCheckedChanged: { onCheckedChanged: {
checkPackage(orig_name, checked); const packs = config.curScheme.banCardPkg;
if (checked) {
const idx = packs.indexOf(orig_name);
if (idx !== -1) packs.splice(idx, 1);
} else {
packs.push(orig_name);
}
lcall("UpdatePackageEnable", orig_name, checked);
config.curSchemeChanged();
} }
} }
} }
@ -121,40 +129,42 @@ Flickable {
} }
function checkPackage(orig_name, checked) { function checkPackage(orig_name, checked) {
const packs = config.disabledPack; const s = config.curScheme;
if (checked) { if (!checked) {
const idx = packs.indexOf(orig_name); s.banPkg[orig_name] = [];
if (idx !== -1) packs.splice(idx, 1); delete s.normalPkg[orig_name];
} else { } else {
packs.push(orig_name); delete s.normalPkg[orig_name];
delete s.banPkg[orig_name];
} }
Backend.callLuaFunction("UpdatePackageEnable", [orig_name, checked]); lcall("UpdatePackageEnable", orig_name, checked);
config.disabledPackChanged(); config.curSchemeChanged();
} }
Component.onCompleted: { Component.onCompleted: {
loading = true; loading = true;
const g = JSON.parse(Backend.callLuaFunction("GetAllGeneralPack", [])); const g = lcall("GetAllGeneralPack");
for (let orig of g) { let orig;
for (orig of g) {
if (config.serverHiddenPacks.includes(orig)) { if (config.serverHiddenPacks.includes(orig)) {
continue; continue;
} }
gpacklist.append({ gpacklist.append({
name: Backend.translate(orig), name: luatr(orig),
orig_name: orig, orig_name: orig,
pkg_enabled: !config.disabledPack.includes(orig), pkg_enabled: !config.curScheme.banPkg[orig],
}); });
} }
const c = JSON.parse(Backend.callLuaFunction("GetAllCardPack", [])); const c = lcall("GetAllCardPack");
for (let orig of c) { for (orig of c) {
if (config.serverHiddenPacks.includes(orig)) { if (config.serverHiddenPacks.includes(orig)) {
continue; continue;
} }
cpacklist.append({ cpacklist.append({
name: Backend.translate(orig), name: luatr(orig),
orig_name: orig, orig_name: orig,
pkg_enabled: !config.disabledPack.includes(orig), pkg_enabled: !config.curScheme.banCardPkg.includes(orig),
}); });
} }
loading = false; loading = false;

View File

@ -11,7 +11,7 @@ ColumnLayout {
anchors.rightMargin: 8 anchors.rightMargin: 8
spacing: 16 spacing: 16
Text { Text {
text: Backend.translate("Username") text: luatr("Username")
} }
Text { Text {
text: Self.screenName text: Self.screenName
@ -28,7 +28,7 @@ ColumnLayout {
anchors.rightMargin: 8 anchors.rightMargin: 8
spacing: 16 spacing: 16
Text { Text {
text: Backend.translate("Avatar") text: luatr("Avatar")
} }
TextField { TextField {
id: avatarName id: avatarName
@ -38,7 +38,7 @@ ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
} }
Button { Button {
text: Backend.translate("Update Avatar") text: luatr("Update Avatar")
enabled: avatarName.text !== "" && !opTimer.running enabled: avatarName.text !== "" && !opTimer.running
onClicked: { onClicked: {
mainWindow.busy = true; mainWindow.busy = true;
@ -55,7 +55,7 @@ ColumnLayout {
anchors.rightMargin: 8 anchors.rightMargin: 8
spacing: 16 spacing: 16
Text { Text {
text: Backend.translate("Old Password") text: luatr("Old Password")
} }
TextField { TextField {
id: oldPassword id: oldPassword
@ -70,7 +70,7 @@ ColumnLayout {
anchors.rightMargin: 8 anchors.rightMargin: 8
spacing: 16 spacing: 16
Text { Text {
text: Backend.translate("New Password") text: luatr("New Password")
} }
TextField { TextField {
id: newPassword id: newPassword
@ -79,8 +79,9 @@ ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
} }
Button { Button {
text: Backend.translate("Update Password") text: luatr("Update Password")
enabled: oldPassword.text !== "" && newPassword.text !== "" && !opTimer.running enabled: oldPassword.text !== "" && newPassword.text !== ""
&& !opTimer.running
onClicked: { onClicked: {
mainWindow.busy = true; mainWindow.busy = true;
opTimer.start(); opTimer.start();

View File

@ -46,8 +46,8 @@ callbacks["NetworkDelayTest"] = (jsonData) => {
// jsonData: RSA pub key // jsonData: RSA pub key
let cipherText; let cipherText;
let aeskey; let aeskey;
if (config.savedPassword[config.serverAddr] !== undefined const savedPw = config.savedPassword[config.serverAddr];
&& config.savedPassword[config.serverAddr].shorten_password === config.password) { if (savedPw?.shorten_password === config.password) {
cipherText = config.savedPassword[config.serverAddr].password; cipherText = config.savedPassword[config.serverAddr].password;
aeskey = config.savedPassword[config.serverAddr].key; aeskey = config.savedPassword[config.serverAddr].key;
config.aeskey = aeskey ?? ""; config.aeskey = aeskey ?? "";
@ -173,7 +173,7 @@ callbacks["Chat"] = (jsonData) => {
const data = JSON.parse(jsonData); const data = JSON.parse(jsonData);
const pid = data.sender; const pid = data.sender;
const userName = data.userName; const userName = data.userName;
const general = Backend.translate(data.general); const general = luatr(data.general);
const time = data.time; const time = data.time;
const msg = data.msg; const msg = data.msg;
@ -181,10 +181,13 @@ callbacks["Chat"] = (jsonData) => {
return; return;
} }
let text;
if (general === "") if (general === "")
current.addToChat(pid, data, `<font color="#3598E8">[${time}] ${userName}:</font> ${msg}`); text = `<font color="#3598E8">[${time}] ${userName}:</font> ${msg}`;
else else
current.addToChat(pid, data, `<font color="#3598E8">[${time}] ${userName}(${general}):</font> ${msg}`); text = `<font color="#3598E8">[${time}] ${userName}` +
`(${general}):</font> ${msg}`;
current.addToChat(pid, data, text);
} }
callbacks["ServerMessage"] = (jsonData) => { callbacks["ServerMessage"] = (jsonData) => {

View File

@ -36,7 +36,9 @@ ColumnLayout {
id: edit id: edit
font.pixelSize: 18 font.pixelSize: 18
Layout.fillWidth: true Layout.fillWidth: true
validator: RegularExpressionValidator { regularExpression: /[0-9A-Za-z_]+/ } validator: RegularExpressionValidator {
regularExpression: /[0-9A-Za-z_]+/
}
} }
Button { Button {

View File

@ -19,7 +19,8 @@ QtObject {
conf.email = email; conf.email = email;
conf.modList = modList; conf.modList = modList;
ModBackend.saveToFile("mymod/config.json", JSON.stringify(conf, undefined, 2)); ModBackend.saveToFile("mymod/config.json",
JSON.stringify(conf, undefined, 2));
} }
function addMod(mod) { function addMod(mod) {

View File

@ -62,7 +62,8 @@ Item {
SwipeDelegate.onClicked: deletePackage(modelData); SwipeDelegate.onClicked: deletePackage(modelData);
background: Rectangle { background: Rectangle {
color: deleteLabel.SwipeDelegate.pressed ? Qt.darker("tomato", 1.1) : "tomato" color: deleteLabel.SwipeDelegate.pressed ? Qt.darker("tomato", 1.1)
: "tomato"
} }
} }
} }
@ -118,14 +119,19 @@ Item {
toast.show(qsTr("cannot use this package name")); toast.show(qsTr("cannot use this package name"));
return; return;
} }
const path = modPath + new_name + "/"; const path = modPath + new_name + "/";
ModBackend.mkdir(path); ModBackend.mkdir(path);
mod.packages.push(new_name); mod.packages.push(new_name);
ModBackend.saveToFile(modPath + "mod.json", JSON.stringify(mod, undefined, 2)); ModBackend.saveToFile(modPath + "mod.json",
JSON.stringify(mod, undefined, 2));
const pkgInfo = { const pkgInfo = {
name: new_name, name: new_name,
}; };
ModBackend.saveToFile(path + "pkg.json", JSON.stringify(pkgInfo, undefined, 2)); ModBackend.saveToFile(path + "pkg.json",
JSON.stringify(pkgInfo, undefined, 2));
root.modChanged(); root.modChanged();
} }
@ -133,7 +139,9 @@ Item {
const path = modPath + name + "/"; const path = modPath + name + "/";
ModBackend.rmrf(path); ModBackend.rmrf(path);
mod.packages.splice(mod.packages.indexOf(name), 1); mod.packages.splice(mod.packages.indexOf(name), 1);
ModBackend.saveToFile(modPath + "mod.json", JSON.stringify(mod, undefined, 2)); ModBackend.saveToFile(modPath + "mod.json",
JSON.stringify(mod, undefined, 2));
root.modChanged(); root.modChanged();
} }
} }

View File

@ -84,7 +84,8 @@ Item {
SwipeDelegate.onClicked: deleteMod(modelData); SwipeDelegate.onClicked: deleteMod(modelData);
background: Rectangle { background: Rectangle {
color: deleteLabel.SwipeDelegate.pressed ? Qt.darker("tomato", 1.1) : "tomato" color: deleteLabel.SwipeDelegate.pressed ? Qt.darker("tomato", 1.1)
: "tomato"
} }
} }
} }
@ -136,7 +137,8 @@ Item {
function createNewMod(name) { function createNewMod(name) {
const banned = [ "test", "standard", "standard_cards", "maneuvering" ]; const banned = [ "test", "standard", "standard_cards", "maneuvering" ];
if (banned.indexOf(name) !== -1 || modConfig.modList.indexOf(name) !== -1) { if (banned.indexOf(name) !== -1 ||
modConfig.modList.indexOf(name) !== -1) {
toast.show(qsTr("cannot use this mod name")); toast.show(qsTr("cannot use this mod name"));
return; return;
} }
@ -146,10 +148,12 @@ Item {
descrption: "", descrption: "",
author: modConfig.userName, author: modConfig.userName,
}; };
ModBackend.saveToFile(`mymod/${name}/mod.json`, JSON.stringify(modInfo, undefined, 2)); ModBackend.saveToFile(`mymod/${name}/mod.json`,
JSON.stringify(modInfo, undefined, 2));
ModBackend.saveToFile(`mymod/${name}/.gitignore`, "init.lua"); ModBackend.saveToFile(`mymod/${name}/.gitignore`, "init.lua");
ModBackend.stageFiles(name); ModBackend.stageFiles(name);
ModBackend.commitChanges(name, "Initial commit", modConfig.userName, modConfig.email); ModBackend.commitChanges(name, "Initial commit", modConfig.userName,
modConfig.email);
modConfig.addMod(name); modConfig.addMod(name);
} }

View File

@ -50,7 +50,7 @@ Item {
anchors.left: logo.right anchors.left: logo.right
anchors.leftMargin: 16 anchors.leftMargin: 16
width: parent.width * 0.65 width: parent.width * 0.65
text: Backend.translate("about_" + dest + "_description") text: luatr("about_" + dest + "_description")
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
textFormat: Text.MarkdownText textFormat: Text.MarkdownText
font.pixelSize: 18 font.pixelSize: 18
@ -73,7 +73,7 @@ Item {
} }
Button { Button {
text: Backend.translate("Quit") text: luatr("Quit")
anchors.right: parent.right anchors.right: parent.right
onClicked: { onClicked: {
swipe.opacity = 0; swipe.opacity = 0;

View File

@ -3,6 +3,7 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Controls import QtQuick.Controls
import Fk
import Fk.RoomElement import Fk.RoomElement
Item { Item {
@ -35,7 +36,7 @@ Item {
height: 40 height: 40
Text { Text {
text: Backend.translate(name) text: luatr(name)
anchors.centerIn: parent anchors.centerIn: parent
} }
@ -62,6 +63,7 @@ Item {
delegate: CardItem { delegate: CardItem {
autoBack: false autoBack: false
showDetail: false
property int dupCount: 0 property int dupCount: 0
Text { Text {
@ -115,10 +117,9 @@ Item {
easing.type: Easing.InOutQuad easing.type: Easing.InOutQuad
} }
onFinished: { onFinished: {
const pkg = [listView.model.get(listView.currentIndex).name]; const pkg = listView.model.get(listView.currentIndex).name;
const idList = JSON.parse(Backend.callLuaFunction("GetCards", pkg)); const idList = lcall("GetCards", pkg);
const cardList = idList.map(id => JSON.parse(Backend.callLuaFunction const cardList = idList.map(id => lcall("GetCardData", id));
("GetCardData",[id])));
const groupedCardList = []; const groupedCardList = [];
let groupedCards = {}; let groupedCards = {};
@ -194,25 +195,11 @@ Item {
property int cid: 1 property int cid: 1
property var cards property var cards
function updateCard() { function updateCard() {
const data = JSON.parse(Backend.callLuaFunction("GetCardData", [cid])); const data = lcall("GetCardData", cid);
const suitTable = { const suitTable = {
spade: "♠", heart: '<font color="red">♥</font>', spade: "♠", heart: '<font color="red">♥</font>',
club: "♣", diamond: '<font color="red">♦</font>', club: "♣", diamond: '<font color="red">♦</font>',
} }
const getNumString = n => {
switch (n) {
case 1:
return "A";
case 11:
return "J";
case 12:
return "Q";
case 13:
return "K";
default:
return n.toString();
}
}
if (!cards) { if (!cards) {
detailCard.setData(data); detailCard.setData(data);
@ -227,22 +214,21 @@ Item {
detailCard.known = true; detailCard.known = true;
cardText.clear(); cardText.clear();
audioRow.clear(); audioRow.clear();
cardText.append(Backend.translate(":" + data.name)); cardText.append(luatr(":" + data.name));
addCardAudio(data) addCardAudio(data)
const skills = JSON.parse(Backend.callLuaFunction const skills = lcall("GetCardSpecialSkills", cid);
("GetCardSpecialSkills", [cid]));
if (skills.length > 0) { if (skills.length > 0) {
cardText.append("<br/>" + Backend.translate("Special card skills:")); cardText.append("<br/>" + luatr("Special card skills:"));
skills.forEach(t => { skills.forEach(t => {
cardText.append("<b>" + Backend.translate(t) + "</b>: " cardText.append("<b>" + luatr(t) + "</b>: "
+ Backend.translate(":" + t)); + luatr(":" + t));
}); });
} }
if (cards) { if (cards) {
cardText.append("<br/>" + Backend.translate("Every suit & number:")); cardText.append("<br/>" + luatr("Every suit & number:"));
cardText.append(cards.map(c => { cardText.append(cards.map(c => {
return (suitTable[c.suit] + getNumString(c.number)) return (suitTable[c.suit] + Util.convertNumber(c.number))
}).join(", ")); }).join(", "));
} }
} }
@ -265,6 +251,7 @@ Item {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
cid: 1 cid: 1
known: false known: false
showDetail: false
property int dupCount: 0 property int dupCount: 0
Text { Text {
@ -303,16 +290,17 @@ Item {
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
text: { text: {
if (gender === "male") { if (gender === "male") {
return Backend.translate("Male Audio"); return luatr("Male Audio");
} else { } else {
return Backend.translate("Female Audio"); return luatr("Female Audio");
} }
} }
font.pixelSize: 14 font.pixelSize: 14
} }
onClicked: { onClicked: {
const data = JSON.parse(Backend.callLuaFunction("GetCardData", [cardDetail.cid])); const data = lcall("GetCardData", cardDetail.cid);
Backend.playSound("./packages/" + extension + "/audio/card/" + gender + "/" + data.name); Backend.playSound("./packages/" + extension + "/audio/card/"
+ gender + "/" + data.name);
} }
} }
} }
@ -322,7 +310,7 @@ Item {
} }
Button { Button {
text: Backend.translate("Quit") text: luatr("Quit")
anchors.right: parent.right anchors.right: parent.right
onClicked: { onClicked: {
mainStack.pop(); mainStack.pop();
@ -331,30 +319,32 @@ Item {
function addCardAudio(card) { function addCardAudio(card) {
const extension = card.extension; const extension = card.extension;
const orig_extension = Backend.callLuaFunction("GetCardExtensionByName", [card.name]); const orig_extension = lcall("GetCardExtensionByName", card.name);
let fname = AppPath + "/packages/" + extension + "/audio/card/male/" + card.name + ".mp3"; const prefix = AppPath + "/packages/";
const suffix = card.name + ".mp3";
let fname = prefix + extension + "/audio/card/male/" + suffix;
if (Backend.exists(fname)) { if (Backend.exists(fname)) {
audioRow.append( {gender: "male", extension: extension} ); audioRow.append( { gender: "male", extension: extension } );
} else { } else {
fname = AppPath + "/packages/" + orig_extension + "/audio/card/male/" + card.name + ".mp3"; fname = prefix + orig_extension + "/audio/card/male/" + suffix;
if (Backend.exists(fname)) { if (Backend.exists(fname)) {
audioRow.append( {gender: "male", extension: orig_extension} ); audioRow.append( {gender: "male", extension: orig_extension} );
} }
} }
fname = AppPath + "/packages/" + extension + "/audio/card/female/" + card.name + ".mp3"; fname = prefix + extension + "/audio/card/female/" + suffix;
if (Backend.exists(fname)) { if (Backend.exists(fname)) {
audioRow.append( {gender: "female", extension: extension} ); audioRow.append( { gender: "female", extension: extension } );
}else { }else {
fname = AppPath + "/packages/" + orig_extension + "/audio/card/female/" + card.name + ".mp3"; fname = prefix + orig_extension + "/audio/card/female/" + suffix;
if (Backend.exists(fname)) { if (Backend.exists(fname)) {
audioRow.append( {gender: "female", extension: orig_extension} ); audioRow.append( { gender: "female", extension: orig_extension } );
} }
} }
} }
function loadPackages() { function loadPackages() {
if (loaded) return; if (loaded) return;
const packs = JSON.parse(Backend.callLuaFunction("GetAllCardPack", [])); const packs = lcall("GetAllCardPack");
packs.forEach(name => { packs.forEach(name => {
if (!config.serverHiddenPacks.includes(name)) { if (!config.serverHiddenPacks.includes(name)) {
packages.append({ name: name }); packages.append({ name: name });

View File

@ -3,6 +3,7 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Controls import QtQuick.Controls
import Fk
import Fk.RoomElement import Fk.RoomElement
import "RoomLogic.js" as RoomLogic import "RoomLogic.js" as RoomLogic
@ -10,39 +11,98 @@ Item {
id: root id: root
property bool loaded: false property bool loaded: false
property int stat: 0 // 0=normal 1=banPkg 2=banChara
Rectangle { Rectangle {
anchors.fill: listView id: listBg
color: "#88EEEEEE" width: 260; height: parent.height
color: "snow"
radius: 6 radius: 6
} }
ListView { ListView {
id: listView id: modList
width: 130; height: parent.height
anchors.top: listBg.top; anchors.left: listBg.left
clip: true clip: true
width: 130
height: parent.height - 20
y: 10
ScrollBar.vertical: ScrollBar {}
model: ListModel { model: ListModel {
id: packages id: mods
} }
highlight: Rectangle { color: "#E91E63"; radius: 5 } Rectangle {
anchors.fill: parent
color: "#A48959"
z: -1
}
highlight: Rectangle { color: "snow" }
highlightMoveDuration: 500 highlightMoveDuration: 500
delegate: Item { delegate: Item {
width: listView.width width: modList.width
height: 40 height: 40
Text { Text {
text: Backend.translate(name) text: luatr(name)
color: modList.currentIndex === index ? "black" : "white"
anchors.centerIn: parent anchors.centerIn: parent
} }
TapHandler { TapHandler {
onTapped: { onTapped: {
listView.currentIndex = index; modList.currentIndex = index;
}
}
}
}
ListView {
id: pkgList
width: 130; height: parent.height
anchors.top: listBg.top; anchors.left: modList.right
clip: true
model: JSON.parse(mods.get(modList.currentIndex)?.pkgs ?? "[]")
highlight: Rectangle { color: "#FFCC3F"; radius: 5; scale: 0.8 }
highlightMoveDuration: 500
delegate: Item {
width: pkgList.width
height: 40
Text {
text: luatr(modelData)
color: !config.curScheme.banPkg[modelData] ? "black" : "grey"
Behavior on color { ColorAnimation { duration: 200 } }
anchors.centerIn: parent
}
Image {
source: AppPath + "/image/button/skill/locked.png"
opacity: !config.curScheme.banPkg[modelData] ? 0 : 1
Behavior on opacity { NumberAnimation { duration: 200 } }
anchors.centerIn: parent
scale: 0.8
}
TapHandler {
onTapped: {
if (stat === 1) {
const name = modelData;
let s = config.curScheme;
if (s.banPkg[name]) {
delete s.banPkg[name];
delete s.normalPkg[name];
} else {
delete s.normalPkg[name];
s.banPkg[name] = [];
}
console.log(JSON.stringify(config.curScheme))
config.curSchemeChanged();
} else {
pkgList.currentIndex = index;
}
} }
} }
} }
@ -50,13 +110,106 @@ Item {
onCurrentIndexChanged: { vanishAnim.start(); } onCurrentIndexChanged: { vanishAnim.start(); }
} }
ToolBar {
id: bar
width: root.width - listBg.width - 16
anchors.left: listBg.right
anchors.leftMargin: 8
y: 8
background: Rectangle {
color: stat === 0 ? "#5cb3cc" : "#869d9d"
Behavior on color { ColorAnimation { duration: 200 } }
}
RowLayout {
anchors.fill: parent
Item { Layout.preferredWidth: 20 }
Label {
text: {
switch (stat) {
case 0: return luatr("Generals Overview");
case 1: return luatr("$BanPkgHelp");
case 2: return luatr("$BanCharaHelp");
}
}
elide: Label.ElideLeft
verticalAlignment: Qt.AlignVCenter
font.pixelSize: 28
}
Item { Layout.fillWidth: true }
TextField {
id: word
clip: true
leftPadding: 5
rightPadding: 5
}
ToolButton {
text: luatr("Search")
font.pixelSize: 20
enabled: word.text !== ""
onClicked: {
pkgList.currentIndex = 0;
vanishAnim.start();
}
}
ToolButton {
id: banButton
font.pixelSize: 20
text: {
if (stat === 2) return luatr("OK");
return luatr("BanGeneral");
}
enabled: stat !== 1
onClicked: {
if (stat === 0) {
stat = 2;
} else {
stat = 0;
}
}
}
ToolButton {
id: banPkgButton
font.pixelSize: 20
text: {
if (stat === 1) return luatr("OK");
return luatr("BanPackage");
}
enabled: stat !== 2
onClicked: {
if (stat === 0) {
stat = 1;
} else {
stat = 0;
}
}
}
ToolButton {
text: luatr("Quit")
font.pixelSize: 20
onClicked: {
mainStack.pop();
config.saveConf();
}
}
}
}
GridView { GridView {
id: gridView id: gridView
clip: true clip: true
width: root.width - listView.width - generalDetail.width - 16 width: root.width - listBg.width - 16
height: parent.height - 20 height: parent.height - bar.height - 24
y: 10 y: 16 + bar.height
anchors.left: listView.right anchors.left: listBg.right
anchors.leftMargin: 8 + (width % 100) / 2 anchors.leftMargin: 8 + (width % 100) / 2
cellHeight: 140 cellHeight: 140
cellWidth: 100 cellWidth: 100
@ -65,24 +218,77 @@ Item {
autoBack: false autoBack: false
name: modelData name: modelData
onClicked: { onClicked: {
generalText.clear(); if (stat === 2) {
generalDetail.general = modelData; const s = config.curScheme;
generalDetail.updateGeneral(); const gdata = lcall("GetGeneralData", modelData);
// generalDetail.open(); const pack = gdata.package;
let arr;
if (s.banPkg[pack]) {
arr = s.banPkg[pack];
} else {
if (!s.normalPkg[pack]) {
s.normalPkg[pack] = [];
}
arr = s.normalPkg[pack];
}
// TODO: /
const idx = arr.indexOf(modelData);
if (idx !== -1) {
arr.splice(idx, 1);
} else {
arr.push(modelData);
}
config.curSchemeChanged();
} else {
generalText.clear();
generalDetail.general = modelData;
generalDetail.updateGeneral();
generalDetail.open();
}
} }
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
color: "black" color: "black"
opacity: config.disabledGenerals.includes(modelData) ? 0.7 : 0 opacity: {
const s = config.curScheme;
const gdata = lcall("GetGeneralData", modelData);
const pack = gdata.package;
if (s.banPkg[pack]) {
if (!s.banPkg[pack].includes(modelData)) return 0.5;
} else {
if (!!s.normalPkg[pack]?.includes(modelData)) return 0.5;
}
return 0;
}
Behavior on opacity { Behavior on opacity {
NumberAnimation {} NumberAnimation {}
} }
} }
GlowText { GlowText {
visible: config.disabledGenerals.includes(modelData) id: banText
text: '禁' visible: {
const s = config.curScheme;
const gdata = lcall("GetGeneralData", modelData);
const pack = gdata.package;
if (s.banPkg[pack]) {
return s.banPkg[pack].includes(modelData);
} else {
return !!s.normalPkg[pack]?.includes(modelData);
}
}
text: {
if (!visible) return '';
const s = config.curScheme;
const gdata = lcall("GetGeneralData", modelData);
const pack = gdata.package;
if (s.banPkg[pack]) {
if (s.banPkg[pack].includes(modelData)) return '启用';
} else {
if (!!s.normalPkg[pack]?.includes(modelData)) return '禁';
}
}
anchors.centerIn: parent anchors.centerIn: parent
font.family: fontLi2.name font.family: fontLi2.name
color: "#E4D5A0" color: "#E4D5A0"
@ -107,17 +313,16 @@ Item {
PropertyAnimation { PropertyAnimation {
target: gridView target: gridView
property: "y" property: "y"
to: 30 to: 36 + bar.height
duration: 150 duration: 150
easing.type: Easing.InOutQuad easing.type: Easing.InOutQuad
} }
onFinished: { onFinished: {
if (word.text !== "") { if (word.text !== "") {
gridView.model = JSON.parse(Backend.callLuaFunction("SearchAllGenerals", gridView.model = lcall("SearchAllGenerals", word.text);
[word.text]));
} else { } else {
gridView.model = JSON.parse(Backend.callLuaFunction("SearchGenerals", gridView.model = lcall("SearchGenerals",
[listView.model.get(listView.currentIndex).name, word.text])); pkgList.model[pkgList.currentIndex], word.text);
} }
word.text = ""; word.text = "";
appearAnim.start(); appearAnim.start();
@ -138,28 +343,83 @@ Item {
PropertyAnimation { PropertyAnimation {
target: gridView target: gridView
property: "y" property: "y"
from: 20 from: 36 + bar.height
to: 10 to: 16 + bar.height
duration: 150 duration: 150
easing.type: Easing.InOutQuad easing.type: Easing.InOutQuad
} }
} }
} }
Rectangle { Component {
id: skillAudioBtn
Button {
Layout.fillWidth: true
contentItem: ColumnLayout {
Text {
Layout.fillWidth: true
text: {
if (name.endsWith("_win_audio")) {
return luatr("Win audio");
}
return luatr(name) + (idx ? " (" + idx.toString() + ")"
: "");
}
font.bold: true
font.pixelSize: 14
}
Text {
Layout.fillWidth: true
text: {
const orig = '$' + name + (idx ? idx.toString() : "");
const orig_trans = luatr(orig);
// try general specific
const orig_g = '$' + name + '_' + detailGeneralCard.name
+ (idx ? idx.toString() : "");
const orig_g_trans = luatr(orig_g);
if (orig_g_trans !== orig_g) {
return orig_g_trans;
}
if (orig_trans !== orig) {
return orig_trans;
}
return "";
}
wrapMode: Text.WordWrap
}
}
onClicked: {
callbacks["LogEvent"](JSON.stringify({
type: "PlaySkillSound",
name: name,
general: detailGeneralCard.name,
i: idx,
}));
}
}
}
Popup {
id: generalDetail id: generalDetail
width: 310 width: realMainWin.width * 0.6
height: parent.height - searcher.height - 20 height: realMainWin.height * 0.8
y: 10 anchors.centerIn: parent
anchors.right: parent.right background: Rectangle {
anchors.rightMargin: 10 color: "#EEEEEEEE"
color: "#88EEEEEE" radius: 5
radius: 8 border.color: "#A6967A"
border.width: 1
}
property string general: "caocao" property string general: "caocao"
function addSpecialSkillAudio(skill) { function addSpecialSkillAudio(skill) {
const gdata = JSON.parse(Backend.callLuaFunction("GetGeneralData", [general])); const gdata = lcall("GetGeneralData", general);
const extension = gdata.extension; const extension = gdata.extension;
let ret = false; let ret = false;
for (let i = 0; i < 999; i++) { for (let i = 0; i < 999; i++) {
@ -178,7 +438,7 @@ Item {
function addSkillAudio(skill) { function addSkillAudio(skill) {
if (addSpecialSkillAudio(skill)) return; if (addSpecialSkillAudio(skill)) return;
const skilldata = JSON.parse(Backend.callLuaFunction("GetSkillData", [skill])); const skilldata = lcall("GetSkillData", skill);
if (!skilldata) return; if (!skilldata) return;
const extension = skilldata.extension; const extension = skilldata.extension;
for (let i = 0; i < 999; i++) { for (let i = 0; i < 999; i++) {
@ -194,8 +454,9 @@ Item {
} }
function findDeathAudio(general) { function findDeathAudio(general) {
const extension = JSON.parse(Backend.callLuaFunction("GetGeneralData", [general])).extension; const extension = lcall("GetGeneralData", general).extension;
const fname = AppPath + "/packages/" + extension + "/audio/death/" + general + ".mp3"; const fname = AppPath + "/packages/" + extension + "/audio/death/"
+ general + ".mp3";
if (Backend.exists(fname)) { if (Backend.exists(fname)) {
audioDeath.visible = true; audioDeath.visible = true;
} else { } else {
@ -205,27 +466,27 @@ Item {
function updateGeneral() { function updateGeneral() {
detailGeneralCard.name = general; detailGeneralCard.name = general;
const data = JSON.parse(Backend.callLuaFunction("GetGeneralDetail", [general])); const data = lcall("GetGeneralDetail", general);
generalText.clear(); generalText.clear();
audioModel.clear(); audioModel.clear();
if (data.companions.length > 0){ if (data.companions.length > 0){
let ret = ''; let ret = "<font color=\"slategrey\"><b>" + luatr("Companions")
ret += "<font color=\"slategrey\"><b>" + Backend.translate("Companions") + "</b>: "; + "</b>: ";
data.companions.forEach(t => { data.companions.forEach(t => {
ret += Backend.translate(t) + ' ' ret += luatr(t) + ' '
}); });
generalText.append(ret) generalText.append(ret)
} }
data.skill.forEach(t => { data.skill.forEach(t => {
generalText.append("<b>" + Backend.translate(t.name) + generalText.append("<b>" + luatr(t.name) +
"</b>: " + t.description); "</b>: " + t.description);
addSkillAudio(t.name); addSkillAudio(t.name);
}); });
data.related_skill.forEach(t => { data.related_skill.forEach(t => {
generalText.append("<font color=\"purple\"><b>" + Backend.translate(t.name) + generalText.append("<font color=\"purple\"><b>" + luatr(t.name) +
"</b>: " + t.description + "</font>"); "</b>: " + t.description + "</font>");
addSkillAudio(t.name); addSkillAudio(t.name);
@ -235,210 +496,153 @@ Item {
addSkillAudio(general + "_win_audio"); addSkillAudio(general + "_win_audio");
} }
Flickable { Item {
flickableDirection: Flickable.VerticalFlick
contentHeight: detailLayout.height
width: parent.width - 40
height: parent.height - 40
clip: true
anchors.centerIn: parent anchors.centerIn: parent
ScrollBar.vertical: ScrollBar {} width: parent.width / mainWindow.scale
height: parent.height / mainWindow.scale
scale: mainWindow.scale
ColumnLayout { Item {
id: detailLayout id: generalInfo
width: parent.width width: 150
ColumnLayout {
GeneralCardItem { width: parent.width
id: detailGeneralCard GeneralCardItem {
Layout.alignment: Qt.AlignHCenter id: detailGeneralCard
name: "caocao" name: "caocao"
} scale: 1.5; transformOrigin: Item.TopLeft
TextEdit {
id: generalText
Layout.fillWidth: true
readOnly: true
selectByKeyboard: true
selectByMouse: false
wrapMode: TextEdit.WordWrap
textFormat: TextEdit.RichText
font.pixelSize: 16
}
Repeater {
model: ListModel {
id: audioModel
} }
Item { Layout.preferredHeight: 130 * 0.5 }
Text {
Layout.fillWidth: true
textFormat: TextEdit.RichText
font.pixelSize: 16
function trans(str) {
const ret = luatr(str);
if (ret === str) {
return luatr("Official");
}
return ret;
}
text: {
const general = generalDetail.general;
return [
luatr(lcall("GetGeneralData", general).package),
luatr("Title") + trans("#" + general),
luatr("Designer") + trans("designer:" + general),
luatr("Voice Actor") + trans("cv:" + general),
luatr("Illustrator") + trans("illustrator:" + general),
].join("<br>");
}
}
Timer {
id: opTimer
interval: 4000
}
Button { Button {
text: luatr("Set as Avatar")
enabled: detailGeneralCard.name !== "" && !opTimer.running
&& Self.avatar !== detailGeneralCard.name
onClicked: {
mainWindow.busy = true;
opTimer.start();
ClientInstance.notifyServer(
"UpdateAvatar",
JSON.stringify([detailGeneralCard.name])
);
}
}
}
}
Flickable {
flickableDirection: Flickable.VerticalFlick
contentHeight: detailLayout.height
width: parent.width - 40 - generalInfo.width
height: parent.height - 40
clip: true
anchors.left: generalInfo.right
anchors.leftMargin: 20
y: 20
ColumnLayout {
id: detailLayout
width: parent.width
TextEdit {
id: generalText
Layout.fillWidth: true
readOnly: true
selectByKeyboard: true
selectByMouse: false
wrapMode: TextEdit.WordWrap
textFormat: TextEdit.RichText
font.pixelSize: 18
}
GridLayout {
Layout.fillWidth: true
columns: 2
Repeater {
model: ListModel {
id: audioModel
}
delegate: skillAudioBtn
}
}
Button {
id: audioDeath
Layout.fillWidth: true Layout.fillWidth: true
contentItem: ColumnLayout { contentItem: ColumnLayout {
Text { Text {
Layout.fillWidth: true Layout.fillWidth: true
text: { text: luatr("Death audio")
if (name.endsWith("_win_audio")) {
return "胜利语音";
}
return Backend.translate(name) + (idx ? " (" + idx.toString() + ")" : "");
}
font.bold: true font.bold: true
font.pixelSize: 14 font.pixelSize: 14
} }
Text { Text {
Layout.fillWidth: true Layout.fillWidth: true
text: { text: {
const orig = '$' + name + (idx ? idx.toString() : ""); const orig = "~" + generalDetail.general;
const orig_trans = Backend.translate(orig); const tr = luatr(orig);
if (tr === orig) {
// try general specific return "";
const orig_g = '$' + name + '_' + detailGeneralCard.name + (idx ? idx.toString() : "");
const orig_g_trans = Backend.translate(orig_g);
if (orig_g_trans !== orig_g) {
return orig_g_trans;
} }
return tr;
if (orig_trans !== orig) {
return orig_trans;
}
return "";
} }
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
} }
} }
onClicked: { onClicked: {
callbacks["LogEvent"](JSON.stringify({ const general = generalDetail.general
type: "PlaySkillSound", const extension = lcall("GetGeneralData", general).extension;
name: name, Backend.playSound("./packages/" + extension + "/audio/death/"
general: detailGeneralCard.name, + general);
i: idx,
}));
} }
} }
} }
Button {
id: audioDeath
Layout.fillWidth: true
contentItem: ColumnLayout {
Text {
Layout.fillWidth: true
text: Backend.translate("Death audio")
font.bold: true
font.pixelSize: 14
}
Text {
Layout.fillWidth: true
text: Backend.translate("~" + generalDetail.general) == "~" + generalDetail.general ? "" : Backend.translate("~" + generalDetail.general)
wrapMode: Text.WordWrap
}
}
onClicked: {
const general = generalDetail.general
const extension = JSON.parse(Backend.callLuaFunction("GetGeneralData", [general])).extension;
Backend.playSound("./packages/" + extension + "/audio/death/" + general);
}
}
}
}
Rectangle {
id: searcher
width: parent.width
height: childrenRect.height
color: "snow"
opacity: 0.75
anchors.top: parent.bottom
radius: 8
RowLayout {
width: parent.width
TextField {
id: word
Layout.fillWidth: true
clip: true
leftPadding: 5
rightPadding: 5
}
Button {
text: Backend.translate("Search")
enabled: word.text !== ""
onClicked: {
listView.currentIndex = 0;
vanishAnim.start();
}
}
}
}
}
ColumnLayout {
anchors.right: parent.right
Button {
text: Backend.translate("Quit")
onClicked: {
mainStack.pop();
config.saveConf();
}
}
Button {
id: banButton
text: Backend.translate(config.disabledGenerals.includes(detailGeneralCard.name) ? 'ResumeGeneral' : 'BanGeneral')
visible: detailGeneralCard.name
onClicked: {
const { disabledGenerals } = config;
const { name } = detailGeneralCard;
if (banButton.text === Backend.translate('ResumeGeneral')) {
const deleteIndex = disabledGenerals.findIndex((general) => general === name);
if (deleteIndex === -1) {
return;
}
disabledGenerals.splice(deleteIndex, 1);
} else {
if (disabledGenerals.includes(name)) {
return;
}
disabledGenerals.push(name);
}
config.disabledGeneralsChanged();
}
}
Timer {
id: opTimer
interval: 4000
}
Button {
text: Backend.translate("Set as Avatar")
enabled: detailGeneralCard.name !== "" && !opTimer.running && Self.avatar !== detailGeneralCard.name
onClicked: {
mainWindow.busy = true;
opTimer.start();
ClientInstance.notifyServer(
"UpdateAvatar",
JSON.stringify([detailGeneralCard.name])
);
} }
} }
} }
function loadPackages() { function loadPackages() {
if (loaded) return; if (loaded) return;
const packs = JSON.parse(Backend.callLuaFunction("GetAllGeneralPack", [])); const _mods = lcall("GetAllModNames")
packs.forEach(name => { const modData = lcall("GetAllMods")
if (!config.serverHiddenPacks.includes(name)) { const packs = lcall("GetAllGeneralPack");
packages.append({ name: name }); _mods.forEach(name => {
} const pkgs = modData[name].filter(p => packs.includes(p)
&& !config.serverHiddenPacks.includes(p));
if (pkgs.length > 0)
mods.append({ name: name, pkgs: JSON.stringify(pkgs) });
}); });
generalDetail.updateGeneral();
loaded = true; loaded = true;
} }
} }

View File

@ -118,7 +118,8 @@ Item {
TapHandler { TapHandler {
onTapped: { onTapped: {
mainStack.push(Qt.createComponent("../Tutorial.qml").createObject()); mainStack.push(Qt.createComponent("../Tutorial.qml")
.createObject());
} }
} }
} }

View File

@ -204,7 +204,8 @@ Item {
Layout.fillWidth: true Layout.fillWidth: true
placeholderText: qsTr("Password") placeholderText: qsTr("Password")
text: "" text: ""
echoMode: showPasswordCheck.checked ? TextInput.Normal : TextInput.Password echoMode: showPasswordCheck.checked ? TextInput.Normal
: TextInput.Password
passwordCharacter: "*" passwordCharacter: "*"
} }
@ -215,10 +216,12 @@ Item {
Button { Button {
Layout.fillWidth: true Layout.fillWidth: true
enabled: serverAddrEdit.text !== "" && screenNameEdit.text !== "" && passwordEdit.text !== "" enabled: serverAddrEdit.text !== "" && screenNameEdit.text !== ""
&& passwordEdit.text !== ""
text: "OK" text: "OK"
onClicked: { onClicked: {
root.addNewServer(serverAddrEdit.text, screenNameEdit.text, passwordEdit.text); root.addNewServer(serverAddrEdit.text, screenNameEdit.text,
passwordEdit.text);
finished(); finished();
} }
} }
@ -259,7 +262,8 @@ Item {
Layout.fillWidth: true Layout.fillWidth: true
placeholderText: qsTr("Password") placeholderText: qsTr("Password")
text: "" text: ""
echoMode: showPasswordCheck.checked ? TextInput.Normal : TextInput.Password echoMode: showPasswordCheck.checked ? TextInput.Normal
: TextInput.Password
passwordCharacter: "*" passwordCharacter: "*"
} }
@ -371,7 +375,11 @@ Item {
const item = serverModel.get(i); const item = serverModel.get(i);
const ip = item.serverIP; const ip = item.serverIP;
if (addr.endsWith(ip)) { // endsWithIPv6ip if (addr.endsWith(ip)) { // endsWithIPv6ip
item.misMatchMsg = FkVersion === ver ? "" : qsTr("@VersionMismatch").arg(ver); item.misMatchMsg = "";
if (FkVersion !== ver) {
item.misMatchMsg = qsTr("@VersionMismatch").arg(ver);
}
item.description = desc; item.description = desc;
item.favicon = icon; item.favicon = icon;
item.online = count.toString(); item.online = count.toString();

View File

@ -41,7 +41,7 @@ Item {
width: parent.width width: parent.width
wrapMode: TextEdit.WordWrap wrapMode: TextEdit.WordWrap
textFormat: Text.MarkdownText textFormat: Text.MarkdownText
text: config.serverMotd + "\n___\n" + Backend.translate('Bulletin Info') text: config.serverMotd + "\n___\n" + luatr('Bulletin Info')
} }
} }
} }
@ -64,13 +64,23 @@ Item {
Text { Text {
horizontalAlignment: Text.AlignLeft horizontalAlignment: Text.AlignLeft
Layout.fillWidth: true Layout.fillWidth: true
text: (hasPassword ? Backend.translate("Has Password") : "") + roomName text: roomName
font.pixelSize: 20 font.pixelSize: 20
elide: Label.ElideRight elide: Label.ElideRight
} }
Item {
Layout.preferredWidth: 16
Image {
source: AppPath + "/image/button/skill/locked.png"
visible: hasPassword
anchors.centerIn: parent
scale: 0.8
}
}
Text { Text {
text: Backend.translate(gameMode) text: luatr(gameMode)
} }
Text { Text {
@ -81,8 +91,8 @@ Item {
} }
Button { Button {
text: (playerNum < capacity) ? Backend.translate("Enter") : text: (playerNum < capacity) ? luatr("Enter") :
Backend.translate("Observe") luatr("Observe")
enabled: !opTimer.running enabled: !opTimer.running
@ -124,7 +134,7 @@ Item {
height: root.height - 80 height: root.height - 80
Button { Button {
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
text: Backend.translate("Refresh Room List") text: luatr("Refresh Room List")
enabled: !opTimer.running enabled: !opTimer.running
onClicked: { onClicked: {
opTimer.start(); opTimer.start();
@ -142,7 +152,7 @@ Item {
Text { Text {
width: parent.width width: parent.width
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
text: Backend.translate("Room List").arg(roomModel.count) text: luatr("Room List").arg(roomModel.count)
} }
ListView { ListView {
id: roomList id: roomList
@ -166,9 +176,10 @@ Item {
width: 120 width: 120
display: AbstractButton.TextUnderIcon display: AbstractButton.TextUnderIcon
icon.name: "media-playback-start" icon.name: "media-playback-start"
text: Backend.translate("Create Room") text: luatr("Create Room")
onClicked: { onClicked: {
lobby_dialog.sourceComponent = Qt.createComponent("../LobbyElement/CreateRoom.qml"); lobby_dialog.sourceComponent =
Qt.createComponent("../LobbyElement/CreateRoom.qml");
lobby_drawer.open(); lobby_drawer.open();
config.observing = false; config.observing = false;
config.replaying = false; config.replaying = false;
@ -180,33 +191,33 @@ Item {
anchors.right: parent.right anchors.right: parent.right
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
Button { Button {
text: Backend.translate("Generals Overview") text: luatr("Generals Overview")
onClicked: { onClicked: {
mainStack.push(mainWindow.generalsOverviewPage); mainStack.push(mainWindow.generalsOverviewPage);
mainStack.currentItem.loadPackages(); mainStack.currentItem.loadPackages();
} }
} }
Button { Button {
text: Backend.translate("Cards Overview") text: luatr("Cards Overview")
onClicked: { onClicked: {
mainStack.push(mainWindow.cardsOverviewPage); mainStack.push(mainWindow.cardsOverviewPage);
mainStack.currentItem.loadPackages(); mainStack.currentItem.loadPackages();
} }
} }
Button { Button {
text: Backend.translate("Scenarios Overview") text: luatr("Scenarios Overview")
onClicked: { onClicked: {
mainStack.push(mainWindow.modesOverviewPage); mainStack.push(mainWindow.modesOverviewPage);
} }
} }
Button { Button {
text: Backend.translate("Replay") text: luatr("Replay")
onClicked: { onClicked: {
mainStack.push(mainWindow.replayPage); mainStack.push(mainWindow.replayPage);
} }
} }
Button { Button {
text: Backend.translate("About") text: luatr("About")
onClicked: { onClicked: {
mainStack.push(mainWindow.aboutPage); mainStack.push(mainWindow.aboutPage);
} }
@ -216,7 +227,7 @@ Item {
Button { Button {
id: exitButton id: exitButton
anchors.right: parent.right anchors.right: parent.right
text: Backend.translate("Exit Lobby") text: luatr("Exit Lobby")
display: AbstractButton.TextBesideIcon display: AbstractButton.TextBesideIcon
icon.name: "application-exit" icon.name: "application-exit"
onClicked: { onClicked: {
@ -269,7 +280,7 @@ Item {
anchors.margins: 16 anchors.margins: 16
Text { Text {
text: Backend.translate("Please input room's password") text: luatr("Please input room's password")
} }
TextField { TextField {
@ -295,7 +306,7 @@ Item {
config.replaying = false; config.replaying = false;
if (playerNum < capacity) { if (playerNum < capacity) {
config.observing = false; config.observing = false;
Backend.callLuaFunction("SetObserving", [false]); lcall("SetObserving", false);
mainWindow.busy = true; mainWindow.busy = true;
ClientInstance.notifyServer( ClientInstance.notifyServer(
"EnterRoom", "EnterRoom",
@ -303,7 +314,7 @@ Item {
); );
} else { } else {
config.observing = true; config.observing = true;
Backend.callLuaFunction("SetObserving", [true]); lcall("SetObserving", true);
mainWindow.busy = true; mainWindow.busy = true;
ClientInstance.notifyServer( ClientInstance.notifyServer(
"ObserveRoom", "ObserveRoom",
@ -334,7 +345,7 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
x: 4; y: 2 x: 4; y: 2
font.pixelSize: 16 font.pixelSize: 16
text: Backend.translate("$OnlineInfo") text: luatr("$OnlineInfo")
.arg(lobbyPlayerNum).arg(serverPlayerNum) + "\n" .arg(lobbyPlayerNum).arg(serverPlayerNum) + "\n"
+ "Powered by FreeKill " + FkVersion + "Powered by FreeKill " + FkVersion
} }
@ -357,8 +368,10 @@ Item {
function addToChat(pid, raw, msg) { function addToChat(pid, raw, msg) {
if (raw.type !== 1) return; if (raw.type !== 1) return;
msg = msg.replace(/\{emoji([0-9]+)\}/g, '<img src="../../image/emoji/$1.png" height="24" width="24" />'); msg = msg.replace(/\{emoji([0-9]+)\}/g,
raw.msg = raw.msg.replace(/\{emoji([0-9]+)\}/g, '<img src="../../image/emoji/$1.png" height="24" width="24" />'); '<img src="../../image/emoji/$1.png" height="24" width="24" />');
raw.msg = raw.msg.replace(/\{emoji([0-9]+)\}/g,
'<img src="../../image/emoji/$1.png" height="24" width="24" />');
lobbyChat.append(msg); lobbyChat.append(msg);
danmaku.sendLog("<b>" + raw.userName + "</b>: " + raw.msg); danmaku.sendLog("<b>" + raw.userName + "</b>: " + raw.msg);
} }
@ -369,6 +382,6 @@ Item {
} }
Component.onCompleted: { Component.onCompleted: {
toast.show(Backend.translate("$WelcomeToLobby")); toast.show(luatr("$WelcomeToLobby"));
} }
} }

View File

@ -51,7 +51,7 @@ Item {
id: modeDesc id: modeDesc
width: parent.width - 16 width: parent.width - 16
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
text: Backend.translate(":" + modeList.get(listView.currentIndex).orig_name) text: luatr(":" + modeList.get(listView.currentIndex).orig_name)
textFormat: Text.MarkdownText textFormat: Text.MarkdownText
font.pixelSize: 16 font.pixelSize: 16
} }
@ -60,7 +60,7 @@ Item {
} }
Button { Button {
text: Backend.translate("Quit") text: luatr("Quit")
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
onClicked: { onClicked: {
mainStack.pop(); mainStack.pop();
@ -68,7 +68,7 @@ Item {
} }
Component.onCompleted: { Component.onCompleted: {
const mode_data = JSON.parse(Backend.callLuaFunction("GetGameModes", [])); const mode_data = lcall("GetGameModes");
for (let d of mode_data) { for (let d of mode_data) {
modeList.append(d); modeList.append(d);
} }

View File

@ -20,7 +20,7 @@ Item {
onClicked: mainStack.pop(); onClicked: mainStack.pop();
} }
Label { Label {
text: Backend.translate("Replay Manager") text: luatr("Replay Manager")
horizontalAlignment: Qt.AlignHCenter horizontalAlignment: Qt.AlignHCenter
Layout.fillWidth: true Layout.fillWidth: true
} }
@ -75,8 +75,9 @@ Item {
Text { Text {
text: { text: {
const win = winner.split("+").indexOf(role) !== -1; const win = winner.split("+").indexOf(role) !== -1;
const winStr = win ? Backend.translate("Game Win") : Backend.translate("Game Lose"); const winStr = win ? luatr("Game Win") : luatr("Game Lose");
return "<b>" + Backend.translate(_general) + "</b> " + Backend.translate(role) + " " + winStr; return "<b>" + luatr(_general) + "</b> " + luatr(role)
+ " " + winStr;
} }
font.pixelSize: 20 font.pixelSize: 20
textFormat: Text.RichText textFormat: Text.RichText
@ -89,16 +90,16 @@ Item {
const h = repDate.slice(8,10); const h = repDate.slice(8,10);
const m = repDate.slice(10,12); const m = repDate.slice(10,12);
const s = repDate.slice(12,14); const s = repDate.slice(12,14);
const dateStr = y + "-" + month + "-" + d + " " + h + ":" + m + ":" + s; const dateStr = `${y}-${month}-${d} ${h}:${m}:${s}`;
return playerName + " " + Backend.translate(gameMode) + " " + dateStr return playerName + " " + luatr(gameMode) + " " + dateStr
} }
} }
} }
Button { Button {
id: replayBtn id: replayBtn
text: Backend.translate("Play the Replay") text: luatr("Play the Replay")
anchors.right: delBtn.left anchors.right: delBtn.left
anchors.rightMargin: 8 anchors.rightMargin: 8
onClicked: { onClicked: {
@ -110,7 +111,7 @@ Item {
Button { Button {
id: delBtn id: delBtn
text: Backend.translate("Delete Replay") text: luatr("Delete Replay")
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: 8 anchors.rightMargin: 8
onClicked: { onClicked: {

View File

@ -73,6 +73,7 @@ Item {
onIsStartedChanged: { onIsStartedChanged: {
if (isStarted) { if (isStarted) {
Backend.playSound("./audio/system/gamestart");
bgm.play(); bgm.play();
canKickOwner = false; canKickOwner = false;
kickOwnerTimer.stop(); kickOwnerTimer.stop();
@ -87,7 +88,7 @@ Item {
anchors.topMargin: 12 anchors.topMargin: 12
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: 12 anchors.rightMargin: 12
text: Backend.translate("Menu") text: luatr("Menu")
onClicked: { onClicked: {
if (menuContainer.visible){ if (menuContainer.visible){
menuContainer.close(); menuContainer.close();
@ -103,7 +104,7 @@ Item {
MenuItem { MenuItem {
id: quitButton id: quitButton
text: Backend.translate("Quit") text: luatr("Quit")
onClicked: { onClicked: {
if (config.replaying) { if (config.replaying) {
Backend.controlReplayer("shutdown"); Backend.controlReplayer("shutdown");
@ -119,14 +120,18 @@ Item {
MenuItem { MenuItem {
id: surrenderButton id: surrenderButton
enabled: !config.observing && !config.replaying enabled: !config.observing && !config.replaying
text: Backend.translate("Surrender") text: luatr("Surrender")
onClicked: { onClicked: {
if (isStarted && !getPhoto(Self.id).dead) { const photo = getPhoto(Self.id);
const surrenderCheck = JSON.parse(Backend.callLuaFunction('CheckSurrenderAvailable', [miscStatus.playedTime])); if (isStarted && !(photo.dead && photo.rest <= 0)) {
const surrenderCheck = lcall('CheckSurrenderAvailable', miscStatus.playedTime);
if (!surrenderCheck.length) { if (!surrenderCheck.length) {
surrenderDialog.informativeText = Backend.translate('Surrender is disabled in this mode'); surrenderDialog.informativeText =
luatr('Surrender is disabled in this mode');
} else { } else {
surrenderDialog.informativeText = surrenderCheck.map(str => `${Backend.translate(str.text)}${str.passed ? '√' : '×'}`).join('<br>'); surrenderDialog.informativeText = surrenderCheck
.map(str => `${luatr(str.text)}${str.passed ? '√' : '×'}`)
.join('<br>');
} }
surrenderDialog.open(); surrenderDialog.open();
} }
@ -135,7 +140,7 @@ Item {
MenuItem { MenuItem {
id: volumeButton id: volumeButton
text: Backend.translate("Audio Settings") text: luatr("Audio Settings")
onClicked: { onClicked: {
volumeDialog.open(); volumeDialog.open();
} }
@ -144,7 +149,7 @@ Item {
} }
Button { Button {
text: Backend.translate("Add Robot") text: luatr("Add Robot")
visible: isOwner && !isStarted && !isFull visible: isOwner && !isStarted && !isFull
anchors.centerIn: parent anchors.centerIn: parent
enabled: config.serverEnableBot enabled: config.serverEnableBot
@ -153,7 +158,7 @@ Item {
} }
} }
Button { Button {
text: Backend.translate("Start Game") text: luatr("Start Game")
visible: isOwner && !isStarted && isFull visible: isOwner && !isStarted && isFull
enabled: isAllReady enabled: isAllReady
anchors.centerIn: parent anchors.centerIn: parent
@ -166,7 +171,7 @@ Item {
interval: 1000 interval: 1000
} }
Button { Button {
text: isReady ? Backend.translate("Cancel Ready") : Backend.translate("Ready") text: isReady ? luatr("Cancel Ready") : luatr("Ready")
visible: !isOwner && !isStarted visible: !isOwner && !isStarted
enabled: !opTimer.running enabled: !opTimer.running
anchors.centerIn: parent anchors.centerIn: parent
@ -205,6 +210,7 @@ Item {
canKickOwner = false; canKickOwner = false;
kickOwnerTimer.stop(); kickOwnerTimer.stop();
} else { } else {
Backend.playSound("./audio/system/ready");
kickOwnerTimer.start(); kickOwnerTimer.start();
} }
} }
@ -237,25 +243,25 @@ Item {
width: parent.width width: parent.width
wrapMode: TextEdit.WordWrap wrapMode: TextEdit.WordWrap
Component.onCompleted: { Component.onCompleted: {
const data = JSON.parse(Backend.callLuaFunction("GetRoomConfig", [])); const data = lcall("GetRoomConfig");
let cardpack = JSON.parse(Backend.callLuaFunction("GetAllCardPack", [])); let cardpack = lcall("GetAllCardPack");
cardpack = cardpack.filter(p => !data.disabledPack.includes(p)); cardpack = cardpack.filter(p => !data.disabledPack.includes(p));
text = Backend.translate("GameMode") + Backend.translate(data.gameMode) + "<br />" text = luatr("GameMode") + luatr(data.gameMode) + "<br />"
+ Backend.translate("LuckCardNum") + "<b>" + data.luckTime + "</b><br />" + luatr("LuckCardNum") + "<b>" + data.luckTime + "</b><br />"
+ Backend.translate("ResponseTime") + "<b>" + config.roomTimeout + "</b><br />" + luatr("ResponseTime") + "<b>" + config.roomTimeout + "</b><br />"
+ Backend.translate("GeneralBoxNum") + "<b>" + data.generalNum + "</b>" + luatr("GeneralBoxNum") + "<b>" + data.generalNum + "</b>"
+ (data.enableFreeAssign ? "<br />" + Backend.translate("IncludeFreeAssign") : "") + (data.enableFreeAssign ? "<br />" + luatr("IncludeFreeAssign")
+ (data.enableDeputy ? " " + Backend.translate("IncludeDeputy") : "") : "")
+ '<br />' + Backend.translate('CardPackages') + cardpack.map(e => { + (data.enableDeputy ? " " + luatr("IncludeDeputy") : "")
let ret = Backend.translate(e); + '<br />' + luatr('CardPackages') + cardpack.map(e => {
if (ret.search(/特殊牌|衍生牌/) === -1) { // TODO: 西= = let ret = luatr(e);
// TODO: 西= =
if (ret.search(/特殊牌|衍生牌/) === -1) {
ret = "<b>" + ret + "</b>"; ret = "<b>" + ret + "</b>";
} }
return ret; return ret;
}).join('') }).join('');
//+ '<br /><b></b>' + data.disabledPack.map(e => Backend.translate(e)).join('')
//+ '<br /><b></b>' + data.disabledGenerals.map(e => Backend.translate(e)).join('')
} }
} }
} }
@ -436,13 +442,14 @@ Item {
anchors.leftMargin: 8 anchors.leftMargin: 8
ColumnLayout { ColumnLayout {
MetroButton { MetroButton {
text: Backend.translate("Choose one handcard") text: luatr("Choose one handcard")
textFont.pixelSize: 28 textFont.pixelSize: 28
visible: { visible: {
if (dashboard.handcardArea.length <= 15) { if (dashboard.handcardArea.length <= 15) {
return false; return false;
} }
if (roomScene.state == "notactive" || roomScene.state == "replying") { if (roomScene.state === "notactive"
|| roomScene.state === "replying") {
return false; return false;
} }
return true; return true;
@ -450,21 +457,21 @@ Item {
onClicked: roomScene.startCheat("../RoomElement/ChooseHandcard"); onClicked: roomScene.startCheat("../RoomElement/ChooseHandcard");
} }
MetroButton { MetroButton {
text: Backend.translate("Revert Selection") text: luatr("Revert Selection")
textFont.pixelSize: 28 textFont.pixelSize: 28
enabled: dashboard.pending_skill !== "" enabled: dashboard.pending_skill !== ""
onClicked: dashboard.revertSelection(); onClicked: dashboard.revertSelection();
} }
// MetroButton { // MetroButton {
// text: Backend.translate("Trust") // text: luatr("Trust")
// } // }
MetroButton { MetroButton {
text: Backend.translate("Sort Cards") text: luatr("Sort Cards")
textFont.pixelSize: 28 textFont.pixelSize: 28
onClicked: Logic.resortHandcards(); onClicked: Logic.resortHandcards();
} }
MetroButton { MetroButton {
text: Backend.translate("Chat") text: luatr("Chat")
textFont.pixelSize: 28 textFont.pixelSize: 28
onClicked: roomDrawer.open(); onClicked: roomDrawer.open();
} }
@ -479,21 +486,21 @@ Item {
onCardSelected: function(card) { onCardSelected: function(card) {
Logic.enableTargets(card); Logic.enableTargets(card);
if (typeof card === "number" && card !== -1 && roomScene.state === "playing" if (typeof card === "number" && card !== -1
&& JSON.parse(Backend.callLuaFunction("GetPlayerHandcards", [Self.id])).includes(card)) { && roomScene.state === "playing"
&& lcall("GetPlayerHandcards", Self.id).includes(card)) {
const skills = JSON.parse(Backend.callLuaFunction("GetCardSpecialSkills", [card])); const skills = lcall("GetCardSpecialSkills", card);
if (JSON.parse(Backend.callLuaFunction("CanUseCard", [card, Self.id]))) { if (lcall("CanUseCard", card, Self.id,
JSON.stringify(roomScene.extra_data))) {
skills.unshift("_normal_use"); skills.unshift("_normal_use");
} }
specialCardSkills.model = skills; specialCardSkills.model = skills;
const skillName = Backend.callLuaFunction("GetCardSkill", [card]); const skillName = lcall("GetCardSkill", card);
const prompt = JSON.parse(Backend.callLuaFunction( const prompt = lcall("ActiveSkillPrompt", skillName, card,
"ActiveSkillPrompt", selected_targets);
[skillName, card, selected_targets]
));
if (prompt !== "") { if (prompt !== "") {
roomScene.setPrompt(processPrompt(prompt)); roomScene.setPrompt(Util.processPrompt(prompt));
} }
} else { } else {
specialCardSkills.model = []; specialCardSkills.model = [];
@ -524,18 +531,19 @@ Item {
const totalMin = Math.floor(replayerDuration / 60); const totalMin = Math.floor(replayerDuration / 60);
const totalSec = replayerDuration % 60; const totalSec = replayerDuration % 60;
return elapsedMin.toString() + ":" + elapsedSec + "/" + totalMin + ":" + totalSec; return elapsedMin.toString() + ":" + elapsedSec + "/" + totalMin
+ ":" + totalSec;
} }
} }
Switch { Switch {
text: Backend.translate("Speed Resume") text: luatr("Speed Resume")
checked: false checked: false
onCheckedChanged: Backend.controlReplayer("uniform"); onCheckedChanged: Backend.controlReplayer("uniform");
} }
Button { Button {
text: Backend.translate("Speed Down") text: luatr("Speed Down")
onClicked: Backend.controlReplayer("slowdown"); onClicked: Backend.controlReplayer("slowdown");
} }
@ -546,13 +554,13 @@ Item {
} }
Button { Button {
text: Backend.translate("Speed Up") text: luatr("Speed Up")
onClicked: Backend.controlReplayer("speedup"); onClicked: Backend.controlReplayer("speedup");
} }
Button { Button {
property bool running: true property bool running: true
text: Backend.translate(running ? "Pause" : "Resume") text: luatr(running ? "Pause" : "Resume")
onClicked: { onClicked: {
running = !running; running = !running;
Backend.controlReplayer("toggle"); Backend.controlReplayer("toggle");
@ -657,7 +665,7 @@ Item {
id: specialCardSkills id: specialCardSkills
RadioButton { RadioButton {
property string orig_text: modelData property string orig_text: modelData
text: Backend.translate(modelData) text: luatr(modelData)
checked: index === 0 checked: index === 0
onCheckedChanged: { onCheckedChanged: {
roomScene.resetPrompt(); roomScene.resetPrompt();
@ -665,23 +673,19 @@ Item {
let prompt = "" let prompt = ""
if (modelData === "_normal_use") { if (modelData === "_normal_use") {
Logic.enableTargets(card); Logic.enableTargets(card);
const skillName = Backend.callLuaFunction("GetCardSkill", [card]); const skillName = lcall("GetCardSkill", card);
prompt = JSON.parse(Backend.callLuaFunction( prompt = lcall("ActiveSkillPrompt", skillName, card,
"ActiveSkillPrompt", selected_targets);
[skillName, card, selected_targets]
));
} else { } else {
Logic.enableTargets(JSON.stringify({ Logic.enableTargets(JSON.stringify({
skill: modelData, skill: modelData,
subcards: [card], subcards: [card],
})); }));
prompt = JSON.parse(Backend.callLuaFunction( prompt = lcall("ActiveSkillPrompt", modelData, card,
"ActiveSkillPrompt", selected_targets);
[modelData, card, selected_targets]
));
} }
if (prompt !== "") { if (prompt !== "") {
roomScene.setPrompt(processPrompt(prompt)); roomScene.setPrompt(Util.processPrompt(prompt));
} }
} }
} }
@ -707,8 +711,9 @@ Item {
Button { Button {
id: skipNullificationButton id: skipNullificationButton
text: Backend.translate("SkipNullification") text: luatr("SkipNullification")
visible: !!extra_data.useEventId && !skippedUseEventId.find(id => id === extra_data.useEventId) visible: !!extra_data.useEventId
&& !skippedUseEventId.find(id => id === extra_data.useEventId)
onClicked: { onClicked: {
skippedUseEventId.push(extra_data.useEventId); skippedUseEventId.push(extra_data.useEventId);
Logic.doCancelButton(); Logic.doCancelButton();
@ -717,20 +722,20 @@ Item {
Button { Button {
id: okButton id: okButton
text: Backend.translate("OK") text: luatr("OK")
onClicked: Logic.doOkButton(); onClicked: Logic.doOkButton();
} }
Button { Button {
id: cancelButton id: cancelButton
text: Backend.translate("Cancel") text: luatr("Cancel")
onClicked: Logic.doCancelButton(); onClicked: Logic.doCancelButton();
} }
} }
Button { Button {
id: endPhaseButton id: endPhaseButton
text: Backend.translate("End") text: luatr("End")
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.bottomMargin: 40 anchors.bottomMargin: 40
anchors.right: parent.right anchors.right: parent.right
@ -793,12 +798,13 @@ Item {
function activateSkill(skill_name, pressed) { function activateSkill(skill_name, pressed) {
if (pressed) { if (pressed) {
const data = JSON.parse(Backend.callLuaFunction("GetInteractionOfSkill", [skill_name])); const data = lcall("GetInteractionOfSkill", skill_name);
if (data) { if (data) {
Backend.callLuaFunction("SetInteractionDataOfSkill", [skill_name, "null"]); lcall("SetInteractionDataOfSkill", skill_name, "null");
switch (data.type) { switch (data.type) {
case "combo": case "combo":
skillInteraction.sourceComponent = Qt.createComponent("../SkillInteraction/SkillCombo.qml"); skillInteraction.sourceComponent =
Qt.createComponent("../SkillInteraction/SkillCombo.qml");
skillInteraction.item.skill = skill_name; skillInteraction.item.skill = skill_name;
skillInteraction.item.default_choice = data["default"]; skillInteraction.item.default_choice = data["default"];
skillInteraction.item.choices = data.choices; skillInteraction.item.choices = data.choices;
@ -807,11 +813,18 @@ Item {
// skillInteraction.item.clicked(); // skillInteraction.item.clicked();
break; break;
case "spin": case "spin":
skillInteraction.sourceComponent = Qt.createComponent("../SkillInteraction/SkillSpin.qml"); skillInteraction.sourceComponent =
Qt.createComponent("../SkillInteraction/SkillSpin.qml");
skillInteraction.item.skill = skill_name; skillInteraction.item.skill = skill_name;
skillInteraction.item.from = data.from; skillInteraction.item.from = data.from;
skillInteraction.item.to = data.to; skillInteraction.item.to = data.to;
break; break;
case "custom":
skillInteraction.sourceComponent =
Qt.createComponent(AppPath + "/" + data.qml_path + ".qml");
skillInteraction.item.skill = skill_name;
skillInteraction.item.extra_data = data;
break;
default: default:
skillInteraction.sourceComponent = undefined; skillInteraction.sourceComponent = undefined;
break; break;
@ -866,11 +879,11 @@ Item {
width: roomDrawer.width width: roomDrawer.width
TabButton { TabButton {
width: roomDrawer.width / 2 width: roomDrawer.width / 2
text: Backend.translate("Log") text: luatr("Log")
} }
TabButton { TabButton {
width: roomDrawer.width / 2 width: roomDrawer.width / 2
text: Backend.translate("Chat") text: luatr("Chat")
} }
} }
} }
@ -913,8 +926,8 @@ Item {
MessageDialog { MessageDialog {
id: quitDialog id: quitDialog
title: Backend.translate("Quit") title: luatr("Quit")
informativeText: Backend.translate("Are you sure to quit?") informativeText: luatr("Are you sure to quit?")
buttons: MessageDialog.Ok | MessageDialog.Cancel buttons: MessageDialog.Ok | MessageDialog.Cancel
onButtonClicked: function (button) { onButtonClicked: function (button) {
switch (button) { switch (button) {
@ -931,14 +944,17 @@ Item {
MessageDialog { MessageDialog {
id: surrenderDialog id: surrenderDialog
title: Backend.translate("Surrender") title: luatr("Surrender")
informativeText: '' informativeText: ''
buttons: MessageDialog.Ok | MessageDialog.Cancel buttons: MessageDialog.Ok | MessageDialog.Cancel
onButtonClicked: function (button, role) { onButtonClicked: function (button, role) {
switch (button) { switch (button) {
case MessageDialog.Ok: { case MessageDialog.Ok: {
const surrenderCheck = JSON.parse(Backend.callLuaFunction('CheckSurrenderAvailable', [miscStatus.playedTime])); const surrenderCheck =
if (surrenderCheck.length && !surrenderCheck.find(check => !check.passed)) { lcall('CheckSurrenderAvailable', miscStatus.playedTime);
if (surrenderCheck.length &&
!surrenderCheck.find(check => !check.passed)) {
ClientInstance.notifyServer("PushRequest", [ ClientInstance.notifyServer("PushRequest", [
"surrender", true "surrender", true
]); ]);
@ -977,7 +993,7 @@ Item {
GlowText { GlowText {
anchors.centerIn: dashboard anchors.centerIn: dashboard
visible: Logic.getPhoto(Self.id).rest > 0 && !config.observing visible: Logic.getPhoto(Self.id).rest > 0 && !config.observing
text: Backend.translate("Resting, don't leave!") text: luatr("Resting, don't leave!")
color: "#DBCC69" color: "#DBCC69"
font.family: fontLibian.name font.family: fontLibian.name
font.pixelSize: 28 font.pixelSize: 28
@ -991,60 +1007,13 @@ Item {
color: "transparent" color: "transparent"
GlowText { GlowText {
anchors.centerIn: parent anchors.centerIn: parent
text: Backend.translate("Observing ...") text: luatr("Observing ...")
color: "#4B83CD" color: "#4B83CD"
font.family: fontLi2.name font.family: fontLi2.name
font.pixelSize: 48 font.pixelSize: 48
} }
} }
/* 西使
Rectangle {
id: easyChat
width: parent.width
height: 28
anchors.bottom: parent.bottom
visible: false
color: "#040403"
radius: 3
border.width: 1
border.color: "#A6967A"
TextInput {
id: easyChatEdit
anchors.fill: parent
anchors.margins: 6
color: "white"
clip: true
font.pixelSize: 14
onAccepted: {
if (text != "") {
ClientInstance.notifyServer(
"Chat",
JSON.stringify({
type: 0,
msg: text
})
);
text = "";
easyChat.visible = false;
easyChatEdit.enabled = false;
}
}
}
}
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
@ -1077,10 +1046,9 @@ Item {
} }
Shortcut { Shortcut {
sequence: "Esc" sequence: "T"
onActivated: { onActivated: {
easyChat.visible = false; roomDrawer.open();
easyChatEdit.enabled = false;
} }
} }
@ -1096,20 +1064,9 @@ Item {
onActivated: Logic.doCancelButton(); onActivated: Logic.doCancelButton();
} }
function processPrompt(prompt) {
const data = prompt.split(":");
let raw = Backend.translate(data[0]);
const src = parseInt(data[1]);
const dest = parseInt(data[2]);
if (raw.match("%src")) raw = raw.replace(/%src/g, Backend.translate(getPhoto(src).general));
if (raw.match("%dest")) raw = raw.replace(/%dest/g, Backend.translate(getPhoto(dest).general));
if (raw.match("%arg2")) raw = raw.replace(/%arg2/g, Backend.translate(data[4]));
if (raw.match("%arg")) raw = raw.replace(/%arg/g, Backend.translate(data[3]));
return raw;
}
function getCurrentCardUseMethod() { function getCurrentCardUseMethod() {
if (specialCardSkills.count === 1 && specialCardSkills.model[0] !== "_normal_use") { if (specialCardSkills.count === 1
&& specialCardSkills.model[0] !== "_normal_use") {
return specialCardSkills.model[0]; return specialCardSkills.model[0];
} }
@ -1125,8 +1082,10 @@ Item {
function addToChat(pid, raw, msg) { function addToChat(pid, raw, msg) {
if (raw.type === 1) return; if (raw.type === 1) return;
msg = msg.replace(/\{emoji([0-9]+)\}/g, '<img src="../../image/emoji/$1.png" height="24" width="24" />'); msg = msg.replace(/\{emoji([0-9]+)\}/g,
raw.msg = raw.msg.replace(/\{emoji([0-9]+)\}/g, '<img src="../../image/emoji/$1.png" height="24" width="24" />'); '<img src="../../image/emoji/$1.png" height="24" width="24" />');
raw.msg = raw.msg.replace(/\{emoji([0-9]+)\}/g,
'<img src="../../image/emoji/$1.png" height="24" width="24" />');
if (raw.msg.startsWith("$")) { if (raw.msg.startsWith("$")) {
if (specialChat(pid, raw, raw.msg.slice(1))) return; if (specialChat(pid, raw, raw.msg.slice(1))) return;
@ -1149,7 +1108,7 @@ Item {
const time = data.time; const time = data.time;
const userName = data.userName; const userName = data.userName;
const general = Backend.translate(data.general); const general = luatr(data.general);
if (msg.startsWith("!")) { if (msg.startsWith("!")) {
const splited = msg.split(":"); const splited = msg.split(":");
@ -1167,10 +1126,15 @@ Item {
// return false; // return false;
const fromItem = Logic.getPhotoOrDashboard(fromId); const fromItem = Logic.getPhotoOrDashboard(fromId);
const fromPos = mapFromItem(fromItem, fromItem.width / 2, fromItem.height / 2); const fromPos = mapFromItem(fromItem, fromItem.width / 2,
fromItem.height / 2);
const toItem = Logic.getPhoto(toId); const toItem = Logic.getPhoto(toId);
const toPos = mapFromItem(toItem, toItem.width / 2, toItem.height / 2); const toPos = mapFromItem(toItem, toItem.width / 2,
const egg = component.createObject(roomScene, { start: fromPos, end: toPos }); toItem.height / 2);
const egg = component.createObject(roomScene, {
start: fromPos,
end: toPos
});
egg.finished.connect(() => egg.destroy()); egg.finished.connect(() => egg.destroy());
egg.running = true; egg.running = true;
@ -1181,11 +1145,11 @@ Item {
} }
} else if (msg.startsWith("~")) { } else if (msg.startsWith("~")) {
const g = msg.slice(1); const g = msg.slice(1);
const extension = JSON.parse(Backend.callLuaFunction("GetGeneralData", [g])).extension; const extension = lcall("GetGeneralData", g).extension;
if (!config.disableMsgAudio) if (!config.disableMsgAudio)
Backend.playSound("./packages/" + extension + "/audio/death/" + g); Backend.playSound("./packages/" + extension + "/audio/death/" + g);
const m = Backend.translate("~" + g); const m = luatr("~" + g);
data.msg = m; data.msg = m;
if (general === "") if (general === "")
chat.append(`[${time}] ${userName}: ${m}`, data); chat.append(`[${time}] ${userName}: ${m}`, data);
@ -1207,15 +1171,17 @@ Item {
const idx = parseInt(splited[1]); const idx = parseInt(splited[1]);
const gene = splited[2]; const gene = splited[2];
try { if (!config.disableMsgAudio)
callbacks["LogEvent"](JSON.stringify({ try {
type: "PlaySkillSound", callbacks["LogEvent"](JSON.stringify({
name: skill, type: "PlaySkillSound",
general: gene, name: skill,
i: idx, general: gene,
})); i: idx,
} catch (e) {} }));
const m = Backend.translate("$" + skill + (gene ? "_" + gene : "") + (idx ? idx.toString() : "")); } catch (e) {}
const m = luatr("$" + skill + (gene ? "_" + gene : "")
+ (idx ? idx.toString() : ""));
data.msg = m; data.msg = m;
if (general === "") if (general === "")
chat.append(`[${time}] ${userName}: ${m}`, data); chat.append(`[${time}] ${userName}: ${m}`, data);
@ -1253,8 +1219,7 @@ Item {
for (let i = 0; i < photoModel.count; i++) { for (let i = 0; i < photoModel.count; i++) {
const item = photos.itemAt(i); const item = photos.itemAt(i);
if (show) { if (show) {
const dis = Backend.callLuaFunction("DistanceTo",[Self.id, item.playerid]); item.distance = lcall("DistanceTo", Self.id, item.playerid);
item.distance = parseInt(dis);
} else { } else {
item.distance = -1; item.distance = -1;
} }
@ -1273,7 +1238,7 @@ Item {
const item = photoModel.get(i); const item = photoModel.get(i);
let gameData; let gameData;
try { try {
gameData = JSON.parse(Backend.callLuaFunction("GetPlayerGameData", [item.id])); gameData = lcall("GetPlayerGameData", item.id);
} catch (e) { } catch (e) {
console.log(e); console.log(e);
gameData = [0, 0, 0, 0]; gameData = [0, 0, 0, 0];
@ -1290,7 +1255,7 @@ Item {
} }
} }
mainStack.pop(); mainStack.pop();
Backend.callLuaFunction("ResetClientLua", []); lcall("ResetClientLua");
mainStack.push(room); mainStack.push(room);
mainStack.currentItem.loadPlayerData(datalist); mainStack.currentItem.loadPlayerData(datalist);
} }
@ -1306,12 +1271,13 @@ Item {
function loadPlayerData(datalist) { function loadPlayerData(datalist) {
datalist.forEach(d => { datalist.forEach(d => {
if (d.id == Self.id) { if (d.id === Self.id) {
roomScene.isOwner = d.isOwner; roomScene.isOwner = d.isOwner;
} else { } else {
Backend.callLuaFunction("ResetAddPlayer", [JSON.stringify([d.id, d.name, d.avatar, d.ready, d.gameData[3]])]); lcall("ResetAddPlayer",
JSON.stringify([d.id, d.name, d.avatar, d.ready, d.gameData[3]]));
} }
Backend.callLuaFunction("SetPlayerGameData", [d.id, d.gameData]); lcall("SetPlayerGameData", d.id, d.gameData);
Logic.getPhotoModel(d.id).isOwner = d.isOwner; Logic.getPhotoModel(d.id).isOwner = d.isOwner;
}); });
} }
@ -1321,7 +1287,7 @@ Item {
} }
Component.onCompleted: { Component.onCompleted: {
toast.show(Backend.translate("$EnterRoom")); toast.show(luatr("$EnterRoom"));
playerNum = config.roomCapacity; playerNum = config.roomCapacity;
for (let i = 0; i < playerNum; i++) { for (let i = 0; i < playerNum; i++) {

View File

@ -28,11 +28,13 @@ function arrangeManyPhotos() {
const roomAreaPadding = -16; const roomAreaPadding = -16;
let horizontalSpacing = 8; let horizontalSpacing = 8;
let photoWidth = (roomArea.width - horizontalSpacing * playerNum) / (playerNum - 1); let photoWidth = (roomArea.width - horizontalSpacing * playerNum)
/ (playerNum - 1);
let photoScale = 0.75; let photoScale = 0.75;
if (photoWidth > photoMaxWidth) { if (photoWidth > photoMaxWidth) {
photoWidth = photoMaxWidth; photoWidth = photoMaxWidth;
horizontalSpacing = (roomArea.width - photoWidth * (playerNum - 1)) / playerNum; horizontalSpacing = (roomArea.width - photoWidth * (playerNum - 1))
/ playerNum;
} else { } else {
photoScale = photoWidth / photoBaseWidth; photoScale = photoWidth / photoBaseWidth;
} }
@ -134,14 +136,13 @@ function arrangePhotos() {
function doOkButton() { function doOkButton() {
if (roomScene.state === "playing" || roomScene.state === "responding") { if (roomScene.state === "playing" || roomScene.state === "responding") {
const reply = JSON.stringify( const reply = JSON.stringify({
{ card: dashboard.getSelectedCard(),
card: dashboard.getSelectedCard(), targets: selected_targets,
targets: selected_targets, special_skill: roomScene.getCurrentCardUseMethod(),
special_skill: roomScene.getCurrentCardUseMethod(), interaction_data: roomScene.skillInteraction.item ?
interaction_data: roomScene.skillInteraction.item ? roomScene.skillInteraction.item.answer : undefined, roomScene.skillInteraction.item.answer : undefined,
} });
);
replyToServer(reply); replyToServer(reply);
return; return;
} }
@ -244,7 +245,8 @@ function getPhotoOrDashboard(id) {
function getAreaItem(area, id) { function getAreaItem(area, id) {
if (area === Card.DrawPile) { if (area === Card.DrawPile) {
return drawPile; return drawPile;
} else if (area === Card.DiscardPile || area === Card.Processing || area === Card.Void) { } else if (area === Card.DiscardPile || area === Card.Processing ||
area === Card.Void) {
return tablePile; return tablePile;
} else if (area === Card.AG) { } else if (area === Card.AG) {
return popupBox.item; return popupBox.item;
@ -372,7 +374,8 @@ function setEmotion(id, emotion, isCardId) {
} }
} }
const animation = component.createObject(photo, {source: (OS === "Win" ? "file:///" : "") + path}); const animation = component.createObject(photo,
{ source: (OS === "Win" ? "file:///" : "") + path });
animation.anchors.centerIn = photo; animation.anchors.centerIn = photo;
if (isCardId) { if (isCardId) {
animation.started.connect(() => photo.busy = true); animation.started.connect(() => photo.busy = true);
@ -452,7 +455,8 @@ function doIndicate(from, tos) {
return; return;
const fromItem = getPhotoOrDashboard(from); const fromItem = getPhotoOrDashboard(from);
const fromPos = mapFromItem(fromItem, fromItem.width / 2, fromItem.height / 2); const fromPos = mapFromItem(fromItem, fromItem.width / 2,
fromItem.height / 2);
const end = []; const end = [];
for (let i = 0; i < tos.length; i++) { for (let i = 0; i < tos.length; i++) {
@ -464,11 +468,31 @@ function doIndicate(from, tos) {
} }
const color = "#96943D"; const color = "#96943D";
const line = component.createObject(roomScene, {start: fromPos, end: end, color: color}); const line = component.createObject(roomScene, {
start: fromPos,
end: end,
color: color
});
line.finished.connect(() => line.destroy()); line.finished.connect(() => line.destroy());
line.running = true; line.running = true;
} }
function processPrompt(prompt) {
const data = prompt.split(":");
let raw = luatr(data[0]);
const src = parseInt(data[1]);
const dest = parseInt(data[2]);
if (raw.match("%src"))
raw = raw.replace(/%src/g, luatr(getPhoto(src).general));
if (raw.match("%dest"))
raw = raw.replace(/%dest/g, luatr(getPhoto(dest).general));
if (raw.match("%arg2"))
raw = raw.replace(/%arg2/g, luatr(data[4]));
if (raw.match("%arg"))
raw = raw.replace(/%arg/g, luatr(data[3]));
return raw;
}
callbacks["MaxCard"] = (jsonData) => { callbacks["MaxCard"] = (jsonData) => {
const data = JSON.parse(jsonData); const data = JSON.parse(jsonData);
const id = data.id; const id = data.id;
@ -480,7 +504,7 @@ callbacks["MaxCard"] = (jsonData) => {
} }
function changeSelf(id) { function changeSelf(id) {
Backend.callLuaFunction("ChangeSelf", [id]); lcall("ChangeSelf", id);
// move new selfPhoto to dashboard // move new selfPhoto to dashboard
let order = new Array(photoModel.count); let order = new Array(photoModel.count);
@ -533,15 +557,15 @@ callbacks["AddPlayer"] = (jsonData) => {
} }
} }
function enableTargets(card) { // card: int | { skill: string, subcards: int[] } // card: int | { skill: string, subcards: int[] }
function enableTargets(card) {
if (roomScene.respond_play) { if (roomScene.respond_play) {
const candidate = (!isNaN(card) && card !== -1) || typeof(card) === "string"; const candidate = (!isNaN(card) && card !== -1)
|| typeof(card) === "string";
if (candidate) { if (candidate) {
okButton.enabled = JSON.parse(Backend.callLuaFunction( okButton.enabled =
"CardFitPattern", lcall("CardFitPattern", card, roomScene.responding_card) &&
[card, roomScene.responding_card] !lcall("CardProhibitedResponse", card);
)) && !JSON.parse(Backend.callLuaFunction(
"CardProhibitedResponse", [card]));
} else { } else {
okButton.enabled = false; okButton.enabled = false;
} }
@ -568,10 +592,8 @@ function enableTargets(card) { // card: int | { skill: string, subcards: int[] }
all_photos.forEach(photo => { all_photos.forEach(photo => {
photo.state = "candidate"; photo.state = "candidate";
const id = photo.playerid; const id = photo.playerid;
const ret = JSON.parse(Backend.callLuaFunction( const ret = lcall("CanUseCardToTarget", card, id, selected_targets,
"CanUseCardToTarget", JSON.stringify(roomScene.extra_data));
[card, id, selected_targets]
));
photo.selectable = ret; photo.selectable = ret;
if (roomScene.extra_data instanceof Object) { if (roomScene.extra_data instanceof Object) {
const must = roomScene.extra_data.must_targets; const must = roomScene.extra_data.must_targets;
@ -588,22 +610,20 @@ function enableTargets(card) { // card: int | { skill: string, subcards: int[] }
if (included instanceof Array) { if (included instanceof Array) {
if (included.filter((val) => { if (included.filter((val) => {
return selected_targets.indexOf(val) !== -1; return selected_targets.indexOf(val) !== -1;
}).length === 0 && included.indexOf(id) === -1) photo.selectable = false; }).length === 0 && included.indexOf(id) === -1)
photo.selectable = false;
} }
} }
}) })
okButton.enabled = JSON.parse(Backend.callLuaFunction( okButton.enabled = lcall("CardFeasible", card, selected_targets);
"CardFeasible", [card, selected_targets]
));
if (okButton.enabled && roomScene.state === "responding") { if (okButton.enabled && roomScene.state === "responding") {
okButton.enabled = JSON.parse(Backend.callLuaFunction( okButton.enabled =
"CardFitPattern", lcall("CardFitPattern", card, roomScene.responding_card) &&
[card, roomScene.responding_card] (roomScene.autoPending || !lcall("CardProhibitedUse", card));
)) && (roomScene.autoPending || !JSON.parse(Backend.callLuaFunction(
"CardProhibitedUse", [card])));
} else if (okButton.enabled && roomScene.state === "playing") { } else if (okButton.enabled && roomScene.state === "playing") {
okButton.enabled = JSON.parse(Backend.callLuaFunction("CanUseCard", [card, Self.id])); okButton.enabled = lcall("CanUseCard", card, Self.id,
JSON.stringify(roomScene.extra_data));
} }
if (okButton.enabled) { if (okButton.enabled) {
if (roomScene.extra_data instanceof Object) { if (roomScene.extra_data instanceof Object) {
@ -646,10 +666,8 @@ function updateSelectedTargets(playerid, selected) {
all_photos.forEach(photo => { all_photos.forEach(photo => {
if (photo.selected) return; if (photo.selected) return;
const id = photo.playerid; const id = photo.playerid;
const ret = JSON.parse(Backend.callLuaFunction( const ret = lcall("CanUseCardToTarget", card, id, selected_targets,
"CanUseCardToTarget", JSON.stringify(roomScene.extra_data));
[card, id, selected_targets]
));
photo.selectable = ret; photo.selectable = ret;
if (roomScene.extra_data instanceof Object) { if (roomScene.extra_data instanceof Object) {
const must = roomScene.extra_data.must_targets; const must = roomScene.extra_data.must_targets;
@ -666,22 +684,20 @@ function updateSelectedTargets(playerid, selected) {
if (included instanceof Array) { if (included instanceof Array) {
if (included.filter((val) => { if (included.filter((val) => {
return selected_targets.indexOf(val) !== -1; return selected_targets.indexOf(val) !== -1;
}).length === 0 && included.indexOf(id) === -1) photo.selectable = false; }).length === 0 && included.indexOf(id) === -1)
photo.selectable = false;
} }
} }
}) })
okButton.enabled = JSON.parse(Backend.callLuaFunction( okButton.enabled = lcall("CardFeasible", card, selected_targets);
"CardFeasible", [card, selected_targets]
));
if (okButton.enabled && roomScene.state === "responding") { if (okButton.enabled && roomScene.state === "responding") {
okButton.enabled = JSON.parse(Backend.callLuaFunction( okButton.enabled =
"CardFitPattern", lcall("CardFitPattern", card, roomScene.responding_card) &&
[card, roomScene.responding_card] (roomScene.autoPending || !lcall("CardProhibitedUse", card));
)) && (roomScene.autoPending || !JSON.parse(Backend.callLuaFunction(
"CardProhibitedUse", [card])));
} else if (okButton.enabled && roomScene.state === "playing") { } else if (okButton.enabled && roomScene.state === "playing") {
okButton.enabled = JSON.parse(Backend.callLuaFunction("CanUseCard", [card, Self.id])); okButton.enabled = lcall("CanUseCard", card, Self.id,
JSON.stringify(roomScene.extra_data));
} }
if (okButton.enabled) { if (okButton.enabled) {
if (roomScene.extra_data instanceof Object) { if (roomScene.extra_data instanceof Object) {
@ -819,8 +835,7 @@ callbacks["UpdateCard"] = (j) => {
return; return;
} }
const data = JSON.parse(Backend.callLuaFunction("GetCardData", [id])); card.setData(lcall("GetCardData", id));
card.setData(data);
} }
callbacks["StartGame"] = (jsonData) => { callbacks["StartGame"] = (jsonData) => {
@ -878,8 +893,8 @@ callbacks["MoveFocus"] = (jsonData) => {
if (focuses.indexOf(model.id) != -1) { if (focuses.indexOf(model.id) != -1) {
item = photos.itemAt(i); item = photos.itemAt(i);
item.progressBar.visible = true; item.progressBar.visible = true;
item.progressTip = Backend.translate(command) item.progressTip = luatr(command)
+ Backend.translate(" thinking..."); + luatr(" thinking...");
/* /*
if (command === "PlayCard") { if (command === "PlayCard") {
@ -914,9 +929,10 @@ callbacks["AskForGeneral"] = (jsonData) => {
const n = data[1]; const n = data[1];
const convert = data[2]; const convert = data[2];
const heg = data[3]; const heg = data[3];
roomScene.setPrompt(Backend.translate("#AskForGeneral"), true); roomScene.setPrompt(luatr("#AskForGeneral"), true);
roomScene.state = "replying"; roomScene.state = "replying";
roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/ChooseGeneralBox.qml"); roomScene.popupBox.sourceComponent =
Qt.createComponent("../RoomElement/ChooseGeneralBox.qml");
const box = roomScene.popupBox.item; const box = roomScene.popupBox.item;
box.accepted.connect(() => { box.accepted.connect(() => {
replyToServer(JSON.stringify(box.choices)); replyToServer(JSON.stringify(box.choices));
@ -934,8 +950,8 @@ callbacks["AskForSkillInvoke"] = (jsonData) => {
const data = JSON.parse(jsonData); const data = JSON.parse(jsonData);
const skill = data[0]; const skill = data[0];
const prompt = data[1]; const prompt = data[1];
roomScene.promptText = prompt ? processPrompt(prompt) : Backend.translate("#AskForSkillInvoke") roomScene.promptText = prompt ? processPrompt(prompt)
.arg(Backend.translate(skill)); : luatr("#AskForSkillInvoke").arg(luatr(skill));
roomScene.state = "replying"; roomScene.state = "replying";
roomScene.okCancel.visible = true; roomScene.okCancel.visible = true;
roomScene.okButton.enabled = true; roomScene.okButton.enabled = true;
@ -953,26 +969,24 @@ callbacks["AskForGuanxing"] = (jsonData) => {
const bottom_area_name = data.bottom_area_name; const bottom_area_name = data.bottom_area_name;
const prompt = data.prompt; const prompt = data.prompt;
roomScene.state = "replying"; roomScene.state = "replying";
roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/GuanxingBox.qml"); roomScene.popupBox.sourceComponent =
data.cards.forEach(id => { Qt.createComponent("../RoomElement/GuanxingBox.qml");
const d = Backend.callLuaFunction("GetCardData", [id]); data.cards.forEach(id => cards.push(lcall("GetCardData", id)));
cards.push(JSON.parse(d));
});
const box = roomScene.popupBox.item; const box = roomScene.popupBox.item;
box.prompt = prompt; box.prompt = prompt;
if (max_top_cards === 0) { if (max_top_cards === 0) {
box.areaCapacities = [max_bottom_cards]; box.areaCapacities = [max_bottom_cards];
box.areaLimits = [min_bottom_cards]; box.areaLimits = [min_bottom_cards];
box.areaNames = [Backend.translate(bottom_area_name)]; box.areaNames = [luatr(bottom_area_name)];
} else { } else {
if (max_bottom_cards === 0) { if (max_bottom_cards === 0) {
box.areaCapacities = [max_top_cards]; box.areaCapacities = [max_top_cards];
box.areaLimits = [min_top_cards]; box.areaLimits = [min_top_cards];
box.areaNames = [Backend.translate(top_area_name)]; box.areaNames = [luatr(top_area_name)];
} else { } else {
box.areaCapacities = [max_top_cards, max_bottom_cards]; box.areaCapacities = [max_top_cards, max_bottom_cards];
box.areaLimits = [min_top_cards, min_bottom_cards]; box.areaLimits = [min_top_cards, min_bottom_cards];
box.areaNames = [Backend.translate(top_area_name), Backend.translate(bottom_area_name)]; box.areaNames = [luatr(top_area_name), luatr(bottom_area_name)];
} }
} }
box.cards = cards; box.cards = cards;
@ -989,18 +1003,16 @@ callbacks["AskForExchange"] = (jsonData) => {
const capacities = []; const capacities = [];
const limits = []; const limits = [];
roomScene.state = "replying"; roomScene.state = "replying";
roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/GuanxingBox.qml"); roomScene.popupBox.sourceComponent =
Qt.createComponent("../RoomElement/GuanxingBox.qml");
let for_i = 0; let for_i = 0;
const box = roomScene.popupBox.item; const box = roomScene.popupBox.item;
data.piles.forEach(ids => { data.piles.forEach(ids => {
if (ids.length > 0) { if (ids.length > 0) {
ids.forEach(id => { ids.forEach(id => cards.push(lcall("GetCardData", id)));
const d = Backend.callLuaFunction("GetCardData", [id]);
cards.push(JSON.parse(d));
});
capacities.push(ids.length); capacities.push(ids.length);
limits.push(0); limits.push(0);
cards_name.push(Backend.translate(data.piles_name[for_i])); cards_name.push(luatr(data.piles_name[for_i]));
for_i ++; for_i ++;
} }
}); });
@ -1024,8 +1036,8 @@ callbacks["AskForChoice"] = (jsonData) => {
const prompt = data[3]; const prompt = data[3];
const detailed = data[4]; const detailed = data[4];
if (prompt === "") { if (prompt === "") {
roomScene.promptText = Backend.translate("#AskForChoice") roomScene.promptText = luatr("#AskForChoice")
.arg(Backend.translate(skill_name)); .arg(luatr(skill_name));
} else { } else {
roomScene.setPrompt(processPrompt(prompt), true); roomScene.setPrompt(processPrompt(prompt), true);
} }
@ -1059,8 +1071,8 @@ callbacks["AskForChoices"] = (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("#AskForChoices") roomScene.promptText = luatr("#AskForChoices")
.arg(Backend.translate(skill_name)); .arg(luatr(skill_name));
} else { } else {
roomScene.setPrompt(processPrompt(prompt), true); roomScene.setPrompt(processPrompt(prompt), true);
} }
@ -1095,13 +1107,14 @@ callbacks["AskForCardChosen"] = (jsonData) => {
const reason = data._reason; const reason = data._reason;
const prompt = data._prompt; const prompt = data._prompt;
if (prompt === "") { if (prompt === "") {
roomScene.promptText = Backend.translate("#AskForChooseCard") roomScene.promptText = luatr(processPrompt("#AskForChooseCard:" + data._id))
.arg(Backend.translate(reason)); .arg(luatr(reason));
} else { } else {
roomScene.setPrompt(processPrompt(prompt), true); roomScene.setPrompt(processPrompt(prompt), true);
} }
roomScene.state = "replying"; roomScene.state = "replying";
roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/PlayerCardBox.qml"); roomScene.popupBox.sourceComponent =
Qt.createComponent("../RoomElement/PlayerCardBox.qml");
const box = roomScene.popupBox.item; const box = roomScene.popupBox.item;
box.prompt = prompt; box.prompt = prompt;
@ -1109,17 +1122,12 @@ callbacks["AskForCardChosen"] = (jsonData) => {
const arr = []; const arr = [];
const ids = d[1]; const ids = d[1];
ids.forEach(id => { ids.forEach(id => arr.push(lcall("GetCardData", id)));
const card_data = JSON.parse(Backend.callLuaFunction("GetCardData", [id]));
arr.push(card_data);
});
box.addCustomCards(d[0], arr); box.addCustomCards(d[0], arr);
} }
roomScene.popupBox.moveToCenter(); roomScene.popupBox.moveToCenter();
box.cardSelected.connect(function(cid){ box.cardSelected.connect(cid => replyToServer(cid));
replyToServer(cid);
});
} }
callbacks["AskForCardsChosen"] = (jsonData) => { callbacks["AskForCardsChosen"] = (jsonData) => {
@ -1131,14 +1139,15 @@ callbacks["AskForCardsChosen"] = (jsonData) => {
const reason = data._reason; const reason = data._reason;
const prompt = data._prompt; const prompt = data._prompt;
if (prompt === "") { if (prompt === "") {
roomScene.promptText = Backend.translate("#AskForChooseCards") roomScene.promptText = luatr(processPrompt("#AskForChooseCards:" + data._id))
.arg(Backend.translate(reason)).arg(min).arg(max); .arg(luatr(reason)).arg(min).arg(max);
} else { } else {
roomScene.setPrompt(processPrompt(prompt), true); roomScene.setPrompt(processPrompt(prompt), true);
} }
roomScene.state = "replying"; roomScene.state = "replying";
roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/PlayerCardBox.qml"); roomScene.popupBox.sourceComponent =
Qt.createComponent("../RoomElement/PlayerCardBox.qml");
const box = roomScene.popupBox.item; const box = roomScene.popupBox.item;
box.multiChoose = true; box.multiChoose = true;
box.min = min; box.min = min;
@ -1148,10 +1157,7 @@ callbacks["AskForCardsChosen"] = (jsonData) => {
const arr = []; const arr = [];
const ids = d[1]; const ids = d[1];
ids.forEach(id => { ids.forEach(id => arr.push(lcall("GetCardData", id)));
const card_data = JSON.parse(Backend.callLuaFunction("GetCardData", [id]));
arr.push(card_data);
});
box.addCustomCards(d[0], arr); box.addCustomCards(d[0], arr);
} }
@ -1165,7 +1171,8 @@ callbacks["AskForPoxi"] = (jsonData) => {
const { type, data, extra_data, cancelable } = JSON.parse(jsonData); const { type, data, extra_data, cancelable } = JSON.parse(jsonData);
roomScene.state = "replying"; roomScene.state = "replying";
roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/PoxiBox.qml"); roomScene.popupBox.sourceComponent =
Qt.createComponent("../RoomElement/PoxiBox.qml");
const box = roomScene.popupBox.item; const box = roomScene.popupBox.item;
box.poxi_type = type; box.poxi_type = type;
box.card_data = data; box.card_data = data;
@ -1175,10 +1182,7 @@ callbacks["AskForPoxi"] = (jsonData) => {
const arr = []; const arr = [];
const ids = d[1]; const ids = d[1];
ids.forEach(id => { ids.forEach(id => arr.push(lcall("GetCardData", id)));
const card_data = JSON.parse(Backend.callLuaFunction("GetCardData", [id]));
arr.push(card_data);
});
box.addCustomCards(d[0], arr); box.addCustomCards(d[0], arr);
} }
@ -1193,13 +1197,14 @@ callbacks["AskForMoveCardInBoard"] = (jsonData) => {
const { cards, cardsPosition, generalNames, playerIds } = data; const { cards, cardsPosition, generalNames, playerIds } = data;
roomScene.state = "replying"; roomScene.state = "replying";
roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/MoveCardInBoardBox.qml"); roomScene.popupBox.sourceComponent =
Qt.createComponent("../RoomElement/MoveCardInBoardBox.qml");
const boxCards = []; const boxCards = [];
cards.forEach(id => { cards.forEach(id => {
const cardPos = cardsPosition[cards.findIndex(cid => cid === id)]; const cardPos = cardsPosition[cards.findIndex(cid => cid === id)];
const d = Backend.callLuaFunction("GetCardData", [id, playerIds[cardPos]]); const d = lcall("GetCardData", id, playerIds[cardPos]);
boxCards.push(JSON.parse(d)); boxCards.push(d);
}); });
const box = roomScene.popupBox.item; const box = roomScene.popupBox.item;
@ -1208,7 +1213,10 @@ callbacks["AskForMoveCardInBoard"] = (jsonData) => {
box.playerIds = playerIds; box.playerIds = playerIds;
box.generalNames = generalNames.map(name => { box.generalNames = generalNames.map(name => {
const namesSplited = name.split('/'); const namesSplited = name.split('/');
return namesSplited.length > 1 ? namesSplited.map(nameSplited => Backend.translate(nameSplited)).join('/') : Backend.translate(name) if (namesSplited.length > 1) {
return namesSplited.map(nameSplited => luatr(nameSplited)).join('/');
}
return luatr(name);
}); });
box.arrangeCards(); box.arrangeCards();
@ -1227,7 +1235,7 @@ callbacks["PlayCard"] = (jsonData) => {
// jsonData: int playerId // jsonData: int playerId
const playerId = parseInt(jsonData); const playerId = parseInt(jsonData);
if (playerId === Self.id) { if (playerId === Self.id) {
roomScene.setPrompt(Backend.translate("#PlayCard"), true); roomScene.setPrompt(luatr("#PlayCard"), true);
roomScene.state = "playing"; roomScene.state = "playing";
okButton.enabled = false; okButton.enabled = false;
} }
@ -1263,19 +1271,6 @@ callbacks["PrelightSkill"] = (jsonData) => {
dashboard.prelightSkill(skill_name, prelight); dashboard.prelightSkill(skill_name, prelight);
} }
// prompt: 'string:<src>:<dest>:<arg>:<arg2>'
function processPrompt(prompt) {
const data = prompt.split(":");
let raw = Backend.translate(data[0]);
const src = parseInt(data[1]);
const dest = parseInt(data[2]);
if (raw.match("%src")) raw = raw.replace(/%src/g, Backend.translate(getPhoto(src).general));
if (raw.match("%dest")) raw = raw.replace(/%dest/g, Backend.translate(getPhoto(dest).general));
if (raw.match("%arg2")) raw = raw.replace(/%arg2/g, Backend.translate(data[4]));
if (raw.match("%arg")) raw = raw.replace(/%arg/g, Backend.translate(data[3]));
return raw;
}
callbacks["AskForUseActiveSkill"] = (jsonData) => { callbacks["AskForUseActiveSkill"] = (jsonData) => {
// jsonData: string skill_name, string prompt // jsonData: string skill_name, string prompt
const data = JSON.parse(jsonData); const data = JSON.parse(jsonData);
@ -1284,8 +1279,8 @@ callbacks["AskForUseActiveSkill"] = (jsonData) => {
const cancelable = data[2]; const cancelable = data[2];
const extra_data = data[3] ?? {}; const extra_data = data[3] ?? {};
if (prompt === "") { if (prompt === "") {
roomScene.promptText = Backend.translate("#AskForUseActiveSkill") roomScene.promptText = luatr("#AskForUseActiveSkill")
.arg(Backend.translate(skill_name)); .arg(luatr(skill_name));
} else { } else {
roomScene.setPrompt(processPrompt(prompt), true); roomScene.setPrompt(processPrompt(prompt), true);
} }
@ -1293,7 +1288,7 @@ callbacks["AskForUseActiveSkill"] = (jsonData) => {
roomScene.respond_play = false; roomScene.respond_play = false;
roomScene.state = "responding"; roomScene.state = "responding";
if (JSON.parse(Backend.callLuaFunction('GetSkillData', [skill_name])).isViewAsSkill) { if (lcall('GetSkillData', skill_name).isViewAsSkill) {
roomScene.responding_card = "."; roomScene.responding_card = ".";
} }
@ -1321,7 +1316,8 @@ callbacks["AskForUseCard"] = (jsonData) => {
const extra_data = data[4]; const extra_data = data[4];
const disabledSkillNames = data[5]; const disabledSkillNames = data[5];
if (extra_data != null) { if (extra_data != null) {
if (extra_data.effectTo !== Self.id && roomScene.skippedUseEventId.find(id => id === extra_data.useEventId)) { if (extra_data.effectTo !== Self.id &&
roomScene.skippedUseEventId.find(id => id === extra_data.useEventId)) {
doCancelButton(); doCancelButton();
return; return;
} else { } else {
@ -1330,8 +1326,8 @@ callbacks["AskForUseCard"] = (jsonData) => {
} }
if (prompt === "") { if (prompt === "") {
roomScene.promptText = Backend.translate("#AskForUseCard") roomScene.promptText = luatr("#AskForUseCard")
.arg(Backend.translate(cardname)); .arg(luatr(cardname));
} else { } else {
roomScene.setPrompt(processPrompt(prompt), true); roomScene.setPrompt(processPrompt(prompt), true);
} }
@ -1352,8 +1348,8 @@ callbacks["AskForResponseCard"] = (jsonData) => {
const disabledSkillNames = data[5]; const disabledSkillNames = data[5];
if (prompt === "") { if (prompt === "") {
roomScene.promptText = Backend.translate("#AskForResponseCard") roomScene.promptText = luatr("#AskForResponseCard")
.arg(Backend.translate(cardname)); .arg(luatr(cardname));
} else { } else {
roomScene.setPrompt(processPrompt(prompt), true); roomScene.setPrompt(processPrompt(prompt), true);
} }
@ -1422,7 +1418,8 @@ callbacks["Animate"] = (jsonData) => {
} }
case "InvokeSkill": { case "InvokeSkill": {
const id = data.player; const id = data.player;
const component = Qt.createComponent("../RoomElement/SkillInvokeAnimation.qml"); const component =
Qt.createComponent("../RoomElement/SkillInvokeAnimation.qml");
if (component.status !== Component.Ready) if (component.status !== Component.Ready)
return; return;
@ -1432,7 +1429,7 @@ callbacks["Animate"] = (jsonData) => {
} }
const animation = component.createObject(photo, { const animation = component.createObject(photo, {
skill_name: Backend.translate(data.name), skill_name: luatr(data.name),
skill_type: (data.skill_type ? data.skill_type : "special"), skill_type: (data.skill_type ? data.skill_type : "special"),
}); });
animation.anchors.centerIn = photo; animation.anchors.centerIn = photo;
@ -1467,7 +1464,8 @@ callbacks["LogEvent"] = (jsonData) => {
setEmotion(data.to, "damage"); setEmotion(data.to, "damage");
item.tremble(); item.tremble();
data.damageType = data.damageType || "normal_damage"; data.damageType = data.damageType || "normal_damage";
Backend.playSound("./audio/system/" + data.damageType + (data.damageNum > 1 ? "2" : "")); Backend.playSound("./audio/system/" + data.damageType +
(data.damageNum > 1 ? "2" : ""));
break; break;
} }
case "LoseHP": { case "LoseHP": {
@ -1489,9 +1487,10 @@ callbacks["LogEvent"] = (jsonData) => {
// try main general // try main general
if (data.general) { if (data.general) {
dat = JSON.parse(Backend.callLuaFunction("GetGeneralData", [data.general])); dat = lcall("GetGeneralData", data.general);
extension = dat.extension; extension = dat.extension;
path = "./packages/" + extension + "/audio/skill/" + skill + "_" + data.general; path = "./packages/" + extension + "/audio/skill/" + skill + "_"
+ data.general;
if (Backend.exists(path + ".mp3") || Backend.exists(path + "1.mp3")) { if (Backend.exists(path + ".mp3") || Backend.exists(path + "1.mp3")) {
Backend.playSound(path, data.i); Backend.playSound(path, data.i);
break; break;
@ -1500,9 +1499,10 @@ callbacks["LogEvent"] = (jsonData) => {
// secondly try deputy general // secondly try deputy general
if (data.deputy) { if (data.deputy) {
dat = JSON.parse(Backend.callLuaFunction("GetGeneralData", [data.deputy])); dat = lcall("GetGeneralData", data.deputy);
extension = dat.extension; extension = dat.extension;
path = "./packages/" + extension + "/audio/skill/" + skill + "_" + data.deputy; path = "./packages/" + extension + "/audio/skill/" + skill + "_"
+ data.deputy;
if (Backend.exists(path + ".mp3") || Backend.exists(path + "1.mp3")) { if (Backend.exists(path + ".mp3") || Backend.exists(path + "1.mp3")) {
Backend.playSound(path, data.i); Backend.playSound(path, data.i);
break; break;
@ -1510,7 +1510,7 @@ callbacks["LogEvent"] = (jsonData) => {
} }
// finally normal skill // finally normal skill
dat = JSON.parse(Backend.callLuaFunction("GetSkillData", [skill])); dat = lcall("GetSkillData", skill);
extension = dat.extension; extension = dat.extension;
path = "./packages/" + extension + "/audio/skill/" + skill; path = "./packages/" + extension + "/audio/skill/" + skill;
Backend.playSound(path, data.i); Backend.playSound(path, data.i);
@ -1522,8 +1522,10 @@ callbacks["LogEvent"] = (jsonData) => {
} }
case "Death": { case "Death": {
const item = getPhoto(data.to); const item = getPhoto(data.to);
const extension = JSON.parse(Backend.callLuaFunction("GetGeneralData", [item.general])).extension; const extension = lcall("GetGeneralData", item.general).extension;
Backend.playSound("./packages/" + extension + "/audio/death/" + item.general); Backend.playSound("./packages/" + extension + "/audio/death/"
+ item.general);
break;
} }
default: default:
break; break;
@ -1532,7 +1534,8 @@ callbacks["LogEvent"] = (jsonData) => {
callbacks["GameOver"] = (jsonData) => { callbacks["GameOver"] = (jsonData) => {
roomScene.state = "notactive"; roomScene.state = "notactive";
roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/GameOverBox.qml"); roomScene.popupBox.sourceComponent =
Qt.createComponent("../RoomElement/GameOverBox.qml");
const box = roomScene.popupBox.item; const box = roomScene.popupBox.item;
box.winner = jsonData; box.winner = jsonData;
// roomScene.isStarted = false; // roomScene.isStarted = false;
@ -1541,7 +1544,8 @@ callbacks["GameOver"] = (jsonData) => {
callbacks["FillAG"] = (j) => { callbacks["FillAG"] = (j) => {
const data = JSON.parse(j); const data = JSON.parse(j);
const ids = data[0]; const ids = data[0];
roomScene.manualBox.sourceComponent = Qt.createComponent("../RoomElement/AG.qml"); roomScene.manualBox.sourceComponent =
Qt.createComponent("../RoomElement/AG.qml");
roomScene.manualBox.item.addIds(ids); roomScene.manualBox.item.addIds(ids);
} }
@ -1556,7 +1560,7 @@ callbacks["TakeAG"] = (j) => {
const pid = data[0]; const pid = data[0];
const cid = data[1]; const cid = data[1];
const item = getPhoto(pid); const item = getPhoto(pid);
const general = Backend.translate(item.general); const general = luatr(item.general);
// the item should be AG box // the item should be AG box
roomScene.manualBox.item.takeAG(general, cid); roomScene.manualBox.item.takeAG(general, cid);
@ -1575,6 +1579,25 @@ callbacks["CustomDialog"] = (j) => {
} }
} }
callbacks["MiniGame"] = (j) => {
const data = JSON.parse(j);
const game = data.type;
const dat = data.data;
const gdata = lcall("GetMiniGame", game, Self.id, JSON.stringify(dat));
roomScene.state = "replying";
roomScene.popupBox.source = AppPath + "/" + gdata.qml_path + ".qml";
if (dat) {
roomScene.popupBox.item.loadData(dat);
}
}
callbacks["UpdateMiniGame"] = (j) => {
const data = JSON.parse(j);
if (roomScene.popupBox.item) {
roomScene.popupBox.item.updateData(data);
}
}
callbacks["UpdateLimitSkill"] = (j) => { callbacks["UpdateLimitSkill"] = (j) => {
const data = JSON.parse(j); const data = JSON.parse(j);
const id = data[0]; const id = data[0];
@ -1633,7 +1656,7 @@ callbacks["AskForLuckCard"] = (j) => {
// jsonData: int time // jsonData: int time
if (config.observing || 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(luatr("#AskForLuckCard").arg(time), true);
roomScene.state = "replying"; roomScene.state = "replying";
roomScene.extra_data = { roomScene.extra_data = {
luckCard: true, luckCard: true,

View File

@ -35,7 +35,7 @@ Item {
Image { Image {
height: 55 * 0.8 height: 55 * 0.8
width: 47 * 0.8 width: 47 * 0.8
source: SkinBank.getDelayedTrickPicture(name) // SkinBank.DELAYED_TRICK_DIR + name source: SkinBank.getDelayedTrickPicture(name)
} }
} }
} }
@ -47,7 +47,7 @@ Item {
inputs = [inputs]; inputs = [inputs];
} }
inputs.forEach(card => { inputs.forEach(card => {
const v = JSON.parse(Backend.callLuaFunction("GetVirtualEquip", [parent.playerid, card.cid])); const v = lcall("GetVirtualEquip", parent.playerid, card.cid);
if (v !== null) { if (v !== null) {
cards.append(v); cards.append(v);
} else { } else {

View File

@ -17,9 +17,15 @@ Item {
height: 70 height: 70
width: 138 width: 138
property int itemHeight: (treasureItem.name === "" && !treasureItem.sealed) ? height / 3 : height / 4 property int itemHeight: {
property var items: [treasureItem, weaponItem, armorItem, defensiveHorseItem, offensiveHorseItem] if (treasureItem.name === "" && !treasureItem.sealed)
property var subtypes: ["treasure", "weapon", "armor", "defensive_horse", "offensive_horse"] return height / 3;
return height / 4;
}
property var items: [treasureItem, weaponItem, armorItem,
defensiveHorseItem, offensiveHorseItem]
property var subtypes: ["treasure", "weapon", "armor",
"defensive_horse", "offensive_horse"]
property int length: area.length property int length: area.length
// FIXME: Qt 6.6 // FIXME: Qt 6.6

View File

@ -30,7 +30,11 @@ Item {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
x: 3 x: 3
source: sealed ? (SkinBank.EQUIP_ICON_DIR + "sealed") : (icon ? SkinBank.getEquipIcon(cid, icon) : "") source: {
if (sealed)
return SkinBank.EQUIP_ICON_DIR + "sealed";
return icon ? SkinBank.getEquipIcon(cid, icon) : "";
}
} }
Image { Image {
@ -44,7 +48,7 @@ Item {
GlowText { GlowText {
id: numberItem id: numberItem
visible: !sealed && number > 0 && number < 14 visible: !sealed && number > 0 && number < 14
text: Utility.convertNumber(number) text: Util.convertNumber(number)
color: "white" color: "white"
font.family: fontLibian.name font.family: fontLibian.name
font.pixelSize: 16 font.pixelSize: 16
@ -132,7 +136,7 @@ Item {
text = "-1" text = "-1"
icon = "horse"; icon = "horse";
} else { } else {
text = Backend.translate(name); text = luatr(name);
if (card.virt_name) { if (card.virt_name) {
icon = card.virt_name; icon = card.virt_name;
} else { } else {
@ -159,6 +163,6 @@ Item {
x = 0; x = 0;
opacity = sealed ? 1 : 0; opacity = sealed ? 1 : 0;
text = ' ' + Backend.translate(subtype + "_sealed") text = ' ' + luatr(subtype + "_sealed")
} }
} }

View File

@ -20,13 +20,24 @@ Column {
id: repeater id: repeater
model: column.visible ? 0 : maxValue model: column.visible ? 0 : maxValue
Magatama { Magatama {
state: (maxValue - 1 - index) >= value ? 0 : (value >= 3 || value >= maxValue ? 3 : (value <= 0 ? 0 : value)) state: {
if (maxValue - 1 - index >= value) {
return 0;
} else if (value >= 3 || value >= maxValue) {
return 3;
} else if (value <= 0) {
return 0;
} else {
return value;
}
}
} }
} }
Column { Column {
id: column id: column
visible: maxValue > 4 || value > maxValue || (shieldNum > 0 && maxValue > 3) visible: maxValue > 4 || value > maxValue ||
(shieldNum > 0 && maxValue > 3)
spacing: -4 spacing: -4
Magatama { Magatama {
@ -37,7 +48,17 @@ Column {
id: hpItem id: hpItem
width: root.width width: root.width
text: value text: value
color: root.colors[(value >= 3 || value >= maxValue) ? 3 : (value <= 0 ? 0 : value)] color: {
let idx;
if (value >= 3 || value >= maxValue) {
idx = 3;
} else if (value <= 0) {
idx = 0;
} else {
idx = value;
}
return root.colors[idx];
}
font.family: fontLibian.name font.family: fontLibian.name
font.pixelSize: 22 font.pixelSize: 22
font.bold: true font.bold: true

View File

@ -25,7 +25,7 @@ Item {
font.family: fontLi2.name font.family: fontLi2.name
style: Text.Outline style: Text.Outline
styleColor: "#3D2D1C" styleColor: "#3D2D1C"
text: Backend.translate(skillname); text: luatr(skillname);
} }
Text { Text {
@ -39,8 +39,7 @@ Item {
} }
onSkillnameChanged: { onSkillnameChanged: {
let data = Backend.callLuaFunction("GetSkillData", [skillname]); let data = lcall("GetSkillData", skillname);
data = JSON.parse(data);
if (data.frequency || data.switchSkillName) { if (data.frequency || data.switchSkillName) {
skilltype = data.switchSkillName ? 'switch' : data.frequency; skilltype = data.switchSkillName ? 'switch' : data.frequency;
visible = true; visible = true;
@ -64,7 +63,8 @@ Item {
} }
} else if (skilltype === 'switch') { } else if (skilltype === 'switch') {
visible = true; visible = true;
bg.source = SkinBank.LIMIT_SKILL_DIR + (usedtimes < 1 ? 'switch' : 'switch-yin'); bg.source = SkinBank.LIMIT_SKILL_DIR +
(usedtimes < 1 ? 'switch' : 'switch-yin');
} else if (skilltype === 'quest') { } else if (skilltype === 'quest') {
visible = true visible = true
if (usedtimes > 1) { if (usedtimes > 1) {

View File

@ -34,7 +34,8 @@ Item {
width: childrenRect.width width: childrenRect.width
height: 22 height: 22
Text { Text {
text: Backend.translate(mark_name) + ' ' + (special_value !== '' ? special_value : mark_extra) text: luatr(mark_name) + ' '
+ (special_value !== '' ? special_value : mark_extra)
font.family: fontLibian.name font.family: fontLibian.name
font.pixelSize: 22 font.pixelSize: 22
font.letterSpacing: -0.6 font.letterSpacing: -0.6
@ -76,15 +77,17 @@ Item {
const mark_type = mark_name.slice(2, close_br); const mark_type = mark_name.slice(2, close_br);
const _data = mark_extra; const _data = mark_extra;
let data = JSON.parse(Backend.callLuaFunction("GetQmlMark", [mark_type, mark_name, _data, root.parent?.playerid])); let data = lcall("GetQmlMark", mark_type, mark_name, _data,
root.parent?.playerid);
if (data && data.qml_path) { if (data && data.qml_path) {
params.data = JSON.parse(_data); params.data = JSON.parse(_data);
params.owner = root.parent?.playerid;
roomScene.startCheat("../../" + data.qml_path, params); roomScene.startCheat("../../" + data.qml_path, params);
} }
return; return;
} else { } else {
if (!root.parent.playerid) return; if (!root.parent.playerid) return;
let data = JSON.parse(Backend.callLuaFunction("GetPile", [root.parent.playerid, mark_name])); let data = lcall("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)
return; return;
@ -92,7 +95,7 @@ Item {
params.ids = data; params.ids = data;
} }
// Just for using room's right drawer // Just for using right drawer of the room
roomScene.startCheat("../RoomElement/ViewPile", params); roomScene.startCheat("../RoomElement/ViewPile", params);
} }
} }
@ -123,13 +126,16 @@ Item {
if (close_br !== -1) { if (close_br !== -1) {
const mark_type = mark.slice(2, close_br); const mark_type = mark.slice(2, close_br);
data = JSON.stringify(data); data = JSON.stringify(data);
const _data = JSON.parse(Backend.callLuaFunction("GetQmlMark", [mark_type, mark, data, root.parent?.playerid])); const _data = lcall("GetQmlMark", mark_type, mark, data,
root.parent?.playerid);
if (_data && _data.text) { if (_data && _data.text) {
special_value = _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) => luatr(markText)).join(' ')
: luatr(data);
} }
if (modelItem) { if (modelItem) {

View File

@ -8,7 +8,7 @@ GraphicsBox {
property bool interactive: false property bool interactive: false
id: root id: root
title.text: Backend.translate("Please choose cards") title.text: luatr("Please choose cards")
width: cards.count * 100 + spacing * (cards.count - 1) + 25 width: cards.count * 100 + spacing * (cards.count - 1) + 25
height: 180 height: 180
@ -46,8 +46,7 @@ GraphicsBox {
function addIds(ids) { function addIds(ids) {
ids.forEach((id) => { ids.forEach((id) => {
let data = Backend.callLuaFunction("GetCardData", [id]); let data = lcall("GetCardData", id);
data = JSON.parse(data);
data.selectable = true; data.selectable = true;
data.footnote = ""; data.footnote = "";
cards.append(data); cards.append(data);

View File

@ -26,7 +26,7 @@ Item {
for (let j = 0; j < outputs.length; j++) { for (let j = 0; j < outputs.length; j++) {
for (let i = cards.length - 1; i >= 0; i--) { for (let i = cards.length - 1; i >= 0; i--) {
if (outputs[j] === cards[i].cid) { if (outputs[j] === cards[i].cid) {
const state = JSON.parse(Backend.callLuaFunction("GetCardData", [cards[i].cid])); const state = lcall("GetCardData", cards[i].cid);
cards[i].setData(state); cards[i].setData(state);
result.push(cards[i]); result.push(cards[i]);
cards.splice(i, 1); cards.splice(i, 1);

View File

@ -51,7 +51,7 @@ Item {
property bool selected: false property bool selected: false
property bool draggable: false property bool draggable: false
property bool autoBack: true property bool autoBack: true
property bool showDetail: false property bool showDetail: true
property int origX: 0 property int origX: 0
property int origY: 0 property int origY: 0
property int initialZ: 0 property int initialZ: 0
@ -76,7 +76,7 @@ Item {
signal hoverChanged(bool enter) signal hoverChanged(bool enter)
onRightClicked: { onRightClicked: {
if (!showDetail) return; if (!showDetail || !known) return;
roomScene.startCheat("CardDetail", { card: this }); roomScene.startCheat("CardDetail", { card: this });
} }
@ -102,7 +102,8 @@ Item {
Image { Image {
id: suitItem id: suitItem
visible: known visible: known
source: (suit !== "" && suit !== "nosuit") ? SkinBank.CARD_SUIT_DIR + suit : "" source: (suit !== "" && suit !== "nosuit") ? SkinBank.CARD_SUIT_DIR + suit
: ""
x: 3 x: 3
y: 19 y: 19
width: 21 width: 21
@ -122,8 +123,10 @@ Item {
Image { Image {
id: colorItem id: colorItem
visible: known && (suit === "" || suit === "nosuit") // && number <= 0 // <- FIXME: visible: known && (suit === "" || suit === "nosuit")
source: (visible && color !== "") ? SkinBank.CARD_SUIT_DIR + "/" + color : "" // && number <= 0 // <- FIXME:
source: (visible && color !== "") ? SkinBank.CARD_SUIT_DIR + "/" + color
: ""
x: 1 x: 1
} }
@ -146,7 +149,7 @@ Item {
font.pixelSize: 16 font.pixelSize: 16
font.family: fontLibian.name font.family: fontLibian.name
font.letterSpacing: -0.6 font.letterSpacing: -0.6
text: Backend.translate(root.virt_name) text: luatr(root.virt_name)
} }
Text { Text {
@ -195,7 +198,7 @@ Item {
font.family: fontLibian.name font.family: fontLibian.name
font.letterSpacing: -0.6 font.letterSpacing: -0.6
text: { text: {
let ret = Backend.translate(modelData.k); let ret = luatr(modelData.k);
if (!modelData.k.startsWith("@@")) { if (!modelData.k.startsWith("@@")) {
ret += modelData.v.toString(); ret += modelData.v.toString();
} }

View File

@ -2,6 +2,7 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Fk
import Fk.Pages import Fk.Pages
GraphicsBox { GraphicsBox {
@ -14,22 +15,10 @@ GraphicsBox {
property var result: [] property var result: []
id: root id: root
title.text: Backend.translate("$Choice").arg(Backend.translate(skill_name)) title.text: luatr("$Choice").arg(luatr(skill_name))
width: Math.max(140, body.width + 20) width: Math.max(140, body.width + 20)
height: buttons.height + body.height + title.height + 20 height: buttons.height + body.height + title.height + 20
function processPrompt(prompt) {
const data = prompt.split(":");
let raw = Backend.translate(data[0]);
const src = parseInt(data[1]);
const dest = parseInt(data[2]);
if (raw.match("%src")) raw = raw.replace(/%src/g, Backend.translate(getPhoto(src).general));
if (raw.match("%dest")) raw = raw.replace(/%dest/g, Backend.translate(getPhoto(dest).general));
if (raw.match("%arg2")) raw = raw.replace(/%arg2/g, Backend.translate(data[4]));
if (raw.match("%arg")) raw = raw.replace(/%arg/g, Backend.translate(data[3]));
return raw;
}
GridLayout { GridLayout {
id: body id: body
// x: 10 // x: 10
@ -43,9 +32,10 @@ GraphicsBox {
model: all_options model: all_options
MetroToggleButton { MetroToggleButton {
// Layout.fillWidth: true Layout.fillWidth: true
text: processPrompt(modelData) text: Util.processPrompt(modelData)
enabled: options.indexOf(modelData) !== -1 && (root.result.length < max_num || triggered) enabled: options.indexOf(modelData) !== -1
&& (root.result.length < max_num || triggered)
onClicked: { onClicked: {
if (triggered) { if (triggered) {
@ -68,7 +58,7 @@ GraphicsBox {
MetroButton { MetroButton {
Layout.fillWidth: true Layout.fillWidth: true
text: processPrompt("OK") text: luatr("OK")
enabled: root.result.length >= min_num enabled: root.result.length >= min_num
onClicked: { onClicked: {
@ -78,7 +68,7 @@ GraphicsBox {
MetroButton { MetroButton {
Layout.fillWidth: true Layout.fillWidth: true
text: processPrompt("Cancel") text: luatr("Cancel")
visible: cancelable visible: cancelable
onClicked: { onClicked: {

View File

@ -2,6 +2,7 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Fk
import Fk.Pages import Fk.Pages
GraphicsBox { GraphicsBox {
@ -11,22 +12,10 @@ GraphicsBox {
property int result property int result
id: root id: root
title.text: Backend.translate("$Choice").arg(Backend.translate(skill_name)) title.text: luatr("$Choice").arg(luatr(skill_name))
width: Math.max(140, body.width + 20) width: Math.max(140, body.width + 20)
height: body.height + title.height + 20 height: body.height + title.height + 20
function processPrompt(prompt) {
const data = prompt.split(":");
let raw = Backend.translate(data[0]);
const src = parseInt(data[1]);
const dest = parseInt(data[2]);
if (raw.match("%src")) raw = raw.replace(/%src/g, Backend.translate(getPhoto(src).general));
if (raw.match("%dest")) raw = raw.replace(/%dest/g, Backend.translate(getPhoto(dest).general));
if (raw.match("%arg2")) raw = raw.replace(/%arg2/g, Backend.translate(data[4]));
if (raw.match("%arg")) raw = raw.replace(/%arg/g, Backend.translate(data[3]));
return raw;
}
GridLayout { GridLayout {
id: body id: body
x: 10 x: 10
@ -40,7 +29,7 @@ GraphicsBox {
MetroButton { MetroButton {
Layout.fillWidth: true Layout.fillWidth: true
text: processPrompt(modelData) text: Util.processPrompt(modelData)
enabled: options.indexOf(modelData) !== -1 enabled: options.indexOf(modelData) !== -1
onClicked: { onClicked: {

View File

@ -19,10 +19,11 @@ GraphicsBox {
} }
id: root id: root
title.text: Backend.translate("$ChooseGeneral").arg(choiceNum) + title.text: luatr("$ChooseGeneral").arg(choiceNum) +
(config.enableFreeAssign ? "(" + Backend.translate("Enable free assign") + ")" : "") (config.enableFreeAssign ? "(" + luatr("Enable free assign") + ")" : "")
width: generalArea.width + body.anchors.leftMargin + body.anchors.rightMargin width: generalArea.width + body.anchors.leftMargin + body.anchors.rightMargin
height: body.implicitHeight + body.anchors.topMargin + body.anchors.bottomMargin height: body.implicitHeight + body.anchors.topMargin +
body.anchors.bottomMargin
Column { Column {
id: body id: body
@ -32,7 +33,8 @@ GraphicsBox {
Item { Item {
id: generalArea id: generalArea
width: (generalList.count > 8 ? Math.ceil(generalList.count / 2) : Math.max(3, generalList.count)) * 97 width: (generalList.count > 8 ? Math.ceil(generalList.count / 2)
: Math.max(3, generalList.count)) * 97
height: generalList.count > 8 ? 290 : 150 height: generalList.count > 8 ? 290 : 150
z: 1 z: 1
@ -43,8 +45,23 @@ GraphicsBox {
Item { Item {
width: 93 width: 93
height: 130 height: 130
x: (index % Math.ceil(generalList.count / (generalList.count > 8 ? 2 : 1))) * 98 + (generalList.count > 8 && index > generalList.count / 2 && generalList.count % 2 == 1 ? 50 : 0) x: {
y: generalList.count <= 8 ? 0 : (index < generalList.count / 2 ? 0 : 135) const count = generalList.count;
let columns = generalList.count;
if (columns > 8) {
columns = Math.ceil(columns / 2);
}
let ret = (index % columns) * 98;
if (count > 8 && index > count / 2 && count % 2 == 1)
ret += 50;
return ret;
}
y: {
if (generalList.count <= 8)
return 0;
return index < generalList.count / 2 ? 0 : 135;
}
} }
} }
} }
@ -93,13 +110,15 @@ GraphicsBox {
MetroButton { MetroButton {
id: convertBtn id: convertBtn
visible: !convertDisabled visible: !convertDisabled
text: Backend.translate("Same General Convert") text: luatr("Same General Convert")
onClicked: roomScene.startCheat("SameConvert", { cards: generalList }); onClicked: {
roomScene.startCheat("SameConvert", { cards: generalList });
}
} }
MetroButton { MetroButton {
id: fightButton id: fightButton
text: Backend.translate("Fight") text: luatr("Fight")
width: 120 width: 120
height: 35 height: 35
enabled: false enabled: false
@ -110,7 +129,7 @@ GraphicsBox {
MetroButton { MetroButton {
id: detailBtn id: detailBtn
enabled: choices.length > 0 enabled: choices.length > 0
text: Backend.translate("Show General Detail") text: luatr("Show General Detail")
onClicked: roomScene.startCheat( onClicked: roomScene.startCheat(
"GeneralDetail", "GeneralDetail",
{ generals: choices } { generals: choices }
@ -233,7 +252,8 @@ GraphicsBox {
for (i = 0; i < generalCardList.count; i++) { for (i = 0; i < generalCardList.count; i++) {
item = generalCardList.itemAt(i); item = generalCardList.itemAt(i);
item.selectable = needSameKingdom ? isHegPair(selectedItem[0], item) : true; item.selectable = needSameKingdom ? isHegPair(selectedItem[0], item)
: true;
if (selectedItem.indexOf(item) != -1) if (selectedItem.indexOf(item) != -1)
continue; continue;
@ -247,9 +267,7 @@ GraphicsBox {
} }
for (let i = 0; i < generalList.count; i++) { for (let i = 0; i < generalList.count; i++) {
if (JSON.parse(Backend.callLuaFunction( if (lcall("GetSameGenerals", generalList.get(i).name).length > 0) {
"GetSameGenerals", [generalList.get(i).name])
).length > 0) {
convertBtn.enabled = true; convertBtn.enabled = true;
return; return;
} }

View File

@ -13,7 +13,7 @@ ColumnLayout {
property var cards: [] property var cards: []
Text { Text {
text: Backend.translate("Handcard selector") text: luatr("Handcard selector")
Layout.fillWidth: true Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
font.pixelSize: 18 font.pixelSize: 18
@ -46,7 +46,7 @@ ColumnLayout {
} }
} }
Component.onCompleted: { Component.onCompleted: {
setData(JSON.parse(Backend.callLuaFunction("GetCardData", [modelData.cid]))); setData(lcall("GetCardData", modelData.cid));
} }
} }
} }

View File

@ -3,6 +3,7 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Qt5Compat.GraphicalEffects import Qt5Compat.GraphicalEffects
import Fk
RowLayout { RowLayout {
id: root id: root
@ -18,6 +19,7 @@ RowLayout {
property alias skillButtons: skillPanel.skill_buttons property alias skillButtons: skillPanel.skill_buttons
property var expanded_piles: ({}) // name -> int[] property var expanded_piles: ({}) // name -> int[]
property var extra_cards: []
property var disabledSkillNames: [] property var disabledSkillNames: []
@ -70,7 +72,7 @@ RowLayout {
handcardAreaItem.unselectAll(expectId); handcardAreaItem.unselectAll(expectId);
} }
function expandPile(pile) { function expandPile(pile, extra_ids, extra_footnote) {
const expanded_pile_names = Object.keys(expanded_piles); const expanded_pile_names = Object.keys(expanded_piles);
if (expanded_pile_names.indexOf(pile) !== -1) if (expanded_pile_names.indexOf(pile) !== -1)
return; return;
@ -79,30 +81,28 @@ RowLayout {
const parentPos = roomScene.mapFromItem(self, 0, 0); const parentPos = roomScene.mapFromItem(self, 0, 0);
expanded_piles[pile] = []; expanded_piles[pile] = [];
let ids, footnote;
if (pile === "_equip") { if (pile === "_equip") {
const equips = self.equipArea.getAllCards(); ids = self.equipArea.getAllCards().map(e => e.cid);
equips.forEach(data => { footnote = "$Equip";
data.x = parentPos.x; } else if (pile === "_extra") {
data.y = parentPos.y; ids = extra_ids;
const card = component.createObject(roomScene, data); extra_cards = ids;
card.footnoteVisible = true; footnote = extra_footnote;
card.footnote = Backend.translate("$Equip");
handcardAreaItem.add(card);
})
handcardAreaItem.updateCardPosition();
} else { } else {
const ids = JSON.parse(Backend.callLuaFunction("GetPile", [self.playerid, pile])); ids = lcall("GetPile", self.playerid, pile);
ids.forEach(id => { footnote = pile;
const data = JSON.parse(Backend.callLuaFunction("GetCardData", [id]));
data.x = parentPos.x;
data.y = parentPos.y;
const card = component.createObject(roomScene, data);
card.footnoteVisible = true;
card.footnote = Backend.translate(pile);
handcardAreaItem.add(card);
});
handcardAreaItem.updateCardPosition();
} }
ids.forEach(id => {
const data = lcall("GetCardData", id);
data.x = parentPos.x;
data.y = parentPos.y;
const card = component.createObject(roomScene, data);
card.footnoteVisible = true;
card.footnote = luatr(footnote);
handcardAreaItem.add(card);
});
handcardAreaItem.updateCardPosition();
} }
function retractPile(pile) { function retractPile(pile) {
@ -124,7 +124,13 @@ RowLayout {
}) })
handcardAreaItem.updateCardPosition(); handcardAreaItem.updateCardPosition();
} else { } else {
const ids = JSON.parse(Backend.callLuaFunction("GetPile", [self.playerid, pile])); let ids = [];
if (pile === "_extra") {
ids = extra_cards;
extra_cards = [];
} else {
ids = lcall("GetPile", self.playerid, pile);
}
ids.forEach(id => { ids.forEach(id => {
const card = handcardAreaItem.remove([id])[0]; const card = handcardAreaItem.remove([id])[0];
card.origX = parentPos.x; card.origX = parentPos.x;
@ -145,25 +151,23 @@ RowLayout {
// If cname is set, we are responding card. // If cname is set, we are responding card.
function enableCards(cname) { function enableCards(cname) {
const cardValid = (cid, cname) => { const cardValid = (cid, cname) => {
let ret = JSON.parse(Backend.callLuaFunction( let ret = lcall("CardFitPattern", cid, cname);
"CardFitPattern", [cid, cname]));
if (ret) { if (ret) {
if (roomScene.respond_play) { if (roomScene.respond_play) {
ret = ret && !JSON.parse(Backend.callLuaFunction( ret = ret && !lcall("CardProhibitedResponse", cid);
"CardProhibitedResponse", [cid]));
} else { } else {
ret = ret && !JSON.parse(Backend.callLuaFunction( ret = ret && !lcall("CardProhibitedUse", cid);
"CardProhibitedUse", [cid]));
} }
} }
return ret; return ret;
} }
const pile_data = JSON.parse(Backend.callLuaFunction("GetAllPiles", [self.playerid])); const pile_data = lcall("GetAllPiles", self.playerid);
extractWoodenOx(); extractWoodenOx();
const handleMethod = roomScene.respond_play ? "response" : "use";
if (cname) { if (cname) {
const ids = []; const ids = [];
let cards = handcardAreaItem.cards; let cards = handcardAreaItem.cards;
@ -172,10 +176,8 @@ RowLayout {
if (cardValid(cards[i].cid, cname)) { if (cardValid(cards[i].cid, cname)) {
ids.push(cards[i].cid); ids.push(cards[i].cid);
} else { } else {
const prohibitReason = Backend.callLuaFunction( const prohibitReason = lcall("GetCardProhibitReason", cards[i].cid,
"GetCardProhibitReason", handleMethod, cname);
[cards[i].cid, roomScene.respond_play ? "response" : "use", cname]
);
if (prohibitReason) { if (prohibitReason) {
cards[i].prohibitReason = prohibitReason; cards[i].prohibitReason = prohibitReason;
} }
@ -190,10 +192,8 @@ RowLayout {
expandPile("_equip"); expandPile("_equip");
} }
} else { } else {
const prohibitReason = Backend.callLuaFunction( const prohibitReason = lcall("GetCardProhibitReason", c.cid,
"GetCardProhibitReason", handleMethod, cname);
[c.cid, roomScene.respond_play ? "response" : "use", cname]
);
if (prohibitReason) { if (prohibitReason) {
c.prohibitReason = prohibitReason; c.prohibitReason = prohibitReason;
} }
@ -223,14 +223,15 @@ RowLayout {
const ids = [], cards = handcardAreaItem.cards; const ids = [], cards = handcardAreaItem.cards;
for (let i = 0; i < cards.length; i++) { for (let i = 0; i < cards.length; i++) {
cards[i].prohibitReason = ""; cards[i].prohibitReason = "";
if (JSON.parse(Backend.callLuaFunction("CanUseCard", [cards[i].cid, Self.id]))) { if (lcall("CanUseCard", cards[i].cid, Self.id,
JSON.stringify(roomScene.extra_data))) {
ids.push(cards[i].cid); ids.push(cards[i].cid);
} else { } else {
// cannot use? considering special_skills // cannot use? considering special_skills
const skills = JSON.parse(Backend.callLuaFunction("GetCardSpecialSkills", [cards[i].cid])); const skills = lcall("GetCardSpecialSkills", cards[i].cid);
for (let j = 0; j < skills.length; j++) { for (let j = 0; j < skills.length; j++) {
const s = skills[j]; const s = skills[j];
if (JSON.parse(Backend.callLuaFunction("ActiveCanUse", [s]))) { if (lcall("ActiveCanUse", s, JSON.stringify(roomScene.extra_data))) {
ids.push(cards[i].cid); ids.push(cards[i].cid);
break; break;
} }
@ -238,7 +239,8 @@ RowLayout {
// still cannot use? show message on card // still cannot use? show message on card
if (!ids.includes(cards[i].cid)) { if (!ids.includes(cards[i].cid)) {
const prohibitReason = Backend.callLuaFunction("GetCardProhibitReason", [cards[i].cid, "play"]); const prohibitReason = lcall("GetCardProhibitReason", cards[i].cid,
"play");
if (prohibitReason) { if (prohibitReason) {
cards[i].prohibitReason = prohibitReason; cards[i].prohibitReason = prohibitReason;
} }
@ -304,21 +306,10 @@ RowLayout {
} }
} }
function processPrompt(prompt) {
const data = prompt.split(":");
let raw = Backend.translate(data[0]);
const src = parseInt(data[1]);
const dest = parseInt(data[2]);
if (raw.match("%src")) raw = raw.replace(/%src/g, Backend.translate(getPhoto(src).general));
if (raw.match("%dest")) raw = raw.replace(/%dest/g, Backend.translate(getPhoto(dest).general));
if (raw.match("%arg2")) raw = raw.replace(/%arg2/g, Backend.translate(data[4]));
if (raw.match("%arg")) raw = raw.replace(/%arg/g, Backend.translate(data[3]));
return raw;
}
function extractWoodenOx() { function extractWoodenOx() {
const pile_data = JSON.parse(Backend.callLuaFunction("GetAllPiles", [self.playerid])); const pile_data = lcall("GetAllPiles", self.playerid);
if (!roomScene.autoPending) { // AskForUseActiveSkill使 if (!roomScene.autoPending) {
// AskForUseActiveSkill使
for (let name in pile_data) { for (let name in pile_data) {
if (name.endsWith("&")) expandPile(name); if (name.endsWith("&")) expandPile(name);
} }
@ -331,28 +322,21 @@ RowLayout {
const enabled_cards = []; const enabled_cards = [];
const targets = roomScene.selected_targets; const targets = roomScene.selected_targets;
const prompt = JSON.parse(Backend.callLuaFunction( const prompt = lcall("ActiveSkillPrompt", pending_skill, pendings,
"ActiveSkillPrompt", targets);
[pending_skill, pendings, targets]
));
if (prompt !== "") { if (prompt !== "") {
roomScene.setPrompt(processPrompt(prompt)); roomScene.setPrompt(Util.processPrompt(prompt));
} }
handcardAreaItem.cards.forEach((card) => { handcardAreaItem.cards.forEach((card) => {
if (card.selected || JSON.parse(Backend.callLuaFunction( if (card.selected || lcall("ActiveCardFilter", pending_skill, card.cid,
"ActiveCardFilter", pendings, targets))
[pending_skill, card.cid, pendings, targets]
)))
enabled_cards.push(card.cid); enabled_cards.push(card.cid);
}); });
const cards = self.equipArea.getAllCards(); const cards = self.equipArea.getAllCards();
cards.forEach(c => { cards.forEach(c => {
if (JSON.parse(Backend.callLuaFunction( if (lcall("ActiveCardFilter", pending_skill, c.cid, pendings, targets)) {
"ActiveCardFilter",
[pending_skill, c.cid, pendings, targets]
))) {
enabled_cards.push(c.cid); enabled_cards.push(c.cid);
if (!expanded_piles["_equip"]) { if (!expanded_piles["_equip"]) {
expandPile("_equip"); expandPile("_equip");
@ -360,26 +344,26 @@ RowLayout {
} }
}) })
const pile = Backend.callLuaFunction("GetExpandPileOfSkill", [pending_skill]); let pile = lcall("GetExpandPileOfSkill", pending_skill);
const pile_ids = JSON.parse(Backend.callLuaFunction("GetPile", [self.playerid, pile])); let pile_ids = pile;
if (typeof pile === "string") {
pile_ids = lcall("GetPile", self.playerid, pile);
} else {
pile = "_extra";
}
pile_ids.forEach(cid => { pile_ids.forEach(cid => {
if (JSON.parse(Backend.callLuaFunction( if (lcall("ActiveCardFilter", pending_skill, cid, pendings, targets)) {
"ActiveCardFilter",
[pending_skill, cid, pendings, targets]
))) {
enabled_cards.push(cid); enabled_cards.push(cid);
}; };
if (!expanded_piles[pile]) { if (!expanded_piles[pile]) {
expandPile(pile); expandPile(pile, pile_ids, pending_skill);
} }
}); });
handcardAreaItem.enableCards(enabled_cards); handcardAreaItem.enableCards(enabled_cards);
if (JSON.parse(Backend.callLuaFunction( if (lcall("CanViewAs", pending_skill, pendings)) {
"CanViewAs",
[pending_skill, pendings]
))) {
pending_card = { pending_card = {
skill: pending_skill, skill: pending_skill,
subcards: pendings subcards: pendings
@ -462,8 +446,8 @@ RowLayout {
continue; continue;
} }
const fitpattern = JSON.parse(Backend.callLuaFunction("SkillFitPattern", [item.orig, cname])); const fitpattern = lcall("SkillFitPattern", item.orig, cname);
const canresp = JSON.parse(Backend.callLuaFunction("SkillCanResponse", [item.orig, cardResponsing])); const canresp = lcall("SkillCanResponse", item.orig, cardResponsing);
item.enabled = fitpattern && canresp; item.enabled = fitpattern && canresp;
} }
return; return;
@ -475,7 +459,8 @@ RowLayout {
continue; continue;
} }
item.enabled = JSON.parse(Backend.callLuaFunction("ActiveCanUse", [item.orig])); item.enabled = lcall("ActiveCanUse", item.orig,
JSON.stringify(roomScene.extra_data));
} }
} }
@ -490,10 +475,9 @@ RowLayout {
} }
function updateHandcards() { function updateHandcards() {
Backend.callLuaFunction("FilterMyHandcards", []); lcall("FilterMyHandcards");
handcardAreaItem.cards.forEach(v => { handcardAreaItem.cards.forEach(v => {
const data = JSON.parse(Backend.callLuaFunction("GetCardData", [v.cid])); v.setData(lcall("GetCardData", v.cid));
v.setData(data);
}); });
} }
@ -514,12 +498,12 @@ RowLayout {
skillPanel.clearSkills(); skillPanel.clearSkills();
const skills = JSON.parse(Backend.callLuaFunction("GetPlayerSkills", [Self.id])); const skills = lcall("GetPlayerSkills", Self.id);
for (let s of skills) { for (let s of skills) {
addSkill(s.name); addSkill(s.name);
} }
cards = roomScene.drawPile.remove(JSON.parse(Backend.callLuaFunction("GetPlayerHandcards", [Self.id]))); cards = roomScene.drawPile.remove(lcall("GetPlayerHandcards", Self.id));
handcardAreaItem.add(cards); handcardAreaItem.add(cards);
} }
} }

View File

@ -15,7 +15,7 @@ GraphicsBox {
property var result: [] property var result: []
id: root id: root
title.text: Backend.translate("$Choice").arg(Backend.translate(skill_name)) title.text: luatr("$Choice").arg(luatr(skill_name))
width: Math.max(140, body.width + 20) width: Math.max(140, body.width + 20)
height: buttons.height + body.height + title.height + 20 height: buttons.height + body.height + title.height + 20
@ -38,8 +38,10 @@ GraphicsBox {
MetroToggleButton { MetroToggleButton {
id: choicetitle id: choicetitle
width: parent.width width: parent.width
text: Backend.translate(modelData) text: luatr(modelData)
enabled: options.indexOf(modelData) !== -1 && (root.result.length < max_num || triggered) triggered: root.result.includes(index)
enabled: options.indexOf(modelData) !== -1
&& (root.result.length < max_num || triggered)
textFont.pixelSize: 24 textFont.pixelSize: 24
anchors.top: choiceDetail.bottom anchors.top: choiceDetail.bottom
anchors.topMargin: 8 anchors.topMargin: 8
@ -64,7 +66,7 @@ GraphicsBox {
Text { Text {
id: detail id: detail
width: parent.width width: parent.width
text: Backend.translate(":" + modelData) text: luatr(":" + modelData)
color: "white" color: "white"
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
font.pixelSize: 16 font.pixelSize: 16
@ -84,7 +86,7 @@ GraphicsBox {
MetroButton { MetroButton {
width: 120 width: 120
height: 35 height: 35
text: Backend.translate("OK") text: luatr("OK")
enabled: root.result.length >= min_num enabled: root.result.length >= min_num
onClicked: { onClicked: {
@ -95,7 +97,7 @@ GraphicsBox {
MetroButton { MetroButton {
width: 120 width: 120
height: 35 height: 35
text: Backend.translate("Cancel") text: luatr("Cancel")
visible: root.cancelable visible: root.cancelable
onClicked: { onClicked: {

View File

@ -12,7 +12,7 @@ GraphicsBox {
property int result property int result
id: root id: root
title.text: Backend.translate("$Choice").arg(Backend.translate(skill_name)) title.text: luatr("$Choice").arg(luatr(skill_name))
width: Math.max(140, body.width + 20) width: Math.max(140, body.width + 20)
height: body.height + title.height + 20 height: body.height + title.height + 20
@ -35,7 +35,7 @@ GraphicsBox {
MetroButton { MetroButton {
id: choicetitle id: choicetitle
width: parent.width width: parent.width
text: Backend.translate(modelData) text: luatr(modelData)
enabled: options.indexOf(modelData) !== -1 enabled: options.indexOf(modelData) !== -1
textFont.pixelSize: 24 textFont.pixelSize: 24
anchors.top: choiceDetail.bottom anchors.top: choiceDetail.bottom
@ -57,7 +57,7 @@ GraphicsBox {
Text { Text {
id: detail id: detail
width: parent.width width: parent.width
text: Backend.translate(":" + modelData) text: luatr(":" + modelData)
color: "white" color: "white"
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
font.pixelSize: 16 font.pixelSize: 16

View File

@ -7,7 +7,7 @@ GraphicsBox {
property string winner: "" property string winner: ""
id: root id: root
title.text: Backend.translate("$GameOver") title.text: luatr("$GameOver")
width: Math.max(140, body.width + 20) width: Math.max(140, body.width + 20)
height: body.height + title.height + 20 height: body.height + title.height + 20
@ -18,12 +18,13 @@ GraphicsBox {
spacing: 10 spacing: 10
Text { Text {
text: winner !== "" ? Backend.translate("$Winner").arg(Backend.translate(winner)) : Backend.translate("$NoWinner") text: winner !== "" ? luatr("$Winner").arg(luatr(winner))
: luatr("$NoWinner")
color: "#E4D5A0" color: "#E4D5A0"
} }
MetroButton { MetroButton {
text: Backend.translate("Back To Room") text: luatr("Back To Room")
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
visible: !config.observing visible: !config.observing
@ -34,7 +35,7 @@ GraphicsBox {
} }
MetroButton { MetroButton {
text: Backend.translate("Back To Lobby") text: luatr("Back To Lobby")
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
onClicked: { onClicked: {
@ -49,13 +50,13 @@ GraphicsBox {
MetroButton { MetroButton {
id: repBtn id: repBtn
text: Backend.translate("Save Replay") text: luatr("Save Replay")
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
visible: !config.replaying visible: !config.replaying
onClicked: { onClicked: {
repBtn.visible = false; repBtn.visible = false;
Backend.callLuaFunction("SaveRecord", []); lcall("SaveRecord");
toast.show("OK."); toast.show("OK.");
} }
} }

View File

@ -29,11 +29,13 @@ CardItem {
suit: "" suit: ""
number: 0 number: 0
footnote: "" footnote: ""
card.source: known ? SkinBank.getGeneralPicture(name) : (SkinBank.GENERALCARD_DIR + 'card-back') card.source: known ? SkinBank.getGeneralPicture(name)
: (SkinBank.GENERALCARD_DIR + 'card-back')
glow.color: "white" //Engine.kingdomColor[kingdom] glow.color: "white" //Engine.kingdomColor[kingdom]
// FIXME: // FIXME:
property bool heg: name.startsWith('hs__') || name.startsWith('ld__') || name.includes('heg__') property bool heg: name.startsWith('hs__') || name.startsWith('ld__') ||
name.includes('heg__')
Image { Image {
source: known ? (SkinBank.GENERALCARD_DIR + "border") : "" source: known ? (SkinBank.GENERALCARD_DIR + "border") : ""
@ -51,7 +53,8 @@ CardItem {
scale: 0.6; x: 9; y: 12 scale: 0.6; x: 9; y: 12
transformOrigin: Item.TopLeft transformOrigin: Item.TopLeft
width: 34; fillMode: Image.PreserveAspectFit width: 34; fillMode: Image.PreserveAspectFit
source: subkingdom ? SkinBank.getGeneralCardDir(subkingdom) + subkingdom : "" source: subkingdom ? SkinBank.getGeneralCardDir(subkingdom) + subkingdom
: ""
visible: detailed && known visible: detailed && known
} }
@ -72,7 +75,8 @@ CardItem {
Image { Image {
id: subkingdomMagatama id: subkingdomMagatama
visible: false visible: false
source: subkingdom ? SkinBank.getGeneralCardDir(subkingdom) + subkingdom + "-magatama" : "" source: subkingdom ? SkinBank.getGeneralCardDir(subkingdom) +
subkingdom + "-magatama" : ""
} }
LinearGradient { LinearGradient {
id: subkingdomMask id: subkingdomMask
@ -153,8 +157,8 @@ CardItem {
height: 80 height: 80
x: 2 x: 2
y: lineCount > 6 ? 30 : 34 y: lineCount > 6 ? 30 : 34
text: name !== "" ? Backend.translate(name) : "nil" text: name !== "" ? luatr(name) : "nil"
visible: Backend.translate(name).length <= 6 && detailed && known visible: luatr(name).length <= 6 && detailed && known
color: "white" color: "white"
font.family: fontLibian.name font.family: fontLibian.name
font.pixelSize: 18 font.pixelSize: 18
@ -168,8 +172,8 @@ CardItem {
y: 12 y: 12
rotation: 90 rotation: 90
transformOrigin: Item.BottomLeft transformOrigin: Item.BottomLeft
text: Backend.translate(name) text: luatr(name)
visible: Backend.translate(name).length > 6 && detailed && known visible: luatr(name).length > 6 && detailed && known
color: "white" color: "white"
font.family: fontLibian.name font.family: fontLibian.name
font.pixelSize: 18 font.pixelSize: 18
@ -191,7 +195,7 @@ CardItem {
border.color: "white" border.color: "white"
border.width: 1 border.width: 1
Text { Text {
text: Backend.translate(pkgName) text: luatr(pkgName)
x: 2; y: 1 x: 2; y: 1
font.family: fontLibian.name font.family: fontLibian.name
font.pixelSize: 14 font.pixelSize: 14
@ -202,7 +206,7 @@ CardItem {
} }
onNameChanged: { onNameChanged: {
const data = JSON.parse(Backend.callLuaFunction("GetGeneralData", [name])); const data = lcall("GetGeneralData", name);
kingdom = data.kingdom; kingdom = data.kingdom;
subkingdom = (data.subkingdom !== kingdom && data.subkingdom) || ""; subkingdom = (data.subkingdom !== kingdom && data.subkingdom) || "";
hp = data.hp; hp = data.hp;

View File

@ -14,7 +14,7 @@ GraphicsBox {
property var areaNames: [] property var areaNames: []
property int padding: 25 property int padding: 25
title.text: Backend.translate(prompt !== "" ? prompt : "Please arrange cards") title.text: luatr(prompt !== "" ? prompt : "Please arrange cards")
width: body.width + padding * 2 width: body.width + padding * 2
height: title.height + body.height + padding * 2 height: title.height + body.height + padding * 2
@ -32,7 +32,8 @@ GraphicsBox {
spacing: 5 spacing: 5
property int areaCapacity: modelData property int areaCapacity: modelData
property string areaName: index < areaNames.length ? qsTr(areaNames[index]) : "" property string areaName: index < areaNames.length
? qsTr(areaNames[index]) : ""
Rectangle { Rectangle {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
@ -81,7 +82,7 @@ GraphicsBox {
MetroButton { MetroButton {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
id: buttonConfirm id: buttonConfirm
text: Backend.translate("OK") text: luatr("OK")
width: 120 width: 120
height: 35 height: 35

View File

@ -34,7 +34,6 @@ Item {
card.autoBack = true; card.autoBack = true;
card.draggable = true; card.draggable = true;
card.selectable = false; card.selectable = false;
card.showDetail = true;
card.clicked.connect(adjustCards); card.clicked.connect(adjustCards);
} }
@ -46,7 +45,6 @@ Item {
card = result[i]; card = result[i];
card.draggable = false; card.draggable = false;
card.selectable = false; card.selectable = false;
card.showDetail = false;
card.selectedChanged.disconnect(adjustCards); card.selectedChanged.disconnect(adjustCards);
card.prohibitReason = ""; card.prohibitReason = "";
} }
@ -57,7 +55,7 @@ Item {
{ {
let card, i; let card, i;
cards.forEach(card => { cards.forEach(card => {
card.selectable = cardIds.contains(card.cid); card.selectable = cardIds.includes(card.cid);
if (!card.selectable) { if (!card.selectable) {
card.selected = false; card.selected = false;
unselectCard(card); unselectCard(card);
@ -93,10 +91,10 @@ Item {
for (let i = 0; i < cards.length; i++) { for (let i = 0; i < cards.length; i++) {
const card = cards[i]; const card = cards[i];
if (card.selected) { if (card.selected) {
if (!selectedCards.contains(card)) if (!selectedCards.includes(card))
selectCard(card); selectCard(card);
} else { } else {
if (selectedCards.contains(card)) if (selectedCards.includes(card))
unselectCard(card); unselectCard(card);
} }
} }

View File

@ -20,7 +20,8 @@ Item {
Rectangle { Rectangle {
width: 6 width: 6
height: Math.sqrt(Math.pow(modelData.x - start.x, 2) + Math.pow(modelData.y - start.y, 2)) * ratio height: Math.sqrt(Math.pow(modelData.x - start.x, 2) +
Math.pow(modelData.y - start.y, 2)) * ratio
x: start.x x: start.x
y: start.y y: start.y
antialiasing: true antialiasing: true

View File

@ -59,7 +59,7 @@ Item {
const items = []; const items = [];
for (let i = 0; i < outputs.length; i++) { for (let i = 0; i < outputs.length; i++) {
if (_contains(outputs[i])) { if (_contains(outputs[i])) {
const state = JSON.parse(Backend.callLuaFunction("GetCardData", [outputs[i]])) const state = lcall("GetCardData", outputs[i]);
state.x = parentPos.x; state.x = parentPos.x;
state.y = parentPos.y; state.y = parentPos.y;
state.opacity = 0; state.opacity = 0;
@ -93,7 +93,8 @@ Item {
const parentPos = roomScene.mapFromItem(root, 0, 0); const parentPos = roomScene.mapFromItem(root, 0, 0);
for (i = 0; i < pendingInput.length; i++) { for (i = 0; i < pendingInput.length; i++) {
card = pendingInput[i]; card = pendingInput[i];
card.origX = parentPos.x - card.width / 2 + ((i - pendingInput.length / 2) * 15); card.origX = parentPos.x - card.width / 2
+ ((i - pendingInput.length / 2) * 15);
card.origY = parentPos.y - card.height / 2; card.origY = parentPos.y - card.height / 2;
card.origOpacity = 0; card.origOpacity = 0;
card.destroyOnStop(); card.destroyOnStop();

View File

@ -19,7 +19,7 @@ Item {
Text { Text {
id: roundTxt id: roundTxt
anchors.right: parent.right anchors.right: parent.right
text: Backend.translate("#currentRoundNum").arg(roundNum) text: luatr("#currentRoundNum").arg(roundNum)
color: "#F0E5DA" color: "#F0E5DA"
font.pixelSize: 18 font.pixelSize: 18
font.family: fontLibian.name font.family: fontLibian.name

View File

@ -13,7 +13,7 @@ GraphicsBox {
property var result property var result
property int padding: 25 property int padding: 25
title.text: Backend.translate("Please click to move card") title.text: luatr("Please click to move card")
width: body.width + padding * 2 width: body.width + padding * 2
height: title.height + body.height + padding * 2 height: title.height + body.height + padding * 2
@ -65,7 +65,7 @@ GraphicsBox {
Text { Text {
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
anchors.centerIn: parent anchors.centerIn: parent
text: Backend.translate(modelData.subtype) text: luatr(modelData.subtype)
color: "#90765F" color: "#90765F"
font.family: fontLibian.name font.family: fontLibian.name
font.pixelSize: 16 font.pixelSize: 16
@ -81,7 +81,7 @@ GraphicsBox {
MetroButton { MetroButton {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
id: buttonConfirm id: buttonConfirm
text: Backend.translate("OK") text: luatr("OK")
width: 120 width: 120
height: 35 height: 35
enabled: false enabled: false
@ -131,7 +131,8 @@ GraphicsBox {
const index = cards.findIndex(data => item.cid === data.cid); const index = cards.findIndex(data => item.cid === data.cid);
result && (result.pos = cardsPosition[index]); result && (result.pos = cardsPosition[index]);
const cardPos = cardsPosition[index] === 0 ? (result ? 1 : 0) : (result ? 0 : 1); const cardPos = cardsPosition[index] === 0 ? (result ? 1 : 0)
: (result ? 0 : 1);
const curArea = areaRepeater.itemAt(cardPos); const curArea = areaRepeater.itemAt(cardPos);
const curBox = curArea.cardRepeater.itemAt(index); const curBox = curArea.cardRepeater.itemAt(index);
const curPos = mapFromItem(curArea, curBox.x, curBox.y); const curPos = mapFromItem(curArea, curBox.x, curBox.y);

View File

@ -179,6 +179,7 @@ Item {
Image { Image {
id: generalImage id: generalImage
width: deputyGeneral ? parent.width / 2 : parent.width width: deputyGeneral ? parent.width / 2 : parent.width
Behavior on width { NumberAnimation { duration: 100 } }
height: parent.height height: parent.height
smooth: true smooth: true
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
@ -187,7 +188,8 @@ Item {
return ""; return "";
} }
if (deputyGeneral) { if (deputyGeneral) {
return SkinBank.getGeneralExtraPic(general, "dual/") ?? SkinBank.getGeneralPicture(general); return SkinBank.getGeneralExtraPic(general, "dual/")
?? SkinBank.getGeneralPicture(general);
} else { } else {
return SkinBank.getGeneralPicture(general) return SkinBank.getGeneralPicture(general)
} }
@ -204,7 +206,8 @@ Item {
source: { source: {
const general = deputyGeneral; const general = deputyGeneral;
if (deputyGeneral != "") { if (deputyGeneral != "") {
return SkinBank.getGeneralExtraPic(general, "dual/") ?? SkinBank.getGeneralPicture(general); return SkinBank.getGeneralExtraPic(general, "dual/")
?? SkinBank.getGeneralPicture(general);
} else { } else {
return ""; return "";
} }
@ -231,7 +234,7 @@ Item {
color: "white" color: "white"
width: 24 width: 24
wrapMode: Text.WrapAnywhere wrapMode: Text.WrapAnywhere
text: Backend.translate(deputyGeneral) text: luatr(deputyGeneral)
style: Text.Outline style: Text.Outline
} }
} }
@ -247,16 +250,18 @@ Item {
} }
OpacityMask { OpacityMask {
id: photoMaskEffect
anchors.fill: photoMask anchors.fill: photoMask
source: generalImgItem source: generalImgItem
maskSource: photoMask maskSource: photoMask
} }
Colorize { Colorize {
anchors.fill: photoMask anchors.fill: photoMaskEffect
source: generalImgItem source: photoMaskEffect
saturation: 0 saturation: 0
visible: root.dead || root.surrendered opacity: (root.dead || root.surrendered) ? 1 : 0
Behavior on opacity { NumberAnimation { duration: 300 } }
} }
Rectangle { Rectangle {
@ -266,9 +271,10 @@ Item {
height: 222 height: 222
radius: 8 radius: 8
visible: root.drank > 0 // visible: root.drank > 0
color: "red" color: "red"
opacity: 0.4 + Math.log(root.drank) * 0.12 opacity: (root.drank <= 0 ? 0 : 0.4) + Math.log(root.drank) * 0.12
Behavior on opacity { NumberAnimation { duration: 300 } }
} }
ColumnLayout { ColumnLayout {
@ -279,7 +285,7 @@ Item {
GlowText { GlowText {
Layout.alignment: Qt.AlignCenter Layout.alignment: Qt.AlignCenter
text: Backend.translate("resting...") text: luatr("resting...")
font.family: fontLibian.name font.family: fontLibian.name
font.pixelSize: 40 font.pixelSize: 40
font.bold: true font.bold: true
@ -303,7 +309,7 @@ Item {
GlowText { GlowText {
Layout.alignment: Qt.AlignCenter Layout.alignment: Qt.AlignCenter
visible: root.rest > 0 && root.rest < 999 visible: root.rest > 0 && root.rest < 999
text: Backend.translate("rest round num") text: luatr("rest round num")
font.family: fontLibian.name font.family: fontLibian.name
font.pixelSize: 28 font.pixelSize: 28
color: "#F0E5D6" color: "#F0E5D6"
@ -333,11 +339,11 @@ Item {
style: Text.Outline style: Text.Outline
text: { text: {
if (totalGame === 0) { if (totalGame === 0) {
return Backend.translate("Newbie"); return luatr("Newbie");
} }
const winRate = (winGame / totalGame) * 100; const winRate = (winGame / totalGame) * 100;
const runRate = (runGame / totalGame) * 100; const runRate = (runGame / totalGame) * 100;
return Backend.translate("Win=%1\nRun=%2\nTotal=%3") return luatr("Win=%1\nRun=%2\nTotal=%3")
.arg(winRate.toFixed(2)) .arg(winRate.toFixed(2))
.arg(runRate.toFixed(2)) .arg(runRate.toFixed(2))
.arg(totalGame); .arg(totalGame);
@ -350,7 +356,8 @@ Item {
anchors.right: parent.right anchors.right: parent.right
anchors.bottomMargin: -8 anchors.bottomMargin: -8
anchors.rightMargin: 4 anchors.rightMargin: 4
source: SkinBank.PHOTO_DIR + (isOwner ? "owner" : (ready ? "ready" : "notready")) source: SkinBank.PHOTO_DIR +
(isOwner ? "owner" : (ready ? "ready" : "notready"))
visible: screenName != "" && !roomScene.isStarted visible: screenName != "" && !roomScene.isStarted
} }
@ -392,7 +399,8 @@ Item {
} }
function updatePileInfo(areaName) { function updatePileInfo(areaName) {
const data = JSON.parse(Backend.callLuaFunction("GetPile", [root.playerid, areaName])); if (areaName.startsWith('#')) return;
const data = lcall("GetPile", root.playerid, areaName);
if (data.length === 0) { if (data.length === 0) {
root.markArea.removeMark(areaName); root.markArea.removeMark(areaName);
} else { } else {
@ -434,10 +442,12 @@ Item {
// id: saveme // id: saveme
visible: (root.dead && !root.rest) || root.dying || root.surrendered visible: (root.dead && !root.rest) || root.dying || root.surrendered
source: { source: {
if (root.dead) { if (root.surrendered) {
return SkinBank.DEATH_DIR + "surrender";
} else if (root.dead) {
return SkinBank.getRoleDeathPic(root.role); return SkinBank.getRoleDeathPic(root.role);
} }
return SkinBank.DEATH_DIR + (root.surrendered ? "surrender" : "saveme") return SkinBank.DEATH_DIR + "saveme";
} }
anchors.centerIn: photoMask anchors.centerIn: photoMask
} }
@ -459,7 +469,14 @@ Item {
x: -6 x: -6
Text { Text {
text: (root.maxCard === root.hp || root.hp < 0 ) ? (root.handcards) : (root.handcards + "/" + (root.maxCard < 900 ? root.maxCard : "∞")) text: {
if (root.maxCard === root.hp || root.hp < 0) {
return root.handcards;
} else {
const maxCard = root.maxCard < 900 ? root.maxCard : "∞";
return root.handcards + "/" + maxCard;
}
}
font.family: fontLibian.name font.family: fontLibian.name
font.pixelSize: (root.maxCard === root.hp || root.hp < 0 ) ? 32 : 27 font.pixelSize: (root.maxCard === root.hp || root.hp < 0 ) ? 32 : 27
//font.weight: 30 //font.weight: 30
@ -471,10 +488,11 @@ Item {
} }
TapHandler { TapHandler {
enabled: (root.state != "candidate" || !root.selectable) && root.playerid !== Self.id enabled: (root.state != "candidate" || !root.selectable)
&& root.playerid !== Self.id
onTapped: { onTapped: {
const params = { name: "hand_card" }; const params = { name: "hand_card" };
let data = JSON.parse(Backend.callLuaFunction("GetPlayerHandcards", [root.playerid])); let data = lcall("GetPlayerHandcards", root.playerid);
data = data.filter((e) => e !== -1); data = data.filter((e) => e !== -1);
if (data.length === 0) if (data.length === 0)
return; return;
@ -531,7 +549,12 @@ Item {
anchors.topMargin: 2 anchors.topMargin: 2
font.pixelSize: 16 font.pixelSize: 16
text: (config.blockedUsers && config.blockedUsers.includes(screenName) ? Backend.translate("<Blocked> ") : "") + screenName text: {
let ret = screenName;
if (config.blockedUsers?.includes(screenName))
ret = luatr("<Blocked> ") + ret;
return ret;
}
glow.radius: 8 glow.radius: 8
} }
@ -548,7 +571,10 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.bottomMargin: -32 anchors.bottomMargin: -32
property var seatChr: ["一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二"] property var seatChr: [
"一", "二", "三", "四", "五", "六",
"七", "八", "九", "十", "十一", "十二",
]
font.family: fontLi2.name font.family: fontLi2.name
font.pixelSize: 32 font.pixelSize: 32
text: seatChr[seatNumber - 1] text: seatChr[seatNumber - 1]
@ -704,7 +730,7 @@ Item {
onGeneralChanged: { onGeneralChanged: {
if (!roomScene.isStarted) return; if (!roomScene.isStarted) return;
const text = Backend.translate(general); const text = luatr(general);
if (text.length > 6) { if (text.length > 6) {
generalName.text = ""; generalName.text = "";
longGeneralName.text = text; longGeneralName.text = text;
@ -712,8 +738,6 @@ Item {
generalName.text = text; generalName.text = text;
longGeneralName.text = ""; longGeneralName.text = "";
} }
// let data = JSON.parse(Backend.callLuaFunction("GetGeneralData", [general]));
// kingdom = data.kingdom;
} }
function chat(msg) { function chat(msg) {

View File

@ -2,13 +2,18 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Fk
import Fk.Pages import Fk.Pages
GraphicsBox { GraphicsBox {
id: root id: root
property string prompt property string prompt
title.text: prompt === "" ? (root.multiChoose ? Backend.translate("$ChooseCards").arg(root.min).arg(root.max) : Backend.translate("$ChooseCard")) : processPrompt(prompt) title.text: prompt === "" ?
(root.multiChoose ?
luatr("$ChooseCards").arg(root.min).arg(root.max)
: luatr("$ChooseCard"))
: Util.processPrompt(prompt)
// TODO: Adjust the UI design in case there are more than 7 cards // TODO: Adjust the UI design in case there are more than 7 cards
width: 70 + 700 width: 70 + 700
@ -50,7 +55,7 @@ GraphicsBox {
Text { Text {
color: "#E4D5A0" color: "#E4D5A0"
text: Backend.translate(areaName) text: luatr(areaName)
anchors.fill: parent anchors.fill: parent
wrapMode: Text.WrapAnywhere wrapMode: Text.WrapAnywhere
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
@ -95,26 +100,15 @@ GraphicsBox {
MetroButton { MetroButton {
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
text: Backend.translate("OK") text: luatr("OK")
visible: root.multiChoose visible: root.multiChoose
enabled: root.selected_ids.length <= root.max && root.selected_ids.length >= root.min enabled: root.selected_ids.length <= root.max
&& root.selected_ids.length >= root.min
onClicked: root.cardsSelected(root.selected_ids) onClicked: root.cardsSelected(root.selected_ids)
} }
onCardSelected: finished(); onCardSelected: finished();
function processPrompt(prompt) {
const data = prompt.split(":");
let raw = Backend.translate(data[0]);
const src = parseInt(data[1]);
const dest = parseInt(data[2]);
if (raw.match("%src")) raw = raw.replace(/%src/g, Backend.translate(getPhoto(src).general));
if (raw.match("%dest")) raw = raw.replace(/%dest/g, Backend.translate(getPhoto(dest).general));
if (raw.match("%arg2")) raw = raw.replace(/%arg2/g, Backend.translate(data[4]));
if (raw.match("%arg")) raw = raw.replace(/%arg/g, Backend.translate(data[3]));
return raw;
}
function findAreaModel(name) { function findAreaModel(name) {
let ret; let ret;
for (let i = 0; i < cardModel.count; i++) { for (let i = 0; i < cardModel.count; i++) {

View File

@ -7,7 +7,7 @@ import Fk.Pages
GraphicsBox { GraphicsBox {
id: root id: root
title.text: Backend.callLuaFunction("PoxiPrompt", [poxi_type, card_data, extra_data]) title.text: lcall("PoxiPrompt", poxi_type, card_data, extra_data)
// TODO: Adjust the UI design in case there are more than 7 cards // TODO: Adjust the UI design in case there are more than 7 cards
width: 70 + 700 width: 70 + 700
@ -50,7 +50,7 @@ GraphicsBox {
Text { Text {
color: "#E4D5A0" color: "#E4D5A0"
text: Backend.translate(areaName) text: luatr(areaName)
anchors.fill: parent anchors.fill: parent
wrapMode: Text.WrapAnywhere wrapMode: Text.WrapAnywhere
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
@ -71,12 +71,10 @@ GraphicsBox {
number: model.number || 0 number: model.number || 0
autoBack: false autoBack: false
known: model.cid !== -1 known: model.cid !== -1
selectable: { selectable: root.selected_ids.includes(model.cid) ||
return root.selected_ids.includes(model.cid) || JSON.parse(Backend.callLuaFunction( lcall("PoxiFilter", root.poxi_type, model.cid, root.selected_ids,
"PoxiFilter", root.card_data, root.extra_data);
[root.poxi_type, model.cid, root.selected_ids, root.card_data, root.extra_data]
));
}
onSelectedChanged: { onSelectedChanged: {
if (selected) { if (selected) {
chosenInBox = true; chosenInBox = true;
@ -102,20 +100,16 @@ GraphicsBox {
MetroButton { MetroButton {
width: 120 width: 120
height: 35 height: 35
text: Backend.translate("OK") text: luatr("OK")
enabled: { enabled: lcall("PoxiFeasible", root.poxi_type, root.selected_ids,
return JSON.parse(Backend.callLuaFunction( root.card_data, root.extra_data);
"PoxiFeasible",
[root.poxi_type, root.selected_ids, root.card_data, root.extra_data]
));
}
onClicked: root.cardsSelected(root.selected_ids) onClicked: root.cardsSelected(root.selected_ids)
} }
MetroButton { MetroButton {
width: 120 width: 120
height: 35 height: 35
text: Backend.translate("Cancel") text: luatr("Cancel")
visible: root.cancelable visible: root.cancelable
onClicked: root.cardsSelected([]) onClicked: root.cardsSelected([])
} }

View File

@ -112,10 +112,7 @@ Flickable {
return false; return false;
}; };
const data = JSON.parse(Backend.callLuaFunction( const data = lcall("GetSkillData", skill_name);
"GetSkillData",
[skill_name]
));
if (prelight) { if (prelight) {
if (!modelContains(prelight_skills, data)) if (!modelContains(prelight_skills, data))

View File

@ -28,12 +28,12 @@ Item {
Text { Text {
text: { text: {
let o = "$" + skillName + "_" + generalName + (index % 2 + 1); let o = "$" + skillName + "_" + generalName + (index % 2 + 1);
let p = Backend.translate(o); let p = luatr(o);
if (o !== p) { if (o !== p) {
return p; return p;
} }
o = "$" + skillName + (index % 2 + 1); o = "$" + skillName + (index % 2 + 1);
p = Backend.translate(o); p = luatr(o);
if (o === p) { if (o === p) {
return "Ultimate Skill Invoked!"; return "Ultimate Skill Invoked!";
} }
@ -59,12 +59,12 @@ Item {
Text { Text {
text: { text: {
let o = "$" + skillName + "_" + generalName + ((index + 1) % 2 + 1); let o = "$" + skillName + "_" + generalName + ((index + 1) % 2 + 1);
let p = Backend.translate(o); let p = luatr(o);
if (o !== p) { if (o !== p) {
return p; return p;
} }
o = "$" + skillName + ((index + 1) % 2 + 1); o = "$" + skillName + ((index + 1) % 2 + 1);
p = Backend.translate(o); p = luatr(o);
if (o === p) { if (o === p) {
return "Ultimate Skill Invoked!"; return "Ultimate Skill Invoked!";
} }
@ -90,7 +90,7 @@ Item {
Text { Text {
topPadding: 5 topPadding: 5
id: skill id: skill
text: Backend.translate(skillName) text: luatr(skillName)
font.family: fontLi2.name font.family: fontLi2.name
font.pixelSize: 40 font.pixelSize: 40
x: root.width / 2 + 100 x: root.width / 2 + 100

View File

@ -13,7 +13,7 @@ ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height + 4 Layout.preferredHeight: childrenRect.height + 4
text: Backend.translate(extra_data.name) text: luatr(extra_data.name)
} }
GridView { GridView {

View File

@ -13,7 +13,7 @@ ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height + 4 Layout.preferredHeight: childrenRect.height + 4
text: Backend.translate(extra_data.name) text: luatr(extra_data.name)
} }
GridView { GridView {
@ -32,7 +32,7 @@ ColumnLayout {
Component.onCompleted: { Component.onCompleted: {
let data = {} let data = {}
if (extra_data.ids) { if (extra_data.ids) {
data = JSON.parse(Backend.callLuaFunction("GetCardData", [modelData])); data = lcall("GetCardData", modelData);
} else { } else {
data.cid = 0; data.cid = 0;
data.name = modelData; data.name = modelData;

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick import QtQuick
import Fk
import Fk.Pages import Fk.Pages
MetroButton { MetroButton {
@ -12,34 +13,21 @@ MetroButton {
property string answer: default_choice property string answer: default_choice
property bool detailed: false property bool detailed: false
function processPrompt(prompt) { text: Util.processPrompt(answer)
const data = prompt.split(":");
let raw = Backend.translate(data[0]);
const src = parseInt(data[1]);
const dest = parseInt(data[2]);
if (raw.match("%src")) raw = raw.replace(/%src/g, Backend.translate(getPhoto(src).general));
if (raw.match("%dest")) raw = raw.replace(/%dest/g, Backend.translate(getPhoto(dest).general));
if (raw.match("%arg2")) raw = raw.replace(/%arg2/g, Backend.translate(data[4]));
if (raw.match("%arg")) raw = raw.replace(/%arg/g, Backend.translate(data[3]));
return raw;
}
text: processPrompt(answer)
onAnswerChanged: { onAnswerChanged: {
if (!answer) return; if (!answer) return;
Backend.callLuaFunction( lcall("SetInteractionDataOfSkill", skill, JSON.stringify(answer));
"SetInteractionDataOfSkill",
[skill, JSON.stringify(answer)]
);
roomScene.dashboard.startPending(skill); roomScene.dashboard.startPending(skill);
} }
onClicked: { onClicked: {
if (detailed) { if (detailed) {
roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/DetailedChoiceBox.qml"); roomScene.popupBox.sourceComponent =
Qt.createComponent("../RoomElement/DetailedChoiceBox.qml");
} else { } else {
roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/ChoiceBox.qml"); roomScene.popupBox.sourceComponent =
Qt.createComponent("../RoomElement/ChoiceBox.qml");
} }
const box = roomScene.popupBox.item; const box = roomScene.popupBox.item;
box.options = choices; box.options = choices;

View File

@ -10,11 +10,7 @@ SpinBox {
// from, to // from, to
onValueChanged: { onValueChanged: {
Backend.callLuaFunction( lcall("SetInteractionDataOfSkill", skill, JSON.stringify(answer));
"SetInteractionDataOfSkill",
[skill, JSON.stringify(answer)]
);
roomScene.dashboard.startPending(skill); roomScene.dashboard.startPending(skill);
} }
} }

View File

@ -89,8 +89,14 @@ Rectangle {
id: textAni id: textAni
running: false running: false
loops: Animation.Infinite loops: Animation.Infinite
NumberAnimation { from: 0; to: 1; duration: 1600; easing.type: Easing.InOutQuad; } NumberAnimation {
NumberAnimation { from: 1; to: 0; duration: 1600; easing.type: Easing.InOutQuad; } from: 0; to: 1; duration: 1600
easing.type: Easing.InOutQuad
}
NumberAnimation {
from: 1; to: 0; duration: 1600
easing.type: Easing.InOutQuad
}
} }
} }

View File

@ -15,7 +15,8 @@ Rectangle {
property real time: defaultTime property real time: defaultTime
readonly property real fadeTime: 300 readonly property real fadeTime: 300
anchors.horizontalCenter: parent != null ? parent.horizontalCenter : undefined anchors.horizontalCenter: parent != null ? parent.horizontalCenter
: undefined
height: message.height + 20 height: message.height + 20
width: message.width + 40 width: message.width + 40
radius: 16 radius: 16

View File

@ -2,8 +2,8 @@
import QtQuick import QtQuick
// copy from https://gist.github.com/jonmcclung/bae669101d17b103e94790341301c129 // https://gist.github.com/jonmcclung/bae669101d17b103e94790341301c129
// and modified some code // modified some code
ListView { ListView {
function show(text, duration) { function show(text, duration) {
if (duration === undefined) { if (duration === undefined) {

View File

@ -45,7 +45,8 @@ Window {
StackView { StackView {
id: mainStack id: mainStack
visible: !mainWindow.busy visible: !mainWindow.busy
// If error occurs during loading initialItem, the program will fall into "polish()" loop // If error occurs during loading initialItem
// the program will fall into "polish()" loop
// initialItem: init // initialItem: init
anchors.fill: parent anchors.fill: parent
} }
@ -159,7 +160,10 @@ Window {
return; return;
} }
if (mainWindow.is_pending && command !== "ChangeSelf") { if (mainWindow.is_pending && command !== "ChangeSelf") {
mainWindow.pending_message.push({ command: command, jsonData: jsonData }); mainWindow.pending_message.push({
command: command,
jsonData: jsonData,
});
} else { } else {
if (command === "StartChangeSelf") { if (command === "StartChangeSelf") {
mainWindow.is_pending = true; mainWindow.is_pending = true;
@ -254,4 +258,27 @@ Window {
exitMessageDialog.open(); exitMessageDialog.open();
} }
} }
// fake global functions
function lcall(funcName, ...params) {
const ret = Backend.callLuaFunction(funcName, [...params]);
try {
return JSON.parse(ret);
} catch (e) {
return ret;
}
}
function leval(lua) {
const ret = Backend.evalLuaExp(`return json.encode(${lua})`);
try {
return JSON.parse(ret);
} catch (e) {
return ret;
}
}
function luatr(src) {
return Backend.translate(src);
}
} }

View File

@ -1,3 +1,3 @@
module Fk module Fk
SkinBank 1.0 skin-bank.js SkinBank 1.0 skin-bank.js
Utility 1.0 util.js Util 1.0 util.js

View File

@ -31,18 +31,20 @@ const searchPkgResource = function(path, name, suffix) {
} }
function getGeneralExtraPic(name, extra) { function getGeneralExtraPic(name, extra) {
const data = JSON.parse(Backend.callLuaFunction("GetGeneralData", [name])); const data = lcall("GetGeneralData", name);
const extension = data.extension; const extension = data.extension;
const path = AppPath + "/packages/" + extension + "/image/generals/" + extra + name + ".jpg"; const path = AppPath + "/packages/" + extension + "/image/generals/"
+ extra + name + ".jpg";
if (Backend.exists(path)) { if (Backend.exists(path)) {
return path; return path;
} }
} }
function getGeneralPicture(name) { function getGeneralPicture(name) {
const data = JSON.parse(Backend.callLuaFunction("GetGeneralData", [name])); const data = lcall("GetGeneralData", name);
const extension = data.extension; const extension = data.extension;
const path = AppPath + "/packages/" + extension + "/image/generals/" + name + ".jpg"; const path = AppPath + "/packages/" + extension + "/image/generals/"
+ name + ".jpg";
if (Backend.exists(path)) { if (Backend.exists(path)) {
return path; return path;
} }
@ -54,14 +56,15 @@ function getCardPicture(cidOrName) {
let name = "unknown"; let name = "unknown";
if (typeof cidOrName === 'string') { if (typeof cidOrName === 'string') {
name = cidOrName; name = cidOrName;
extension = Backend.callLuaFunction("GetCardExtensionByName", [cidOrName]); extension = lcall("GetCardExtensionByName", cidOrName);
} else { } else {
const data = JSON.parse(Backend.callLuaFunction("GetCardData", [cid])); const data = lcall("GetCardData", cid);
extension = data.extension; extension = data.extension;
name = data.name; name = data.name;
} }
let path = AppPath + "/packages/" + extension + "/image/card/" + name + ".png"; let path = AppPath + "/packages/" + extension + "/image/card/"
+ name + ".png";
if (Backend.exists(path)) { if (Backend.exists(path)) {
return path; return path;
} else { } else {
@ -72,9 +75,10 @@ function getCardPicture(cidOrName) {
} }
function getDelayedTrickPicture(name) { function getDelayedTrickPicture(name) {
const extension = Backend.callLuaFunction("GetCardExtensionByName", [name]); const extension = lcall("GetCardExtensionByName", name);
let path = AppPath + "/packages/" + extension + "/image/card/delayedTrick/" + name + ".png"; let path = AppPath + "/packages/" + extension + "/image/card/delayedTrick/"
+ name + ".png";
if (Backend.exists(path)) { if (Backend.exists(path)) {
return path; return path;
} else { } else {
@ -86,10 +90,11 @@ function getDelayedTrickPicture(name) {
function getEquipIcon(cid, icon) { function getEquipIcon(cid, icon) {
const data = JSON.parse(Backend.callLuaFunction("GetCardData", [cid])); const data = lcall("GetCardData", cid);
const extension = data.extension; const extension = data.extension;
const name = icon || data.name; const name = icon || data.name;
let path = AppPath + "/packages/" + extension + "/image/card/equipIcon/" + name + ".png"; let path = AppPath + "/packages/" + extension + "/image/card/equipIcon/"
+ name + ".png";
if (Backend.exists(path)) { if (Backend.exists(path)) {
return path; return path;
} else { } else {

View File

@ -1,7 +1,5 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
.pragma library
function convertNumber(number) { function convertNumber(number) {
if (number === 1) if (number === 1)
return "A"; return "A";
@ -14,8 +12,20 @@ function convertNumber(number) {
return ""; return "";
} }
Array.prototype.contains = function(element) { function processPrompt(prompt) {
return this.indexOf(element) != -1; const data = prompt.split(":");
let raw = luatr(data[0]);
const src = parseInt(data[1]);
const dest = parseInt(data[2]);
if (raw.match("%src"))
raw = raw.replace(/%src/g, luatr(getPhoto(src).general));
if (raw.match("%dest"))
raw = raw.replace(/%dest/g, luatr(getPhoto(dest).general));
if (raw.match("%arg2"))
raw = raw.replace(/%arg2/g, luatr(data[4]));
if (raw.match("%arg"))
raw = raw.replace(/%arg/g, luatr(data[3]));
return raw;
} }
Array.prototype.prepend = function() { Array.prototype.prepend = function() {

View File

@ -14,21 +14,11 @@ ___
新月杀FreeKill是一款开源的三国杀游戏但其目的不在于补完官方所有武将而是着力于提供一个最适合DIY的框架。 新月杀FreeKill是一款开源的三国杀游戏但其目的不在于补完官方所有武将而是着力于提供一个最适合DIY的框架。
___
## 项目文档 ## 项目文档
[新月杀文档](https://qsgs-fans.github.io/FreeKill/usr/index.html) https://fkbook-all-in-one.readthedocs.io/
### 依赖的库
以下是新月杀运行所必不可少的依赖库:
* [![](https://img.shields.io/badge/qt6-50D160?style=for-the-badge&logo=qt&logoColor=white)](https://www.qt.io)
* [![](https://img.shields.io/badge/lua5.4-030380?style=for-the-badge&logo=lua)](https://www.lua.org)
* [![](https://img.shields.io/badge/sqlite3-7ABEEA?style=for-the-badge&logo=sqlite)](https://www.sqlite.org)
* [![](https://img.shields.io/badge/libgit2-FFFFFF?style=for-the-badge&logo=git)](https://www.libgit2.org)
* [![](https://img.shields.io/badge/openssl-721412?style=for-the-badge&logo=openssl)](https://www.openssl.org)
新月杀在编译过程中需要用到cmake, flex, bison, swig。
___ ___
@ -46,7 +36,7 @@ ___
## 如何构建 ## 如何构建
关于如何从头构建新月杀,详见[编译教程](https://qsgs-fans.github.io/FreeKill/inner/01-compile.html)。 https://fkbook-all-in-one.readthedocs.io/zh-cn/latest/develop/02-env.html
___ ___

View File

@ -3,8 +3,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.notify.FreeKill" package="org.notify.FreeKill"
android:installLocation="preferExternal" android:installLocation="preferExternal"
android:versionCode="402" android:versionCode="409"
android:versionName="0.4.2"> android:versionName="0.4.9">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

BIN
audio/system/gamestart.mp3 Normal file

Binary file not shown.

BIN
audio/system/ice_damage.mp3 Normal file

Binary file not shown.

Binary file not shown.

BIN
audio/system/ready.mp3 Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -107,6 +107,10 @@
<source>[%1/%2] upgrading package '%3'</source> <source>[%1/%2] upgrading package '%3'</source>
<translation>[%1/%2] '%3'</translation> <translation>[%1/%2] '%3'</translation>
</message> </message>
<message>
<source>packages/%1: some error occured.</source>
<translation> %1 </translation>
</message>
</context> </context>
<context> <context>

View File

@ -1,16 +1,14 @@
-- SPDX-License-Identifier: GPL-3.0-or-later -- SPDX-License-Identifier: GPL-3.0-or-later
---@class Client ---@class Client : AbstractRoom
---@field public client fk.Client ---@field public client fk.Client
---@field public players ClientPlayer[] @ 所有参战玩家的数组 ---@field public players ClientPlayer[] @ 所有参战玩家的数组
---@field public alive_players ClientPlayer[] @ 所有存活玩家的数组 ---@field public alive_players ClientPlayer[] @ 所有存活玩家的数组
---@field public observers ClientPlayer[] @ 观察者的数组 ---@field public observers ClientPlayer[] @ 观察者的数组
---@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 banners table<string, any> @ 左上角显示的东西
---@field public observing boolean ---@field public observing boolean
Client = class('Client') Client = AbstractRoom:subclass('Client')
-- load client classes -- load client classes
ClientPlayer = require "client.clientplayer" ClientPlayer = require "client.clientplayer"
@ -26,6 +24,7 @@ local pattern_refresh_commands = {
} }
function Client:initialize() function Client:initialize()
AbstractRoom.initialize(self)
self.client = fk.ClientInstance self.client = fk.ClientInstance
self.notifyUI = function(self, command, jsonData) self.notifyUI = function(self, command, jsonData)
fk.Backend:emitNotifyUI(command, jsonData) fk.Backend:emitNotifyUI(command, jsonData)
@ -49,21 +48,8 @@ function Client:initialize()
end end
end end
self.players = {} -- ClientPlayer[]
self.alive_players = {}
self.observers = {}
self.discard_pile = {} self.discard_pile = {}
self.status_skills = {}
for class, skills in pairs(Fk.global_status_skill) do
self.status_skills[class] = {table.unpack(skills)}
end
self.banners = {}
self.skill_costs = {}
self.card_marks = {}
self.filtered_cards = {}
self.printed_cards = {}
self.disabled_packs = {} self.disabled_packs = {}
self.disabled_generals = {} self.disabled_generals = {}
@ -71,7 +57,7 @@ function Client:initialize()
end end
---@param id integer ---@param id integer
---@return ClientPlayer ---@return ClientPlayer?
function Client:getPlayerById(id) function Client:getPlayerById(id)
if id == Self.id then return Self end if id == Self.id then return Self end
for _, p in ipairs(self.players) do for _, p in ipairs(self.players) do
@ -225,7 +211,11 @@ end
---@param msg LogMessage ---@param msg LogMessage
function Client:appendLog(msg) function Client:appendLog(msg)
self:notifyUI("GameLog", parseMsg(msg)) local text = parseMsg(msg)
self:notifyUI("GameLog", text)
if msg.toast then
self:notifyUI("ShowToast", text)
end
end end
---@param msg LogMessage ---@param msg LogMessage
@ -237,15 +227,6 @@ 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]);
@ -401,6 +382,7 @@ fk.client_callback["AskForCardChosen"] = function(jsonData)
judge = {} judge = {}
end end
ui_data = { ui_data = {
_id = id,
_reason = reason, _reason = reason,
card_data = {}, card_data = {},
_prompt = prompt, _prompt = prompt,
@ -409,6 +391,7 @@ fk.client_callback["AskForCardChosen"] = function(jsonData)
if #equip ~= 0 then table.insert(ui_data.card_data, { "$Equip", equip }) end if #equip ~= 0 then table.insert(ui_data.card_data, { "$Equip", equip }) end
if #judge ~= 0 then table.insert(ui_data.card_data, { "$Judge", judge }) end if #judge ~= 0 then table.insert(ui_data.card_data, { "$Judge", judge }) end
else else
ui_data._id = id
ui_data._reason = reason ui_data._reason = reason
ui_data._prompt = prompt ui_data._prompt = prompt
end end
@ -436,6 +419,7 @@ fk.client_callback["AskForCardsChosen"] = function(jsonData)
judge = {} judge = {}
end end
ui_data = { ui_data = {
_id = id,
_min = min, _min = min,
_max = max, _max = max,
_reason = reason, _reason = reason,
@ -446,6 +430,7 @@ fk.client_callback["AskForCardsChosen"] = function(jsonData)
if #equip ~= 0 then table.insert(ui_data.card_data, { "$Equip", equip }) end if #equip ~= 0 then table.insert(ui_data.card_data, { "$Equip", equip }) end
if #judge ~= 0 then table.insert(ui_data.card_data, { "$Judge", judge }) end if #judge ~= 0 then table.insert(ui_data.card_data, { "$Judge", judge }) end
else else
ui_data._id = id
ui_data._min = min ui_data._min = min
ui_data._max = max ui_data._max = max
ui_data._reason = reason ui_data._reason = reason
@ -469,6 +454,7 @@ local function separateMoves(moves)
moveReason = move.moveReason, moveReason = move.moveReason,
specialName = move.specialName, specialName = move.specialName,
fromSpecialName = info.fromSpecialName, fromSpecialName = info.fromSpecialName,
proposer = move.proposer,
}) })
end end
end end
@ -480,9 +466,9 @@ local function mergeMoves(moves)
local ret = {} local ret = {}
local temp = {} local temp = {}
for _, move in ipairs(moves) do for _, move in ipairs(moves) do
local info = string.format("%q,%q,%q,%q,%s,%s", local info = string.format("%q,%q,%q,%q,%s,%s,%q",
move.from, move.to, move.fromArea, move.toArea, move.from, move.to, move.fromArea, move.toArea,
move.specialName, move.fromSpecialName) move.specialName, move.fromSpecialName, move.proposer)
if temp[info] == nil then if temp[info] == nil then
temp[info] = { temp[info] = {
ids = {}, ids = {},
@ -493,6 +479,7 @@ local function mergeMoves(moves)
moveReason = move.moveReason, moveReason = move.moveReason,
specialName = move.specialName, specialName = move.specialName,
fromSpecialName = move.fromSpecialName, fromSpecialName = move.fromSpecialName,
proposer = move.proposer,
} }
end end
table.insert(temp[info].ids, move.ids[1]) table.insert(temp[info].ids, move.ids[1])
@ -504,12 +491,96 @@ local function mergeMoves(moves)
end end
local function sendMoveCardLog(move) local function sendMoveCardLog(move)
local client = ClientInstance local client = ClientInstance ---@class Client
if #move.ids == 0 then return end if #move.ids == 0 then return end
local hidden = table.contains(move.ids, -1) local hidden = table.contains(move.ids, -1)
local msgtype local msgtype
if move.from and move.toArea == Card.DrawPile then if move.toArea == Card.PlayerHand then
if move.fromArea == Card.PlayerSpecial then
client:appendLog{
type = "$GetCardsFromPile",
from = move.to,
arg = move.fromSpecialName,
arg2 = #move.ids,
card = move.ids,
}
elseif move.fromArea == Card.DrawPile then
client:appendLog{
type = "$DrawCards",
from = move.to,
card = move.ids,
arg = #move.ids,
}
elseif move.fromArea == Card.Processing then
client:appendLog{
type = "$GotCardBack",
from = move.to,
card = move.ids,
arg = #move.ids,
}
elseif move.fromArea == Card.DiscardPile then
client:appendLog{
type = "$RecycleCard",
from = move.to,
card = move.ids,
arg = #move.ids,
}
elseif move.from then
client:appendLog{
type = "$MoveCards",
from = move.from,
to = { move.to },
arg = #move.ids,
card = move.ids,
}
else
client:appendLog{
type = "$PreyCardsFromPile",
from = move.to,
card = move.ids,
arg = #move.ids,
}
end
elseif move.toArea == Card.PlayerEquip then
client:appendLog{
type = "$InstallEquip",
from = move.to,
card = move.ids,
}
elseif move.toArea == Card.PlayerJudge then
if move.from ~= move.to and move.fromArea == Card.PlayerJudge then
client:appendLog{
type = "$LightningMove",
from = move.from,
to = { move.to },
card = move.ids,
}
elseif move.from then
client:appendLog{
type = "$PasteCard",
from = move.from,
to = { move.to },
card = move.ids,
}
end
elseif move.toArea == Card.PlayerSpecial then
client:appendLog{
type = "$AddToPile",
arg = move.specialName,
arg2 = #move.ids,
from = move.to,
card = move.ids,
}
elseif move.fromArea == Card.PlayerEquip then
client:appendLog{
type = "$UninstallEquip",
from = move.from,
card = move.ids,
}
-- elseif move.toArea == Card.Processing then
-- nop
elseif move.from and move.toArea == Card.DrawPile then
msgtype = hidden and "$PutCard" or "$PutKnownCard" msgtype = hidden and "$PutCard" or "$PutKnownCard"
client:appendLog{ client:appendLog{
type = msgtype, type = msgtype,
@ -521,85 +592,37 @@ local function sendMoveCardLog(move)
type = "$$PutCard", type = "$$PutCard",
from = move.from, from = move.from,
}) })
elseif move.toArea == Card.PlayerSpecial then elseif move.toArea == Card.DiscardPile then
msgtype = hidden and "$RemoveCardFromGame" or "$AddToPile" if move.moveReason == fk.ReasonDiscard then
client:appendLog{ if move.proposer and move.proposer ~= move.from then
type = msgtype, client:appendLog{
arg = move.specialName, type = "$DiscardOther",
arg2 = #move.ids, from = move.from,
card = move.ids, to = {move.proposer},
} card = move.ids,
elseif move.fromArea == Card.PlayerSpecial and move.to then arg = #move.ids,
client:appendLog{ }
type = "$GetCardsFromPile", else
from = move.to, client:appendLog{
arg = move.fromSpecialName, type = "$DiscardCards",
arg2 = #move.ids, from = move.from,
card = move.ids, card = move.ids,
} arg = #move.ids,
elseif move.moveReason == fk.ReasonDraw then }
client:appendLog{ end
type = "$DrawCards", elseif move.moveReason == fk.ReasonPutIntoDiscardPile then
from = move.to,
card = move.ids,
arg = #move.ids,
}
elseif (move.fromArea == Card.DrawPile or move.fromArea == Card.DiscardPile)
and move.moveReason == fk.ReasonPrey then
client:appendLog{
type = "$PreyCardsFromPile",
from = move.to,
card = move.ids,
arg = #move.ids,
}
elseif (move.fromArea == Card.Processing or move.fromArea == Card.PlayerJudge)
and move.toArea == Card.PlayerHand then
client:appendLog{
type = "$GotCardBack",
from = move.to,
card = move.ids,
arg = #move.ids,
}
elseif move.fromArea == Card.DiscardPile and move.toArea == Card.PlayerHand then
client:appendLog{
type = "$RecycleCard",
from = move.to,
card = move.ids,
arg = #move.ids,
}
elseif move.from and move.fromArea ~= Card.PlayerJudge and
move.toArea ~= Card.PlayerJudge and move.to and move.from ~= move.to then
client:appendLog{
type = "$MoveCards",
from = move.from,
to = { move.to },
arg = #move.ids,
card = move.ids,
}
elseif move.from and move.to and move.toArea == Card.PlayerJudge then
if move.fromArea == Card.PlayerJudge and move.from ~= move.to then
msgtype = "$LightningMove"
elseif move.fromArea ~= Card.PlayerJudge then
msgtype = "$PasteCard"
end
if msgtype then
client:appendLog{ client:appendLog{
type = msgtype, type = "$PutToDiscard",
from = move.from,
to = { move.to },
card = move.ids, card = move.ids,
arg = #move.ids,
} }
end end
-- elseif move.toArea == Card.Void then
-- nop
end end
-- TODO ... -- TODO: footnote
if move.moveReason == fk.ReasonDiscard then if move.moveReason == fk.ReasonDiscard then
client:appendLog{
type = "$DiscardCards",
from = move.from,
card = move.ids,
arg = #move.ids,
}
client:setCardNote(move.ids, { client:setCardNote(move.ids, {
type = "$$DiscardCards", type = "$$DiscardCards",
from = move.from from = move.from
@ -753,9 +776,7 @@ fk.client_callback["AskForUseActiveSkill"] = function(jsonData)
local data = json.decode(jsonData) local data = json.decode(jsonData)
local skill = Fk.skills[data[1]] local skill = Fk.skills[data[1]]
local extra_data = data[4] local extra_data = data[4]
for k, v in pairs(extra_data) do skill._extra_data = extra_data
skill[k] = v
end
Fk.currentResponseReason = extra_data.skillName Fk.currentResponseReason = extra_data.skillName
ClientInstance:notifyUI("AskForUseActiveSkill", jsonData) ClientInstance:notifyUI("AskForUseActiveSkill", jsonData)
@ -775,9 +796,19 @@ fk.client_callback["SetPlayerMark"] = 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)
local player, mark, value = data[1], data[2], data[3] local player, mark, value = data[1], data[2], data[3]
ClientInstance:getPlayerById(player):setMark(mark, value) local p = ClientInstance:getPlayerById(player)
p:setMark(mark, value)
if string.sub(mark, 1, 1) == "@" then if string.sub(mark, 1, 1) == "@" then
if mark:startsWith("@[") and mark:find(']') then
local close = mark:find(']')
local mtype = mark:sub(3, close - 1)
local spec = Fk.qml_marks[mtype]
if spec then
local text = spec.how_to_show(mark, value, p)
if text == "#hidden" then return end
end
end
ClientInstance:notifyUI("SetPlayerMark", jsonData) ClientInstance:notifyUI("SetPlayerMark", jsonData)
end end
end end

View File

@ -127,6 +127,14 @@ function GetCardExtensionByName(cardName)
return card and card.package.extensionName or "" return card and card.package.extensionName or ""
end end
function GetAllMods()
return json.encode(Fk.extensions)
end
function GetAllModNames()
return json.encode(Fk.extension_names)
end
function GetAllGeneralPack() function GetAllGeneralPack()
local ret = {} local ret = {}
for _, name in ipairs(Fk.package_names) do for _, name in ipairs(Fk.package_names) do
@ -138,6 +146,7 @@ function GetAllGeneralPack()
end end
function GetGenerals(pack_name) function GetGenerals(pack_name)
if not Fk.packages[pack_name] then return "{}" end
local ret = {} local ret = {}
for _, g in ipairs(Fk.packages[pack_name].generals) do for _, g in ipairs(Fk.packages[pack_name].generals) do
if not g.total_hidden then if not g.total_hidden then
@ -251,8 +260,10 @@ end
---@param card string | integer ---@param card string | integer
---@param player integer ---@param player integer
function CanUseCard(card, player) ---@param extra_data_str string
function CanUseCard(card, player, extra_data_str)
local c ---@type Card local c ---@type Card
local extra_data = extra_data_str == "" and nil or json.decode(extra_data_str)
if type(card) == "number" then if type(card) == "number" then
c = Fk:getCardById(card) c = Fk:getCardById(card)
else else
@ -271,13 +282,13 @@ function CanUseCard(card, player)
end end
player = ClientInstance:getPlayerById(player) player = ClientInstance:getPlayerById(player)
local ret = c.skill:canUse(player, c) local ret = c.skill:canUse(player, c, extra_data)
ret = ret and not player:prohibitUse(c) ret = ret and not player:prohibitUse(c)
if ret then if ret then
local min_target = c.skill:getMinTargetNum() local min_target = c.skill:getMinTargetNum()
if min_target > 0 then if min_target > 0 then
for _, p in ipairs(ClientInstance.players) do for _, p in ipairs(ClientInstance.players) do
if c.skill:targetFilter(p.id, {}, {}, c) then if c.skill:targetFilter(p.id, {}, {}, c, extra_data) then
return "true" return "true"
end end
end end
@ -311,7 +322,9 @@ end
---@param card string | integer ---@param card string | integer
---@param to_select integer @ id of the target ---@param to_select integer @ id of the target
---@param selected integer[] @ ids of selected targets ---@param selected integer[] @ ids of selected targets
function CanUseCardToTarget(card, to_select, selected) ---@param extra_data_str string @ extra data
function CanUseCardToTarget(card, to_select, selected, extra_data_str)
local extra_data = extra_data_str == "" and nil or json.decode(extra_data_str)
if ClientInstance:getPlayerById(to_select).dead then if ClientInstance:getPlayerById(to_select).dead then
return "false" return "false"
end end
@ -322,10 +335,10 @@ function CanUseCardToTarget(card, to_select, selected)
selected_cards = {card} selected_cards = {card}
else else
local t = json.decode(card) local t = json.decode(card)
return ActiveTargetFilter(t.skill, to_select, selected, t.subcards) return ActiveTargetFilter(t.skill, to_select, selected, t.subcards, extra_data)
end end
local ret = c.skill:targetFilter(to_select, selected, selected_cards, c) local ret = c.skill:targetFilter(to_select, selected, selected_cards, c, extra_data)
ret = ret and not Self:isProhibited(Fk:currentRoom():getPlayerById(to_select), c) ret = ret and not Self:isProhibited(Fk:currentRoom():getPlayerById(to_select), c)
return json.encode(ret) return json.encode(ret)
end end
@ -392,12 +405,13 @@ function GetSkillData(skill_name)
} }
end end
function ActiveCanUse(skill_name) function ActiveCanUse(skill_name, extra_data_str)
local extra_data = extra_data_str == "" and nil or json.decode(extra_data_str)
local skill = Fk.skills[skill_name] local skill = Fk.skills[skill_name]
local ret = false local ret = false
if skill then if skill then
if skill:isInstanceOf(ActiveSkill) then if skill:isInstanceOf(ActiveSkill) then
ret = skill:canUse(Self) ret = skill:canUse(Self, extra_data)
elseif skill:isInstanceOf(ViewAsSkill) then elseif skill:isInstanceOf(ViewAsSkill) then
ret = skill:enabledAtPlay(Self) ret = skill:enabledAtPlay(Self)
if ret then if ret then
@ -414,7 +428,7 @@ function ActiveCanUse(skill_name)
for _, n in ipairs(cnames) do for _, n in ipairs(cnames) do
local c = Fk:cloneCard(n) local c = Fk:cloneCard(n)
c.skillName = skill_name c.skillName = skill_name
ret = c.skill:canUse(Self, c) ret = c.skill:canUse(Self, c, extra_data)
if ret then break end if ret then break end
end end
end end
@ -449,7 +463,7 @@ function ActiveCardFilter(skill_name, to_select, selected, selected_targets)
return json.encode(ret) return json.encode(ret)
end end
function ActiveTargetFilter(skill_name, to_select, selected, selected_cards) function ActiveTargetFilter(skill_name, to_select, selected, selected_cards, extra_data)
local skill = Fk.skills[skill_name] local skill = Fk.skills[skill_name]
local ret = false local ret = false
if skill then if skill then
@ -458,7 +472,7 @@ function ActiveTargetFilter(skill_name, to_select, selected, selected_cards)
elseif skill:isInstanceOf(ViewAsSkill) then elseif skill:isInstanceOf(ViewAsSkill) then
local card = skill:viewAs(selected_cards) local card = skill:viewAs(selected_cards)
if card then if card then
ret = card.skill:targetFilter(to_select, selected, selected_cards, card) ret = card.skill:targetFilter(to_select, selected, selected_cards, card, extra_data)
ret = ret and not Self:isProhibited(Fk:currentRoom():getPlayerById(to_select), card) ret = ret and not Self:isProhibited(Fk:currentRoom():getPlayerById(to_select), card)
end end
end end
@ -572,7 +586,17 @@ end
function GetExpandPileOfSkill(skillName) function GetExpandPileOfSkill(skillName)
local skill = Fk.skills[skillName] local skill = Fk.skills[skillName]
return skill and (skill.expand_pile or "") or "" if not skill then return "" end
local e = skill.expand_pile
if type(e) == "function" then
e = e(skill)
end
if type(e) == "table" then
return json.encode(e)
else
return e or ""
end
end end
function GetGameModes() function GetGameModes()
@ -722,7 +746,7 @@ function PoxiPrompt(poxi_type, data, extra_data)
local poxi = Fk.poxi_methods[poxi_type] local poxi = Fk.poxi_methods[poxi_type]
if not poxi or not poxi.prompt then return "" end if not poxi or not poxi.prompt then return "" end
if type(poxi.prompt) == "string" then return Fk:translate(poxi.prompt) end if type(poxi.prompt) == "string" then return Fk:translate(poxi.prompt) end
return poxi.prompt(data, extra_data) return Fk:translate(poxi.prompt(data, extra_data))
end end
function PoxiFilter(poxi_type, to_select, selected, data, extra_data) function PoxiFilter(poxi_type, to_select, selected, data, extra_data)
@ -748,6 +772,15 @@ function GetQmlMark(mtype, name, value, p)
} }
end end
function GetMiniGame(gtype, p, data)
local spec = Fk.mini_games[gtype]
p = ClientInstance:getPlayerById(p)
data = json.decode(data)
return json.encode {
qml_path = type(spec.qml_path) == "function" and spec.qml_path(p, data) or spec.qml_path,
}
end
function YuqiPrompt(yuqi_type, data, extra_data) function YuqiPrompt(yuqi_type, data, extra_data)
local yuqi = Fk.yuqi_methods[yuqi_type] local yuqi = Fk.yuqi_methods[yuqi_type]
if not yuqi or not yuqi.prompt then return "" end if not yuqi or not yuqi.prompt then return "" end

View File

@ -134,8 +134,17 @@ Fk:loadTranslationTable({
-- ["Quit"] = "退出", -- ["Quit"] = "退出",
["BanGeneral"] = "Ban", ["BanGeneral"] = "Ban",
["ResumeGeneral"] = "Unban", ["ResumeGeneral"] = "Unban",
["BanPackage"] = "Ban packages",
["$BanPkgHelp"] = "Banning packages",
["$BanCharaHelp"] = "Banning characters",
-- ["Companions"] = "珠联璧合", -- ["Companions"] = "珠联璧合",
-- ["Death audio"] = "阵亡", -- ["Death audio"] = "阵亡",
-- ["Win audio"] = "胜利语音",
-- ["Official"] = "官方",
["Title"] = "Title: ",
["Designer"] = "Designer: ",
["Voice Actor"] = "Voice Actor: ",
["Illustrator"] = "Illustrator: ",
["$WelcomeToLobby"] = "Welcome to FreeKill lobby!", ["$WelcomeToLobby"] = "Welcome to FreeKill lobby!",
["GameMode"] = "Game mode: ", ["GameMode"] = "Game mode: ",
@ -176,12 +185,13 @@ Fk:loadTranslationTable({
["AskForKingdom"] = "Choosing kingdom", ["AskForKingdom"] = "Choosing kingdom",
["AskForPindian"] = "Point fight", ["AskForPindian"] = "Point fight",
["AskForMoveCardInBoard"] = "Moving cards", ["AskForMoveCardInBoard"] = "Moving cards",
["replaceEquip"] = "Replacing Equip",
["PlayCard"] = "Playing card", ["PlayCard"] = "Playing card",
["AskForCardChosen"] = "Choosing card", ["AskForCardChosen"] = "Choosing card",
["AskForCardsChosen"] = "Choosing card", ["AskForCardsChosen"] = "Choosing card",
["#AskForChooseCard"] = "%1: please choose a card", ["#AskForChooseCard"] = "%1: please choose a card from %src",
["#AskForChooseCards"] = "%1: please choose %2~%3 cards", ["#AskForChooseCards"] = "%1: please choose %2~%3 cards from %src",
["$ChooseCard"] = "Choose a card", ["$ChooseCard"] = "Choose a card",
["$ChooseCards"] = "Choose %1~%2 cards", ["$ChooseCards"] = "Choose %1~%2 cards",
["$Hand"] = "Hand", ["$Hand"] = "Hand",
@ -196,8 +206,11 @@ Fk:loadTranslationTable({
["#AskForPeaches"] = "%src is dying, please use %arg Peach(es) to save him", ["#AskForPeaches"] = "%src is dying, please use %arg Peach(es) to save him",
["#AskForPeachesSelf"] = "You are dying, please use %arg Peach(es)/Alcohol to save yourself", ["#AskForPeachesSelf"] = "You are dying, please use %arg Peach(es)/Alcohol to save yourself",
["#AskForDiscard"] = "Please discard %arg cards (at least %arg2)", ["#AskForDiscard"] = "Please discard %arg cards (%arg2 at least)",
["#AskForCard"] = "Please choose %arg cards (at least %arg2)", ["#AskForCard"] = "Please choose %arg cards (%arg2 at least)",
["#AskForDistribution"] = "Please distribute cards (%arg at least , %arg2 total)",
["@DistributionTo"] = "",
["#replaceEquip"] = "Please Choose a Equip Card to be replaced",
["#askForPindian"] = "%arg: please choose a hand card for point fight", ["#askForPindian"] = "%arg: please choose a hand card for point fight",
["#StartPindianReason"] = "%from started point fight (%arg)", ["#StartPindianReason"] = "%from started point fight (%arg)",
["#ShowPindianCard"] = "The point fight card of %from is %card", ["#ShowPindianCard"] = "The point fight card of %from is %card",
@ -268,6 +281,7 @@ Fk:loadTranslationTable({
["thunder_damage"] = "Thunder", ["thunder_damage"] = "Thunder",
["ice_damage"] = "Ice", ["ice_damage"] = "Ice",
["hp_lost"] = "HP lost", ["hp_lost"] = "HP lost",
["lose_hp"] = "lose HP",
["phase_start"] = "Prepare phase", ["phase_start"] = "Prepare phase",
["phase_judge"] = "Judge phase", ["phase_judge"] = "Judge phase",
@ -343,6 +357,7 @@ Fk:loadTranslationTable({
["$LightningMove"] = "%card transfered from %from to %to", ["$LightningMove"] = "%card transfered from %from to %to",
["$PasteCard"] = "%from used %card to %to", ["$PasteCard"] = "%from used %card to %to",
["$DiscardCards"] = "%from discarded %arg card(s) %card", ["$DiscardCards"] = "%from discarded %arg card(s) %card",
["$DiscardOther"] = "%to discarded %arg card(s) %card from %from",
["$InstallEquip"] = "%from equipped %card", ["$InstallEquip"] = "%from equipped %card",
["$UninstallEquip"] = "%from uninstalled %card", ["$UninstallEquip"] = "%from uninstalled %card",

View File

@ -79,12 +79,16 @@ Fk:loadTranslationTable{
["Clear"] = "清空", ["Clear"] = "清空",
["Help_Ban_List"] = "导出键会将这个方案的内容复制到剪贴板中;" .. ["Help_Ban_List"] = "导出键会将这个方案的内容复制到剪贴板中;" ..
"导入键会自动读取剪贴板,若可以导入则导入,不能导入则报错。", "导入键会自动读取剪贴板,若可以导入则导入,不能导入则报错。",
["Ban_Generals"] = "已禁用武将",
["Ban_Packages"] = "禁用拓展包",
["Whitelist_Generals"] = "白名单武将",
["Export"] = "导出", ["Export"] = "导出",
["Export Success"] = "禁将方案已经复制到剪贴板。", ["Export Success"] = "禁将方案已经复制到剪贴板。",
["Import"] = "导入", ["Import"] = "导入",
["Not Legal"] = "导入失败不是合法的JSON字符串。", ["Not Legal"] = "导入失败不是合法的JSON字符串。",
["Not JSON"] = "导入失败:数据格式不对。", ["Not JSON"] = "导入失败:数据格式不对。",
["Import Success"] = "从剪贴板导入禁将方案成功。", ["Import Success"] = "从剪贴板导入禁将方案成功。",
["Rename"] = "重命名",
["$OnlineInfo"] = "大厅人数:%1总在线人数%2", ["$OnlineInfo"] = "大厅人数:%1总在线人数%2",
@ -184,8 +188,17 @@ FreeKill使用的是libgit2的C API与此同时使用Git完成拓展包的下
-- ["Quit"] = "退出", -- ["Quit"] = "退出",
["BanGeneral"] = "禁将", ["BanGeneral"] = "禁将",
["ResumeGeneral"] = "解禁", ["ResumeGeneral"] = "解禁",
["BanPackage"] = "禁拓展包",
["$BanPkgHelp"] = "正在禁用拓展包",
["$BanCharaHelp"] = "正在禁用武将",
["Companions"] = "珠联璧合", ["Companions"] = "珠联璧合",
["Death audio"] = "阵亡", ["Death audio"] = "阵亡",
["Win audio"] = "胜利语音",
["Official"] = "官方",
["Title"] = "称号:",
["Designer"] = "设计:",
["Voice Actor"] = "配音:",
["Illustrator"] = "画师:",
["$WelcomeToLobby"] = "欢迎进入新月杀游戏大厅!", ["$WelcomeToLobby"] = "欢迎进入新月杀游戏大厅!",
["GameMode"] = "游戏模式:", ["GameMode"] = "游戏模式:",
@ -226,12 +239,13 @@ FreeKill使用的是libgit2的C API与此同时使用Git完成拓展包的下
["AskForKingdom"] = "选择势力", ["AskForKingdom"] = "选择势力",
["AskForPindian"] = "拼点", ["AskForPindian"] = "拼点",
["AskForMoveCardInBoard"] = "移动卡牌", ["AskForMoveCardInBoard"] = "移动卡牌",
["replaceEquip"] = "替换装备",
["PlayCard"] = "出牌", ["PlayCard"] = "出牌",
["AskForCardChosen"] = "选牌", ["AskForCardChosen"] = "选牌",
["AskForCardsChosen"] = "选牌", ["AskForCardsChosen"] = "选牌",
["#AskForChooseCard"] = "%1请选择一张卡牌", ["#AskForChooseCard"] = "%1请选择%src的一张卡牌",
["#AskForChooseCards"] = "%1请选择%2至%3张卡牌", ["#AskForChooseCards"] = "%1请选择%src的%2至%3张卡牌",
["$ChooseCard"] = "请选择一张卡牌", ["$ChooseCard"] = "请选择一张卡牌",
["$ChooseCards"] = "请选择%1至%2张卡牌", ["$ChooseCards"] = "请选择%1至%2张卡牌",
["$Hand"] = "手牌区", ["$Hand"] = "手牌区",
@ -248,6 +262,9 @@ FreeKill使用的是libgit2的C API与此同时使用Git完成拓展包的下
["#AskForDiscard"] = "请弃置 %arg 张牌,最少 %arg2 张", ["#AskForDiscard"] = "请弃置 %arg 张牌,最少 %arg2 张",
["#AskForCard"] = "请选择 %arg 张牌,最少 %arg2 张", ["#AskForCard"] = "请选择 %arg 张牌,最少 %arg2 张",
["#AskForDistribution"] = "请分配这些牌,至少 %arg 张,至多 %arg2 张",
["@DistributionTo"] = "",
["#replaceEquip"] = "选择一张装备牌替换之",
["#askForPindian"] = "%arg请选择一张手牌作为拼点牌", ["#askForPindian"] = "%arg请选择一张手牌作为拼点牌",
["#StartPindianReason"] = "%from 由于 %arg 而发起拼点", ["#StartPindianReason"] = "%from 由于 %arg 而发起拼点",
["#ShowPindianCard"] = "%from 的拼点牌是 %card", ["#ShowPindianCard"] = "%from 的拼点牌是 %card",
@ -304,9 +321,9 @@ FreeKill使用的是libgit2的C API与此同时使用Git完成拓展包的下
["Resume"] = "继续", ["Resume"] = "继续",
["Bulletin Info"] = [==[ ["Bulletin Info"] = [==[
## v0.4.2 ## v0.4.9
bug
]==], ]==],
} }
@ -323,7 +340,8 @@ Fk:loadTranslationTable{
["fire_damage"] = "火属性", ["fire_damage"] = "火属性",
["thunder_damage"] = "雷属性", ["thunder_damage"] = "雷属性",
["ice_damage"] = "冰属性", ["ice_damage"] = "冰属性",
["hp_lost"] = "体力流失", ["hp_lost"] = "失去体力",
["lose_hp"] = "失去体力",
["phase_start"] = "准备阶段", ["phase_start"] = "准备阶段",
["phase_judge"] = "判定阶段", ["phase_judge"] = "判定阶段",
@ -386,22 +404,27 @@ Fk:loadTranslationTable{
["#LoseSkill"] = "%from 失去了技能 “%arg”", ["#LoseSkill"] = "%from 失去了技能 “%arg”",
-- moveCards (they are sent by notifyMoveCards) -- moveCards (they are sent by notifyMoveCards)
["$PutCard"] = "%from 的 %arg 张牌被置于牌堆",
["$PutKnownCard"] = "%from 的牌 %card 被置于牌堆",
["$RemoveCardFromGame"] = "%arg2 张牌被作为 %arg 移出游戏",
["$AddToPile"] = "%card 被作为 %arg 移出游戏",
["$GetCardsFromPile"] = "%from 从 %arg 中获得了 %arg2 张牌 %card", ["$GetCardsFromPile"] = "%from 从 %arg 中获得了 %arg2 张牌 %card",
["$DrawCards"] = "%from 摸了 %arg 张牌 %card", ["$DrawCards"] = "%from 摸了 %arg 张牌 %card",
["$MoveCards"] = "%to 从 %from 处获得了 %arg 张牌 %card",
["$PreyCardsFromPile"] = "%from 获得了 %arg 张牌 %card", ["$PreyCardsFromPile"] = "%from 获得了 %arg 张牌 %card",
["$GotCardBack"] = "%from 收回了 %arg 张牌 %card", ["$GotCardBack"] = "%from 收回了 %arg 张牌 %card",
["$RecycleCard"] = "%from 从弃牌堆回收了 %arg 张牌 %card", ["$RecycleCard"] = "%from 从弃牌堆回收了 %arg 张牌 %card",
["$MoveCards"] = "%to 从 %from 处获得了 %arg 张牌 %card",
["$LightningMove"] = "%card 从 %from 转移到了 %to",
["$PasteCard"] = "%from 给 %to 贴了张 %card",
["$DiscardCards"] = "%from 弃置了 %arg 张牌 %card",
["$InstallEquip"] = "%from 装备了 %card", ["$InstallEquip"] = "%from 装备了 %card",
["$UninstallEquip"] = "%from 卸载了 %card", ["$UninstallEquip"] = "%from 卸载了 %card",
["$LightningMove"] = "%card 从 %from 转移到了 %to",
["$PasteCard"] = "%from 给 %to 贴了张 %card",
["$AddToPile"] = "%arg2 张牌 %card 被作为 %from 的 %arg 移出游戏",
["$PutCard"] = "%from 的 %arg 张牌被置于牌堆",
["$PutKnownCard"] = "%from 的牌 %card 被置于牌堆",
["$DiscardCards"] = "%from 弃置了 %arg 张牌 %card",
["$DiscardOther"] = "%to 弃置了 %from 的 %arg 张牌 %card",
["$PutToDiscard"] = "%arg 张牌 %card 被置入弃牌堆",
["#ShowCard"] = "%from 展示了牌 %card", ["#ShowCard"] = "%from 展示了牌 %card",
["#Recast"] = "%from 重铸了 %card", ["#Recast"] = "%from 重铸了 %card",
["#RecastBySkill"] = "%from 发动了 “%arg” 重铸了 %card", ["#RecastBySkill"] = "%from 发动了 “%arg” 重铸了 %card",

View File

@ -0,0 +1,52 @@
-- 作Room和Client的基类这二者有不少共通之处
---@class AbstractRoom : Object
---@fiele public players Player[] @ 房内参战角色们
---@field public alive_players Player[] @ 所有存活玩家的数组
---@field public observers Player[] @ 看戏的
---@field public current Player @ 当前行动者
---@field public status_skills table<class, Skill[]> @ 这个房间中含有的状态技列表
---@field public filtered_cards table<integer, Card> @ 见于Engine其实在这
---@field public printed_cards table<integer, Card> @ 同上
---@field public skill_costs table<string, any> @ 用来存skill.cost_data
---@field public card_marks table<integer, any> @ 用来存实体卡的card.mark
---@field public banners table<string, any> @ 全局mark
local AbstractRoom = class("AbstractRoom")
function AbstractRoom:initialize()
self.players = {}
self.alive_players = {}
self.observers = {}
self.current = nil
self.status_skills = {}
for class, skills in pairs(Fk.global_status_skill) do
self.status_skills[class] = {table.unpack(skills)}
end
self.filtered_cards = {}
self.printed_cards = {}
self.skill_costs = {}
self.card_marks = {}
self.banners = {}
end
-- 仅供注释,其余空函数一样
---@param id integer
---@return Player?
function AbstractRoom:getPlayerById(id) end
--- 获取一张牌所处的区域。
---@param cardId integer | Card @ 要获得区域的那张牌可以是Card或者一个id
---@return CardArea @ 这张牌的区域
function AbstractRoom:getCardArea(cardId) end
function AbstractRoom:setBanner(name, value)
if value == 0 then value = nil end
self.banners[name] = value
end
function AbstractRoom:getBanner(name)
return self.banners[name]
end
return AbstractRoom

View File

@ -24,6 +24,7 @@
---@field public special_skills? string[] @ 衍生技能,如重铸 ---@field public special_skills? string[] @ 衍生技能,如重铸
---@field public is_damage_card boolean @ 是否为会造成伤害的牌 ---@field public is_damage_card boolean @ 是否为会造成伤害的牌
---@field public multiple_targets boolean @ 是否为指定多个目标的牌 ---@field public multiple_targets boolean @ 是否为指定多个目标的牌
---@field public is_passive? boolean @ 是否只能在响应时使用或打出
---@field public is_derived? boolean @ 判断是否为衍生牌 ---@field public is_derived? boolean @ 判断是否为衍生牌
local Card = class("Card") local Card = class("Card")
@ -145,11 +146,12 @@ function Card:clone(suit, number)
newCard.special_skills = self.special_skills newCard.special_skills = self.special_skills
newCard.is_damage_card = self.is_damage_card newCard.is_damage_card = self.is_damage_card
newCard.multiple_targets = self.multiple_targets newCard.multiple_targets = self.multiple_targets
newCard.is_passive = self.is_passive
newCard.is_derived = self.is_derived newCard.is_derived = self.is_derived
return newCard return newCard
end end
--- 检测是否为虚拟卡牌如果其ID为0及以下,则为虚拟卡牌。 --- 检测是否为虚拟卡牌如果其ID为0,则为虚拟卡牌。
function Card:isVirtual() function Card:isVirtual()
return self.id == 0 return self.id == 0
end end
@ -171,7 +173,7 @@ local function updateColorAndNumber(card)
local different_color = false local different_color = false
for i, id in ipairs(card.subcards) do for i, id in ipairs(card.subcards) do
local c = Fk:getCardById(id) local c = Fk:getCardById(id)
number = math.min(number + c.number, 13) number = #card.subcards == 1 and math.min(number + c.number, 13) or 0
if i == 1 then if i == 1 then
card.suit = c.suit card.suit = c.suit
else else
@ -406,17 +408,12 @@ function Card:getMark(mark)
if (not self:isVirtual()) and next(self.mark) == nil then if (not self:isVirtual()) and next(self.mark) == nil then
self.mark = nil self.mark = nil
end end
if type(ret) == "table" then
ret = table.simpleClone(ret)
end
return ret return ret
end end
--- 判定卡牌是否拥有对应的Mark。
---@param mark string @ 标记
---@return boolean
function Card:hasMark(mark)
fk.qWarning("hasMark will be deleted in future version!")
return self:getMark(mark) ~= 0
end
--- 获取卡牌有哪些Mark。 --- 获取卡牌有哪些Mark。
function Card:getMarkNames() function Card:getMarkNames()
local ret = {} local ret = {}

View File

@ -7,6 +7,8 @@
--- 同时也提供了许多常用的函数。 --- 同时也提供了许多常用的函数。
--- ---
---@class Engine : Object ---@class Engine : Object
---@field public extensions table<string, string[]> @ 所有mod列表及其包含的拓展包
---@field public extension_names string[] @ Mod名字的数组为了方便排序
---@field public packages table<string, Package> @ 所有拓展包的列表 ---@field public packages table<string, Package> @ 所有拓展包的列表
---@field public package_names string[] @ 含所有拓展包名字的数组,为了方便排序 ---@field public package_names string[] @ 含所有拓展包名字的数组,为了方便排序
---@field public skills table<string, Skill> @ 所有的技能 ---@field public skills table<string, Skill> @ 所有的技能
@ -28,6 +30,7 @@
---@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标记的表 ---@field public qml_marks table<string, QmlMarkSpec> @ 自定义Qml标记的表
---@field public mini_games table<string, MiniGameSpec> @ 自定义多人交互表
---@field public yuqi_methods table<string, YuqiSpec> @ “隅泣”框操作方法表 ---@field public yuqi_methods table<string, YuqiSpec> @ “隅泣”框操作方法表
local Engine = class("Engine") local Engine = class("Engine")
@ -44,6 +47,12 @@ function Engine:initialize()
Fk = self Fk = self
self.extensions = {
["standard"] = { "standard" },
["standard_cards"] = { "standard_cards" },
["maneuvering"] = { "maneuvering" },
}
self.extension_names = { "standard", "standard_cards", "maneuvering" }
self.packages = {} -- name --> Package self.packages = {} -- name --> Package
self.package_names = {} self.package_names = {}
self.skills = {} -- name --> Skill self.skills = {} -- name --> Skill
@ -62,6 +71,7 @@ function Engine:initialize()
self._custom_events = {} self._custom_events = {}
self.poxi_methods = {} self.poxi_methods = {}
self.qml_marks = {} self.qml_marks = {}
self.mini_games = {}
self:loadPackages() self:loadPackages()
self:loadDisabled() self:loadDisabled()
@ -69,20 +79,20 @@ function Engine:initialize()
end end
local _foreign_keys = { local _foreign_keys = {
"currentResponsePattern", ["currentResponsePattern"] = true,
"currentResponseReason", ["currentResponseReason"] = true,
"filtered_cards", ["filtered_cards"] = true,
"printed_cards", ["printed_cards"] = true,
} }
function Engine:__index(k) function Engine:__index(k)
if table.contains(_foreign_keys, k) then if _foreign_keys[k] then
return self:currentRoom()[k] return self:currentRoom()[k]
end end
end end
function Engine:__newindex(k, v) function Engine:__newindex(k, v)
if table.contains(_foreign_keys, k) then if _foreign_keys[k] then
self:currentRoom()[k] = v self:currentRoom()[k] = v
else else
rawset(self, k, v) rawset(self, k, v)
@ -140,11 +150,15 @@ function Engine:loadPackages()
-- Note that instance of Package is a table too -- Note that instance of Package is a table too
-- so dont use type(pack) == "table" here -- so dont use type(pack) == "table" here
if type(pack) == "table" then if type(pack) == "table" then
table.insert(self.extension_names, dir)
if pack[1] ~= nil then if pack[1] ~= nil then
self.extensions[dir] = {}
for _, p in ipairs(pack) do for _, p in ipairs(pack) do
table.insert(self.extensions[dir], p.name)
self:loadPackage(p) self:loadPackage(p)
end end
else else
self.extensions[dir] = { pack.name }
self:loadPackage(pack) self:loadPackage(pack)
end end
end end
@ -274,8 +288,8 @@ end
---@param name string @ 要查询的武将名字 ---@param name string @ 要查询的武将名字
---@return string[] @ 这个武将对应的同名武将列表 ---@return string[] @ 这个武将对应的同名武将列表
function Engine:getSameGenerals(name) function Engine:getSameGenerals(name)
local tmp = name:split("__") if not self.generals[name] then return {} end
local tName = tmp[#tmp] local tName = self.generals[name].trueName
local ret = self.same_generals[tName] or {} local ret = self.same_generals[tName] or {}
return table.filter(ret, function(g) return table.filter(ret, function(g)
return g ~= name and self.generals[g] ~= nil and self:canUseGeneral(g) return g ~= name and self.generals[g] ~= nil and self:canUseGeneral(g)
@ -358,7 +372,7 @@ function Engine:addPoxiMethod(spec)
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
--- 向Engine中添加一个隅泣用方法。 --- 向Engine中添加一个QML标记方法。
---@param spec QmlMarkSpec ---@param spec QmlMarkSpec
function Engine:addQmlMark(spec) function Engine:addQmlMark(spec)
assert(type(spec.name) == "string") assert(type(spec.name) == "string")
@ -368,6 +382,16 @@ function Engine:addQmlMark(spec)
self.qml_marks[spec.name] = spec self.qml_marks[spec.name] = spec
end end
--- 向Engine中添加一个多人交互用方法。
---@param spec MiniGameSpec
function Engine:addMiniGame(spec)
assert(type(spec.name) == "string")
if self.mini_games[spec.name] then
fk.qCritical("Warning: duplicated mini game type " .. spec.name)
end
self.mini_games[spec.name] = spec
end
--- 向Engine中添加一个隅泣用方法。 --- 向Engine中添加一个隅泣用方法。
---@param spec YuqiSpec ---@param spec YuqiSpec
function Engine:addYuqiMethod(spec) function Engine:addYuqiMethod(spec)
@ -538,7 +562,7 @@ function Engine:_addPrintedCard(card)
end end
--- 获知当前的Engine是跑在服务端还是客户端并返回相应的实例。 --- 获知当前的Engine是跑在服务端还是客户端并返回相应的实例。
---@return Room | Client ---@return AbstractRoom
function Engine:currentRoom() function Engine:currentRoom()
if RoomInstance then if RoomInstance then
return RoomInstance return RoomInstance

View File

@ -10,7 +10,7 @@
2. Matcher ('|') 2. Matcher ('|')
3. Matcher ',' 3. Matcher ','
Matcher ||||||id Matcher ||||||id
'~' AJQK '~' AJQK

View File

@ -27,7 +27,7 @@ end
---@param victim ServerPlayer @ 死者 ---@param victim ServerPlayer @ 死者
---@return string @ 胜者阵营 ---@return string @ 胜者阵营
function GameMode:getWinner(victim) function GameMode:getWinner(victim)
if victim.rest > 0 then if not victim.surrendered and victim.rest > 0 then
return "" return ""
end end

View File

@ -71,7 +71,7 @@ function General:__tostring()
end end
--- 为武将增加技能,需要注意增加其他武将技能时的处理方式。 --- 为武将增加技能,需要注意增加其他武将技能时的处理方式。
---@param skill Skill @ (单个)武将技能 ---@param skill Skill|string @ (单个)武将技能
function General:addSkill(skill) function General:addSkill(skill)
if (type(skill) == "string") then if (type(skill) == "string") then
table.insert(self.other_skills, skill) table.insert(self.other_skills, skill)
@ -82,7 +82,7 @@ function General:addSkill(skill)
end end
--- 为武将增加相关技能,需要注意增加其他武将技能时的处理方式。 --- 为武将增加相关技能,需要注意增加其他武将技能时的处理方式。
---@param skill Skill @ (单个)武将技能 ---@param skill Skill|string @ (单个)武将技能
function General:addRelatedSkill(skill) function General:addRelatedSkill(skill)
if (type(skill) == "string") then if (type(skill) == "string") then
table.insert(self.related_other_skills, skill) table.insert(self.related_other_skills, skill)
@ -94,12 +94,19 @@ function General:addRelatedSkill(skill)
end end
--- 获取武将所有技能。 --- 获取武将所有技能。
---@param include_lord? boolean
---@return string[]
function General:getSkillNameList(include_lord) function General:getSkillNameList(include_lord)
local ret = table.map(self.skills, Util.NameMapper) local ret = {}
table.insertTable(ret, self.other_skills) local other_skills = table.map(self.other_skills, Util.Name2SkillMapper)
local skills = table.connect(self.skills, other_skills)
if not include_lord then for _, skill in ipairs(skills) do
if include_lord or not skill.lordSkill then
table.insert(ret, skill.name)
end
end end
-- table.insertTable(ret, self.other_skills)
return ret return ret
end end

View File

@ -215,15 +215,10 @@ end
---@param mark string @ 标记 ---@param mark string @ 标记
---@return any ---@return any
function Player:getMark(mark) function Player:getMark(mark)
return (self.mark[mark] or 0) local mark = self.mark[mark]
end if not mark then return 0 end
if type(mark) == "table" then return table.simpleClone(mark) end
--- 判定角色是否拥有对应的Mark。 return mark
---@param mark string @ 标记
---@return boolean
function Player:hasMark(mark)
fk.qWarning("hasMark will be deleted in future version!")
return self:getMark(mark) ~= 0
end end
--- 获取角色有哪些Mark。 --- 获取角色有哪些Mark。
@ -363,13 +358,13 @@ function Player:getCardIds(playerAreas, specialName)
return cardIds return cardIds
end end
--- 通过名字检索获取玩家是否存在对应私人牌堆。 --- 通过名字检索获取玩家对应私人牌堆。
---@param name string @ 私人牌堆名 ---@param name string @ 私人牌堆名
function Player:getPile(name) function Player:getPile(name)
return self.special_cards[name] or {} return table.simpleClone(self.special_cards[name] or {})
end end
--- 通过ID检索获取玩家是否存在对应私人牌堆。 --- 通过ID检索获取玩家对应私人牌堆。
---@param id integer @ 私人牌堆ID ---@param id integer @ 私人牌堆ID
---@return string? ---@return string?
function Player:getPileNameOfId(id) function Player:getPileNameOfId(id)
@ -454,23 +449,43 @@ end
--- 获取玩家攻击范围。 --- 获取玩家攻击范围。
function Player:getAttackRange() function Player:getAttackRange()
local weapon = Fk:getCardById(self:getEquipment(Card.SubtypeWeapon)) local baseValue = 1
local baseAttackRange = math.max(weapon and weapon.attack_range or 1, 0) local weapons = self:getEquipments(Card.SubtypeWeapon)
if #weapons > 0 then
local status_skills = Fk:currentRoom().status_skills[AttackRangeSkill] or Util.DummyTable baseValue = 0
for _, skill in ipairs(status_skills) do for _, id in ipairs(weapons) do
local correct = skill:getCorrect(self) local weapon = Fk:getCardById(id)
baseAttackRange = baseAttackRange + (correct or 0) baseValue = math.max(baseValue, weapon.attack_range or 1)
end
end end
return math.max(baseAttackRange, 0) local status_skills = Fk:currentRoom().status_skills[AttackRangeSkill] or Util.DummyTable
local max_fixed, correct = nil, 0
for _, skill in ipairs(status_skills) do
local f = skill:getFixed(self)
if f ~= nil then
max_fixed = max_fixed and math.max(max_fixed, f) or f
end
local c = skill:getCorrect(self)
correct = correct + (c or 0)
end
return math.max(math.max(baseValue, (max_fixed or 0)) + correct, 0)
end end
--- 获取角色是否被移除。 --- 获取角色是否被移除。
function Player:isRemoved() function Player:isRemoved()
return self:getMark(MarkEnum.PlayerRemoved) ~= 0 or table.find(MarkEnum.TempMarkSuffix, function(s) for mark, _ in pairs(self.mark) do
return self:getMark(MarkEnum.PlayerRemoved .. s) ~= 0 if mark == MarkEnum.PlayerRemoved then return true end
end) if mark:startsWith(MarkEnum.PlayerRemoved .. "-") then
for _, suffix in ipairs(MarkEnum.TempMarkSuffix) do
if mark:find(suffix, 1, true) then return true end
end
end
end
-- return self:getMark(MarkEnum.PlayerRemoved) ~= 0 or table.find(MarkEnum.TempMarkSuffix, function(s)
-- return self:getMark(MarkEnum.PlayerRemoved .. s) ~= 0
-- end)
end end
--- 修改玩家与其他角色的固定距离。 --- 修改玩家与其他角色的固定距离。
@ -857,9 +872,20 @@ end
--- 确认玩家是否可以使用特定牌。 --- 确认玩家是否可以使用特定牌。
---@param card Card @ 特定牌 ---@param card Card @ 特定牌
function Player:canUse(card) ---@param extra_data? UseExtraData @ 额外数据
assert(card, "Error: No Card") function Player:canUse(card, extra_data)
return card.skill:canUse(self, card) return card.skill:canUse(self, card, extra_data)
end
--- 确认玩家是否可以对特定玩家使用特定牌。
---@param card Card @ 特定牌
---@param to Player @ 特定玩家
---@param extra_data? UseExtraData @ 额外数据
function Player:canUseTo(card, to, extra_data)
if self:prohibitUse(card) or self:isProhibited(to, card) then return false end
local distance_limited = not (extra_data and extra_data.bypass_distances)
local can_use = self:canUse(card, extra_data)
return can_use and card.skill:modTargetFilter(to.id, {}, self.id, card, distance_limited)
end end
--- 确认玩家是否被禁止对特定玩家使用特定牌。 --- 确认玩家是否被禁止对特定玩家使用特定牌。
@ -911,8 +937,12 @@ function Player:prohibitResponse(card)
end end
--- 确认玩家是否被禁止弃置特定牌。 --- 确认玩家是否被禁止弃置特定牌。
---@param card Card @ 特定的牌 ---@param card Card|integer @ 特定的牌
function Player:prohibitDiscard(card) function Player:prohibitDiscard(card)
if type(card) == "number" then
card = Fk:getCardById(card)
end
local status_skills = Fk:currentRoom().status_skills[ProhibitSkill] or Util.DummyTable local status_skills = Fk:currentRoom().status_skills[ProhibitSkill] or Util.DummyTable
for _, skill in ipairs(status_skills) do for _, skill in ipairs(status_skills) do
if skill:prohibitDiscard(self, card) then if skill:prohibitDiscard(self, card) then
@ -928,13 +958,21 @@ function Player:prohibitReveal(isDeputy)
if type(self:getMark(MarkEnum.RevealProhibited)) == "table" and table.contains(self:getMark(MarkEnum.RevealProhibited), place) then if type(self:getMark(MarkEnum.RevealProhibited)) == "table" and table.contains(self:getMark(MarkEnum.RevealProhibited), place) then
return true return true
end end
for _, m in ipairs(table.map(MarkEnum.TempMarkSuffix, function(s)
return self:getMark(MarkEnum.RevealProhibited .. s) for mark, value in pairs(self.mark) do
end)) do if mark:startsWith(MarkEnum.RevealProhibited .. "-") and type(value) == "table" then
if type(m) == "table" and table.contains(m, place) then for _, suffix in ipairs(MarkEnum.TempMarkSuffix) do
return true if mark:find(suffix, 1, true) then return true end
end
end end
end end
-- for _, m in ipairs(table.map(MarkEnum.TempMarkSuffix, function(s)
-- return self:getMark(MarkEnum.RevealProhibited .. s)
-- end)) do
-- if type(m) == "table" and table.contains(m, place) then
-- return true
-- end
-- end
return false return false
end end
@ -959,6 +997,21 @@ function Player:canPindian(to, ignoreFromKong, ignoreToKong)
return true return true
end end
--- 判断一张牌能否移动至某角色的装备区
---@param cardId integer @ 移动的牌
---@param convert? boolean @ 是否可以替换装备(默认可以)
---@return boolean
function Player:canMoveCardIntoEquip(cardId, convert)
convert = (convert == nil) and true or convert
local card = Fk:getCardById(cardId)
if not (card.sub_type >= 3 and card.sub_type <= 7) then return false end
if self.dead or table.contains(self:getCardIds("e"), cardId) then return false end
if self:hasEmptyEquipSlot(card.sub_type) or (#self:getEquipments(card.sub_type) > 0 and convert) then
return true
end
return false
end
--转换技状态阳 --转换技状态阳
fk.SwitchYang = 0 fk.SwitchYang = 0
--转换技状态阴 --转换技状态阴
@ -991,7 +1044,7 @@ function Player:canMoveCardInBoardTo(to, id)
return return
not ( not (
table.find(to:getCardIds(Player.Judge), function(cardId) table.find(to:getCardIds(Player.Judge), function(cardId)
return Fk:getCardById(cardId).name == card.name return (to:getVirualEquip(cardId) or Fk:getCardById(cardId)).name == card.name
end) or end) or
table.contains(to.sealedSlots, Player.JudgeSlot) table.contains(to.sealedSlots, Player.JudgeSlot)
) )

Some files were not shown because too many files have changed in this diff Show More