diff --git a/Fk/Pages/GeneralsOverview.qml b/Fk/Pages/GeneralsOverview.qml
index 08cc9cfa..1d47987e 100644
--- a/Fk/Pages/GeneralsOverview.qml
+++ b/Fk/Pages/GeneralsOverview.qml
@@ -244,6 +244,7 @@ Item {
config.curSchemeChanged();
} else {
generalText.clear();
+ generalText.clearSavedText();
generalDetail.general = modelData;
generalDetail.updateGeneral();
generalDetail.open();
@@ -505,6 +506,7 @@ Item {
detailFlickable.contentY = 0; // 重置滚动条
const data = lcall("GetGeneralDetail", general);
generalText.clear();
+ generalText.clearSavedText();
audioModel.clear();
if (data.companions.length > 0){
@@ -619,6 +621,10 @@ Item {
TextEdit {
id: generalText
+ property var savedtext: []
+ function clearSavedText() {
+ savedtext = [];
+ }
Layout.fillWidth: true
readOnly: true
selectByKeyboard: true
@@ -626,6 +632,14 @@ Item {
wrapMode: TextEdit.WordWrap
textFormat: TextEdit.RichText
font.pixelSize: 18
+ onLinkActivated: (link) => {
+ if (link === "back") {
+ text = savedtext.pop();
+ } else {
+ savedtext.push(text);
+ text = '点击返回
' + luatr(link);
+ }
+ }
}
GridLayout {
diff --git a/Fk/Pages/Room.qml b/Fk/Pages/Room.qml
index 654b32e7..c2294ebf 100644
--- a/Fk/Pages/Room.qml
+++ b/Fk/Pages/Room.qml
@@ -1290,6 +1290,10 @@ Item {
});
}
+ function getPhoto(id) {
+ return Logic.getPhoto(id);
+ }
+
function applyChange(uiUpdate) {
uiUpdate["_delete"]?.forEach(data => {
if (data.type == "Interaction") {
diff --git a/Fk/Pages/RoomLogic.js b/Fk/Pages/RoomLogic.js
index f8c7b638..ce0b422e 100644
--- a/Fk/Pages/RoomLogic.js
+++ b/Fk/Pages/RoomLogic.js
@@ -199,14 +199,15 @@ function getAreaItem(area, id) {
return null;
}
-function moveCards(moves) {
+function moveCards(data) {
+ const moves = data.merged;
for (let i = 0; i < moves.length; i++) {
const move = moves[i];
const from = getAreaItem(move.fromArea, move.from);
const to = getAreaItem(move.toArea, move.to);
if (!from || !to || (from === to && move.fromArea !== Card.DiscardPile))
continue;
- const items = from.remove(move.ids, move.fromSpecialName);
+ const items = from.remove(move.ids, move.fromSpecialName, data);
if (to === tablePile) {
let vanished = items.filter(c => c.cid === -1);
if (vanished.length > 0) {
@@ -569,30 +570,6 @@ callbacks["MaxCard"] = (data) => {
}
}
-function changeSelf(id) {
- lcall("ChangeSelf", id);
-
- // move new selfPhoto to dashboard
- let order = new Array(photoModel.count);
- for (let i = 0; i < photoModel.count; i++) {
- const item = photoModel.get(i);
- order[item.seatNumber - 1] = item.id;
- if (item.id === Self.id) {
- dashboard.self = photos.itemAt(i);
- }
- }
- callbacks["ArrangeSeats"](order);
-
- // update dashboard
- dashboard.update();
-
- // handle pending messages
- if (mainWindow.is_pending) {
- const data = mainWindow.fetchMessage();
- return mainWindow.handleMessage(data.command, data.jsonData);
- }
-}
-
callbacks["AddPlayer"] = (data) => {
// jsonData: int id, string screenName, string avatar, bool ready
for (let i = 0; i < photoModel.count; i++) {
@@ -1538,21 +1515,20 @@ callbacks["UpdateGameData"] = (data) => {
}
// 神貂蝉
-
-callbacks["StartChangeSelf"] = (j) => {
- const id = parseInt(j);
- ClientInstance.notifyServer("PushRequest", "changeself," + j);
-}
-
callbacks["ChangeSelf"] = (j) => {
- const data = parseInt(j);
- if (Self.id === data) {
- const msg = mainWindow.fetchMessage();
- if (!msg) return;
- mainWindow.handleMessage(msg.command, msg.jsonData);
- return;
+ // move new selfPhoto to dashboard
+ let order = new Array(photoModel.count);
+ for (let i = 0; i < photoModel.count; i++) {
+ const item = photoModel.get(i);
+ order[item.seatNumber - 1] = item.id;
+ if (item.id === Self.id) {
+ dashboard.self = photos.itemAt(i);
+ }
}
- changeSelf(data);
+ callbacks["ArrangeSeats"](order);
+
+ // update dashboard
+ dashboard.update();
}
callbacks["UpdateRequestUI"] = (uiUpdate) => {
diff --git a/Fk/PhotoElement/MarkArea.qml b/Fk/PhotoElement/MarkArea.qml
index ba36da59..41057e2f 100644
--- a/Fk/PhotoElement/MarkArea.qml
+++ b/Fk/PhotoElement/MarkArea.qml
@@ -88,7 +88,7 @@ Item {
} else {
if (!root.parent.playerid) return;
let data = lcall("GetPile", root.parent.playerid, mark_name);
- data = data.filter((e) => e !== -1);
+ data = data.filter((e) => lcall("CardVisibility", e));
if (data.length === 0)
return;
diff --git a/Fk/RoomElement/InvisibleCardArea.qml b/Fk/RoomElement/InvisibleCardArea.qml
index 4d533f77..339cece5 100644
--- a/Fk/RoomElement/InvisibleCardArea.qml
+++ b/Fk/RoomElement/InvisibleCardArea.qml
@@ -48,7 +48,7 @@ Item {
return false;
}
- function remove(outputs)
+ function remove(outputs, _, visibleData)
{
const component = Qt.createComponent("CardItem.qml");
if (component.status !== Component.Ready)
@@ -67,6 +67,7 @@ Item {
card.x -= card.width / 2;
card.x += (i - outputs.length / 2) * 15;
card.y -= card.height / 2;
+ if (visibleData) card.known = !!visibleData[outputs[i].toString()];
items.push(card);
if (checkExisting) {
for (let j = 0; j < length; j++) {
diff --git a/Fk/RoomElement/Photo.qml b/Fk/RoomElement/Photo.qml
index 99b00058..1d648b6f 100644
--- a/Fk/RoomElement/Photo.qml
+++ b/Fk/RoomElement/Photo.qml
@@ -507,23 +507,6 @@ Item {
anchors.bottomMargin: 4
style: Text.Outline
}
-
- TapHandler {
- enabled: (root.state != "candidate" || !root.selectable)
- && root.playerid !== Self.id
- onTapped: {
- const params = { name: "hand_card" };
- let data = lcall("GetPlayerHandcards", root.playerid);
- data = data.filter((e) => e !== -1);
- if (data.length === 0)
- return;
-
- params.ids = data;
-
- // Just for using room's right drawer
- roomScene.startCheat("../RoomElement/ViewPile", params);
- }
- }
}
TapHandler {
@@ -548,7 +531,10 @@ Item {
RoleComboBox {
id: role
- value: root.role
+ value: {
+ if (root.role === "hidden") return "hidden";
+ lcall("RoleVisibility", root.playerid) ? root.role : "unknown";
+ }
anchors.top: parent.top
anchors.topMargin: -4
anchors.right: parent.right
@@ -798,6 +784,91 @@ Item {
}
}
+ Rectangle {
+ color: "#CC2E2C27"
+ radius: 6
+ border.color: "#A6967A"
+ border.width: 1
+ width: 44
+ height: 112
+ /* 有点小问题,因为绝大部分都是手机玩家我还是无脑放左
+ x: {
+ const roomX = mapToItem(roomScene, root.x, root.y).x;
+ if (roomX < 48) return 175;
+ return -44;
+ }
+ */
+ x: -44
+ y: 128
+ visible: {
+ if (root.playerid === Self.id) return false;
+ if (root.handcards === 0) return false; // 优先绑定再判buddy,否则不会更新
+ if (!lcall("IsMyBuddy", Self.id, root.playerid) &&
+ !lcall("HasVisibleCard", Self.id, root.playerid)) return false;
+ return true;
+ }
+
+ Text {
+ x: 2; y: 2
+ width: 42
+ text: {
+ if (!parent.visible) return "";
+ const unused = root.handcards; // 绑定
+ const ids = lcall("GetPlayerHandcards", root.playerid);
+ const txt = [];
+ for (const cid of ids) {
+ if (txt.length >= 4) {
+ // txt.push(" ...");
+ txt.push("...");
+ break;
+ }
+ if (!lcall("CardVisibility", cid)) continue;
+ const data = lcall("GetCardData", cid);
+ let a = luatr(data.name);
+ /* if (a.length === 1) {
+ a = " " + a;
+ } else */
+ if (a.length >= 2) {
+ a = a.slice(0, 2);
+ }
+ txt.push(a);
+ }
+
+ if (txt.length < 5) {
+ const unknownCards = ids.length - txt.length;
+ for (let i = 0; i < unknownCards; i++) {
+ if (txt.length >= 4) {
+ txt.push("...");
+ break;
+ } else {
+ txt.push("?");
+ }
+ }
+ }
+
+ return txt.join("
");
+ }
+ color: "#E4D5A0"
+ font.family: fontLibian.name
+ font.pixelSize: 18
+ textFormat: Text.RichText
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+ TapHandler {
+ onTapped: {
+ const params = { name: "hand_card" };
+ let data = lcall("GetPlayerHandcards", root.playerid);
+ data = data.filter((e) => lcall("CardVisibility", e));
+
+ params.ids = data;
+
+ // Just for using room's right drawer
+ roomScene.startCheat("../RoomElement/ViewPile", params);
+ }
+ }
+ }
+
onGeneralChanged: {
if (!roomScene.isStarted) return;
const text = luatr(general);
diff --git a/Fk/RoomElement/PlayerCardBox.qml b/Fk/RoomElement/PlayerCardBox.qml
index 1d4fcfdb..1dc51865 100644
--- a/Fk/RoomElement/PlayerCardBox.qml
+++ b/Fk/RoomElement/PlayerCardBox.qml
@@ -75,7 +75,7 @@ GraphicsBox {
suit: model.suit || ""
number: model.number || 0
autoBack: false
- known: model.cid !== -1
+ known: model.known
selectable: true
onClicked: {
if (!root.multiChoose) {
diff --git a/Fk/RoomElement/PoxiBox.qml b/Fk/RoomElement/PoxiBox.qml
index 62a98c53..88f76efc 100644
--- a/Fk/RoomElement/PoxiBox.qml
+++ b/Fk/RoomElement/PoxiBox.qml
@@ -71,7 +71,7 @@ GraphicsBox {
suit: model.suit || ""
number: model.number || 0
autoBack: false
- known: model.cid !== -1
+ known: model.known
selectable: chosenInBox ||
lcall("PoxiFilter", root.poxi_type, model.cid, root.selected_ids,
root.card_data, root.extra_data);
diff --git a/Fk/RoomElement/TablePile.qml b/Fk/RoomElement/TablePile.qml
index 7de8d1ac..b373425f 100644
--- a/Fk/RoomElement/TablePile.qml
+++ b/Fk/RoomElement/TablePile.qml
@@ -21,7 +21,7 @@ Item {
function inTable(cid) {
return leval(`(function()
local client = Fk:currentRoom()
- if client._processing[${cid}] then
+ if table.contains(client.processing_area, ${cid}) then
return true
end
return false
diff --git a/Fk/main.qml b/Fk/main.qml
index 469fea57..da6b8826 100644
--- a/Fk/main.qml
+++ b/Fk/main.qml
@@ -26,8 +26,6 @@ Window {
? 540 : 960 * parent.height / parent.width
scale: parent.width / width
anchors.centerIn: parent
- property bool is_pending: false
- property var pending_message: []
Config {
id: config
@@ -158,17 +156,7 @@ Window {
errDialog.open();
return;
}
- if (mainWindow.is_pending && command !== "ChangeSelf") {
- mainWindow.pending_message.push({
- command: command,
- jsonData: jsonData,
- });
- } else {
- if (command === "StartChangeSelf") {
- mainWindow.is_pending = true;
- }
- mainWindow.handleMessage(command, jsonData);
- }
+ mainWindow.handleMessage(command, jsonData);
}
}
diff --git a/packages/test/init.lua b/packages/test/init.lua
index 12b8385b..001d4499 100644
--- a/packages/test/init.lua
+++ b/packages/test/init.lua
@@ -356,6 +356,22 @@ test2:addSkill(test_zhenggong)
test2:addSkill(change_hero)
-- test2:addSkill(test_feichu)
+local kansha=fk.CreateVisibilitySkill{
+ name='test_kansha',
+ frequency=Skill.Compulsory,
+ card_visible = function(self, player, card)
+ if player:hasSkill(self) and card.trueName == 'slash' and
+ Fk:currentRoom():getCardArea(card) == Card.PlayerHand then
+ return true
+ end
+ end
+}
+test2:addSkill(kansha)
+Fk:loadTranslationTable{
+ ["test_kansha"] = "看杀",
+ [":test_kansha"] = "锁定技,你看得到人们手中的【杀】"
+}
+
local shibing = General(extension, "blank_shibing", "qun", 5)
shibing.hidden = true
Fk:loadTranslationTable{
diff --git a/src/swig/qt.i b/src/swig/qt.i
index a24de19f..3bf94c3d 100644
--- a/src/swig/qt.i
+++ b/src/swig/qt.i
@@ -50,3 +50,26 @@ public:
QByteArray toJson(QJsonDocument::JsonFormat format = 1) const;
QVariant toVariant() const;
};
+
+class QRandomGenerator {
+public:
+ QRandomGenerator(unsigned int seed = 1);
+ unsigned int generate();
+ unsigned int bounded(unsigned int lowest, unsigned int highest);
+};
+
+%extend QRandomGenerator {
+ QVariant random(int low = -1, int high = -1) {
+ QVariant ret;
+ if (high < 0) {
+ if (low < 1) {
+ ret.setValue($self->bounded(0, 100001) / 100000);
+ } else {
+ ret.setValue($self->bounded(1, low + 1));
+ }
+ } else {
+ ret.setValue($self->bounded(low, high + 1));
+ }
+ return ret;
+ }
+}
diff --git a/test/lua/lib/fk.lua b/test/lua/lib/fk.lua
index 5fc4c449..b4f6c9e9 100644
--- a/test/lua/lib/fk.lua
+++ b/test/lua/lib/fk.lua
@@ -3,6 +3,8 @@
local fk = {}
local testFail = false
+local json = require "lua.lib.json"
+
local os, io = os, io
-- 这下Linux专用了
@@ -13,12 +15,35 @@ end
function fk.QmlBackend_isDir(dir)
local f = io.popen("if [ -d " .. dir .. " ]; then echo OK; fi")
- return f:read("*a"):startsWith("OK")
+ return f:read("*a"):sub(1, 2) == "OK"
end
function fk.QmlBackend_exists(dir)
local f = io.popen("if [ -e " .. dir .. " ]; then echo OK; fi")
- return f:read("*a"):startsWith("OK")
+ return f:read("*a"):sub(1, 2) == "OK"
+end
+
+function fk.QmlBackend_pwd()
+ local f = io.popen("pwd")
+ return f:read("*a")
+end
+
+function fk.QmlBackend_cd(dir) end
+
+function fk.QJsonDocument_fromVariant(e)
+ return {
+ toJson = function(_, __)
+ return json.encode(e)
+ end,
+ }
+end
+
+function fk.QJsonDocument_fromJson(str)
+ return {
+ toVariant = function(_)
+ return json.decode(str)
+ end,
+ }
end
function fk.GetDisabledPacks()
@@ -46,7 +71,7 @@ end
function fk.roomtest(croom, f)
local room = Room(croom)
RoomInstance = room
- room.action = function() f(room) end
+ --room.action = function() f(room) end
while true do
local over = room:resume()
if over then break else room.in_delay = false end
diff --git a/test/lua/lib/room.lua b/test/lua/lib/room.lua
index f6cc166b..6f325774 100644
--- a/test/lua/lib/room.lua
+++ b/test/lua/lib/room.lua
@@ -7,6 +7,11 @@ function Room:getPlayers() return self.players end
function Room:getTimeout() return 15 end
function Room:updateWinRate() end
function Room:gameOver() end
+function Room:setRequestTimer() end
+function Room:destroyRequestTimer() end
+function Room:delay(ms)
+-- fk.io.popen("sleep " .. ms/1000):read()
+end
function Room:settings()
return json.encode{
enableFreeAssign = false,
diff --git a/test/lua/lib/serverplayer.lua b/test/lua/lib/serverplayer.lua
index e62e8db1..4ae3b5f8 100644
--- a/test/lua/lib/serverplayer.lua
+++ b/test/lua/lib/serverplayer.lua
@@ -581,7 +581,7 @@ function ServerPlayer:waitForReply()
return ""
end
function ServerPlayer:doNotify(cmd, j)
- if self.id ~= 1 then
+ if self.id ~= 100 then
return
end
if cmd == "GameLog" then
diff --git a/test/lua/server/logic.lua b/test/lua/server/logic.lua
index e68a23d6..e453319a 100644
--- a/test/lua/server/logic.lua
+++ b/test/lua/server/logic.lua
@@ -5,7 +5,7 @@ _TestGameLogic = {
setup = function()
croom = fk.Room:new()
croom.players = {
- fk.ServerPlayer:new(1),
+ fk.ServerPlayer:new(100),
fk.ServerPlayer:new(2),
fk.ServerPlayer:new(3),
fk.ServerPlayer:new(4),