diff --git a/Fk/LobbyElement/UserInfo.qml b/Fk/LobbyElement/UserInfo.qml index 68549203..c70fa7ac 100644 --- a/Fk/LobbyElement/UserInfo.qml +++ b/Fk/LobbyElement/UserInfo.qml @@ -19,6 +19,11 @@ ColumnLayout { } } + Timer { + id: opTimer + interval: 1000 + } + RowLayout { anchors.rightMargin: 8 spacing: 16 @@ -34,9 +39,10 @@ ColumnLayout { } Button { text: Backend.translate("Update Avatar") - enabled: avatarName.text !== "" + enabled: avatarName.text !== "" && !opTimer.running onClicked: { mainWindow.busy = true; + opTimer.start(); ClientInstance.notifyServer( "UpdateAvatar", JSON.stringify([avatarName.text]) @@ -74,9 +80,10 @@ ColumnLayout { } Button { text: Backend.translate("Update Password") - enabled: oldPassword.text !== "" && newPassword.text !== "" + enabled: oldPassword.text !== "" && newPassword.text !== "" && !opTimer.running onClicked: { mainWindow.busy = true; + opTimer.start(); ClientInstance.notifyServer( "UpdatePassword", JSON.stringify([oldPassword.text, newPassword.text]) diff --git a/Fk/Pages/GeneralsOverview.qml b/Fk/Pages/GeneralsOverview.qml index ad87beb7..51fc56a9 100644 --- a/Fk/Pages/GeneralsOverview.qml +++ b/Fk/Pages/GeneralsOverview.qml @@ -359,6 +359,8 @@ Item { id: word Layout.fillWidth: true clip: true + leftPadding: 5 + rightPadding: 5 } Button { @@ -408,6 +410,24 @@ Item { 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() { diff --git a/Fk/Pages/RoomLogic.js b/Fk/Pages/RoomLogic.js index ca03ed1f..8936a9dd 100644 --- a/Fk/Pages/RoomLogic.js +++ b/Fk/Pages/RoomLogic.js @@ -298,25 +298,29 @@ function resortHandcards() { } dashboard.handcardArea.cards.sort((prev, next) => { - if (prev.type === next.type) { - const prevSubtypeNumber = subtypeString2Number[prev.subtype]; - const nextSubtypeNumber = subtypeString2Number[next.subtype]; - if (prevSubtypeNumber === nextSubtypeNumber) { - const splitedPrevName = prev.name.split('__'); - const prevTrueName = splitedPrevName[splitedPrevName.length - 1]; + if (prev.footnote === next.footnote) { + if (prev.type === next.type) { + const prevSubtypeNumber = subtypeString2Number[prev.subtype]; + const nextSubtypeNumber = subtypeString2Number[next.subtype]; + if (prevSubtypeNumber === nextSubtypeNumber) { + const splitedPrevName = prev.name.split('__'); + const prevTrueName = splitedPrevName[splitedPrevName.length - 1]; - const splitedNextName = next.name.split('__'); - const nextTrueName = splitedNextName[splitedNextName.length - 1]; - if (prevTrueName === nextTrueName) { - return prev.cid - next.cid; + const splitedNextName = next.name.split('__'); + const nextTrueName = splitedNextName[splitedNextName.length - 1]; + if (prevTrueName === nextTrueName) { + return prev.cid - next.cid; + } else { + return prevTrueName > nextTrueName ? -1 : 1; + } } else { - return prevTrueName > nextTrueName ? -1 : 1; + return prevSubtypeNumber - nextSubtypeNumber; } } else { - return prevSubtypeNumber - nextSubtypeNumber; + return prev.type - next.type; } } else { - return prev.type - next.type; + return prev.footnote > next.footnote ? 1 : -1; } }); @@ -1055,13 +1059,18 @@ callbacks["AskForCardChosen"] = (jsonData) => { // string reason ] const data = JSON.parse(jsonData); const reason = data._reason; - - roomScene.promptText = Backend.translate("#AskForChooseCard") - .arg(Backend.translate(reason)); + const prompt = data._prompt; + if (prompt === "") { + roomScene.promptText = Backend.translate("#AskForChooseCard") + .arg(Backend.translate(reason)); + } else { + roomScene.setPrompt(processPrompt(prompt), true); + } roomScene.state = "replying"; roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/PlayerCardBox.qml"); const box = roomScene.popupBox.item; + box.prompt = prompt; for (let d of data.card_data) { const arr = []; const ids = d[1]; @@ -1086,15 +1095,21 @@ callbacks["AskForCardsChosen"] = (jsonData) => { const min = data._min; const max = data._max; const reason = data._reason; - - roomScene.promptText = Backend.translate("#AskForChooseCards") + const prompt = data._prompt; + if (prompt === "") { + roomScene.promptText = Backend.translate("#AskForChooseCards") .arg(Backend.translate(reason)).arg(min).arg(max); + } else { + roomScene.setPrompt(processPrompt(prompt), true); + } + roomScene.state = "replying"; roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/PlayerCardBox.qml"); const box = roomScene.popupBox.item; box.multiChoose = true; box.min = min; box.max = max; + box.prompt = prompt; for (let d of data.card_data) { const arr = []; const ids = d[1]; @@ -1384,7 +1399,7 @@ callbacks["Animate"] = (jsonData) => { roomScene.bigAnim.source = "../RoomElement/UltSkillAnimation.qml"; roomScene.bigAnim.item.loadData({ skill_name: data.name, - general: photo.general, + general: data.deputy ? photo.deputyGeneral : photo.general, }); break; } diff --git a/Fk/RoomElement/GuanxingBox.qml b/Fk/RoomElement/GuanxingBox.qml index 01a07e38..b70d34d9 100644 --- a/Fk/RoomElement/GuanxingBox.qml +++ b/Fk/RoomElement/GuanxingBox.qml @@ -100,6 +100,7 @@ GraphicsBox { name: modelData.name suit: modelData.suit number: modelData.number + mark: modelData.mark draggable: true onReleased: arrangeCards(); } diff --git a/Fk/RoomElement/PlayerCardBox.qml b/Fk/RoomElement/PlayerCardBox.qml index e9d4a5bf..1139dd8f 100644 --- a/Fk/RoomElement/PlayerCardBox.qml +++ b/Fk/RoomElement/PlayerCardBox.qml @@ -6,8 +6,9 @@ import Fk.Pages GraphicsBox { id: root + property string prompt - title.text: root.multiChoose ? Backend.translate("$ChooseCards").arg(root.min).arg(root.max) : Backend.translate("$ChooseCard") + title.text: prompt === "" ? (root.multiChoose ? Backend.translate("$ChooseCards").arg(root.min).arg(root.max) : Backend.translate("$ChooseCard")) : processPrompt(prompt) // TODO: Adjust the UI design in case there are more than 7 cards width: 70 + 700 @@ -102,6 +103,18 @@ GraphicsBox { 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) { let ret; for (let i = 0; i < cardModel.count; i++) { diff --git a/Fk/RoomElement/UltSkillAnimation.qml b/Fk/RoomElement/UltSkillAnimation.qml index 6812333c..9c95489b 100644 --- a/Fk/RoomElement/UltSkillAnimation.qml +++ b/Fk/RoomElement/UltSkillAnimation.qml @@ -27,8 +27,13 @@ Item { model: 40 Text { text: { - const o = "$" + skillName + (index % 2 + 1); - const p = Backend.translate(o); + let o = "$" + skillName + "_" + generalName + (index % 2 + 1); + let p = Backend.translate(o); + if (o !== p) { + return p; + } + o = "$" + skillName + (index % 2 + 1); + p = Backend.translate(o); if (o === p) { return "Ultimate Skill Invoked!"; } @@ -53,8 +58,13 @@ Item { model: 40 Text { text: { - const o = "$" + skillName + ((index + 1) % 2 + 1); - const p = Backend.translate(o); + let o = "$" + skillName + "_" + generalName + ((index + 1) % 2 + 1); + let p = Backend.translate(o); + if (o !== p) { + return p; + } + o = "$" + skillName + ((index + 1) % 2 + 1); + p = Backend.translate(o); if (o === p) { return "Ultimate Skill Invoked!"; } diff --git a/lua/client/client.lua b/lua/client/client.lua index 81c68554..1fd6aa20 100644 --- a/lua/client/client.lua +++ b/lua/client/client.lua @@ -370,7 +370,7 @@ end fk.client_callback["AskForCardChosen"] = function(jsonData) -- jsonData: [ int target_id, string flag, int reason ] local data = json.decode(jsonData) - local id, flag, reason = data[1], data[2], data[3] + local id, flag, reason, prompt = data[1], data[2], data[3], data[4] local target = ClientInstance:getPlayerById(id) local hand = target.player_cards[Player.Hand] local equip = target.player_cards[Player.Equip] @@ -390,12 +390,14 @@ fk.client_callback["AskForCardChosen"] = function(jsonData) ui_data = { _reason = reason, card_data = {}, + _prompt = prompt, } if #hand ~= 0 then table.insert(ui_data.card_data, { "$Hand", hand }) 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 else ui_data._reason = reason + ui_data._prompt = prompt end ClientInstance:notifyUI("AskForCardChosen", json.encode(ui_data)) end @@ -403,7 +405,7 @@ end fk.client_callback["AskForCardsChosen"] = function(jsonData) -- jsonData: [ int target_id, int min, int max, string flag, int reason ] local data = json.decode(jsonData) - local id, min, max, flag, reason = data[1], data[2], data[3], data[4], data[5] + local id, min, max, flag, reason, prompt = data[1], data[2], data[3], data[4], data[5], data[6] local target = ClientInstance:getPlayerById(id) local hand = target.player_cards[Player.Hand] local equip = target.player_cards[Player.Equip] @@ -424,7 +426,8 @@ fk.client_callback["AskForCardsChosen"] = function(jsonData) _min = min, _max = max, _reason = reason, - card_data = {} + card_data = {}, + _prompt = prompt, } if #hand ~= 0 then table.insert(ui_data.card_data, { "$Hand", hand }) end if #equip ~= 0 then table.insert(ui_data.card_data, { "$Equip", equip }) end @@ -433,6 +436,7 @@ fk.client_callback["AskForCardsChosen"] = function(jsonData) ui_data._min = min ui_data._max = max ui_data._reason = reason + ui_data._prompt = prompt end ClientInstance:notifyUI("AskForCardsChosen", json.encode(ui_data)) end diff --git a/lua/client/client_util.lua b/lua/client/client_util.lua index 2f47cc00..adc00859 100644 --- a/lua/client/client_util.lua +++ b/lua/client/client_util.lua @@ -546,7 +546,7 @@ function CardProhibitedResponse(card) if c == nil then return "true" else - ret = Self:prohibitUse(c) + ret = Self:prohibitResponse(c) end return json.encode(ret) end diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index 4be3b9d8..b5e73754 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -25,6 +25,7 @@ Fk:loadTranslationTable{ ["Disable message audio"] = "禁用聊天语音", ["Hide unselectable cards"] = "下移不可选卡牌", ["Ban General Settings"] = "禁将", + ["Set as Avatar"] = "设为头像", ["Search"] = "搜索", ["Back"] = "返回", diff --git a/lua/server/ai/random_ai.lua b/lua/server/ai/random_ai.lua index b93ff28e..a2c39951 100644 --- a/lua/server/ai/random_ai.lua +++ b/lua/server/ai/random_ai.lua @@ -15,7 +15,7 @@ local function useActiveSkill(self, skill, card) filter_func = function() return false end end - if self.command == "PlayCard" and (not skill:canUse(player, card) or player:prohibitUse(card)) then + if self.command == "PlayCard" and card and (not player:canUse(card) or player:prohibitUse(card)) then return "" end @@ -31,7 +31,7 @@ local function useActiveSkill(self, skill, card) local avail_targets = table.filter(self.room:getAlivePlayers(), function(p) local ret = skill:targetFilter(p.id, selected_targets, selected_cards, card or Fk:cloneCard'zixing') if ret and card then - if player:prohibitUse(card) then + if player:isProhibited(p, card) then ret = false end end @@ -135,8 +135,7 @@ random_cb["AskForUseCard"] = function(self, jsonData) local cancelable = data[4] or true local exp = Exppattern:Parse(pattern) - local avail_cards = table.map(player:getCardIds("he"), - function(id) return Fk:getCardById(id) end) + local avail_cards = table.map(player:getCardIds("he"), Util.Id2CardMapper) avail_cards = table.filter(avail_cards, function(c) return exp:match(c) and not player:prohibitUse(c) end) @@ -187,8 +186,7 @@ random_cb["AskForResponseCard"] = function(self, jsonData) end random_cb["PlayCard"] = function(self, jsonData) - local cards = table.map(self.player:getCardIds(Player.Hand), - function(id) return Fk:getCardById(id) end) + local cards = table.map(self.player:getCardIds(Player.Hand), Util.Id2CardMapper) local actives = table.filter(self.player:getAllSkills(), function(s) return s:isInstanceOf(ActiveSkill) end) diff --git a/lua/server/gameevent.lua b/lua/server/gameevent.lua index 23304f8f..2b2c494a 100644 --- a/lua/server/gameevent.lua +++ b/lua/server/gameevent.lua @@ -143,7 +143,7 @@ function GameEvent:searchEvents(eventType, n, func, endEvent) local events = logic.event_recorder[eventType] or Util.DummyTable local from = self.id local to = endEvent and endEvent.id or self.end_id - if to == -1 then to = #logic.all_game_events end + if math.abs(to) == 1 then to = #logic.all_game_events end n = n or 1 func = func or Util.TrueFunc diff --git a/lua/server/room.lua b/lua/server/room.lua index d1296bba..810ad5c8 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -1020,6 +1020,7 @@ function Room:notifySkillInvoked(player, skill_name, skill_type) self:doAnimate("InvokeUltSkill", { name = skill_name, player = player.id, + deputy = player.deputyGeneral and player.deputyGeneral ~= "" and table.contains(Fk.generals[player.deputyGeneral]:getSkillNameList(), skill_name), }) self:delay(2000) end @@ -1520,11 +1521,13 @@ end ---@param target ServerPlayer @ 被选牌的人 ---@param flag any @ 用"hej"三个字母的组合表示能选择哪些区域, h 手牌区, e - 装备区, j - 判定区 ---@param reason string @ 原因,一般是技能名 +---@param prompt string|nil @ 提示信息 ---@return integer @ 选择的卡牌id -function Room:askForCardChosen(chooser, target, flag, reason) +function Room:askForCardChosen(chooser, target, flag, reason, prompt) local command = "AskForCardChosen" + prompt = prompt or "" self:notifyMoveFocus(chooser, command) - local data = {target.id, flag, reason} + local data = {target.id, flag, reason, prompt} local result = self:doRequest(chooser, command, json.encode(data)) if result == "" then @@ -1564,15 +1567,17 @@ end ---@param max integer @ 最大选牌数 ---@param flag any @ 用"hej"三个字母的组合表示能选择哪些区域, h 手牌区, e - 装备区, j - 判定区 ---@param reason string @ 原因,一般是技能名 +---@param prompt string|nil @ 提示信息 ---@return integer[] @ 选择的id -function Room:askForCardsChosen(chooser, target, min, max, flag, reason) +function Room:askForCardsChosen(chooser, target, min, max, flag, reason, prompt) if min == 1 and max == 1 then return { self:askForCardChosen(chooser, target, flag, reason) } end local command = "AskForCardsChosen" + prompt = prompt or "" self:notifyMoveFocus(chooser, command) - local data = {target.id, min, max, flag, reason} + local data = {target.id, min, max, flag, reason, prompt} local result = self:doRequest(chooser, command, json.encode(data)) local ret diff --git a/lua/server/serverplayer.lua b/lua/server/serverplayer.lua index ee7fcafe..edfb21a9 100644 --- a/lua/server/serverplayer.lua +++ b/lua/server/serverplayer.lua @@ -920,7 +920,28 @@ function ServerPlayer:revealGeneral(isDeputy, no_trigger) } if not no_trigger then - room.logic:trigger(fk.GeneralRevealed, self, generalName) + local current_event = room.logic:getCurrentEvent() + if table.contains({GameEvent.Round, GameEvent.Turn, GameEvent.Phase}, current_event.event) then + room.logic:trigger(fk.GeneralRevealed, self, {[isDeputy and "d" or "m"] = generalName}) + else + current_event:addExitFunc(function () + room.logic:trigger(fk.GeneralRevealed, self, {[isDeputy and "d" or "m"] = generalName}) + end) + end + end +end + +function ServerPlayer:revealGenerals() + self:revealGeneral(false, true) + self:revealGeneral(true, true) + local room = self.room + local current_event = room.logic:getCurrentEvent() + if table.contains({GameEvent.Round, GameEvent.Turn, GameEvent.Phase}, current_event.event) then + room.logic:trigger(fk.GeneralRevealed, self, {["m"] = self:getMark("__heg_general"), ["d"] = self:getMark("__heg_deputy")}) + else + current_event:addExitFunc(function () + room.logic:trigger(fk.GeneralRevealed, self, {["m"] = self:getMark("__heg_general"), ["d"] = self:getMark("__heg_deputy")}) + end) end end diff --git a/packages/standard/aux_skills.lua b/packages/standard/aux_skills.lua index ba2e878f..8d54d6c4 100644 --- a/packages/standard/aux_skills.lua +++ b/packages/standard/aux_skills.lua @@ -184,8 +184,13 @@ local revealProhibited = fk.CreateInvaliditySkill { if type(from._fake_skills) == "table" and not table.contains(from._fake_skills, skill) then return false end local sname = skill.name for _, g in ipairs(generals) do - local ret = g == "m" and from:getMark("__heg_general") or from:getMark("__heg_deputy") - local general = Fk.generals[ret] + if g == "m" then + if from.general ~= "anjiang" then return false end + else + if from.deputyGeneral ~= "anjiang" then return false end + end + local generalName = g == "m" and from:getMark("__heg_general") or from:getMark("__heg_deputy") + local general = Fk.generals[generalName] if table.contains(general:getSkillNameList(), sname) then return true end