diff --git a/Fk/Pages/RoomLogic.js b/Fk/Pages/RoomLogic.js index 46fb9508..bbf2fc29 100644 --- a/Fk/Pages/RoomLogic.js +++ b/Fk/Pages/RoomLogic.js @@ -1192,6 +1192,46 @@ callbacks["AskForPoxi"] = (jsonData) => { }); } +callbacks["AskForYuqi"] = (jsonData) => { + const { type, data, extra_data, cancelable } = JSON.parse(jsonData); + + roomScene.state = "replying"; + roomScene.popupBox.sourceComponent = + Qt.createComponent("../RoomElement/YuqiBox.qml"); + const box = roomScene.popupBox.item; + const area_names = []; + const card_data = []; + const card_items = []; + const carditems = []; + for (let d of data) { + const arr = d[0]; + const card_arr = []; + const ids = d[1]; + + area_names.push(arr); + card_data.push(ids); + ids.forEach(id => { + const cdata = lcall("GetCardData", id) + card_arr.push(cdata); + carditems.push(cdata); + }); + card_items.push(card_arr); + } + box.yuqi_type = type; + box.areaNames = area_names; + box.pilecards = card_data; + box.result = card_items; + box.extra_data = extra_data; + box.cancelable = cancelable; + + box.cards = carditems; + box.arrangeCards(); + roomScene.popupBox.moveToCenter(); + box.cardsSelected.connect((ids) => { + replyToServer(JSON.stringify(ids)); + }); +} + callbacks["AskForMoveCardInBoard"] = (jsonData) => { const data = JSON.parse(jsonData); const { cards, cardsPosition, generalNames, playerIds } = data; diff --git a/Fk/RoomElement/YuqiBox.qml b/Fk/RoomElement/YuqiBox.qml index 7f7bd8bf..340406cf 100644 --- a/Fk/RoomElement/YuqiBox.qml +++ b/Fk/RoomElement/YuqiBox.qml @@ -7,7 +7,7 @@ import Fk.Pages GraphicsBox { id: root - property string Yuqi_type + property string yuqi_type property var cards: [] //全体卡牌枚举 property var result: [] //最终牌堆 property var pilecards: [] //初始牌堆 @@ -16,6 +16,8 @@ GraphicsBox { property var extra_data property int padding: 25 + signal cardsSelected(var ids) + title.text: "" width: body.width + padding * 2 @@ -63,7 +65,7 @@ GraphicsBox { height: 130 } - // property alias cardsArea: cardsArea + property alias cardsArea: cardsArea } } @@ -77,9 +79,9 @@ GraphicsBox { width: 120 height: 35 text: luatr("OK") - enabled: lcall("YuqiFeasible", root.Yuqi_type, root.selected_ids, - root.card_data, root.extra_data); - onClicked: root.cardsSelected(findAllModel()) + enabled: lcall("YuqiFeasible", root.yuqi_type, root.getResult(), + root.pilecards, root.extra_data); + onClicked: root.cardsSelected(root.getResult()) } MetroButton { @@ -87,14 +89,14 @@ GraphicsBox { height: 35 text: luatr("Cancel") visible: root.cancelable - onClicked: root.cardsSelected(card_data) + onClicked: root.cardsSelected([]) } } } Repeater { - id: cardItem + id: cardsItem model: cards CardItem { @@ -105,16 +107,38 @@ GraphicsBox { suit: modelData.suit number: modelData.number draggable: true - onReleased: updateCardsReleased(); + onReleased: updateCardsReleased(this); } } - function updateCardsReleased() { - for (i = 0; i < cardItem.count; i++) { - _card = result[0][i] - if (Math.abs(card.x - _card.x) <= 50) { - result[1][result[1].indexOf(card)] = _card; - result[0][i] = card; + function updateCardsReleased(card) { + let orig, from, to; + let i, j; + const result_cards = getResult(); + for (i = 0; i < pilecards.count; i++) { + const _pile = result[i]; + const box = pile.cardsArea; + const pos = mapFromItem(pile, box.x, box.y); + const posid = _pile.indexOf(card.cid) + if (posid !== -1) { + from = i; + orig = posid; + } + const spacing = (_pile.length > 8) ? (700 / (_pile.length - 1)) : 100 + if (Math.abs(card.y - pos.y) <= spacing / 2) { + to = i + } + if (from !== null && to !== null) { + if (pilecards[to].indexOf(card.cid) === -1 && !lcall("YuqiEntryFilter", root.yuqi_type, card.cid, from, to, + result_cards, root.extra_data) ) break; + result[from].splice(orig, 1) + for (j = 0; j < result[0].length; j++) { + let _card = result[orig][i] + if (Math.abs(card.x - _card.x) <= card.width / 2) { + result[to].splice(j, 0, card.cid); + break; + } + } break; } } @@ -124,7 +148,8 @@ GraphicsBox { function arrangeCards() { let i, j; let card, box, pos, pile; - let spacing + let spacing; + const result_cards = getResult(); for (j = 0; j < pilecards.length; j++){ pile = areaRepeater.itemAt(j); if (pile.y === 0){ @@ -135,7 +160,8 @@ GraphicsBox { box = pile.cardsArea; pos = mapFromItem(pile, box.x, box.y); card = result[j][i]; - card.draggable = (j > 0) + card.draggable = lcall("YuqiOutFilter", root.yuqi_type, card.cid, j, + result_cards, root.extra_data); card.origX = pos.x + i * spacing; card.origY = pos.y; card.z = i + 1; @@ -147,7 +173,17 @@ GraphicsBox { refreshPrompt(); } + function getResult() { + const ret = []; + result.forEach(t => { + const t2 = []; + t.forEach(v => t2.push(v.cid)); + ret.push(t2); + }); + return ret; + } + function refreshPrompt() { - root.title.text = Util.processPrompt(lcall("YuqiPrompt", Yuqi_type, card_data, extra_data)) + root.title.text = Util.processPrompt(lcall("YuqiPrompt", yuqi_type, root.result, root.pilecards, extra_data)) } } diff --git a/lua/client/client_util.lua b/lua/client/client_util.lua index 01128f82..a0a8ca5b 100644 --- a/lua/client/client_util.lua +++ b/lua/client/client_util.lua @@ -791,13 +791,13 @@ end function YuqiEntryFilter(yuqi_type, to_select, from_pile, to_pile, data, extra_data) local yuqi = Fk.yuqi_methods[yuqi_type] if not yuqi then return "false" end - return json.encode(yuqi.entry_filter(to_select, from_pile, to_pile, data, extra_data)) + return json.encode(yuqi.entry_filter(to_select, from_pile + 1, to_pile + 1, data, extra_data)) end function YuqiOutFilter(yuqi_type, to_select, from_pile, data, extra_data) local yuqi = Fk.yuqi_methods[yuqi_type] if not yuqi then return "false" end - return json.encode(yuqi.out_filter(to_select, from_pile, data, extra_data)) + return json.encode(yuqi.out_filter(to_select, from_pile + 1, data, extra_data)) end function YuqiFeasible(yuqi_type, current, origin, extra_data) diff --git a/lua/core/engine.lua b/lua/core/engine.lua index b25df73d..222d8ec0 100644 --- a/lua/core/engine.lua +++ b/lua/core/engine.lua @@ -72,6 +72,7 @@ function Engine:initialize() self.poxi_methods = {} self.qml_marks = {} self.mini_games = {} + self.yuqi_methods = {} self:loadPackages() self:loadDisabled() @@ -396,10 +397,10 @@ end ---@param spec YuqiSpec function Engine:addYuqiMethod(spec) assert(type(spec.name) == "string") - assert(type(spec.feasible) == "function") self.yuqi_methods[spec.name] = spec - spec.entry_filter = spec.entry_filter or function() return true end - spec.out_filter = spec.out_filter or function() return true end + spec.entry_filter = spec.entry_filter or Util.TrueFunc + spec.out_filter = spec.out_filter or Util.TrueFunc + spec.feasible = spec.feasible or Util.TrueFunc end --- 从已经开启的拓展包中,随机选出若干名武将。 diff --git a/lua/fk_ex.lua b/lua/fk_ex.lua index af483e3f..d473d984 100644 --- a/lua/fk_ex.lua +++ b/lua/fk_ex.lua @@ -635,7 +635,7 @@ end ---@class YuqiSpec ---@field name string ----@field feasible fun(current_data: any, old_data: any, extra_data: any): bool ----@field entry_filter fun(card: int, from_pile: int, to_pile: int, data: any, extra_data: any): bool ----@field out_filter fun(card: int, from_pile: int, data: any, extra_data: any): bool +---@field feasible? fun(current_data: any, old_data: any, extra_data: any): bool +---@field entry_filter? fun(card: int, from_pile: int, to_pile: int, data: any, extra_data: any): bool +---@field out_filter? fun(card: int, from_pile: int, data: any, extra_data: any): bool ---@field prompt? string | fun(data: any, extra_data: any): string diff --git a/lua/server/room.lua b/lua/server/room.lua index 5f6b9688..527de6c4 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -1756,6 +1756,31 @@ function Room:askForPoxi(player, poxi_type, data, extra_data, cancelable) end end +--- 咕了太久的隅泣 +--- +--- data填入所有卡的列表(类似ui.card_data) +---@param player ServerPlayer +---@param yuqi_type string +---@param data any +---@param extra_data any +---@param cancelable? boolean +---@return integer[] +function Room:askForYuqi(player, yuqi_type, data, extra_data, cancelable) + local yuqi = Fk.yuqi_methods[yuqi_type] + if not yuqi then return {} end + + local command = "AskForYuqi" + self:notifyMoveFocus(player, yuqi_type) + local result = self:doRequest(player, command, json.encode { + type = yuqi_type, + data = data, + extra_data = extra_data, + cancelable = (cancelable == nil) and true or cancelable + }) + + return result == "" and {} or json.decode(result) +end + --- 询问一名玩家从众多选项中选择一个。 ---@param player ServerPlayer @ 要询问的玩家 ---@param choices string[] @ 可选选项列表 diff --git a/packages/test/init.lua b/packages/test/init.lua index 802b1535..9db7ea75 100644 --- a/packages/test/init.lua +++ b/packages/test/init.lua @@ -83,6 +83,11 @@ local control = fk.CreateActiveSkill{ -- p(room:askForYiji(from, from:getCardIds(Player.Hand), table.map(effect.tos, Util.Id2PlayerMapper), self.name, 2, 10, nil, false, nil, false, 3, true)) for _, pid in ipairs(effect.tos) do local to = room:getPlayerById(pid) + p(room:askForYuqi(from, "test", { + {"牌堆顶", table.slice(room.draw_pile, 1, 5)}, + {"你自己", {}}, + {"对方", {}}, + }, to.hp, true)) -- p(room:askForPoxi(from, "test", { -- { "你自己", from:getCardIds "h" }, -- { "对方", to:getCardIds "h" }, @@ -130,6 +135,16 @@ local control = fk.CreateActiveSkill{ -- room:useVirtualCard("slash", nil, from, room:getOtherPlayers(from), self.name, true) end, } +Fk:addYuqiMethod{ + name = "test", + prompt = "隅泣:请分配这些卡牌", + entry_filter = function(card, from_pile, to_pile, data, extra_data) + if table.contains({2, 3}, to_pile) and #data[to_pile] >= 5 then + return false + end + return true + end +} --[[ Fk:addMiniGame{ name = "test",