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),