From 51a10ebcf46359d8d124e5cc40c4b634d8b9aa47 Mon Sep 17 00:00:00 2001 From: notify Date: Wed, 10 Jan 2024 22:51:29 +0800 Subject: [PATCH 01/30] =?UTF-8?q?=E4=BF=AE=E5=B0=8Fbug=EF=BC=8C=E5=88=86?= =?UTF-8?q?=E7=A6=BB=E4=BA=8B=E4=BB=B6=E6=A0=88=20(#303)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lua/core/util.lua | 4 +- lua/server/events/init.lua | 12 ++- lua/server/events/misc.lua | 26 +++++ lua/server/gameevent.lua | 184 +++--------------------------------- lua/server/gamelogic.lua | 138 ++++++++++++++++++++++++++- lua/server/room.lua | 8 +- lua/server/scheduler.lua | 6 ++ lua/server/serverplayer.lua | 10 ++ src/swig/qt.i | 2 +- 9 files changed, 205 insertions(+), 185 deletions(-) diff --git a/lua/core/util.lua b/lua/core/util.lua index ded584c7..975c5dd2 100644 --- a/lua/core/util.lua +++ b/lua/core/util.lua @@ -505,7 +505,9 @@ end function Stack:pop() if self.p == 0 then return nil end self.p = self.p - 1 - return self.t[self.p + 1] + local ret = self.t[self.p + 1] + self.t[self.p + 1] = nil + return ret end diff --git a/lua/server/events/init.lua b/lua/server/events/init.lua index a5e308f5..dbbe92b5 100644 --- a/lua/server/events/init.lua +++ b/lua/server/events/init.lua @@ -5,6 +5,8 @@ -- 某类事件对应的结束事件,其id刚好就是那个事件的相反数 -- GameEvent.EventFinish = -1 +GameEvent.Game = 0 + GameEvent.ChangeHp = 1 GameEvent.Damage = 2 GameEvent.LoseHp = 3 @@ -44,10 +46,10 @@ dofile "lua/server/events/pindian.lua" -- 20 = CardEffect GameEvent.ChangeProperty = 21 -dofile "lua/server/events/misc.lua" --- TODO: fix this -GameEvent.BreakEvent = 999 +-- 新的clear函数专用 +GameEvent.ClearEvent = 9999 +dofile "lua/server/events/misc.lua" for _, l in ipairs(Fk._custom_events) do local name, p, m, c, e = l.name, l.p, l.m, l.c, l.e @@ -58,6 +60,8 @@ for _, l in ipairs(Fk._custom_events) do end local eventTranslations = { + [GameEvent.Game] = "GameEvent.Game", + [GameEvent.ChangeHp] = "GameEvent.ChangeHp", [GameEvent.Damage] = "GameEvent.Damage", [GameEvent.LoseHp] = "GameEvent.LoseHp", @@ -80,7 +84,7 @@ local eventTranslations = { [GameEvent.ChangeProperty] = "GameEvent.ChangeProperty", - [GameEvent.BreakEvent] = "GameEvent.BreakEvent", + [GameEvent.ClearEvent] = "GameEvent.ClearEvent", } function GameEvent.static:translate(id) diff --git a/lua/server/events/misc.lua b/lua/server/events/misc.lua index ff30ffbc..6b73e3e4 100644 --- a/lua/server/events/misc.lua +++ b/lua/server/events/misc.lua @@ -1,5 +1,9 @@ -- SPDX-License-Identifier: GPL-3.0-or-later +GameEvent.functions[GameEvent.Game] = function(self) + self.room.logic:run() +end + GameEvent.functions[GameEvent.ChangeProperty] = function(self) local data = table.unpack(self.data) local room = self.room @@ -120,3 +124,25 @@ GameEvent.functions[GameEvent.ChangeProperty] = function(self) logic:trigger(fk.AfterPropertyChange, player, data) end + +GameEvent.functions[GameEvent.ClearEvent] = function(self) + local event = self.data[1] + local logic = self.room.logic + event:clear_func() + for _, f in ipairs(event.extra_clear_funcs) do + if type(f) == "function" then f(event) end + end + + -- cleaner顺利执行完了,出栈吧 + local end_id = logic.current_event_id + 1 + if event.id ~= end_id - 1 then + logic.all_game_events[end_id] = event.event + logic.current_event_id = end_id + event.end_id = end_id + else + event.end_id = event.id + end + + logic.game_event_stack:pop() + logic.cleaner_stack:pop() +end diff --git a/lua/server/gameevent.lua b/lua/server/gameevent.lua index f7d3840b..1a1e36b6 100644 --- a/lua/server/gameevent.lua +++ b/lua/server/gameevent.lua @@ -13,9 +13,9 @@ ---@field public extra_clear_funcs fun(self:GameEvent)[] @ 事件结束时执行的自定义函数列表 ---@field public exit_func fun(self: GameEvent) @ 事件结束后执行的函数 ---@field public extra_exit_funcs fun(self:GameEvent)[] @ 事件结束后执行的自定义函数 +---@field public exec_ret boolean? @ exec函数的返回值,可能不存在 ---@field public interrupted boolean @ 事件是否是因为被中断而结束的,可能是防止事件或者被杀 ---@field public killed boolean @ 事件因为终止一切结算而被中断(所谓的“被杀”) ----@field public revived boolean @ 事件被killed,但因为在cleaner中发生而被复活 local GameEvent = class("GameEvent") ---@type (fun(self: GameEvent): bool)[] @@ -163,190 +163,28 @@ function GameEvent:searchEvents(eventType, n, func, endEvent) return ret end -function GameEvent:clear() - local clear_co = coroutine.create(function() - self:clear_func() - for _, f in ipairs(self.extra_clear_funcs) do - if type(f) == "function" then f(self) end - end - end) +function GameEvent:exec() + local room = self.room + local logic = room.logic + self.parent = logic:getCurrentEvent() - local zhuran_jmp, zhuran_msg -- SB老朱然 + if self:prepare_func() then return true end - while true do - local err, yield_result, extra_yield_result = coroutine.resume(clear_co) + logic:pushEvent(self) - if err == false then - -- handle error, then break - if not string.find(yield_result, "__manuallyBreak") then - fk.qCritical(yield_result .. "\n" .. debug.traceback(clear_co)) - end - coroutine.close(clear_co) - break - end + local co = coroutine.create(self.main_func) + self._co = co - if yield_result == "__handleRequest" then - -- yield to requestLoop - coroutine.yield(yield_result, extra_yield_result) + coroutine.yield(self, "__newEvent") - elseif type(yield_result) == "table" and yield_result.class - and yield_result:isInstanceOf(GameEvent) and self ~= yield_result then - - -- 不是,谁TM还在cleaner里面玩老朱然啊 - -- 总之,cleaner不能断 - -- 倒是没必要手动resume,新一轮while true会自动resume,只要把返回值 - -- 传回去就行 - - -- 一般来说都是由cleaner中的trigger引起 - -- 以胆守合击为例就是trigger -> SkillEffect事件 -> UseCard事件 -> 胆守 - -- 此时胆守的话最后从SkillEffect事件的exec内部yield出来 - -- 当前协程就应该正在执行room:useSkill函数,resume会去只会让那个函数返回 - - if zhuran_jmp == nil or zhuran_jmp.id > yield_result.id then - zhuran_jmp = yield_result - zhuran_msg = extra_yield_result - end - - -- 自己本来应该被杀的但是因为自己正在执行self:clear()而逃过一劫啊 - -- 还是得标记一下被杀才行,顺便因为实际上没死所以标记被复活 - self.killed = true - self.revived = true - -- 什么都不做,等下轮while自己resume - else - coroutine.close(clear_co) - break - end - end - - -- cleaner顺利执行完了,出栈吧 - local logic = RoomInstance.logic - local end_id = logic.current_event_id + 1 - if self.id ~= end_id - 1 then - logic.all_game_events[end_id] = self.event - logic.current_event_id = end_id - self.end_id = end_id - else - self.end_id = self.id - end - - logic.game_event_stack:pop() - - -- 好了确保cleaner走完了,此时中断就会进入下层事件的正常中断处理 - if zhuran_jmp then - coroutine.close(self._co) - coroutine.yield(zhuran_jmp, zhuran_msg) - - -- 此时仍可能出现在插结在其他事件的clear函数中 - -- 但就算被交付回去了,也能安然返回而不是继续while - -- 但愿如此吧 - end - - -- 保险而已,其实如果被杀的话应该已经在前面的yield终止了 - -- 但担心cleaner嵌套(三国杀是这样的)还是补一刀 - if self.killed then return end - - -- 恭喜没被杀掉,我们来执行一些事件结束之后的结算吧 Pcall(self.exit_func, self) for _, f in ipairs(self.extra_exit_funcs) do if type(f) == "function" then Pcall(f, self) end end -end -local function breakEvent(self, extra_yield_result) - local cancelEvent = GameEvent:new(GameEvent.BreakEvent, self) - cancelEvent.toId = self.id - local notcanceled = cancelEvent:exec() - local ret, extra_ret = false, nil - if not notcanceled then - self.interrupted = true - self:clear() - ret = true - extra_ret = extra_yield_result - end - return ret, extra_ret -end - -function GameEvent:exec() - local room = self.room - local logic = room.logic - local ret = false -- false or nil means this event is running normally - local extra_ret - self.parent = logic:getCurrentEvent() - - if self:prepare_func() then return true end - - logic.game_event_stack:push(self) - - logic.current_event_id = logic.current_event_id + 1 - self.id = logic.current_event_id - logic.all_game_events[self.id] = self - logic.event_recorder[self.event] = logic.event_recorder[self.event] or {} - table.insert(logic.event_recorder[self.event], self) - - local co = coroutine.create(self.main_func) - self._co = co - while true do - local err, yield_result, extra_yield_result = coroutine.resume(co) - - if err == false then - -- handle error, then break - if not string.find(yield_result, "__manuallyBreak") then - fk.qCritical(yield_result .. "\n" .. debug.traceback(co)) - end - self.interrupted = true - self:clear() - ret = true - coroutine.close(co) - break - end - - if yield_result == "__handleRequest" then - -- yield to requestLoop - coroutine.yield(yield_result, extra_yield_result) - - elseif type(yield_result) == "table" and yield_result.class - and yield_result:isInstanceOf(GameEvent) then - - if self ~= yield_result then - -- yield to corresponding GameEvent, first pop self from stack - self.interrupted = true - self.killed = true -- 老朱然!你不得好死 - self:clear() - -- logic.game_event_stack:pop(self) - coroutine.close(co) - - -- then, call yield - coroutine.yield(yield_result, extra_yield_result) - - -- 如果是在cleaner/exit里面发生此类中断的话是会被cleaner原地返回的 - -- 此时正常执行程序流就变成继续while循环了,这是不行的 - break - elseif extra_yield_result == "__breakEvent" then - if breakEvent(self) then - coroutine.close(co) - break - end - end - - elseif yield_result == "__breakEvent" then - -- try to break this event - if breakEvent(self) then - coroutine.close(co) - break - end - - else - -- normally exit, simply break the loop - self:clear() - extra_ret = yield_result - coroutine.close(co) - break - end - end - - return ret, extra_ret + return self.interrupted, self.exec_ret end function GameEvent:shutdown() diff --git a/lua/server/gamelogic.lua b/lua/server/gamelogic.lua index 6818aedf..0f8f184c 100644 --- a/lua/server/gamelogic.lua +++ b/lua/server/gamelogic.lua @@ -7,6 +7,7 @@ ---@field public refresh_skill_table table ---@field public skills string[] ---@field public game_event_stack Stack +---@field public cleaner_stack Stack ---@field public role_table string[][] ---@field public all_game_events GameEvent[] ---@field public event_recorder table @@ -20,6 +21,7 @@ function GameLogic:initialize(room) self.refresh_skill_table = {} self.skills = {} -- skillName[] self.game_event_stack = Stack:new() + self.cleaner_stack = Stack:new() self.all_game_events = {} self.event_recorder = {} self.current_event_id = 0 @@ -389,9 +391,7 @@ function GameLogic:trigger(event, target, data, refresh_only) skill_names = table.map(table.filter(skills, filter_func), Util.NameMapper) broken = broken or (event == fk.AskForPeaches - and room:getPlayerById(data.who).hp > 0) or cur_event.revived - -- ^^^^^^^^^^^^^^^^^ - -- 如果事件复活了,那么其实说明事件已经死过了,赶紧break掉 + and room:getPlayerById(data.who).hp > 0) or cur_event.killed if broken then break end end @@ -408,6 +408,138 @@ function GameLogic:trigger(event, target, data, refresh_only) return broken end +-- 此为启动事件管理器并启动第一个事件的初始函数 +function GameLogic:start() + local root_event = GameEvent:new(GameEvent.Game) + + self:pushEvent(root_event) + + -- 此时的协程:room.main_co + -- 事件管理器协程,同时也是Game事件 + -- 当新事件想要exec时,就切回此处,由这里负责调度协程 + -- 一个事件结束后也切回此处,然后resume + local co = coroutine.create(root_event.main_func) + root_event._co = co + + local jump_to -- shutdown函数用 + + while true do + -- 对于cleaner和正常事件,处理更后面来的 + local ne = self:getCurrentEvent() + local ce = self:getCurrentCleaner() + local e = ce and (ce.id >= ne.id and ce or ne) or ne + + -- 如果正在jump的话,判断是否需要继续clean,否则正常继续 + if e == ne and jump_to ~= nil then + e.interrupted = true + e.killed = e ~= jump_to + self:clearEvent(e) + coroutine.close(e._co) + if e == jump_to then jump_to = nil end -- shutdown结束了 + e = self:getCurrentCleaner() + end + + -- ret, evt解释: + -- * true, nil: 中止 + -- * false, nil: 正常结束 + -- * true, GameEvent: 中止直到某event + -- * false, GameEvent: 未结束,插入新event + -- 若jump_to不为nil,表示正在中断至某某事件 + local ret, evt = self:resumeEvent(e) + if evt == nil then + e.interrupted = ret + self:clearEvent(e) + coroutine.close(e._co) + elseif ret == true then + -- 跳到越早发生的事件越好 + if not jump_to then + jump_to = evt + else + jump_to = jump_to.id < evt.id and jump_to or evt + end + end + end +end + +---@param event GameEvent +function GameLogic:pushEvent(event) + self.game_event_stack:push(event) + + self.current_event_id = self.current_event_id + 1 + event.id = self.current_event_id + self.all_game_events[event.id] = event + self.event_recorder[event.event] = self.event_recorder[event.event] or {} + table.insert(self.event_recorder[event.event], event) +end + +-- 一般来说从GameEvent:exec切回start再被start调用 +-- 作用是启动新事件 都是结构差不多的函数 +---@param event GameEvent +---@return boolean, GameEvent? +function GameLogic:resumeEvent(event, ...) + local ret, evt + + local co = event._co + + while true do + local err, yield_result, extra_yield_result = coroutine.resume(co, ...) + + if err == false then + -- handle error, then break + if not string.find(yield_result, "__manuallyBreak") then + fk.qCritical(yield_result .. "\n" .. debug.traceback(co)) + end + ret = true + break + end + + if yield_result == "__handleRequest" then + -- yield to requestLoop + coroutine.yield(yield_result, extra_yield_result) + + elseif type(yield_result) == "table" and yield_result.class + and yield_result:isInstanceOf(GameEvent) then + + if extra_yield_result == "__newEvent" then + ret, evt = false, yield_result + break + elseif extra_yield_result == "__breakEvent" then + ret, evt = true, yield_result + if event.event ~= GameEvent.ClearEvent then break end + end + + elseif yield_result == "__breakEvent" then + ret = true + if event.event ~= GameEvent.ClearEvent then break end + + else + ret = false + event.exec_ret = yield_result + break + end + end + + return ret, evt +end + +---@return GameEvent +function GameLogic:getCurrentCleaner() + return self.cleaner_stack.t[self.cleaner_stack.p] +end + +-- 事件中的清理。 +-- cleaner单独开协程运行,exitFunc须转到上个事件的协程内执行 +-- 注意插入新event +---@param event GameEvent +function GameLogic:clearEvent(event) + if event.event == GameEvent.ClearEvent then return end + local ce = GameEvent(GameEvent.ClearEvent, event) + ce.id = self.current_event_id + local co = coroutine.create(ce.main_func) + ce._co = co + self.cleaner_stack:push(ce) +end + ---@return GameEvent function GameLogic:getCurrentEvent() return self.game_event_stack.t[self.game_event_stack.p] diff --git a/lua/server/room.lua b/lua/server/room.lua index ab836da6..8d161157 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -254,9 +254,11 @@ function Room:run() end local mode = Fk.game_modes[self.settings.gameMode] - self.logic = (mode.logic and mode.logic() or GameLogic):new(self) - if mode.rule then self.logic:addTriggerSkill(mode.rule) end - self.logic:run() + local logic = (mode.logic and mode.logic() or GameLogic):new(self) + self.logic = logic + if mode.rule then logic:addTriggerSkill(mode.rule) end + -- GameEvent(GameEvent.Game):exec() + logic:start() end ------------------------------------------------------------------------ diff --git a/lua/server/scheduler.lua b/lua/server/scheduler.lua index d2c04e77..2c1a16dd 100644 --- a/lua/server/scheduler.lua +++ b/lua/server/scheduler.lua @@ -93,6 +93,12 @@ local function mainLoop() if over then -- verbose('[#] %s is finished, removing ...', tostring(room)) + for _, e in ipairs(room.logic.game_event_stack.t) do + coroutine.close(e._co) + end + for _, e in ipairs(room.logic.cleaner_stack.t) do + coroutine.close(e._co) + end room.logic = nil runningRooms[room.id] = nil else diff --git a/lua/server/serverplayer.lua b/lua/server/serverplayer.lua index 2a86b2f0..9e16670b 100644 --- a/lua/server/serverplayer.lua +++ b/lua/server/serverplayer.lua @@ -321,6 +321,16 @@ function ServerPlayer:reconnect() local room = self.room self.serverplayer:setState(fk.Player_Online) + self:doNotify("Setup", json.encode{ + self.id, + self._splayer:getScreenName(), + self._splayer:getAvatar(), + }) + self:doNotify("AddTotalGameTime", json.encode { + self.id, + self._splayer:getTotalGameTime(), + }) + self:doNotify("EnterLobby", "") self:doNotify("EnterRoom", json.encode{ #room.players, room.timeout, room.settings, diff --git a/src/swig/qt.i b/src/swig/qt.i index 366c6560..523a10b3 100644 --- a/src/swig/qt.i +++ b/src/swig/qt.i @@ -46,7 +46,7 @@ public: static int GetMicroSecond(lua_State *L) { struct timeval tv; gettimeofday(&tv, nullptr); - long long microsecond = tv.tv_sec * 1000000 + tv.tv_usec; + long long microsecond = (long long)tv.tv_sec * 1000000 + tv.tv_usec; lua_pushnumber(L, microsecond); return 1; } From 1bc6453599cd3cc851dbe14a948fd702935276e9 Mon Sep 17 00:00:00 2001 From: YoumuKon <38815081+YoumuKon@users.noreply.github.com> Date: Thu, 11 Jan 2024 18:35:10 +0800 Subject: [PATCH 02/30] =?UTF-8?q?=E5=B0=8Fbugfix=20(#304)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改了无双相关的描述 - 添加了“体力上限改变前”,新三窟专属 - 将多选框改成了等宽按钮 - 添加了遗漏的装备技能名 --- Fk/RoomElement/CheckBox.qml | 2 +- lua/server/event.lua | 1 + lua/server/events/hp.lua | 2 +- lua/server/room.lua | 6 +++++- packages/maneuvering/i18n/en_US.lua | 3 +++ packages/maneuvering/init.lua | 3 +++ packages/standard_cards/i18n/en_US.lua | 6 +++++- packages/standard_cards/i18n/zh_CN.lua | 6 +++++- 8 files changed, 24 insertions(+), 5 deletions(-) diff --git a/Fk/RoomElement/CheckBox.qml b/Fk/RoomElement/CheckBox.qml index dcbdc892..b07c4edf 100644 --- a/Fk/RoomElement/CheckBox.qml +++ b/Fk/RoomElement/CheckBox.qml @@ -43,7 +43,7 @@ GraphicsBox { model: all_options MetroToggleButton { - // Layout.fillWidth: true + Layout.fillWidth: true text: processPrompt(modelData) enabled: options.indexOf(modelData) !== -1 && (root.result.length < max_num || triggered) diff --git a/lua/server/event.lua b/lua/server/event.lua index 1936bfdf..1a977f02 100644 --- a/lua/server/event.lua +++ b/lua/server/event.lua @@ -33,6 +33,7 @@ fk.PreHpLost = 17 fk.HpLost = 18 fk.BeforeHpChanged = 19 fk.HpChanged = 20 +fk.BeforeMaxHpChanged = 97 fk.MaxHpChanged = 21 fk.EventLoseSkill = 22 diff --git a/lua/server/events/hp.lua b/lua/server/events/hp.lua index 037b8c89..44d4d34a 100644 --- a/lua/server/events/hp.lua +++ b/lua/server/events/hp.lua @@ -290,7 +290,7 @@ end GameEvent.functions[GameEvent.ChangeMaxHp] = function(self) local player, num = table.unpack(self.data) local room = self.room - if num == 0 then + if room.logic:trigger(fk.BeforeMaxHpChanged, player, { num = num }) or num == 0 then return false end diff --git a/lua/server/room.lua b/lua/server/room.lua index 8d161157..9ca4496c 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -2715,7 +2715,11 @@ function Room:handleCardEffect(event, cardEffectEvent) local to = self:getPlayerById(cardEffectEvent.to) local prompt = "" if cardEffectEvent.from then - prompt = "#slash-jink:" .. cardEffectEvent.from .. "::" .. 1 + if loopTimes == 1 then + prompt = "#slash-jink:" .. cardEffectEvent.from + else + prompt = "#slash-jink-multi:" .. cardEffectEvent.from .. "::" .. i .. ":" .. loopTimes + end end local use = self:askForUseCard( diff --git a/packages/maneuvering/i18n/en_US.lua b/packages/maneuvering/i18n/en_US.lua index 0f1c9cf7..1186e44e 100644 --- a/packages/maneuvering/i18n/en_US.lua +++ b/packages/maneuvering/i18n/en_US.lua @@ -36,6 +36,7 @@ return { ["guding_blade"] = "Ancient Scimitar", [":guding_blade"] = "Ancient Scimitar (equip card, weapon)
ATK range: 2
Weapon skill: When your used Slash is about to cause DMG, if the target player has no hand cards: the DMG is increased by +1.", + ["#guding_blade_skill"] = "Ancient Scimitar", ["fan"] = "Fan", [":fan"] = "Fan (equip card, weapon)
ATK range: 4
Weapon skill: You can use any basic Slash as Fire Slash.", @@ -43,9 +44,11 @@ return { ["vine"] = "Vine", [":vine"] = "Vine (equip card, armor)
Armor skill: Savage Assault, Archery Attack and basic Slash have no effect on you. When you are about to suffer Fire DMG, the DMG is increased by +1.", + ["#vine_skill"] = "Vine", ["silver_lion"] = "Sliver Lion", [":silver_lion"] = "Sliver Lion (equip card, armor)
Armor skill: When you are about to suffer DMG: that DMG is reduced to 1. When you lose this card in your equipment area: you heal 1 HP.", + ["#silver_lion_skill"] = "Sliver Lion", ["hualiu"] = "Hua Liu", [":hualiu"] = "Hua Liu (equip card, horse)
Horse skill: The distance from other players to you is increased by +1.", diff --git a/packages/maneuvering/init.lua b/packages/maneuvering/init.lua index 583f677c..a9e08227 100644 --- a/packages/maneuvering/init.lua +++ b/packages/maneuvering/init.lua @@ -518,6 +518,7 @@ Fk:loadTranslationTable{ ["guding_blade"] = "古锭刀", [":guding_blade"] = "装备牌·武器
攻击范围:2
武器技能:锁定技。每当你使用【杀】对目标角色造成伤害时,若该角色没有手牌,此伤害+1。", + ["#guding_blade_skill"] = "古锭刀", ["fan"] = "朱雀羽扇", [":fan"] = "装备牌·武器
攻击范围:4
武器技能:你可以将一张普通【杀】当火【杀】使用。", @@ -525,9 +526,11 @@ Fk:loadTranslationTable{ ["vine"] = "藤甲", [":vine"] = "装备牌·防具
防具技能:锁定技。【南蛮入侵】、【万箭齐发】和普通【杀】对你无效。每当你受到火焰伤害时,此伤害+1。", + ["#vine_skill"] = "藤甲", ["silver_lion"] = "白银狮子", [":silver_lion"] = "装备牌·防具
防具技能:锁定技。每当你受到伤害时,若此伤害大于1点,防止多余的伤害。每当你失去装备区里的【白银狮子】后,你回复1点体力。", + ["#silver_lion_skill"] = "白银狮子", ["hualiu"] = "骅骝", [":hualiu"] = "装备牌·坐骑
坐骑技能:其他角色与你的距离+1。", diff --git a/packages/standard_cards/i18n/en_US.lua b/packages/standard_cards/i18n/en_US.lua index b5fcde3a..0c0f9d6c 100644 --- a/packages/standard_cards/i18n/en_US.lua +++ b/packages/standard_cards/i18n/en_US.lua @@ -54,7 +54,8 @@ Fk:loadTranslationTable({ ["slash"] = "Slash", [":slash"] = "Slash (basic card)
Phase: Action phase
Target: Another player within your ATK range
Effect: Deal 1 DMG to the targets.
Note: You can only use 1 Slash per action phase.", - ["#slash-jink"] = "%src used Slash to you, please use %arg Dodge(s)", + ["#slash-jink"] = "%src used Slash to you, please use a Dodge", + ["#slash-jink-multi"] = "%src used Slash to you, please use a Dodge( %arg th, %arg2 total )", ["#slash_skill"] = "Choose 1 player within your ATK range, deal 1 DMG to him", ["#slash_skill_multi"] = "Choose up to %arg players within your ATK range. Deal 1 DMG to them", @@ -119,6 +120,7 @@ Fk:loadTranslationTable({ ["crossbow"] = "Crossbow", [":crossbow"] = "Crossbow (equip card, weapon)
ATK range: 1
Weapon skill: You can use any amount of Slash in your action phase.", + ["#crossbow_skill"] = "Crossbow", ["qinggang_sword"] = "Qinggang Sword", [":qinggang_sword"] = "Qinggang Sword (equip card, weapon)
ATK range: 2
Weapon skill: Your Slash ignores the target's armor.", @@ -151,6 +153,7 @@ Fk:loadTranslationTable({ ["halberd"] = "Halberd", [":halberd"] = "Halberd (equip card, weapon)
ATK range: 4
Weapon skill: When you are about to use Slash which is your last hand card, you can target up to +2 extra targets.", + ["#halberd_skill"] = "Halberd", ["kylin_bow"] = "Kylin Bow", [":kylin_bow"] = "Kylin Bow (equip card, weapon)
ATK range: 5
Weapon skill: When your used Slash is about to cause DMG, you can discard 1 of his equipped horse.", @@ -162,6 +165,7 @@ Fk:loadTranslationTable({ ["nioh_shield"] = "Nioh Shield", [":nioh_shield"] = "Nioh Shield (equip card, armor)
Armor skill: Black Slash has no effect on you.", + ["#nioh_shield_skill"] = "Nioh Shield", ["dilu"] = "Di Lu", [":dilu"] = "Di Lu (equip card, horse)
Horse skill: The distance from other players to you is increased by +1.", diff --git a/packages/standard_cards/i18n/zh_CN.lua b/packages/standard_cards/i18n/zh_CN.lua index f1aad0d6..66f7700f 100644 --- a/packages/standard_cards/i18n/zh_CN.lua +++ b/packages/standard_cards/i18n/zh_CN.lua @@ -54,7 +54,8 @@ Fk:loadTranslationTable{ ["slash"] = "杀", [":slash"] = "基本牌
时机:出牌阶段
目标:攻击范围内的一名角色
效果:对目标角色造成1点伤害。", - ["#slash-jink"] = "%src 对你使用了杀,请使用 %arg 张闪", + ["#slash-jink"] = "%src 对你使用了杀,请使用一张闪", + ["#slash-jink-multi"] = "%src 对你使用了杀,请使用一张闪(此为第 %arg 张,共需 %arg2 张)", ["#slash_skill"] = "选择攻击范围内的一名角色,对其造成1点伤害", ["#slash_skill_multi"] = "选择攻击范围内的至多%arg名角色,对这些角色各造成1点伤害", @@ -119,6 +120,7 @@ Fk:loadTranslationTable{ ["crossbow"] = "诸葛连弩", [":crossbow"] = "装备牌·武器
攻击范围:1
武器技能:锁定技,你于出牌阶段内使用【杀】无次数限制。", + ["#crossbow_skill"] = "诸葛连弩", ["qinggang_sword"] = "青釭剑", [":qinggang_sword"] = "装备牌·武器
攻击范围:2
武器技能:锁定技,你的【杀】无视目标角色的防具。", @@ -151,6 +153,7 @@ Fk:loadTranslationTable{ ["halberd"] = "方天画戟", [":halberd"] = "装备牌·武器
攻击范围:4
武器技能:锁定技,你使用最后的手牌【杀】可以额外选择至多两名目标。", + ["#halberd_skill"] = "方天画戟", ["kylin_bow"] = "麒麟弓", [":kylin_bow"] = "装备牌·武器
攻击范围:5
武器技能:当你使用【杀】对目标角色造成伤害时,你可以弃置其装备区里的一张坐骑牌。", @@ -162,6 +165,7 @@ Fk:loadTranslationTable{ ["nioh_shield"] = "仁王盾", [":nioh_shield"] = "装备牌·防具
防具技能:锁定技,黑色【杀】对你无效。", + ["#nioh_shield_skill"] = "仁王盾", ["dilu"] = "的卢", [":dilu"] = "装备牌·坐骑
坐骑技能:其他角色与你的距离+1。", From 92768735fa41e8acc415ae91a3c31cfd5c6dc2d7 Mon Sep 17 00:00:00 2001 From: notify Date: Thu, 11 Jan 2024 18:36:05 +0800 Subject: [PATCH 03/30] =?UTF-8?q?2v2=E9=80=89=E5=B0=86=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=20(#305)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 以及完善了移牌的log --- Fk/Pages/RoomLogic.js | 19 ++++ lua/client/client.lua | 170 +++++++++++++++++++-------------- lua/client/client_util.lua | 9 ++ lua/client/i18n/zh_CN.lua | 20 ++-- lua/core/engine.lua | 12 +++ lua/fk_ex.lua | 7 ++ lua/server/gamelogic.lua | 4 + lua/server/request.lua | 12 +++ lua/server/room.lua | 28 ++++++ lua/server/serverplayer.lua | 1 + packages/test/init.lua | 9 ++ packages/test/qml/TestMini.qml | 47 +++++++++ 12 files changed, 258 insertions(+), 80 deletions(-) create mode 100644 packages/test/qml/TestMini.qml diff --git a/Fk/Pages/RoomLogic.js b/Fk/Pages/RoomLogic.js index aaebc51b..3f7a789e 100644 --- a/Fk/Pages/RoomLogic.js +++ b/Fk/Pages/RoomLogic.js @@ -1575,6 +1575,25 @@ callbacks["CustomDialog"] = (j) => { } } +callbacks["MiniGame"] = (j) => { + const data = JSON.parse(j); + const game = data.type; + const dat = data.data; + const gdata = JSON.parse(Backend.callLuaFunction("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) => { const data = JSON.parse(j); const id = data[0]; diff --git a/lua/client/client.lua b/lua/client/client.lua index c4352732..b057794e 100644 --- a/lua/client/client.lua +++ b/lua/client/client.lua @@ -509,7 +509,91 @@ local function sendMoveCardLog(move) local hidden = table.contains(move.ids, -1) 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" client:appendLog{ type = msgtype, @@ -521,85 +605,27 @@ local function sendMoveCardLog(move) type = "$$PutCard", from = move.from, }) - elseif move.toArea == Card.PlayerSpecial then - msgtype = hidden and "$RemoveCardFromGame" or "$AddToPile" - client:appendLog{ - type = msgtype, - arg = move.specialName, - arg2 = #move.ids, - card = move.ids, - } - elseif move.fromArea == Card.PlayerSpecial and move.to then - client:appendLog{ - type = "$GetCardsFromPile", - from = move.to, - arg = move.fromSpecialName, - arg2 = #move.ids, - card = move.ids, - } - elseif move.moveReason == fk.ReasonDraw then - client:appendLog{ - type = "$DrawCards", - 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 + elseif move.toArea == Card.DiscardPile then + if move.moveReason == fk.ReasonDiscard then client:appendLog{ - type = msgtype, + type = "$DiscardCards", from = move.from, - to = { move.to }, card = move.ids, + arg = #move.ids, + } + elseif move.moveReason == fk.ReasonPutIntoDiscardPile then + client:appendLog{ + type = "$PutToDiscard", + card = move.ids, + arg = #move.ids, } end + -- elseif move.toArea == Card.Void then + -- nop end - -- TODO ... + -- TODO: footnote if move.moveReason == fk.ReasonDiscard then - client:appendLog{ - type = "$DiscardCards", - from = move.from, - card = move.ids, - arg = #move.ids, - } client:setCardNote(move.ids, { type = "$$DiscardCards", from = move.from diff --git a/lua/client/client_util.lua b/lua/client/client_util.lua index 7b0bd104..07809821 100644 --- a/lua/client/client_util.lua +++ b/lua/client/client_util.lua @@ -748,4 +748,13 @@ function GetQmlMark(mtype, name, value, p) } 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 + dofile "lua/client/i18n/init.lua" diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index 02ef90f3..804a9edf 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -386,22 +386,26 @@ Fk:loadTranslationTable{ ["#LoseSkill"] = "%from 失去了技能 “%arg”", -- moveCards (they are sent by notifyMoveCards) - ["$PutCard"] = "%from 的 %arg 张牌被置于牌堆", - ["$PutKnownCard"] = "%from 的牌 %card 被置于牌堆", - ["$RemoveCardFromGame"] = "%arg2 张牌被作为 %arg 移出游戏", - ["$AddToPile"] = "%card 被作为 %arg 移出游戏", ["$GetCardsFromPile"] = "%from 从 %arg 中获得了 %arg2 张牌 %card", ["$DrawCards"] = "%from 摸了 %arg 张牌 %card", + ["$MoveCards"] = "%to 从 %from 处获得了 %arg 张牌 %card", ["$PreyCardsFromPile"] = "%from 获得了 %arg 张牌 %card", ["$GotCardBack"] = "%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", ["$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", + ["$PutToDiscard"] = "%arg 张牌 %card 被置入弃牌堆", + ["#ShowCard"] = "%from 展示了牌 %card", ["#Recast"] = "%from 重铸了 %card", ["#RecastBySkill"] = "%from 发动了 “%arg” 重铸了 %card", diff --git a/lua/core/engine.lua b/lua/core/engine.lua index 00d3bf93..f451c208 100644 --- a/lua/core/engine.lua +++ b/lua/core/engine.lua @@ -28,6 +28,7 @@ ---@field private _custom_events any[] @ 自定义事件列表 ---@field public poxi_methods table @ “魄袭”框操作方法表 ---@field public qml_marks table @ 自定义Qml标记的表 +---@field public mini_games table @ 自定义多人交互表 local Engine = class("Engine") --- Engine的构造函数。 @@ -61,6 +62,7 @@ function Engine:initialize() self._custom_events = {} self.poxi_methods = {} self.qml_marks = {} + self.mini_games = {} self:loadPackages() self:loadDisabled() @@ -356,6 +358,7 @@ function Engine:addPoxiMethod(spec) spec.post_select = spec.post_select or function(s) return s end end +---@param spec QmlMarkSpec function Engine:addQmlMark(spec) assert(type(spec.name) == "string") if self.qml_marks[spec.name] then @@ -364,6 +367,15 @@ function Engine:addQmlMark(spec) self.qml_marks[spec.name] = spec end +---@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 + --- 从已经开启的拓展包中,随机选出若干名武将。 --- --- 对于同名武将不会重复选取。 diff --git a/lua/fk_ex.lua b/lua/fk_ex.lua index 8d94de46..b352f004 100644 --- a/lua/fk_ex.lua +++ b/lua/fk_ex.lua @@ -608,3 +608,10 @@ end ---@field name string ---@field qml_path string | fun(name: string, value?: any, player?: Player): string ---@field how_to_show fun(name: string, value?: any, player?: Player): string? + +-- TODO: 断连 不操作的人观看 现在只做了专为22设计的框 +---@class MiniGameSpec +---@field name string +---@field qml_path string | fun(player: Player, data: any): string +---@field update_func? fun(player: ServerPlayer, data: any) +---@field default_choice? fun(player: ServerPlayer, data: any): any diff --git a/lua/server/gamelogic.lua b/lua/server/gamelogic.lua index 0f8f184c..54fdc624 100644 --- a/lua/server/gamelogic.lua +++ b/lua/server/gamelogic.lua @@ -439,6 +439,10 @@ function GameLogic:start() e = self:getCurrentCleaner() end + if not e then -- 没有事件,按理说不应该,平局处理 + self.room:gameOver("") + end + -- ret, evt解释: -- * true, nil: 中止 -- * false, nil: 正常结束 diff --git a/lua/server/request.lua b/lua/server/request.lua index 329aa148..79a0a70e 100644 --- a/lua/server/request.lua +++ b/lua/server/request.lua @@ -182,6 +182,18 @@ request_handlers["surrender"] = function(room, id, reqlist) end end +request_handlers["updatemini"] = function(room, pid, reqlist) + local player = room:getPlayerById(pid) + local data = player.mini_game_data + if not data then return end + local game = Fk.mini_games[data.type] + if not (game and game.update_func) then return end + local dat = table.simpleClone(reqlist) + table.remove(dat, 1) + table.remove(dat, 1) + game.update_func(player, dat) +end + request_handlers["newroom"] = function(s, id) s:registerRoom(id) end diff --git a/lua/server/room.lua b/lua/server/room.lua index 9ca4496c..6a4fdf94 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -2214,6 +2214,34 @@ function Room:closeAG(player) else self:doBroadcastNotify("CloseAG", "") end end +-- TODO: 重构request机制,不然这个还得手动拿client_reply +---@param players ServerPlayer[] +---@param focus string +---@param game_type string +---@param data_table table @ 对应每个player +function Room:askForMiniGame(players, focus, game_type, data_table) + local command = "MiniGame" + local game = Fk.mini_games[game_type] + if #players == 0 or not game then return end + for _, p in ipairs(players) do + local data = data_table[p.id] + p.mini_game_data = { type = game_type, data = data } + p.request_data = json.encode(p.mini_game_data) + p.default_reply = game.default_choice and json.encode(game.default_choice(p, data)) or "" + end + + self:notifyMoveFocus(players, focus) + self:doBroadcastRequest(command, players) + + for _, p in ipairs(players) do + p.mini_game_data = nil + if not p.reply_ready then + p.client_reply = p.default_reply + p.reply_ready = true + end + end +end + -- Show a qml dialog and return qml's ClientInstance.replyToServer -- Do anything you like through this function diff --git a/lua/server/serverplayer.lua b/lua/server/serverplayer.lua index 9e16670b..75a26cae 100644 --- a/lua/server/serverplayer.lua +++ b/lua/server/serverplayer.lua @@ -5,6 +5,7 @@ ---@field public room Room ---@field public next ServerPlayer ---@field public request_data string +---@field public mini_game_data any ---@field public client_reply string ---@field public default_reply string ---@field public reply_ready boolean diff --git a/packages/test/init.lua b/packages/test/init.lua index 10c163fc..32d4d995 100644 --- a/packages/test/init.lua +++ b/packages/test/init.lua @@ -88,6 +88,8 @@ local control = fk.CreateActiveSkill{ -- }, from.hp, false)) -- room:setPlayerMark(from, "@$a", {1,2,3}) -- room:setPlayerMark(from, "@$b", {'slash','duel','axe'}) + --room:askForMiniGame({from}, "test", "test", { [from.id] = {"Helloworld"} }) + --print(from.client_reply) if to:getMark("mouxushengcontrolled") == 0 then room:addPlayerMark(to, "mouxushengcontrolled") from:control(to) @@ -126,6 +128,13 @@ local control = fk.CreateActiveSkill{ end, } --[[ +Fk:addMiniGame{ + name = "test", + qml_path = "packages/test/qml/TestMini", + update_func = function(player, data) + player:doNotify("UpdateMiniGame", json.encode(data)) + end +} Fk:addPoxiMethod{ name = "test", card_filter = function(to_select, selected, data, extra_data) diff --git a/packages/test/qml/TestMini.qml b/packages/test/qml/TestMini.qml new file mode 100644 index 00000000..84a532ca --- /dev/null +++ b/packages/test/qml/TestMini.qml @@ -0,0 +1,47 @@ +// 割圆的例子 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Fk.RoomElement + +GraphicsBox { + id: root + height: 200; width: 300 +ColumnLayout { + Text { + id: txt + color: "white" + } + + Button { + text: "Btn 1" + onClicked: { + ClientInstance.notifyServer("PushRequest", "updatemini,B1") + } + } + + Button { + text: "Btn 2" + onClicked: { + ClientInstance.notifyServer("PushRequest", "updatemini,B2") + } + } + + Button { + text: "Reply" + onClicked: { + close(); + roomScene.state = "notactive"; + ClientInstance.replyToServer("", JSON.stringify("Hello")); + } + } +} + + function loadData(data) { + txt.text = data[0] + } + + function updateData(data) { + txt.text = JSON.stringify(data) + " updated" + } +} From db651f572e2d242b7c12c8c92fead3d5e3a71a88 Mon Sep 17 00:00:00 2001 From: notify Date: Thu, 11 Jan 2024 18:41:18 +0800 Subject: [PATCH 04/30] Changelog: v0.4.3 --- CHANGELOG.md | 8 ++++++++ CMakeLists.txt | 2 +- android/AndroidManifest.xml | 4 ++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1295912c..a8564b19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # ChangeLog +## v0.4.3 + +1. 事件栈和实际的函数调用栈分离 +2. 2v2选将专用的MiniGame +3. 各种小修小补 + +___ + ## v0.4.2 && v0.4.1 1. 修复和完善qml mark diff --git a/CMakeLists.txt b/CMakeLists.txt index 84812c68..f311507d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16) -project(FreeKill VERSION 0.4.2) +project(FreeKill VERSION 0.4.3) add_definitions(-DFK_VERSION=\"${CMAKE_PROJECT_VERSION}\") find_package(Qt6 REQUIRED COMPONENTS diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 09a828f4..c85c1750 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -3,8 +3,8 @@ + android:versionCode="403" + android:versionName="0.4.3"> From 21e4c652044f70e47e7262e82beec2376b0a33cb Mon Sep 17 00:00:00 2001 From: YoumuKon <38815081+YoumuKon@users.noreply.github.com> Date: Thu, 25 Jan 2024 03:13:57 +0800 Subject: [PATCH 05/30] =?UTF-8?q?=E7=BB=88=E7=BB=93=E6=A0=87=E8=AE=B0?= =?UTF-8?q?=E4=B8=8E=E7=95=8Cbugfix=20(#307)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 正式移除了作为临时手段的-tmp标记,现在can_use和target_filter支持读取extra_data(card_filter暂时搁置) - 规范了askForUseCard中card_name和pattern的关系,现在的格式将以pattern为主,若无pattern才会将card_name视为pattern - 将withinDistanceLimit迁移至ActiveSkill内 - 添加了令卡牌无距离/次数限制的标记判断 - 添加放大了⑨倍的冰伤音效 - 优化了同将判断的逻辑,使其能够准确读取trueName - 身份场主公选将后其他角色能看见主公技能(只是看见,无实际功能) - 开局添加不存在的技能时会放出警报 - 修复了findParent在当前事件无parent时报错的bug - 修复了人工洗牌后不刷新摸牌堆的bug - 修复了getPile返回牌堆实例的bug - 修复了getSkillNameList无法过滤主公技的bug - 修复了死亡后武将牌没有圆角效果的bug --- Fk/Pages/Room.qml | 2 +- Fk/Pages/RoomLogic.js | 8 ++--- Fk/RoomElement/Dashboard.qml | 6 ++-- Fk/RoomElement/Photo.qml | 5 +-- audio/system/ice_damage.mp3 | Bin 0 -> 16173 bytes audio/system/ice_damage2.mp3 | Bin 0 -> 28845 bytes lua/client/client_util.lua | 29 +++++++++------- lua/client/i18n/en_US.lua | 1 + lua/client/i18n/zh_CN.lua | 1 + lua/core/card.lua | 3 ++ lua/core/engine.lua | 4 +-- lua/core/exppattern.lua | 2 +- lua/core/general.lua | 14 +++++--- lua/core/player.lua | 11 +++--- lua/core/skill_type/active.lua | 34 ++++++++++++++++-- lua/core/skill_type/usable_skill.lua | 37 ++++++-------------- lua/fk_ex.lua | 4 +-- lua/server/ai/random_ai.lua | 4 +++ lua/server/events/misc.lua | 8 ++--- lua/server/gameevent.lua | 4 +-- lua/server/gamelogic.lua | 4 +++ lua/server/room.lua | 40 +++++++-------------- lua/server/serverplayer.lua | 8 ++--- packages/maneuvering/init.lua | 17 ++++----- packages/standard/aux_skills.lua | 6 ++-- packages/standard/game_rule.lua | 2 +- packages/standard/init.lua | 50 +++++++++++++++++++++++++++ packages/standard_cards/init.lua | 16 +++++---- packages/test/init.lua | 7 ++++ 29 files changed, 206 insertions(+), 121 deletions(-) create mode 100644 audio/system/ice_damage.mp3 create mode 100644 audio/system/ice_damage2.mp3 diff --git a/Fk/Pages/Room.qml b/Fk/Pages/Room.qml index 501e23bc..c4844cc8 100644 --- a/Fk/Pages/Room.qml +++ b/Fk/Pages/Room.qml @@ -483,7 +483,7 @@ Item { && JSON.parse(Backend.callLuaFunction("GetPlayerHandcards", [Self.id])).includes(card)) { const skills = JSON.parse(Backend.callLuaFunction("GetCardSpecialSkills", [card])); - if (JSON.parse(Backend.callLuaFunction("CanUseCard", [card, Self.id]))) { + if (JSON.parse(Backend.callLuaFunction("CanUseCard", [card, Self.id, JSON.stringify(roomScene.extra_data)]))) { skills.unshift("_normal_use"); } specialCardSkills.model = skills; diff --git a/Fk/Pages/RoomLogic.js b/Fk/Pages/RoomLogic.js index 3f7a789e..faa991df 100644 --- a/Fk/Pages/RoomLogic.js +++ b/Fk/Pages/RoomLogic.js @@ -570,7 +570,7 @@ function enableTargets(card) { // card: int | { skill: string, subcards: int[] } const id = photo.playerid; const ret = JSON.parse(Backend.callLuaFunction( "CanUseCardToTarget", - [card, id, selected_targets] + [card, id, selected_targets, JSON.stringify(roomScene.extra_data)] )); photo.selectable = ret; if (roomScene.extra_data instanceof Object) { @@ -603,7 +603,7 @@ function enableTargets(card) { // card: int | { skill: string, subcards: int[] } )) && (roomScene.autoPending || !JSON.parse(Backend.callLuaFunction( "CardProhibitedUse", [card]))); } else if (okButton.enabled && roomScene.state === "playing") { - okButton.enabled = JSON.parse(Backend.callLuaFunction("CanUseCard", [card, Self.id])); + okButton.enabled = JSON.parse(Backend.callLuaFunction("CanUseCard", [card, Self.id, JSON.stringify(roomScene.extra_data)])); } if (okButton.enabled) { if (roomScene.extra_data instanceof Object) { @@ -648,7 +648,7 @@ function updateSelectedTargets(playerid, selected) { const id = photo.playerid; const ret = JSON.parse(Backend.callLuaFunction( "CanUseCardToTarget", - [card, id, selected_targets] + [card, id, selected_targets, JSON.stringify(roomScene.extra_data)] )); photo.selectable = ret; if (roomScene.extra_data instanceof Object) { @@ -681,7 +681,7 @@ function updateSelectedTargets(playerid, selected) { )) && (roomScene.autoPending || !JSON.parse(Backend.callLuaFunction( "CardProhibitedUse", [card]))); } else if (okButton.enabled && roomScene.state === "playing") { - okButton.enabled = JSON.parse(Backend.callLuaFunction("CanUseCard", [card, Self.id])); + okButton.enabled = JSON.parse(Backend.callLuaFunction("CanUseCard", [card, Self.id, JSON.stringify(roomScene.extra_data)])); } if (okButton.enabled) { if (roomScene.extra_data instanceof Object) { diff --git a/Fk/RoomElement/Dashboard.qml b/Fk/RoomElement/Dashboard.qml index 53986a8f..61dea611 100644 --- a/Fk/RoomElement/Dashboard.qml +++ b/Fk/RoomElement/Dashboard.qml @@ -223,14 +223,14 @@ RowLayout { const ids = [], cards = handcardAreaItem.cards; for (let i = 0; i < cards.length; i++) { cards[i].prohibitReason = ""; - if (JSON.parse(Backend.callLuaFunction("CanUseCard", [cards[i].cid, Self.id]))) { + if (JSON.parse(Backend.callLuaFunction("CanUseCard", [cards[i].cid, Self.id, JSON.stringify(roomScene.extra_data)]))) { ids.push(cards[i].cid); } else { // cannot use? considering special_skills const skills = JSON.parse(Backend.callLuaFunction("GetCardSpecialSkills", [cards[i].cid])); for (let j = 0; j < skills.length; j++) { const s = skills[j]; - if (JSON.parse(Backend.callLuaFunction("ActiveCanUse", [s]))) { + if (JSON.parse(Backend.callLuaFunction("ActiveCanUse", [s, JSON.stringify(roomScene.extra_data)]))) { ids.push(cards[i].cid); break; } @@ -475,7 +475,7 @@ RowLayout { continue; } - item.enabled = JSON.parse(Backend.callLuaFunction("ActiveCanUse", [item.orig])); + item.enabled = JSON.parse(Backend.callLuaFunction("ActiveCanUse", [item.orig, JSON.stringify(roomScene.extra_data)])); } } diff --git a/Fk/RoomElement/Photo.qml b/Fk/RoomElement/Photo.qml index 7c111b94..869d0e60 100644 --- a/Fk/RoomElement/Photo.qml +++ b/Fk/RoomElement/Photo.qml @@ -247,14 +247,15 @@ Item { } OpacityMask { + id: photoMaskEffect anchors.fill: photoMask source: generalImgItem maskSource: photoMask } Colorize { - anchors.fill: photoMask - source: generalImgItem + anchors.fill: photoMaskEffect + source: photoMaskEffect saturation: 0 visible: root.dead || root.surrendered } diff --git a/audio/system/ice_damage.mp3 b/audio/system/ice_damage.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..bb69be2221971c3378285b727dd1ab578a8dd210 GIT binary patch literal 16173 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 literal 0 HcmV?d00001 diff --git a/lua/client/client_util.lua b/lua/client/client_util.lua index 07809821..55ae80dc 100644 --- a/lua/client/client_util.lua +++ b/lua/client/client_util.lua @@ -251,8 +251,10 @@ end ---@param card string | 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 extra_data = extra_data_str == "" and nil or json.decode(extra_data_str) if type(card) == "number" then c = Fk:getCardById(card) else @@ -271,13 +273,13 @@ function CanUseCard(card, player) end 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) if ret then local min_target = c.skill:getMinTargetNum() if min_target > 0 then 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" end end @@ -311,7 +313,9 @@ end ---@param card string | integer ---@param to_select integer @ id of the target ---@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 return "false" end @@ -322,10 +326,10 @@ function CanUseCardToTarget(card, to_select, selected) selected_cards = {card} else 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 - 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) return json.encode(ret) end @@ -392,12 +396,13 @@ function GetSkillData(skill_name) } 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 ret = false if skill then if skill:isInstanceOf(ActiveSkill) then - ret = skill:canUse(Self) + ret = skill:canUse(Self, extra_data) elseif skill:isInstanceOf(ViewAsSkill) then ret = skill:enabledAtPlay(Self) if ret then @@ -414,7 +419,7 @@ function ActiveCanUse(skill_name) for _, n in ipairs(cnames) do local c = Fk:cloneCard(n) c.skillName = skill_name - ret = c.skill:canUse(Self, c) + ret = c.skill:canUse(Self, c, extra_data) if ret then break end end end @@ -449,7 +454,7 @@ function ActiveCardFilter(skill_name, to_select, selected, selected_targets) return json.encode(ret) 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 ret = false if skill then @@ -458,7 +463,7 @@ function ActiveTargetFilter(skill_name, to_select, selected, selected_cards) elseif skill:isInstanceOf(ViewAsSkill) then local card = skill:viewAs(selected_cards) 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) end end @@ -722,7 +727,7 @@ function PoxiPrompt(poxi_type, data, extra_data) local poxi = Fk.poxi_methods[poxi_type] if not poxi or not poxi.prompt then return "" 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 function PoxiFilter(poxi_type, to_select, selected, data, extra_data) diff --git a/lua/client/i18n/en_US.lua b/lua/client/i18n/en_US.lua index 1da0ad40..29156e18 100644 --- a/lua/client/i18n/en_US.lua +++ b/lua/client/i18n/en_US.lua @@ -268,6 +268,7 @@ Fk:loadTranslationTable({ ["thunder_damage"] = "Thunder", ["ice_damage"] = "Ice", ["hp_lost"] = "HP lost", + ["lose_hp"] = "lose HP", ["phase_start"] = "Prepare phase", ["phase_judge"] = "Judge phase", diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index 804a9edf..9ae9ca97 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -324,6 +324,7 @@ Fk:loadTranslationTable{ ["thunder_damage"] = "雷属性", ["ice_damage"] = "冰属性", ["hp_lost"] = "体力流失", + ["lose_hp"] = "失去体力", ["phase_start"] = "准备阶段", ["phase_judge"] = "判定阶段", diff --git a/lua/core/card.lua b/lua/core/card.lua index 6cb51c34..71ec5e15 100644 --- a/lua/core/card.lua +++ b/lua/core/card.lua @@ -406,6 +406,9 @@ function Card:getMark(mark) if (not self:isVirtual()) and next(self.mark) == nil then self.mark = nil end + if type(ret) == "table" then + ret = table.simpleClone(ret) + end return ret end diff --git a/lua/core/engine.lua b/lua/core/engine.lua index f451c208..c8d2a4f2 100644 --- a/lua/core/engine.lua +++ b/lua/core/engine.lua @@ -275,8 +275,8 @@ end ---@param name string @ 要查询的武将名字 ---@return string[] @ 这个武将对应的同名武将列表 function Engine:getSameGenerals(name) - local tmp = name:split("__") - local tName = tmp[#tmp] + if not self.generals[name] then return {} end + local tName = self.generals[name].trueName local ret = self.same_generals[tName] or {} return table.filter(ret, function(g) return g ~= name and self.generals[g] ~= nil and self:canUseGeneral(g) diff --git a/lua/core/exppattern.lua b/lua/core/exppattern.lua index 1cf1f9fd..1a959bd1 100644 --- a/lua/core/exppattern.lua +++ b/lua/core/exppattern.lua @@ -10,7 +10,7 @@ 2. 对于 Matcher 字符串,它是用 ('|') 分割的 3. 然后在 Matcher 的每一个细分中,又可以用 ',' 来进行更进一步的分割 - 其中 Matcher 的格式为 牌名|花色|点数|位置|详细牌名|类型|牌的id + 其中 Matcher 的格式为 牌名|点数|花色|区域|完整牌名|牌类型|牌id 更进一步,“点数” 可以用 '~' 符号表示数字的范围,并且可以用 AJQK 表示对应点数 例如: diff --git a/lua/core/general.lua b/lua/core/general.lua index 3ab607d3..33e220b4 100644 --- a/lua/core/general.lua +++ b/lua/core/general.lua @@ -94,12 +94,18 @@ function General:addRelatedSkill(skill) end --- 获取武将所有技能。 +---@param include_lord bool function General:getSkillNameList(include_lord) - local ret = table.map(self.skills, Util.NameMapper) - table.insertTable(ret, self.other_skills) - - if not include_lord then + local ret = {} + local other_skills = table.map(self.other_skills, Util.Name2SkillMapper) + local skills = table.connect(self.skills, other_skills) + for _, skill in ipairs(skills) do + if include_lord or not skill.lordSkill then + table.insert(ret, skill.name) + end end + + -- table.insertTable(ret, self.other_skills) return ret end diff --git a/lua/core/player.lua b/lua/core/player.lua index 4e86703b..e54dcfef 100644 --- a/lua/core/player.lua +++ b/lua/core/player.lua @@ -215,7 +215,10 @@ end ---@param mark string @ 标记 ---@return any function Player:getMark(mark) - return (self.mark[mark] or 0) + local mark = self.mark[mark] + if not mark then return 0 end + if type(mark) == "table" then return table.simpleClone(mark) end + return mark end --- 判定角色是否拥有对应的Mark。 @@ -363,13 +366,13 @@ function Player:getCardIds(playerAreas, specialName) return cardIds end ---- 通过名字检索获取玩家是否存在对应私人牌堆。 +--- 通过名字检索获取玩家对应的私人牌堆。 ---@param name string @ 私人牌堆名 function Player:getPile(name) - return self.special_cards[name] or {} + return table.simpleClone(self.special_cards[name] or {}) end ---- 通过ID检索获取玩家是否存在对应私人牌堆。 +--- 通过ID检索获取玩家对应的私人牌堆。 ---@param id integer @ 私人牌堆ID ---@return string? function Player:getPileNameOfId(id) diff --git a/lua/core/skill_type/active.lua b/lua/core/skill_type/active.lua index 643a8e47..874299ec 100644 --- a/lua/core/skill_type/active.lua +++ b/lua/core/skill_type/active.lua @@ -27,7 +27,7 @@ end --- Determine whether the skill can be used in playing phase ---@param player Player ---@param card Card @ helper -function ActiveSkill:canUse(player, card) +function ActiveSkill:canUse(player, card, extra_data) return self:isEffectable(player) end @@ -45,8 +45,9 @@ end ---@param to_select integer @ id of the target ---@param selected integer[] @ ids of selected targets ---@param selected_cards integer[] @ ids of selected cards +---@param extra_data any @ extra_data ---@param card Card @ helper -function ActiveSkill:targetFilter(to_select, selected, selected_cards, card) +function ActiveSkill:targetFilter(to_select, selected, selected_cards, card, extra_data) return false end @@ -142,6 +143,35 @@ function ActiveSkill:getDistanceLimit(player, card, to) return ret end +function ActiveSkill:withinDistanceLimit(player, isattack, card, to) + if to and to.dead then return false end + local status_skills = Fk:currentRoom().status_skills[TargetModSkill] or Util.DummyTable + if not card and self.name:endsWith("_skill") then + card = Fk:cloneCard(self.name:sub(1, #self.name - 6)) + end + for _, skill in ipairs(status_skills) do + if skill:bypassDistancesCheck(player, self, card, to) then return true end + end + + local temp_suf = table.simpleClone(MarkEnum.TempMarkSuffix) + local card_temp_suf = table.simpleClone(MarkEnum.CardTempMarkSuffix) + table.insert(temp_suf, 1, "") + table.insert(temp_suf, "-tmp") + table.insert(card_temp_suf, 1, "") + + return (isattack and player:inMyAttackRange(to)) or + (player:distanceTo(to) > 0 and player:distanceTo(to) <= self:getDistanceLimit(player, card, to)) or + (card and table.find(card_temp_suf, function(s) + return card:getMark(MarkEnum.BypassDistancesLimit .. s) ~= 0 + end)) or + (table.find(temp_suf, function(s) + return player:getMark(MarkEnum.BypassDistancesLimit .. s) ~= 0 + end)) or + (to and (table.find(temp_suf, function(s) + return to:getMark(MarkEnum.BypassDistancesLimitTo .. s) ~= 0 + end))) +end + --- Determine if selected cards and targets are valid for this skill --- If returns true, the OK button should be enabled --- only used in skill of players diff --git a/lua/core/skill_type/usable_skill.lua b/lua/core/skill_type/usable_skill.lua index f3429296..70e00244 100644 --- a/lua/core/skill_type/usable_skill.lua +++ b/lua/core/skill_type/usable_skill.lua @@ -35,41 +35,24 @@ function UsableSkill:withinTimesLimit(player, scope, card, card_name, to) for _, skill in ipairs(status_skills) do if skill:bypassTimesCheck(player, self, scope, card, to) then return true end end + card_name = card_name or card.trueName local temp_suf = table.simpleClone(MarkEnum.TempMarkSuffix) + local card_temp_suf = table.simpleClone(MarkEnum.CardTempMarkSuffix) + table.insert(temp_suf, 1, "") table.insert(temp_suf, "-tmp") + table.insert(card_temp_suf, 1, "") + return player:usedCardTimes(card_name, scope) < self:getMaxUseTime(player, scope, card, to) or - (player:getMark(MarkEnum.BypassTimesLimit) ~= 0 or - table.find(temp_suf, function(s) + (card and table.find(card_temp_suf, function(s) + return card:getMark(MarkEnum.BypassTimesLimit .. s) ~= 0 + end)) or + (table.find(temp_suf, function(s) return player:getMark(MarkEnum.BypassTimesLimit .. s) ~= 0 end)) or - (to and (to:getMark(MarkEnum.BypassTimesLimitTo) ~= 0 or - table.find(temp_suf, function(s) + (to and (table.find(temp_suf, function(s) return to:getMark(MarkEnum.BypassTimesLimitTo .. s) ~= 0 end))) end -function UsableSkill:withinDistanceLimit(player, isattack, card, to) - if to and to.dead then return false end - local status_skills = Fk:currentRoom().status_skills[TargetModSkill] or Util.DummyTable - if not card and self.name:endsWith("_skill") then - card = Fk:cloneCard(self.name:sub(1, #self.name - 6)) - end - for _, skill in ipairs(status_skills) do - if skill:bypassDistancesCheck(player, self, card, to) then return true end - end - local temp_suf = table.simpleClone(MarkEnum.TempMarkSuffix) - table.insert(temp_suf, "-tmp") - return (isattack and player:inMyAttackRange(to)) or - (player:distanceTo(to) > 0 and player:distanceTo(to) <= self:getDistanceLimit(player, card, to)) or - (player:getMark(MarkEnum.BypassDistancesLimit) ~= 0 or - table.find(temp_suf, function(s) - return player:getMark(MarkEnum.BypassDistancesLimit .. s) ~= 0 - end)) or - (to and (to:getMark(MarkEnum.BypassDistancesLimitTo) ~= 0 or - table.find(temp_suf, function(s) - return to:getMark(MarkEnum.BypassDistancesLimitTo .. s) ~= 0 - end))) -end - return UsableSkill diff --git a/lua/fk_ex.lua b/lua/fk_ex.lua index b352f004..1cf0d062 100644 --- a/lua/fk_ex.lua +++ b/lua/fk_ex.lua @@ -170,9 +170,9 @@ function fk.CreateTriggerSkill(spec) end ---@class ActiveSkillSpec: UsableSkillSpec ----@field public can_use? fun(self: ActiveSkill, player: Player, card: Card): boolean? +---@field public can_use? fun(self: ActiveSkill, player: Player, card: Card, extra_data: any): boolean? ---@field public card_filter? fun(self: ActiveSkill, to_select: integer, selected: integer[], selected_targets: integer[]): boolean? ----@field public target_filter? fun(self: ActiveSkill, to_select: integer, selected: integer[], selected_cards: integer[], card: Card): boolean? +---@field public target_filter? fun(self: ActiveSkill, to_select: integer, selected: integer[], selected_cards: integer[], card: Card, extra_data: any): boolean? ---@field public feasible? fun(self: ActiveSkill, selected: integer[], selected_cards: integer[]): boolean? ---@field public on_use? fun(self: ActiveSkill, room: Room, cardUseEvent: CardUseStruct): boolean? ---@field public about_to_effect? fun(self: ActiveSkill, room: Room, cardEffectEvent: CardEffectEvent): boolean? diff --git a/lua/server/ai/random_ai.lua b/lua/server/ai/random_ai.lua index 1108b17b..cfb12ac9 100644 --- a/lua/server/ai/random_ai.lua +++ b/lua/server/ai/random_ai.lua @@ -27,6 +27,7 @@ function RandomAI:useActiveSkill(skill, card) -- local max = skill:getMaxTargetNum(player, card) -- local min_card = skill:getMinCardNum() -- local max_card = skill:getMaxCardNum() + -- FIXME: ViewAsSkill can be buggy here for _ = 0, max_try_times do if skill:feasible(selected_targets, selected_cards, self.player, card) then break end local avail_targets = table.filter(room:getAlivePlayers(), function(p) @@ -121,6 +122,9 @@ random_cb["AskForUseActiveSkill"] = function(self, jsonData) for k, v in pairs(extra_data) do skill[k] = v end + if skill:isInstanceOf(ViewAsSkill) then + return RandomAI.useVSSkill(skill) + end return RandomAI.useActiveSkill(self, skill) end diff --git a/lua/server/events/misc.lua b/lua/server/events/misc.lua index 6b73e3e4..36128b8b 100644 --- a/lua/server/events/misc.lua +++ b/lua/server/events/misc.lua @@ -19,12 +19,12 @@ GameEvent.functions[GameEvent.ChangeProperty] = function(self) if data.general and data.general ~= "" and data.general ~= player.general then local originalGeneral = Fk.generals[player.general] or Fk.generals["blank_shibing"] - local originalSkills = originalGeneral and originalGeneral:getSkillNameList() or Util.DummyTable + local originalSkills = originalGeneral and originalGeneral:getSkillNameList(true) or Util.DummyTable table.insertTableIfNeed(skills, table.map(originalSkills, function(e) return "-" .. e end)) local newGeneral = Fk.generals[data.general] or Fk.generals["blank_shibing"] - for _, name in ipairs(newGeneral:getSkillNameList()) do + for _, name in ipairs(newGeneral:getSkillNameList(data.isLord)) do local s = Fk.skills[name] if not s.relate_to_place or s.relate_to_place == "m" then table.insertIfNeed(skills, name) @@ -45,14 +45,14 @@ GameEvent.functions[GameEvent.ChangeProperty] = function(self) if data.deputyGeneral and data.deputyGeneral ~= player.deputyGeneral then local originalDeputy = Fk.generals[player.deputyGeneral] or Fk.generals["blank_shibing"] - local originalSkills = originalDeputy and originalDeputy:getSkillNameList() or Util.DummyTable + local originalSkills = originalDeputy and originalDeputy:getSkillNameList(true) or Util.DummyTable table.insertTableIfNeed(skills, table.map(originalSkills, function(e) return "-" .. e end)) if data.deputyGeneral ~= "" then local newDeputy = Fk.generals[data.deputyGeneral] or Fk.generals["blank_shibing"] - for _, name in ipairs(newDeputy:getSkillNameList()) do + for _, name in ipairs(newDeputy:getSkillNameList(data.isLord)) do local s = Fk.skills[name] if not s.relate_to_place or s.relate_to_place == "d" then table.insertIfNeed(skills, name) diff --git a/lua/server/gameevent.lua b/lua/server/gameevent.lua index 1a1e36b6..e89012b6 100644 --- a/lua/server/gameevent.lua +++ b/lua/server/gameevent.lua @@ -85,10 +85,10 @@ end function GameEvent:findParent(eventType, includeSelf) if includeSelf and self.event == eventType then return self end local e = self.parent - repeat + while e do if e.event == eventType then return e end e = e.parent - until not e + end return nil end diff --git a/lua/server/gamelogic.lua b/lua/server/gamelogic.lua index 54fdc624..ed174f29 100644 --- a/lua/server/gamelogic.lua +++ b/lua/server/gamelogic.lua @@ -218,6 +218,10 @@ function GameLogic:attachSkillToPlayers() local addRoleModSkills = function(player, skillName) local skill = Fk.skills[skillName] + if not skill then + fk.qCritical("Skill: "..skillName.." doesn't exist!") + return + end if skill.lordSkill and (player.role ~= "lord" or #room.players < 5) then return end diff --git a/lua/server/room.lua b/lua/server/room.lua index 6a4fdf94..571859ec 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -1035,7 +1035,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), + deputy = player.deputyGeneral and player.deputyGeneral ~= "" and table.contains(Fk.generals[player.deputyGeneral]:getSkillNameList(true), skill_name), }) self:delay(2000) end @@ -1978,36 +1978,26 @@ end ---@param event_data? CardEffectEvent @ 事件信息 ---@return CardUseStruct? @ 返回关于本次使用牌的数据,以便后续处理 function Room:askForUseCard(player, card_name, pattern, prompt, cancelable, extra_data, event_data) + pattern = pattern or card_name if event_data and (event_data.disresponsive or table.contains(event_data.disresponsiveList or Util.DummyTable, player.id)) then return nil end - if event_data and event_data.prohibitedCardNames and card_name then - local splitedCardNames = card_name:split(",") - splitedCardNames = table.filter(splitedCardNames, function(name) - return not table.contains(event_data.prohibitedCardNames, name) - end) - - if #splitedCardNames == 0 then - return nil + if event_data and event_data.prohibitedCardNames then + local exp = Exppattern:Parse(pattern) + for _, matcher in ipairs(exp.matchers) do + matcher.name = table.filter(matcher.name, function(name) + return not table.contains(event_data.prohibitedCardNames, name) + end) + if #matcher.name == 0 then return nil end end - - card_name = table.concat(splitedCardNames, ",") + pattern = tostring(exp) end - if extra_data then - if extra_data.bypass_distances then - player.room:setPlayerMark(player, MarkEnum.BypassDistancesLimit .. "-tmp", 1) -- FIXME: 缺少直接传入无限制的手段 - end - if extra_data.bypass_times == nil or extra_data.bypass_times then - player.room:setPlayerMark(player, MarkEnum.BypassTimesLimit .. "-tmp", 1) -- FIXME: 缺少直接传入无限制的手段 - end - end local command = "AskForUseCard" self:notifyMoveFocus(player, card_name) cancelable = (cancelable == nil) and true or cancelable extra_data = extra_data or Util.DummyTable - pattern = pattern or card_name prompt = prompt or "" local askForUseCardData = { @@ -2020,8 +2010,6 @@ function Room:askForUseCard(player, card_name, pattern, prompt, cancelable, extr self.logic:trigger(fk.AskForCardUse, player, askForUseCardData) if askForUseCardData.result and type(askForUseCardData.result) == 'table' then - player.room:setPlayerMark(player, MarkEnum.BypassDistancesLimit .. "-tmp", 0) -- FIXME: 缺少直接传入无限制的手段 - player.room:setPlayerMark(player, MarkEnum.BypassTimesLimit .. "-tmp", 0) -- FIXME: 缺少直接传入无限制的手段 return askForUseCardData.result else local useResult @@ -2036,8 +2024,6 @@ function Room:askForUseCard(player, card_name, pattern, prompt, cancelable, extr Fk.currentResponsePattern = nil if result ~= "" then - player.room:setPlayerMark(player, MarkEnum.BypassDistancesLimit .. "-tmp", 0) -- FIXME: 缺少直接传入无限制的手段 - player.room:setPlayerMark(player, MarkEnum.BypassTimesLimit .. "-tmp", 0) -- FIXME: 缺少直接传入无限制的手段 useResult = self:handleUseCardReply(player, result) if type(useResult) == "string" and useResult ~= "" then @@ -2045,12 +2031,8 @@ function Room:askForUseCard(player, card_name, pattern, prompt, cancelable, extr end end until type(useResult) ~= "string" - player.room:setPlayerMark(player, MarkEnum.BypassDistancesLimit .. "-tmp", 0) -- FIXME: 缺少直接传入无限制的手段 - player.room:setPlayerMark(player, MarkEnum.BypassTimesLimit .. "-tmp", 0) -- FIXME: 缺少直接传入无限制的手段 return useResult end - player.room:setPlayerMark(player, MarkEnum.BypassDistancesLimit .. "-tmp", 0) -- FIXME: 缺少直接传入无限制的手段 - player.room:setPlayerMark(player, MarkEnum.BypassTimesLimit .. "-tmp", 0) -- FIXME: 缺少直接传入无限制的手段 return nil end @@ -3337,6 +3319,8 @@ function Room:shuffleDrawPile() self.discard_pile = {} table.shuffle(self.draw_pile) + self:doBroadcastNotify("UpdateDrawPile", #self.draw_pile) + self.logic:trigger(fk.AfterDrawPileShuffle, nil, {}) end diff --git a/lua/server/serverplayer.lua b/lua/server/serverplayer.lua index 75a26cae..754c5ced 100644 --- a/lua/server/serverplayer.lua +++ b/lua/server/serverplayer.lua @@ -923,7 +923,7 @@ function ServerPlayer:revealGeneral(isDeputy, no_trigger) end local general = Fk.generals[generalName] or Fk.generals["blank_shibing"] - for _, s in ipairs(general:getSkillNameList()) do + for _, s in ipairs(general:getSkillNameList(true)) do local skill = Fk.skills[s] self:loseFakeSkill(skill) end @@ -931,7 +931,7 @@ function ServerPlayer:revealGeneral(isDeputy, no_trigger) local ret = true if not ((isDeputy and self.general ~= "anjiang") or (not isDeputy and self.deputyGeneral ~= "anjiang")) then local other = Fk.generals[self:getMark(isDeputy and "__heg_general" or "__heg_deputy")] or Fk.generals["blank_shibing"] - for _, sname in ipairs(other:getSkillNameList()) do + for _, sname in ipairs(other:getSkillNameList(true)) do local s = Fk.skills[sname] if s.frequency == Skill.Compulsory and s.relate_to_place ~= (isDeputy and "m" or "d") then ret = false @@ -1017,7 +1017,7 @@ function ServerPlayer:revealBySkillName(skill_name) if main then if table.contains(Fk.generals[self:getMark("__heg_general")] - :getSkillNameList(), skill_name) then + :getSkillNameList(true), skill_name) then self:revealGeneral(false) return end @@ -1025,7 +1025,7 @@ function ServerPlayer:revealBySkillName(skill_name) if deputy then if table.contains(Fk.generals[self:getMark("__heg_deputy")] - :getSkillNameList(), skill_name) then + :getSkillNameList(true), skill_name) then self:revealGeneral(true) return end diff --git a/packages/maneuvering/init.lua b/packages/maneuvering/init.lua index a9e08227..cdcf2f20 100644 --- a/packages/maneuvering/init.lua +++ b/packages/maneuvering/init.lua @@ -109,13 +109,13 @@ local analepticSkill = fk.CreateActiveSkill{ prompt = "#analeptic_skill", max_turn_use_time = 1, mod_target_filter = function(self, to_select, _, _, card, _) - return self:withinTimesLimit(Fk:currentRoom():getPlayerById(to_select), Player.HistoryTurn, card, "analeptic", Fk:currentRoom():getPlayerById(to_select)) and - not table.find(Fk:currentRoom().alive_players, function(p) - return p.dying - end) + return not table.find(Fk:currentRoom().alive_players, function(p) + return p.dying + end) end, - can_use = function(self, player, card) - return self:withinTimesLimit(player, Player.HistoryTurn, card, "analeptic", player) + can_use = function(self, player, card, extra_data) + return ((extra_data and (extra_data.bypass_times or extra_data.analepticRecover)) or + self:withinTimesLimit(player, Player.HistoryTurn, card, "analeptic", player)) end, on_use = function(_, _, use) if not use.tos or #TargetGroup:getRealTargets(use.tos) == 0 then @@ -291,8 +291,9 @@ local supplyShortageSkill = fk.CreateActiveSkill{ local from = Fk:currentRoom():getPlayerById(user) return from ~= player and not (distance_limited and not self:withinDistanceLimit(from, false, card, player)) end, - target_filter = function(self, to_select, selected, _, card) - return #selected == 0 and self:modTargetFilter(to_select, selected, Self.id, card, true) + target_filter = function(self, to_select, selected, _, card, extra_data) + local count_distances = not (extra_data and extra_data.bypass_distances) + return #selected == 0 and self:modTargetFilter(to_select, selected, Self.id, card, count_distances) end, target_num = 1, on_effect = function(self, room, effect) diff --git a/packages/standard/aux_skills.lua b/packages/standard/aux_skills.lua index 310a0b0c..3cc3a116 100644 --- a/packages/standard/aux_skills.lua +++ b/packages/standard/aux_skills.lua @@ -207,7 +207,7 @@ local revealProhibited = fk.CreateInvaliditySkill { 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 + if table.contains(general:getSkillNameList(true), sname) then return true end end @@ -223,7 +223,7 @@ local revealSkill = fk.CreateActiveSkill{ local choiceList = {} if (Self.general == "anjiang" and not Self:prohibitReveal()) then local general = Fk.generals[Self:getMark("__heg_general")] - for _, sname in ipairs(general:getSkillNameList()) do + for _, sname in ipairs(general:getSkillNameList(true)) do local s = Fk.skills[sname] if s.frequency == Skill.Compulsory and s.relate_to_place ~= "m" then table.insert(choiceList, "revealMain") @@ -233,7 +233,7 @@ local revealSkill = fk.CreateActiveSkill{ end if (Self.deputyGeneral == "anjiang" and not Self:prohibitReveal(true)) then local general = Fk.generals[Self:getMark("__heg_deputy")] - for _, sname in ipairs(general:getSkillNameList()) do + for _, sname in ipairs(general:getSkillNameList(true)) do local s = Fk.skills[sname] if s.frequency == Skill.Compulsory and s.relate_to_place ~= "d" then table.insert(choiceList, "revealDeputy") diff --git a/packages/standard/game_rule.lua b/packages/standard/game_rule.lua index 72bb0ec8..3cc23b40 100644 --- a/packages/standard/game_rule.lua +++ b/packages/standard/game_rule.lua @@ -53,7 +53,7 @@ GameRule = fk.CreateTriggerSkill{ end) if #cardNames == 0 then return end - local peach_use = room:askForUseCard(player, "peach", table.concat(cardNames, ",") , prompt) + local peach_use = room:askForUseCard(player, "peach", table.concat(cardNames, ",") , prompt, true, {analepticRecover = true}) if not peach_use then break end peach_use.tos = { {dyingPlayer.id} } if peach_use.card.trueName == "analeptic" then diff --git a/packages/standard/init.lua b/packages/standard/init.lua index a1fbb3fb..aaea8820 100644 --- a/packages/standard/init.lua +++ b/packages/standard/init.lua @@ -1172,6 +1172,56 @@ local role_getlogic = function() room:broadcastProperty(lord, "kingdom") room:setDeputyGeneral(lord, deputy) room:broadcastProperty(lord, "deputyGeneral") + + -- 显示技能 + local canAttachSkill = function(player, skillName) + local skill = Fk.skills[skillName] + if not skill then + fk.qCritical("Skill: "..skillName.." doesn't exist!") + return false + end + if skill.lordSkill and (player.role ~= "lord" or #room.players < 5) then + return false + end + + if #skill.attachedKingdom > 0 and not table.contains(skill.attachedKingdom, player.kingdom) then + return false + end + + return true + end + + local lord_skills = {} + for _, s in ipairs(Fk.generals[lord.general].skills) do + if canAttachSkill(lord, s.name) then + table.insertIfNeed(lord_skills, s.name) + end + end + for _, sname in ipairs(Fk.generals[lord.general].other_skills) do + if canAttachSkill(lord, sname) then + table.insertIfNeed(lord_skills, sname) + end + end + + local deputyGeneral = Fk.generals[lord.deputyGeneral] + if deputyGeneral then + for _, s in ipairs(deputyGeneral.skills) do + if canAttachSkill(lord, s.name) then + table.insertIfNeed(lord_skills, s.name) + end + end + for _, sname in ipairs(deputyGeneral.other_skills) do + if canAttachSkill(lord, sname) then + table.insertIfNeed(lord_skills, sname) + end + end + end + for _, skill in ipairs(lord_skills) do + room:doBroadcastNotify("AddSkill", json.encode{ + lord.id, + skill + }) + end end local nonlord = room:getOtherPlayers(lord, true) diff --git a/packages/standard_cards/init.lua b/packages/standard_cards/init.lua index 26b674b0..abfacd02 100644 --- a/packages/standard_cards/init.lua +++ b/packages/standard_cards/init.lua @@ -20,8 +20,8 @@ local slashSkill = fk.CreateActiveSkill{ end, max_phase_use_time = 1, target_num = 1, - can_use = function(self, player, card) - return + can_use = function(self, player, card, extra_data) + return (extra_data and extra_data.bypass_times) or table.find(Fk:currentRoom().alive_players, function(p) return self:withinTimesLimit(player, Player.HistoryPhase, card, "slash", p) end) @@ -31,11 +31,12 @@ local slashSkill = fk.CreateActiveSkill{ local from = Fk:currentRoom():getPlayerById(user) return from ~= player and not (distance_limited and not self:withinDistanceLimit(from, true, card, player)) end, - target_filter = function(self, to_select, selected, _, card) + target_filter = function(self, to_select, selected, _, card, extra_data) + local count_distances = not (extra_data and extra_data.bypass_distances) if #selected < self:getMaxTargetNum(Self, card) then local player = Fk:currentRoom():getPlayerById(to_select) - return self:modTargetFilter(to_select, selected, Self.id, card, true) and - (#selected > 0 or self:withinTimesLimit(Self, Player.HistoryPhase, card, "slash", player)) + return self:modTargetFilter(to_select, selected, Self.id, card, count_distances) and + (#selected > 0 or (extra_data and extra_data.bypass_times) or self:withinTimesLimit(Self, Player.HistoryPhase, card, "slash", player)) end end, on_effect = function(self, room, effect) @@ -229,9 +230,10 @@ local snatchSkill = fk.CreateActiveSkill{ local from = Fk:currentRoom():getPlayerById(user) return from ~= player and not (player:isAllNude() or (distance_limited and not self:withinDistanceLimit(from, false, card, player))) end, - target_filter = function(self, to_select, selected, _, card) + target_filter = function(self, to_select, selected, _, card, extra_data) + local count_distances = not (extra_data and extra_data.bypass_distances) if #selected < self:getMaxTargetNum(Self, card) then - return self:modTargetFilter(to_select, selected, Self.id, card, true) + return self:modTargetFilter(to_select, selected, Self.id, card, count_distances) end end, target_num = 1, diff --git a/packages/test/init.lua b/packages/test/init.lua index 32d4d995..3ae6a71a 100644 --- a/packages/test/init.lua +++ b/packages/test/init.lua @@ -90,6 +90,8 @@ local control = fk.CreateActiveSkill{ -- room:setPlayerMark(from, "@$b", {'slash','duel','axe'}) --room:askForMiniGame({from}, "test", "test", { [from.id] = {"Helloworld"} }) --print(from.client_reply) + -- p(Fk.generals[to.general]:getSkillNameList()) + -- p(Fk.generals[to.general]:getSkillNameList(true)) if to:getMark("mouxushengcontrolled") == 0 then room:addPlayerMark(to, "mouxushengcontrolled") from:control(to) @@ -412,6 +414,11 @@ Fk:loadTranslationTable{ ["$change_hero"] = "敌军色厉内荏,可筑假城以退敌!", ["~mouxusheng"] = "来世,愿再为我江东之臣……", + + ["heal_hp"] = "回复体力", + ["lose_max_hp"] = "减少体力上限", + ["heal_max_hp"] = "增加体力上限", + ["revive"] = "复活", } return { extension } From be03b04ef0784c6e593d2a83c86f7ae21eaa38dc Mon Sep 17 00:00:00 2001 From: notify Date: Thu, 25 Jan 2024 03:23:29 +0800 Subject: [PATCH 06/30] =?UTF-8?q?=E5=B0=8F=E4=BF=AE=E5=A4=8D&Qml=E7=BE=8E?= =?UTF-8?q?=E5=8C=96=20(#308)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复了虚拟牌点数相加的bug - 修复cleaner出错直接爆炸 - QML的代码将宽度控制到80以内 --- Fk/ChatAnim/Egg.qml | 6 +- Fk/ChatAnim/Flower.qml | 6 +- Fk/ChatAnim/GiantEgg.qml | 6 +- Fk/ChatAnim/Shoe.qml | 3 +- Fk/ChatAnim/Wine.qml | 3 +- Fk/Cheat/CardDetail.qml | 4 +- Fk/Cheat/FreeAssign.qml | 16 +- Fk/Cheat/GeneralDetail.qml | 14 +- Fk/Cheat/PlayerDetail.qml | 39 ++-- Fk/Cheat/SameConvert.qml | 4 +- Fk/Common/Avatar.qml | 3 +- Fk/Common/AvatarChatBox.qml | 12 +- Fk/Common/ChatBox.qml | 7 +- Fk/LobbyElement/AudioSetting.qml | 8 +- Fk/LobbyElement/BGSetting.qml | 8 +- Fk/LobbyElement/BanGeneralSetting.qml | 30 +-- Fk/LobbyElement/CreateRoom.qml | 6 +- Fk/LobbyElement/EditProfile.qml | 6 +- Fk/LobbyElement/PersonalSettings.qml | 6 +- Fk/LobbyElement/RoomGeneralSettings.qml | 54 ++--- Fk/LobbyElement/RoomPackageSettings.qml | 29 +-- Fk/LobbyElement/UserInfo.qml | 15 +- Fk/Logic.js | 13 +- Fk/ModMaker/CreateSomething.qml | 4 +- Fk/ModMaker/ModConfig.qml | 3 +- Fk/ModMaker/ModDetail.qml | 16 +- Fk/ModMaker/ModInit.qml | 12 +- Fk/Pages/About.qml | 4 +- Fk/Pages/CardsOverview.qml | 72 +++--- Fk/Pages/GeneralsOverview.qml | 76 ++++--- Fk/Pages/Init.qml | 3 +- Fk/Pages/JoinServer.qml | 18 +- Fk/Pages/Lobby.qml | 47 ++-- Fk/Pages/ModesOverview.qml | 6 +- Fk/Pages/Replay.qml | 15 +- Fk/Pages/Room.qml | 213 +++++++++--------- Fk/Pages/RoomLogic.js | 282 ++++++++++++------------ Fk/PhotoElement/DelayedTrickArea.qml | 4 +- Fk/PhotoElement/EquipArea.qml | 12 +- Fk/PhotoElement/EquipItem.qml | 12 +- Fk/PhotoElement/HpBar.qml | 27 ++- Fk/PhotoElement/LimitSkillItem.qml | 7 +- Fk/PhotoElement/MarkArea.qml | 15 +- Fk/RoomElement/AG.qml | 4 +- Fk/RoomElement/CardArea.qml | 2 +- Fk/RoomElement/CardItem.qml | 13 +- Fk/RoomElement/CheckBox.qml | 22 +- Fk/RoomElement/ChoiceBox.qml | 17 +- Fk/RoomElement/ChooseGeneralBox.qml | 46 ++-- Fk/RoomElement/ChooseHandcard.qml | 4 +- Fk/RoomElement/Dashboard.qml | 111 ++++------ Fk/RoomElement/DetailedCheckBox.qml | 13 +- Fk/RoomElement/DetailedChoiceBox.qml | 6 +- Fk/RoomElement/GameOverBox.qml | 13 +- Fk/RoomElement/GeneralCardItem.qml | 24 +- Fk/RoomElement/GuanxingBox.qml | 7 +- Fk/RoomElement/HandcardArea.qml | 8 +- Fk/RoomElement/IndicatorLine.qml | 3 +- Fk/RoomElement/InvisibleCardArea.qml | 6 +- Fk/RoomElement/MiscStatus.qml | 2 +- Fk/RoomElement/MoveCardInBoardBox.qml | 9 +- Fk/RoomElement/Photo.qml | 51 +++-- Fk/RoomElement/PlayerCardBox.qml | 26 +-- Fk/RoomElement/PoxiBox.qml | 26 +-- Fk/RoomElement/SkillArea.qml | 5 +- Fk/RoomElement/UltSkillAnimation.qml | 10 +- Fk/RoomElement/ViewGeneralPile.qml | 2 +- Fk/RoomElement/ViewPile.qml | 4 +- Fk/SkillInteraction/SkillCombo.qml | 26 +-- Fk/SkillInteraction/SkillSpin.qml | 6 +- Fk/Splash.qml | 10 +- Fk/Toast.qml | 3 +- Fk/ToastManager.qml | 4 +- Fk/main.qml | 22 +- Fk/qmldir | 2 +- Fk/skin-bank.js | 27 ++- Fk/util.js | 18 +- lua/client/client_util.lua | 4 + lua/core/card.lua | 2 +- lua/core/engine.lua | 9 + lua/server/events/misc.lua | 5 +- packages/test/qml/TestDialog.qml | 2 +- src/core/packman.cpp | 2 +- 83 files changed, 914 insertions(+), 808 deletions(-) diff --git a/Fk/ChatAnim/Egg.qml b/Fk/ChatAnim/Egg.qml index f9ef7260..f5e68da1 100644 --- a/Fk/ChatAnim/Egg.qml +++ b/Fk/ChatAnim/Egg.qml @@ -46,7 +46,8 @@ Item { } 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 { @@ -91,7 +92,8 @@ Item { } 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 { diff --git a/Fk/ChatAnim/Flower.qml b/Fk/ChatAnim/Flower.qml index 179d744b..738e434e 100644 --- a/Fk/ChatAnim/Flower.qml +++ b/Fk/ChatAnim/Flower.qml @@ -43,7 +43,8 @@ Item { id: pointToAnimation running: false 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 { @@ -80,7 +81,8 @@ Item { } 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 { diff --git a/Fk/ChatAnim/GiantEgg.qml b/Fk/ChatAnim/GiantEgg.qml index ee784761..26a44869 100644 --- a/Fk/ChatAnim/GiantEgg.qml +++ b/Fk/ChatAnim/GiantEgg.qml @@ -46,7 +46,8 @@ Item { } 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 { @@ -91,7 +92,8 @@ Item { } 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 { diff --git a/Fk/ChatAnim/Shoe.qml b/Fk/ChatAnim/Shoe.qml index 269d4594..365606cc 100644 --- a/Fk/ChatAnim/Shoe.qml +++ b/Fk/ChatAnim/Shoe.qml @@ -64,7 +64,8 @@ Item { script: { egg.opacity = 0; 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 { diff --git a/Fk/ChatAnim/Wine.qml b/Fk/ChatAnim/Wine.qml index e5ebde6e..85b105a1 100644 --- a/Fk/ChatAnim/Wine.qml +++ b/Fk/ChatAnim/Wine.qml @@ -27,7 +27,8 @@ Item { y: start.y - height / 2 + yOffset scale: 0.7 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) } diff --git a/Fk/Cheat/CardDetail.qml b/Fk/Cheat/CardDetail.qml index cd069808..5a0eb8c1 100644 --- a/Fk/Cheat/CardDetail.qml +++ b/Fk/Cheat/CardDetail.qml @@ -57,7 +57,7 @@ Flickable { if (!card) return; cardPic.setData(card.toData()); const name = card.virt_name ? card.virt_name : card.name; - screenName.text = Backend.translate(name); - skillDesc.text = Backend.translate(":" + name); + screenName.text = luatr(name); + skillDesc.text = luatr(":" + name); } } diff --git a/Fk/Cheat/FreeAssign.qml b/Fk/Cheat/FreeAssign.qml index 6a610f42..1efcf805 100644 --- a/Fk/Cheat/FreeAssign.qml +++ b/Fk/Cheat/FreeAssign.qml @@ -21,12 +21,12 @@ Item { ToolButton { opacity: stack.depth > 1 ? 1 : 0 Behavior on opacity { NumberAnimation { duration: 100 } } - text: Backend.translate("Back") + text: luatr("Back") onClicked: stack.pop() } Label { - text: Backend.translate("Enable free assign") + text: luatr("Enable free assign") elide: Label.ElideRight horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter @@ -46,12 +46,11 @@ Item { } ToolButton { - text: Backend.translate("Search") + text: luatr("Search") enabled: word.text !== "" onClicked: { if (stack.depth > 1) stack.pop(); - generalModel = JSON.parse(Backend.callLuaFunction("SearchAllGenerals", - [word.text])); + generalModel = lcall("SearchAllGenerals", word.text); stack.push(generalList); word.text = ""; } @@ -88,14 +87,13 @@ Item { height: 40 Text { - text: Backend.translate(name) + text: luatr(name) color: "#E4D5A0" anchors.centerIn: parent } onClicked: { - generalModel = JSON.parse(Backend.callLuaFunction("GetGenerals", - [packages.get(index).name])); + generalModel = lcall("GetGenerals", packages.get(index).name); stack.push(generalList); } } @@ -134,7 +132,7 @@ Item { } function load() { - const packs = JSON.parse(Backend.callLuaFunction("GetAllGeneralPack", [])); + const packs = lcall("GetAllGeneralPack"); packs.forEach((name) => packages.append({ name: name })); } diff --git a/Fk/Cheat/GeneralDetail.qml b/Fk/Cheat/GeneralDetail.qml index 428c3d33..707f7068 100644 --- a/Fk/Cheat/GeneralDetail.qml +++ b/Fk/Cheat/GeneralDetail.qml @@ -39,21 +39,23 @@ Flickable { skillDesc.text = ""; extra_data.generals.forEach((g) => { - const data = JSON.parse(Backend.callLuaFunction("GetGeneralDetail", [g])); - skillDesc.append(Backend.translate(data.kingdom) + " " + Backend.translate(g) + " " + data.hp + "/" + data.maxHp); + const data = lcall("GetGeneralDetail", g); + skillDesc.append(luatr(data.kingdom) + " " + luatr(g) + " " + data.hp + + "/" + data.maxHp); if (data.companions.length > 0){ let ret = ''; - ret += "" + Backend.translate("Companions") + ": "; + ret +="" + luatr("Companions") + ": "; data.companions.forEach(t => { - ret += Backend.translate(t) + ' ' + ret += luatr(t) + ' ' }); skillDesc.append(ret) } data.skill.forEach(t => { - skillDesc.append("" + Backend.translate(t.name) + ": " + t.description) + skillDesc.append("" + luatr(t.name) + ": " + t.description) }); data.related_skill.forEach(t => { - skillDesc.append("" + Backend.translate(t.name) + ": " + t.description + "") + skillDesc.append("" + luatr(t.name) + + ": " + t.description + "") }); skillDesc.append("\n"); }); diff --git a/Fk/Cheat/PlayerDetail.qml b/Fk/Cheat/PlayerDetail.qml index a582a993..38088397 100644 --- a/Fk/Cheat/PlayerDetail.qml +++ b/Fk/Cheat/PlayerDetail.qml @@ -38,7 +38,7 @@ Flickable { RowLayout { MetroButton { - text: Backend.translate("Give Flower") + text: luatr("Give Flower") onClicked: { enabled = false; root.givePresent("Flower"); @@ -47,7 +47,7 @@ Flickable { } MetroButton { - text: Backend.translate("Give Egg") + text: luatr("Give Egg") onClicked: { enabled = false; if (Math.random() < 0.03) { @@ -60,7 +60,7 @@ Flickable { } MetroButton { - text: Backend.translate("Give Wine") + text: luatr("Give Wine") enabled: Math.random() < 0.3 onClicked: { enabled = false; @@ -70,7 +70,7 @@ Flickable { } MetroButton { - text: Backend.translate("Give Shoe") + text: luatr("Give Shoe") enabled: Math.random() < 0.3 onClicked: { enabled = false; @@ -80,7 +80,10 @@ Flickable { } 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 onClicked: { const idx = config.blockedUsers.indexOf(screenName.text); @@ -94,7 +97,7 @@ Flickable { } MetroButton { - text: Backend.translate("Kick From Room") + text: luatr("Kick From Room") visible: !roomScene.isStarted && roomScene.isOwner enabled: pid !== Self.id onClicked: { @@ -157,7 +160,7 @@ Flickable { skillDesc.text = ""; const id = extra_data.photo.playerid; - if (id == 0) return; + if (id === 0) return; root.pid = id; screenName.text = extra_data.photo.screenName; @@ -165,36 +168,34 @@ Flickable { deputyChara.name = extra_data.photo.deputyGeneral; if (!config.observing) { - const gamedata = JSON.parse(Backend.callLuaFunction("GetPlayerGameData", [id])); + const gamedata = lcall("GetPlayerGameData", id); const total = gamedata[0]; const win = gamedata[1]; const run = gamedata[2]; const totalTime = gamedata[3]; const winRate = (win / total) * 100; const runRate = (run / total) * 100; - playerGameData.text = total === 0 ? Backend.translate("Newbie") : - Backend.translate("Win=%1 Run=%2 Total=%3").arg(winRate.toFixed(2)) + playerGameData.text = total === 0 ? luatr("Newbie") : + luatr("Win=%1 Run=%2 Total=%3").arg(winRate.toFixed(2)) .arg(runRate.toFixed(2)).arg(total); const h = (totalTime / 3600).toFixed(2); const m = Math.floor(totalTime / 60); if (m < 100) { - playerGameData.text += " " + Backend.translate("TotalGameTime: %1 min").arg(m); + playerGameData.text += " " + luatr("TotalGameTime: %1 min").arg(m); } 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])); - data.forEach(t => { - skillDesc.append("" + Backend.translate(t.name) + ": " + t.description) + lcall("GetPlayerSkills", id).forEach(t => { + skillDesc.append("" + luatr(t.name) + ": " + t.description) }); - const equips = JSON.parse(Backend.callLuaFunction("GetPlayerEquips", [id])); - equips.forEach(cid => { - const t = JSON.parse(Backend.callLuaFunction("GetCardData", [cid])); + lcall("GetPlayerEquips", id).forEach(cid => { + const t = lcall("GetCardData", cid); skillDesc.append("--------------------"); - skillDesc.append("" + Backend.translate(t.name) + ": " + Backend.translate(":" + t.name)); + skillDesc.append("" + luatr(t.name) + ": " + luatr(":" + t.name)); }); } } diff --git a/Fk/Cheat/SameConvert.qml b/Fk/Cheat/SameConvert.qml index 6617ec50..31ccf4ca 100644 --- a/Fk/Cheat/SameConvert.qml +++ b/Fk/Cheat/SameConvert.qml @@ -28,13 +28,13 @@ Item { ColumnLayout { Text { color: "#E4D5A0" - text: Backend.translate(gname) + text: luatr(gname) } GridLayout { columns: 6 Repeater { - model: JSON.parse(Backend.callLuaFunction("GetSameGenerals", [gname])) + model: lcall("GetSameGenerals", gname) GeneralCardItem { name: modelData diff --git a/Fk/Common/Avatar.qml b/Fk/Common/Avatar.qml index 87eebd52..98444e05 100644 --- a/Fk/Common/Avatar.qml +++ b/Fk/Common/Avatar.qml @@ -6,7 +6,8 @@ Image { width: 64 height: 64 - source: SkinBank.getGeneralExtraPic(general, "avatar/") ?? SkinBank.getGeneralPicture(general) + source: SkinBank.getGeneralExtraPic(general, "avatar/") + ?? SkinBank.getGeneralPicture(general) // sourceSize.width: 250 // sourceSize.height: 292 property bool useSmallPic: !!SkinBank.getGeneralExtraPic(general, "avatar/") diff --git a/Fk/Common/AvatarChatBox.qml b/Fk/Common/AvatarChatBox.qml index b79c4d6a..d089ee43 100644 --- a/Fk/Common/AvatarChatBox.qml +++ b/Fk/Common/AvatarChatBox.qml @@ -18,7 +18,8 @@ Rectangle { avatar = "__observer"; } chatLogBox.append({ - avatar: data.general || roomScene.getPhoto(data.sender)?.general || avatar || "unknown", + avatar: data.general || roomScene.getPhoto(data.sender)?.general || + avatar || "unknown", general: general, msg: data.msg, userName: data.userName, @@ -60,7 +61,7 @@ Rectangle { anchors.right: !isSelf ? undefined : avatarPic.left anchors.margins: 6 font.pixelSize: 14 - text: userName + (general ? (" (" + Backend.translate(general) + ")") : "") + text: userName + (general ? (" (" + luatr(general) + ")") : "") + ' [' + time + "]" } @@ -123,7 +124,8 @@ Rectangle { anchors.centerIn: parent 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 { width: soundSelector.width height: 30 - text: Backend.translate("$" + name + (idx ? idx.toString() : "")) + text: luatr("$" + name + (idx ? idx.toString() : "")) onClicked: { opTimer.start(); const general = roomScene.getPhoto(Self.id).general; let skill = "fastchat_m"; if (general !== "") { - const data = JSON.parse(Backend.callLuaFunction("GetGeneralDetail", [general])); + const data = lcall("GetGeneralDetail", general); const gender = data.gender; if (gender !== 1) { skill = "fastchat_f"; diff --git a/Fk/Common/ChatBox.qml b/Fk/Common/ChatBox.qml index 058dae1c..c5a5d041 100644 --- a/Fk/Common/ChatBox.qml +++ b/Fk/Common/ChatBox.qml @@ -54,7 +54,8 @@ Rectangle { anchors.centerIn: parent 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 { width: soundSelector.width height: 30 - text: Backend.translate("$" + name + (idx ? idx.toString() : "")) + text: luatr("$" + name + (idx ? idx.toString() : "")) onClicked: { opTimer.start(); const general = roomScene.getPhoto(Self.id).general; let skill = "fastchat_m"; if (general !== "") { - const data = JSON.parse(Backend.callLuaFunction("GetGeneralDetail", [general])); + const data = lcall("GetGeneralDetail", general); const gender = data.gender; if (gender !== 1) { skill = "fastchat_f"; diff --git a/Fk/LobbyElement/AudioSetting.qml b/Fk/LobbyElement/AudioSetting.qml index 15ef8489..94a5e6c1 100644 --- a/Fk/LobbyElement/AudioSetting.qml +++ b/Fk/LobbyElement/AudioSetting.qml @@ -9,7 +9,7 @@ ColumnLayout { anchors.rightMargin: 8 spacing: 16 Text { - text: Backend.translate("BGM Volume") + text: luatr("BGM Volume") } Slider { Layout.rightMargin: 16 @@ -25,7 +25,7 @@ ColumnLayout { anchors.rightMargin: 8 spacing: 16 Text { - text: Backend.translate("Effect Volume") + text: luatr("Effect Volume") } Slider { Layout.rightMargin: 16 @@ -38,13 +38,13 @@ ColumnLayout { } Switch { - text: Backend.translate("Disable message audio") + text: luatr("Disable message audio") checked: config.disableMsgAudio onCheckedChanged: config.disableMsgAudio = checked; } Switch { - text: Backend.translate("Hide unselectable cards") + text: luatr("Hide unselectable cards") checked: config.hideUseless onCheckedChanged: { config.hideUseless = checked; diff --git a/Fk/LobbyElement/BGSetting.qml b/Fk/LobbyElement/BGSetting.qml index f6b279fb..5076ade5 100644 --- a/Fk/LobbyElement/BGSetting.qml +++ b/Fk/LobbyElement/BGSetting.qml @@ -10,7 +10,7 @@ ColumnLayout { anchors.rightMargin: 8 spacing: 16 Text { - text: Backend.translate("Lobby BG") + text: luatr("Lobby BG") } TextField { Layout.fillWidth: true @@ -30,7 +30,7 @@ ColumnLayout { anchors.rightMargin: 8 spacing: 16 Text { - text: Backend.translate("Room BG") + text: luatr("Room BG") } TextField { Layout.fillWidth: true @@ -50,7 +50,7 @@ ColumnLayout { anchors.rightMargin: 8 spacing: 16 Text { - text: Backend.translate("Game BGM") + text: luatr("Game BGM") } TextField { Layout.fillWidth: true @@ -70,7 +70,7 @@ ColumnLayout { anchors.rightMargin: 8 spacing: 16 Text { - text: Backend.translate("Poster Girl") + text: luatr("Poster Girl") } TextField { Layout.fillWidth: true diff --git a/Fk/LobbyElement/BanGeneralSetting.qml b/Fk/LobbyElement/BanGeneralSetting.qml index 7eac2671..63e6711d 100644 --- a/Fk/LobbyElement/BanGeneralSetting.qml +++ b/Fk/LobbyElement/BanGeneralSetting.qml @@ -14,7 +14,7 @@ Item { anchors.rightMargin: 8 spacing: 16 Text { - text: Backend.translate("Ban List") + text: luatr("Ban List") } ComboBox { id: banCombo @@ -30,54 +30,54 @@ Item { } Button { - text: Backend.translate("New") + text: luatr("New") onClicked: { const i = config.disableGeneralSchemes.length; banComboList.append({ - name: Backend.translate("List") + (i + 1), + name: luatr("List") + (i + 1), }); config.disableGeneralSchemes.push([]); } } Button { - text: Backend.translate("Clear") + text: luatr("Clear") onClicked: { config.disabledGenerals = []; } } Button { - text: Backend.translate("Export") + text: luatr("Export") onClicked: { Backend.copyToClipboard(JSON.stringify(config.disabledGenerals)); - toast.show(Backend.translate("Export Success")); + toast.show(luatr("Export Success")); } } Button { - text: Backend.translate("Import") + text: luatr("Import") onClicked: { const str = Backend.readClipboard(); let data; try { data = JSON.parse(str); } catch (e) { - toast.show(Backend.translate("Not Legal")); + toast.show(luatr("Not Legal")); return; } if (!data instanceof Array) { - toast.show(Backend.translate("Not JSON")); + toast.show(luatr("Not JSON")); return; } let d = []; for (let e of data) { - if (typeof e === "string" && Backend.translate(e) !== e) { + if (typeof e === "string" && luatr(e) !== e) { d.push(e); } } config.disabledGenerals = d; - toast.show(Backend.translate("Import Success")); + toast.show(luatr("Import Success")); } } } @@ -86,7 +86,7 @@ Item { Layout.fillWidth: true Layout.margins: 8 wrapMode: Text.WrapAnywhere - text: Backend.translate("Help_Ban_List") + text: luatr("Help_Ban_List") } GridView { @@ -101,9 +101,9 @@ Item { width: listView.width text: { const prefix = modelData.split("__")[0]; - let name = Backend.translate(modelData); + let name = luatr(modelData); if (prefix !== modelData) { - name += (" (" + Backend.translate(prefix) + ")"); + name += (" (" + luatr(prefix) + ")"); } return name; } @@ -115,7 +115,7 @@ Item { Component.onCompleted: { for (let i = 0; i < config.disableGeneralSchemes.length; i++) { banComboList.append({ - name: Backend.translate("List") + (i + 1), + name: luatr("List") + (i + 1), }); } banCombo.currentIndex = config.disableSchemeIdx; diff --git a/Fk/LobbyElement/CreateRoom.qml b/Fk/LobbyElement/CreateRoom.qml index 2449f536..64437deb 100644 --- a/Fk/LobbyElement/CreateRoom.qml +++ b/Fk/LobbyElement/CreateRoom.qml @@ -18,13 +18,13 @@ Item { width: root.height background: Rectangle { color: "#EEEEEEEE" } TabButton { - text: Backend.translate("General Settings") + text: luatr("General Settings") } TabButton { - text: Backend.translate("Package Settings") + text: luatr("Package Settings") } TabButton { - text: Backend.translate("Ban General Settings") + text: luatr("Ban General Settings") } } diff --git a/Fk/LobbyElement/EditProfile.qml b/Fk/LobbyElement/EditProfile.qml index c64651e2..12eeb658 100644 --- a/Fk/LobbyElement/EditProfile.qml +++ b/Fk/LobbyElement/EditProfile.qml @@ -17,13 +17,13 @@ Item { width: root.height background: Rectangle { color: "#EEEEEEEE" } TabButton { - text: Backend.translate("Userinfo Settings") + text: luatr("Userinfo Settings") } TabButton { - text: Backend.translate("BG Settings") + text: luatr("BG Settings") } TabButton { - text: Backend.translate("Audio Settings") + text: luatr("Audio Settings") } } diff --git a/Fk/LobbyElement/PersonalSettings.qml b/Fk/LobbyElement/PersonalSettings.qml index 70bba354..6a852614 100644 --- a/Fk/LobbyElement/PersonalSettings.qml +++ b/Fk/LobbyElement/PersonalSettings.qml @@ -23,14 +23,14 @@ Item { Text { text: { config.totalTime; - const gamedata = JSON.parse(Backend.callLuaFunction("GetPlayerGameData", [Self.id])); + const gamedata = lcall("GetPlayerGameData", Self.id); const totalTime = gamedata[3]; const h = (totalTime / 3600).toFixed(2); const m = Math.floor(totalTime / 60); if (m < 100) { - return Backend.translate("TotalGameTime: %1 min").arg(m); + return luatr("TotalGameTime: %1 min").arg(m); } else { - return Backend.translate("TotalGameTime: %1 h").arg(h); + return luatr("TotalGameTime: %1 h").arg(h); } } x: 12; y: 1 diff --git a/Fk/LobbyElement/RoomGeneralSettings.qml b/Fk/LobbyElement/RoomGeneralSettings.qml index 3e418f3b..ed7cca5b 100644 --- a/Fk/LobbyElement/RoomGeneralSettings.qml +++ b/Fk/LobbyElement/RoomGeneralSettings.qml @@ -16,7 +16,7 @@ Flickable { anchors.rightMargin: 8 spacing: 16 Text { - text: Backend.translate("Room Name") + text: luatr("Room Name") } TextField { id: roomName @@ -24,7 +24,7 @@ Flickable { font.pixelSize: 18 Layout.rightMargin: 16 Layout.fillWidth: true - text: Backend.translate("$RoomName").arg(Self.screenName) + text: luatr("$RoomName").arg(Self.screenName) } } @@ -32,7 +32,7 @@ Flickable { anchors.rightMargin: 8 spacing: 16 Text { - text: Backend.translate("Game Mode") + text: luatr("Game Mode") } ComboBox { id: gameModeCombo @@ -57,16 +57,16 @@ Flickable { columnSpacing: 20 columns: 4 Text { - text: Backend.translate("Player num") + text: luatr("Player num") } Text { - text: Backend.translate("Select generals num") + text: luatr("Select generals num") } Text { - text: Backend.translate("Operation timeout") + text: luatr("Operation timeout") } Text { - text: Backend.translate("Luck Card Times") + text: luatr("Luck Card Times") } SpinBox { id: playerNum @@ -114,11 +114,12 @@ Flickable { anchors.rightMargin: 8 visible: { //config.disabledPack; // 没什么用,只是为了禁包刷新时刷新visible罢了 - const avail = JSON.parse(Backend.callLuaFunction("GetAvailableGeneralsNum", [])); - const ret = avail < config.preferredGeneralNum * config.preferedPlayerNum; + const avail = lcall("GetAvailableGeneralsNum"); + const ret = avail < + config.preferredGeneralNum * config.preferedPlayerNum; return ret; } - text: Backend.translate("No enough generals") + text: luatr("No enough generals") color: "red" } @@ -126,7 +127,7 @@ Flickable { anchors.rightMargin: 8 spacing: 16 Text { - text: Backend.translate("Room Password") + text: luatr("Room Password") } TextField { id: roomPassword @@ -143,13 +144,13 @@ Flickable { Switch { id: freeAssignCheck checked: Debugging ? true : false - text: Backend.translate("Enable free assign") + text: luatr("Enable free assign") } Switch { id: deputyCheck checked: Debugging ? true : false - text: Backend.translate("Enable deputy general") + text: luatr("Enable deputy general") } } @@ -157,7 +158,7 @@ Flickable { anchors.rightMargin: 8 spacing: 16 Button { - text: Backend.translate("OK") + text: luatr("OK") enabled: !(warning.visible) onClicked: { config.saveConf(); @@ -166,10 +167,11 @@ Flickable { let disabledGenerals = config.disabledGenerals.slice(); if (disabledGenerals.length) { - const availablePack = JSON.parse(Backend.callLuaFunction("GetAllGeneralPack", [])). + const availablePack = lcall("GetAllGeneralPack"). filter((pack) => !config.disabledPack.includes(pack)); disabledGenerals = disabledGenerals.filter((general) => { - return availablePack.find((pack) => JSON.parse(Backend.callLuaFunction("GetGenerals", [pack])).includes(general)); + return availablePack.find(pack => + lcall("GetGenerals", pack).includes(general)); }); disabledGenerals = Array.from(new Set(disabledGenerals)); @@ -181,20 +183,22 @@ Flickable { disabledPack.push(p); } }); - const generalPacks = JSON.parse(Backend.callLuaFunction("GetAllGeneralPack", [])); + const generalPacks = lcall("GetAllGeneralPack"); for (let pk of generalPacks) { if (disabledPack.includes(pk)) continue; - let generals = JSON.parse(Backend.callLuaFunction("GetGenerals", [pk])); + let generals = lcall("GetGenerals", pk); let t = generals.filter(g => !disabledGenerals.includes(g)); if (t.length === 0) { disabledPack.push(pk); - disabledGenerals = disabledGenerals.filter(g1 => !generals.includes(g1)); + disabledGenerals = disabledGenerals + .filter(g1 => !generals.includes(g1)); } } ClientInstance.notifyServer( "CreateRoom", - JSON.stringify([roomName.text, playerNum.value, config.preferredTimeout, { + JSON.stringify([roomName.text, playerNum.value, + config.preferredTimeout, { enableFreeAssign: freeAssignCheck.checked, enableDeputy: deputyCheck.checked, gameMode: config.preferedMode, @@ -208,7 +212,7 @@ Flickable { } } Button { - text: Backend.translate("Cancel") + text: luatr("Cancel") onClicked: { root.finished(); } @@ -216,11 +220,11 @@ Flickable { } Component.onCompleted: { - const mode_data = JSON.parse(Backend.callLuaFunction("GetGameModes", [])); + const mode_data = lcall("GetGameModes"); let i = 0; for (let d of mode_data) { gameModeList.append(d); - if (d.orig_name == config.preferedMode) { + if (d.orig_name === config.preferedMode) { gameModeCombo.currentIndex = i; } i += 1; @@ -228,9 +232,7 @@ Flickable { playerNum.value = config.preferedPlayerNum; - config.disabledPack.forEach(p => { - Backend.callLuaFunction("UpdatePackageEnable", [p, false]); - }); + config.disabledPack.forEach(p => lcall("UpdatePackageEnable", p, false)); config.disabledPackChanged(); } } diff --git a/Fk/LobbyElement/RoomPackageSettings.qml b/Fk/LobbyElement/RoomPackageSettings.qml index 7b7edb13..fff8328f 100644 --- a/Fk/LobbyElement/RoomPackageSettings.qml +++ b/Fk/LobbyElement/RoomPackageSettings.qml @@ -23,16 +23,16 @@ Flickable { anchors.topMargin: 8 Switch { - text: Backend.translate("Disable Extension") + text: luatr("Disable Extension") } RowLayout { Text { - text: Backend.translate("General Packages") + text: luatr("General Packages") font.bold: true } Button { - text: Backend.translate("Select All") + text: luatr("Select All") onClicked: { for (let i = 0; i < gpacks.count; i++) { const item = gpacks.itemAt(i); @@ -41,7 +41,7 @@ Flickable { } } Button { - text: Backend.translate("Revert Selection") + text: luatr("Revert Selection") onClicked: { for (let i = 0; i < gpacks.count; i++) { const item = gpacks.itemAt(i); @@ -76,11 +76,11 @@ Flickable { RowLayout { Text { - text: Backend.translate("Card Packages") + text: luatr("Card Packages") font.bold: true } Button { - text: Backend.translate("Select All") + text: luatr("Select All") onClicked: { for (let i = 0; i < cpacks.count; i++) { const item = cpacks.itemAt(i); @@ -89,7 +89,7 @@ Flickable { } } Button { - text: Backend.translate("Revert Selection") + text: luatr("Revert Selection") onClicked: { for (let i = 0; i < cpacks.count; i++) { const item = cpacks.itemAt(i); @@ -128,31 +128,32 @@ Flickable { } else { packs.push(orig_name); } - Backend.callLuaFunction("UpdatePackageEnable", [orig_name, checked]); + lcall("UpdatePackageEnable", orig_name, checked); config.disabledPackChanged(); } Component.onCompleted: { loading = true; - const g = JSON.parse(Backend.callLuaFunction("GetAllGeneralPack", [])); - for (let orig of g) { + const g = lcall("GetAllGeneralPack"); + let orig; + for (orig of g) { if (config.serverHiddenPacks.includes(orig)) { continue; } gpacklist.append({ - name: Backend.translate(orig), + name: luatr(orig), orig_name: orig, pkg_enabled: !config.disabledPack.includes(orig), }); } - const c = JSON.parse(Backend.callLuaFunction("GetAllCardPack", [])); - for (let orig of c) { + const c = lcall("GetAllCardPack"); + for (orig of c) { if (config.serverHiddenPacks.includes(orig)) { continue; } cpacklist.append({ - name: Backend.translate(orig), + name: luatr(orig), orig_name: orig, pkg_enabled: !config.disabledPack.includes(orig), }); diff --git a/Fk/LobbyElement/UserInfo.qml b/Fk/LobbyElement/UserInfo.qml index c70fa7ac..a634ec81 100644 --- a/Fk/LobbyElement/UserInfo.qml +++ b/Fk/LobbyElement/UserInfo.qml @@ -11,7 +11,7 @@ ColumnLayout { anchors.rightMargin: 8 spacing: 16 Text { - text: Backend.translate("Username") + text: luatr("Username") } Text { text: Self.screenName @@ -28,7 +28,7 @@ ColumnLayout { anchors.rightMargin: 8 spacing: 16 Text { - text: Backend.translate("Avatar") + text: luatr("Avatar") } TextField { id: avatarName @@ -38,7 +38,7 @@ ColumnLayout { Layout.fillWidth: true } Button { - text: Backend.translate("Update Avatar") + text: luatr("Update Avatar") enabled: avatarName.text !== "" && !opTimer.running onClicked: { mainWindow.busy = true; @@ -55,7 +55,7 @@ ColumnLayout { anchors.rightMargin: 8 spacing: 16 Text { - text: Backend.translate("Old Password") + text: luatr("Old Password") } TextField { id: oldPassword @@ -70,7 +70,7 @@ ColumnLayout { anchors.rightMargin: 8 spacing: 16 Text { - text: Backend.translate("New Password") + text: luatr("New Password") } TextField { id: newPassword @@ -79,8 +79,9 @@ ColumnLayout { Layout.fillWidth: true } Button { - text: Backend.translate("Update Password") - enabled: oldPassword.text !== "" && newPassword.text !== "" && !opTimer.running + text: luatr("Update Password") + enabled: oldPassword.text !== "" && newPassword.text !== "" + && !opTimer.running onClicked: { mainWindow.busy = true; opTimer.start(); diff --git a/Fk/Logic.js b/Fk/Logic.js index 01eebe73..5e2ad89d 100644 --- a/Fk/Logic.js +++ b/Fk/Logic.js @@ -46,8 +46,8 @@ callbacks["NetworkDelayTest"] = (jsonData) => { // jsonData: RSA pub key let cipherText; let aeskey; - if (config.savedPassword[config.serverAddr] !== undefined - && config.savedPassword[config.serverAddr].shorten_password === config.password) { + const savedPw = config.savedPassword[config.serverAddr]; + if (savedPw?.shorten_password === config.password) { cipherText = config.savedPassword[config.serverAddr].password; aeskey = config.savedPassword[config.serverAddr].key; config.aeskey = aeskey ?? ""; @@ -173,7 +173,7 @@ callbacks["Chat"] = (jsonData) => { const data = JSON.parse(jsonData); const pid = data.sender; const userName = data.userName; - const general = Backend.translate(data.general); + const general = luatr(data.general); const time = data.time; const msg = data.msg; @@ -181,10 +181,13 @@ callbacks["Chat"] = (jsonData) => { return; } + let text; if (general === "") - current.addToChat(pid, data, `[${time}] ${userName}: ${msg}`); + text = `[${time}] ${userName}: ${msg}`; else - current.addToChat(pid, data, `[${time}] ${userName}(${general}): ${msg}`); + text = `[${time}] ${userName}` + + `(${general}): ${msg}`; + current.addToChat(pid, data, text); } callbacks["ServerMessage"] = (jsonData) => { diff --git a/Fk/ModMaker/CreateSomething.qml b/Fk/ModMaker/CreateSomething.qml index d3cc7485..6db0988c 100644 --- a/Fk/ModMaker/CreateSomething.qml +++ b/Fk/ModMaker/CreateSomething.qml @@ -36,7 +36,9 @@ ColumnLayout { id: edit font.pixelSize: 18 Layout.fillWidth: true - validator: RegularExpressionValidator { regularExpression: /[0-9A-Za-z_]+/ } + validator: RegularExpressionValidator { + regularExpression: /[0-9A-Za-z_]+/ + } } Button { diff --git a/Fk/ModMaker/ModConfig.qml b/Fk/ModMaker/ModConfig.qml index 51bfc119..2ac5e04a 100644 --- a/Fk/ModMaker/ModConfig.qml +++ b/Fk/ModMaker/ModConfig.qml @@ -19,7 +19,8 @@ QtObject { conf.email = email; 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) { diff --git a/Fk/ModMaker/ModDetail.qml b/Fk/ModMaker/ModDetail.qml index 780d392a..779ec4c9 100644 --- a/Fk/ModMaker/ModDetail.qml +++ b/Fk/ModMaker/ModDetail.qml @@ -62,7 +62,8 @@ Item { SwipeDelegate.onClicked: deletePackage(modelData); 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")); return; } + const path = modPath + new_name + "/"; ModBackend.mkdir(path); 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 = { 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(); } @@ -133,7 +139,9 @@ Item { const path = modPath + name + "/"; ModBackend.rmrf(path); 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(); } } diff --git a/Fk/ModMaker/ModInit.qml b/Fk/ModMaker/ModInit.qml index 80a6b569..dd6a6fbe 100644 --- a/Fk/ModMaker/ModInit.qml +++ b/Fk/ModMaker/ModInit.qml @@ -84,7 +84,8 @@ Item { SwipeDelegate.onClicked: deleteMod(modelData); 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) { 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")); return; } @@ -146,10 +148,12 @@ Item { descrption: "", 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.stageFiles(name); - ModBackend.commitChanges(name, "Initial commit", modConfig.userName, modConfig.email); + ModBackend.commitChanges(name, "Initial commit", modConfig.userName, + modConfig.email); modConfig.addMod(name); } diff --git a/Fk/Pages/About.qml b/Fk/Pages/About.qml index 19e16c21..a49b6b6c 100644 --- a/Fk/Pages/About.qml +++ b/Fk/Pages/About.qml @@ -50,7 +50,7 @@ Item { anchors.left: logo.right anchors.leftMargin: 16 width: parent.width * 0.65 - text: Backend.translate("about_" + dest + "_description") + text: luatr("about_" + dest + "_description") wrapMode: Text.WordWrap textFormat: Text.MarkdownText font.pixelSize: 18 @@ -73,7 +73,7 @@ Item { } Button { - text: Backend.translate("Quit") + text: luatr("Quit") anchors.right: parent.right onClicked: { swipe.opacity = 0; diff --git a/Fk/Pages/CardsOverview.qml b/Fk/Pages/CardsOverview.qml index a2716b58..70c95a11 100644 --- a/Fk/Pages/CardsOverview.qml +++ b/Fk/Pages/CardsOverview.qml @@ -3,6 +3,7 @@ import QtQuick import QtQuick.Layouts import QtQuick.Controls +import Fk import Fk.RoomElement Item { @@ -35,7 +36,7 @@ Item { height: 40 Text { - text: Backend.translate(name) + text: luatr(name) anchors.centerIn: parent } @@ -115,10 +116,9 @@ Item { easing.type: Easing.InOutQuad } onFinished: { - const pkg = [listView.model.get(listView.currentIndex).name]; - const idList = JSON.parse(Backend.callLuaFunction("GetCards", pkg)); - const cardList = idList.map(id => JSON.parse(Backend.callLuaFunction - ("GetCardData",[id]))); + const pkg = listView.model.get(listView.currentIndex).name; + const idList = lcall("GetCards", pkg); + const cardList = idList.map(id => lcall("GetCardData", id)); const groupedCardList = []; let groupedCards = {}; @@ -194,25 +194,11 @@ Item { property int cid: 1 property var cards function updateCard() { - const data = JSON.parse(Backend.callLuaFunction("GetCardData", [cid])); + const data = lcall("GetCardData", cid); const suitTable = { spade: "♠", heart: '', club: "♣", diamond: '', } - 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) { detailCard.setData(data); @@ -227,22 +213,21 @@ Item { detailCard.known = true; cardText.clear(); audioRow.clear(); - cardText.append(Backend.translate(":" + data.name)); + cardText.append(luatr(":" + data.name)); addCardAudio(data) - const skills = JSON.parse(Backend.callLuaFunction - ("GetCardSpecialSkills", [cid])); + const skills = lcall("GetCardSpecialSkills", cid); if (skills.length > 0) { - cardText.append("
" + Backend.translate("Special card skills:")); + cardText.append("
" + luatr("Special card skills:")); skills.forEach(t => { - cardText.append("" + Backend.translate(t) + ": " - + Backend.translate(":" + t)); + cardText.append("" + luatr(t) + ": " + + luatr(":" + t)); }); } if (cards) { - cardText.append("
" + Backend.translate("Every suit & number:")); + cardText.append("
" + luatr("Every suit & number:")); cardText.append(cards.map(c => { - return (suitTable[c.suit] + getNumString(c.number)) + return (suitTable[c.suit] + Util.convertNumber(c.number)) }).join(", ")); } } @@ -303,16 +288,17 @@ Item { verticalAlignment: Text.AlignVCenter text: { if (gender === "male") { - return Backend.translate("Male Audio"); + return luatr("Male Audio"); } else { - return Backend.translate("Female Audio"); + return luatr("Female Audio"); } } font.pixelSize: 14 } onClicked: { - const data = JSON.parse(Backend.callLuaFunction("GetCardData", [cardDetail.cid])); - Backend.playSound("./packages/" + extension + "/audio/card/" + gender + "/" + data.name); + const data = lcall("GetCardData", cardDetail.cid); + Backend.playSound("./packages/" + extension + "/audio/card/" + + gender + "/" + data.name); } } } @@ -322,7 +308,7 @@ Item { } Button { - text: Backend.translate("Quit") + text: luatr("Quit") anchors.right: parent.right onClicked: { mainStack.pop(); @@ -331,30 +317,32 @@ Item { function addCardAudio(card) { const extension = card.extension; - const orig_extension = Backend.callLuaFunction("GetCardExtensionByName", [card.name]); - let fname = AppPath + "/packages/" + extension + "/audio/card/male/" + card.name + ".mp3"; + const orig_extension = lcall("GetCardExtensionByName", card.name); + const prefix = AppPath + "/packages/"; + const suffix = card.name + ".mp3"; + let fname = prefix + extension + "/audio/card/male/" + suffix; if (Backend.exists(fname)) { - audioRow.append( {gender: "male", extension: extension} ); + audioRow.append( { gender: "male", extension: extension } ); } else { - fname = AppPath + "/packages/" + orig_extension + "/audio/card/male/" + card.name + ".mp3"; + fname = prefix + orig_extension + "/audio/card/male/" + suffix; if (Backend.exists(fname)) { 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)) { - audioRow.append( {gender: "female", extension: extension} ); + audioRow.append( { gender: "female", extension: extension } ); }else { - fname = AppPath + "/packages/" + orig_extension + "/audio/card/female/" + card.name + ".mp3"; + fname = prefix + orig_extension + "/audio/card/female/" + suffix; if (Backend.exists(fname)) { - audioRow.append( {gender: "female", extension: orig_extension} ); + audioRow.append( { gender: "female", extension: orig_extension } ); } } } function loadPackages() { if (loaded) return; - const packs = JSON.parse(Backend.callLuaFunction("GetAllCardPack", [])); + const packs = lcall("GetAllCardPack"); packs.forEach(name => { if (!config.serverHiddenPacks.includes(name)) { packages.append({ name: name }); diff --git a/Fk/Pages/GeneralsOverview.qml b/Fk/Pages/GeneralsOverview.qml index 51fc56a9..d1fae2fd 100644 --- a/Fk/Pages/GeneralsOverview.qml +++ b/Fk/Pages/GeneralsOverview.qml @@ -3,6 +3,7 @@ import QtQuick import QtQuick.Layouts import QtQuick.Controls +import Fk import Fk.RoomElement import "RoomLogic.js" as RoomLogic @@ -36,7 +37,7 @@ Item { height: 40 Text { - text: Backend.translate(name) + text: luatr(name) anchors.centerIn: parent } @@ -113,11 +114,10 @@ Item { } onFinished: { if (word.text !== "") { - gridView.model = JSON.parse(Backend.callLuaFunction("SearchAllGenerals", - [word.text])); + gridView.model = lcall("SearchAllGenerals", word.text); } else { - gridView.model = JSON.parse(Backend.callLuaFunction("SearchGenerals", - [listView.model.get(listView.currentIndex).name, word.text])); + gridView.model = lcall("SearchGenerals", + listView.model.get(listView.currentIndex).name, word.text); } word.text = ""; appearAnim.start(); @@ -159,7 +159,7 @@ Item { property string general: "caocao" function addSpecialSkillAudio(skill) { - const gdata = JSON.parse(Backend.callLuaFunction("GetGeneralData", [general])); + const gdata = lcall("GetGeneralData", general); const extension = gdata.extension; let ret = false; for (let i = 0; i < 999; i++) { @@ -178,7 +178,7 @@ Item { function addSkillAudio(skill) { if (addSpecialSkillAudio(skill)) return; - const skilldata = JSON.parse(Backend.callLuaFunction("GetSkillData", [skill])); + const skilldata = lcall("GetSkillData", skill); if (!skilldata) return; const extension = skilldata.extension; for (let i = 0; i < 999; i++) { @@ -194,8 +194,9 @@ Item { } function findDeathAudio(general) { - const extension = JSON.parse(Backend.callLuaFunction("GetGeneralData", [general])).extension; - const fname = AppPath + "/packages/" + extension + "/audio/death/" + general + ".mp3"; + const extension = lcall("GetGeneralData", general).extension; + const fname = AppPath + "/packages/" + extension + "/audio/death/" + + general + ".mp3"; if (Backend.exists(fname)) { audioDeath.visible = true; } else { @@ -205,27 +206,27 @@ Item { function updateGeneral() { detailGeneralCard.name = general; - const data = JSON.parse(Backend.callLuaFunction("GetGeneralDetail", [general])); + const data = lcall("GetGeneralDetail", general); generalText.clear(); audioModel.clear(); if (data.companions.length > 0){ - let ret = ''; - ret += "" + Backend.translate("Companions") + ": "; + let ret = "" + luatr("Companions") + + ": "; data.companions.forEach(t => { - ret += Backend.translate(t) + ' ' + ret += luatr(t) + ' ' }); generalText.append(ret) } data.skill.forEach(t => { - generalText.append("" + Backend.translate(t.name) + + generalText.append("" + luatr(t.name) + ": " + t.description); addSkillAudio(t.name); }); data.related_skill.forEach(t => { - generalText.append("" + Backend.translate(t.name) + + generalText.append("" + luatr(t.name) + ": " + t.description + ""); addSkillAudio(t.name); @@ -279,7 +280,8 @@ Item { if (name.endsWith("_win_audio")) { return "胜利语音"; } - return Backend.translate(name) + (idx ? " (" + idx.toString() + ")" : ""); + return luatr(name) + (idx ? " (" + idx.toString() + ")" + : ""); } font.bold: true font.pixelSize: 14 @@ -288,11 +290,12 @@ Item { Layout.fillWidth: true text: { const orig = '$' + name + (idx ? idx.toString() : ""); - const orig_trans = Backend.translate(orig); + const orig_trans = luatr(orig); // try general specific - const orig_g = '$' + name + '_' + detailGeneralCard.name + (idx ? idx.toString() : ""); - const orig_g_trans = Backend.translate(orig_g); + 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; @@ -325,21 +328,29 @@ Item { contentItem: ColumnLayout { Text { Layout.fillWidth: true - text: Backend.translate("Death audio") + text: luatr("Death audio") font.bold: true font.pixelSize: 14 } Text { Layout.fillWidth: true - text: Backend.translate("~" + generalDetail.general) == "~" + generalDetail.general ? "" : Backend.translate("~" + generalDetail.general) + text: { + const orig = "~" + generalDetail.general; + const tr = luatr(orig); + if (tr === orig) { + return ""; + } + return tr; + } wrapMode: Text.WordWrap } } onClicked: { const general = generalDetail.general - const extension = JSON.parse(Backend.callLuaFunction("GetGeneralData", [general])).extension; - Backend.playSound("./packages/" + extension + "/audio/death/" + general); + const extension = lcall("GetGeneralData", general).extension; + Backend.playSound("./packages/" + extension + "/audio/death/" + + general); } } } @@ -364,7 +375,7 @@ Item { } Button { - text: Backend.translate("Search") + text: luatr("Search") enabled: word.text !== "" onClicked: { listView.currentIndex = 0; @@ -378,7 +389,7 @@ Item { ColumnLayout { anchors.right: parent.right Button { - text: Backend.translate("Quit") + text: luatr("Quit") onClicked: { mainStack.pop(); config.saveConf(); @@ -387,14 +398,16 @@ Item { Button { id: banButton - text: Backend.translate(config.disabledGenerals.includes(detailGeneralCard.name) ? 'ResumeGeneral' : 'BanGeneral') + text: luatr(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 (banButton.text === luatr('ResumeGeneral')) { + const deleteIndex = disabledGenerals.findIndex( + (general) => general === name); if (deleteIndex === -1) { return; } @@ -417,8 +430,9 @@ Item { } Button { - text: Backend.translate("Set as Avatar") - enabled: detailGeneralCard.name !== "" && !opTimer.running && Self.avatar !== detailGeneralCard.name + text: luatr("Set as Avatar") + enabled: detailGeneralCard.name !== "" && !opTimer.running + && Self.avatar !== detailGeneralCard.name onClicked: { mainWindow.busy = true; opTimer.start(); @@ -432,7 +446,7 @@ Item { function loadPackages() { if (loaded) return; - const packs = JSON.parse(Backend.callLuaFunction("GetAllGeneralPack", [])); + const packs = lcall("GetAllGeneralPack"); packs.forEach(name => { if (!config.serverHiddenPacks.includes(name)) { packages.append({ name: name }); diff --git a/Fk/Pages/Init.qml b/Fk/Pages/Init.qml index 88eb55b9..ea636a9e 100644 --- a/Fk/Pages/Init.qml +++ b/Fk/Pages/Init.qml @@ -118,7 +118,8 @@ Item { TapHandler { onTapped: { - mainStack.push(Qt.createComponent("../Tutorial.qml").createObject()); + mainStack.push(Qt.createComponent("../Tutorial.qml") + .createObject()); } } } diff --git a/Fk/Pages/JoinServer.qml b/Fk/Pages/JoinServer.qml index 70414668..4a716f2b 100644 --- a/Fk/Pages/JoinServer.qml +++ b/Fk/Pages/JoinServer.qml @@ -204,7 +204,8 @@ Item { Layout.fillWidth: true placeholderText: qsTr("Password") text: "" - echoMode: showPasswordCheck.checked ? TextInput.Normal : TextInput.Password + echoMode: showPasswordCheck.checked ? TextInput.Normal + : TextInput.Password passwordCharacter: "*" } @@ -215,10 +216,12 @@ Item { Button { Layout.fillWidth: true - enabled: serverAddrEdit.text !== "" && screenNameEdit.text !== "" && passwordEdit.text !== "" + enabled: serverAddrEdit.text !== "" && screenNameEdit.text !== "" + && passwordEdit.text !== "" text: "OK" onClicked: { - root.addNewServer(serverAddrEdit.text, screenNameEdit.text, passwordEdit.text); + root.addNewServer(serverAddrEdit.text, screenNameEdit.text, + passwordEdit.text); finished(); } } @@ -259,7 +262,8 @@ Item { Layout.fillWidth: true placeholderText: qsTr("Password") text: "" - echoMode: showPasswordCheck.checked ? TextInput.Normal : TextInput.Password + echoMode: showPasswordCheck.checked ? TextInput.Normal + : TextInput.Password passwordCharacter: "*" } @@ -371,7 +375,11 @@ Item { const item = serverModel.get(i); const ip = item.serverIP; if (addr.endsWith(ip)) { // endsWith是为了应付IPv6格式的ip - item.misMatchMsg = FkVersion === ver ? "" : qsTr("@VersionMismatch").arg(ver); + item.misMatchMsg = ""; + if (FkVersion !== ver) { + item.misMatchMsg = qsTr("@VersionMismatch").arg(ver); + } + item.description = desc; item.favicon = icon; item.online = count.toString(); diff --git a/Fk/Pages/Lobby.qml b/Fk/Pages/Lobby.qml index 9e73489c..6971e25a 100644 --- a/Fk/Pages/Lobby.qml +++ b/Fk/Pages/Lobby.qml @@ -41,7 +41,7 @@ Item { width: parent.width wrapMode: TextEdit.WordWrap textFormat: Text.MarkdownText - text: config.serverMotd + "\n___\n" + Backend.translate('Bulletin Info') + text: config.serverMotd + "\n___\n" + luatr('Bulletin Info') } } } @@ -64,13 +64,13 @@ Item { Text { horizontalAlignment: Text.AlignLeft Layout.fillWidth: true - text: (hasPassword ? Backend.translate("Has Password") : "") + roomName + text: (hasPassword ? luatr("Has Password") : "") + roomName font.pixelSize: 20 elide: Label.ElideRight } Text { - text: Backend.translate(gameMode) + text: luatr(gameMode) } Text { @@ -81,8 +81,8 @@ Item { } Button { - text: (playerNum < capacity) ? Backend.translate("Enter") : - Backend.translate("Observe") + text: (playerNum < capacity) ? luatr("Enter") : + luatr("Observe") enabled: !opTimer.running @@ -124,7 +124,7 @@ Item { height: root.height - 80 Button { Layout.alignment: Qt.AlignRight - text: Backend.translate("Refresh Room List") + text: luatr("Refresh Room List") enabled: !opTimer.running onClicked: { opTimer.start(); @@ -142,7 +142,7 @@ Item { Text { width: parent.width horizontalAlignment: Text.AlignHCenter - text: Backend.translate("Room List").arg(roomModel.count) + text: luatr("Room List").arg(roomModel.count) } ListView { id: roomList @@ -166,9 +166,10 @@ Item { width: 120 display: AbstractButton.TextUnderIcon icon.name: "media-playback-start" - text: Backend.translate("Create Room") + text: luatr("Create Room") onClicked: { - lobby_dialog.sourceComponent = Qt.createComponent("../LobbyElement/CreateRoom.qml"); + lobby_dialog.sourceComponent = + Qt.createComponent("../LobbyElement/CreateRoom.qml"); lobby_drawer.open(); config.observing = false; config.replaying = false; @@ -180,33 +181,33 @@ Item { anchors.right: parent.right anchors.bottom: parent.bottom Button { - text: Backend.translate("Generals Overview") + text: luatr("Generals Overview") onClicked: { mainStack.push(mainWindow.generalsOverviewPage); mainStack.currentItem.loadPackages(); } } Button { - text: Backend.translate("Cards Overview") + text: luatr("Cards Overview") onClicked: { mainStack.push(mainWindow.cardsOverviewPage); mainStack.currentItem.loadPackages(); } } Button { - text: Backend.translate("Scenarios Overview") + text: luatr("Scenarios Overview") onClicked: { mainStack.push(mainWindow.modesOverviewPage); } } Button { - text: Backend.translate("Replay") + text: luatr("Replay") onClicked: { mainStack.push(mainWindow.replayPage); } } Button { - text: Backend.translate("About") + text: luatr("About") onClicked: { mainStack.push(mainWindow.aboutPage); } @@ -216,7 +217,7 @@ Item { Button { id: exitButton anchors.right: parent.right - text: Backend.translate("Exit Lobby") + text: luatr("Exit Lobby") display: AbstractButton.TextBesideIcon icon.name: "application-exit" onClicked: { @@ -269,7 +270,7 @@ Item { anchors.margins: 16 Text { - text: Backend.translate("Please input room's password") + text: luatr("Please input room's password") } TextField { @@ -295,7 +296,7 @@ Item { config.replaying = false; if (playerNum < capacity) { config.observing = false; - Backend.callLuaFunction("SetObserving", [false]); + lcall("SetObserving", false); mainWindow.busy = true; ClientInstance.notifyServer( "EnterRoom", @@ -303,7 +304,7 @@ Item { ); } else { config.observing = true; - Backend.callLuaFunction("SetObserving", [true]); + lcall("SetObserving", true); mainWindow.busy = true; ClientInstance.notifyServer( "ObserveRoom", @@ -334,7 +335,7 @@ Item { anchors.horizontalCenter: parent.horizontalCenter x: 4; y: 2 font.pixelSize: 16 - text: Backend.translate("$OnlineInfo") + text: luatr("$OnlineInfo") .arg(lobbyPlayerNum).arg(serverPlayerNum) + "\n" + "Powered by FreeKill " + FkVersion } @@ -357,8 +358,10 @@ Item { function addToChat(pid, raw, msg) { if (raw.type !== 1) return; - msg = msg.replace(/\{emoji([0-9]+)\}/g, ''); - raw.msg = raw.msg.replace(/\{emoji([0-9]+)\}/g, ''); + msg = msg.replace(/\{emoji([0-9]+)\}/g, + ''); + raw.msg = raw.msg.replace(/\{emoji([0-9]+)\}/g, + ''); lobbyChat.append(msg); danmaku.sendLog("" + raw.userName + ": " + raw.msg); } @@ -369,6 +372,6 @@ Item { } Component.onCompleted: { - toast.show(Backend.translate("$WelcomeToLobby")); + toast.show(luatr("$WelcomeToLobby")); } } diff --git a/Fk/Pages/ModesOverview.qml b/Fk/Pages/ModesOverview.qml index 6001b4f9..d97c84a5 100644 --- a/Fk/Pages/ModesOverview.qml +++ b/Fk/Pages/ModesOverview.qml @@ -51,7 +51,7 @@ Item { id: modeDesc width: parent.width - 16 wrapMode: Text.WordWrap - text: Backend.translate(":" + modeList.get(listView.currentIndex).orig_name) + text: luatr(":" + modeList.get(listView.currentIndex).orig_name) textFormat: Text.MarkdownText font.pixelSize: 16 } @@ -60,7 +60,7 @@ Item { } Button { - text: Backend.translate("Quit") + text: luatr("Quit") anchors.bottom: parent.bottom onClicked: { mainStack.pop(); @@ -68,7 +68,7 @@ Item { } Component.onCompleted: { - const mode_data = JSON.parse(Backend.callLuaFunction("GetGameModes", [])); + const mode_data = lcall("GetGameModes"); for (let d of mode_data) { modeList.append(d); } diff --git a/Fk/Pages/Replay.qml b/Fk/Pages/Replay.qml index 17261b65..daa26b89 100644 --- a/Fk/Pages/Replay.qml +++ b/Fk/Pages/Replay.qml @@ -20,7 +20,7 @@ Item { onClicked: mainStack.pop(); } Label { - text: Backend.translate("Replay Manager") + text: luatr("Replay Manager") horizontalAlignment: Qt.AlignHCenter Layout.fillWidth: true } @@ -75,8 +75,9 @@ Item { Text { text: { const win = winner.split("+").indexOf(role) !== -1; - const winStr = win ? Backend.translate("Game Win") : Backend.translate("Game Lose"); - return "" + Backend.translate(_general) + " " + Backend.translate(role) + " " + winStr; + const winStr = win ? luatr("Game Win") : luatr("Game Lose"); + return "" + luatr(_general) + " " + luatr(role) + + " " + winStr; } font.pixelSize: 20 textFormat: Text.RichText @@ -89,16 +90,16 @@ Item { const h = repDate.slice(8,10); const m = repDate.slice(10,12); 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 { id: replayBtn - text: Backend.translate("Play the Replay") + text: luatr("Play the Replay") anchors.right: delBtn.left anchors.rightMargin: 8 onClicked: { @@ -110,7 +111,7 @@ Item { Button { id: delBtn - text: Backend.translate("Delete Replay") + text: luatr("Delete Replay") anchors.right: parent.right anchors.rightMargin: 8 onClicked: { diff --git a/Fk/Pages/Room.qml b/Fk/Pages/Room.qml index c4844cc8..b83569fc 100644 --- a/Fk/Pages/Room.qml +++ b/Fk/Pages/Room.qml @@ -87,7 +87,7 @@ Item { anchors.topMargin: 12 anchors.right: parent.right anchors.rightMargin: 12 - text: Backend.translate("Menu") + text: luatr("Menu") onClicked: { if (menuContainer.visible){ menuContainer.close(); @@ -103,7 +103,7 @@ Item { MenuItem { id: quitButton - text: Backend.translate("Quit") + text: luatr("Quit") onClicked: { if (config.replaying) { Backend.controlReplayer("shutdown"); @@ -119,14 +119,18 @@ Item { MenuItem { id: surrenderButton enabled: !config.observing && !config.replaying - text: Backend.translate("Surrender") + text: luatr("Surrender") onClicked: { if (isStarted && !getPhoto(Self.id).dead) { - const surrenderCheck = JSON.parse(Backend.callLuaFunction('CheckSurrenderAvailable', [miscStatus.playedTime])); + const surrenderCheck = + lcall('CheckSurrenderAvailable', miscStatus.playedTime); if (!surrenderCheck.length) { - surrenderDialog.informativeText = Backend.translate('Surrender is disabled in this mode'); + surrenderDialog.informativeText = + luatr('Surrender is disabled in this mode'); } else { - surrenderDialog.informativeText = surrenderCheck.map(str => `${Backend.translate(str.text)}(${str.passed ? '√' : '×'})`).join('
'); + surrenderDialog.informativeText = surrenderCheck + .map(str => `${luatr(str.text)}(${str.passed ? '√' : '×'})`) + .join('
'); } surrenderDialog.open(); } @@ -135,7 +139,7 @@ Item { MenuItem { id: volumeButton - text: Backend.translate("Audio Settings") + text: luatr("Audio Settings") onClicked: { volumeDialog.open(); } @@ -144,7 +148,7 @@ Item { } Button { - text: Backend.translate("Add Robot") + text: luatr("Add Robot") visible: isOwner && !isStarted && !isFull anchors.centerIn: parent enabled: config.serverEnableBot @@ -153,7 +157,7 @@ Item { } } Button { - text: Backend.translate("Start Game") + text: luatr("Start Game") visible: isOwner && !isStarted && isFull enabled: isAllReady anchors.centerIn: parent @@ -166,7 +170,7 @@ Item { interval: 1000 } Button { - text: isReady ? Backend.translate("Cancel Ready") : Backend.translate("Ready") + text: isReady ? luatr("Cancel Ready") : luatr("Ready") visible: !isOwner && !isStarted enabled: !opTimer.running anchors.centerIn: parent @@ -237,25 +241,25 @@ Item { width: parent.width wrapMode: TextEdit.WordWrap Component.onCompleted: { - const data = JSON.parse(Backend.callLuaFunction("GetRoomConfig", [])); - let cardpack = JSON.parse(Backend.callLuaFunction("GetAllCardPack", [])); + const data = lcall("GetRoomConfig"); + let cardpack = lcall("GetAllCardPack"); cardpack = cardpack.filter(p => !data.disabledPack.includes(p)); - text = Backend.translate("GameMode") + Backend.translate(data.gameMode) + "
" - + Backend.translate("LuckCardNum") + "" + data.luckTime + "
" - + Backend.translate("ResponseTime") + "" + config.roomTimeout + "
" - + Backend.translate("GeneralBoxNum") + "" + data.generalNum + "" - + (data.enableFreeAssign ? "
" + Backend.translate("IncludeFreeAssign") : "") - + (data.enableDeputy ? " " + Backend.translate("IncludeDeputy") : "") - + '
' + Backend.translate('CardPackages') + cardpack.map(e => { - let ret = Backend.translate(e); - if (ret.search(/特殊牌|衍生牌/) === -1) { // TODO: 这种东西最好还是变量名规范化= = + text = luatr("GameMode") + luatr(data.gameMode) + "
" + + luatr("LuckCardNum") + "" + data.luckTime + "
" + + luatr("ResponseTime") + "" + config.roomTimeout + "
" + + luatr("GeneralBoxNum") + "" + data.generalNum + "" + + (data.enableFreeAssign ? "
" + luatr("IncludeFreeAssign") + : "") + + (data.enableDeputy ? " " + luatr("IncludeDeputy") : "") + + '
' + luatr('CardPackages') + cardpack.map(e => { + let ret = luatr(e); + // TODO: 这种东西最好还是变量名规范化= = + if (ret.search(/特殊牌|衍生牌/) === -1) { ret = "" + ret + ""; } return ret; - }).join(',') - //+ '
禁包:' + data.disabledPack.map(e => Backend.translate(e)).join(',') - //+ '
禁将:' + data.disabledGenerals.map(e => Backend.translate(e)).join(',') + }).join(','); } } } @@ -436,13 +440,14 @@ Item { anchors.leftMargin: 8 ColumnLayout { MetroButton { - text: Backend.translate("Choose one handcard") + text: luatr("Choose one handcard") textFont.pixelSize: 28 visible: { if (dashboard.handcardArea.length <= 15) { return false; } - if (roomScene.state == "notactive" || roomScene.state == "replying") { + if (roomScene.state === "notactive" + || roomScene.state === "replying") { return false; } return true; @@ -450,21 +455,21 @@ Item { onClicked: roomScene.startCheat("../RoomElement/ChooseHandcard"); } MetroButton { - text: Backend.translate("Revert Selection") + text: luatr("Revert Selection") textFont.pixelSize: 28 enabled: dashboard.pending_skill !== "" onClicked: dashboard.revertSelection(); } // MetroButton { - // text: Backend.translate("Trust") + // text: luatr("Trust") // } MetroButton { - text: Backend.translate("Sort Cards") + text: luatr("Sort Cards") textFont.pixelSize: 28 onClicked: Logic.resortHandcards(); } MetroButton { - text: Backend.translate("Chat") + text: luatr("Chat") textFont.pixelSize: 28 onClicked: roomDrawer.open(); } @@ -479,21 +484,21 @@ Item { onCardSelected: function(card) { Logic.enableTargets(card); - if (typeof card === "number" && card !== -1 && roomScene.state === "playing" - && JSON.parse(Backend.callLuaFunction("GetPlayerHandcards", [Self.id])).includes(card)) { + if (typeof card === "number" && card !== -1 + && roomScene.state === "playing" + && lcall("GetPlayerHandcards", Self.id).includes(card)) { - const skills = JSON.parse(Backend.callLuaFunction("GetCardSpecialSkills", [card])); - if (JSON.parse(Backend.callLuaFunction("CanUseCard", [card, Self.id, JSON.stringify(roomScene.extra_data)]))) { + const skills = lcall("GetCardSpecialSkills", card); + if (lcall("CanUseCard", card, Self.id, + JSON.stringify(roomScene.extra_data))) { skills.unshift("_normal_use"); } specialCardSkills.model = skills; - const skillName = Backend.callLuaFunction("GetCardSkill", [card]); - const prompt = JSON.parse(Backend.callLuaFunction( - "ActiveSkillPrompt", - [skillName, card, selected_targets] - )); + const skillName = lcall("GetCardSkill", card); + const prompt = lcall("ActiveSkillPrompt", skillName, card, + selected_targets); if (prompt !== "") { - roomScene.setPrompt(processPrompt(prompt)); + roomScene.setPrompt(Util.processPrompt(prompt)); } } else { specialCardSkills.model = []; @@ -524,18 +529,19 @@ Item { const totalMin = Math.floor(replayerDuration / 60); const totalSec = replayerDuration % 60; - return elapsedMin.toString() + ":" + elapsedSec + "/" + totalMin + ":" + totalSec; + return elapsedMin.toString() + ":" + elapsedSec + "/" + totalMin + + ":" + totalSec; } } Switch { - text: Backend.translate("Speed Resume") + text: luatr("Speed Resume") checked: false onCheckedChanged: Backend.controlReplayer("uniform"); } Button { - text: Backend.translate("Speed Down") + text: luatr("Speed Down") onClicked: Backend.controlReplayer("slowdown"); } @@ -546,13 +552,13 @@ Item { } Button { - text: Backend.translate("Speed Up") + text: luatr("Speed Up") onClicked: Backend.controlReplayer("speedup"); } Button { property bool running: true - text: Backend.translate(running ? "Pause" : "Resume") + text: luatr(running ? "Pause" : "Resume") onClicked: { running = !running; Backend.controlReplayer("toggle"); @@ -657,7 +663,7 @@ Item { id: specialCardSkills RadioButton { property string orig_text: modelData - text: Backend.translate(modelData) + text: luatr(modelData) checked: index === 0 onCheckedChanged: { roomScene.resetPrompt(); @@ -665,23 +671,19 @@ Item { let prompt = "" if (modelData === "_normal_use") { Logic.enableTargets(card); - const skillName = Backend.callLuaFunction("GetCardSkill", [card]); - prompt = JSON.parse(Backend.callLuaFunction( - "ActiveSkillPrompt", - [skillName, card, selected_targets] - )); + const skillName = lcall("GetCardSkill", card); + prompt = lcall("ActiveSkillPrompt", skillName, card, + selected_targets); } else { Logic.enableTargets(JSON.stringify({ skill: modelData, subcards: [card], })); - prompt = JSON.parse(Backend.callLuaFunction( - "ActiveSkillPrompt", - [modelData, card, selected_targets] - )); + prompt = lcall("ActiveSkillPrompt", modelData, card, + selected_targets); } if (prompt !== "") { - roomScene.setPrompt(processPrompt(prompt)); + roomScene.setPrompt(Util.processPrompt(prompt)); } } } @@ -707,8 +709,9 @@ Item { Button { id: skipNullificationButton - text: Backend.translate("SkipNullification") - visible: !!extra_data.useEventId && !skippedUseEventId.find(id => id === extra_data.useEventId) + text: luatr("SkipNullification") + visible: !!extra_data.useEventId + && !skippedUseEventId.find(id => id === extra_data.useEventId) onClicked: { skippedUseEventId.push(extra_data.useEventId); Logic.doCancelButton(); @@ -717,20 +720,20 @@ Item { Button { id: okButton - text: Backend.translate("OK") + text: luatr("OK") onClicked: Logic.doOkButton(); } Button { id: cancelButton - text: Backend.translate("Cancel") + text: luatr("Cancel") onClicked: Logic.doCancelButton(); } } Button { id: endPhaseButton - text: Backend.translate("End") + text: luatr("End") anchors.bottom: parent.bottom anchors.bottomMargin: 40 anchors.right: parent.right @@ -793,12 +796,13 @@ Item { function activateSkill(skill_name, pressed) { if (pressed) { - const data = JSON.parse(Backend.callLuaFunction("GetInteractionOfSkill", [skill_name])); + const data = lcall("GetInteractionOfSkill", skill_name); if (data) { - Backend.callLuaFunction("SetInteractionDataOfSkill", [skill_name, "null"]); + lcall("SetInteractionDataOfSkill", skill_name, "null"); switch (data.type) { case "combo": - skillInteraction.sourceComponent = Qt.createComponent("../SkillInteraction/SkillCombo.qml"); + skillInteraction.sourceComponent = + Qt.createComponent("../SkillInteraction/SkillCombo.qml"); skillInteraction.item.skill = skill_name; skillInteraction.item.default_choice = data["default"]; skillInteraction.item.choices = data.choices; @@ -807,7 +811,8 @@ Item { // skillInteraction.item.clicked(); break; case "spin": - skillInteraction.sourceComponent = Qt.createComponent("../SkillInteraction/SkillSpin.qml"); + skillInteraction.sourceComponent = + Qt.createComponent("../SkillInteraction/SkillSpin.qml"); skillInteraction.item.skill = skill_name; skillInteraction.item.from = data.from; skillInteraction.item.to = data.to; @@ -866,11 +871,11 @@ Item { width: roomDrawer.width TabButton { width: roomDrawer.width / 2 - text: Backend.translate("Log") + text: luatr("Log") } TabButton { width: roomDrawer.width / 2 - text: Backend.translate("Chat") + text: luatr("Chat") } } } @@ -913,8 +918,8 @@ Item { MessageDialog { id: quitDialog - title: Backend.translate("Quit") - informativeText: Backend.translate("Are you sure to quit?") + title: luatr("Quit") + informativeText: luatr("Are you sure to quit?") buttons: MessageDialog.Ok | MessageDialog.Cancel onButtonClicked: function (button) { switch (button) { @@ -931,14 +936,17 @@ Item { MessageDialog { id: surrenderDialog - title: Backend.translate("Surrender") + title: luatr("Surrender") informativeText: '' buttons: MessageDialog.Ok | MessageDialog.Cancel onButtonClicked: function (button, role) { switch (button) { case MessageDialog.Ok: { - const surrenderCheck = JSON.parse(Backend.callLuaFunction('CheckSurrenderAvailable', [miscStatus.playedTime])); - if (surrenderCheck.length && !surrenderCheck.find(check => !check.passed)) { + const surrenderCheck = + lcall('CheckSurrenderAvailable', miscStatus.playedTime); + if (surrenderCheck.length && + !surrenderCheck.find(check => !check.passed)) { + ClientInstance.notifyServer("PushRequest", [ "surrender", true ]); @@ -977,7 +985,7 @@ Item { GlowText { anchors.centerIn: dashboard visible: Logic.getPhoto(Self.id).rest > 0 && !config.observing - text: Backend.translate("Resting, don't leave!") + text: luatr("Resting, don't leave!") color: "#DBCC69" font.family: fontLibian.name font.pixelSize: 28 @@ -991,7 +999,7 @@ Item { color: "transparent" GlowText { anchors.centerIn: parent - text: Backend.translate("Observing ...") + text: luatr("Observing ...") color: "#4B83CD" font.family: fontLi2.name font.pixelSize: 48 @@ -1096,20 +1104,9 @@ Item { 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() { - if (specialCardSkills.count === 1 && specialCardSkills.model[0] !== "_normal_use") { + if (specialCardSkills.count === 1 + && specialCardSkills.model[0] !== "_normal_use") { return specialCardSkills.model[0]; } @@ -1125,8 +1122,10 @@ Item { function addToChat(pid, raw, msg) { if (raw.type === 1) return; - msg = msg.replace(/\{emoji([0-9]+)\}/g, ''); - raw.msg = raw.msg.replace(/\{emoji([0-9]+)\}/g, ''); + msg = msg.replace(/\{emoji([0-9]+)\}/g, + ''); + raw.msg = raw.msg.replace(/\{emoji([0-9]+)\}/g, + ''); if (raw.msg.startsWith("$")) { if (specialChat(pid, raw, raw.msg.slice(1))) return; @@ -1149,7 +1148,7 @@ Item { const time = data.time; const userName = data.userName; - const general = Backend.translate(data.general); + const general = luatr(data.general); if (msg.startsWith("!")) { const splited = msg.split(":"); @@ -1167,10 +1166,15 @@ Item { // return false; 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 toPos = mapFromItem(toItem, toItem.width / 2, toItem.height / 2); - const egg = component.createObject(roomScene, { start: fromPos, end: toPos }); + const toPos = mapFromItem(toItem, toItem.width / 2, + toItem.height / 2); + const egg = component.createObject(roomScene, { + start: fromPos, + end: toPos + }); egg.finished.connect(() => egg.destroy()); egg.running = true; @@ -1181,11 +1185,11 @@ Item { } } else if (msg.startsWith("~")) { const g = msg.slice(1); - const extension = JSON.parse(Backend.callLuaFunction("GetGeneralData", [g])).extension; + const extension = lcall("GetGeneralData", g).extension; if (!config.disableMsgAudio) Backend.playSound("./packages/" + extension + "/audio/death/" + g); - const m = Backend.translate("~" + g); + const m = luatr("~" + g); data.msg = m; if (general === "") chat.append(`[${time}] ${userName}: ${m}`, data); @@ -1215,7 +1219,8 @@ Item { i: idx, })); } catch (e) {} - const m = Backend.translate("$" + skill + (gene ? "_" + gene : "") + (idx ? idx.toString() : "")); + const m = luatr("$" + skill + (gene ? "_" + gene : "") + + (idx ? idx.toString() : "")); data.msg = m; if (general === "") chat.append(`[${time}] ${userName}: ${m}`, data); @@ -1253,8 +1258,7 @@ Item { for (let i = 0; i < photoModel.count; i++) { const item = photos.itemAt(i); if (show) { - const dis = Backend.callLuaFunction("DistanceTo",[Self.id, item.playerid]); - item.distance = parseInt(dis); + item.distance = lcall("DistanceTo", Self.id, item.playerid); } else { item.distance = -1; } @@ -1273,7 +1277,7 @@ Item { const item = photoModel.get(i); let gameData; try { - gameData = JSON.parse(Backend.callLuaFunction("GetPlayerGameData", [item.id])); + gameData = lcall("GetPlayerGameData", item.id); } catch (e) { console.log(e); gameData = [0, 0, 0, 0]; @@ -1290,7 +1294,7 @@ Item { } } mainStack.pop(); - Backend.callLuaFunction("ResetClientLua", []); + lcall("ResetClientLua"); mainStack.push(room); mainStack.currentItem.loadPlayerData(datalist); } @@ -1306,12 +1310,13 @@ Item { function loadPlayerData(datalist) { datalist.forEach(d => { - if (d.id == Self.id) { + if (d.id === Self.id) { roomScene.isOwner = d.isOwner; } 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; }); } @@ -1321,7 +1326,7 @@ Item { } Component.onCompleted: { - toast.show(Backend.translate("$EnterRoom")); + toast.show(luatr("$EnterRoom")); playerNum = config.roomCapacity; for (let i = 0; i < playerNum; i++) { diff --git a/Fk/Pages/RoomLogic.js b/Fk/Pages/RoomLogic.js index faa991df..8d3eca11 100644 --- a/Fk/Pages/RoomLogic.js +++ b/Fk/Pages/RoomLogic.js @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later +.import Fk.Util as Util + const Card = { Unknown : 0, PlayerHand : 1, @@ -28,11 +30,13 @@ function arrangeManyPhotos() { const roomAreaPadding = -16; let horizontalSpacing = 8; - let photoWidth = (roomArea.width - horizontalSpacing * playerNum) / (playerNum - 1); + let photoWidth = (roomArea.width - horizontalSpacing * playerNum) + / (playerNum - 1); let photoScale = 0.75; if (photoWidth > photoMaxWidth) { photoWidth = photoMaxWidth; - horizontalSpacing = (roomArea.width - photoWidth * (playerNum - 1)) / playerNum; + horizontalSpacing = (roomArea.width - photoWidth * (playerNum - 1)) + / playerNum; } else { photoScale = photoWidth / photoBaseWidth; } @@ -134,14 +138,13 @@ function arrangePhotos() { function doOkButton() { if (roomScene.state === "playing" || roomScene.state === "responding") { - const reply = JSON.stringify( - { - card: dashboard.getSelectedCard(), - targets: selected_targets, - special_skill: roomScene.getCurrentCardUseMethod(), - interaction_data: roomScene.skillInteraction.item ? roomScene.skillInteraction.item.answer : undefined, - } - ); + const reply = JSON.stringify({ + card: dashboard.getSelectedCard(), + targets: selected_targets, + special_skill: roomScene.getCurrentCardUseMethod(), + interaction_data: roomScene.skillInteraction.item ? + roomScene.skillInteraction.item.answer : undefined, + }); replyToServer(reply); return; } @@ -244,7 +247,8 @@ function getPhotoOrDashboard(id) { function getAreaItem(area, id) { if (area === Card.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; } else if (area === Card.AG) { return popupBox.item; @@ -372,7 +376,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; if (isCardId) { animation.started.connect(() => photo.busy = true); @@ -452,7 +457,8 @@ function doIndicate(from, tos) { return; 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 = []; for (let i = 0; i < tos.length; i++) { @@ -464,7 +470,11 @@ function doIndicate(from, tos) { } 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.running = true; } @@ -480,7 +490,7 @@ callbacks["MaxCard"] = (jsonData) => { } function changeSelf(id) { - Backend.callLuaFunction("ChangeSelf", [id]); + lcall("ChangeSelf", id); // move new selfPhoto to dashboard let order = new Array(photoModel.count); @@ -533,15 +543,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) { - const candidate = (!isNaN(card) && card !== -1) || typeof(card) === "string"; + const candidate = (!isNaN(card) && card !== -1) + || typeof(card) === "string"; if (candidate) { - okButton.enabled = JSON.parse(Backend.callLuaFunction( - "CardFitPattern", - [card, roomScene.responding_card] - )) && !JSON.parse(Backend.callLuaFunction( - "CardProhibitedResponse", [card])); + okButton.enabled = + lcall("CardFitPattern", card, roomScene.responding_card) && + !lcall("CardProhibitedResponse", card); } else { okButton.enabled = false; } @@ -568,10 +578,8 @@ function enableTargets(card) { // card: int | { skill: string, subcards: int[] } all_photos.forEach(photo => { photo.state = "candidate"; const id = photo.playerid; - const ret = JSON.parse(Backend.callLuaFunction( - "CanUseCardToTarget", - [card, id, selected_targets, JSON.stringify(roomScene.extra_data)] - )); + const ret = lcall("CanUseCardToTarget", card, id, selected_targets, + JSON.stringify(roomScene.extra_data)); photo.selectable = ret; if (roomScene.extra_data instanceof Object) { const must = roomScene.extra_data.must_targets; @@ -588,22 +596,20 @@ function enableTargets(card) { // card: int | { skill: string, subcards: int[] } if (included instanceof Array) { if (included.filter((val) => { 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( - "CardFeasible", [card, selected_targets] - )); + okButton.enabled = lcall("CardFeasible", card, selected_targets); if (okButton.enabled && roomScene.state === "responding") { - okButton.enabled = JSON.parse(Backend.callLuaFunction( - "CardFitPattern", - [card, roomScene.responding_card] - )) && (roomScene.autoPending || !JSON.parse(Backend.callLuaFunction( - "CardProhibitedUse", [card]))); + okButton.enabled = + lcall("CardFitPattern", card, roomScene.responding_card) && + (roomScene.autoPending || !lcall("CardProhibitedUse", card)); } else if (okButton.enabled && roomScene.state === "playing") { - okButton.enabled = JSON.parse(Backend.callLuaFunction("CanUseCard", [card, Self.id, JSON.stringify(roomScene.extra_data)])); + okButton.enabled = lcall("CanUseCard", card, Self.id, + JSON.stringify(roomScene.extra_data)); } if (okButton.enabled) { if (roomScene.extra_data instanceof Object) { @@ -646,10 +652,8 @@ function updateSelectedTargets(playerid, selected) { all_photos.forEach(photo => { if (photo.selected) return; const id = photo.playerid; - const ret = JSON.parse(Backend.callLuaFunction( - "CanUseCardToTarget", - [card, id, selected_targets, JSON.stringify(roomScene.extra_data)] - )); + const ret = lcall("CanUseCardToTarget", card, id, selected_targets, + JSON.stringify(roomScene.extra_data)); photo.selectable = ret; if (roomScene.extra_data instanceof Object) { const must = roomScene.extra_data.must_targets; @@ -666,22 +670,20 @@ function updateSelectedTargets(playerid, selected) { if (included instanceof Array) { if (included.filter((val) => { 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( - "CardFeasible", [card, selected_targets] - )); + okButton.enabled = lcall("CardFeasible", card, selected_targets); if (okButton.enabled && roomScene.state === "responding") { - okButton.enabled = JSON.parse(Backend.callLuaFunction( - "CardFitPattern", - [card, roomScene.responding_card] - )) && (roomScene.autoPending || !JSON.parse(Backend.callLuaFunction( - "CardProhibitedUse", [card]))); + okButton.enabled = + lcall("CardFitPattern", card, roomScene.responding_card) && + (roomScene.autoPending || !lcall("CardProhibitedUse", card)); } else if (okButton.enabled && roomScene.state === "playing") { - okButton.enabled = JSON.parse(Backend.callLuaFunction("CanUseCard", [card, Self.id, JSON.stringify(roomScene.extra_data)])); + okButton.enabled = lcall("CanUseCard", card, Self.id, + JSON.stringify(roomScene.extra_data)); } if (okButton.enabled) { if (roomScene.extra_data instanceof Object) { @@ -819,8 +821,7 @@ callbacks["UpdateCard"] = (j) => { return; } - const data = JSON.parse(Backend.callLuaFunction("GetCardData", [id])); - card.setData(data); + card.setData(lcall("GetCardData", id)); } callbacks["StartGame"] = (jsonData) => { @@ -878,8 +879,8 @@ callbacks["MoveFocus"] = (jsonData) => { if (focuses.indexOf(model.id) != -1) { item = photos.itemAt(i); item.progressBar.visible = true; - item.progressTip = Backend.translate(command) - + Backend.translate(" thinking..."); + item.progressTip = luatr(command) + + luatr(" thinking..."); /* if (command === "PlayCard") { @@ -914,9 +915,10 @@ callbacks["AskForGeneral"] = (jsonData) => { const n = data[1]; const convert = data[2]; const heg = data[3]; - roomScene.setPrompt(Backend.translate("#AskForGeneral"), true); + roomScene.setPrompt(luatr("#AskForGeneral"), true); roomScene.state = "replying"; - roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/ChooseGeneralBox.qml"); + roomScene.popupBox.sourceComponent = + Qt.createComponent("../RoomElement/ChooseGeneralBox.qml"); const box = roomScene.popupBox.item; box.accepted.connect(() => { replyToServer(JSON.stringify(box.choices)); @@ -934,8 +936,8 @@ callbacks["AskForSkillInvoke"] = (jsonData) => { const data = JSON.parse(jsonData); const skill = data[0]; const prompt = data[1]; - roomScene.promptText = prompt ? processPrompt(prompt) : Backend.translate("#AskForSkillInvoke") - .arg(Backend.translate(skill)); + roomScene.promptText = prompt ? Util.processPrompt(prompt) + : luatr("#AskForSkillInvoke").arg(luatr(skill)); roomScene.state = "replying"; roomScene.okCancel.visible = true; roomScene.okButton.enabled = true; @@ -953,26 +955,24 @@ callbacks["AskForGuanxing"] = (jsonData) => { const bottom_area_name = data.bottom_area_name; const prompt = data.prompt; roomScene.state = "replying"; - roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/GuanxingBox.qml"); - data.cards.forEach(id => { - const d = Backend.callLuaFunction("GetCardData", [id]); - cards.push(JSON.parse(d)); - }); + roomScene.popupBox.sourceComponent = + Qt.createComponent("../RoomElement/GuanxingBox.qml"); + data.cards.forEach(id => cards.push(lcall("GetCardData", id))); const box = roomScene.popupBox.item; box.prompt = prompt; if (max_top_cards === 0) { box.areaCapacities = [max_bottom_cards]; box.areaLimits = [min_bottom_cards]; - box.areaNames = [Backend.translate(bottom_area_name)]; + box.areaNames = [luatr(bottom_area_name)]; } else { if (max_bottom_cards === 0) { box.areaCapacities = [max_top_cards]; box.areaLimits = [min_top_cards]; - box.areaNames = [Backend.translate(top_area_name)]; + box.areaNames = [luatr(top_area_name)]; } else { box.areaCapacities = [max_top_cards, max_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; @@ -989,18 +989,16 @@ callbacks["AskForExchange"] = (jsonData) => { const capacities = []; const limits = []; roomScene.state = "replying"; - roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/GuanxingBox.qml"); + roomScene.popupBox.sourceComponent = + Qt.createComponent("../RoomElement/GuanxingBox.qml"); let for_i = 0; const box = roomScene.popupBox.item; data.piles.forEach(ids => { if (ids.length > 0) { - ids.forEach(id => { - const d = Backend.callLuaFunction("GetCardData", [id]); - cards.push(JSON.parse(d)); - }); + ids.forEach(id => cards.push(lcall("GetCardData", id))); capacities.push(ids.length); limits.push(0); - cards_name.push(Backend.translate(data.piles_name[for_i])); + cards_name.push(luatr(data.piles_name[for_i])); for_i ++; } }); @@ -1024,10 +1022,10 @@ callbacks["AskForChoice"] = (jsonData) => { const prompt = data[3]; const detailed = data[4]; if (prompt === "") { - roomScene.promptText = Backend.translate("#AskForChoice") - .arg(Backend.translate(skill_name)); + roomScene.promptText = luatr("#AskForChoice") + .arg(luatr(skill_name)); } else { - roomScene.setPrompt(processPrompt(prompt), true); + roomScene.setPrompt(Util.processPrompt(prompt), true); } roomScene.state = "replying"; let qmlSrc; @@ -1059,10 +1057,10 @@ callbacks["AskForChoices"] = (jsonData) => { const prompt = data[5]; const detailed = data[6]; if (prompt === "") { - roomScene.promptText = Backend.translate("#AskForChoices") - .arg(Backend.translate(skill_name)); + roomScene.promptText = luatr("#AskForChoices") + .arg(luatr(skill_name)); } else { - roomScene.setPrompt(processPrompt(prompt), true); + roomScene.setPrompt(Util.processPrompt(prompt), true); } roomScene.state = "replying"; let qmlSrc; @@ -1095,13 +1093,14 @@ callbacks["AskForCardChosen"] = (jsonData) => { const reason = data._reason; const prompt = data._prompt; if (prompt === "") { - roomScene.promptText = Backend.translate("#AskForChooseCard") - .arg(Backend.translate(reason)); + roomScene.promptText = luatr("#AskForChooseCard") + .arg(luatr(reason)); } else { - roomScene.setPrompt(processPrompt(prompt), true); + roomScene.setPrompt(Util.processPrompt(prompt), true); } roomScene.state = "replying"; - roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/PlayerCardBox.qml"); + roomScene.popupBox.sourceComponent = + Qt.createComponent("../RoomElement/PlayerCardBox.qml"); const box = roomScene.popupBox.item; box.prompt = prompt; @@ -1109,17 +1108,12 @@ callbacks["AskForCardChosen"] = (jsonData) => { const arr = []; const ids = d[1]; - ids.forEach(id => { - const card_data = JSON.parse(Backend.callLuaFunction("GetCardData", [id])); - arr.push(card_data); - }); + ids.forEach(id => arr.push(lcall("GetCardData", id))); box.addCustomCards(d[0], arr); } roomScene.popupBox.moveToCenter(); - box.cardSelected.connect(function(cid){ - replyToServer(cid); - }); + box.cardSelected.connect(cid => replyToServer(cid)); } callbacks["AskForCardsChosen"] = (jsonData) => { @@ -1131,14 +1125,15 @@ callbacks["AskForCardsChosen"] = (jsonData) => { const reason = data._reason; const prompt = data._prompt; if (prompt === "") { - roomScene.promptText = Backend.translate("#AskForChooseCards") - .arg(Backend.translate(reason)).arg(min).arg(max); + roomScene.promptText = luatr("#AskForChooseCards") + .arg(luatr(reason)).arg(min).arg(max); } else { - roomScene.setPrompt(processPrompt(prompt), true); + roomScene.setPrompt(Util.processPrompt(prompt), true); } roomScene.state = "replying"; - roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/PlayerCardBox.qml"); + roomScene.popupBox.sourceComponent = + Qt.createComponent("../RoomElement/PlayerCardBox.qml"); const box = roomScene.popupBox.item; box.multiChoose = true; box.min = min; @@ -1148,10 +1143,7 @@ callbacks["AskForCardsChosen"] = (jsonData) => { const arr = []; const ids = d[1]; - ids.forEach(id => { - const card_data = JSON.parse(Backend.callLuaFunction("GetCardData", [id])); - arr.push(card_data); - }); + ids.forEach(id => arr.push(lcall("GetCardData", id))); box.addCustomCards(d[0], arr); } @@ -1165,7 +1157,8 @@ callbacks["AskForPoxi"] = (jsonData) => { const { type, data, extra_data, cancelable } = JSON.parse(jsonData); roomScene.state = "replying"; - roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/PoxiBox.qml"); + roomScene.popupBox.sourceComponent = + Qt.createComponent("../RoomElement/PoxiBox.qml"); const box = roomScene.popupBox.item; box.poxi_type = type; box.card_data = data; @@ -1175,10 +1168,7 @@ callbacks["AskForPoxi"] = (jsonData) => { const arr = []; const ids = d[1]; - ids.forEach(id => { - const card_data = JSON.parse(Backend.callLuaFunction("GetCardData", [id])); - arr.push(card_data); - }); + ids.forEach(id => arr.push(lcall("GetCardData", id))); box.addCustomCards(d[0], arr); } @@ -1193,13 +1183,14 @@ callbacks["AskForMoveCardInBoard"] = (jsonData) => { const { cards, cardsPosition, generalNames, playerIds } = data; roomScene.state = "replying"; - roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/MoveCardInBoardBox.qml"); + roomScene.popupBox.sourceComponent = + Qt.createComponent("../RoomElement/MoveCardInBoardBox.qml"); const boxCards = []; cards.forEach(id => { const cardPos = cardsPosition[cards.findIndex(cid => cid === id)]; - const d = Backend.callLuaFunction("GetCardData", [id, playerIds[cardPos]]); - boxCards.push(JSON.parse(d)); + const d = lcall("GetCardData", id, playerIds[cardPos]); + boxCards.push(d); }); const box = roomScene.popupBox.item; @@ -1208,7 +1199,10 @@ callbacks["AskForMoveCardInBoard"] = (jsonData) => { box.playerIds = playerIds; box.generalNames = generalNames.map(name => { 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(); @@ -1227,7 +1221,7 @@ callbacks["PlayCard"] = (jsonData) => { // jsonData: int playerId const playerId = parseInt(jsonData); if (playerId === Self.id) { - roomScene.setPrompt(Backend.translate("#PlayCard"), true); + roomScene.setPrompt(luatr("#PlayCard"), true); roomScene.state = "playing"; okButton.enabled = false; } @@ -1263,19 +1257,6 @@ callbacks["PrelightSkill"] = (jsonData) => { dashboard.prelightSkill(skill_name, prelight); } -// prompt: 'string::::' -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) => { // jsonData: string skill_name, string prompt const data = JSON.parse(jsonData); @@ -1284,16 +1265,16 @@ callbacks["AskForUseActiveSkill"] = (jsonData) => { const cancelable = data[2]; const extra_data = data[3] ?? {}; if (prompt === "") { - roomScene.promptText = Backend.translate("#AskForUseActiveSkill") - .arg(Backend.translate(skill_name)); + roomScene.promptText = luatr("#AskForUseActiveSkill") + .arg(luatr(skill_name)); } else { - roomScene.setPrompt(processPrompt(prompt), true); + roomScene.setPrompt(Util.processPrompt(prompt), true); } roomScene.respond_play = false; roomScene.state = "responding"; - if (JSON.parse(Backend.callLuaFunction('GetSkillData', [skill_name])).isViewAsSkill) { + if (lcall('GetSkillData', skill_name).isViewAsSkill) { roomScene.responding_card = "."; } @@ -1321,7 +1302,8 @@ callbacks["AskForUseCard"] = (jsonData) => { const extra_data = data[4]; const disabledSkillNames = data[5]; 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(); return; } else { @@ -1330,10 +1312,10 @@ callbacks["AskForUseCard"] = (jsonData) => { } if (prompt === "") { - roomScene.promptText = Backend.translate("#AskForUseCard") - .arg(Backend.translate(cardname)); + roomScene.promptText = luatr("#AskForUseCard") + .arg(luatr(cardname)); } else { - roomScene.setPrompt(processPrompt(prompt), true); + roomScene.setPrompt(Util.processPrompt(prompt), true); } roomScene.responding_card = pattern; roomScene.respond_play = false; @@ -1352,10 +1334,10 @@ callbacks["AskForResponseCard"] = (jsonData) => { const disabledSkillNames = data[5]; if (prompt === "") { - roomScene.promptText = Backend.translate("#AskForResponseCard") - .arg(Backend.translate(cardname)); + roomScene.promptText = luatr("#AskForResponseCard") + .arg(luatr(cardname)); } else { - roomScene.setPrompt(processPrompt(prompt), true); + roomScene.setPrompt(Util.processPrompt(prompt), true); } roomScene.responding_card = pattern; roomScene.respond_play = true; @@ -1422,7 +1404,8 @@ callbacks["Animate"] = (jsonData) => { } case "InvokeSkill": { const id = data.player; - const component = Qt.createComponent("../RoomElement/SkillInvokeAnimation.qml"); + const component = + Qt.createComponent("../RoomElement/SkillInvokeAnimation.qml"); if (component.status !== Component.Ready) return; @@ -1432,7 +1415,7 @@ callbacks["Animate"] = (jsonData) => { } 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"), }); animation.anchors.centerIn = photo; @@ -1467,7 +1450,8 @@ callbacks["LogEvent"] = (jsonData) => { setEmotion(data.to, "damage"); item.tremble(); 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; } case "LoseHP": { @@ -1489,9 +1473,10 @@ callbacks["LogEvent"] = (jsonData) => { // try main general if (data.general) { - dat = JSON.parse(Backend.callLuaFunction("GetGeneralData", [data.general])); + dat = lcall("GetGeneralData", data.general); 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")) { Backend.playSound(path, data.i); break; @@ -1500,9 +1485,10 @@ callbacks["LogEvent"] = (jsonData) => { // secondly try deputy general if (data.deputy) { - dat = JSON.parse(Backend.callLuaFunction("GetGeneralData", [data.deputy])); + dat = lcall("GetGeneralData", data.deputy); 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")) { Backend.playSound(path, data.i); break; @@ -1510,7 +1496,7 @@ callbacks["LogEvent"] = (jsonData) => { } // finally normal skill - dat = JSON.parse(Backend.callLuaFunction("GetSkillData", [skill])); + dat = lcall("GetSkillData", skill); extension = dat.extension; path = "./packages/" + extension + "/audio/skill/" + skill; Backend.playSound(path, data.i); @@ -1522,8 +1508,10 @@ callbacks["LogEvent"] = (jsonData) => { } case "Death": { const item = getPhoto(data.to); - const extension = JSON.parse(Backend.callLuaFunction("GetGeneralData", [item.general])).extension; - Backend.playSound("./packages/" + extension + "/audio/death/" + item.general); + const extension = lcall("GetGeneralData", item.general).extension; + Backend.playSound("./packages/" + extension + "/audio/death/" + + item.general); + break; } default: break; @@ -1532,7 +1520,8 @@ callbacks["LogEvent"] = (jsonData) => { callbacks["GameOver"] = (jsonData) => { roomScene.state = "notactive"; - roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/GameOverBox.qml"); + roomScene.popupBox.sourceComponent = + Qt.createComponent("../RoomElement/GameOverBox.qml"); const box = roomScene.popupBox.item; box.winner = jsonData; // roomScene.isStarted = false; @@ -1541,7 +1530,8 @@ callbacks["GameOver"] = (jsonData) => { callbacks["FillAG"] = (j) => { const data = JSON.parse(j); 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); } @@ -1556,7 +1546,7 @@ callbacks["TakeAG"] = (j) => { const pid = data[0]; const cid = data[1]; const item = getPhoto(pid); - const general = Backend.translate(item.general); + const general = luatr(item.general); // the item should be AG box roomScene.manualBox.item.takeAG(general, cid); @@ -1579,7 +1569,7 @@ callbacks["MiniGame"] = (j) => { const data = JSON.parse(j); const game = data.type; const dat = data.data; - const gdata = JSON.parse(Backend.callLuaFunction("GetMiniGame", [game, Self.id, JSON.stringify(dat)])); + const gdata = lcall("GetMiniGame", game, Self.id, JSON.stringify(dat)); roomScene.state = "replying"; roomScene.popupBox.source = AppPath + "/" + gdata.qml_path + ".qml"; if (dat) { @@ -1652,7 +1642,7 @@ callbacks["AskForLuckCard"] = (j) => { // jsonData: int time if (config.observing || config.replaying) return; const time = parseInt(j); - roomScene.setPrompt(Backend.translate("#AskForLuckCard").arg(time), true); + roomScene.setPrompt(luatr("#AskForLuckCard").arg(time), true); roomScene.state = "replying"; roomScene.extra_data = { luckCard: true, diff --git a/Fk/PhotoElement/DelayedTrickArea.qml b/Fk/PhotoElement/DelayedTrickArea.qml index 1b09672a..0d468130 100644 --- a/Fk/PhotoElement/DelayedTrickArea.qml +++ b/Fk/PhotoElement/DelayedTrickArea.qml @@ -35,7 +35,7 @@ Item { Image { height: 55 * 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.forEach(card => { - const v = JSON.parse(Backend.callLuaFunction("GetVirtualEquip", [parent.playerid, card.cid])); + const v = lcall("GetVirtualEquip", parent.playerid, card.cid); if (v !== null) { cards.append(v); } else { diff --git a/Fk/PhotoElement/EquipArea.qml b/Fk/PhotoElement/EquipArea.qml index 01dd735f..b862ab12 100644 --- a/Fk/PhotoElement/EquipArea.qml +++ b/Fk/PhotoElement/EquipArea.qml @@ -17,9 +17,15 @@ Item { height: 70 width: 138 - property int itemHeight: (treasureItem.name === "" && !treasureItem.sealed) ? height / 3 : height / 4 - property var items: [treasureItem, weaponItem, armorItem, defensiveHorseItem, offensiveHorseItem] - property var subtypes: ["treasure", "weapon", "armor", "defensive_horse", "offensive_horse"] + property int itemHeight: { + if (treasureItem.name === "" && !treasureItem.sealed) + 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 // FIXME: Qt 6.6 diff --git a/Fk/PhotoElement/EquipItem.qml b/Fk/PhotoElement/EquipItem.qml index f519c985..b5be6f90 100644 --- a/Fk/PhotoElement/EquipItem.qml +++ b/Fk/PhotoElement/EquipItem.qml @@ -30,7 +30,11 @@ Item { anchors.verticalCenter: parent.verticalCenter 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 { @@ -44,7 +48,7 @@ Item { GlowText { id: numberItem visible: !sealed && number > 0 && number < 14 - text: Utility.convertNumber(number) + text: Util.convertNumber(number) color: "white" font.family: fontLibian.name font.pixelSize: 16 @@ -132,7 +136,7 @@ Item { text = "-1" icon = "horse"; } else { - text = Backend.translate(name); + text = luatr(name); if (card.virt_name) { icon = card.virt_name; } else { @@ -159,6 +163,6 @@ Item { x = 0; opacity = sealed ? 1 : 0; - text = ' ' + Backend.translate(subtype + "_sealed") + text = ' ' + luatr(subtype + "_sealed") } } diff --git a/Fk/PhotoElement/HpBar.qml b/Fk/PhotoElement/HpBar.qml index d78435b7..a494ec7a 100644 --- a/Fk/PhotoElement/HpBar.qml +++ b/Fk/PhotoElement/HpBar.qml @@ -20,13 +20,24 @@ Column { id: repeater model: column.visible ? 0 : maxValue 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 { id: column - visible: maxValue > 4 || value > maxValue || (shieldNum > 0 && maxValue > 3) + visible: maxValue > 4 || value > maxValue || + (shieldNum > 0 && maxValue > 3) spacing: -4 Magatama { @@ -37,7 +48,17 @@ Column { id: hpItem width: root.width 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.pixelSize: 22 font.bold: true diff --git a/Fk/PhotoElement/LimitSkillItem.qml b/Fk/PhotoElement/LimitSkillItem.qml index 9493a0a7..ef81094d 100644 --- a/Fk/PhotoElement/LimitSkillItem.qml +++ b/Fk/PhotoElement/LimitSkillItem.qml @@ -25,7 +25,7 @@ Item { font.family: fontLi2.name style: Text.Outline styleColor: "#3D2D1C" - text: Backend.translate(skillname); + text: luatr(skillname); } Text { @@ -39,7 +39,7 @@ Item { } onSkillnameChanged: { - let data = Backend.callLuaFunction("GetSkillData", [skillname]); + let data = lcall("GetSkillData", skillname); data = JSON.parse(data); if (data.frequency || data.switchSkillName) { skilltype = data.switchSkillName ? 'switch' : data.frequency; @@ -64,7 +64,8 @@ Item { } } else if (skilltype === 'switch') { 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') { visible = true if (usedtimes > 1) { diff --git a/Fk/PhotoElement/MarkArea.qml b/Fk/PhotoElement/MarkArea.qml index 8a1d625a..4b7deaa3 100644 --- a/Fk/PhotoElement/MarkArea.qml +++ b/Fk/PhotoElement/MarkArea.qml @@ -34,7 +34,8 @@ Item { width: childrenRect.width height: 22 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.pixelSize: 22 font.letterSpacing: -0.6 @@ -76,7 +77,8 @@ Item { const mark_type = mark_name.slice(2, close_br); 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) { params.data = JSON.parse(_data); roomScene.startCheat("../../" + data.qml_path, params); @@ -84,7 +86,7 @@ Item { return; } else { 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); if (data.length === 0) return; @@ -123,13 +125,16 @@ Item { if (close_br !== -1) { const mark_type = mark.slice(2, close_br); 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) { special_value = _data.text; } } } 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) { diff --git a/Fk/RoomElement/AG.qml b/Fk/RoomElement/AG.qml index 40ac9b7a..b35c4f64 100644 --- a/Fk/RoomElement/AG.qml +++ b/Fk/RoomElement/AG.qml @@ -8,7 +8,7 @@ GraphicsBox { property bool interactive: false id: root - title.text: Backend.translate("Please choose cards") + title.text: luatr("Please choose cards") width: cards.count * 100 + spacing * (cards.count - 1) + 25 height: 180 @@ -46,7 +46,7 @@ GraphicsBox { function addIds(ids) { ids.forEach((id) => { - let data = Backend.callLuaFunction("GetCardData", [id]); + let data = lcall("GetCardData", id); data = JSON.parse(data); data.selectable = true; data.footnote = ""; diff --git a/Fk/RoomElement/CardArea.qml b/Fk/RoomElement/CardArea.qml index 7066a40d..f9d39708 100644 --- a/Fk/RoomElement/CardArea.qml +++ b/Fk/RoomElement/CardArea.qml @@ -26,7 +26,7 @@ Item { for (let j = 0; j < outputs.length; j++) { for (let i = cards.length - 1; i >= 0; i--) { 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); result.push(cards[i]); cards.splice(i, 1); diff --git a/Fk/RoomElement/CardItem.qml b/Fk/RoomElement/CardItem.qml index bd9cdc9f..ea4f4d0c 100644 --- a/Fk/RoomElement/CardItem.qml +++ b/Fk/RoomElement/CardItem.qml @@ -102,7 +102,8 @@ Item { Image { id: suitItem visible: known - source: (suit !== "" && suit !== "nosuit") ? SkinBank.CARD_SUIT_DIR + suit : "" + source: (suit !== "" && suit !== "nosuit") ? SkinBank.CARD_SUIT_DIR + suit + : "" x: 3 y: 19 width: 21 @@ -122,8 +123,10 @@ Item { Image { id: colorItem - visible: known && (suit === "" || suit === "nosuit") // && number <= 0 // <- FIXME: 需要区分“黑色有点数”和“无色有点数” - source: (visible && color !== "") ? SkinBank.CARD_SUIT_DIR + "/" + color : "" + visible: known && (suit === "" || suit === "nosuit") + // && number <= 0 // <- FIXME: 需要区分“黑色有点数”和“无色有点数” + source: (visible && color !== "") ? SkinBank.CARD_SUIT_DIR + "/" + color + : "" x: 1 } @@ -146,7 +149,7 @@ Item { font.pixelSize: 16 font.family: fontLibian.name font.letterSpacing: -0.6 - text: Backend.translate(root.virt_name) + text: luatr(root.virt_name) } Text { @@ -195,7 +198,7 @@ Item { font.family: fontLibian.name font.letterSpacing: -0.6 text: { - let ret = Backend.translate(modelData.k); + let ret = luatr(modelData.k); if (!modelData.k.startsWith("@@")) { ret += modelData.v.toString(); } diff --git a/Fk/RoomElement/CheckBox.qml b/Fk/RoomElement/CheckBox.qml index b07c4edf..9839e46a 100644 --- a/Fk/RoomElement/CheckBox.qml +++ b/Fk/RoomElement/CheckBox.qml @@ -2,6 +2,7 @@ import QtQuick import QtQuick.Layouts +import Fk import Fk.Pages GraphicsBox { @@ -14,22 +15,10 @@ GraphicsBox { property var result: [] 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) 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 { id: body // x: 10 @@ -44,8 +33,9 @@ GraphicsBox { MetroToggleButton { Layout.fillWidth: true - text: processPrompt(modelData) - enabled: options.indexOf(modelData) !== -1 && (root.result.length < max_num || triggered) + text: Util.processPrompt(modelData) + enabled: options.indexOf(modelData) !== -1 + && (root.result.length < max_num || triggered) onClicked: { if (triggered) { @@ -68,7 +58,7 @@ GraphicsBox { MetroButton { Layout.fillWidth: true - text: processPrompt("OK") + text: Util.processPrompt("OK") enabled: root.result.length >= min_num onClicked: { diff --git a/Fk/RoomElement/ChoiceBox.qml b/Fk/RoomElement/ChoiceBox.qml index cf0106b6..364e05c1 100644 --- a/Fk/RoomElement/ChoiceBox.qml +++ b/Fk/RoomElement/ChoiceBox.qml @@ -2,6 +2,7 @@ import QtQuick import QtQuick.Layouts +import Fk import Fk.Pages GraphicsBox { @@ -11,22 +12,10 @@ GraphicsBox { property int result 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) 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 { id: body x: 10 @@ -40,7 +29,7 @@ GraphicsBox { MetroButton { Layout.fillWidth: true - text: processPrompt(modelData) + text: Util.processPrompt(modelData) enabled: options.indexOf(modelData) !== -1 onClicked: { diff --git a/Fk/RoomElement/ChooseGeneralBox.qml b/Fk/RoomElement/ChooseGeneralBox.qml index ba5d2a6f..50c6adf8 100644 --- a/Fk/RoomElement/ChooseGeneralBox.qml +++ b/Fk/RoomElement/ChooseGeneralBox.qml @@ -19,10 +19,11 @@ GraphicsBox { } id: root - title.text: Backend.translate("$ChooseGeneral").arg(choiceNum) + - (config.enableFreeAssign ? "(" + Backend.translate("Enable free assign") + ")" : "") + title.text: luatr("$ChooseGeneral").arg(choiceNum) + + (config.enableFreeAssign ? "(" + luatr("Enable free assign") + ")" : "") 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 { id: body @@ -32,7 +33,8 @@ GraphicsBox { Item { 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 z: 1 @@ -43,8 +45,23 @@ GraphicsBox { Item { width: 93 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) - y: generalList.count <= 8 ? 0 : (index < generalList.count / 2 ? 0 : 135) + x: { + 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 { id: convertBtn visible: !convertDisabled - text: Backend.translate("Same General Convert") - onClicked: roomScene.startCheat("SameConvert", { cards: generalList }); + text: luatr("Same General Convert") + onClicked: { + roomScene.startCheat("SameConvert", { cards: generalList }); + } } MetroButton { id: fightButton - text: Backend.translate("Fight") + text: luatr("Fight") width: 120 height: 35 enabled: false @@ -110,7 +129,7 @@ GraphicsBox { MetroButton { id: detailBtn enabled: choices.length > 0 - text: Backend.translate("Show General Detail") + text: luatr("Show General Detail") onClicked: roomScene.startCheat( "GeneralDetail", { generals: choices } @@ -233,7 +252,8 @@ GraphicsBox { for (i = 0; i < generalCardList.count; 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) continue; @@ -247,9 +267,7 @@ GraphicsBox { } for (let i = 0; i < generalList.count; i++) { - if (JSON.parse(Backend.callLuaFunction( - "GetSameGenerals", [generalList.get(i).name]) - ).length > 0) { + if (lcall("GetSameGenerals", generalList.get(i).name).length > 0) { convertBtn.enabled = true; return; } diff --git a/Fk/RoomElement/ChooseHandcard.qml b/Fk/RoomElement/ChooseHandcard.qml index 45281e18..5a4e9a36 100644 --- a/Fk/RoomElement/ChooseHandcard.qml +++ b/Fk/RoomElement/ChooseHandcard.qml @@ -13,7 +13,7 @@ ColumnLayout { property var cards: [] Text { - text: Backend.translate("Handcard selector") + text: luatr("Handcard selector") Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter font.pixelSize: 18 @@ -46,7 +46,7 @@ ColumnLayout { } } Component.onCompleted: { - setData(JSON.parse(Backend.callLuaFunction("GetCardData", [modelData.cid]))); + setData(lcall("GetCardData", modelData.cid)); } } } diff --git a/Fk/RoomElement/Dashboard.qml b/Fk/RoomElement/Dashboard.qml index 61dea611..6c9718dc 100644 --- a/Fk/RoomElement/Dashboard.qml +++ b/Fk/RoomElement/Dashboard.qml @@ -3,6 +3,7 @@ import QtQuick import QtQuick.Layouts import Qt5Compat.GraphicalEffects +import Fk RowLayout { id: root @@ -86,19 +87,19 @@ RowLayout { data.y = parentPos.y; const card = component.createObject(roomScene, data); card.footnoteVisible = true; - card.footnote = Backend.translate("$Equip"); + card.footnote = luatr("$Equip"); handcardAreaItem.add(card); }) handcardAreaItem.updateCardPosition(); } else { - const ids = JSON.parse(Backend.callLuaFunction("GetPile", [self.playerid, pile])); + const ids = lcall("GetPile", self.playerid, pile); ids.forEach(id => { - const data = JSON.parse(Backend.callLuaFunction("GetCardData", [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 = Backend.translate(pile); + card.footnote = luatr(pile); handcardAreaItem.add(card); }); handcardAreaItem.updateCardPosition(); @@ -124,7 +125,7 @@ RowLayout { }) handcardAreaItem.updateCardPosition(); } else { - const ids = JSON.parse(Backend.callLuaFunction("GetPile", [self.playerid, pile])); + const ids = lcall("GetPile", self.playerid, pile); ids.forEach(id => { const card = handcardAreaItem.remove([id])[0]; card.origX = parentPos.x; @@ -145,25 +146,23 @@ RowLayout { // If cname is set, we are responding card. function enableCards(cname) { const cardValid = (cid, cname) => { - let ret = JSON.parse(Backend.callLuaFunction( - "CardFitPattern", [cid, cname])); + let ret = lcall("CardFitPattern", cid, cname); if (ret) { if (roomScene.respond_play) { - ret = ret && !JSON.parse(Backend.callLuaFunction( - "CardProhibitedResponse", [cid])); + ret = ret && !lcall("CardProhibitedResponse", cid); } else { - ret = ret && !JSON.parse(Backend.callLuaFunction( - "CardProhibitedUse", [cid])); + ret = ret && !lcall("CardProhibitedUse", cid); } } return ret; } - const pile_data = JSON.parse(Backend.callLuaFunction("GetAllPiles", [self.playerid])); + const pile_data = lcall("GetAllPiles", self.playerid); extractWoodenOx(); + const handleMethod = roomScene.respond_play ? "response" : "use"; if (cname) { const ids = []; let cards = handcardAreaItem.cards; @@ -172,10 +171,8 @@ RowLayout { if (cardValid(cards[i].cid, cname)) { ids.push(cards[i].cid); } else { - const prohibitReason = Backend.callLuaFunction( - "GetCardProhibitReason", - [cards[i].cid, roomScene.respond_play ? "response" : "use", cname] - ); + const prohibitReason = lcall("GetCardProhibitReason", cards[i].cid, + handleMethod, cname); if (prohibitReason) { cards[i].prohibitReason = prohibitReason; } @@ -190,10 +187,8 @@ RowLayout { expandPile("_equip"); } } else { - const prohibitReason = Backend.callLuaFunction( - "GetCardProhibitReason", - [c.cid, roomScene.respond_play ? "response" : "use", cname] - ); + const prohibitReason = lcall("GetCardProhibitReason", c.cid, + handleMethod, cname); if (prohibitReason) { c.prohibitReason = prohibitReason; } @@ -223,14 +218,15 @@ RowLayout { const ids = [], cards = handcardAreaItem.cards; for (let i = 0; i < cards.length; i++) { cards[i].prohibitReason = ""; - if (JSON.parse(Backend.callLuaFunction("CanUseCard", [cards[i].cid, Self.id, JSON.stringify(roomScene.extra_data)]))) { + if (lcall("CanUseCard", cards[i].cid, Self.id, + JSON.stringify(roomScene.extra_data))) { ids.push(cards[i].cid); } else { // 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++) { const s = skills[j]; - if (JSON.parse(Backend.callLuaFunction("ActiveCanUse", [s, JSON.stringify(roomScene.extra_data)]))) { + if (lcall("ActiveCanUse", s, JSON.stringify(roomScene.extra_data))) { ids.push(cards[i].cid); break; } @@ -238,7 +234,8 @@ RowLayout { // still cannot use? show message on card 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) { cards[i].prohibitReason = prohibitReason; } @@ -304,21 +301,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() { - const pile_data = JSON.parse(Backend.callLuaFunction("GetAllPiles", [self.playerid])); - if (!roomScene.autoPending) { // 先屏蔽AskForUseActiveSkill再说,这下只剩使用打出以及出牌阶段了 + const pile_data = lcall("GetAllPiles", self.playerid); + if (!roomScene.autoPending) { + // 先屏蔽AskForUseActiveSkill再说,这下只剩使用打出以及出牌阶段了 for (let name in pile_data) { if (name.endsWith("&")) expandPile(name); } @@ -331,28 +317,21 @@ RowLayout { const enabled_cards = []; const targets = roomScene.selected_targets; - const prompt = JSON.parse(Backend.callLuaFunction( - "ActiveSkillPrompt", - [pending_skill, pendings, targets] - )); + const prompt = lcall("ActiveSkillPrompt", pending_skill, pendings, + targets); if (prompt !== "") { - roomScene.setPrompt(processPrompt(prompt)); + roomScene.setPrompt(Util.processPrompt(prompt)); } handcardAreaItem.cards.forEach((card) => { - if (card.selected || JSON.parse(Backend.callLuaFunction( - "ActiveCardFilter", - [pending_skill, card.cid, pendings, targets] - ))) + if (card.selected || lcall("ActiveCardFilter", pending_skill, card.cid, + pendings, targets)) enabled_cards.push(card.cid); }); const cards = self.equipArea.getAllCards(); cards.forEach(c => { - if (JSON.parse(Backend.callLuaFunction( - "ActiveCardFilter", - [pending_skill, c.cid, pendings, targets] - ))) { + if (lcall("ActiveCardFilter", pending_skill, c.cid, pendings, targets)) { enabled_cards.push(c.cid); if (!expanded_piles["_equip"]) { expandPile("_equip"); @@ -360,13 +339,10 @@ RowLayout { } }) - const pile = Backend.callLuaFunction("GetExpandPileOfSkill", [pending_skill]); - const pile_ids = JSON.parse(Backend.callLuaFunction("GetPile", [self.playerid, pile])); + const pile = lcall("GetExpandPileOfSkill", pending_skill); + const pile_ids = lcall("GetPile", self.playerid, pile); pile_ids.forEach(cid => { - if (JSON.parse(Backend.callLuaFunction( - "ActiveCardFilter", - [pending_skill, cid, pendings, targets] - ))) { + if (lcall("ActiveCardFilter", pending_skill, cid, pendings, targets)) { enabled_cards.push(cid); }; if (!expanded_piles[pile]) { @@ -376,10 +352,7 @@ RowLayout { handcardAreaItem.enableCards(enabled_cards); - if (JSON.parse(Backend.callLuaFunction( - "CanViewAs", - [pending_skill, pendings] - ))) { + if (lcall("CanViewAs", pending_skill, pendings)) { pending_card = { skill: pending_skill, subcards: pendings @@ -462,8 +435,8 @@ RowLayout { continue; } - const fitpattern = JSON.parse(Backend.callLuaFunction("SkillFitPattern", [item.orig, cname])); - const canresp = JSON.parse(Backend.callLuaFunction("SkillCanResponse", [item.orig, cardResponsing])); + const fitpattern = lcall("SkillFitPattern", item.orig, cname); + const canresp = lcall("SkillCanResponse", item.orig, cardResponsing); item.enabled = fitpattern && canresp; } return; @@ -475,7 +448,8 @@ RowLayout { continue; } - item.enabled = JSON.parse(Backend.callLuaFunction("ActiveCanUse", [item.orig, JSON.stringify(roomScene.extra_data)])); + item.enabled = lcall("ActiveCanUse", item.orig, + JSON.stringify(roomScene.extra_data)); } } @@ -490,10 +464,9 @@ RowLayout { } function updateHandcards() { - Backend.callLuaFunction("FilterMyHandcards", []); + lcall("FilterMyHandcards"); handcardAreaItem.cards.forEach(v => { - const data = JSON.parse(Backend.callLuaFunction("GetCardData", [v.cid])); - v.setData(data); + v.setData(lcall("GetCardData", v.cid)); }); } @@ -514,12 +487,12 @@ RowLayout { skillPanel.clearSkills(); - const skills = JSON.parse(Backend.callLuaFunction("GetPlayerSkills", [Self.id])); + const skills = lcall("GetPlayerSkills", Self.id); for (let s of skills) { 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); } } diff --git a/Fk/RoomElement/DetailedCheckBox.qml b/Fk/RoomElement/DetailedCheckBox.qml index f9bae3a0..7a435bc7 100644 --- a/Fk/RoomElement/DetailedCheckBox.qml +++ b/Fk/RoomElement/DetailedCheckBox.qml @@ -15,7 +15,7 @@ GraphicsBox { property var result: [] 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) height: buttons.height + body.height + title.height + 20 @@ -38,8 +38,9 @@ GraphicsBox { MetroToggleButton { id: choicetitle width: parent.width - text: Backend.translate(modelData) - enabled: options.indexOf(modelData) !== -1 && (root.result.length < max_num || triggered) + text: luatr(modelData) + enabled: options.indexOf(modelData) !== -1 + && (root.result.length < max_num || triggered) textFont.pixelSize: 24 anchors.top: choiceDetail.bottom anchors.topMargin: 8 @@ -64,7 +65,7 @@ GraphicsBox { Text { id: detail width: parent.width - text: Backend.translate(":" + modelData) + text: luatr(":" + modelData) color: "white" wrapMode: Text.WordWrap font.pixelSize: 16 @@ -84,7 +85,7 @@ GraphicsBox { MetroButton { width: 120 height: 35 - text: Backend.translate("OK") + text: luatr("OK") enabled: root.result.length >= min_num onClicked: { @@ -95,7 +96,7 @@ GraphicsBox { MetroButton { width: 120 height: 35 - text: Backend.translate("Cancel") + text: luatr("Cancel") visible: root.cancelable onClicked: { diff --git a/Fk/RoomElement/DetailedChoiceBox.qml b/Fk/RoomElement/DetailedChoiceBox.qml index 43c139a2..ddcd9b44 100644 --- a/Fk/RoomElement/DetailedChoiceBox.qml +++ b/Fk/RoomElement/DetailedChoiceBox.qml @@ -12,7 +12,7 @@ GraphicsBox { property int result 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) height: body.height + title.height + 20 @@ -35,7 +35,7 @@ GraphicsBox { MetroButton { id: choicetitle width: parent.width - text: Backend.translate(modelData) + text: luatr(modelData) enabled: options.indexOf(modelData) !== -1 textFont.pixelSize: 24 anchors.top: choiceDetail.bottom @@ -57,7 +57,7 @@ GraphicsBox { Text { id: detail width: parent.width - text: Backend.translate(":" + modelData) + text: luatr(":" + modelData) color: "white" wrapMode: Text.WordWrap font.pixelSize: 16 diff --git a/Fk/RoomElement/GameOverBox.qml b/Fk/RoomElement/GameOverBox.qml index f5a91bad..6928fd95 100644 --- a/Fk/RoomElement/GameOverBox.qml +++ b/Fk/RoomElement/GameOverBox.qml @@ -7,7 +7,7 @@ GraphicsBox { property string winner: "" id: root - title.text: Backend.translate("$GameOver") + title.text: luatr("$GameOver") width: Math.max(140, body.width + 20) height: body.height + title.height + 20 @@ -18,12 +18,13 @@ GraphicsBox { spacing: 10 Text { - text: winner !== "" ? Backend.translate("$Winner").arg(Backend.translate(winner)) : Backend.translate("$NoWinner") + text: winner !== "" ? luatr("$Winner").arg(luatr(winner)) + : luatr("$NoWinner") color: "#E4D5A0" } MetroButton { - text: Backend.translate("Back To Room") + text: luatr("Back To Room") anchors.horizontalCenter: parent.horizontalCenter visible: !config.observing @@ -34,7 +35,7 @@ GraphicsBox { } MetroButton { - text: Backend.translate("Back To Lobby") + text: luatr("Back To Lobby") anchors.horizontalCenter: parent.horizontalCenter onClicked: { @@ -49,13 +50,13 @@ GraphicsBox { MetroButton { id: repBtn - text: Backend.translate("Save Replay") + text: luatr("Save Replay") anchors.horizontalCenter: parent.horizontalCenter visible: !config.replaying onClicked: { repBtn.visible = false; - Backend.callLuaFunction("SaveRecord", []); + lcall("SaveRecord"); toast.show("OK."); } } diff --git a/Fk/RoomElement/GeneralCardItem.qml b/Fk/RoomElement/GeneralCardItem.qml index 70bac4e5..87332f2a 100644 --- a/Fk/RoomElement/GeneralCardItem.qml +++ b/Fk/RoomElement/GeneralCardItem.qml @@ -29,11 +29,13 @@ CardItem { suit: "" number: 0 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] // 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 { source: known ? (SkinBank.GENERALCARD_DIR + "border") : "" @@ -51,7 +53,8 @@ CardItem { scale: 0.6; x: 9; y: 12 transformOrigin: Item.TopLeft width: 34; fillMode: Image.PreserveAspectFit - source: subkingdom ? SkinBank.getGeneralCardDir(subkingdom) + subkingdom : "" + source: subkingdom ? SkinBank.getGeneralCardDir(subkingdom) + subkingdom + : "" visible: detailed && known } @@ -72,7 +75,8 @@ CardItem { Image { id: subkingdomMagatama visible: false - source: subkingdom ? SkinBank.getGeneralCardDir(subkingdom) + subkingdom + "-magatama" : "" + source: subkingdom ? SkinBank.getGeneralCardDir(subkingdom) + + subkingdom + "-magatama" : "" } LinearGradient { id: subkingdomMask @@ -153,8 +157,8 @@ CardItem { height: 80 x: 2 y: lineCount > 6 ? 30 : 34 - text: name !== "" ? Backend.translate(name) : "nil" - visible: Backend.translate(name).length <= 6 && detailed && known + text: name !== "" ? luatr(name) : "nil" + visible: luatr(name).length <= 6 && detailed && known color: "white" font.family: fontLibian.name font.pixelSize: 18 @@ -168,8 +172,8 @@ CardItem { y: 12 rotation: 90 transformOrigin: Item.BottomLeft - text: Backend.translate(name) - visible: Backend.translate(name).length > 6 && detailed && known + text: luatr(name) + visible: luatr(name).length > 6 && detailed && known color: "white" font.family: fontLibian.name font.pixelSize: 18 @@ -191,7 +195,7 @@ CardItem { border.color: "white" border.width: 1 Text { - text: Backend.translate(pkgName) + text: luatr(pkgName) x: 2; y: 1 font.family: fontLibian.name font.pixelSize: 14 @@ -202,7 +206,7 @@ CardItem { } onNameChanged: { - const data = JSON.parse(Backend.callLuaFunction("GetGeneralData", [name])); + const data = lcall("GetGeneralData", name); kingdom = data.kingdom; subkingdom = (data.subkingdom !== kingdom && data.subkingdom) || ""; hp = data.hp; diff --git a/Fk/RoomElement/GuanxingBox.qml b/Fk/RoomElement/GuanxingBox.qml index b70d34d9..0023ad34 100644 --- a/Fk/RoomElement/GuanxingBox.qml +++ b/Fk/RoomElement/GuanxingBox.qml @@ -14,7 +14,7 @@ GraphicsBox { property var areaNames: [] 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 height: title.height + body.height + padding * 2 @@ -32,7 +32,8 @@ GraphicsBox { spacing: 5 property int areaCapacity: modelData - property string areaName: index < areaNames.length ? qsTr(areaNames[index]) : "" + property string areaName: index < areaNames.length + ? qsTr(areaNames[index]) : "" Rectangle { anchors.verticalCenter: parent.verticalCenter @@ -81,7 +82,7 @@ GraphicsBox { MetroButton { Layout.alignment: Qt.AlignHCenter id: buttonConfirm - text: Backend.translate("OK") + text: luatr("OK") width: 120 height: 35 diff --git a/Fk/RoomElement/HandcardArea.qml b/Fk/RoomElement/HandcardArea.qml index 2d96bea1..8541d7d9 100644 --- a/Fk/RoomElement/HandcardArea.qml +++ b/Fk/RoomElement/HandcardArea.qml @@ -46,7 +46,7 @@ Item { card = result[i]; card.draggable = false; card.selectable = false; - card.showDetail = false; + // card.showDetail = false; card.selectedChanged.disconnect(adjustCards); card.prohibitReason = ""; } @@ -57,7 +57,7 @@ Item { { let card, i; cards.forEach(card => { - card.selectable = cardIds.contains(card.cid); + card.selectable = cardIds.includes(card.cid); if (!card.selectable) { card.selected = false; unselectCard(card); @@ -93,10 +93,10 @@ Item { for (let i = 0; i < cards.length; i++) { const card = cards[i]; if (card.selected) { - if (!selectedCards.contains(card)) + if (!selectedCards.includes(card)) selectCard(card); } else { - if (selectedCards.contains(card)) + if (selectedCards.includes(card)) unselectCard(card); } } diff --git a/Fk/RoomElement/IndicatorLine.qml b/Fk/RoomElement/IndicatorLine.qml index 00531b70..14ea6511 100644 --- a/Fk/RoomElement/IndicatorLine.qml +++ b/Fk/RoomElement/IndicatorLine.qml @@ -20,7 +20,8 @@ Item { Rectangle { 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 y: start.y antialiasing: true diff --git a/Fk/RoomElement/InvisibleCardArea.qml b/Fk/RoomElement/InvisibleCardArea.qml index 14e0a570..25ba6ea4 100644 --- a/Fk/RoomElement/InvisibleCardArea.qml +++ b/Fk/RoomElement/InvisibleCardArea.qml @@ -59,11 +59,12 @@ Item { const items = []; for (let i = 0; i < outputs.length; 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.y = parentPos.y; state.opacity = 0; card = component.createObject(roomScene.dynamicCardArea, state); + card.showDetail = true card.x -= card.width / 2; card.x += (i - outputs.length / 2) * 15; card.y -= card.height / 2; @@ -93,7 +94,8 @@ Item { const parentPos = roomScene.mapFromItem(root, 0, 0); for (i = 0; i < pendingInput.length; 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.origOpacity = 0; card.destroyOnStop(); diff --git a/Fk/RoomElement/MiscStatus.qml b/Fk/RoomElement/MiscStatus.qml index 00116dbb..9e7e7630 100644 --- a/Fk/RoomElement/MiscStatus.qml +++ b/Fk/RoomElement/MiscStatus.qml @@ -19,7 +19,7 @@ Item { Text { id: roundTxt anchors.right: parent.right - text: Backend.translate("#currentRoundNum").arg(roundNum) + text: luatr("#currentRoundNum").arg(roundNum) color: "#F0E5DA" font.pixelSize: 18 font.family: fontLibian.name diff --git a/Fk/RoomElement/MoveCardInBoardBox.qml b/Fk/RoomElement/MoveCardInBoardBox.qml index f88f3773..eea07571 100644 --- a/Fk/RoomElement/MoveCardInBoardBox.qml +++ b/Fk/RoomElement/MoveCardInBoardBox.qml @@ -13,7 +13,7 @@ GraphicsBox { property var result 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 height: title.height + body.height + padding * 2 @@ -65,7 +65,7 @@ GraphicsBox { Text { horizontalAlignment: Text.AlignHCenter anchors.centerIn: parent - text: Backend.translate(modelData.subtype) + text: luatr(modelData.subtype) color: "#90765F" font.family: fontLibian.name font.pixelSize: 16 @@ -81,7 +81,7 @@ GraphicsBox { MetroButton { Layout.alignment: Qt.AlignHCenter id: buttonConfirm - text: Backend.translate("OK") + text: luatr("OK") width: 120 height: 35 enabled: false @@ -131,7 +131,8 @@ GraphicsBox { const index = cards.findIndex(data => item.cid === data.cid); 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 curBox = curArea.cardRepeater.itemAt(index); const curPos = mapFromItem(curArea, curBox.x, curBox.y); diff --git a/Fk/RoomElement/Photo.qml b/Fk/RoomElement/Photo.qml index 869d0e60..bbd9bbe4 100644 --- a/Fk/RoomElement/Photo.qml +++ b/Fk/RoomElement/Photo.qml @@ -187,7 +187,8 @@ Item { return ""; } if (deputyGeneral) { - return SkinBank.getGeneralExtraPic(general, "dual/") ?? SkinBank.getGeneralPicture(general); + return SkinBank.getGeneralExtraPic(general, "dual/") + ?? SkinBank.getGeneralPicture(general); } else { return SkinBank.getGeneralPicture(general) } @@ -204,7 +205,8 @@ Item { source: { const general = deputyGeneral; if (deputyGeneral != "") { - return SkinBank.getGeneralExtraPic(general, "dual/") ?? SkinBank.getGeneralPicture(general); + return SkinBank.getGeneralExtraPic(general, "dual/") + ?? SkinBank.getGeneralPicture(general); } else { return ""; } @@ -231,7 +233,7 @@ Item { color: "white" width: 24 wrapMode: Text.WrapAnywhere - text: Backend.translate(deputyGeneral) + text: luatr(deputyGeneral) style: Text.Outline } } @@ -280,7 +282,7 @@ Item { GlowText { Layout.alignment: Qt.AlignCenter - text: Backend.translate("resting...") + text: luatr("resting...") font.family: fontLibian.name font.pixelSize: 40 font.bold: true @@ -304,7 +306,7 @@ Item { GlowText { Layout.alignment: Qt.AlignCenter visible: root.rest > 0 && root.rest < 999 - text: Backend.translate("rest round num") + text: luatr("rest round num") font.family: fontLibian.name font.pixelSize: 28 color: "#F0E5D6" @@ -334,11 +336,11 @@ Item { style: Text.Outline text: { if (totalGame === 0) { - return Backend.translate("Newbie"); + return luatr("Newbie"); } const winRate = (winGame / 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(runRate.toFixed(2)) .arg(totalGame); @@ -351,7 +353,8 @@ Item { anchors.right: parent.right anchors.bottomMargin: -8 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 } @@ -393,7 +396,7 @@ Item { } function updatePileInfo(areaName) { - const data = JSON.parse(Backend.callLuaFunction("GetPile", [root.playerid, areaName])); + const data = lcall("GetPile", root.playerid, areaName); if (data.length === 0) { root.markArea.removeMark(areaName); } else { @@ -460,7 +463,14 @@ Item { x: -6 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.pixelSize: (root.maxCard === root.hp || root.hp < 0 ) ? 32 : 27 //font.weight: 30 @@ -472,10 +482,11 @@ Item { } TapHandler { - enabled: (root.state != "candidate" || !root.selectable) && root.playerid !== Self.id + enabled: (root.state != "candidate" || !root.selectable) + && root.playerid !== Self.id onTapped: { 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); if (data.length === 0) return; @@ -532,7 +543,12 @@ Item { anchors.topMargin: 2 font.pixelSize: 16 - text: (config.blockedUsers && config.blockedUsers.includes(screenName) ? Backend.translate(" ") : "") + screenName + text: { + let ret = screenName; + if (config.blockedUsers?.includes(screenName)) + ret = luatr(" ") + ret; + return ret; + } glow.radius: 8 } @@ -549,7 +565,10 @@ Item { anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: parent.bottom anchors.bottomMargin: -32 - property var seatChr: ["一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二"] + property var seatChr: [ + "一", "二", "三", "四", "五", "六", + "七", "八", "九", "十", "十一", "十二", + ] font.family: fontLi2.name font.pixelSize: 32 text: seatChr[seatNumber - 1] @@ -705,7 +724,7 @@ Item { onGeneralChanged: { if (!roomScene.isStarted) return; - const text = Backend.translate(general); + const text = luatr(general); if (text.length > 6) { generalName.text = ""; longGeneralName.text = text; @@ -713,8 +732,6 @@ Item { generalName.text = text; longGeneralName.text = ""; } - // let data = JSON.parse(Backend.callLuaFunction("GetGeneralData", [general])); - // kingdom = data.kingdom; } function chat(msg) { diff --git a/Fk/RoomElement/PlayerCardBox.qml b/Fk/RoomElement/PlayerCardBox.qml index 1139dd8f..1d4fcfdb 100644 --- a/Fk/RoomElement/PlayerCardBox.qml +++ b/Fk/RoomElement/PlayerCardBox.qml @@ -2,13 +2,18 @@ import QtQuick import QtQuick.Layouts +import Fk import Fk.Pages GraphicsBox { id: root 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 width: 70 + 700 @@ -50,7 +55,7 @@ GraphicsBox { Text { color: "#E4D5A0" - text: Backend.translate(areaName) + text: luatr(areaName) anchors.fill: parent wrapMode: Text.WrapAnywhere verticalAlignment: Text.AlignVCenter @@ -95,26 +100,15 @@ GraphicsBox { MetroButton { anchors.bottom: parent.bottom - text: Backend.translate("OK") + text: luatr("OK") 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) } 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/PoxiBox.qml b/Fk/RoomElement/PoxiBox.qml index 3abdd791..27340916 100644 --- a/Fk/RoomElement/PoxiBox.qml +++ b/Fk/RoomElement/PoxiBox.qml @@ -7,7 +7,7 @@ import Fk.Pages GraphicsBox { 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 width: 70 + 700 @@ -50,7 +50,7 @@ GraphicsBox { Text { color: "#E4D5A0" - text: Backend.translate(areaName) + text: luatr(areaName) anchors.fill: parent wrapMode: Text.WrapAnywhere verticalAlignment: Text.AlignVCenter @@ -71,12 +71,10 @@ GraphicsBox { number: model.number || 0 autoBack: false known: model.cid !== -1 - selectable: { - return root.selected_ids.includes(model.cid) || JSON.parse(Backend.callLuaFunction( - "PoxiFilter", - [root.poxi_type, model.cid, root.selected_ids, root.card_data, root.extra_data] - )); - } + selectable: root.selected_ids.includes(model.cid) || + lcall("PoxiFilter", root.poxi_type, model.cid, root.selected_ids, + root.card_data, root.extra_data); + onSelectedChanged: { if (selected) { chosenInBox = true; @@ -102,20 +100,16 @@ GraphicsBox { MetroButton { width: 120 height: 35 - text: Backend.translate("OK") - enabled: { - return JSON.parse(Backend.callLuaFunction( - "PoxiFeasible", - [root.poxi_type, root.selected_ids, root.card_data, root.extra_data] - )); - } + text: luatr("OK") + enabled: lcall("PoxiFeasible", root.poxi_type, root.selected_ids, + root.card_data, root.extra_data); onClicked: root.cardsSelected(root.selected_ids) } MetroButton { width: 120 height: 35 - text: Backend.translate("Cancel") + text: luatr("Cancel") visible: root.cancelable onClicked: root.cardsSelected([]) } diff --git a/Fk/RoomElement/SkillArea.qml b/Fk/RoomElement/SkillArea.qml index 6aa1f8b7..42c08542 100644 --- a/Fk/RoomElement/SkillArea.qml +++ b/Fk/RoomElement/SkillArea.qml @@ -112,10 +112,7 @@ Flickable { return false; }; - const data = JSON.parse(Backend.callLuaFunction( - "GetSkillData", - [skill_name] - )); + const data = lcall("GetSkillData", skill_name); if (prelight) { if (!modelContains(prelight_skills, data)) diff --git a/Fk/RoomElement/UltSkillAnimation.qml b/Fk/RoomElement/UltSkillAnimation.qml index 9c95489b..04c41fa3 100644 --- a/Fk/RoomElement/UltSkillAnimation.qml +++ b/Fk/RoomElement/UltSkillAnimation.qml @@ -28,12 +28,12 @@ Item { Text { text: { let o = "$" + skillName + "_" + generalName + (index % 2 + 1); - let p = Backend.translate(o); + let p = luatr(o); if (o !== p) { return p; } o = "$" + skillName + (index % 2 + 1); - p = Backend.translate(o); + p = luatr(o); if (o === p) { return "Ultimate Skill Invoked!"; } @@ -59,12 +59,12 @@ Item { Text { text: { let o = "$" + skillName + "_" + generalName + ((index + 1) % 2 + 1); - let p = Backend.translate(o); + let p = luatr(o); if (o !== p) { return p; } o = "$" + skillName + ((index + 1) % 2 + 1); - p = Backend.translate(o); + p = luatr(o); if (o === p) { return "Ultimate Skill Invoked!"; } @@ -90,7 +90,7 @@ Item { Text { topPadding: 5 id: skill - text: Backend.translate(skillName) + text: luatr(skillName) font.family: fontLi2.name font.pixelSize: 40 x: root.width / 2 + 100 diff --git a/Fk/RoomElement/ViewGeneralPile.qml b/Fk/RoomElement/ViewGeneralPile.qml index 46645f41..36e1fd1e 100644 --- a/Fk/RoomElement/ViewGeneralPile.qml +++ b/Fk/RoomElement/ViewGeneralPile.qml @@ -13,7 +13,7 @@ ColumnLayout { Layout.fillWidth: true Layout.preferredHeight: childrenRect.height + 4 - text: Backend.translate(extra_data.name) + text: luatr(extra_data.name) } GridView { diff --git a/Fk/RoomElement/ViewPile.qml b/Fk/RoomElement/ViewPile.qml index 4b0bb269..f44c8610 100644 --- a/Fk/RoomElement/ViewPile.qml +++ b/Fk/RoomElement/ViewPile.qml @@ -13,7 +13,7 @@ ColumnLayout { Layout.fillWidth: true Layout.preferredHeight: childrenRect.height + 4 - text: Backend.translate(extra_data.name) + text: luatr(extra_data.name) } GridView { @@ -32,7 +32,7 @@ ColumnLayout { Component.onCompleted: { let data = {} if (extra_data.ids) { - data = JSON.parse(Backend.callLuaFunction("GetCardData", [modelData])); + data = lcall("GetCardData", modelData); } else { data.cid = 0; data.name = modelData; diff --git a/Fk/SkillInteraction/SkillCombo.qml b/Fk/SkillInteraction/SkillCombo.qml index 97eb8c4a..ccf15719 100644 --- a/Fk/SkillInteraction/SkillCombo.qml +++ b/Fk/SkillInteraction/SkillCombo.qml @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later import QtQuick +import Fk import Fk.Pages MetroButton { @@ -12,34 +13,21 @@ MetroButton { property string answer: default_choice property bool detailed: false - 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; - } - - text: processPrompt(answer) + text: Util.processPrompt(answer) onAnswerChanged: { if (!answer) return; - Backend.callLuaFunction( - "SetInteractionDataOfSkill", - [skill, JSON.stringify(answer)] - ); + lcall("SetInteractionDataOfSkill", skill, JSON.stringify(answer)); roomScene.dashboard.startPending(skill); } onClicked: { if (detailed) { - roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/DetailedChoiceBox.qml"); + roomScene.popupBox.sourceComponent = + Qt.createComponent("../RoomElement/DetailedChoiceBox.qml"); } else { - roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/ChoiceBox.qml"); + roomScene.popupBox.sourceComponent = + Qt.createComponent("../RoomElement/ChoiceBox.qml"); } const box = roomScene.popupBox.item; box.options = choices; diff --git a/Fk/SkillInteraction/SkillSpin.qml b/Fk/SkillInteraction/SkillSpin.qml index a58368d9..924501fa 100644 --- a/Fk/SkillInteraction/SkillSpin.qml +++ b/Fk/SkillInteraction/SkillSpin.qml @@ -10,11 +10,7 @@ SpinBox { // from, to onValueChanged: { - Backend.callLuaFunction( - "SetInteractionDataOfSkill", - [skill, JSON.stringify(answer)] - ); + lcall("SetInteractionDataOfSkill", skill, JSON.stringify(answer)); roomScene.dashboard.startPending(skill); } - } diff --git a/Fk/Splash.qml b/Fk/Splash.qml index 4d9cb736..c88370f5 100644 --- a/Fk/Splash.qml +++ b/Fk/Splash.qml @@ -89,8 +89,14 @@ Rectangle { id: textAni running: false loops: Animation.Infinite - NumberAnimation { from: 0; to: 1; duration: 1600; easing.type: Easing.InOutQuad; } - NumberAnimation { from: 1; to: 0; duration: 1600; easing.type: Easing.InOutQuad; } + NumberAnimation { + from: 0; to: 1; duration: 1600 + easing.type: Easing.InOutQuad + } + NumberAnimation { + from: 1; to: 0; duration: 1600 + easing.type: Easing.InOutQuad + } } } diff --git a/Fk/Toast.qml b/Fk/Toast.qml index e65ddb09..dee94a74 100644 --- a/Fk/Toast.qml +++ b/Fk/Toast.qml @@ -15,7 +15,8 @@ Rectangle { property real time: defaultTime readonly property real fadeTime: 300 - anchors.horizontalCenter: parent != null ? parent.horizontalCenter : undefined + anchors.horizontalCenter: parent != null ? parent.horizontalCenter + : undefined height: message.height + 20 width: message.width + 40 radius: 16 diff --git a/Fk/ToastManager.qml b/Fk/ToastManager.qml index 3649c42a..78bf6bd3 100644 --- a/Fk/ToastManager.qml +++ b/Fk/ToastManager.qml @@ -2,8 +2,8 @@ import QtQuick -// copy from https://gist.github.com/jonmcclung/bae669101d17b103e94790341301c129 -// and modified some code +// https://gist.github.com/jonmcclung/bae669101d17b103e94790341301c129 +// modified some code ListView { function show(text, duration) { if (duration === undefined) { diff --git a/Fk/main.qml b/Fk/main.qml index 02b4f81b..75fe3c0b 100644 --- a/Fk/main.qml +++ b/Fk/main.qml @@ -45,7 +45,8 @@ Window { StackView { id: mainStack 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 anchors.fill: parent } @@ -159,7 +160,10 @@ Window { return; } if (mainWindow.is_pending && command !== "ChangeSelf") { - mainWindow.pending_message.push({ command: command, jsonData: jsonData }); + mainWindow.pending_message.push({ + command: command, + jsonData: jsonData, + }); } else { if (command === "StartChangeSelf") { mainWindow.is_pending = true; @@ -254,4 +258,18 @@ Window { 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 luatr(src) { + return Backend.translate(src); + } } diff --git a/Fk/qmldir b/Fk/qmldir index 3a502eb2..452ae67b 100644 --- a/Fk/qmldir +++ b/Fk/qmldir @@ -1,3 +1,3 @@ module Fk SkinBank 1.0 skin-bank.js -Utility 1.0 util.js +Util 1.0 util.js diff --git a/Fk/skin-bank.js b/Fk/skin-bank.js index 74c608d0..ca305c01 100644 --- a/Fk/skin-bank.js +++ b/Fk/skin-bank.js @@ -31,18 +31,20 @@ const searchPkgResource = function(path, name, suffix) { } function getGeneralExtraPic(name, extra) { - const data = JSON.parse(Backend.callLuaFunction("GetGeneralData", [name])); + const data = lcall("GetGeneralData", name); 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)) { return path; } } function getGeneralPicture(name) { - const data = JSON.parse(Backend.callLuaFunction("GetGeneralData", [name])); + const data = lcall("GetGeneralData", name); 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)) { return path; } @@ -54,14 +56,15 @@ function getCardPicture(cidOrName) { let name = "unknown"; if (typeof cidOrName === 'string') { name = cidOrName; - extension = Backend.callLuaFunction("GetCardExtensionByName", [cidOrName]); + extension = lcall("GetCardExtensionByName", cidOrName); } else { - const data = JSON.parse(Backend.callLuaFunction("GetCardData", [cid])); + const data = lcall("GetCardData", cid); extension = data.extension; 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)) { return path; } else { @@ -72,9 +75,10 @@ function getCardPicture(cidOrName) { } 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)) { return path; } else { @@ -86,10 +90,11 @@ function getDelayedTrickPicture(name) { function getEquipIcon(cid, icon) { - const data = JSON.parse(Backend.callLuaFunction("GetCardData", [cid])); + const data = lcall("GetCardData", cid); const extension = data.extension; 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)) { return path; } else { diff --git a/Fk/util.js b/Fk/util.js index 785d29d0..e7f53fa1 100644 --- a/Fk/util.js +++ b/Fk/util.js @@ -1,7 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -.pragma library - function convertNumber(number) { if (number === 1) return "A"; @@ -14,8 +12,20 @@ function convertNumber(number) { return ""; } -Array.prototype.contains = function(element) { - return this.indexOf(element) != -1; +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; } Array.prototype.prepend = function() { diff --git a/lua/client/client_util.lua b/lua/client/client_util.lua index 55ae80dc..2baa02b6 100644 --- a/lua/client/client_util.lua +++ b/lua/client/client_util.lua @@ -127,6 +127,10 @@ function GetCardExtensionByName(cardName) return card and card.package.extensionName or "" end +function GetAllMods() + return json.encode(Fk.extensions) +end + function GetAllGeneralPack() local ret = {} for _, name in ipairs(Fk.package_names) do diff --git a/lua/core/card.lua b/lua/core/card.lua index 71ec5e15..256dff7f 100644 --- a/lua/core/card.lua +++ b/lua/core/card.lua @@ -171,7 +171,7 @@ local function updateColorAndNumber(card) local different_color = false for i, id in ipairs(card.subcards) do 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 card.suit = c.suit else diff --git a/lua/core/engine.lua b/lua/core/engine.lua index c8d2a4f2..9b21d705 100644 --- a/lua/core/engine.lua +++ b/lua/core/engine.lua @@ -7,6 +7,7 @@ --- 同时也提供了许多常用的函数。 --- ---@class Engine : Object +---@field public extensions table @ 所有mod列表及其包含的拓展包 ---@field public packages table @ 所有拓展包的列表 ---@field public package_names string[] @ 含所有拓展包名字的数组,为了方便排序 ---@field public skills table @ 所有的技能 @@ -44,6 +45,11 @@ function Engine:initialize() Fk = self + self.extensions = { + ["standard"] = { "standard" }, + ["standard_cards"] = { "standard_cards" }, + ["maneuvering"] = { "maneuvering" }, + } self.packages = {} -- name --> Package self.package_names = {} self.skills = {} -- name --> Skill @@ -142,10 +148,13 @@ function Engine:loadPackages() -- so dont use type(pack) == "table" here if type(pack) == "table" then if pack[1] ~= nil then + self.extensions[dir] = {} for _, p in ipairs(pack) do + table.insert(self.extensions[dir], p.name) self:loadPackage(p) end else + self.extensions[dir] = { pack.name } self:loadPackage(pack) end end diff --git a/lua/server/events/misc.lua b/lua/server/events/misc.lua index 36128b8b..db18d589 100644 --- a/lua/server/events/misc.lua +++ b/lua/server/events/misc.lua @@ -128,9 +128,10 @@ end GameEvent.functions[GameEvent.ClearEvent] = function(self) local event = self.data[1] local logic = self.room.logic - event:clear_func() + -- 不可中断 + Pcall(event.clear_func, event) for _, f in ipairs(event.extra_clear_funcs) do - if type(f) == "function" then f(event) end + if type(f) == "function" then Pcall(f, event) end end -- cleaner顺利执行完了,出栈吧 diff --git a/packages/test/qml/TestDialog.qml b/packages/test/qml/TestDialog.qml index be4ad3e6..e377da40 100644 --- a/packages/test/qml/TestDialog.qml +++ b/packages/test/qml/TestDialog.qml @@ -16,7 +16,7 @@ ColumnLayout { Layout.fillWidth: true Layout.preferredHeight: childrenRect.height + 4 - text: Backend.translate(extra_data.name) + text: luatr(extra_data.name) } PathView { diff --git a/src/core/packman.cpp b/src/core/packman.cpp index a98d7ea8..dead59c2 100644 --- a/src/core/packman.cpp +++ b/src/core/packman.cpp @@ -393,7 +393,7 @@ clean: int PackMan::status(const QString &name) { git_repository *repo = NULL; int error; - git_status_list *status_list; + git_status_list *status_list = NULL; size_t i, maxi; const git_status_entry *s; auto path = QString("packages/%1").arg(name).toUtf8(); From 3031131e1b73528041d8560f328204a37fe47d8e Mon Sep 17 00:00:00 2001 From: notify Date: Fri, 26 Jan 2024 14:02:55 +0800 Subject: [PATCH 07/30] =?UTF-8?q?=E7=A6=81=E5=B0=86=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E6=94=B9=E8=BF=9B=20(#309)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RT 不影响Lua API --- Fk/Config.qml | 44 +- Fk/LobbyElement/BanGeneralSetting.qml | 246 +++++++--- Fk/LobbyElement/RoomGeneralSettings.qml | 49 +- Fk/LobbyElement/RoomPackageSettings.qml | 27 +- Fk/Pages/GeneralsOverview.qml | 627 +++++++++++++++--------- Fk/Pages/Lobby.qml | 12 +- Fk/Pages/RoomLogic.js | 34 +- image/button/skill/locked.png | Bin 0 -> 2239 bytes lua/client/client_util.lua | 5 + lua/client/i18n/zh_CN.lua | 7 + lua/core/engine.lua | 3 + 11 files changed, 707 insertions(+), 347 deletions(-) create mode 100644 image/button/skill/locked.png diff --git a/Fk/Config.qml b/Fk/Config.qml index 1d5c079c..c488af08 100644 --- a/Fk/Config.qml +++ b/Fk/Config.qml @@ -15,7 +15,7 @@ QtObject { property string roomBg property string bgmFile property string language - property list disabledPack: [] + // property list disabledPack: [] property string preferedMode property int preferedPlayerNum property int preferredGeneralNum @@ -23,9 +23,12 @@ QtObject { property real bgmVolume property bool disableMsgAudio property bool hideUseless - property list disabledGenerals: [] - property list disableGeneralSchemes: [] - property int disableSchemeIdx: 0 + // property list disabledGenerals: [] + // property list disableGeneralSchemes: [] + // property int disableSchemeIdx: 0 + property list disableSchemes: [] + property int currentDisableIdx: 0 + property var curScheme property int preferredTimeout property int preferredLuckTime @@ -52,9 +55,9 @@ QtObject { property list blockedUsers: [] property int totalTime: 0 // FIXME: only for notifying - onDisabledGeneralsChanged: { - disableGeneralSchemes[disableSchemeIdx] = disabledGenerals; - } + // onDisabledGeneralsChanged: { + // disableGeneralSchemes[disableSchemeIdx] = disabledGenerals; + // } function loadConf() { conf = JSON.parse(Backend.loadConf()); @@ -75,7 +78,7 @@ QtObject { return 'en_US'; } })(); - disabledPack = conf.disabledPack ?? [ "test_p_0" ]; + // disabledPack = conf.disabledPack ?? [ "test_p_0" ]; preferedMode = conf.preferedMode ?? "aaa_role_mode"; preferedPlayerNum = conf.preferedPlayerNum ?? 2; preferredGeneralNum = conf.preferredGeneralNum ?? 3; @@ -87,9 +90,17 @@ QtObject { preferredTimeout = conf.preferredTimeout ?? 15; preferredLuckTime = conf.preferredLuckTime ?? 0; firstRun = conf.firstRun ?? true; - disabledGenerals = conf.disabledGenerals ?? []; - disableGeneralSchemes = conf.disableGeneralSchemes ?? [ disabledGenerals ]; - disableSchemeIdx = conf.disableSchemeIdx ?? 0; + // disabledGenerals = conf.disabledGenerals ?? []; + // disableGeneralSchemes = conf.disableGeneralSchemes ?? [ disabledGenerals ]; + // disableSchemeIdx = conf.disableSchemeIdx ?? 0; + disableSchemes = conf.disableSchemes ?? [{ + name: "", + banPkg: {}, // 被禁用的包,内部数据为 包名: 白名单武将名数组 + normalPkg: {}, // 未被禁用的包,内部数据为 包名: 黑名单武将名数组 + banCardPkg: [], // 被禁用的卡包 + }]; + currentDisableIdx = conf.currentDisableIdx ?? 0; + curScheme = disableSchemes[currentDisableIdx]; blockedUsers = conf.blockedUsers ?? []; } @@ -104,7 +115,7 @@ QtObject { conf.roomBg = roomBg; conf.bgmFile = bgmFile; conf.language = language; - conf.disabledPack = disabledPack; + // conf.disabledPack = disabledPack; conf.preferedMode = preferedMode; conf.preferedPlayerNum = preferedPlayerNum; conf.ladyImg = ladyImg; @@ -116,9 +127,12 @@ QtObject { conf.preferredTimeout = preferredTimeout; conf.preferredLuckTime = preferredLuckTime; conf.firstRun = firstRun; - conf.disabledGenerals = disabledGenerals; - conf.disableGeneralSchemes = disableGeneralSchemes; - conf.disableSchemeIdx = disableSchemeIdx; + // conf.disabledGenerals = disabledGenerals; + // conf.disableGeneralSchemes = disableGeneralSchemes; + // conf.disableSchemeIdx = disableSchemeIdx; + disableSchemes[currentDisableIdx] = curScheme; + conf.disableSchemes = disableSchemes; + conf.currentDisableIdx = currentDisableIdx; conf.blockedUsers = blockedUsers; Backend.saveConf(JSON.stringify(conf, undefined, 2)); diff --git a/Fk/LobbyElement/BanGeneralSetting.qml b/Fk/LobbyElement/BanGeneralSetting.qml index 63e6711d..59ef8439 100644 --- a/Fk/LobbyElement/BanGeneralSetting.qml +++ b/Fk/LobbyElement/BanGeneralSetting.qml @@ -11,6 +11,7 @@ Item { ColumnLayout { anchors.fill: parent RowLayout { + Layout.fillWidth: true anchors.rightMargin: 8 spacing: 16 Text { @@ -19,65 +20,94 @@ Item { ComboBox { id: banCombo textRole: "name" + Layout.fillWidth: true model: ListModel { id: banComboList } onCurrentIndexChanged: { - config.disableSchemeIdx = currentIndex; - config.disabledGenerals = config.disableGeneralSchemes[currentIndex]; + word.text = ""; + config.disableSchemes[config.currentDisableIdx] = config.curScheme; + config.currentDisableIdx = currentIndex; + config.curScheme = config.disableSchemes[currentIndex]; } } - Button { - text: luatr("New") - onClicked: { - const i = config.disableGeneralSchemes.length; - banComboList.append({ - name: luatr("List") + (i + 1), - }); - config.disableGeneralSchemes.push([]); - } - } + GridLayout { + columns: 2 - Button { - text: luatr("Clear") - onClicked: { - config.disabledGenerals = []; - } - } - - Button { - text: luatr("Export") - onClicked: { - Backend.copyToClipboard(JSON.stringify(config.disabledGenerals)); - 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; + Button { + text: luatr("New") + onClicked: { + const i = config.disableSchemes.length; + banComboList.append({ + name: luatr("List") + (i + 1), + }); + config.disableSchemes.push({ + name: "", + banPkg: {}, + normalPkg: {}, + banCardPkg: [], + }); } - if (!data instanceof Array) { - toast.show(luatr("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" && luatr(e) !== e) { - d.push(e); + } + + Button { + 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(luatr("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 = ""; } } } @@ -89,35 +119,119 @@ Item { text: luatr("Help_Ban_List") } - GridView { - id: listView + GridLayout { + id: grid + flow: GridLayout.TopToBottom + rows: 2 Layout.fillWidth: true Layout.fillHeight: true - clip: true - cellWidth: width / 4 - cellHeight: 24 - model: config.disabledGenerals - delegate: Text { - width: listView.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_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.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: { - for (let i = 0; i < config.disableGeneralSchemes.length; i++) { + for (let i = 0; i < config.disableSchemes.length; i++) { banComboList.append({ - name: luatr("List") + (i + 1), + name: config.disableSchemes[i]?.name || (luatr("List") + (i + 1)), }); } - banCombo.currentIndex = config.disableSchemeIdx; + banCombo.currentIndex = config.currentDisableIdx; } } diff --git a/Fk/LobbyElement/RoomGeneralSettings.qml b/Fk/LobbyElement/RoomGeneralSettings.qml index ed7cca5b..1889a42a 100644 --- a/Fk/LobbyElement/RoomGeneralSettings.qml +++ b/Fk/LobbyElement/RoomGeneralSettings.qml @@ -113,7 +113,6 @@ Flickable { id: warning anchors.rightMargin: 8 visible: { - //config.disabledPack; // 没什么用,只是为了禁包刷新时刷新visible罢了 const avail = lcall("GetAvailableGeneralsNum"); const ret = avail < config.preferredGeneralNum * config.preferedPlayerNum; @@ -164,36 +163,32 @@ Flickable { config.saveConf(); root.finished(); mainWindow.busy = true; + let k, arr; - let disabledGenerals = config.disabledGenerals.slice(); - if (disabledGenerals.length) { - const availablePack = lcall("GetAllGeneralPack"). - filter((pack) => !config.disabledPack.includes(pack)); - disabledGenerals = disabledGenerals.filter((general) => { - return availablePack.find(pack => - lcall("GetGenerals", pack).includes(general)); - }); - - disabledGenerals = Array.from(new Set(disabledGenerals)); + let disabledGenerals = []; + for (k in config.curScheme.banPkg) { + arr = config.curScheme.banPkg[k]; + if (arr.length !== 0) { + const generals = lcall("GetGenerals", k); + disabledGenerals.push(...generals.filter(g => !arr.includes(g))); + } + } + 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 => { if (!disabledPack.includes(p)) { disabledPack.push(p); } }); - const generalPacks = lcall("GetAllGeneralPack"); - for (let pk of generalPacks) { - if (disabledPack.includes(pk)) continue; - let generals = lcall("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( "CreateRoom", @@ -232,8 +227,12 @@ Flickable { playerNum.value = config.preferedPlayerNum; - config.disabledPack.forEach(p => lcall("UpdatePackageEnable", p, false)); - config.disabledPackChanged(); + for (let k in config.curScheme.banPkg) { + lcall("UpdatePackageEnable", k, false); + } + config.curScheme.banCardPkg.forEach(p => + lcall("UpdatePackageEnable", p, false)); + config.curSchemeChanged(); } } } diff --git a/Fk/LobbyElement/RoomPackageSettings.qml b/Fk/LobbyElement/RoomPackageSettings.qml index fff8328f..cbd2a405 100644 --- a/Fk/LobbyElement/RoomPackageSettings.qml +++ b/Fk/LobbyElement/RoomPackageSettings.qml @@ -113,7 +113,15 @@ Flickable { checked: pkg_enabled 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,15 +129,16 @@ Flickable { } function checkPackage(orig_name, checked) { - const packs = config.disabledPack; - if (checked) { - const idx = packs.indexOf(orig_name); - if (idx !== -1) packs.splice(idx, 1); + const s = config.curScheme; + if (!checked) { + s.banPkg[orig_name] = []; + s.normalPkg[orig_name] = undefined; } else { - packs.push(orig_name); + s.normalPkg[orig_name] = undefined; + s.banPkg[orig_name] = undefined; } lcall("UpdatePackageEnable", orig_name, checked); - config.disabledPackChanged(); + config.curSchemeChanged(); } Component.onCompleted: { @@ -143,7 +152,7 @@ Flickable { gpacklist.append({ name: luatr(orig), orig_name: orig, - pkg_enabled: !config.disabledPack.includes(orig), + pkg_enabled: !config.curScheme.banPkg[orig], }); } @@ -155,7 +164,7 @@ Flickable { cpacklist.append({ name: luatr(orig), orig_name: orig, - pkg_enabled: !config.disabledPack.includes(orig), + pkg_enabled: !config.curScheme.banCardPkg.includes(orig), }); } loading = false; diff --git a/Fk/Pages/GeneralsOverview.qml b/Fk/Pages/GeneralsOverview.qml index d1fae2fd..840b515e 100644 --- a/Fk/Pages/GeneralsOverview.qml +++ b/Fk/Pages/GeneralsOverview.qml @@ -11,39 +11,97 @@ Item { id: root property bool loaded: false + property int stat: 0 // 0=normal 1=banPkg 2=banChara Rectangle { - anchors.fill: listView - color: "#88EEEEEE" + id: listBg + width: 260; height: parent.height + color: "snow" radius: 6 } ListView { - id: listView + id: modList + width: 130; height: parent.height + anchors.top: listBg.top; anchors.left: listBg.left clip: true - width: 130 - height: parent.height - 20 - y: 10 - ScrollBar.vertical: ScrollBar {} 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 delegate: Item { - width: listView.width + width: modList.width height: 40 Text { text: luatr(name) + color: modList.currentIndex === index ? "black" : "white" anchors.centerIn: parent } TapHandler { 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]) { + s.banPkg[name] = undefined; + s.normalPkg[name] = undefined; + } else { + s.normalPkg[name] = undefined; + s.banPkg[name] = []; + } + config.curSchemeChanged(); + } else { + pkgList.currentIndex = index; + } } } } @@ -51,13 +109,102 @@ Item { 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: 24 + } + + Item { Layout.fillWidth: true } + + TextField { + id: word + clip: true + leftPadding: 5 + rightPadding: 5 + } + + ToolButton { + text: luatr("Search") + enabled: word.text !== "" + onClicked: { + pkgList.currentIndex = 0; + vanishAnim.start(); + } + } + + ToolButton { + id: banButton + 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 + 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") + onClicked: { + mainStack.pop(); + config.saveConf(); + } + } + } + } + GridView { id: gridView clip: true - width: root.width - listView.width - generalDetail.width - 16 - height: parent.height - 20 - y: 10 - anchors.left: listView.right + width: root.width - listBg.width - 16 + height: parent.height - bar.height - 24 + y: 16 + bar.height + anchors.left: listBg.right anchors.leftMargin: 8 + (width % 100) / 2 cellHeight: 140 cellWidth: 100 @@ -66,24 +213,77 @@ Item { autoBack: false name: modelData onClicked: { - generalText.clear(); - generalDetail.general = modelData; - generalDetail.updateGeneral(); - // generalDetail.open(); + if (stat === 2) { + const s = config.curScheme; + const gdata = lcall("GetGeneralData", modelData); + 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 { anchors.fill: parent 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.7; + } else { + if (!!s.normalPkg[pack]?.includes(modelData)) return 0.7; + } + return 0; + } Behavior on opacity { NumberAnimation {} } } GlowText { - visible: config.disabledGenerals.includes(modelData) - text: '禁' + id: banText + 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 font.family: fontLi2.name color: "#E4D5A0" @@ -108,7 +308,7 @@ Item { PropertyAnimation { target: gridView property: "y" - to: 30 + to: 36 + bar.height duration: 150 easing.type: Easing.InOutQuad } @@ -117,7 +317,7 @@ Item { gridView.model = lcall("SearchAllGenerals", word.text); } else { gridView.model = lcall("SearchGenerals", - listView.model.get(listView.currentIndex).name, word.text); + pkgList.model[pkgList.currentIndex], word.text); } word.text = ""; appearAnim.start(); @@ -138,23 +338,78 @@ Item { PropertyAnimation { target: gridView property: "y" - from: 20 - to: 10 + from: 36 + bar.height + to: 16 + bar.height duration: 150 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 "胜利语音"; + } + 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 - width: 310 - height: parent.height - searcher.height - 20 - y: 10 - anchors.right: parent.right - anchors.rightMargin: 10 - color: "#88EEEEEE" - radius: 8 + width: realMainWin.width * 0.6 + height: realMainWin.height * 0.8 + anchors.centerIn: parent + background: Rectangle { + color: "#EEEEEEEE" + radius: 5 + border.color: "#A6967A" + border.width: 1 + } property string general: "caocao" @@ -196,7 +451,7 @@ Item { function findDeathAudio(general) { const extension = lcall("GetGeneralData", general).extension; const fname = AppPath + "/packages/" + extension + "/audio/death/" - + general + ".mp3"; + + general + ".mp3"; if (Backend.exists(fname)) { audioDeath.visible = true; } else { @@ -212,7 +467,7 @@ Item { if (data.companions.length > 0){ let ret = "" + luatr("Companions") - + ": "; + + "
: "; data.companions.forEach(t => { ret += luatr(t) + ' ' }); @@ -236,223 +491,153 @@ Item { addSkillAudio(general + "_win_audio"); } - Flickable { - flickableDirection: Flickable.VerticalFlick - contentHeight: detailLayout.height - width: parent.width - 40 - height: parent.height - 40 - clip: true + Item { anchors.centerIn: parent - ScrollBar.vertical: ScrollBar {} + width: parent.width / mainWindow.scale + height: parent.height / mainWindow.scale + scale: mainWindow.scale - ColumnLayout { - id: detailLayout - width: parent.width - - GeneralCardItem { - id: detailGeneralCard - Layout.alignment: Qt.AlignHCenter - name: "caocao" - } - - 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 { + id: generalInfo + width: 150 + ColumnLayout { + width: parent.width + GeneralCardItem { + id: detailGeneralCard + name: "caocao" + scale: 1.5; transformOrigin: Item.TopLeft } + + 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 "官方"; + } + return ret; + } + text: { + const general = generalDetail.general; + return [ + luatr(lcall("GetGeneralData", general).package), + "称号:" + trans("#" + general), + "设计:" + trans("designer:" + general), + "配音:" + trans("cv:" + general), + "画师:" + trans("illustrator:" + general), + ].join("
"); + } + } + + Timer { + id: opTimer + interval: 4000 + } + 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 contentItem: ColumnLayout { Text { Layout.fillWidth: true - text: { - if (name.endsWith("_win_audio")) { - return "胜利语音"; - } - return luatr(name) + (idx ? " (" + idx.toString() + ")" - : ""); - } + text: luatr("Death audio") 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; + const orig = "~" + generalDetail.general; + const tr = luatr(orig); + if (tr === orig) { + return ""; } - - if (orig_trans !== orig) { - return orig_trans; - } - - return ""; + return tr; } wrapMode: Text.WordWrap } } onClicked: { - callbacks["LogEvent"](JSON.stringify({ - type: "PlaySkillSound", - name: name, - general: detailGeneralCard.name, - i: idx, - })); + const general = generalDetail.general + const extension = lcall("GetGeneralData", general).extension; + Backend.playSound("./packages/" + extension + "/audio/death/" + + general); } } } - - Button { - id: audioDeath - Layout.fillWidth: true - contentItem: ColumnLayout { - Text { - Layout.fillWidth: true - text: luatr("Death audio") - font.bold: true - font.pixelSize: 14 - } - Text { - Layout.fillWidth: true - text: { - const orig = "~" + generalDetail.general; - const tr = luatr(orig); - if (tr === orig) { - return ""; - } - return tr; - } - wrapMode: Text.WordWrap - } - } - - onClicked: { - const general = generalDetail.general - const extension = lcall("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: luatr("Search") - enabled: word.text !== "" - onClicked: { - listView.currentIndex = 0; - vanishAnim.start(); - } - } - } - } - } - - ColumnLayout { - anchors.right: parent.right - Button { - text: luatr("Quit") - onClicked: { - mainStack.pop(); - config.saveConf(); - } - } - - Button { - id: banButton - text: luatr(config.disabledGenerals.includes(detailGeneralCard.name) ? - 'ResumeGeneral' : 'BanGeneral') - visible: detailGeneralCard.name - onClicked: { - const { disabledGenerals } = config; - const { name } = detailGeneralCard; - - if (banButton.text === luatr('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: 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]) - ); } } } function loadPackages() { if (loaded) return; + const _mods = lcall("GetAllModNames") + const modData = lcall("GetAllMods") const packs = lcall("GetAllGeneralPack"); - packs.forEach(name => { - if (!config.serverHiddenPacks.includes(name)) { - 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; } } diff --git a/Fk/Pages/Lobby.qml b/Fk/Pages/Lobby.qml index 6971e25a..8da9162c 100644 --- a/Fk/Pages/Lobby.qml +++ b/Fk/Pages/Lobby.qml @@ -64,11 +64,21 @@ Item { Text { horizontalAlignment: Text.AlignLeft Layout.fillWidth: true - text: (hasPassword ? luatr("Has Password") : "") + roomName + text: roomName font.pixelSize: 20 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: luatr(gameMode) } diff --git a/Fk/Pages/RoomLogic.js b/Fk/Pages/RoomLogic.js index 8d3eca11..025960fe 100644 --- a/Fk/Pages/RoomLogic.js +++ b/Fk/Pages/RoomLogic.js @@ -1,7 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -.import Fk.Util as Util - const Card = { Unknown : 0, PlayerHand : 1, @@ -479,6 +477,22 @@ function doIndicate(from, tos) { 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) => { const data = JSON.parse(jsonData); const id = data.id; @@ -936,7 +950,7 @@ callbacks["AskForSkillInvoke"] = (jsonData) => { const data = JSON.parse(jsonData); const skill = data[0]; const prompt = data[1]; - roomScene.promptText = prompt ? Util.processPrompt(prompt) + roomScene.promptText = prompt ? processPrompt(prompt) : luatr("#AskForSkillInvoke").arg(luatr(skill)); roomScene.state = "replying"; roomScene.okCancel.visible = true; @@ -1025,7 +1039,7 @@ callbacks["AskForChoice"] = (jsonData) => { roomScene.promptText = luatr("#AskForChoice") .arg(luatr(skill_name)); } else { - roomScene.setPrompt(Util.processPrompt(prompt), true); + roomScene.setPrompt(processPrompt(prompt), true); } roomScene.state = "replying"; let qmlSrc; @@ -1060,7 +1074,7 @@ callbacks["AskForChoices"] = (jsonData) => { roomScene.promptText = luatr("#AskForChoices") .arg(luatr(skill_name)); } else { - roomScene.setPrompt(Util.processPrompt(prompt), true); + roomScene.setPrompt(processPrompt(prompt), true); } roomScene.state = "replying"; let qmlSrc; @@ -1096,7 +1110,7 @@ callbacks["AskForCardChosen"] = (jsonData) => { roomScene.promptText = luatr("#AskForChooseCard") .arg(luatr(reason)); } else { - roomScene.setPrompt(Util.processPrompt(prompt), true); + roomScene.setPrompt(processPrompt(prompt), true); } roomScene.state = "replying"; roomScene.popupBox.sourceComponent = @@ -1128,7 +1142,7 @@ callbacks["AskForCardsChosen"] = (jsonData) => { roomScene.promptText = luatr("#AskForChooseCards") .arg(luatr(reason)).arg(min).arg(max); } else { - roomScene.setPrompt(Util.processPrompt(prompt), true); + roomScene.setPrompt(processPrompt(prompt), true); } roomScene.state = "replying"; @@ -1268,7 +1282,7 @@ callbacks["AskForUseActiveSkill"] = (jsonData) => { roomScene.promptText = luatr("#AskForUseActiveSkill") .arg(luatr(skill_name)); } else { - roomScene.setPrompt(Util.processPrompt(prompt), true); + roomScene.setPrompt(processPrompt(prompt), true); } roomScene.respond_play = false; @@ -1315,7 +1329,7 @@ callbacks["AskForUseCard"] = (jsonData) => { roomScene.promptText = luatr("#AskForUseCard") .arg(luatr(cardname)); } else { - roomScene.setPrompt(Util.processPrompt(prompt), true); + roomScene.setPrompt(processPrompt(prompt), true); } roomScene.responding_card = pattern; roomScene.respond_play = false; @@ -1337,7 +1351,7 @@ callbacks["AskForResponseCard"] = (jsonData) => { roomScene.promptText = luatr("#AskForResponseCard") .arg(luatr(cardname)); } else { - roomScene.setPrompt(Util.processPrompt(prompt), true); + roomScene.setPrompt(processPrompt(prompt), true); } roomScene.responding_card = pattern; roomScene.respond_play = true; diff --git a/image/button/skill/locked.png b/image/button/skill/locked.png new file mode 100644 index 0000000000000000000000000000000000000000..ce89fb8ba3edfccf10cdb76d8e3c8b5c33a8f771 GIT binary patch literal 2239 zcmV;w2tfCVP)760jDSJGsZxs<+>7_t(|PQ~QB3~^$JaJg;%lzmU7 zypxg#=3q`HA__?}%iF6`nUd14tdbm#=ueiMtlBd;jmmL?^P*#w3;?d{rcD-=4?3OB?#|B6-&y}O-U#8X0f@GE zKH}C;7J*r&1z$#B&2#`~0iaT;Q~==R%a_03*x2}qI&hq1;pFpqzh1A`n>=~)iYiBfAzQ?xg0zMr*HY9hMoCBBGzlsIJbU)+!TS38Pai&f@CyosLLx&83kv{% zZnulY#l`IA=H_nzU~6mZkD>}mYQUPQ2&*2^JK>o)aOr@hxmC-zTCGN_)oS#5J*cYc z2h%iR20*vlrC@fuU6T*g@Am-!?RI-ZQItghi_$nc0)v#~ZiNc7F%^k1ZoYL^XHD+( z)vH%;pFe;8GygUi3`!zt0>GU+cdD&c>vz|#T>}8Teft&wpzHe5U@+*hrnGiP>>C*c z(raHzQ7T8fGD)N$;-ct$LDX9WfR&XMOHmYMI2=OLG`xB9<{u9qKK!$7+hfMG1<6x* z|Ni|yzkU0rZf_P1EG% zzS$cL2C*|N!1teD#F%7}N!6lUBcFKL?^0Rd4C+!G3)3{M)z#Ir#bOZvKyk0v0|4vb z;9#cBXf%Rh_`J$v{lco|5i^;xH2Cp4CqfC{j42|N02~9j0N_KlT2-sns@?DRmEmxR z8#iuTv2FWAQ55~+#fwz{SX)~|xm;HE_xItt?jZp6;NZYKckY~;%jGZ_3{a_5aOKLC zY9^BzRVo!6A0OKY)~^bn0?(in;7N-gG4hm`RZc$Ra9!0QNv7FsHtO|yqtoex_MOL% zAGf%=H5!do*LBALaOu*eLjbsY_wKoNyS*VK=A+eWHMY06cNIl}ZQBr&NefgL3=k7D zQX;+No&hc5f{4oimf?8@daYK=x7+RZ{rmSX16Xuj?^HlWLDMwox(@mt2dXcK_B{tp z)A04{*W+5PR($m6(I0kpc6LRR6L?+(dF9649l>>7Wqo}etE;P* z0sIxSp37L!JrN-XV8$a>F}rp`Fbu=ibv^Ay$H(sOF822JVB0ntjRw}%*5+-B&!0a> zeWtY|$9`cFVwp=@BY@|4?(OXX0LtYuUc7h#(=<`9*ZC$oFLWe$2n4={PIF-b$8iz^ zFgwHbdL8@w`v3q{Ra4u?AZmjBZ)uk#bm}BOuIM-p#^W)v*<`P2JRU<)6ny#e1prX1 z)zItpP_0&R=FFMYJ{-sKEuX*?;mb?fCqp=uR5;{CEu&N_`4Z`LIskyBr6t_Db!)En z5;Jq-yBSJ@X?jPVX0r*?H1X-vC)~b$`&33jc6jGOnzv2U$gh|FSFofw5=jzrSg-(? z)9JJ@9*^f)xHBq~%jJ;CWH1~K0eJgU62{{(&YwSz;c)06uyDr+_8p6*ePZ~i{Pxm< z=lXE;5aS)BpNdeP{zbkwGmM_^$TAblU=MJ|_vVHmJ1 z3#zKlwU5Oh@{A>Tn8c}^15ZNyi9@k0s}EqH>v}cKN<~4_G-#UVj-@eFDwX6<2Etdw zkp#qf+vLBg@Xqr74e8CBH-FV?wOZG&UvJEI7eX=VJ%7|h#FVu+3r{7ygza0md3qWn6A;7UsQaZ;hTd(Y#|-$^*gPqaplm z!|TvPl}t?Pdydo>Z4n$JDduC6>Gr|zw)uMypJ`ePB}6}PsjQr1W>rft6Y_jwh|wb{ z&jTUR$^qfG8S0Fe^Vc{2oT4ISjUaF=sZUqRM@fv27l|}af!86?o*?`_!|%r^Ezf=w zqMj%T7Qzg2#^l#>!UTx^+k-#Xb3*02JNcD6?ruB|X$;9%RXpa*P&$?&f_&B>PeGOH zQhtK(_ud`WpZtFU{}#nasBYAQ7j+!>?@ruX(Y*KMagg6j{}*Q3CcWUrflCDV z@n N002ovPDHLkV1jV8OpE{k literal 0 HcmV?d00001 diff --git a/lua/client/client_util.lua b/lua/client/client_util.lua index 2baa02b6..bf6827e0 100644 --- a/lua/client/client_util.lua +++ b/lua/client/client_util.lua @@ -131,6 +131,10 @@ function GetAllMods() return json.encode(Fk.extensions) end +function GetAllModNames() + return json.encode(Fk.extension_names) +end + function GetAllGeneralPack() local ret = {} for _, name in ipairs(Fk.package_names) do @@ -142,6 +146,7 @@ function GetAllGeneralPack() end function GetGenerals(pack_name) + if not Fk.packages[pack_name] then return "{}" end local ret = {} for _, g in ipairs(Fk.packages[pack_name].generals) do if not g.total_hidden then diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index 9ae9ca97..e6b485f2 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -79,12 +79,16 @@ Fk:loadTranslationTable{ ["Clear"] = "清空", ["Help_Ban_List"] = "导出键会将这个方案的内容复制到剪贴板中;" .. "导入键会自动读取剪贴板,若可以导入则导入,不能导入则报错。", + ["Ban_Generals"] = "已禁用武将", + ["Ban_Packages"] = "禁用拓展包", + ["Whitelist_Generals"] = "白名单武将", ["Export"] = "导出", ["Export Success"] = "禁将方案已经复制到剪贴板。", ["Import"] = "导入", ["Not Legal"] = "导入失败:不是合法的JSON字符串。", ["Not JSON"] = "导入失败:数据格式不对。", ["Import Success"] = "从剪贴板导入禁将方案成功。", + ["Rename"] = "重命名", ["$OnlineInfo"] = "大厅人数:%1,总在线人数:%2", @@ -184,6 +188,9 @@ FreeKill使用的是libgit2的C API,与此同时使用Git完成拓展包的下 -- ["Quit"] = "退出", ["BanGeneral"] = "禁将", ["ResumeGeneral"] = "解禁", + ["BanPackage"] = "禁拓展包", + ["$BanPkgHelp"] = "正在禁用拓展包", + ["$BanCharaHelp"] = "正在禁用武将", ["Companions"] = "珠联璧合", ["Death audio"] = "阵亡", diff --git a/lua/core/engine.lua b/lua/core/engine.lua index 9b21d705..596af613 100644 --- a/lua/core/engine.lua +++ b/lua/core/engine.lua @@ -8,6 +8,7 @@ --- ---@class Engine : Object ---@field public extensions table @ 所有mod列表及其包含的拓展包 +---@field public extension_names string[] @ Mod名字的数组,为了方便排序 ---@field public packages table @ 所有拓展包的列表 ---@field public package_names string[] @ 含所有拓展包名字的数组,为了方便排序 ---@field public skills table @ 所有的技能 @@ -50,6 +51,7 @@ function Engine:initialize() ["standard_cards"] = { "standard_cards" }, ["maneuvering"] = { "maneuvering" }, } + self.extension_names = { "standard", "standard_cards", "maneuvering" } self.packages = {} -- name --> Package self.package_names = {} self.skills = {} -- name --> Skill @@ -147,6 +149,7 @@ function Engine:loadPackages() -- Note that instance of Package is a table too -- so dont use type(pack) == "table" here if type(pack) == "table" then + table.insert(self.extension_names, dir) if pack[1] ~= nil then self.extensions[dir] = {} for _, p in ipairs(pack) do From 0c3f9863a5b74ba2f9e912a11fe0a2dfcb64d24d Mon Sep 17 00:00:00 2001 From: notify Date: Fri, 26 Jan 2024 14:56:07 +0800 Subject: [PATCH 08/30] Changelog: v0.4.4 --- CHANGELOG.md | 8 +++++ CMakeLists.txt | 2 +- Fk/Pages/Room.qml | 52 ++------------------------- Fk/PhotoElement/LimitSkillItem.qml | 1 - Fk/RoomElement/AG.qml | 1 - Fk/RoomElement/CheckBox.qml | 4 +-- Fk/RoomElement/Dashboard.qml | 53 +++++++++++++++------------- android/AndroidManifest.xml | 4 +-- lua/client/client_util.lua | 12 ++++++- lua/client/i18n/zh_CN.lua | 4 +-- lua/core/skill_type/usable_skill.lua | 2 +- 11 files changed, 57 insertions(+), 86 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8564b19..f19b2ced 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # ChangeLog +## v0.4.4 + +禁将增强;修复bug + +UsableSkill的expand_pile功能加强 + +___ + ## v0.4.3 1. 事件栈和实际的函数调用栈分离 diff --git a/CMakeLists.txt b/CMakeLists.txt index f311507d..8a7e4122 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16) -project(FreeKill VERSION 0.4.3) +project(FreeKill VERSION 0.4.4) add_definitions(-DFK_VERSION=\"${CMAKE_PROJECT_VERSION}\") find_package(Qt6 REQUIRED COMPONENTS diff --git a/Fk/Pages/Room.qml b/Fk/Pages/Room.qml index b83569fc..16264eb9 100644 --- a/Fk/Pages/Room.qml +++ b/Fk/Pages/Room.qml @@ -1006,53 +1006,6 @@ Item { } } - /* 这东西似乎一直不好使啊 - 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 { id: miscStatus anchors.right: menuButton.left @@ -1085,10 +1038,9 @@ Item { } Shortcut { - sequence: "Esc" + sequence: "T" onActivated: { - easyChat.visible = false; - easyChatEdit.enabled = false; + roomDrawer.open(); } } diff --git a/Fk/PhotoElement/LimitSkillItem.qml b/Fk/PhotoElement/LimitSkillItem.qml index ef81094d..41c7a62e 100644 --- a/Fk/PhotoElement/LimitSkillItem.qml +++ b/Fk/PhotoElement/LimitSkillItem.qml @@ -40,7 +40,6 @@ Item { onSkillnameChanged: { let data = lcall("GetSkillData", skillname); - data = JSON.parse(data); if (data.frequency || data.switchSkillName) { skilltype = data.switchSkillName ? 'switch' : data.frequency; visible = true; diff --git a/Fk/RoomElement/AG.qml b/Fk/RoomElement/AG.qml index b35c4f64..ce899e6a 100644 --- a/Fk/RoomElement/AG.qml +++ b/Fk/RoomElement/AG.qml @@ -47,7 +47,6 @@ GraphicsBox { function addIds(ids) { ids.forEach((id) => { let data = lcall("GetCardData", id); - data = JSON.parse(data); data.selectable = true; data.footnote = ""; cards.append(data); diff --git a/Fk/RoomElement/CheckBox.qml b/Fk/RoomElement/CheckBox.qml index 9839e46a..61059631 100644 --- a/Fk/RoomElement/CheckBox.qml +++ b/Fk/RoomElement/CheckBox.qml @@ -58,7 +58,7 @@ GraphicsBox { MetroButton { Layout.fillWidth: true - text: Util.processPrompt("OK") + text: luatr("OK") enabled: root.result.length >= min_num onClicked: { @@ -68,7 +68,7 @@ GraphicsBox { MetroButton { Layout.fillWidth: true - text: processPrompt("Cancel") + text: luatr("Cancel") visible: cancelable onClicked: { diff --git a/Fk/RoomElement/Dashboard.qml b/Fk/RoomElement/Dashboard.qml index 6c9718dc..8e601289 100644 --- a/Fk/RoomElement/Dashboard.qml +++ b/Fk/RoomElement/Dashboard.qml @@ -71,7 +71,7 @@ RowLayout { handcardAreaItem.unselectAll(expectId); } - function expandPile(pile) { + function expandPile(pile, extra_ids, extra_footnote) { const expanded_pile_names = Object.keys(expanded_piles); if (expanded_pile_names.indexOf(pile) !== -1) return; @@ -80,30 +80,27 @@ RowLayout { const parentPos = roomScene.mapFromItem(self, 0, 0); expanded_piles[pile] = []; + let ids, footnote; if (pile === "_equip") { - const equips = self.equipArea.getAllCards(); - equips.forEach(data => { - data.x = parentPos.x; - data.y = parentPos.y; - const card = component.createObject(roomScene, data); - card.footnoteVisible = true; - card.footnote = luatr("$Equip"); - handcardAreaItem.add(card); - }) - handcardAreaItem.updateCardPosition(); + ids = self.equipArea.getAllCards(); + footnote = "$Equip"; + } else if (pile === "_extra") { + ids = extra_ids; + footnote = extra_footnote; } else { - const ids = lcall("GetPile", self.playerid, pile); - 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(pile); - handcardAreaItem.add(card); - }); - handcardAreaItem.updateCardPosition(); + ids = lcall("GetPile", self.playerid, pile); + footnote = pile; } + 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) { @@ -339,14 +336,20 @@ RowLayout { } }) - const pile = lcall("GetExpandPileOfSkill", pending_skill); - const pile_ids = lcall("GetPile", self.playerid, pile); + let pile = lcall("GetExpandPileOfSkill", pending_skill); + let pile_ids = pile; + if (typeof pile === "string") { + pile_ids = lcall("GetPile", self.playerid, pile); + } else { + pile = "_extra"; + } + pile_ids.forEach(cid => { if (lcall("ActiveCardFilter", pending_skill, cid, pendings, targets)) { enabled_cards.push(cid); }; if (!expanded_piles[pile]) { - expandPile(pile); + expandPile(pile, pile_ids, pending_skill); } }); diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index c85c1750..d2fc268e 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -3,8 +3,8 @@ + android:versionCode="404" + android:versionName="0.4.4"> diff --git a/lua/client/client_util.lua b/lua/client/client_util.lua index bf6827e0..1bf67992 100644 --- a/lua/client/client_util.lua +++ b/lua/client/client_util.lua @@ -586,7 +586,17 @@ end function GetExpandPileOfSkill(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 function GetGameModes() diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index e6b485f2..df4fb1a7 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -311,9 +311,9 @@ FreeKill使用的是libgit2的C API,与此同时使用Git完成拓展包的下 ["Resume"] = "继续", ["Bulletin Info"] = [==[ - ## v0.4.2 + ## v0.4.4 - 增加游玩计时和屏蔽提示。 + 禁将体验优化,然而烦请重新禁将。请善用禁用拓展包与白名单功能,旧方案已经不可导入。 ]==], } diff --git a/lua/core/skill_type/usable_skill.lua b/lua/core/skill_type/usable_skill.lua index 70e00244..7ef7bd87 100644 --- a/lua/core/skill_type/usable_skill.lua +++ b/lua/core/skill_type/usable_skill.lua @@ -3,7 +3,7 @@ ---@class UsableSkill : Skill ---@field public main_skill UsableSkill ---@field public max_use_time integer[] ----@field public expand_pile string +---@field public expand_pile? string | integer[] | fun(self: UsableSkill): integer[]|string? local UsableSkill = Skill:subclass("UsableSkill") function UsableSkill:initialize(name, frequency) From bdd1a68b1c1211c2b8f854fb9821ac6b6cda68b7 Mon Sep 17 00:00:00 2001 From: notify Date: Fri, 26 Jan 2024 16:01:41 +0800 Subject: [PATCH 09/30] Changelog: v0.4.5 --- CHANGELOG.md | 2 +- CMakeLists.txt | 2 +- Fk/LobbyElement/RoomGeneralSettings.qml | 4 +++- Fk/LobbyElement/RoomPackageSettings.qml | 6 +++--- Fk/Pages/GeneralsOverview.qml | 13 +++++++++---- Fk/Pages/Room.qml | 17 +++++++++-------- Fk/RoomElement/Dashboard.qml | 2 +- android/AndroidManifest.xml | 4 ++-- lua/client/i18n/zh_CN.lua | 2 +- 9 files changed, 30 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f19b2ced..ccfd4e26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # ChangeLog -## v0.4.4 +## v0.4.4 & 0.4.5 禁将增强;修复bug diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a7e4122..f62fe2b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16) -project(FreeKill VERSION 0.4.4) +project(FreeKill VERSION 0.4.5) add_definitions(-DFK_VERSION=\"${CMAKE_PROJECT_VERSION}\") find_package(Qt6 REQUIRED COMPONENTS diff --git a/Fk/LobbyElement/RoomGeneralSettings.qml b/Fk/LobbyElement/RoomGeneralSettings.qml index 1889a42a..0da41eda 100644 --- a/Fk/LobbyElement/RoomGeneralSettings.qml +++ b/Fk/LobbyElement/RoomGeneralSettings.qml @@ -109,6 +109,7 @@ Flickable { } } + /* Text { id: warning anchors.rightMargin: 8 @@ -121,6 +122,7 @@ Flickable { text: luatr("No enough generals") color: "red" } + */ RowLayout { anchors.rightMargin: 8 @@ -158,7 +160,7 @@ Flickable { spacing: 16 Button { text: luatr("OK") - enabled: !(warning.visible) + // enabled: !(warning.visible) onClicked: { config.saveConf(); root.finished(); diff --git a/Fk/LobbyElement/RoomPackageSettings.qml b/Fk/LobbyElement/RoomPackageSettings.qml index cbd2a405..3b03385b 100644 --- a/Fk/LobbyElement/RoomPackageSettings.qml +++ b/Fk/LobbyElement/RoomPackageSettings.qml @@ -132,10 +132,10 @@ Flickable { const s = config.curScheme; if (!checked) { s.banPkg[orig_name] = []; - s.normalPkg[orig_name] = undefined; + delete s.normalPkg[orig_name]; } else { - s.normalPkg[orig_name] = undefined; - s.banPkg[orig_name] = undefined; + delete s.normalPkg[orig_name]; + delete s.banPkg[orig_name]; } lcall("UpdatePackageEnable", orig_name, checked); config.curSchemeChanged(); diff --git a/Fk/Pages/GeneralsOverview.qml b/Fk/Pages/GeneralsOverview.qml index 840b515e..8a01f8a8 100644 --- a/Fk/Pages/GeneralsOverview.qml +++ b/Fk/Pages/GeneralsOverview.qml @@ -92,12 +92,13 @@ Item { const name = modelData; let s = config.curScheme; if (s.banPkg[name]) { - s.banPkg[name] = undefined; - s.normalPkg[name] = undefined; + delete s.banPkg[name]; + delete s.normalPkg[name]; } else { - s.normalPkg[name] = undefined; + delete s.normalPkg[name]; s.banPkg[name] = []; } + console.log(JSON.stringify(config.curScheme)) config.curSchemeChanged(); } else { pkgList.currentIndex = index; @@ -135,7 +136,7 @@ Item { } elide: Label.ElideLeft verticalAlignment: Qt.AlignVCenter - font.pixelSize: 24 + font.pixelSize: 28 } Item { Layout.fillWidth: true } @@ -149,6 +150,7 @@ Item { ToolButton { text: luatr("Search") + font.pixelSize: 20 enabled: word.text !== "" onClicked: { pkgList.currentIndex = 0; @@ -158,6 +160,7 @@ Item { ToolButton { id: banButton + font.pixelSize: 20 text: { if (stat === 2) return luatr("OK"); return luatr("BanGeneral"); @@ -174,6 +177,7 @@ Item { ToolButton { id: banPkgButton + font.pixelSize: 20 text: { if (stat === 1) return luatr("OK"); return luatr("BanPackage"); @@ -190,6 +194,7 @@ Item { ToolButton { text: luatr("Quit") + font.pixelSize: 20 onClicked: { mainStack.pop(); config.saveConf(); diff --git a/Fk/Pages/Room.qml b/Fk/Pages/Room.qml index 16264eb9..b35e462e 100644 --- a/Fk/Pages/Room.qml +++ b/Fk/Pages/Room.qml @@ -1163,14 +1163,15 @@ Item { const idx = parseInt(splited[1]); const gene = splited[2]; - try { - callbacks["LogEvent"](JSON.stringify({ - type: "PlaySkillSound", - name: skill, - general: gene, - i: idx, - })); - } catch (e) {} + if (!config.disableMsgAudio) + try { + callbacks["LogEvent"](JSON.stringify({ + type: "PlaySkillSound", + name: skill, + general: gene, + i: idx, + })); + } catch (e) {} const m = luatr("$" + skill + (gene ? "_" + gene : "") + (idx ? idx.toString() : "")); data.msg = m; diff --git a/Fk/RoomElement/Dashboard.qml b/Fk/RoomElement/Dashboard.qml index 8e601289..39454312 100644 --- a/Fk/RoomElement/Dashboard.qml +++ b/Fk/RoomElement/Dashboard.qml @@ -82,7 +82,7 @@ RowLayout { expanded_piles[pile] = []; let ids, footnote; if (pile === "_equip") { - ids = self.equipArea.getAllCards(); + ids = self.equipArea.getAllCards().map(e => e.cid); footnote = "$Equip"; } else if (pile === "_extra") { ids = extra_ids; diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index d2fc268e..c086fe0c 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -3,8 +3,8 @@ + android:versionCode="405" + android:versionName="0.4.5"> diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index df4fb1a7..0163c376 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -311,7 +311,7 @@ FreeKill使用的是libgit2的C API,与此同时使用Git完成拓展包的下 ["Resume"] = "继续", ["Bulletin Info"] = [==[ - ## v0.4.4 + ## v0.4.5 禁将体验优化,然而烦请重新禁将。请善用禁用拓展包与白名单功能,旧方案已经不可导入。 From 3b9dd89b10d74cde631c05495fd6f2e417e07d86 Mon Sep 17 00:00:00 2001 From: notify Date: Mon, 29 Jan 2024 10:19:10 +0800 Subject: [PATCH 10/30] Fix bugs (#311) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1、攻击范围状态技类新增基础值修正函数 2、伤害值在一个技能处理后小于1会终止当前事件 3、不向不能使用【无懈可击】的角色询问使用【无懈可击】 4、修正在濒死插结中有人死亡后仍然会向该角色求桃的情况 5、将PreCardUse和PreCardRespond时机移至实体牌移动之前 6、调整改判函数原判定牌置入弃牌堆的原因 6、修正【朱雀羽扇】、【借刀杀人】、【酒】 Co-authored-by: xxyheaven <1433191064@qq.com> --- lua/core/player.lua | 28 +++++++++---- lua/core/skill_type/attack_range.lua | 6 +++ lua/core/skill_type/distance.lua | 2 +- lua/fk_ex.lua | 6 ++- lua/server/events/death.lua | 6 ++- lua/server/events/usecard.lua | 16 ++++---- lua/server/gamelogic.lua | 4 +- lua/server/room.lua | 59 ++++++++++++++++------------ packages/maneuvering/init.lua | 17 +++++--- packages/standard/game_rule.lua | 2 +- packages/standard_cards/init.lua | 31 ++++++++++----- 11 files changed, 115 insertions(+), 62 deletions(-) diff --git a/lua/core/player.lua b/lua/core/player.lua index e54dcfef..8a9fe445 100644 --- a/lua/core/player.lua +++ b/lua/core/player.lua @@ -457,16 +457,28 @@ end --- 获取玩家攻击范围。 function Player:getAttackRange() - local weapon = Fk:getCardById(self:getEquipment(Card.SubtypeWeapon)) - local baseAttackRange = math.max(weapon and weapon.attack_range or 1, 0) - - local status_skills = Fk:currentRoom().status_skills[AttackRangeSkill] or Util.DummyTable - for _, skill in ipairs(status_skills) do - local correct = skill:getCorrect(self) - baseAttackRange = baseAttackRange + (correct or 0) + local baseValue = 1 + local weapons = self:getEquipments(Card.SubtypeWeapon) + if #weapons > 0 then + baseValue = 0 + for _, id in ipairs(weapons) do + local weapon = Fk:getCardById(id) + baseValue = math.max(baseValue, weapon.attack_range or 1) + 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 --- 获取角色是否被移除。 diff --git a/lua/core/skill_type/attack_range.lua b/lua/core/skill_type/attack_range.lua index 6a646532..aa48de04 100644 --- a/lua/core/skill_type/attack_range.lua +++ b/lua/core/skill_type/attack_range.lua @@ -9,6 +9,12 @@ function AttackRangeSkill:getCorrect(from) return 0 end +---@param from Player +---@return integer|nil +function AttackRangeSkill:getFixed(from) + return nil +end + function AttackRangeSkill:withinAttackRange(from, to) return false end diff --git a/lua/core/skill_type/distance.lua b/lua/core/skill_type/distance.lua index 681dfc01..b0d6db42 100644 --- a/lua/core/skill_type/distance.lua +++ b/lua/core/skill_type/distance.lua @@ -12,7 +12,7 @@ end ---@param from Player ---@param to Player ----@return integer +---@return integer|nil function DistanceSkill:getFixed(from, to) return nil end diff --git a/lua/fk_ex.lua b/lua/fk_ex.lua index 1cf0d062..f0a9eb0e 100644 --- a/lua/fk_ex.lua +++ b/lua/fk_ex.lua @@ -320,19 +320,23 @@ end ---@class AttackRangeSpec: StatusSkillSpec ---@field public correct_func? fun(self: AttackRangeSkill, from: Player, to: Player): number? +---@field public fixed_func? fun(self: AttackRangeSkill, player: Player): number? ---@field public within_func? fun(self: AttackRangeSkill, from: Player, to: Player): boolean? ---@param spec AttackRangeSpec ---@return AttackRangeSkill function fk.CreateAttackRangeSkill(spec) assert(type(spec.name) == "string") - assert(type(spec.correct_func) == "function" or type(spec.within_func) == "function") + assert(type(spec.correct_func) == "function" or type(spec.fixed_func) == "function" or type(spec.within_func) == "function") local skill = AttackRangeSkill:new(spec.name) readStatusSpecToSkill(skill, spec) if spec.correct_func then skill.getCorrect = spec.correct_func end + if spec.fixed_func then + skill.getFixed = spec.fixed_func + end if spec.within_func then skill.withinAttackRange = spec.within_func end diff --git a/lua/server/events/death.lua b/lua/server/events/death.lua index 67652760..884b775b 100644 --- a/lua/server/events/death.lua +++ b/lua/server/events/death.lua @@ -17,8 +17,10 @@ GameEvent.functions[GameEvent.Dying] = function(self) -- room.logic:trigger(fk.Dying, dyingPlayer, dyingStruct) local savers = room:getAlivePlayers() for _, p in ipairs(savers) do - if dyingPlayer.hp > 0 or dyingPlayer.dead or logic:trigger(fk.AskForPeaches, p, dyingStruct) then - break + if not p.dead then + if dyingPlayer.hp > 0 or dyingPlayer.dead or logic:trigger(fk.AskForPeaches, p, dyingStruct) then + break + end end end logic:trigger(fk.AskForPeachesDone, dyingPlayer, dyingStruct) diff --git a/lua/server/events/usecard.lua b/lua/server/events/usecard.lua index b540c3bf..e0300b92 100644 --- a/lua/server/events/usecard.lua +++ b/lua/server/events/usecard.lua @@ -170,6 +170,10 @@ GameEvent.functions[GameEvent.UseCard] = function(self) local _card = sendCardEmotionAndLog(room, cardUseEvent) + if logic:trigger(fk.PreCardUse, room:getPlayerById(cardUseEvent.from), cardUseEvent) then + logic:breakEvent() + end + room:moveCardTo(cardUseEvent.card, Card.Processing, nil, fk.ReasonUse) local card = cardUseEvent.card @@ -196,10 +200,6 @@ GameEvent.functions[GameEvent.UseCard] = function(self) end end - if logic:trigger(fk.PreCardUse, room:getPlayerById(cardUseEvent.from), cardUseEvent) then - logic:breakEvent() - end - if not cardUseEvent.extraUse then room:getPlayerById(cardUseEvent.from):addCardUseHistory(cardUseEvent.card.trueName, 1) end @@ -270,6 +270,10 @@ GameEvent.functions[GameEvent.RespondCard] = function(self) playCardEmotionAndSound(room, room:getPlayerById(from), card) + if logic:trigger(fk.PreCardRespond, room:getPlayerById(cardResponseEvent.from), cardResponseEvent) then + logic:breakEvent() + end + room:moveCardTo(card, Card.Processing, nil, fk.ReasonResonpse) if #cardIds > 0 then room:sendFootnote(cardIds, { @@ -281,10 +285,6 @@ GameEvent.functions[GameEvent.RespondCard] = function(self) end end - if logic:trigger(fk.PreCardRespond, room:getPlayerById(cardResponseEvent.from), cardResponseEvent) then - logic:breakEvent() - end - logic:trigger(fk.CardResponding, room:getPlayerById(cardResponseEvent.from), cardResponseEvent) end diff --git a/lua/server/gamelogic.lua b/lua/server/gamelogic.lua index ed174f29..e1c72130 100644 --- a/lua/server/gamelogic.lua +++ b/lua/server/gamelogic.lua @@ -395,7 +395,9 @@ function GameLogic:trigger(event, target, data, refresh_only) skill_names = table.map(table.filter(skills, filter_func), Util.NameMapper) broken = broken or (event == fk.AskForPeaches - and room:getPlayerById(data.who).hp > 0) or cur_event.killed + and room:getPlayerById(data.who).hp > 0) or + (table.contains({fk.PreDamage, fk.DamageCaused, fk.DamageInflicted}, event) and data.damage < 1) or + cur_event.killed if broken then break end end diff --git a/lua/server/room.lua b/lua/server/room.lua index 571859ec..f28fd8f7 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -2760,27 +2760,13 @@ function Room:handleCardEffect(event, cardEffectEvent) then local players = {} Fk.currentResponsePattern = "nullification" + local cardCloned = Fk:cloneCard("nullification") for _, p in ipairs(self.alive_players) do - local cards = p:getHandlyIds() - for _, cid in ipairs(cards) do - if - Fk:getCardById(cid).trueName == "nullification" and - not ( - table.contains(cardEffectEvent.disresponsiveList or Util.DummyTable, p.id) or - table.contains(cardEffectEvent.unoffsetableList or Util.DummyTable, p.id) - ) - then - table.insert(players, p) - break - end - end - if not table.contains(players, p) then - Self = p -- for enabledAtResponse - for _, s in ipairs(table.connect(p.player_skills, p._fake_skills)) do + if not p:prohibitUse(cardCloned) then + local cards = p:getHandlyIds() + for _, cid in ipairs(cards) do if - s.pattern and - Exppattern:Parse("nullification"):matchExp(s.pattern) and - not (s.enabledAtResponse and not s:enabledAtResponse(p)) and + Fk:getCardById(cid).trueName == "nullification" and not ( table.contains(cardEffectEvent.disresponsiveList or Util.DummyTable, p.id) or table.contains(cardEffectEvent.unoffsetableList or Util.DummyTable, p.id) @@ -2790,6 +2776,23 @@ function Room:handleCardEffect(event, cardEffectEvent) break end end + if not table.contains(players, p) then + Self = p -- for enabledAtResponse + for _, s in ipairs(table.connect(p.player_skills, p._fake_skills)) do + if + s.pattern and + Exppattern:Parse("nullification"):matchExp(s.pattern) and + not (s.enabledAtResponse and not s:enabledAtResponse(p)) and + not ( + table.contains(cardEffectEvent.disresponsiveList or Util.DummyTable, p.id) or + table.contains(cardEffectEvent.unoffsetableList or Util.DummyTable, p.id) + ) + then + table.insert(players, p) + break + end + end + end end end @@ -3176,12 +3179,6 @@ function Room:retrial(card, player, judge, skillName, exchange) local rebyre = judge.retrial_by_response judge.retrial_by_response = player - local move2 = {} ---@type CardsMoveInfo - move2.ids = { oldJudge:getEffectiveId() } - move2.toArea = exchange and Card.PlayerHand or Card.DiscardPile - move2.moveReason = fk.ReasonJustMove - move2.to = exchange and player.id or nil - self:sendLog{ type = "#ChangedJudge", from = player.id, @@ -3190,8 +3187,18 @@ function Room:retrial(card, player, judge, skillName, exchange) arg = skillName, } - self:moveCards(move2) Fk:filterCard(judge.card.id, judge.who, judge) + + exchange = exchange and not player.dead + + local move2 = {} ---@type CardsMoveInfo + move2.ids = { oldJudge:getEffectiveId() } + move2.toArea = exchange and Card.PlayerHand or Card.DiscardPile + move2.moveReason = exchange and fk.ReasonJustMove or fk.ReasonJudge + move2.to = exchange and player.id or nil + move2.skillName = skillName + + self:moveCards(move2) end --- 弃置一名角色的牌。 diff --git a/packages/maneuvering/init.lua b/packages/maneuvering/init.lua index cdcf2f20..0283fcdc 100644 --- a/packages/maneuvering/init.lua +++ b/packages/maneuvering/init.lua @@ -146,7 +146,7 @@ local analepticEffect = fk.CreateTriggerSkill{ name = "analeptic_effect", global = true, priority = 0, -- game rule - events = { fk.PreCardUse, fk.EventPhaseStart }, + events = { fk.PreCardUse, fk.AfterTurnEnd }, can_trigger = function(_, event, target, player, data) if target ~= player then return false @@ -155,7 +155,7 @@ local analepticEffect = fk.CreateTriggerSkill{ if event == fk.PreCardUse then return data.card.trueName == "slash" and player.drank > 0 else - return target.phase == Player.NotActive + return true end end, on_trigger = function(_, event, _, player, data) @@ -360,9 +360,16 @@ local fanSkill = fk.CreateTriggerSkill{ return target == player and player:hasSkill(self) and data.card.name == "slash" end, on_use = function(_, _, _, _, data) - local card = Fk:cloneCard("fire__slash") + local card = Fk:cloneCard("fire__slash", data.card.suit, data.card.number) + for k, v in pairs(data.card) do + if card[k] == nil then + card[k] = v + end + end + if not data.card:isVirtual() then + card.id = data.card.id + end card.skillName = "fan" - card:addSubcard(data.card) data.card = card end, } @@ -522,7 +529,7 @@ Fk:loadTranslationTable{ ["#guding_blade_skill"] = "古锭刀", ["fan"] = "朱雀羽扇", - [":fan"] = "装备牌·武器
攻击范围:4
武器技能:你可以将一张普通【杀】当火【杀】使用。", + [":fan"] = "装备牌·武器
攻击范围:4
武器技能:当你声明使用普【杀】后,你可以将此【杀】改为火【杀】。", ["#fan_skill"] = "朱雀羽扇", ["vine"] = "藤甲", diff --git a/packages/standard/game_rule.lua b/packages/standard/game_rule.lua index 3cc23b40..e893d2b4 100644 --- a/packages/standard/game_rule.lua +++ b/packages/standard/game_rule.lua @@ -39,7 +39,7 @@ GameRule = fk.CreateTriggerSkill{ switch(event, { [fk.AskForPeaches] = function() local dyingPlayer = room:getPlayerById(data.who) - while dyingPlayer.hp < 1 do + while not (player.dead or dyingPlayer.dead) and dyingPlayer.hp < 1 do local cardNames = {"peach"} local prompt = "#AskForPeaches:" .. dyingPlayer.id .. "::" .. tostring(1 - dyingPlayer.hp) if player == dyingPlayer then diff --git a/packages/standard_cards/init.lua b/packages/standard_cards/init.lua index abfacd02..8038ae1a 100644 --- a/packages/standard_cards/init.lua +++ b/packages/standard_cards/init.lua @@ -352,18 +352,22 @@ local collateralSkill = fk.CreateActiveSkill{ prompt = "#collateral_skill", mod_target_filter = function(self, to_select, selected, user, card, distance_limited) local player = Fk:currentRoom():getPlayerById(to_select) - return user ~= to_select and player:getEquipment(Card.SubtypeWeapon) + if #selected == 0 then + return user ~= to_select and player:getEquipment(Card.SubtypeWeapon) and not player:prohibitUse(Fk:cloneCard("slash")) + elseif #selected == 1 then + local target = Fk:currentRoom():getPlayerById(to_select) + local from = Fk:currentRoom():getPlayerById(selected[1]) + return from:inMyAttackRange(target) and not from:isProhibited(player, Fk:cloneCard("slash")) + end end, target_filter = function(self, to_select, selected, _, card) if #selected >= (self:getMaxTargetNum(Self, card) - 1) * 2 then return false--修改借刀的目标选择 elseif #selected % 2 == 0 then - return self:modTargetFilter(to_select, selected, Self.id, card) + return self:modTargetFilter(to_select, {}, Self.id, card) else - local player = Fk:currentRoom():getPlayerById(to_select) - local from = Fk:currentRoom():getPlayerById(selected[#selected]) - return self:modTargetFilter(selected[#selected], selected, Self.id, card) - and from:inMyAttackRange(player) and not from:isProhibited(player, Fk:cloneCard("slash")) + return self:modTargetFilter(selected[#selected], {}, Self.id, card) + and self:modTargetFilter(to_select, {selected[#selected]}, Self.id, card) end end, target_num = 2, @@ -382,17 +386,26 @@ local collateralSkill = fk.CreateActiveSkill{ end, on_effect = function(self, room, effect) local to = room:getPlayerById(effect.to) - if to.dead or not to:getEquipment(Card.SubtypeWeapon) then return end + if to.dead then return end local prompt = "#collateral-slash:"..effect.from..":"..effect.subTargets[1] if #effect.subTargets > 1 then prompt = nil end - local use = room:askForUseCard(to, "slash", nil, prompt, nil, { must_targets = effect.subTargets }, effect) + local extra_data = { + must_targets = effect.subTargets, + bypass_times = true, + } + local use = room:askForUseCard(to, "slash", nil, prompt, nil, extra_data, effect) if use then use.extraUse = true room:useCard(use) else - room:moveCardTo(to:getEquipment(Card.SubtypeWeapon), Card.PlayerHand, room:getPlayerById(effect.from), fk.ReasonGive, "collateral", nil, true, to.id) + local from = room:getPlayerById(effect.from) + if from.dead then return end + local weapons = to:getEquipments(Card.SubtypeWeapon) + if #weapons > 0 then + room:moveCardTo(weapons, Card.PlayerHand, from, fk.ReasonGive, "collateral", nil, true, to.id) + end end end } From 58e76c1b9c042d62b1bcd07cb3f39aa024cdc39e Mon Sep 17 00:00:00 2001 From: Ho-spair <62695577+Ho-spair@users.noreply.github.com> Date: Sun, 4 Feb 2024 15:29:39 +0800 Subject: [PATCH 11/30] Modify Use-Event (#314) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 为使用流程和Aim流程增加属性additionalEffect,用于指定额外结算次数(OL版),顺带移动【五谷丰登】开启和关闭AG的位置; - 为视为技新增after_use方法处理转化牌后的后续操作; - 修复伤害流程时机触发者不变问题; - 修复旁观休整的问题; - 修复可移动场上牌判断函数未判断虚拟牌名的问题。 --- Fk/Pages/Room.qml | 6 +- lua/core/player.lua | 2 +- lua/core/skill_type/active.lua | 5 ++ lua/core/skill_type/view_as.lua | 4 ++ lua/fk_ex.lua | 6 ++ lua/server/events/hp.lua | 12 ++-- lua/server/events/usecard.lua | 7 +++ lua/server/gamelogic.lua | 2 +- lua/server/room.lua | 104 ++++++++++++++++++++----------- lua/server/serverplayer.lua | 2 +- lua/server/system_enum.lua | 2 + packages/standard_cards/init.lua | 37 +++++++++++ 12 files changed, 142 insertions(+), 47 deletions(-) diff --git a/Fk/Pages/Room.qml b/Fk/Pages/Room.qml index b35e462e..caf6b187 100644 --- a/Fk/Pages/Room.qml +++ b/Fk/Pages/Room.qml @@ -121,9 +121,9 @@ Item { enabled: !config.observing && !config.replaying text: luatr("Surrender") onClicked: { - if (isStarted && !getPhoto(Self.id).dead) { - const surrenderCheck = - lcall('CheckSurrenderAvailable', miscStatus.playedTime); + const photo = getPhoto(Self.id); + if (isStarted && !(photo.dead && photo.rest <= 0)) { + const surrenderCheck = lcall('CheckSurrenderAvailable', miscStatus.playedTime); if (!surrenderCheck.length) { surrenderDialog.informativeText = luatr('Surrender is disabled in this mode'); diff --git a/lua/core/player.lua b/lua/core/player.lua index 8a9fe445..cf608f55 100644 --- a/lua/core/player.lua +++ b/lua/core/player.lua @@ -1006,7 +1006,7 @@ function Player:canMoveCardInBoardTo(to, id) return not ( 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 table.contains(to.sealedSlots, Player.JudgeSlot) ) diff --git a/lua/core/skill_type/active.lua b/lua/core/skill_type/active.lua index 874299ec..20760a04 100644 --- a/lua/core/skill_type/active.lua +++ b/lua/core/skill_type/active.lua @@ -190,6 +190,11 @@ end ---@param cardUseEvent CardUseStruct function ActiveSkill:onUse(room, cardUseEvent) end +---@param room Room +---@param cardUseEvent CardUseStruct +---@param isEnding? bool +function ActiveSkill:onAction(room, cardUseEvent, finished) end + ---@param room Room ---@param cardEffectEvent CardEffectEvent | SkillEffectEvent function ActiveSkill:aboutToEffect(room, cardEffectEvent) end diff --git a/lua/core/skill_type/view_as.lua b/lua/core/skill_type/view_as.lua index b8e695e9..b3798753 100644 --- a/lua/core/skill_type/view_as.lua +++ b/lua/core/skill_type/view_as.lua @@ -39,6 +39,10 @@ end ---@param cardUseStruct CardUseStruct function ViewAsSkill:beforeUse(player, cardUseStruct) end +---@param player Player +---@param cardUseStruct CardUseStruct +function ViewAsSkill:afterUse(player, cardUseStruct) end + ---@param selected integer[] @ ids of selected players ---@param selected_cards integer[] @ ids of selected cards function ViewAsSkill:prompt(selected, selected_cards) return "" end diff --git a/lua/fk_ex.lua b/lua/fk_ex.lua index f0a9eb0e..aaca1ea8 100644 --- a/lua/fk_ex.lua +++ b/lua/fk_ex.lua @@ -175,6 +175,7 @@ end ---@field public target_filter? fun(self: ActiveSkill, to_select: integer, selected: integer[], selected_cards: integer[], card: Card, extra_data: any): boolean? ---@field public feasible? fun(self: ActiveSkill, selected: integer[], selected_cards: integer[]): boolean? ---@field public on_use? fun(self: ActiveSkill, room: Room, cardUseEvent: CardUseStruct): boolean? +---@field public on_action? fun(self: ActiveSkill, room: Room, cardUseEvent: CardUseStruct, finished: boolean): boolean? ---@field public about_to_effect? fun(self: ActiveSkill, room: Room, cardEffectEvent: CardEffectEvent): boolean? ---@field public on_effect? fun(self: ActiveSkill, room: Room, cardEffectEvent: CardEffectEvent): boolean? ---@field public on_nullified? fun(self: ActiveSkill, room: Room, cardEffectEvent: CardEffectEvent): boolean? @@ -202,6 +203,7 @@ function fk.CreateActiveSkill(spec) skill.feasible = spec.feasible end if spec.on_use then skill.onUse = spec.on_use end + if spec.on_action then skill.onAction = spec.on_action end if spec.about_to_effect then skill.aboutToEffect = spec.about_to_effect end if spec.on_effect then skill.onEffect = spec.on_effect end if spec.on_nullified then skill.onNullified = spec.on_nullified end @@ -274,6 +276,10 @@ function fk.CreateViewAsSkill(spec) skill.beforeUse = spec.before_use end + if spec.after_use and type(spec.after_use) == "function" then + skill.afterUse = spec.after_use + end + return skill end diff --git a/lua/server/events/hp.lua b/lua/server/events/hp.lua index 44d4d34a..06ef099f 100644 --- a/lua/server/events/hp.lua +++ b/lua/server/events/hp.lua @@ -136,16 +136,16 @@ GameEvent.functions[GameEvent.Damage] = function(self) assert(damageStruct.to:isInstanceOf(ServerPlayer)) local stages = { - {fk.PreDamage, damageStruct.from}, + {fk.PreDamage, "from"}, } if not damageStruct.isVirtualDMG then - table.insertTable(stages, { { fk.DamageCaused, damageStruct.from }, { fk.DamageInflicted, damageStruct.to } }) + table.insertTable(stages, { { fk.DamageCaused, "from" }, { fk.DamageInflicted, "to" } }) end for _, struct in ipairs(stages) do local event, player = table.unpack(struct) - if logic:trigger(event, player, damageStruct) or damageStruct.damage < 1 then + if logic:trigger(event, damageStruct[player], damageStruct) or damageStruct.damage < 1 then logic:breakEvent(false) end @@ -184,13 +184,13 @@ GameEvent.functions[GameEvent.Damage] = function(self) stages = { - {fk.Damage, damageStruct.from}, - {fk.Damaged, damageStruct.to}, + {fk.Damage, "from"}, + {fk.Damaged, "to"}, } for _, struct in ipairs(stages) do local event, player = table.unpack(struct) - logic:trigger(event, player, damageStruct) + logic:trigger(event, damageStruct[player], damageStruct) end return true diff --git a/lua/server/events/usecard.lua b/lua/server/events/usecard.lua index e0300b92..d73f93d0 100644 --- a/lua/server/events/usecard.lua +++ b/lua/server/events/usecard.lua @@ -335,9 +335,16 @@ GameEvent.functions[GameEvent.CardEffect] = function(self) if event == fk.PreCardEffect then if cardEffectEvent.from and logic:trigger(event, room:getPlayerById(cardEffectEvent.from), cardEffectEvent) then + if cardEffectEvent.to then + cardEffectEvent.nullifiedTargets = cardEffectEvent.nullifiedTargets or {} + table.insert(cardEffectEvent.nullifiedTargets, cardEffectEvent.to) + end logic:breakEvent() end elseif cardEffectEvent.to and logic:trigger(event, room:getPlayerById(cardEffectEvent.to), cardEffectEvent) then + cardEffectEvent.nullifiedTargets = cardEffectEvent.nullifiedTargets or {} + table.insert(cardEffectEvent.nullifiedTargets, cardEffectEvent.to) + logic:breakEvent() end diff --git a/lua/server/gamelogic.lua b/lua/server/gamelogic.lua index e1c72130..db8470a1 100644 --- a/lua/server/gamelogic.lua +++ b/lua/server/gamelogic.lua @@ -26,7 +26,7 @@ function GameLogic:initialize(room) self.event_recorder = {} self.current_event_id = 0 self.specific_events_id = { - [GameEvent.Damage] = 0, + [GameEvent.Damage] = 1, } self.role_table = { diff --git a/lua/server/room.lua b/lua/server/room.lua index f28fd8f7..98aceb58 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -1927,6 +1927,7 @@ function Room:handleUseCardReply(player, data) use.card = c self:useSkill(player, skill, Util.DummyFunc) + use.attachedSkillAndUser = { skillName = skill.name, user = player.id } local rejectSkillName = skill:beforeUse(player, use) if type(rejectSkillName) == "string" then @@ -2402,7 +2403,23 @@ end ---@param cardUseEvent CardUseStruct @ 使用数据 ---@return boolean function Room:useCard(cardUseEvent) - return execGameEvent(GameEvent.UseCard, cardUseEvent) + local attachedSkillAndUser + if type(cardUseEvent.attachedSkillAndUser) == "table" then + attachedSkillAndUser = table.simpleClone(cardUseEvent.attachedSkillAndUser) + cardUseEvent.attachedSkillAndUser = nil + end + + local ret = execGameEvent(GameEvent.UseCard, cardUseEvent) + + if + type(attachedSkillAndUser) == "table" and + Fk.skills[attachedSkillAndUser.skillName] and + Fk.skills[attachedSkillAndUser.skillName].afterUse + then + Fk.skills[attachedSkillAndUser.skillName]:afterUse(self:getPlayerById(attachedSkillAndUser.user), cardUseEvent) + end + + return ret end ---@param room Room @@ -2439,6 +2456,7 @@ local onAim = function(room, cardUseEvent, aimEventCollaborators) firstTarget = firstTarget, additionalDamage = cardUseEvent.additionalDamage, additionalRecover = cardUseEvent.additionalRecover, + additionalEffect = cardUseEvent.additionalEffect, extra_data = cardUseEvent.extra_data, } @@ -2466,6 +2484,7 @@ local onAim = function(room, cardUseEvent, aimEventCollaborators) aimStruct.targetGroup = cardUseEvent.tos aimStruct.nullifiedTargets = cardUseEvent.nullifiedTargets or {} aimStruct.firstTarget = firstTarget + aimStruct.additionalEffect = cardUseEvent.additionalEffect aimStruct.extra_data = cardUseEvent.extra_data end @@ -2483,6 +2502,7 @@ local onAim = function(room, cardUseEvent, aimEventCollaborators) cardUseEvent.from = aimStruct.from cardUseEvent.tos = aimEventTargetGroup cardUseEvent.nullifiedTargets = aimStruct.nullifiedTargets + cardUseEvent.additionalEffect = aimStruct.additionalEffect cardUseEvent.extra_data = aimStruct.extra_data if #AimGroup:getAllTargets(aimStruct.tos) == 0 then @@ -2643,57 +2663,71 @@ function Room:doCardUseEffect(cardUseEvent) return end - -- Else: do effect to all targets - local collaboratorsIndex = {} - for _, toId in ipairs(TargetGroup:getRealTargets(cardUseEvent.tos)) do - if not table.contains(cardUseEvent.nullifiedTargets, toId) and self:getPlayerById(toId):isAlive() then - if aimEventCollaborators[toId] then - cardEffectEvent.to = toId - collaboratorsIndex[toId] = collaboratorsIndex[toId] or 1 - local curAimEvent = aimEventCollaborators[toId][collaboratorsIndex[toId]] + for i = 1, (cardUseEvent.additionalEffect or 0) + 1 do + if #TargetGroup:getRealTargets(cardUseEvent.tos) > 0 and cardUseEvent.card.skill.onAction then + cardUseEvent.card.skill:onAction(self, cardUseEvent) + end - cardEffectEvent.subTargets = curAimEvent.subTargets - cardEffectEvent.additionalDamage = curAimEvent.additionalDamage - cardEffectEvent.additionalRecover = curAimEvent.additionalRecover + -- Else: do effect to all targets + local collaboratorsIndex = {} + for _, toId in ipairs(TargetGroup:getRealTargets(cardUseEvent.tos)) do + if not table.contains(cardUseEvent.nullifiedTargets, toId) and self:getPlayerById(toId):isAlive() then + if aimEventCollaborators[toId] then + cardEffectEvent.to = toId + collaboratorsIndex[toId] = collaboratorsIndex[toId] or 1 + local curAimEvent = aimEventCollaborators[toId][collaboratorsIndex[toId]] - if curAimEvent.disresponsiveList then - cardEffectEvent.disresponsiveList = cardEffectEvent.disresponsiveList or {} + cardEffectEvent.subTargets = curAimEvent.subTargets + cardEffectEvent.additionalDamage = curAimEvent.additionalDamage + cardEffectEvent.additionalRecover = curAimEvent.additionalRecover - for _, disresponsivePlayer in ipairs(curAimEvent.disresponsiveList) do - if not table.contains(cardEffectEvent.disresponsiveList, disresponsivePlayer) then - table.insert(cardEffectEvent.disresponsiveList, disresponsivePlayer) + if curAimEvent.disresponsiveList then + cardEffectEvent.disresponsiveList = cardEffectEvent.disresponsiveList or {} + + for _, disresponsivePlayer in ipairs(curAimEvent.disresponsiveList) do + if not table.contains(cardEffectEvent.disresponsiveList, disresponsivePlayer) then + table.insert(cardEffectEvent.disresponsiveList, disresponsivePlayer) + end end end - end - if curAimEvent.unoffsetableList then - cardEffectEvent.unoffsetableList = cardEffectEvent.unoffsetableList or {} + if curAimEvent.unoffsetableList then + cardEffectEvent.unoffsetableList = cardEffectEvent.unoffsetableList or {} - for _, unoffsetablePlayer in ipairs(curAimEvent.unoffsetableList) do - if not table.contains(cardEffectEvent.unoffsetableList, unoffsetablePlayer) then - table.insert(cardEffectEvent.unoffsetableList, unoffsetablePlayer) + for _, unoffsetablePlayer in ipairs(curAimEvent.unoffsetableList) do + if not table.contains(cardEffectEvent.unoffsetableList, unoffsetablePlayer) then + table.insert(cardEffectEvent.unoffsetableList, unoffsetablePlayer) + end end end - end - cardEffectEvent.disresponsive = curAimEvent.disresponsive - cardEffectEvent.unoffsetable = curAimEvent.unoffsetable - cardEffectEvent.fixedResponseTimes = curAimEvent.fixedResponseTimes - cardEffectEvent.fixedAddTimesResponsors = curAimEvent.fixedAddTimesResponsors + cardEffectEvent.disresponsive = curAimEvent.disresponsive + cardEffectEvent.unoffsetable = curAimEvent.unoffsetable + cardEffectEvent.fixedResponseTimes = curAimEvent.fixedResponseTimes + cardEffectEvent.fixedAddTimesResponsors = curAimEvent.fixedAddTimesResponsors - collaboratorsIndex[toId] = collaboratorsIndex[toId] + 1 + collaboratorsIndex[toId] = collaboratorsIndex[toId] + 1 - local curCardEffectEvent = table.simpleClone(cardEffectEvent) - self:doCardEffect(curCardEffectEvent) + local curCardEffectEvent = table.simpleClone(cardEffectEvent) + self:doCardEffect(curCardEffectEvent) - if curCardEffectEvent.cardsResponded then - cardUseEvent.cardsResponded = cardUseEvent.cardsResponded or {} - for _, card in ipairs(curCardEffectEvent.cardsResponded) do - table.insertIfNeed(cardUseEvent.cardsResponded, card) + if curCardEffectEvent.cardsResponded then + cardUseEvent.cardsResponded = cardUseEvent.cardsResponded or {} + for _, card in ipairs(curCardEffectEvent.cardsResponded) do + table.insertIfNeed(cardUseEvent.cardsResponded, card) + end + end + + if type(curCardEffectEvent.nullifiedTargets) == 'table' then + table.insertTableIfNeed(cardUseEvent.nullifiedTargets, curCardEffectEvent.nullifiedTargets) end end end end + + if #TargetGroup:getRealTargets(cardUseEvent.tos) > 0 and cardUseEvent.card.skill.onAction then + cardUseEvent.card.skill:onAction(self, cardUseEvent, true) + end end end diff --git a/lua/server/serverplayer.lua b/lua/server/serverplayer.lua index 754c5ced..fb6e76ce 100644 --- a/lua/server/serverplayer.lua +++ b/lua/server/serverplayer.lua @@ -215,7 +215,7 @@ function ServerPlayer:marshal(player, observe) if self.dead then room:notifyProperty(player, self, "dead") - room:notifyProperty(player, self, "role") + room:notifyProperty(player, self, self.rest > 0 and "rest" or "role") else room:notifyProperty(player, self, "seat") room:notifyProperty(player, self, "phase") diff --git a/lua/server/system_enum.lua b/lua/server/system_enum.lua index 9f0b0b88..5c8dd9bd 100644 --- a/lua/server/system_enum.lua +++ b/lua/server/system_enum.lua @@ -110,6 +110,7 @@ fk.IceDamage = 4 ---@field public cardsResponded? Card[] ---@field public prohibitedCardNames? string[] ---@field public damageDealt? table +---@field public additionalEffect? integer ---@class AimStruct ---@field public from integer @@ -126,6 +127,7 @@ fk.IceDamage = 4 ---@field public unoffsetableList? boolean ---@field public additionalResponseTimes? table|integer ---@field public fixedAddTimesResponsors? integer[] +---@field public additionalEffect? integer ---@class CardEffectEvent ---@field public from integer diff --git a/packages/standard_cards/init.lua b/packages/standard_cards/init.lua index 8038ae1a..136dd20a 100644 --- a/packages/standard_cards/init.lua +++ b/packages/standard_cards/init.lua @@ -608,6 +608,43 @@ local amazingGraceSkill = fk.CreateActiveSkill{ can_use = Util.GlobalCanUse, on_use = Util.GlobalOnUse, mod_target_filter = Util.TrueFunc, + on_action = function(self, room, use, finished) + if not finished then + local toDisplay = room:getNCards(#TargetGroup:getRealTargets(use.tos)) + room:moveCards({ + ids = toDisplay, + toArea = Card.Processing, + moveReason = fk.ReasonPut, + }) + + table.forEach(room.players, function(p) + room:fillAG(p, toDisplay) + end) + + use.extra_data = use.extra_data or {} + use.extra_data.AGFilled = toDisplay + else + if use.extra_data and use.extra_data.AGFilled then + table.forEach(room.players, function(p) + room:closeAG(p) + end) + + local toDiscard = table.filter(use.extra_data.AGFilled, function(id) + return room:getCardArea(id) == Card.Processing + end) + + if #toDiscard > 0 then + room:moveCards({ + ids = toDiscard, + toArea = Card.DiscardPile, + moveReason = fk.ReasonPutIntoDiscardPile, + }) + end + end + + use.extra_data.AGFilled = nil + end + end, on_effect = function(self, room, effect) local to = room:getPlayerById(effect.to) if not (effect.extra_data and effect.extra_data.AGFilled) then From f72aaa23cf6b6e81080ff2054a92a088c855a35a Mon Sep 17 00:00:00 2001 From: Nyutanislavsky Date: Sun, 4 Feb 2024 15:30:27 +0800 Subject: [PATCH 12/30] Bugfix (#313) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 修复传入数组的extraPile无法收回 2. 被弃置牌的log添加操作者 3. beforeMaxHpChanged的num可以被修改 4. 额外回合增加skillName 5. 修复亮将技能和禁止亮将 6. 水一些注释和格式 --------- Signed-off-by: Mechanel --- Fk/RoomElement/Dashboard.qml | 10 +++++++- lua/client/client.lua | 30 +++++++++++++++++------- lua/client/i18n/en_US.lua | 1 + lua/client/i18n/zh_CN.lua | 3 ++- lua/core/general.lua | 3 ++- lua/core/skill_type/active.lua | 2 +- lua/server/events/hp.lua | 17 ++++++++++---- lua/server/room.lua | 29 ++++++++++++++++------- lua/server/serverplayer.lua | 25 +++++++------------- lua/server/system_enum.lua | 4 ++++ packages/standard/aux_skills.lua | 40 ++++++++++++++++++++++++-------- packages/standard_cards/init.lua | 2 +- packages/test/init.lua | 33 ++++++++------------------ 13 files changed, 123 insertions(+), 76 deletions(-) diff --git a/Fk/RoomElement/Dashboard.qml b/Fk/RoomElement/Dashboard.qml index 39454312..e91a05e0 100644 --- a/Fk/RoomElement/Dashboard.qml +++ b/Fk/RoomElement/Dashboard.qml @@ -19,6 +19,7 @@ RowLayout { property alias skillButtons: skillPanel.skill_buttons property var expanded_piles: ({}) // name -> int[] + property var extra_cards: [] property var disabledSkillNames: [] @@ -86,6 +87,7 @@ RowLayout { footnote = "$Equip"; } else if (pile === "_extra") { ids = extra_ids; + extra_cards = ids; footnote = extra_footnote; } else { ids = lcall("GetPile", self.playerid, pile); @@ -122,7 +124,13 @@ RowLayout { }) handcardAreaItem.updateCardPosition(); } else { - const ids = lcall("GetPile", self.playerid, pile); + let ids = []; + if (pile === "_extra") { + ids = extra_cards; + extra_cards = []; + } else { + ids = lcall("GetPile", self.playerid, pile); + } ids.forEach(id => { const card = handcardAreaItem.remove([id])[0]; card.origX = parentPos.x; diff --git a/lua/client/client.lua b/lua/client/client.lua index b057794e..95978400 100644 --- a/lua/client/client.lua +++ b/lua/client/client.lua @@ -469,6 +469,7 @@ local function separateMoves(moves) moveReason = move.moveReason, specialName = move.specialName, fromSpecialName = info.fromSpecialName, + proposer = move.proposer, }) end end @@ -480,9 +481,9 @@ local function mergeMoves(moves) local ret = {} local temp = {} 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.specialName, move.fromSpecialName) + move.specialName, move.fromSpecialName, move.proposer) if temp[info] == nil then temp[info] = { ids = {}, @@ -493,6 +494,7 @@ local function mergeMoves(moves) moveReason = move.moveReason, specialName = move.specialName, fromSpecialName = move.fromSpecialName, + proposer = move.proposer, } end table.insert(temp[info].ids, move.ids[1]) @@ -504,7 +506,7 @@ local function mergeMoves(moves) end local function sendMoveCardLog(move) - local client = ClientInstance + local client = ClientInstance ---@class Client if #move.ids == 0 then return end local hidden = table.contains(move.ids, -1) local msgtype @@ -607,12 +609,22 @@ local function sendMoveCardLog(move) }) elseif move.toArea == Card.DiscardPile then if move.moveReason == fk.ReasonDiscard then - client:appendLog{ - type = "$DiscardCards", - from = move.from, - card = move.ids, - arg = #move.ids, - } + if move.proposer and move.proposer ~= move.from then + client:appendLog{ + type = "$DiscardOther", + from = move.from, + to = {move.proposer}, + card = move.ids, + arg = #move.ids, + } + else + client:appendLog{ + type = "$DiscardCards", + from = move.from, + card = move.ids, + arg = #move.ids, + } + end elseif move.moveReason == fk.ReasonPutIntoDiscardPile then client:appendLog{ type = "$PutToDiscard", diff --git a/lua/client/i18n/en_US.lua b/lua/client/i18n/en_US.lua index 29156e18..f0b12a8f 100644 --- a/lua/client/i18n/en_US.lua +++ b/lua/client/i18n/en_US.lua @@ -344,6 +344,7 @@ Fk:loadTranslationTable({ ["$LightningMove"] = "%card transfered from %from to %to", ["$PasteCard"] = "%from used %card to %to", ["$DiscardCards"] = "%from discarded %arg card(s) %card", + ["$DiscardOther"] = "%to discarded %arg card(s) %card from %from", ["$InstallEquip"] = "%from equipped %card", ["$UninstallEquip"] = "%from uninstalled %card", diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index 0163c376..d946ce20 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -330,7 +330,7 @@ Fk:loadTranslationTable{ ["fire_damage"] = "火属性", ["thunder_damage"] = "雷属性", ["ice_damage"] = "冰属性", - ["hp_lost"] = "体力流失", + ["hp_lost"] = "失去体力", ["lose_hp"] = "失去体力", ["phase_start"] = "准备阶段", @@ -412,6 +412,7 @@ Fk:loadTranslationTable{ ["$PutCard"] = "%from 的 %arg 张牌被置于牌堆", ["$PutKnownCard"] = "%from 的牌 %card 被置于牌堆", ["$DiscardCards"] = "%from 弃置了 %arg 张牌 %card", + ["$DiscardOther"] = "%to 弃置了 %from 的 %arg 张牌 %card", ["$PutToDiscard"] = "%arg 张牌 %card 被置入弃牌堆", ["#ShowCard"] = "%from 展示了牌 %card", diff --git a/lua/core/general.lua b/lua/core/general.lua index 33e220b4..24f4beee 100644 --- a/lua/core/general.lua +++ b/lua/core/general.lua @@ -94,7 +94,8 @@ function General:addRelatedSkill(skill) end --- 获取武将所有技能。 ----@param include_lord bool +---@param include_lord? boolean +---@return string[] function General:getSkillNameList(include_lord) local ret = {} local other_skills = table.map(self.other_skills, Util.Name2SkillMapper) diff --git a/lua/core/skill_type/active.lua b/lua/core/skill_type/active.lua index 20760a04..4e448c03 100644 --- a/lua/core/skill_type/active.lua +++ b/lua/core/skill_type/active.lua @@ -45,8 +45,8 @@ end ---@param to_select integer @ id of the target ---@param selected integer[] @ ids of selected targets ---@param selected_cards integer[] @ ids of selected cards ----@param extra_data any @ extra_data ---@param card Card @ helper +---@param extra_data? any @ extra_data function ActiveSkill:targetFilter(to_select, selected, selected_cards, card, extra_data) return false end diff --git a/lua/server/events/hp.lua b/lua/server/events/hp.lua index 06ef099f..e62f786a 100644 --- a/lua/server/events/hp.lua +++ b/lua/server/events/hp.lua @@ -74,14 +74,14 @@ GameEvent.functions[GameEvent.ChangeHp] = function(self) room:sendLog{ type = "#LoseHP", from = player.id, - arg = 0 - num, + arg = 0 - data.num, } room:sendLogEvent("LoseHP", {}) elseif reason == "recover" then room:sendLog{ type = "#HealHP", from = player.id, - arg = num, + arg = data.num, } end @@ -290,12 +290,19 @@ end GameEvent.functions[GameEvent.ChangeMaxHp] = function(self) local player, num = table.unpack(self.data) local room = self.room - if room.logic:trigger(fk.BeforeMaxHpChanged, player, { num = num }) or num == 0 then + + ---@type MaxHpChangedData + local data = { + num = num, + } + + if room.logic:trigger(fk.BeforeMaxHpChanged, player, data) or data.num == 0 then return false end - player.maxHp = math.max(player.maxHp + num, 0) - room:broadcastProperty(player, "maxHp") + num = data.num + + room:setPlayerProperty(player, "maxHp", math.max(player.maxHp + num, 0)) room:sendLogEvent("ChangeMaxHp", { player = player.id, num = num, diff --git a/lua/server/room.lua b/lua/server/room.lua index 98aceb58..47467285 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -872,11 +872,11 @@ function Room:notifyMoveCards(players, card_moves, forceVisible) if not (move.moveVisible or forceVisible or containArea(move.toArea, move.to and p.isBuddy and p:isBuddy(move.to))) then for _, info in ipairs(move.moveInfo) do if not containArea(info.fromArea, move.from == p.id) then - info.cardId = -1 + info.cardId = -1 + end end end end - end p:doNotify("MoveCards", json.encode(arg)) end end @@ -1195,7 +1195,7 @@ function Room:askForDiscard(player, minNum, maxNum, includeEquip, skillName, can toDiscard = ret.cards else if cancelable then return {} end - toDiscard = table.random(canDiscards, minNum) + toDiscard = table.random(canDiscards, minNum) ---@type integer[] end if not skipDiscard then @@ -1272,7 +1272,7 @@ function Room:askForCard(player, minNum, maxNum, includeEquip, skillName, cancel pattern = pattern, expand_pile = expand_pile, } - local prompt = prompt or ("#AskForCard:::" .. maxNum .. ":" .. minNum) + prompt = prompt or ("#AskForCard:::" .. maxNum .. ":" .. minNum) local _, ret = self:askForUseActiveSkill(player, "choose_cards_skill", prompt, cancelable, data, no_indicate) if ret then chosenCards = ret.cards @@ -2633,7 +2633,7 @@ function Room:doCardUseEffect(cardUseEvent) return end - ---@type CardEffectEvent + ---@class CardEffectEvent local cardEffectEvent = { from = cardUseEvent.from, tos = cardUseEvent.tos, @@ -2916,7 +2916,8 @@ end ---@param cid integer|Card @ 要拿到的卡牌 ---@param unhide? boolean @ 是否明着拿 ---@param reason? CardMoveReason @ 卡牌移动的原因 -function Room:obtainCard(player, cid, unhide, reason) +---@param proposer? integer @ 移动操作者的id +function Room:obtainCard(player, cid, unhide, reason, proposer) if type(cid) ~= "number" then assert(cid and cid:isInstanceOf(Card)) cid = cid:isVirtual() and cid.subcards or {cid.id} @@ -2935,7 +2936,7 @@ function Room:obtainCard(player, cid, unhide, reason) to = player, toArea = Card.PlayerHand, moveReason = reason or fk.ReasonJustMove, - proposer = player, + proposer = proposer or player, moveVisible = unhide or false, }) end @@ -2987,7 +2988,6 @@ function Room:moveCardTo(card, to_place, target, reason, skill_name, special_nam reason = reason or fk.ReasonJustMove skill_name = skill_name or "" special_name = special_name or "" - proposer = proposer or nil local ids = Card:getIdList(card) local to @@ -3587,6 +3587,10 @@ function Room:printCard(name, suit, number) return cd end +--- 刷新使命技状态 +---@param player ServerPlayer +---@param skillName Suit +---@param failed? boolean function Room:updateQuestSkillState(player, skillName, failed) assert(Fk.skills[skillName].frequency == Skill.Quest) @@ -3600,6 +3604,9 @@ function Room:updateQuestSkillState(player, skillName, failed) }) end +--- 废除区域 +---@param player ServerPlayer +---@param playerSlots string | string[] function Room:abortPlayerArea(player, playerSlots) assert(type(playerSlots) == "string" or type(playerSlots) == "table") @@ -3652,6 +3659,9 @@ function Room:abortPlayerArea(player, playerSlots) self.logic:trigger(fk.AreaAborted, player, { slots = slotsSealed }) end +--- 恢复区域 +---@param player ServerPlayer +---@param playerSlots string | string[] function Room:resumePlayerArea(player, playerSlots) assert(type(playerSlots) == "string" or type(playerSlots) == "table") @@ -3675,6 +3685,9 @@ function Room:resumePlayerArea(player, playerSlots) end end +--- 设置休整 +---@param player ServerPlayer +---@param roundNum integer function Room:setPlayerRest(player, roundNum) player.rest = roundNum self:broadcastProperty(player, "rest") diff --git a/lua/server/serverplayer.lua b/lua/server/serverplayer.lua index fb6e76ce..e76e88fe 100644 --- a/lua/server/serverplayer.lua +++ b/lua/server/serverplayer.lua @@ -630,15 +630,18 @@ function ServerPlayer:endPlayPhase() -- TODO: send log end +--- 获得一个额外回合 ---@param delay? boolean -function ServerPlayer:gainAnExtraTurn(delay) +---@param skillName? string +function ServerPlayer:gainAnExtraTurn(delay, skillName) local room = self.room delay = (delay == nil) and true or delay + skillName = (skillName == nil) and room.logic:getCurrentSkillName() or skillName if delay then local logic = room.logic local turn = logic:getCurrentEvent():findParent(GameEvent.Turn, true) if turn then - turn:prependExitFunc(function() self:gainAnExtraTurn(false) end) + turn:prependExitFunc(function() self:gainAnExtraTurn(false, skillName) end) return end end @@ -653,7 +656,6 @@ function ServerPlayer:gainAnExtraTurn(delay) self.tag["_extra_turn_count"] = self.tag["_extra_turn_count"] or {} local ex_tag = self.tag["_extra_turn_count"] - local skillName = room.logic:getCurrentSkillName() table.insert(ex_tag, skillName) GameEvent(GameEvent.Turn, self):exec() @@ -833,7 +835,7 @@ end -- Hegemony func ----@param skill Skill +---@param skill Skill | string function ServerPlayer:addFakeSkill(skill) assert(type(skill) == "string" or skill:isInstanceOf(Skill)) if type(skill) == "string" then @@ -854,7 +856,7 @@ function ServerPlayer:addFakeSkill(skill) self:doNotify("AddSkill", json.encode{ self.id, skill.name, true }) end ----@param skill Skill +---@param skill Skill | string function ServerPlayer:loseFakeSkill(skill) assert(type(skill) == "string" or skill:isInstanceOf(Skill)) if type(skill) == "string" then @@ -873,6 +875,7 @@ function ServerPlayer:loseFakeSkill(skill) self:doNotify("LoseSkill", json.encode{ self.id, skill.name, true }) end +---@param skill Skill | string function ServerPlayer:isFakeSkill(skill) if type(skill) == "string" then skill = Fk.skills[skill] end assert(skill:isInstanceOf(Skill)) @@ -1049,18 +1052,8 @@ function ServerPlayer:hideGeneral(isDeputy) end local general = Fk.generals[generalName] - local skills = general.skills local place = isDeputy and "m" or "d" - for _, s in ipairs(skills) do - room:handleAddLoseSkills(self, "-" .. s.name, nil, false, true) - if s.relate_to_place ~= place then - if s.frequency == Skill.Compulsory then - self:addFakeSkill("reveal_skill") - end - self:addFakeSkill(s) - end - end - for _, sname in ipairs(general.other_skills) do + for _, sname in ipairs(general:getSkillNameList()) do room:handleAddLoseSkills(self, "-" .. sname, nil, false, true) local s = Fk.skills[sname] if s.relate_to_place ~= place then diff --git a/lua/server/system_enum.lua b/lua/server/system_enum.lua index 5c8dd9bd..483de916 100644 --- a/lua/server/system_enum.lua +++ b/lua/server/system_enum.lua @@ -50,6 +50,10 @@ ---@field public num integer @ 失去体力的数值 ---@field public skillName string @ 导致这次失去的技能名 +--- 描述跟体力上限变化有关的数据 +---@class MaxHpChangedData +---@field public num integer @ 体力上限变化量,可能是正数或者负数 + ---@alias DamageType integer fk.NormalDamage = 1 diff --git a/packages/standard/aux_skills.lua b/packages/standard/aux_skills.lua index 3cc3a116..9c3d6b40 100644 --- a/packages/standard/aux_skills.lua +++ b/packages/standard/aux_skills.lua @@ -197,18 +197,14 @@ local revealProhibited = fk.CreateInvaliditySkill { end if #generals == 0 then return false end - 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 - 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(true), sname) then - return true + if (g == "m" and from.general == "anjiang") or (g == "d" and from.deputyGeneral == "anjiang") then + local generalName = g == "m" and from:getMark("__heg_general") or from:getMark("__heg_deputy") + local general = Fk.generals[generalName] + if table.contains(general:getSkillNameList(true), sname) then + return true + end end end return false @@ -254,6 +250,30 @@ local revealSkill = fk.CreateActiveSkill{ elseif choice == "revealMain" then player:revealGeneral(false) elseif choice == "revealDeputy" then player:revealGeneral(true) end end, + can_use = function(self, player) + local choiceList = {} + if (player.general == "anjiang" and not player:prohibitReveal()) then + local general = Fk.generals[player:getMark("__heg_general")] + for _, sname in ipairs(general:getSkillNameList(true)) do + local s = Fk.skills[sname] + if s.frequency == Skill.Compulsory and s.relate_to_place ~= "m" then + table.insert(choiceList, "revealMain") + break + end + end + end + if (player.deputyGeneral == "anjiang" and not player:prohibitReveal(true)) then + local general = Fk.generals[player:getMark("__heg_deputy")] + for _, sname in ipairs(general:getSkillNameList(true)) do + local s = Fk.skills[sname] + if s.frequency == Skill.Compulsory and s.relate_to_place ~= "d" then + table.insert(choiceList, "revealDeputy") + break + end + end + end + return #choiceList > 0 + end } AuxSkills = { diff --git a/packages/standard_cards/init.lua b/packages/standard_cards/init.lua index 136dd20a..a8cc087c 100644 --- a/packages/standard_cards/init.lua +++ b/packages/standard_cards/init.lua @@ -1048,7 +1048,7 @@ local spearSkill = fk.CreateViewAsSkill{ pattern = "slash", card_filter = function(self, to_select, selected) if #selected == 2 then return false end - return Fk:currentRoom():getCardArea(to_select) ~= Player.Equip + return table.contains(Self:getHandlyIds(true), to_select) end, view_as = function(self, cards) if #cards ~= 2 then diff --git a/packages/test/init.lua b/packages/test/init.lua index 3ae6a71a..98b9aa71 100644 --- a/packages/test/init.lua +++ b/packages/test/init.lua @@ -208,19 +208,10 @@ local test_vs = fk.CreateViewAsSkill{ } local test_trig = fk.CreateTriggerSkill{ name = "test_trig", - events = {fk.EventPhaseEnd}, - can_trigger = function(self, event, target, player, data) - return target == player and player:hasSkill(self.name) and player.phase == Player.Discard - end, - on_cost = function(self, event, target, player, data) - local cards = player.room:askForDiscard(player, 1, 1, false, self.name, true, nil, "#test_trig-ask", true) - if #cards > 0 then - self.cost_data = cards - return true - end - end, + events = {fk.BeforeHpChanged}, + on_cost = Util.TrueFunc, on_use = function(self, event, target, player, data) - player.room:throwCard(self.cost_data, self.name, player, player) + data.num = data.num - 1 end, } local damage_maker = fk.CreateActiveSkill{ @@ -245,7 +236,7 @@ local damage_maker = fk.CreateActiveSkill{ } end, on_use = function(self, room, effect) local from = room:getPlayerById(effect.from) - local victim = #effect.tos > 0 and room:getPlayerById(effect.tos[1]) + local victim = room:getPlayerById(effect.tos[1]) local target = #effect.tos > 1 and room:getPlayerById(effect.tos[2]) local choice = self.interaction.data local number @@ -254,7 +245,7 @@ local damage_maker = fk.CreateActiveSkill{ for i = 1, 99 do table.insert(choices, tostring(i)) end - number = tonumber(room:askForChoice(from, choices, self.name, nil)) + number = tonumber(room:askForChoice(from, choices, self.name, nil)) ---@type integer end if target then from = target end if choice == "heal_hp" then @@ -316,9 +307,6 @@ local change_hero = fk.CreateActiveSkill{ local choice = self.interaction.data local generals = room:getNGenerals(8) local general = room:askForGeneral(from, generals, 1) - if general == nil then - general = table.random(generals) - end table.removeOne(generals, general) room:changeHero(target, general, false, choice == "deputyGeneral", true) room:returnToGeneralPile(generals) @@ -330,7 +318,7 @@ local test_zhenggong = fk.CreateTriggerSkill{ frequency = Skill.Compulsory, anim_type = "negative", can_trigger = function(self, event, target, player, data) - return player:hasSkill(self.name) and player.room:getTag("RoundCount") == 1 + return player:hasSkill(self) and player.room:getTag("RoundCount") == 1 end, on_use = function(self, event, target, player, data) player:gainAnExtraTurn() @@ -359,8 +347,8 @@ test2.hidden = true test2:addSkill("rende") test2:addSkill(cheat) test2:addSkill(control) ---test2:addSkill(test_vs) ---test2:addSkill(test_trig) +-- test2:addSkill(test_vs) +-- test2:addSkill(test_trig) test2:addSkill(damage_maker) test2:addSkill(test_zhenggong) test2:addSkill(change_hero) @@ -390,7 +378,6 @@ Fk:loadTranslationTable{ ["$cheat"] = "喝啊!", -- ["@@test_cheat-phase"] = "苦肉", -- ["@@test_cheat-inhand"] = "连营", - --["#test_trig-ask"] = "你可弃置一张手牌", ["control"] = "控制", [":control"] = "出牌阶段,你可以控制/解除控制若干名其他角色。", ["$control"] = "战将临阵,斩关刈城!", @@ -416,8 +403,8 @@ Fk:loadTranslationTable{ ["~mouxusheng"] = "来世,愿再为我江东之臣……", ["heal_hp"] = "回复体力", - ["lose_max_hp"] = "减少体力上限", - ["heal_max_hp"] = "增加体力上限", + ["lose_max_hp"] = "减体力上限", + ["heal_max_hp"] = "加体力上限", ["revive"] = "复活", } From ea65a3dd4b589ec35334b17652f14441741ad029 Mon Sep 17 00:00:00 2001 From: notify Date: Sun, 4 Feb 2024 15:54:51 +0800 Subject: [PATCH 13/30] =?UTF-8?q?=E4=BC=98=E5=8C=96=20(#315)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - git报错优化 - 防止反复shutdown同一事件 --- Fk/RoomElement/Photo.qml | 9 ++- lang/zh_CN.ts | 4 ++ lua/server/gameevent.lua | 6 ++ lua/server/gamelogic.lua | 5 ++ src/core/packman.cpp | 125 +++++++++++++++------------------------ 5 files changed, 70 insertions(+), 79 deletions(-) diff --git a/Fk/RoomElement/Photo.qml b/Fk/RoomElement/Photo.qml index bbd9bbe4..cb73de73 100644 --- a/Fk/RoomElement/Photo.qml +++ b/Fk/RoomElement/Photo.qml @@ -179,6 +179,7 @@ Item { Image { id: generalImage width: deputyGeneral ? parent.width / 2 : parent.width + Behavior on width { NumberAnimation { duration: 100 } } height: parent.height smooth: true fillMode: Image.PreserveAspectCrop @@ -259,7 +260,8 @@ Item { anchors.fill: photoMaskEffect source: photoMaskEffect saturation: 0 - visible: root.dead || root.surrendered + opacity: (root.dead || root.surrendered) ? 1 : 0 + Behavior on opacity { NumberAnimation { duration: 300 } } } Rectangle { @@ -269,9 +271,10 @@ Item { height: 222 radius: 8 - visible: root.drank > 0 + // visible: root.drank > 0 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 { diff --git a/lang/zh_CN.ts b/lang/zh_CN.ts index e5dbf625..371eae03 100644 --- a/lang/zh_CN.ts +++ b/lang/zh_CN.ts @@ -107,6 +107,10 @@ [%1/%2] upgrading package '%3' [%1/%2] 更新拓展包 '%3' + + packages/%1: some error occured. + 拓展包 %1 出了点问题,尝试在管理拓展包中删除之再试试 + diff --git a/lua/server/gameevent.lua b/lua/server/gameevent.lua index e89012b6..b5401faf 100644 --- a/lua/server/gameevent.lua +++ b/lua/server/gameevent.lua @@ -14,6 +14,7 @@ ---@field public exit_func fun(self: GameEvent) @ 事件结束后执行的函数 ---@field public extra_exit_funcs fun(self:GameEvent)[] @ 事件结束后执行的自定义函数 ---@field public exec_ret boolean? @ exec函数的返回值,可能不存在 +---@field public status string @ ready, running, exiting, dead ---@field public interrupted boolean @ 事件是否是因为被中断而结束的,可能是防止事件或者被杀 ---@field public killed boolean @ 事件因为终止一切结算而被中断(所谓的“被杀”) local GameEvent = class("GameEvent") @@ -49,6 +50,7 @@ function GameEvent:initialize(event, ...) self.extra_clear_funcs = Util.DummyTable self.exit_func = GameEvent.exit_funcs[event] or dummyFunc self.extra_exit_funcs = Util.DummyTable + self.status = "ready" self.interrupted = false end @@ -166,6 +168,8 @@ end function GameEvent:exec() local room = self.room local logic = room.logic + if self.status ~= "ready" then return true end + self.parent = logic:getCurrentEvent() if self:prepare_func() then return true end @@ -174,6 +178,7 @@ function GameEvent:exec() local co = coroutine.create(self.main_func) self._co = co + self.status = "running" coroutine.yield(self, "__newEvent") @@ -188,6 +193,7 @@ function GameEvent:exec() end function GameEvent:shutdown() + if self.status ~= "running" then return end -- yield to self and break coroutine.yield(self, "__breakEvent") end diff --git a/lua/server/gamelogic.lua b/lua/server/gamelogic.lua index db8470a1..9e1e340d 100644 --- a/lua/server/gamelogic.lua +++ b/lua/server/gamelogic.lua @@ -441,6 +441,7 @@ function GameLogic:start() e.killed = e ~= jump_to self:clearEvent(e) coroutine.close(e._co) + e.status = "dead" if e == jump_to then jump_to = nil end -- shutdown结束了 e = self:getCurrentCleaner() end @@ -460,6 +461,7 @@ function GameLogic:start() e.interrupted = ret self:clearEvent(e) coroutine.close(e._co) + e.status = "dead" elseif ret == true then -- 跳到越早发生的事件越好 if not jump_to then @@ -543,6 +545,8 @@ end ---@param event GameEvent function GameLogic:clearEvent(event) if event.event == GameEvent.ClearEvent then return end + if event.status == "exiting" then return end + event.status = "exiting" local ce = GameEvent(GameEvent.ClearEvent, event) ce.id = self.current_event_id local co = coroutine.create(ce.main_func) @@ -657,6 +661,7 @@ end function GameLogic:breakTurn() local event = self:getCurrentEvent():findParent(GameEvent.Turn) + if not event then return end event:shutdown() end diff --git a/src/core/packman.cpp b/src/core/packman.cpp index dead59c2..7f3430f0 100644 --- a/src/core/packman.cpp +++ b/src/core/packman.cpp @@ -169,7 +169,11 @@ void PackMan::updatePack(const QString &pack) { int error; error = status(pack); if (error != 0) { - qCritical("packages/%s: Workspace is dirty, or some error occured.", pack.toLatin1().constData()); +#ifndef FK_SERVER_ONLY + if (Backend != nullptr) { + Backend->showToast(tr("packages/%1: some error occured.").arg(pack)); + } +#endif return; } error = pull(pack); @@ -187,7 +191,11 @@ void PackMan::upgradePack(const QString &pack) { return; error = status(pack); if (error != 0) { - qCritical("Workspace is dirty, or some error occured."); +#ifndef FK_SERVER_ONLY + if (Backend != nullptr) { + Backend->showToast(tr("packages/%1: some error occured.").arg(pack)); + } +#endif return; } error = pull(pack); @@ -219,6 +227,12 @@ QString PackMan::listPackages() { const git_error *e = git_error_last(); \ qCritical("Error %d/%d: %s\n", error, e->klass, e->message) +#define GIT_CHK_CLEAN \ + if (error < 0) { \ + GIT_FAIL; \ + goto clean; \ + } + static int transfer_progress_cb(const git_indexer_progress *stats, void *payload) { (void)payload; @@ -290,37 +304,22 @@ int PackMan::pull(const QString &name) { opt2.checkout_strategy = GIT_CHECKOUT_FORCE; error = git_repository_open(&repo, path); - if (error < 0) { - GIT_FAIL; - goto clean; - } + GIT_CHK_CLEAN; // first git fetch origin error = git_remote_lookup(&remote, repo, "origin"); - if (error < 0) { - GIT_FAIL; - goto clean; - } + GIT_CHK_CLEAN; error = git_remote_fetch(remote, NULL, &opt, NULL); - if (error < 0) { - GIT_FAIL; - goto clean; - } + GIT_CHK_CLEAN; // then git checkout FETCH_HEAD error = git_repository_set_head(repo, "FETCH_HEAD"); - if (error < 0) { - GIT_FAIL; - goto clean; - } + GIT_CHK_CLEAN; error = git_checkout_head(repo, &opt2); - if (error < 0) { - GIT_FAIL; - goto clean; - } else { - if (Backend == nullptr) - printf("\n"); - } + GIT_CHK_CLEAN; + + if (Backend == nullptr) + printf("\n"); clean: git_remote_free(remote); @@ -337,25 +336,13 @@ int PackMan::checkout(const QString &name, const QString &hash) { auto path = QString("packages/%1").arg(name).toUtf8(); auto sha = hash.toLatin1(); error = git_repository_open(&repo, path); - if (error < 0) { - GIT_FAIL; - goto clean; - } + GIT_CHK_CLEAN; error = git_oid_fromstr(&oid, sha); - if (error < 0) { - GIT_FAIL; - goto clean; - } + GIT_CHK_CLEAN; error = git_repository_set_head_detached(repo, &oid); - if (error < 0) { - GIT_FAIL; - goto clean; - } + GIT_CHK_CLEAN; error = git_checkout_head(repo, &opt); - if (error < 0) { - GIT_FAIL; - goto clean; - } + GIT_CHK_CLEAN; clean: git_repository_free(repo); @@ -369,20 +356,11 @@ int PackMan::checkout_branch(const QString &name, const QString &branch) { git_object *obj = NULL; auto path = QString("packages/%1").arg(name).toUtf8(); error = git_repository_open(&repo, path); - if (error < 0) { - GIT_FAIL; - goto clean; - } + GIT_CHK_CLEAN; error = git_revparse_single(&obj, repo, branch.toUtf8()); - if (error < 0) { - GIT_FAIL; - goto clean; - } + GIT_CHK_CLEAN; error = git_checkout_tree(repo, obj, NULL); - if (error < 0) { - GIT_FAIL; - goto clean; - } + GIT_CHK_CLEAN; clean: git_object_free(obj); @@ -398,21 +376,19 @@ int PackMan::status(const QString &name) { const git_status_entry *s; auto path = QString("packages/%1").arg(name).toUtf8(); error = git_repository_open(&repo, path); - if (error < 0) { - GIT_FAIL; - goto clean; - } + GIT_CHK_CLEAN; error = git_status_list_new(&status_list, repo, NULL); - if (error < 0) { - GIT_FAIL; - goto clean; - } + GIT_CHK_CLEAN; maxi = git_status_list_entrycount(status_list); for (i = 0; i < maxi; ++i) { char *istatus = NULL; s = git_status_byindex(status_list, i); - if (s->status != GIT_STATUS_CURRENT && s->status != GIT_STATUS_IGNORED) + if (s->status != GIT_STATUS_CURRENT && s->status != GIT_STATUS_IGNORED) { + git_status_list_free(status_list); + git_repository_free(repo); + qCritical("Workspace is dirty."); return 1; + } } clean: @@ -425,28 +401,25 @@ QString PackMan::head(const QString &name) { git_repository *repo = NULL; int error; git_object *obj = NULL; + const git_oid *oid; + char buf[42] = {0}; auto path = QString("packages/%1").arg(name).toUtf8(); error = git_repository_open(&repo, path); - if (error < 0) { - GIT_FAIL; - git_object_free(obj); - git_repository_free(repo); - return QString(); - } + GIT_CHK_CLEAN; error = git_revparse_single(&obj, repo, "HEAD"); - if (error < 0) { - GIT_FAIL; - git_object_free(obj); - git_repository_free(repo); - return QString(); - } + GIT_CHK_CLEAN; - const git_oid *oid = git_object_id(obj); - char buf[42]; + oid = git_object_id(obj); git_oid_tostr(buf, 41, oid); git_object_free(obj); git_repository_free(repo); return QString(buf); + +clean: + git_object_free(obj); + git_repository_free(repo); + return QString(); } #undef GIT_FAIL +#undef GIT_CHK_CLEAN From d4bb4e21bb63ec83303d48532f46644e1eeaa8b7 Mon Sep 17 00:00:00 2001 From: YoumuKon <38815081+YoumuKon@users.noreply.github.com> Date: Sun, 4 Feb 2024 15:55:44 +0800 Subject: [PATCH 14/30] =?UTF-8?q?=E7=A6=85=E4=B8=8Ebugfix=20(#312)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将Utility如canUseCardTo的一些函数搬运到了本体 - 为技能添加hooked_piles属性,当失去技能时自动弃置hooked_piles内的所有私人牌堆 - 修复了添加技能没写source_skill的bug - 修复了ActiveSkill的interaction不传入Skill本身而是metatable的bug - 修复了主动询问canUse时没有传入extra_data的bug - 修复了多选时按钮选项变回空白的bug - 修复了判定阶段被中途拿走判定牌后报错的bug --- Fk/RoomElement/DetailedCheckBox.qml | 1 + lua/core/card.lua | 4 +- lua/core/player.lua | 17 +++- lua/core/skill_type/usable_skill.lua | 1 + lua/fk_ex.lua | 15 ++- lua/server/events/gameflow.lua | 33 +++--- lua/server/gamelogic.lua | 145 +++++++++++++++++++++++++++ lua/server/mark_enum.lua | 31 ++++-- lua/server/room.lua | 41 +++++++- lua/server/system_enum.lua | 2 +- packages/standard_cards/init.lua | 2 + 11 files changed, 257 insertions(+), 35 deletions(-) diff --git a/Fk/RoomElement/DetailedCheckBox.qml b/Fk/RoomElement/DetailedCheckBox.qml index 7a435bc7..c7c94ad3 100644 --- a/Fk/RoomElement/DetailedCheckBox.qml +++ b/Fk/RoomElement/DetailedCheckBox.qml @@ -39,6 +39,7 @@ GraphicsBox { id: choicetitle width: parent.width text: luatr(modelData) + triggered: root.result.includes(index) enabled: options.indexOf(modelData) !== -1 && (root.result.length < max_num || triggered) textFont.pixelSize: 24 diff --git a/lua/core/card.lua b/lua/core/card.lua index 256dff7f..378d3c5c 100644 --- a/lua/core/card.lua +++ b/lua/core/card.lua @@ -24,6 +24,7 @@ ---@field public special_skills? string[] @ 衍生技能,如重铸 ---@field public is_damage_card boolean @ 是否为会造成伤害的牌 ---@field public multiple_targets boolean @ 是否为指定多个目标的牌 +---@field public is_passive? boolean @ 是否只能在响应时使用或打出 ---@field public is_derived? boolean @ 判断是否为衍生牌 local Card = class("Card") @@ -145,11 +146,12 @@ function Card:clone(suit, number) newCard.special_skills = self.special_skills newCard.is_damage_card = self.is_damage_card newCard.multiple_targets = self.multiple_targets + newCard.is_passive = self.is_passive newCard.is_derived = self.is_derived return newCard end ---- 检测是否为虚拟卡牌,如果其ID为0及以下,则为虚拟卡牌。 +--- 检测是否为虚拟卡牌,如果其ID为0,则为虚拟卡牌。 function Card:isVirtual() return self.id == 0 end diff --git a/lua/core/player.lua b/lua/core/player.lua index cf608f55..5862b820 100644 --- a/lua/core/player.lua +++ b/lua/core/player.lua @@ -872,9 +872,20 @@ end --- 确认玩家是否可以使用特定牌。 ---@param card Card @ 特定牌 -function Player:canUse(card) - assert(card, "Error: No Card") - return card.skill:canUse(self, card) +---@param extra_data? UseExtraData @ 额外数据 +function Player:canUse(card, extra_data) + 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 --- 确认玩家是否被禁止对特定玩家使用特定牌。 diff --git a/lua/core/skill_type/usable_skill.lua b/lua/core/skill_type/usable_skill.lua index 7ef7bd87..34e9d430 100644 --- a/lua/core/skill_type/usable_skill.lua +++ b/lua/core/skill_type/usable_skill.lua @@ -4,6 +4,7 @@ ---@field public main_skill UsableSkill ---@field public max_use_time integer[] ---@field public expand_pile? string | integer[] | fun(self: UsableSkill): integer[]|string? +---@field public derived_piles? string | string[] local UsableSkill = Skill:subclass("UsableSkill") function UsableSkill:initialize(name, frequency) diff --git a/lua/fk_ex.lua b/lua/fk_ex.lua index aaca1ea8..6ceb1866 100644 --- a/lua/fk_ex.lua +++ b/lua/fk_ex.lua @@ -48,6 +48,11 @@ end local function readUsableSpecToSkill(skill, spec) readCommonSpecToSkill(skill, spec) assert(spec.main_skill == nil or spec.main_skill:isInstanceOf(UsableSkill)) + if type(spec.derived_piles) == "string" then + skill.derived_piles = {spec.derived_piles} + else + skill.derived_piles = spec.derived_piles or {} + end skill.main_skill = spec.main_skill skill.target_num = spec.target_num or skill.target_num skill.min_target_num = spec.min_target_num or skill.min_target_num @@ -191,8 +196,8 @@ function fk.CreateActiveSkill(spec) readUsableSpecToSkill(skill, spec) if spec.can_use then - skill.canUse = function(curSkill, player, card) - return spec.can_use(curSkill, player, card) and curSkill:isEffectable(player) + skill.canUse = function(curSkill, player, card, extra_data) + return spec.can_use(curSkill, player, card, extra_data) and curSkill:isEffectable(player) end end if spec.card_filter then skill.cardFilter = spec.card_filter end @@ -211,9 +216,9 @@ function fk.CreateActiveSkill(spec) if spec.interaction then skill.interaction = setmetatable({}, { - __call = function(self) + __call = function() if type(spec.interaction) == "function" then - return spec.interaction(self) + return spec.interaction(skill) else return spec.interaction end @@ -445,6 +450,7 @@ end ---@field public special_skills? string[] ---@field public is_damage_card? boolean ---@field public multiple_targets? boolean +---@field public is_passive? boolean local defaultCardSkill = fk.CreateActiveSkill{ name = "default_card_skill", @@ -487,6 +493,7 @@ local function readCardSpecToCard(card, spec) card.special_skills = spec.special_skills card.is_damage_card = spec.is_damage_card card.multiple_targets = spec.multiple_targets + card.is_passive = spec.is_passive end ---@param spec CardSpec diff --git a/lua/server/events/gameflow.lua b/lua/server/events/gameflow.lua index fe66b5b8..b44f1041 100644 --- a/lua/server/events/gameflow.lua +++ b/lua/server/events/gameflow.lua @@ -268,7 +268,7 @@ GameEvent.functions[GameEvent.Phase] = function(self) local room = self.room local logic = room.logic - local player = self.data[1] + local player = self.data[1] ---@type Player if not logic:trigger(fk.EventPhaseStart, player) then if player.phase ~= Player.NotActive then logic:trigger(fk.EventPhaseProceeding, player) @@ -285,23 +285,26 @@ GameEvent.functions[GameEvent.Phase] = function(self) end, [Player.Judge] = function() local cards = player:getCardIds(Player.Judge) - for i = #cards, 1, -1 do - local card - card = player:removeVirtualEquip(cards[i]) + while #cards > 0 do + local cid = table.remove(cards) + if not cid then return end + local card = player:removeVirtualEquip(cid) if not card then - card = Fk:getCardById(cards[i]) + card = Fk:getCardById(cid) end - room:moveCardTo(card, Card.Processing, nil, fk.ReasonPut, self.name) + if table.contains(player:getCardIds(Player.Judge), cid) then + room:moveCardTo(card, Card.Processing, nil, fk.ReasonPut, self.name) - ---@type CardEffectEvent - local effect_data = { - card = card, - to = player.id, - tos = { {player.id} }, - } - room:doCardEffect(effect_data) - if effect_data.isCancellOut and card.skill then - card.skill:onNullified(room, effect_data) + ---@type CardEffectEvent + local effect_data = { + card = card, + to = player.id, + tos = { {player.id} }, + } + room:doCardEffect(effect_data) + if effect_data.isCancellOut and card.skill then + card.skill:onNullified(room, effect_data) + end end end end, diff --git a/lua/server/gamelogic.lua b/lua/server/gamelogic.lua index 9e1e340d..71c556d7 100644 --- a/lua/server/gamelogic.lua +++ b/lua/server/gamelogic.lua @@ -599,6 +599,151 @@ function GameLogic:getEventsOfScope(eventType, n, func, scope) return start_event:searchEvents(eventType, n, func) end +-- 在指定历史范围中找符合条件的事件(逆序) +---@param eventType integer @ 要查找的事件类型 +---@param func fun(e: GameEvent): boolean @ 过滤用的函数 +---@param n integer @ 最多找多少个 +---@param end_id integer @ 查询历史范围:从最后的事件开始逆序查找直到id为end_id的事件(不含) +---@return GameEvent[] @ 找到的符合条件的所有事件,最多n个但不保证有n个 +function GameLogic:getEventsByRule(eventType, n, func, end_id) + local ret = {} + local events = self.event_recorder[eventType] or Util.DummyTable + for i = #events, 1, -1 do + local e = events[i] + if e.id <= end_id then break end + if func(e) then + table.insert(ret, e) + if #ret >= n then break end + end + end + return ret +end + + +--- 获取实际的伤害事件 +---@param n integer @ 最多找多少个 +---@param func fun(e: GameEvent): boolean @ 过滤用的函数 +---@param scope? integer @ 查询历史范围,只能是当前阶段/回合/轮次 +---@param end_id? integer @ 查询历史范围:从最后的事件开始逆序查找直到id为end_id的事件(不含) +---@return GameEvent[] @ 找到的符合条件的所有事件,最多n个但不保证有n个 +function GameLogic:getActualDamageEvents(n, func, scope, end_id) + if not end_id then + scope = scope or Player.HistoryTurn + end + + n = n or 1 + func = func or Util.TrueFunc + + local eventType = GameEvent.Damage + local ret = {} + local endIdRecorded + local tempEvents = {} + + local addTempEvents = function(reverse) + if #tempEvents > 0 and #ret < n then + table.sort(tempEvents, function(a, b) + if reverse then + return a.data[1].dealtRecorderId > b.data[1].dealtRecorderId + else + return a.data[1].dealtRecorderId < b.data[1].dealtRecorderId + end + end) + + for _, e in ipairs(tempEvents) do + table.insert(ret, e) + if #ret >= n then return true end + end + end + + endIdRecorded = nil + tempEvents = {} + + return false + end + + if scope then + local event = self:getCurrentEvent() + local start_event ---@type GameEvent + if scope == Player.HistoryGame then + start_event = self.all_game_events[1] + elseif scope == Player.HistoryRound then + start_event = event:findParent(GameEvent.Round, true) + elseif scope == Player.HistoryTurn then + start_event = event:findParent(GameEvent.Turn, true) + elseif scope == Player.HistoryPhase then + start_event = event:findParent(GameEvent.Phase, true) + end + + if not start_event then return {} end + + local events = self.event_recorder[eventType] or Util.DummyTable + local from = start_event.id + local to = start_event.end_id + if math.abs(to) == 1 then to = #self.all_game_events end + + for _, v in ipairs(events) do + local damageStruct = v.data[1] + if damageStruct.dealtRecorderId then + if endIdRecorded and v.id > endIdRecorded then + local result = addTempEvents() + if result then + return ret + end + end + + if v.id >= from and v.id <= to then + if not endIdRecorded and v.end_id > -1 and v.end_id > v.id then + endIdRecorded = v.end_id + end + + if func(v) then + if endIdRecorded then + table.insert(tempEvents, v) + else + table.insert(ret, v) + end + end + end + if #ret >= n then break end + end + end + + addTempEvents() + else + local events = self.event_recorder[eventType] or Util.DummyTable + + for i = #events, 1, -1 do + local e = events[i] + if e.id <= end_id then break end + + local damageStruct = e.data[1] + if damageStruct.dealtRecorderId then + if e.end_id == -1 or (endIdRecorded and endIdRecorded > e.end_id) then + local result = addTempEvents(true) + if result then + return ret + end + + if func(e) then + table.insert(ret, e) + end + else + endIdRecorded = e.end_id + if func(e) then + table.insert(tempEvents, e) + end + end + + if #ret >= n then break end + end + end + + addTempEvents(true) + end + + return ret +end + function GameLogic:dumpEventStack(detailed) local top = self:getCurrentEvent() local i = self.game_event_stack.p diff --git a/lua/server/mark_enum.lua b/lua/server/mark_enum.lua index e9308841..f13cb258 100644 --- a/lua/server/mark_enum.lua +++ b/lua/server/mark_enum.lua @@ -19,23 +19,38 @@ MarkEnum.MinusMaxCards = "MinusMaxCards" ---于本回合内减少标记值数量的手牌上限 MarkEnum.MinusMaxCardsInTurn = "MinusMaxCards-turn" ----使用牌无次数限制,可带清除标记后缀(-tmp为请求专用) +---使用牌无次数限制 MarkEnum.BypassTimesLimit = "BypassTimesLimit" ----使用牌无距离限制,可带清除标记后缀(-tmp为请求专用) +---使用牌无距离限制 MarkEnum.BypassDistancesLimit = "BypassDistancesLimit" ----对其使用牌无次数限制,可带清除标记后缀 +---对其使用牌无次数限制 MarkEnum.BypassTimesLimitTo = "BypassTimesLimitTo" ----对其使用牌无距离限制,可带清除标记后缀 +---对其使用牌无距离限制 MarkEnum.BypassDistancesLimitTo = "BypassDistancesLimitTo" ----非锁定技失效,可带清除标记后缀 +---非锁定技失效 MarkEnum.UncompulsoryInvalidity = "UncompulsoryInvalidity" ----不可明置,可带清除标记后缀(值为表,m - 主将, d - 副将) +---不可明置(值为表,m - 主将, d - 副将) MarkEnum.RevealProhibited = "RevealProhibited" ----不计入距离、座次后缀,可带清除标记后缀 +---不计入距离、座次后缀 MarkEnum.PlayerRemoved = "PlayerRemoved" ---各种清除标记后缀 +--- +---phase:阶段结束后 +--- +---turn:回合结束后 +--- +---round:轮次结束后 MarkEnum.TempMarkSuffix = { "-phase", "-turn", "-round" } ---卡牌标记版本的清除标记后缀 -MarkEnum.CardTempMarkSuffix = { "-phase", "-turn", "-round", "-inhand" } +--- +---phase:阶段结束后 +--- +---turn:回合结束后 +--- +---round:轮次结束后 +--- +---inhand:离开手牌区后 +MarkEnum.CardTempMarkSuffix = { "-phase", "-turn", "-round", + "-inhand" } diff --git a/lua/server/room.lua b/lua/server/room.lua index 47467285..96c2e118 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -1726,6 +1726,26 @@ function Room:askForSkillInvoke(player, skill_name, data, prompt) return invoked end +-- 获取使用牌的合法额外目标(【借刀杀人】等带副目标的卡牌除外) +---@param data CardUseStruct @ 使用事件的data +---@param bypass_distances boolean? @ 是否无距离关系的限制 +---@param use_AimGroup boolean? @ 某些场合需要使用AimGroup,by smart Ho-spair +---@return integer[] @ 返回满足条件的player的id列表 +function Room:getUseExtraTargets(data, bypass_distances, use_AimGroup) + if not (data.card.type == Card.TypeBasic or data.card:isCommonTrick()) then return {} end + if data.card.skill:getMinTargetNum() > 1 then return {} end --stupid collateral + local tos = {} + local current_targets = use_AimGroup and AimGroup:getAllTargets(data.tos) or TargetGroup:getRealTargets(data.tos) + for _, p in ipairs(self.alive_players) do + if not table.contains(current_targets, p.id) and not self:getPlayerById(data.from):isProhibited(p, data.card) then + if data.card.skill:modTargetFilter(p.id, {}, data.from, data.card, not bypass_distances) then + table.insert(tos, p.id) + end + end + end + return tos +end + --为使用牌增减目标 ---@param player ServerPlayer @ 执行的玩家 ---@param targets ServerPlayer[] @ 可选的目标范围 @@ -2913,7 +2933,7 @@ end --- 让一名玩家获得一张牌 ---@param player integer|ServerPlayer @ 要拿牌的玩家 ----@param cid integer|Card @ 要拿到的卡牌 +---@param cid integer|Card|integer[] @ 要拿到的卡牌 ---@param unhide? boolean @ 是否明着拿 ---@param reason? CardMoveReason @ 卡牌移动的原因 ---@param proposer? integer @ 移动操作者的id @@ -3114,6 +3134,7 @@ function Room:handleAddLoseSkills(player, skill_names, source_skill, sendlog, no if #skill_names == 0 then return end local losts = {} ---@type boolean[] local triggers = {} ---@type Skill[] + local lost_piles = {} ---@type integer[] for _, skill in ipairs(skill_names) do if string.sub(skill, 1, 1) == "-" then local actual_skill = string.sub(skill, 2, #skill) @@ -3135,12 +3156,17 @@ function Room:handleAddLoseSkills(player, skill_names, source_skill, sendlog, no table.insert(losts, true) table.insert(triggers, s) + if s.derived_piles then + for _, pile_name in ipairs(s.derived_piles) do + table.insertTableIfNeed(lost_piles, player:getPile(pile_name)) + end + end end end else local sk = Fk.skills[skill] if sk and not player:hasSkill(sk, true, true) then - local got_skills = player:addSkill(sk) + local got_skills = player:addSkill(sk, source_skill) for _, s in ipairs(got_skills) do -- TODO: limit skill mark @@ -3171,6 +3197,15 @@ function Room:handleAddLoseSkills(player, skill_names, source_skill, sendlog, no self.logic:trigger(event, player, triggers[i]) end end + + if #lost_piles > 0 then + self:moveCards({ + ids = lost_piles, + from = player.id, + toArea = Card.DiscardPile, + moveReason = fk.ReasonPutIntoDiscardPile, + }) + end end -- 判定 @@ -3236,7 +3271,7 @@ function Room:retrial(card, player, judge, skillName, exchange) end --- 弃置一名角色的牌。 ----@param card_ids integer[] @ 被弃掉的牌 +---@param card_ids integer[]|integer @ 被弃掉的牌 ---@param skillName? string @ 技能名 ---@param who ServerPlayer @ 被弃牌的人 ---@param thrower? ServerPlayer @ 弃别人牌的人 diff --git a/lua/server/system_enum.lua b/lua/server/system_enum.lua index 483de916..e75d679a 100644 --- a/lua/server/system_enum.lua +++ b/lua/server/system_enum.lua @@ -134,7 +134,7 @@ fk.IceDamage = 4 ---@field public additionalEffect? integer ---@class CardEffectEvent ----@field public from integer +---@field public from? integer ---@field public to integer ---@field public subTargets? integer[] ---@field public tos TargetGroup diff --git a/packages/standard_cards/init.lua b/packages/standard_cards/init.lua index a8cc087c..c0559fcf 100644 --- a/packages/standard_cards/init.lua +++ b/packages/standard_cards/init.lua @@ -112,6 +112,7 @@ local jink = fk.CreateBasicCard{ suit = Card.Heart, number = 2, skill = jinkSkill, + is_passive = true, } extension:addCards({ @@ -468,6 +469,7 @@ local nullification = fk.CreateTrickCard{ suit = Card.Spade, number = 11, skill = nullificationSkill, + is_passive = true, } extension:addCards({ From bd723c02f136dd41ded025c112df0c8f7bbfeb0c Mon Sep 17 00:00:00 2001 From: notify Date: Sun, 4 Feb 2024 16:09:36 +0800 Subject: [PATCH 15/30] Changelog: v0.4.6 --- CHANGELOG.md | 33 +++++++++++++++++++++++++++++++++ CMakeLists.txt | 2 +- android/AndroidManifest.xml | 4 ++-- lua/client/i18n/zh_CN.lua | 4 ++-- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ccfd4e26..553d5116 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,38 @@ # ChangeLog +## v0.4.6 + +- 攻击范围状态技类新增基础值修正函数 +- 伤害值在一个技能处理后小于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 diff --git a/CMakeLists.txt b/CMakeLists.txt index f62fe2b4..c8656160 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16) -project(FreeKill VERSION 0.4.5) +project(FreeKill VERSION 0.4.6) add_definitions(-DFK_VERSION=\"${CMAKE_PROJECT_VERSION}\") find_package(Qt6 REQUIRED COMPONENTS diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index c086fe0c..d6ad8892 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -3,8 +3,8 @@ + android:versionCode="406" + android:versionName="0.4.6"> diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index d946ce20..76de68b0 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -311,9 +311,9 @@ FreeKill使用的是libgit2的C API,与此同时使用Git完成拓展包的下 ["Resume"] = "继续", ["Bulletin Info"] = [==[ - ## v0.4.5 + ## v0.4.6 - 禁将体验优化,然而烦请重新禁将。请善用禁用拓展包与白名单功能,旧方案已经不可导入。 + 底层结算优化 ]==], } From 9815c6d9e2f567fba525fefe4437d97b8a91b3ae Mon Sep 17 00:00:00 2001 From: Ho-spair Date: Sun, 4 Feb 2024 22:29:54 +0800 Subject: [PATCH 16/30] fixup Amazing Grace and afterUse --- lua/server/events/usecard.lua | 14 +++++++ lua/server/room.lua | 19 +--------- packages/standard_cards/init.lua | 63 ++++---------------------------- 3 files changed, 23 insertions(+), 73 deletions(-) diff --git a/lua/server/events/usecard.lua b/lua/server/events/usecard.lua index d73f93d0..e1e0a0a8 100644 --- a/lua/server/events/usecard.lua +++ b/lua/server/events/usecard.lua @@ -164,6 +164,20 @@ GameEvent.functions[GameEvent.UseCard] = function(self) local room = self.room local logic = room.logic + if type(cardUseEvent.attachedSkillAndUser) == "table" then + local attachedSkillAndUser = table.simpleClone(cardUseEvent.attachedSkillAndUser) + self:addExitFunc(function() + if + type(attachedSkillAndUser) == "table" and + Fk.skills[attachedSkillAndUser.skillName] and + Fk.skills[attachedSkillAndUser.skillName].afterUse + then + Fk.skills[attachedSkillAndUser.skillName]:afterUse(room:getPlayerById(attachedSkillAndUser.user), cardUseEvent) + end + end) + cardUseEvent.attachedSkillAndUser = nil + end + if cardUseEvent.card.skill then cardUseEvent.card.skill:onUse(room, cardUseEvent) end diff --git a/lua/server/room.lua b/lua/server/room.lua index 96c2e118..d194dd6d 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -2423,23 +2423,7 @@ end ---@param cardUseEvent CardUseStruct @ 使用数据 ---@return boolean function Room:useCard(cardUseEvent) - local attachedSkillAndUser - if type(cardUseEvent.attachedSkillAndUser) == "table" then - attachedSkillAndUser = table.simpleClone(cardUseEvent.attachedSkillAndUser) - cardUseEvent.attachedSkillAndUser = nil - end - - local ret = execGameEvent(GameEvent.UseCard, cardUseEvent) - - if - type(attachedSkillAndUser) == "table" and - Fk.skills[attachedSkillAndUser.skillName] and - Fk.skills[attachedSkillAndUser.skillName].afterUse - then - Fk.skills[attachedSkillAndUser.skillName]:afterUse(self:getPlayerById(attachedSkillAndUser.user), cardUseEvent) - end - - return ret + return execGameEvent(GameEvent.UseCard, cardUseEvent) end ---@param room Room @@ -2686,6 +2670,7 @@ function Room:doCardUseEffect(cardUseEvent) for i = 1, (cardUseEvent.additionalEffect or 0) + 1 do if #TargetGroup:getRealTargets(cardUseEvent.tos) > 0 and cardUseEvent.card.skill.onAction then cardUseEvent.card.skill:onAction(self, cardUseEvent) + cardEffectEvent.extra_data = cardUseEvent.extra_data end -- Else: do effect to all targets diff --git a/packages/standard_cards/init.lua b/packages/standard_cards/init.lua index c0559fcf..9374e9a6 100644 --- a/packages/standard_cards/init.lua +++ b/packages/standard_cards/init.lua @@ -21,7 +21,7 @@ local slashSkill = fk.CreateActiveSkill{ max_phase_use_time = 1, target_num = 1, can_use = function(self, player, card, extra_data) - return (extra_data and extra_data.bypass_times) or + return (extra_data and extra_data.bypass_times) or player.phase ~= Player.Play or table.find(Fk:currentRoom().alive_players, function(p) return self:withinTimesLimit(player, Player.HistoryPhase, card, "slash", p) end) @@ -36,7 +36,12 @@ local slashSkill = fk.CreateActiveSkill{ if #selected < self:getMaxTargetNum(Self, card) then local player = Fk:currentRoom():getPlayerById(to_select) return self:modTargetFilter(to_select, selected, Self.id, card, count_distances) and - (#selected > 0 or (extra_data and extra_data.bypass_times) or self:withinTimesLimit(Self, Player.HistoryPhase, card, "slash", player)) + ( + #selected > 0 or + Self.phase ~= Player.Play or + (extra_data and extra_data.bypass_times) or + self:withinTimesLimit(Self, Player.HistoryPhase, card, "slash", player) + ) end end, on_effect = function(self, room, effect) @@ -660,60 +665,6 @@ local amazingGraceSkill = fk.CreateActiveSkill{ end } -local amazingGraceAction = fk.CreateTriggerSkill{ - name = "amazing_grace_action", - global = true, - priority = { [fk.BeforeCardUseEffect] = 0, [fk.CardUseFinished] = 10 }, -- game rule - events = { fk.BeforeCardUseEffect, fk.CardUseFinished }, - can_trigger = function(self, event, target, player, data) - local frameFilled = data.extra_data and data.extra_data.AGFilled - if event == fk.BeforeCardUseEffect then - return data.card.trueName == 'amazing_grace' and not frameFilled - else - return frameFilled - end - end, - on_trigger = function(self, event, target, player, data) - local room = player.room - if event == fk.BeforeCardUseEffect then - local toDisplay = room:getNCards(#TargetGroup:getRealTargets(data.tos)) - room:moveCards({ - ids = toDisplay, - toArea = Card.Processing, - moveReason = fk.ReasonPut, - }) - - table.forEach(room.players, function(p) - room:fillAG(p, toDisplay) - end) - - data.extra_data = data.extra_data or {} - data.extra_data.AGFilled = toDisplay - else - table.forEach(room.players, function(p) - room:closeAG(p) - end) - - if data.extra_data and data.extra_data.AGFilled then - local toDiscard = table.filter(data.extra_data.AGFilled, function(id) - return room:getCardArea(id) == Card.Processing - end) - - if #toDiscard > 0 then - room:moveCards({ - ids = toDiscard, - toArea = Card.DiscardPile, - moveReason = fk.ReasonPutIntoDiscardPile, - }) - end - end - - data.extra_data.AGFilled = nil - end - end, -} -Fk:addSkill(amazingGraceAction) - local amazingGrace = fk.CreateTrickCard{ name = "amazing_grace", suit = Card.Heart, From b2a438c200181e340987f520dedc80b9a2528e3d Mon Sep 17 00:00:00 2001 From: Nyutanislavsky Date: Mon, 5 Feb 2024 11:07:09 +0800 Subject: [PATCH 17/30] Yiji (#316) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 标遗计运用expand_pile --- Fk/Pages/GeneralsOverview.qml | 12 ++-- lua/client/i18n/en_US.lua | 9 +++ lua/client/i18n/zh_CN.lua | 6 ++ packages/standard/i18n/zh_CN.lua | 52 +++++++++++++++- packages/standard/init.lua | 102 +++++++++++-------------------- 5 files changed, 108 insertions(+), 73 deletions(-) diff --git a/Fk/Pages/GeneralsOverview.qml b/Fk/Pages/GeneralsOverview.qml index 8a01f8a8..2f7ee52e 100644 --- a/Fk/Pages/GeneralsOverview.qml +++ b/Fk/Pages/GeneralsOverview.qml @@ -360,7 +360,7 @@ Item { Layout.fillWidth: true text: { if (name.endsWith("_win_audio")) { - return "胜利语音"; + return luatr("Win audio"); } return luatr(name) + (idx ? " (" + idx.toString() + ")" : ""); @@ -522,7 +522,7 @@ Item { function trans(str) { const ret = luatr(str); if (ret === str) { - return "官方"; + return luatr("Official"); } return ret; } @@ -530,10 +530,10 @@ Item { const general = generalDetail.general; return [ luatr(lcall("GetGeneralData", general).package), - "称号:" + trans("#" + general), - "设计:" + trans("designer:" + general), - "配音:" + trans("cv:" + general), - "画师:" + trans("illustrator:" + general), + luatr("Title") + trans("#" + general), + luatr("Designer") + trans("designer:" + general), + luatr("Voice Actor") + trans("cv:" + general), + luatr("Illustrator") + trans("illustrator:" + general), ].join("
"); } } diff --git a/lua/client/i18n/en_US.lua b/lua/client/i18n/en_US.lua index f0b12a8f..4e9ef76f 100644 --- a/lua/client/i18n/en_US.lua +++ b/lua/client/i18n/en_US.lua @@ -134,8 +134,17 @@ Fk:loadTranslationTable({ -- ["Quit"] = "退出", ["BanGeneral"] = "Ban", ["ResumeGeneral"] = "Unban", + ["BanPackage"] = "Ban packages", + ["$BanPkgHelp"] = "Banning packages", + ["$BanCharaHelp"] = "Banning characters", -- ["Companions"] = "珠联璧合", -- ["Death audio"] = "阵亡", + -- ["Win audio"] = "胜利语音", + -- ["Official"] = "官方", + ["Title"] = "Title: ", + ["Designer"] = "Designer: ", + ["Voice Actor"] = "Voice Actor: ", + ["Illustrator"] = "Illustrator: ", ["$WelcomeToLobby"] = "Welcome to FreeKill lobby!", ["GameMode"] = "Game mode: ", diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index 76de68b0..acb33f1f 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -193,6 +193,12 @@ FreeKill使用的是libgit2的C API,与此同时使用Git完成拓展包的下 ["$BanCharaHelp"] = "正在禁用武将", ["Companions"] = "珠联璧合", ["Death audio"] = "阵亡", + ["Win audio"] = "胜利语音", + ["Official"] = "官方", + ["Title"] = "称号:", + ["Designer"] = "设计:", + ["Voice Actor"] = "配音:", + ["Illustrator"] = "画师:", ["$WelcomeToLobby"] = "欢迎进入新月杀游戏大厅!", ["GameMode"] = "游戏模式:", diff --git a/packages/standard/i18n/zh_CN.lua b/packages/standard/i18n/zh_CN.lua index ee58a4a7..f468b57c 100644 --- a/packages/standard/i18n/zh_CN.lua +++ b/packages/standard/i18n/zh_CN.lua @@ -8,6 +8,8 @@ Fk:loadTranslationTable{ ["qun"] = "群", ["caocao"] = "曹操", + ["#caocao"] = "魏武帝", + ["illustrator:caocao"] = "KayaK", ["~caocao"] = "霸业未成!未成啊!", ["$jianxiong1"] = "宁教我负天下人,休教天下人负我!", ["$jianxiong2"] = "吾好梦中杀人!", @@ -20,6 +22,8 @@ Fk:loadTranslationTable{ ["#hujia-ask"] = "护驾:你可打出一张闪,视为 %src 使用或打出", ["simayi"] = "司马懿", + ["#simayi"] = "狼顾之鬼", + ["illustrator:simayi"] = "KayaK", ["~simayi"] = "难道真是天意难违?", ["$guicai1"] = "天命?哈哈哈哈……", ["$guicai2"] = "吾乃天命之子!", @@ -32,6 +36,8 @@ Fk:loadTranslationTable{ [":fankui"] = "当你受到伤害后,你可以获得伤害来源的一张牌。", ["xiahoudun"] = "夏侯惇", + ["#xiahoudun"] = "独眼的罗刹", + ["illustrator:xiahoudun"] = "KayaK", ["~xiahoudun"] = "两边都看不见了……", ["$ganglie1"] = "鼠辈,竟敢伤我!", ["$ganglie2"] = "以彼之道,还施彼身!", @@ -39,6 +45,8 @@ Fk:loadTranslationTable{ [":ganglie"] = "当你受到伤害后,你可以进行判定:若结果不为红桃,则伤害来源选择一项:弃置两张手牌,或受到1点伤害。", ["zhangliao"] = "张辽", + ["#zhangliao"] = "前将军", + ["illustrator:zhangliao"] = "KayaK", ["~zhangliao"] = "真的没想到……", ["$tuxi1"] = "哼,没想到吧!", ["$tuxi2"] = "拿来吧!", @@ -47,6 +55,8 @@ Fk:loadTranslationTable{ ["#tuxi-ask"] = "是否发动“突袭”,改为获得1-2名角色各一张手牌?", ["xuchu"] = "许褚", + ["#xuchu"] = "虎痴", + ["illustrator:xuchu"] = "KayaK", ["~xuchu"] = "冷,好冷啊……", ["$luoyi1"] = "脱!", ["$luoyi2"] = "谁来与我大战三百回合?", @@ -54,6 +64,8 @@ Fk:loadTranslationTable{ [":luoyi"] = "摸牌阶段,你可以少摸一张牌,然后本回合你使用【杀】或【决斗】对目标角色造成伤害时,此伤害+1。", ["guojia"] = "郭嘉", + ["#guojia"] = "早终的先知", + ["illustrator:guojia"] = "KayaK", ["~guojia"] = "咳,咳……", ["$tiandu1"] = "就这样吧。", ["$tiandu2"] = "哦?", @@ -67,6 +79,8 @@ Fk:loadTranslationTable{ ["#yiji-give"] = "遗计:你可以将这些牌分配给任意角色,点“取消”自己保留", ["zhenji"] = "甄姬", + ["#zhenji"] = "薄幸的美人", + ["illustrator:zhenji"] = "KayaK", ["~zhenji"] = "悼良会之永绝兮,哀一逝而异乡。", ["$luoshen1"] = "髣髴兮若轻云之蔽月。", ["$luoshen2"] = "飘飖兮若流风之回雪。", @@ -79,6 +93,8 @@ Fk:loadTranslationTable{ [":qingguo"] = "你可以将一张黑色手牌当【闪】使用或打出。", ["liubei"] = "刘备", + ["#liubei"] = "乱世的枭雄", + ["illustrator:liubei"] = "KayaK", ["~liubei"] = "这就是桃园吗?", ["$rende1"] = "以德服人。", ["$rende2"] = "唯贤唯德,能服于人。", @@ -91,6 +107,8 @@ Fk:loadTranslationTable{ ["#jijiang-ask"] = "激将:你可打出一张杀,视为 %src 使用或打出", ["guanyu"] = "关羽", + ["#guanyu"] = "美髯公", + ["illustrator:guanyu"] = "KayaK", ["~guanyu"] = "什么?此地名叫麦城?", ["$wusheng1"] = "关羽在此,尔等受死!", ["$wusheng2"] = "看尔乃插标卖首!", @@ -98,6 +116,8 @@ Fk:loadTranslationTable{ [":wusheng"] = "你可以将一张红色牌当【杀】使用或打出。", ["zhangfei"] = "张飞", + ["#zhangfei"] = "万夫不当", + ["illustrator:zhangfei"] = "KayaK", ["~zhangfei"] = "实在是,杀不动了……", ["$paoxiao1"] = "啊~~~!", ["$paoxiao2"] = "燕人张飞在此!", @@ -105,6 +125,8 @@ Fk:loadTranslationTable{ [":paoxiao"] = "锁定技,出牌阶段,你使用【杀】无次数限制。", ["zhugeliang"] = "诸葛亮", + ["#zhugeliang"] = "迟暮的丞相", + ["illustrator:zhugeliang"] = "KayaK", ["~zhugeliang"] = "将星陨落,天命难违。", ["$guanxing1"] = "观今夜天象,知天下大事。", ["$guanxing2"] = "知天易,逆天难。", @@ -116,6 +138,8 @@ Fk:loadTranslationTable{ [":kongcheng"] = "锁定技,若你没有手牌,你不能被选择为【杀】或【决斗】的目标。", ["zhaoyun"] = "赵云", + ["#zhaoyun"] = "少年将军", + ["illustrator:zhaoyun"] = "KayaK", ["~zhaoyun"] = "这,就是失败的滋味吗?", ["$longdan1"] = "能进能退乃真正法器!", ["$longdan2"] = "吾乃常山赵子龙也!", @@ -123,6 +147,8 @@ Fk:loadTranslationTable{ [":longdan"] = "你可以将一张【杀】当【闪】使用或打出,或将一张【闪】当普通【杀】使用或打出。", ["machao"] = "马超", + ["#machao"] = "一骑当千", + ["illustrator:machao"] = "KayaK", ["~machao"] = "(马蹄远去声)", ["mashu"] = "马术", [":mashu"] = "锁定技,你与其他角色的距离-1。", @@ -132,6 +158,8 @@ Fk:loadTranslationTable{ [":tieqi"] = "当你指定【杀】的目标后,你可以进行判定:若结果为红色,该角色不能使用【闪】响应此【杀】。", ["huangyueying"] = "黄月英", + ["#huangyueying"] = "归隐的杰女", + ["illustrator:huangyueying"] = "KayaK", ["~huangyueying"] = "亮……", ["$jizhi1"] = "哼哼~", ["$jizhi2"] = "哼~", @@ -141,6 +169,8 @@ Fk:loadTranslationTable{ [":qicai"] = "锁定技,你使用锦囊牌无距离限制。", ["sunquan"] = "孙权", + ["#sunquan"] = "年轻的贤君", + ["illustrator:sunquan"] = "KayaK", ["~sunquan"] = "父亲,大哥,仲谋愧矣……", ["$zhiheng1"] = "容我三思。", ["$zhiheng2"] = "且慢。", @@ -152,6 +182,8 @@ Fk:loadTranslationTable{ [":jiuyuan"] = "主公技,其他吴势力角色使用【桃】令你回复体力时,回复值+1。", ["ganning"] = "甘宁", + ["#ganning"] = "锦帆游侠", + ["illustrator:ganning"] = "KayaK", ["~ganning"] = "二十年后,又是一条好汉……", ["$qixi1"] = "接招吧!", ["$qixi2"] = "你的牌太多啦!", @@ -159,6 +191,8 @@ Fk:loadTranslationTable{ [":qixi"] = "你可以将一张黑色牌当【过河拆桥】使用。", ["lvmeng"] = "吕蒙", + ["#lvmeng"] = "白衣渡江", + ["illustrator:lvmeng"] = "KayaK", ["~lvmeng"] = "被看穿了吗……", ["$keji1"] = "不是不报,时候未到!", ["$keji2"] = "留得青山在,不怕没柴烧!", @@ -166,6 +200,8 @@ Fk:loadTranslationTable{ [":keji"] = "若你未于出牌阶段内使用或打出【杀】,你可以跳过弃牌阶段。", ["huanggai"] = "黄盖", + ["#huanggai"] = "轻身为国", + ["illustrator:huanggai"] = "KayaK", ["~huanggai"] = "失血……过多了……", ["$kurou1"] = "请鞭笞我吧,公瑾!", ["$kurou2"] = "赴汤蹈火,在所不辞!", @@ -173,6 +209,8 @@ Fk:loadTranslationTable{ [":kurou"] = "出牌阶段,你可以失去1点体力然后摸两张牌。", ["zhouyu"] = "周瑜", + ["#zhouyu"] = "大都督", + ["illustrator:zhouyu"] = "KayaK", ["~zhouyu"] = "既生瑜,何生……", ["$yingzi1"] = "哈哈哈哈……", ["$yingzi2"] = "汝等看好了!", @@ -184,6 +222,8 @@ Fk:loadTranslationTable{ [":fanjian"] = "阶段技。你可以令一名其他角色选择一种花色,然后正面朝上获得你的一张手牌。若此牌花色与该角色所选花色不同,你对其造成1点伤害。", ["daqiao"] = "大乔", + ["#daqiao"] = "矜持之花", + ["illustrator:daqiao"] = "KayaK", ["~daqiao"] = "伯符,我去了……", ["$guose1"] = "请休息吧。", ["$guose2"] = "你累了。", @@ -196,6 +236,8 @@ Fk:loadTranslationTable{ ["#liuli-target"] = "流离:你可以弃置一张牌,将【杀】的目标转移给一名其他角色", ["luxun"] = "陆逊", + ["#luxun"] = "儒生雄才", + ["illustrator:luxun"] = "KayaK", ["~luxun"] = "我还是太年轻了……", ["$qianxun1"] = "儒生脱尘,不为贪逸淫乐之事。", ["$qianxun2"] = "谦谦君子,不饮盗泉之水。", @@ -207,6 +249,8 @@ Fk:loadTranslationTable{ [":lianying"] = "当你失去最后的手牌后,你可以摸一张牌。", ["sunshangxiang"] = "孙尚香", + ["#sunshangxiang"] = "弓腰姬", + ["illustrator:sunshangxiang"] = "KayaK", ["~sunshangxiang"] = "不,还不可以死……", ["$xiaoji1"] = "哼!", ["$xiaoji2"] = "看我的厉害!", @@ -218,6 +262,8 @@ Fk:loadTranslationTable{ [":jieyin"] = "出牌阶段限一次,你可以弃置两张手牌并选择一名已受伤的男性角色:若如此做,你和该角色各回复1点体力。", ["huatuo"] = "华佗", + ["#huatuo"] = "神医", + ["illustrator:huatuo"] = "KayaK", ["~huatuo"] = "医者……不能自医啊……", ["$qingnang1"] = "早睡早起,方能养生。", ["$qingnang2"] = "越老越要补啊。", @@ -229,6 +275,8 @@ Fk:loadTranslationTable{ [":jijiu"] = "你的回合外,你可以将一张红色牌当【桃】使用。", ["lvbu"] = "吕布", + ["#lvbu"] = "武的化身", + ["illustrator:lvbu"] = "KayaK", ["~lvbu"] = "不可能……!", ["$wushuang1"] = "谁能挡我!", ["$wushuang2"] = "神挡杀神,佛挡杀佛!", @@ -236,11 +284,13 @@ Fk:loadTranslationTable{ [":wushuang"] = "锁定技,当你使用【杀】指定目标后,其使用【闪】抵消此【杀】的方式改为需连续使用两张【闪】;当你使用【决斗】指定目标后,或当你成为【决斗】的目标后,你令其打出【杀】响应此【决斗】的方式改为需连续打出两张【杀】。", ["diaochan"] = "貂蝉", + ["#diaochan"] = "绝世的舞姬", + ["illustrator:diaochan"] = "KayaK", ["~diaochan"] = "父亲大人,对不起……", ["$lijian1"] = "嗯呵呵~~呵呵~~", ["$lijian2"] = "夫君,你要替妾身作主啊……", ["lijian"] = "离间", - [":lijian"] = "出牌阶段限一次,你可以弃置一张牌并选择两名其他男性角色,后选择的角色视为对先选择的角色使用了一张不能被无懈可击的决斗。", + [":lijian"] = "出牌阶段限一次,你可以弃置一张牌并选择两名其他男性角色,后选择的角色视为对先选择的角色使用了一张不能被【无懈可击】的【决斗】。", ["$biyue1"] = "失礼了~", ["$biyue2"] = "羡慕吧~", ["biyue"] = "闭月", diff --git a/packages/standard/init.lua b/packages/standard/init.lua index aaea8820..2d1d5c7e 100644 --- a/packages/standard/init.lua +++ b/packages/standard/init.lua @@ -225,6 +225,21 @@ local tiandu = fk.CreateTriggerSkill{ player.room:obtainCard(player.id, data.card, true, fk.ReasonJustMove) end, } +local yiji_active = fk.CreateActiveSkill{ + name = "yiji_active", + expand_pile = function(self) + return type(Self:getMark("yiji_cards")) == "table" and Self:getMark("yiji_cards") or {} + end, + min_card_num = 1, + target_num = 1, + card_filter = function(self, to_select, selected, targets) + local ids = Self:getMark("yiji_cards") + return type(ids) == "table" and table.contains(ids, to_select) + end, + target_filter = function(self, to_select, selected, selected_cards) + return #selected == 0 and to_select ~= Self.id + end, +} local yiji = fk.CreateTriggerSkill{ name = "yiji", anim_type = "masochism", @@ -246,77 +261,32 @@ local yiji = fk.CreateTriggerSkill{ on_use = function(self, event, target, player, data) local room = player.room local ids = room:getNCards(2) - local fakemove = { - toArea = Card.PlayerHand, - to = player.id, - moveInfo = table.map(ids, function(id) return {cardId = id, fromArea = Card.Void} end), - moveReason = fk.ReasonJustMove, - } - room:notifyMoveCards({player}, {fakemove}) - for _, id in ipairs(ids) do - room:setCardMark(Fk:getCardById(id), "yiji", 1) - end - player.tag["yiji_ids"] = ids --存储遗技卡牌表 - while table.find(ids, function(id) return Fk:getCardById(id):getMark("yiji") > 0 end) do - if not room:askForUseActiveSkill(player, "yiji_active", "#yiji-give", true) then - for _, id in ipairs(ids) do - room:setCardMark(Fk:getCardById(id), "yiji", 0) + while true do + room:setPlayerMark(player, "yiji_cards", ids) + local _, ret = room:askForUseActiveSkill(player, "yiji_active", "#yiji-give", true, nil, true) + room:setPlayerMark(player, "yiji_cards", 0) + if ret then + for _, id in ipairs(ret.cards) do + table.removeOne(ids, id) end - ids = table.filter(ids, function(id) return room:getCardArea(id) ~= Card.PlayerHand end) - fakemove = { - from = player.id, - toArea = Card.Void, - moveInfo = table.map(ids, function(id) return {cardId = id, fromArea = Card.PlayerHand} end), - moveReason = fk.ReasonGive, - } - room:notifyMoveCards({player}, {fakemove}) - room:moveCards({ - fromArea = Card.Void, - ids = ids, - to = player.id, - toArea = Card.PlayerHand, - moveReason = fk.ReasonGive, - skillName = self.name, - }) + room:moveCardTo(ret.cards, Card.PlayerHand, room:getPlayerById(ret.targets[1]), fk.ReasonGive, self.name, nil, false, player.id) + if #ids == 0 then break end + if player.dead then + room:moveCards({ + ids = ids, + toArea = Card.DiscardPile, + moveReason = fk.ReasonJustMove, + skillName = self.name, + }) + break + end + else + room:moveCardTo(ids, Player.Hand, player, fk.ReasonGive, self.name, nil, false, player.id) + break end end end, } -local yiji_active = fk.CreateActiveSkill{ - name = "yiji_active", - mute = true, - min_card_num = 1, - target_num = 1, - card_filter = function(self, to_select, selected, targets) - return Fk:getCardById(to_select):getMark("yiji") > 0 - end, - target_filter = function(self, to_select, selected, selected_cards) - return #selected == 0 - end, - on_use = function(self, room, effect) - local player = room:getPlayerById(effect.from) - local target = room:getPlayerById(effect.tos[1]) - room:doIndicate(player.id, {target.id}) - for _, id in ipairs(effect.cards) do - room:setCardMark(Fk:getCardById(id), "yiji", 0) - end - local fakemove = { - from = player.id, - toArea = Card.Void, - moveInfo = table.map(effect.cards, function(id) return {cardId = id, fromArea = Card.PlayerHand} end), - moveReason = fk.ReasonGive, - } - room:notifyMoveCards({player}, {fakemove}) - room:moveCards({ - fromArea = Card.Void, - ids = effect.cards, - to = target.id, - toArea = Card.PlayerHand, - moveReason = fk.ReasonGive, - skillName = self.name, - }) - end, -} local guojia = General:new(extension, "guojia", "wei", 3) Fk:addSkill(yiji_active) guojia:addSkill(tiandu) From 851a3873fac68004ae61302f56fc928e5a3f0284 Mon Sep 17 00:00:00 2001 From: notify Date: Mon, 5 Feb 2024 11:07:54 +0800 Subject: [PATCH 18/30] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=9F=B3=E9=A2=91=20(#?= =?UTF-8?q?317)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Fk/Common/AvatarChatBox.qml | 2 +- Fk/Common/ChatBox.qml | 2 +- Fk/Pages/Room.qml | 2 ++ audio/system/gamestart.mp3 | Bin 0 -> 42130 bytes audio/system/ready.mp3 | Bin 0 -> 16054 bytes packages/standard/audio/skill/fastchat_f17.mp3 | Bin 0 -> 40169 bytes packages/standard/audio/skill/fastchat_f18.mp3 | Bin 0 -> 40796 bytes packages/standard/audio/skill/fastchat_f19.mp3 | Bin 0 -> 25436 bytes packages/standard/audio/skill/fastchat_f20.mp3 | Bin 0 -> 17912 bytes packages/standard/audio/skill/fastchat_f21.mp3 | Bin 0 -> 21674 bytes packages/standard/audio/skill/fastchat_f22.mp3 | Bin 0 -> 32332 bytes packages/standard/audio/skill/fastchat_f23.mp3 | Bin 0 -> 25122 bytes packages/standard/audio/skill/fastchat_m17.mp3 | Bin 0 -> 53334 bytes packages/standard/audio/skill/fastchat_m18.mp3 | Bin 0 -> 49259 bytes packages/standard/audio/skill/fastchat_m19.mp3 | Bin 0 -> 29511 bytes packages/standard/audio/skill/fastchat_m20.mp3 | Bin 0 -> 25749 bytes packages/standard/audio/skill/fastchat_m21.mp3 | Bin 0 -> 25436 bytes packages/standard/audio/skill/fastchat_m22.mp3 | Bin 0 -> 42363 bytes packages/standard/audio/skill/fastchat_m23.mp3 | Bin 0 -> 26063 bytes packages/standard/i18n/zh_CN.lua | 14 ++++++++++++++ 20 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 audio/system/gamestart.mp3 create mode 100644 audio/system/ready.mp3 create mode 100644 packages/standard/audio/skill/fastchat_f17.mp3 create mode 100644 packages/standard/audio/skill/fastchat_f18.mp3 create mode 100644 packages/standard/audio/skill/fastchat_f19.mp3 create mode 100644 packages/standard/audio/skill/fastchat_f20.mp3 create mode 100644 packages/standard/audio/skill/fastchat_f21.mp3 create mode 100644 packages/standard/audio/skill/fastchat_f22.mp3 create mode 100644 packages/standard/audio/skill/fastchat_f23.mp3 create mode 100644 packages/standard/audio/skill/fastchat_m17.mp3 create mode 100644 packages/standard/audio/skill/fastchat_m18.mp3 create mode 100644 packages/standard/audio/skill/fastchat_m19.mp3 create mode 100644 packages/standard/audio/skill/fastchat_m20.mp3 create mode 100644 packages/standard/audio/skill/fastchat_m21.mp3 create mode 100644 packages/standard/audio/skill/fastchat_m22.mp3 create mode 100644 packages/standard/audio/skill/fastchat_m23.mp3 diff --git a/Fk/Common/AvatarChatBox.qml b/Fk/Common/AvatarChatBox.qml index d089ee43..84f757ac 100644 --- a/Fk/Common/AvatarChatBox.qml +++ b/Fk/Common/AvatarChatBox.qml @@ -29,7 +29,7 @@ Rectangle { } function loadSkills() { - for (let i = 1; i <= 16; i++) { + for (let i = 1; i <= 23; i++) { skills.append({ name: "fastchat_m", idx: i }); } } diff --git a/Fk/Common/ChatBox.qml b/Fk/Common/ChatBox.qml index c5a5d041..417192f6 100644 --- a/Fk/Common/ChatBox.qml +++ b/Fk/Common/ChatBox.qml @@ -13,7 +13,7 @@ Rectangle { } function loadSkills() { - for (let i = 1; i <= 16; i++) { + for (let i = 1; i <= 23; i++) { skills.append({ name: "fastchat_m", idx: i }); } } diff --git a/Fk/Pages/Room.qml b/Fk/Pages/Room.qml index caf6b187..fc907544 100644 --- a/Fk/Pages/Room.qml +++ b/Fk/Pages/Room.qml @@ -73,6 +73,7 @@ Item { onIsStartedChanged: { if (isStarted) { + Backend.playSound("./audio/system/gamestart"); bgm.play(); canKickOwner = false; kickOwnerTimer.stop(); @@ -209,6 +210,7 @@ Item { canKickOwner = false; kickOwnerTimer.stop(); } else { + Backend.playSound("./audio/system/ready"); kickOwnerTimer.start(); } } diff --git a/audio/system/gamestart.mp3 b/audio/system/gamestart.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..45b3057b5854cf5f42fd76ac8c5ff6ecc1ab200a GIT binary patch literal 42130 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 literal 0 HcmV?d00001 diff --git a/packages/standard/audio/skill/fastchat_f17.mp3 b/packages/standard/audio/skill/fastchat_f17.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..08d7299fb7e432fe1e49278c5eee0b5c3931a2ce GIT binary patch literal 40169 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 literal 0 HcmV?d00001 diff --git a/packages/standard/audio/skill/fastchat_f18.mp3 b/packages/standard/audio/skill/fastchat_f18.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..0d551803f03bb2cd5d0c5abfe5714ab2691d64fa GIT binary patch literal 40796 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 literal 0 HcmV?d00001 diff --git a/packages/standard/audio/skill/fastchat_f19.mp3 b/packages/standard/audio/skill/fastchat_f19.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..e30bcc4eafab78edb21c4f5b918bcf7c0a3efa0d GIT binary patch literal 25436 zcmd>`cTiK`+u##I2@sJQLN)Xbp-Gp}d+$|x2k8O|gx& uSn?q&E>z={4}BcNC-u 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

ARvsC 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 AJ^%m! literal 0 HcmV?d00001 diff --git a/packages/standard/audio/skill/fastchat_f20.mp3 b/packages/standard/audio/skill/fastchat_f20.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..b3079cca7a6f734231672681bd88fd52d0920313 GIT binary patch literal 17912 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 a$>SmZD?jo7dFB4o2W$Knl>2{Nxc?7`>)I9o literal 0 HcmV?d00001 diff --git a/packages/standard/audio/skill/fastchat_f21.mp3 b/packages/standard/audio/skill/fastchat_f21.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..51aec32dee8bcb3c757be60dea6fa7d66b0a5e0f GIT binary patch literal 21674 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%0Dio{^dl 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 Ie~Xrtb)5+fdU1J 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 p7dPGi3EY1jxc^zJ|6S<*2f6<)ZkV@Z! literal 0 HcmV?d00001 diff --git a/packages/standard/audio/skill/fastchat_m18.mp3 b/packages/standard/audio/skill/fastchat_m18.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..3c0e4373531a4c09d908b9d0c82cc6362b0f78d3 GIT binary patch literal 49259 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�(s(FbX-=O{4zy{G)-QXsz|eTXTHe1X2xuG90q2?oem=#7rQe zG%&!=><)i?bO}8rVa8`ZX=cLMD%0bGLP-tx>BwDeZv6KJJTqB>BM)XWVC5HQ8WXqf z0T7Kt3ORczI{!oD)IsNjvLhz|qK#s4KV!k{3?D?XJwJ(n$HeixJS5Th4E!#fi_UBe z2qf3JO1&kA)^DbZ896UdRPZQdCZqvmST0DAOSPk+{rvRFu6`3y47f;~Zt4!6=JhyO zINH#6>6bn{QOt34d^bdG>HQSDJ3NlexWWOD?usHXG@JY)QNE~7kjk*bXlV(90Al!K zK|oN~Cv_gw;iTTX;z}cA&U`hDDKLQbl{D_;kpXAwcGA=d$J0ke7CntROAZhCfIk9G z?SuCdkKS4=iAUh(wy$CScb+soo7)QiL!i7Nvi739{?9lmpqvs)4q*f0Pq-RXZIw6} z%#EL7To;Y(eTIKZ{p-b`de`BMVRh)b-!o$K;kHF>?xT;Wf{(Sz&nVO#?#I22D2-Q> z$!fXNaYTKD_p3=Y9>h4}wsu`xmK{+DL`oYT7KVfP$#b=MOrQR-q>+MBDa>1B%F4n$ z{adGMZpn%31kxq!{!I?a>GJbmGn;m903&ejI-r{d-tutg=nyx>?){W_dz)HyoSHKK z_>@o;Vf67?uvO-OfeF^gR(HkB2KtJN`1g`DA;^`SL>LPYALqi7QY(q+z2Lmh+)z>t z6V-hi6NELbIILZA`SZxoPU~LCx|i;1=8Y7KSK#B+3K(oRPSTTd6e|Zrr>mCloRIQJ z&;_o+t@J2e$nRk6=O7+qcjp+JJ{_LQ&26(o6#@Cy;(k9}M zq_v5FH!=W3%9}ZRtO9IZ+qT_j*B&vPSXky)?kFtDZfKVk8!sH|EbI}KFcD$A#=W@t zar$462U%<&JOOvUW}7M!`pl4i2u>h+{&)z0M*H<7f2$(H3SpUf9y85jyZ1-MKXiJa z@pv(DpzSvIJ2DDJRfo)-;;}7%)yqzoTshYU&8SlIDARF0P zhaci(B`s1$yDpvQXC=qgSDi$Ak&BhiKGm^AjKGShsRE7^--^(pE)5_-1=6xN9Y?>rHiI;mxP7<`zrO zKi#%fah+-8YXpdYMP-PaS8tsQ@XK2q<`-=b@b@7})f_1DT`|3jj{K z@e;VF_>I+NX_i2JQH12_nDKR*5khv3z^O=jM-4_-A|f+z_$=`0-D7`yRlU1HTEeYH_ZtQyaSnaxn=gXx2== zo*326!+|UY&XL@Ho-SyTp|kb6j3}VW}~5t>SeWFgQ}Q3I-sAP!Ul; z2iD!kq2kFD8PS=MkMHD`Tvm$5VExR(6%UcJ34Tcbh{`3^0I`uiH82m^U7dWf@;*KJ z!eN~}0oM5U`fC0^9$a+D2t2nmo!Ut>x3Ybz%l#p?833fK2FZ3IselBi-T4KSkaiV+ z+*=wM5mhQ-df}TV3eAN00dFOhH^_POVDG0ggl?g|8EcNJgz?- z3HFogsnPf+XAXQnqOx%RVMjh%;J5m=H!>f#-rshuc+hf)RlBohJurJsPG`zolrJc+ zxw-hTA)q(q@G9E^?;0<$gMwAmvj#cwhg-VgmO0Z>epH^Ctm^W(W6TBIaL-|=`d3lU zg1K(V394n&)d1FBrempSUwZTZjxG*bZtvNN8<^cb(-4ZS2SCMX7#L(FlycE&zax$N zg{10dD)JJsWwi@`1dR7Mgyb+Ae3Mt0hNKdG5j(-=66bJ@!0xDP_kbYz)c9h9P*L}G zhPpSnaD(qxsc&BH9R2wnpiu&?+4YOch9;ZB)oz#Mmw%md^w9-k?aZW8(cjwa$UmsgwtfYLDvWN@7^zb-0E;j zeY?pFmkawk^>#4WO7Z4TbBD9oc<@_$79~!oUfZ+xr?lwTjhhcB6(te|nl~9;`Qf`5 zH?NHjnY=kSCsVPamv+CujOU%L9+_8PS!^_q$Nn4Yvtyga&0975x@XBkpo(z@W2LOB zqkWcjOHM~qlCAniac(^R-iP_T)^XIGbd;4&i{mG{%-w};9;(v_ZwCT#2 z^eq@~7UXC!(`|fcg5y^551v!q@_O4C( zU7x&%{e#{mw#JfAOFd>DyuJ#1ujs`yrKa`G@=%mU#w($9eEU+TmUnx=wP|Np|B4$X z^RQ3hikU8MTQqK{rTJ72QTdOZ&}6zVm9d)iu)YA|&rjIUYD0`M z4NK~fBHG&1FP!+pi(cUTS0Q$ROAEJ8C`Q5N`sIylxxo}ckfLsEl7wUVITz^~Lq9#& zk3v$T$E*hv!eXg4jmTO`8Y3FgxFUPbT9F<-XleF$DW(@;DZ_V?X0OP?*0Ks__ibKe z4>h=o2N74+Bb_fvx97R_G+v)69|^Ng$~EcMUB#K@NG2P(|OsNHpG(%e@mpSIVx-HX*mKPa9YNBrz%>idCJV2*@oO)t4h%;P$d_iHnRUlIG52 zxqjAE{tNI{Tk;UcGP8?UWRkqz-kA$KHwPv*KTV61S(Tu=_zCWS99MuKCNRR=&vRNP zsB;$S`zJ^i%+87ke`1>c*^95>VTgDfTnV8j<^jtSTXC|vYjQ2K0W@J!_#=lXoQK%V zB9A%smM@{0;<3v|)D2>+xu0t1$sMiV@VOILx6fZ~f=P(u>x0#Q>r^T@oa7)%XxG_j zq6s?hesISbXdt9y@v356rRrGbg+%ap=GmUoWPA<}-$2V_t%3rv>_hiotzzVye}1Ny z>mt)MsN%RktD;q&uTB{dxBujs*iA3-d_ij!^^i~N)O2j>jr_QCKGTMm5=Qkxd8slX zIGW}G^^aR}6RO;Fl}HIT+33qoj;J3@3C7-eyfj?-jVuJ4e21U&o>YTO)s`w4SPow+ zYm`{0%FLab)oYiH$`mF3D$Ym5t^nexzw+3V2nnBd`ZRvmbp}rxqO?P3xSzjTTt5W- zen3B!J?FnXz7}W4Ne`!kI^#!hyD=pi>T78E?|HTfVi0*9NWk`WZvU$AxuyE}-IF%@ zhOsf+4`W`T2h3GS1GU+GRKe7kMwpbaRYi?ygcY6MoKAi0kAM8fZAsoiX1*z`T=$BY$UQ^Z4UC-z|AlW$T9G_wJl?bHU^A!u_gL}mbKkSO^6d@G{oTzChI-zJ3OAXGQPO(Vz89z?_z^NvK4nFrC=FCY0LQ85P5^j8Kool~Rs|tOc&!IUx#OMmtP!t(+I0CH zj806v;`LeiXe7oD@iSnQYph}FS;fHn;5}o3qPg?-v0D1aX06|<;^GtMx^HLG% z1h&Hz3DHl?jt^DGUJ5 zbm}>rdQL*1s74lazWEd-fy}W|G9I~ukh7iQ1QjjxAZN^!qzJy9q&&0NfNhC z0uIB%0Hl#AcI61t*l{9i0E74CeU^?Ve6Hi1`OfeQh$`OD+E1X5h77LrRhArHJsUWO zCz$uk{W#mswPSb47w?fKjzkC43#_p3W(xAcd6_U1|nda_khL zXh>zxH^qrWS#}){>?#4o53>S@)<@0KiSefxRly4Ynu@S5&T6EwDz4@BV=)jgf(4sC z1vUJ9I3sz5uTo{CJArWng2Kw@?~$)G)*t2TE%+BU(Mvhte<@WsW*7F?(0n)xL?vsm zakrp38pitzMlNJ@60;w3>r^r)ECjulAv>dZx1=m=X~;fep^A%25^kg&{yWE7K5?U; zO&0?=E`4pIC?7GI_>Xu^7OJe1IsIKc5z`H8=i{5{n^&R#KD!C3`f%D(7|du7UHO#D z>q@I@3cmc&o1>fKT2m5WMye4q*U zG3I$CqMafe*VJE+amWE`Ty`6h)OHURd0?5BcfK9I%exE&zc5J{Wse4dmL)-S;P_>C z8Z?|W9|r=_toKcZ^&1Jx0BVf1k*s&oM54 z6g--#!o20kLibYK*#pAO35iG52WCp{Q7>=P3X~8t(P`u2Eg7M?hKJTI$#=|;XkSsY z3q82gKcY&kozsU&n95H zhw8@#fx_m;G?t&31$TP%9=u=gXDqc+`M}gbfpMDo7U+IPbvBU`hNyeM@f2Pg0!7fI zZRo2!i(SKBzvca!C9yFhh`Fm?(5m3!@G}vt4}@Gb6MmqF7U!yktI6GF=%KY07m5r!7J62YN|Nlc%v{FMo@K~ znuz;($t+><-z^Z9k$>CgC zTRyo4ZS?fo>dVzM0AVS7YD>?4f!6jl)t#9d$$d`Br=S-@ z=JA}CNc@9ph8dB{eFn`esy5VQ{uthU3@0LK74VfuCr#5`wh0|(u4%tTSeuU_NQLCp zZadYsoTTASz)P&=tobK<76)KXNC-4rVlcAs0Fdp z$#T?g*u@o#_5n4wWIOKu+4JyEm}3toiC^R2QwxRfonFM1Zm>6pOjlmKyCpYeAoyhF)4e`feqZa#^L||gtp%y; zr7-``p2(?D!c}NWz9cm(_X{0QSR8pvfD7q@kfb7~Nfc)7A|!IadqCZ4y|yD6mRi@2{IL;Fh0L8{qq;6Fs#QlfNF|rYe_0Fb3~W(IFC} zXJ8h<0p%>&y97boLe6(l%Q0XjtRz1exKZO^lT>T(QsHAik%~e=M}gF&pf$V*U^ZL4 z8mfH&p_yls7=NJ>lmbyB0yy_SDw+G2A$8Vl-OqRd_eXdC*(t7S33jY$edtLm=Hq2v`~0Hi z^@HZl9g)nIIzsbbk|4~?yPNLGM>pTEIyaW4Z}{K+`!zP^olnFKuRVqB!omH_(BktT z4LoSWt37Acajamp#<8kPrwEHDLc!Qa_i%oe;|}-9qg2W#VGbXxzPYvNF~K0e3|^#& zXlM_OsC2seBLdcd)Bpk!z}()6s}K|b?Tf5RP!oa0;Ag}^tD>4iPmal{+0qqvV!~j$+ZFV|1&hUx+JwmyMDU{M*)oz2EQf;R5t3I}T z-5KV<;dOate=@eTILzxI*?42mIGL<)PeNkr_^on@-Y=h5k#3Jyi!jF7lW9Jp*`Lk_ z=GuNeXzx1wc;nymEiLS)P1`W;%L6ILCxBB!ND7~7-^lN-MP?Xsj9oGXJOarlnyeSuvN>WnMDF>daq&A~KIZGfP^X1tv!vE!S;-Ulov|U!X_`~w_bQGLH5daaB zM{;q4X@D?rzMmWbAJ7p8k2b?Q?GZ--OnIj;6q(?Ksx*HX&23wK=Y2@w4EQeHQ=8zc z7lL@7u4X)w!*a4H@QW3ovI?v1Q=|m;ae=37nPH??bcr&t_yqt1Dcrq{_YsDj2+Alz zc3&i_OW6FW0n*FNW^iDjv_`>};!-NJ5l;+7$z`-|63PHH%ae2rc>j!tI$TEhb9@|c zSC^&%pwOxgrtCnW z#NdSl9*Pr{1vLTydp|o=6_8H^g5bMgQ)+59pOFlU&^y41NM2o0v`YFI9DK2qi%9#Y ziAq!0L1445dfO^AKRKt63_w@LkbSC{dMK< zI+pu{U?u{2=k`rm1*9T*zQ?Pm9LW!#=VArM3D+B*T$D;)ziIxksHQhz2uvJM8F`Oz{jD z3S%VF-ga6K@n3eS*PxrVHGG=b>wo;_d0u`rJKx)$#2fv%;t2{@DGA0gxcy$uw?& zW+M%M)zbimV=@6mU{?>{lHW1)b&Xg{;~GrSEeQD-MMf&mjG?yxRH!m^-{+qPfrV$)TwnP)odQl5gkFbzwHBLPl~*=EbdEIC@j_ zd?RU6EWP>t>2-xRf)Zrx(CwFEmU0bGxi1khu&IU>ldZoR0eLAP78D&}R4)7T(y5mK z43X2xJ9>=obM%LraODMEOCkVSj!c2n3A6`*gElv+|GFh7p|~bef_uZTlZV;+;nP@m z-yj(GGh8>c$W+f>IQA!gnZjPP7a&^!cE(1s^C?)WUdrb6q;Tyv=>E23Xa`-0m4|B# z5kdj=%wov&ES@8u5;wwcjz-9@i|{Tbwg8L$$q<`aT2WW)DvV|5Y_2?kq{KKMy#&?D zsxF^(W}*?bE_-5nQhNZlVf^UgZV-8$o=?C=W@%qIaKp(AJE#;~;Ilm(8r=2klU0+| zD}mH|YAtTzucgDUpNlPhI{z6W^Tq$4)$i^2hMYflqo}!;`^6|?<8@5fXBebb$>};g zi6RTyJ^ipn(iJpV?Q*d$Uhd~P5}cz1pi0b$V7PQ(z87z>&D3#B_XCenzc!joswmV_ z5(Ax(Hr)Auy8+8lh#CdtuBJGlyg%;Fg@%omc;+12(BxCN!|)f}km4 zIghesfY|2PGBYB;FN49#DAdYykOeery+3c@ zYxt8X@Nj2^%h}cDsSJtDm)CmO_g&m!kL`uOFn%uUJ*}Tru1>-eO|2WMN^Y-6Oe~=M zRfs&qIvBtdNOnN_bC|?ut@a`*q7+~`GY%V_*1gByn32GrQjAS)<$vw)Q0>fn#RWq~ z{w$fIvB{CWCmYvJRi2l!T0xas3O9D4cZq6nystvZXuTws+u66fxzJanv0BSjZre6K zHKL8Qp2dROHD1(o$Zs-+Un`VI4k*YO^8}yU$IOsOJijHEuPj8DU%d%`{rHA!qr!MX zw|>Q=<>)G@nD%w9D135Nu`#j}$VoUWBArNN#(Q&fGx6qkZ;4ln;9Vx_sQft$`(2tB z-sUPV9xbZ~yUBIVbtJzo$jm z`%kZp*T&&AGOtFF*%6yT`*z;`S+MI{i2~G6|33%YcW%keqlMtyzdfwfz{nMRx@p3H1DvaSz zf6^EPpa*s&9lT2J6o*t@yuB^ZHL zhU`v6N!|ogV}~%0oH=}ZB${2Y8%at_%#IMpFPAYU1)c=3)Q1)*w9l}2KGl(|UY6*E zy%OdpF`y38oe)4$Wj_~dLDL!6s`2K`)^xA^aETYub+vtwST1QU*`m`$E}6@E(L2|? zwlhPLJ!~}Xu@}C}JM>0Pm#D90#E+*Tk>aUDDNXUx&3VuHJ0)#!UWmWc(QaZ|t?A`K z!B1x+*`=0Vt)M2;^+SKmM>aRdi8&lrMIA$6T@ZSnbW83#CW0;m^af!XeNx7p>|Mat z$ONp8$xaQ*3+4~>6cMpy?@HQP>oYG_;y-9Yu_5Fd0|)wMho$7jMEbekr-@ZkEQ*z@ z889S*BB3eeFc1wyOsssje1tZzd;YQz15U5m(WEQ>W09*SG9MJ_rINKZ>!ARecTR7p z+q5B;d_f+=z-w#XL)mBi@}ZrgJH`L0i#2`n1wFC{< zS#Xr8&-%JQ<8FTOwt1nCa9@$13e*sZj+|vrazA1*azIxk4y(^>42`U+vJ)|rlM<=E zFi&~y-=cMx+ffyD>SAOyC{D`OrX**_XXio1!3FE@L`(<_=a{D82!=BQAnh)tjJSCc`E)!=0_LCvCP?H~#9R{sfQmi= z3{@~0-biXixxFP}JV}!4l*)$UkdT9`qyxgF+j@kI5_k=D`XVo*uv@0!(2Lw5Co-XztGOjic;c5<+!tc9-PK}s5dRQs zhPMtyy-4?qFp1`4Q0Dzlw)`zmSHC@XXq8TBWk~M+(IS76yE^nixIhJJ75RL##HT#| z*qCPqN6BvP(p3bMwo`qi41fM`amafxP-4&b9yLszxb0r;)u1oULzGmTa>90dzHD3p z0Y5;Sw^CEdIVMSkI`j7*d0S>%y&q|&IkimFbzjiRX@NR8uH4kmQ!;=}f(2Y!#Xhq& z>%Gi(j&KaA(a6cNM$IB9iOT1$YY#^R&38CnI0KFymRLS}8hapyfcTGXZqL&BqELW#dfF8~I3e53ZG2^OZ4w?lMLF z&K{BV1e1bm`TnJZlN9Z6&c^l??VzQji){|ATXN?*Lv%T4S2k0)Ppnz!Rw*yH1)rTj zj=oO6=T1wGsFm&`4!!r%L*|19|9Ocn68m?& z2#JkPxbVV6Ho#mpgz~$kiO4g6P$Yp^K6Wft4M_lHM~jtnPjRimqD&IWmLcLmPJ+s? zo~6O_q6d#YDo{)Wy$#wS{xcs)SGjHmKdKpc$e>7P20-N=2?ZU9y%HV#wuhwgdQHF4 z(dlYij;)w$9GL(0GT-YXOK1w1fCngic@}&ik{KkkFt)+;>|b(Qi+tuvF%iSuj*AA< z9x?iL)EU$eg0^C)_gwC+WS(C3j7#s=m-~C6k^NN~&HU)PfOWtHn47&BZGl z(VJENZVGI*P}^v--duj7vBdMN>Gv(UGmT4lCjMZtdE3`h$dfmN>xrzR2^(Xp4aN4R zho)FdQ;(t8t3Xqu6ztK^^Bu&=D3XCj z2*i#wRTXd;I~I3^3Q5D&T!v)C*ZTRTXvgsxa;DVoL>%H4Q;gb9V=Bh%5c~>YnE~z2 zKyHS`!cY}_x)G6atU{iez)_-@2-nCc@BzY_fFeg7xPjCLRJsPNg<9r?QOZ^Vgf`1_ zPkOiVJXzpFgx!-G4-WsCqVK|u*T$AhveNX5GM*Y3G*CY=o)mqqMon=jqq*1G^U0SU z@a%)K;=jrn7B!K=I`R8vpux*t{!f}Gz8De9>cZ+lmeezuvhRa8H)U+!?m3d8QSI%6 z!n${S3l$8m-*0|x9J;wBcc~4fi$uR6H$4u&ck$}j;PYyPSnhOHZc1fPwWot!TpWX9 zXsm2m9ZR;+^Pd8rCzC4)h(Q1K>BHY;`Nj7Zbz%z=NFvPy4beiA2?W3q5nw7U5eau< zGiY3s&X9&pLUkP5BQxzUL~u?ac41Nm648lcBY|W_=g*ACIaaefQ^7n6iqi4WOs0zb z{lv+Z0>dB3^cRzO_9+Sh+z2~tjRz`f;_x6D#Yt$Y?K)>QOZ|5JMnxP$IFY|gRTurn zXI4@b0t%&FlE9iV=*j{KX=XIR0dDh0b6DLqSW1!LRhFQYPI~713K-341h0amby|cG zJB#ZeC2Atyzi{p1Pyfm(`&TTzqK2%--wySP+Po56a6i-j3|0|K!wnp%--rZP>yfLd zJn&2wA&63Y5h&d9%~;Q0Y_V@8zg2 zVCp1ZT$fJ5wF52X*~!d~?yG5l=epDmKqvEE6G3r*y%?%+gpq3^H7&FdNy;p`OGo#M%@ z@t&1awA}xMK^tz35D|wnUW5t1NECtdKkTf&ui<)V-Q6Fd1|Tg~_vpn3I3LjpK}X&q zisET#p?wRq%6vWr zHmLt@x(%|8BW9wpjn2XrpFl-SkJV(;sj-Rg;e&WYIo?;NprtC59jP;{G!`#d-!gMK z*;BynCl8#p-L};m-SB_^24`aQr@__;6>AfmPZ_0G_>XB{@O$S88l;aLHdoqv6rgKQ{hnZf5_Zg!XH)sP9agXvDdy zg4jXzi>iiH3yB_2fKzb{+?ao;5wungmK2F7C@F(o^HrguKG#q?mQ}nJv1-Fr zBm_z-=Omb4z(IItEX(cGg&fZ7s=7K!7J~)JNSHy5I=ak7aKzse!DKw)$xYj#4dJfs&8NLm(jWx`;>IY zQ~0nsUse(}e-(O`m{6*BOYWm)F;NWojhoGYB; zlM9VdxuTUdK8G&(caTFkS(SAq>Hkdb|4JOE@ymctqC|ceo5km`+)0?uVfnrgJJcM( zNUO*NSL8-pKIafeFFs%UQ9K1DgQ;_MIy#Ki7N8R#V$pU^4)i0!R;`X7#52&IF5?TM zukV*y5cyP3^Od-L`}?4fVr+8kb_}}=t+Bw@G;6qYH-u+zmi3zr_G|NK9FEbJs@_!p z@f!ZH-8>NBP@7szd#21#Sn{(Y-Lyt-k9#5$TTeU}5vWWo+3eh!b-5GV;cy|`V1gVODUO~WURN<=c_OXk-Px8!zI1BtR*H!ZJ* zIqf8RO`KxmG#-$UyxQ83g9=?03_DgnauiVbBtQNhyW`PiIX`@seRUTTg;jCkgsqgW z9TN~R>`n+3A^?9o9v~7LVtfopBN-fx_n#496KhY$5xJ^vb(oHcIQAe?^jyCQ7vvke zWRR?Z>$EC%Xxt16t26^cSJne}2$994Q=SY#Cf&CAkprAAlzFDw+(RICW)dsf@QcBG z6LFsxJqo>_3|dbPDMreO9QT`FWN74W=OzGq`M5_DjCJ$?YDt9f_P4FoQaAxff5BkV zpWxC-;bhwOL6({+VWb&t*sya$T0=UxMDK8d=Dk`eKLeemadBbn6MBA>-IJAW|E8`@ zCCNk|{m_?F%3cdQ_pI*g6xu!`SjB3S>Dds~iWV>covMyDJ+E)c{lOHSdjpR1|FDpH zQIFInu1L>s*qv-l9=y1SUUv_)TYOn(;P&c{jCAU}1mVqTh~VcpG#_3C`QlCQ=PU?P zNt>!s0T|qTc^RxLBmJsKz$iGbAu*Pk0ce%Ynx7cLoiIiKJwa|wMZV>pBa(G?XqmF7 zxu-_#9_5}$WC{{umL+w-xzYjLj$EI$y6tPxrMr2=i6k&!6>$!gDr5WjhWd{AA063B z@wEz%MI$V`_%%p*@}In?Xl2Ql%WvIy#B7!g(B`S1u^y42VkIVE7$)Cwd{eHgp#A?cR~T}wiV9P>>XqBb2ANVg~(;xC!EQFghlT z4g#xlJx;|SU^<0xvkL*~Iz#lFKpoBoi6Qs`?{ak8C51?2^x|m1J&Xr-eF**9C|O{> z0g)C{o^Me?RY(BD%lM>tQ0xLQUOO2=gJmujnsJ>IZgp{nDXspzWPy$=I*$&RRj+OC z52ePeS-MwuXskZjuJ%+3Ri|^GVrw%Yq-H27=+>|+Ox7zca2c!Le5@APDAE6Sz^1!z zD$WFr)bTwV;0EzEoLM+Q?z3L-p&R(MnG&_IvKp!+Opy@cGhlDcaBrT{Pl!r)oWJ;~ z_S4;aPi{8$=Y9LIVc!2T{p%m~oqxZ}F&CJVTqeS`60FUQcODte^-BShgjM5BSwiWp zt2xUM&zne|v@H4qP#->u=7zaD$e<4vZ7q@+8n*G{-RjlMPBXHD_h4$Q1bU%El= zN!9bq;*EJ@lBaV`R(p~KruwS_I{fX=AFJspe%e!~k({rbUzwf<8?m7ZO-`5kpSDqo z%oeiqJV9wY;TuXY^8~@%yp@t_S;5Zgy*8`7|G!tD4tJje<`- z69eWV{=}Ay62th9aEVe)g0P>)bU7^@%Zq zKT-Kl+VmuBB9(r$O}el=k&T!QOgCe`}0oL@HS1id@Sg;o1puJG#FQy$Rzy zJfm{v@81Tx6mPGbSG0gZ$L}wzh85CY<`Z1ZV*~Y#0!rK~7i&K_M~I*sMZ+il6}kW1 zlKkfwi(`6h?LTz(w3Xk+*Jk^teml9=$gkTC*8^xE!BL)Fsh!Rf__{@Bf6?9ewrY6(McBf^Mb4d3H^<2Yj(Z zuG7_y;VDBtw!CwR(x`{{Xc2-ww%}j^dmX4X<)y$zWQ{kcG!!jFr8a!XJTsc8+rQas z`diSs3@I6@*H)`hYV}agYCYuJR@Zde+HvIa`Zslq4&Ar<7V;^GrAgK~_rc_JN@aoa zd_B!2{H#bSQ$UM@K!yWE6Yg#482ckV^t)P%^NkAN?zsU*6B|n!yb_BRKRe6Ay?88R z0EBT8KPy<~sV%Z+wAs-|@|YCw@e5(V0)ki;2oUKWMj=}VU;DfeC$E(V$d?#<7t_<} z>mu^#f|HisovCa`cY7dR9#@3Iwa|1!gD)3LT5=Qo1Hj_B6ngK;nUWIHuRrEgeO^=# zox$e-GZCl$cA$bJ!~;azGg8f@2R&Nhj2ELMhKVSdY_?Fv@`VFbXR}rQmkzycu z!*oxBF+7EKA##|P>Jv&z77GT5>8SdC3k;1^m8jdNlm8qE4dNv}gPG&7dcR5#yoq-N z+lVt7F(DrF&#P)vCo*G|ZkG7;{Ta7CLGH*{1EK<+S*W(M*{(M8cmY+Fgm z$vEPKevN1P%I8$r?zU8w*QyD>zFV-OS&re=?_~%h%8|nK-5wdHC#(2?XwsGaZ;U)# zWtdYAU#I2EA+TJg_7}of3vGux?!_1ghktD^CcPa@eLED)5&v#`Mt4?ZXK15X+gxG* zj%LtLVnopk>dGXDW}TY~HxJsIuslKT6Jh-$1~Buyzu=YBhlh{em@Tz4x<0sFtaRWv zBDFQ+c_G%-lN6=1VVB?d&@Me8_34~&VEI+u2nGbs)x{Pg&mPIl^Ai}GBwu(MAVZ@Z zOQ{=m@bRNox9FQ+fJFM}1n8H}yS#C0lX z6lBh%?O-y%dDyB72JONy)Vn1PC|;R2ht6}Sbevir?EeJ#JzYz9xAptToUQyhvY};@ z0eRy`wi@KwGdw(H<*N`!DkI)RJrf^f0M{`W*1J?H>0o$^(zBPWZfx(cr9A>jn@nqc zH*B;fb;|Svxi#%7u6LY$^UIBM?`BgnSFtMu)6kat>45!U4xzR39&f*t0#%*R?)1W> zUH8e;co%FM#U%)F%fU6!dQC$Kq*Go3&5zI#>rMct$kFMigL&oLX37XUGS;``Qm}SO zfdPPGpiH|^c?42F3KFgdv_;R_)NA`dw$%`CRmU1=y|9s)p+4DXn~r-$04A zN%3ov8paYI7ws38{hnfc^D$6^Fs0AUaV0xJWjRbp3QjL>~*iPPvo24-?3JzNf z15lg@2*|4>f+mQQ-uwgRahae)@v2YNi$RyN!I{?)VO4mjz`J#x5Y-_w^2m+Ltqkcm z{U}!oMzi;budAeEzp6TJ2e>|wexk|{v{s;I?uJ5#s@Q0!(mx~cbX$-B;od$0bO>|+rn~G ztAM9LJ^-v+V{IPmOrIj7fR`=L+x~2uGS^v;-7+rUaX%LH6v%pCv%M^jH~nro-{I_> z>jBEEYHr|WOsNxL@yYh!i*j%DaT&HX9Ske69j&zdmbW3!PyS531aNZWO>ASX%!G=N z#U%JOcZBjKgi0q2`x;P6W`!}o3`G?T40nD(wsxk+ky0XIGyY4v(em|6oWFS*#`iMvG2g# zN#Kr%%UoqA52te}DHojZ?1}w?R~KxXeZl$Q&v^Dn*i;Aa)KqQ;0Up$vV26Q7^{$$z z5N}vRfqq?xF)>_T4;}<8S32HU>bi?94%zww+L2!c zz()FVa#B=zYA*5%`~h>!FTTS0FfO<5$C(kRVcCJNNvVQk?Z_|r{ONiEgWJou!@~l) zb9z#o^<&(es9~o(6PWPbWxPUgI0sFUhZ&R?a84N`gyn}tQiQa%_QGrg3QPsrp_8HH zoSyF{?C=DJA?V~O4MPb5b{LgCNyv8(SEM7r#uCFMg!3{*!lR@5HozVXk%9ur`FRIG z)d-y;9YOZIpJeJkAJ0%TImH$%mUKLZuym&B>&NAs%++n(CeCf&ndv4O&9KL4&X_3e zKKGI1FFZ~Jo%0Ux3Xr6#ib|0}e;;`p^HkHD^>HIr*+zMO|Gpml=LYzH&3J;`e`A94 zKluOu>jU@S`Y!zs0%!5RK63wo98mMWI#&G;B8Mg80BnahdsQe;|A@0H;0KN-2c9?f zQ11Pn@Hou_qB62;_B}cp-jq=x3j!IUnCUJZebksWz0m^~<7K7!vt?5s-aGoUd#?Hd z@M`uDGMh;=GPO`4+01S9&9OY{ZmKUYLloG@Ycl}=DNXVywtUuWFd!S4HF|TrvOF8; zd3?=LO{=rDz>_a&NBS=8AfMP?h$)PNlNbye6X&E3ijlR-aKw|#j*fn}yxl8}pRJdA z;(w`-C)QO2?0Q-^l~v1J6K4+tGC~-`wy<;%03C#fjvyTA!5nt{nKy@Deoq{|Rga_| z{s^dFmb%iT+1?^b7Nf&SvEhFO|KA-nz|bAVsj=<=`pZwg$iH2kZPQWGsn99P>3A!+f@hBV zJ-7lwh=_64{LbsNq1*YTsARws*XrAuQ20RGFQ+uUrI}a~x9`0YoW1DOmSE_|@mfhq zsxiqyxr!T9F)*#&9e8y%aWyjjwKF{mBZL{dF~yE6WB1q+U$M_BgyB29X(%}pQV*Ff zVq0tt$&6KIV+q45&@th)VjWqp07Nii0-lVT@hA`==+d*PtgfJ_-O4T7U#?{B%<41@ zdwQABdFBYYJaax}TNl0j4plZZHe8<_7Xw|VyFo^$y}`&D+Ry@oJKP4nx^cserc7jd zLqAhm{kra0)(ro#oI0<6H-L(~B|1L|8hnVvy5h-54zL0!0pyATjPm1-{UA8(NpLX` zV8M%mt^uA4hRWLXfXrx7>i7VTcz+wVuVy=1@NJ{2@Scy(>cI`xCPE^0#6 z|9a_V(@|+nvzcpULT2A{j}?DDby&4WbXv*2oa}mOVkjPr4L;TR!V}p$1iJ1OGQP-& zfOjgu2tZGevoOnHN+8djuI{#ILG3<6$r}O;p9@Sj3HgSUb@wEYBCbhU2S!*=NRB{v!|}Yz$nm9mi!T&k0^0kYeDa-%BPJ(bF~D zyU~-JuTF+S1<@r?)dT@*89S+H+~*3AOej$NT4bDr8~tA3%hOC~`Bs^qUBiWHowNso z!SXL-Kg&vrI5Cp<<$nmYY9`B=N!+-;O}^;@K(=(d8!Atm&V*uW)Qr z*hyl)5BkXowXkC9qgp-_~r3=7%Tky#eT z?47N3g>r(N!i6Ga0l>q?+PPLzThDWZw5iV(#)C{UAral1)T}P_agrNK8M`y7h0mTD zeGb^Ls!Ss@rFe*CZEs*0;nq3mhJ|ghazU~fS9_OFK^iFh++|Ql20N35b(d?tXyIvN zS5$_1jwT3+*K&~6ifUb1b>%b!N&{9(Xj?ag8DHt|hl3+7{D=`SnPQHLrsjZcJRi+h zv^x9Ssa=fWx)iSMjU@3OojH~*+wfjC{Q~#_2t$ zNXmsO>+3n{0q-6sH&2>MTpjXeq_nrbEgSj5ohAJ6@yk0}4-#Y?9o&QF@{5&}EKl?1 zm3O8|ojpNLOs^kVgYn=Akziucsv~h!s`GUVSQPxe6#cp-6Rp?ZE$b3JkOP+DGPpP6 zC8epEk_SzvaSGn2Cp^jzENyQtl}BgF)t(Qyb*V8+NqQl8uwv}h1TBq?0H0>rHn}k&Bx5a?44o|7YJH#`Tm*6VHtE<{$fYQ z88&24Q1`=&9EFAoy~~jRqHVcZL`-NkHnDM_x}OUmkWynIV4|48mYs?vG)YhzB6TH{ zaN$<1Yj+=>+lAw593AAN*{Ji@-& zAV|8>3PPb~tE%9B_6e<@iihzl3Gncw>4QXR^9@c)as~7rRw5`A#05BR;Wz_9vCBzF)8o9z)f-z!X9hnfd01}1?v$hgl;RP>4 zxM^O~5yH5H)50TLrG9+`RBEPVk+lV@u2OK|OR}eqeHnXje^gS6OU5@#jH|sio$DK` zWo~g&oH>nIBex2t%`ESeIWv5wKk2*k=9*%t|M#-qOWP8w)1q>>xSgNhcJ4ga zR#EWNAw#}Vp?Ts>X00x<``)Rh!>2NL7yBls)$0bQqORQjX!9_uX)NfY-%5I>4ejZC zd`4C0hwDz4&RL5uM1UfbbR)4wI(<;$9hDo-j0G51Nke4?I4Qzb4*Nyxu14qQpuOAp z9>ISaykzy34ymAHC5Z?cM$!Z@%fVMSg`6}Qm;{n2C?`rC>=8CET!>CRmhK|$Mi`9( zWFMPHjW7~JXt;MM5eS~{Jy2%1%-U@ydY;KxS*-0k9wduWkb(JclaxBKgeqso7UI-S1xC z`}6cm?Y;Lyd{8tX3dTT<{Ca>dXtrdD0ff(hMj-6|A>6{>bfp=vhVz9#&OB6Vob9sg z?YwtH4L<7na&%Mn(+HQf`-o)zaE(nrH6Ihk-`RN}e&gWT$i@53kNJ7bB>s+Rx)vj! zJujPzc_!4i=>0v!P2k|(7)n$VSC+S``-LmkZKD#7objhpeuM$O=CYZA?C=UYU0d&r10zhK$ zt6K}gp^9H2bV>3lXr5)0sIOA44uP*qoxeTtDK>2V^vLymy~}OJpHB`LUIZ>TONJG4 zG^6e1xMBuvR;37>-Bb}g?}D_6Hh6Kr0TzC%EWKPYzko|0E%I)5jps~f!qcOpgZo=& zcckJx@=S$ z4bHCHGp%4cdDC}e`75~Cc-s6%V60rOPOql0GZvVIs^i5+le(qkiHqNX^?aIq$5{I;q&*jWlHB1)KfpFJMkKc1MW%*==wr=SSxZdxP|cIOH4ZP@LOgbjQCDACZva|ev>U!w8y@=GTAvY+RJoHa z(!bc3sV$jZJU)2l%d5K!PfZ7IKit@lLabqu*(BAJcOGsX9p1iw^kl~>!Nr4+z2+Q1 z$rlifFzGd=ckM#^CNKypSiXp0KKMijWG(kkGB@u7I*3;1qLKpw%r6wt_gygOO4s(r zxZGj@n68po!)eK_&=%g*vZ1}pIcwF0<}pA4C6(deMVLATRbM<;Q}Lwp?B5V++v6_Q zf_`&#vx+RuCFYHql$S39PnTb_;1Npj?!z717%BzJlbeufvrT(S3x3I#A6mB^g?iV!t6-NJu6THHFqJ z7iPnG>sSj54OoBx6@BkIVoJ=>4Mt8$YC8CxGuj|pbyjnI|#zFNMn%~*)FvVApQ`i{~PO52}U+~;W+5Le?fDSS#JzT1bjf^oVYNY+0= zE>**|D;>W@f7ZOhd1U^nW#eDAC!Ys8-yYq6zxV9RR$WnP#*{kykFcmVnbh{~?Ae=* z>eG*3b4$hJR}b7ZDz|rRpI%{o4?F4%QiTTbaEZkq9Zl~hjs}}=RET^~LN&n0R%a8< zREz&hae(dPx`l$AczYs^9W2l*$pQ^0T;#qSu=)~~g<*<>MzSEu3!~4HGJNib8>je3 zea1TBkQUc8JZi{WkI&X8N@cSLLAob^BweV4o>9=>rNg$I4^|$hCsN8v?vSnHuQ+Z< zb#Z=bN8pc3zpQ=XWnA}pqUdD;1(Pj(dKQXt_L%m-n|GT2T)Q!yKAEKL|5GF-tUv!T zy_Vx`F7JSe>{sVjwO{|f98}+CUv1HvG0T|AyMQnQl1kN*^uW3_*w@gSlfd<=7gi*J zmY!`~Nlv+6P+w;&{fTGP$AVmyev-8QK9~HwhBWdX_FHZ{Pc4%N)Avw8cPNnlUttIX zVEAmFg6x#t%njq7K)|^nS{DeMPPz=z1V!V+7$Qq%>k9_-Ei_FyuP2T||1`Wh92;!AqF!%zeZA#P>~`}=J%8&1K_ZuZ&*^7|rxIe{~|_wRB!G6)ni zwC!rlo=7wX~9+bBBXg21mkUhT&gvH89hw$wDJ_Lpx&% z#bN%#DsDHYokuf_rZV=u}3>SH-&D&}J6JiMac zmIdkb<+mE-K$Wz^$j<5BO~QMMm$X0_q8wcg+xYV2Z+St)bijMv4CSs zCRf=9Ks~p|CIgd|w)nt;f+#64qQ@5DB5n?YAmECyP$xi)d?0s-7tNa8ydawPd2q!! zCj)?X?-p#|_iH5mV81^o=2lzj7`W(?Xmzc$ymtNjgRbE{{SPH`nR?p!EXjP$;AhK@ zg_%=>dDj+=oi~VJF*jT<9KLs>VTtyui)cs0oTD;lkTvgOBf_9^z+PvSkhSvAX_bdR zE_05J5^QXa=S0*vgTZrH~NH*Qh2I0wBVFMUe}a#GzCc3}vJV z*^mU`$qIAXhZbf4#TmB1BHZ$l8YJ5A=bazY^T51-!8(`-M6Eu2V1snKBTth=?uH?x zofT7fc?A)B=Os5(3~sHquJNFf2pAqRtXEUhNWz=p0m2(uOY$1xRF&xwrh02(CK1f? zkDEt4i7LP4%r7T9@Q3fmFTEeE`L?1?(7lyUKH#sR^p7<}j(od=E;xe{P&Y4IC4@Kh z_wd9%&q5!*^BsF13g+!3)K=?nOn+o6K$&^Yt#aZ7z|sH0{3nX|nX86%mmWGcM8 zoQiA5X2@wW;S|{e zm`=DsBb<+mVu|oLMdyL0ggK+S11LMXN)6FAw89>SbhR59cQ~`;BBV5UFiD_h(`4q9 zRY}ALYpzZr_pNR*bbpeRq`VpDEz?Z;TSE)&uRvriZ!U#E(+EKtr-fIsfzXI?W@OL-np4oun0@=S32%!TzX(_oV@Q= z%)9njwyQ&WWcio!72%^fKc-A~UFlMbip*L2hI8%Pte4F^7^nRUFHn9prTcp&$a*}_ z_gQ*=^*yuW(#s~v5C0e%{xqyY+MfhYMw-V?QR1l6^QZLq&U!1Umf;>-;SEJI`RzCb zrYH&hHYLGlMXl}H`3*hIubahMdh?4BtoCvk6pjmM2E`1?=j%qCF>6;m%@_e9V<6;( zFq}3`85kJxP6o_@r$Z&dnIzSyK6ol~t3D}T7BMQ;> zd8%M>I^vQJpukM`%H^DLpziwcShtnYV%|6`j65z{`3N3r<7Zvv9#mbtXqOb=6xI9s z>BLp(Ql?Ako{S@fQJ&?svx6qc1oL~YN?uYk#Qo~(RomA8g`Mc0!}R9) zueKQ*KH(1ED@JcyW^{bCnLb*t{cHKO{c%I~V~>{WCAEWIZn)XszaetscB#T)?v~pD ze{R?Q{q*qfmrIE!$o(?>gT%2ob_0(1-T%HhGqSMWar^1VZ$4Wgk7cJYBqmI3|K}{$K>J7@Cj+bIuH4>6~Dh(VJi9&opJ7XZ}u|OCE zhRbut665}=*tjtPt`_RwfHUH5z+SNmp%jRRw(0(-1-UXg`!K0hMXXRq1-$7+%Typn z?Rt2)UwMT+{`dVJ^&SV?W*w&yI(>5i#~FEMbFmynpnYXWs4m+ZJl-CktCLQ(vk zOXZ=nyl?LJabd?0mTGoYldIb1rxPJO^~<-9E%cm3XgR+4)_^-&B7G4aG+#6Z;O7+h z^o<8QFIt7>)tHq=B~T6T9o{*-TmN^>{@8XIl_R^9&&kSB?8?E)Ql~9a@f@M045u!O z1@p79{G`Yod!!Zg|MF`>;($3T0Qj;2U4&c$WE8{&t2nDvVa4`0xb5f%-_gCrUkp!B zPeTyUv0UEh^>HC@Jyuj%nR}8Lh318abIP~}iAQ#?0B_#cjKVWAfKgN z0Kh>p=BjDD#Xk{Ya#i$p52PT=S$UlB;x!3g6}$Vzbmq9H5Y75*P2Rd5s(f_9zUFq! zRz{f0L$0ECbfWT962}+nveL3vxEF7)uU|Qwm`v>RW08@Rm|_&JqVSj(6p73;u9>@^ z-q^G~SkMG#wzD|fc+Pjd)LB18kkpe#TV06?X~bN2GNk12se z`lgC{dZ;}siap&FYk=G-fZFybgG31^T+_*%iG?oGl}>9$If297jN>{>D&CNc2na9= zqc5?=cx37kmp#Z@0U_}rT&>+u*gGE6`M5f0}{5aSEuEBuZjDBo;!;_uHdqbNp(mo~3T++9hY z48k-FmGh|o{G+v{Y^>qsPLNskopQ)f`~nA9f1epB>A4udDO~{~Q=BizSv$^RF>-d6 zRj7JyS2BPJ3U<64Xnb1T{gcU6e}7r253%o0PLPu`ygf%IZRLLBGIAH=K{->|E#)GKQ_0SNyg{76!@K;}`^xlKVt2l)`Aub5 z@VcRaUi?bp7Sz) zJf`NK^g+Q8hKSMJO4f+Jtk3E+>O67>$)1qE=NCriV5QfKE&&6XUaDTDGH|H6q3!#t zm1T)LPC*t$&N@(_Jjx&JNZdp)hCgk2<<#Q=cGAkn3~WI1wViu~MtZQi_vs#-69B)I zSc^Gx>N1ZCJg3I%p}WT&+mPcv=do`@zvb(8OQq?hqd?_OecYN~r|>VI6r>7w=6=GW1-c|It11gn67}K7LGw3&62D32EJ5^$?W;gb&oRvS+3w;$3AKO`f&OnEmwRqc4mha%(hm=Jf6w|f zvzMkLz|lDAW@~ZLxakgCG~(||9+Mv*l!t3AeOl_y_0oaAxRR5d_j~8r2gU*9cxP{pR&$)0|1=^?B|q#PP8oqX$=$cVg<}U*~wAGIoPg_m9nN3xcJI$<`QCH7=UTeX~>ZC%u9<>x3f zZ?q=Ab8IHydW=oHWol<(1Ynu@}LV7lWBeMjwh;h|52=ZT0KopG*87nrD^c*wKrF1SsrN1 zt7Vg}sn_dq4W!bItE#^-w7lgXax2o6fl3&q14sZQQ!_Hc1sIp!PtKrf72FL+)NL&$ zUY5R>cM$w~Ui|2&_Nd{70h^WS)%%X8-#ohV$K^LqRUz0r&D&n$Q{0^WKR} zswfB@XPuM2b&P&b#0CKlI^Gy+nJzJ?qO_8!DW?|m`U3n8su%!?FzYk{{VLvG28a$3 zgPkD5;HYOY+DVay3Fg+F`ocJPAtS_j-~*BSBk>!d-0mer;(d^TkDW;KqCe^4i`n*v zJJ$;(eH#^h&Xv^gaYSamU2K0M;{5#P>7@QHK`le3j~K2Pnd{}31VMG>K?qL9jcZd| z^wIyQsf*8=by2~K%x_1PV%pf0JZf1-9(6JFMx|Z0%nmK9#D7$~K(94)i!Llch5--X zaKBi4^jT`n30l^wlcFXxb7WX~?{HK>uI##{l~k=k6zQTen4Ad~(arkrRXSw$NM~7` z%NYv69uCMUQDOR;1W5`n3XG@HX!@9tAtEvKC*0qMiGgw2wR@$sN5WZXt@{S2y$ZB1 z3Z`FP4g^w_yu5k6P+nYT94&UL*xm4@dP0I9u2jxF7{B|s&@Zj%V#4)<%gdr4`@eR( znn=F9vhIH=$5wp~DT3cJG!E%rMd4oRDC6T20pQAmj!1ZJTymB1eA}4Iku7hz`0UK|mzOE6sYdd$ce<1I zzoYy1%S}(k={ej8f~EC+x}B{20&V0hYvaotkRknxRqKphgw6F?ew5{aY7_xXlV^yP z#MT_SBumsS{Wxwoy#uAZ7^i$8%CW>FfCH#?rf8*|F_IU(Q0B5-$eE@3 znF<>rR&PH&&;x*A0V`-|DIJ)k4>h;r)=-i5G$DuHth;>HJR>%&? z6--;x*4tCXvpJd8lK0UJJ*x0EHw5V;W;Fi?aC_y`ZhLVtly> ze5&`7!O&kFk=c>?k1*no(TD^7=N^_bP~dmqto5*sOwODE#58H%Q5+0K?Qahs%OMJm zd-dIrG0>%%b6?5Hx_%bp{Cd~`Yf=NX5X#O@Sg9zHd1-B0H~@{pW9(+j3aKO+BM?f9 zXW+jgMMnB62BW)F`uA5_f&L_50A0Dr?#l zuNhGW9UV8AKgcGrz>RXpTI4}{sug>bH(ON*-As$#;nx~qqix30Cv@7=WUISz`Yo%E z9YL1nvv+t+N86z#3HvP@kv_X7jDQF)`<&um)|{a(G3;o0xX9uOa$oep$TY=~Kn*vZ zTb*egBxhMTBax-o_}T#ukS`B!4lobo<#@q6c@fa0(?Qa4;BU{ngzanZjs%z+Sri=~ zYJA;|VoQqgG&ETZ`0McaDSoW68fk};sC2u-2-A4T%b;3vS-$GL_ITgVbfIA$1)BE5 zniq`BEvD!EU}-azRqaEUjT5QxUL7Go6=Ql#gF(Pr}Wj0 z7G4dhMm-t$j7^MvIXaU5=P7=xKK;1hOJE-1b}{UF^`j%^Zn5Zw$N)!$`L%z(_?ifq<8~ zd;eJp6j8U!NtsETPs%RtuI1i;&DRl{(kDHP+&hJ|)O%m@!X<-!q~Kz{q_fGYo3Frp zZb%vh@rXy%(d?X(eb9SF4RU+=JJwSMAq6%O{^hn#*S3eB9Jg5)IX<13|FPmz9DGfx zUt%|A74a5x)z%1w|9kV2^+8Ch27ig##l9(Lci`5``sw-q$*Bhp#- zl6&qEG8I|j{D)@-X=P<`1{jFI03lUlQlfm08$1Hwr9t;3knN9%TJ>a!Hc_Ffx9W_} zSAcx|Z?&hG7+Cr;sV~bv_*Gf|NE&wkH(Mb`m zegEBj_Wbv^e0FX!>I!q*!!bj4hziN%$<~XNFT8BqA_JluktTUq^F)8dE9Iu-n+nD(Na9#U#r^I9^;YpXo`d9i}l^at-;kFw{ttjd`c zI2y52_%7>o3iJ&k*n|Cm06~2ib{DeO_ch@WUb0P?+8XSYbX2Y@%@!K$LjV3cJylFU z!0R5HB(}3Ew_pNbo`ymTnYAFuR>?w})W7G*Hn=iD47&0Jxxa?}$b^;Kd-f_r+}&36 zwU+c%l&un<+qdq6!T#Qp#~S%0TH$*whVWiA-9?xR)C(q67U>?E#LOka&%o{k59q5p z>VIc#psn0kJ=qr3P0+yjjZVqP{I1H}-EKU0pn(67*J0fii|OVBRw*kgn%@^1nRNM> zz^Oa!X13!atP-kGC_+;D7`r6%2ft*XJ2_g&H|c@yMK_Dz!N%nqFib zvw30TU#a6VCC=il3u~tJ)d|gr`OCyt3_WU_|JFIV_(4g=>T{-lh8_9|Pi`V%rEFw7 zfvCG#H^Xvy?3;i)Vk@-iMKRE*^ONVqvdP)_VlnA7)}7;w^|*#^)PYRaJnmsF{Vo#i z#%;*;{RvN1HPMk`EMt29!f}FqI~OhbjcBbE6raE0 zZRP`{ddP^6alpj%9>prL#phoxlz$N`#@MvZ!>!1W@S5l!~lhutk~C`+=;PWXCZSAJr*1Qjja#J zZk+4BB<`~7UcW&*ABBDayhfEG$w$MX0x^MWk_WL5ZgF337kjiO3+(+ZH$Hg35O)C^DEv zU&I5{DhMwz35+lKaAsfv9LElmRqm+UeSRAe7N(g&A5$c;^RptO{AV_!S@GFaONsDU zx5LSWypfuN%1glgFsaxY)@_L5-c#TVfg>4zpyC8^_U zVNiOrYRFm}l@q|^l|ZhPC$CtGyfXWohI26~Ph=z)D4dHZc;C$nz@@^c^40{g7ly1A zJ?OZ{^qGBTo?cUu^&)*qjry=0wDbOGeedS7?|#V_idAK0$eJJ^?`dW;7V%dRb>m5m zlzPSWS7ucu9wQ5tpMe8lD9>Fp71bGH1b1& zZJKsU>6`*=T?rMnX&T2pH@nl<@y;b#oRZ^hog~;N8^kVb=IF*e72L67T4O2I{M66c zRPT0EV~`|QX^m~|4G>hHwgUB#I?k@D5@VtY$9!Tye9<7?sSmf`bxn<}SvomCbWA5- z>X4korLt?gC)-2~77#E*uheH^TN?0iqbLlHq(*OiY%w56=gBe)f}2z<#RBO|7b@)n;sF$ zZ}*<{U^J{h`k2L#rCkX3P^ zZMDG)i51sayP8kepAK7@R;MZpeh(@c7nYYTd-0EbP?yH3sHB3WfwTNd=a=uuTkc8v zS;;Y>RKp`sC9LHF_m19|L?m7IBzXnsW3Aym?-^db&Ry7==oo33F(jY4(o>VdNPGpO zU&+QHpwx2Q>s*%2wAWl?UfqK;#?R6|;U_}PKPyf2P{q!9ZHnl;^E4D>r*nYF_w=dx zD)a~J2ghkCJzh6B#~??$!5AAs4%d$g2fc_@gNL@pxn&!UmH0XkUWfcnQR044e!iyv zVdad>;|s$f?=Q?~Nx!eEn^=z*#B+`2c%o3+}Z?2K{$sz1Bn>(s2nwvMkHWI2F^9lZ+0t|kXDw9uHCx$S zihcaVnM5seiPJBG4Fk;>0XvEld~@%<_vT31!NNimB2gE@$i(mg0^Hi%?Kzf!+Se@Y zN#%i|Dp7l=od}|dK2PnX%t7ypxwPqLGR+Hjyh{%rygjqeF!IJ7-p{Q2Z8Kn3!FFkI zPdt^n|5zn3U1rCt%Dc7F*Y;NS-BZzSoQU!Dle^qkLwnXxz?PAQRJE)5w8ZaczbCvM z+K&#~?|nB-E7l7tOe?^Mh4ktIHQ9SKoj&)t$=erO>PawVZ5dYbT1b!uuiMDMHOI=# zhTudgc4ih>ld3Xv(ok;ziS%rz5i88X6mTzrmn^^iXMvy~_b5S1R;hx-q^f9gd=hmk zABbAuC|@2k1PWLwe0Pbwn%bUX)K*nuxa?+t-D5y!(cAS`I2(^FI|bMkOy|tc=sB2skts%S~8(X4k#z(aod_#F?3 zWN-prpxoWlqORkHoIPE%i}Z;0eBg%MWKy_rbm4*06jOy|4$sp4aO#bpa+EHSA0@f6!UMRmC2V!_R0;Zy-MRc zR>R6fj?05gfm&PHv8I~qFH1dhf6Z`AtiACh8+4!LZjtcndOy4@;ba@X_`<^Darq(Z zzEResQ0S1YcG=MjtD`3^?s>1(V+YF(-hA(?WDfQw4-Zg>e+On!n;U$sJ6QM(T#OA3 z47VpyYY!B2sn>~7I(JroFL5(oj0VqbT~NCzQB$NG9=Qp3E6izoJkoou^P!5R@59~? z^Sah9@uz>0`=+5n6Ck|9Iz3jafIDzNW=_&nFdM2V+T6puW||R>O+W8uELvWvuC30{ z;!n(NqqV0ewi9YcD-kOx!lV%hku({(05L0e@s`)fq6lg!v=(_hmUQ9;YrHNA`h+o_ zhY!t8rcCsW3Lp-`S0)6N&?!+;M~Rb!eBCTousa?3#^%td%>hT1NyvlGpzFiX@tGb&_!Tsb7p?%9a60mGa)^kF zw~ayXpHIK@_QS_hBL_K?50~q{B(!-X)(@lvfxAo4NE{@t4WhXwm$Aw$4=_^w=^ zE#3LN#MAg79=bbnxu;4c{2{aUeA_meRW>}p;7&gb*!3R5KGU!lBdbI09m4XApUjaV zpfzEROJ9s>Db_d)WJb_lfbT-L2~Y&&RNG}+v^u4xNz78Pt!d;!ir&&hXu_LGf zGrbi>Zr;nxNls8h(QaNG55I}nQTd~L2GvHAzJr3JbLpt^=nv!_$cya8YKlXHhKScZ zI(*a|(NfW#zaJv9my2AV&Um-x3DDRl4-LiAePxN@xRass9sSktP20zz?iLs6g~g3e zLGx}V=l>?bx0(^@+kS}nXIk=$BKIkn>Q{2^BG&^;`n4?8yl<1k8r zP&GJ`uC%;8VTdF~F`RB>I=tUAd=eGigOzrm<0J^h17U=Auuy#G{Fy??TJz}Y+oCI_ z`8Bl^w;*HQ4jE;TT9wD%n5#|6!m+x*V)lZRWzN)ney4vrJ>Y$Ig!rd7kFvI9Xp11A zv=2Ngd?6YQYaN&#q}4{st}Ut~-atXMBqMvaKGSPcW$Ig;vcG9AnA_x!YpF*nq@FgN zHS?ta#7sV)Lzphcd75Ia@}xu4VM5Jn7l5>qWG;)B?%cV}{9&*PMyLa*hE$VCy+C;Oo-4Zi8IXF#pPy*2{$LI)ZaP~>V zmRHG8R;Sh*o^5A(Tr?~f%YI4xaUKY-&D7cZGnou3x< z-0&{VP{nGAN_&FtN~6eL&zZt)pNBR6yF2GpzlI#|as?i}@FCAg7fcb20mlcAQpoWE z54aRv(tZ(`M3)@HhA_(HuPE;%Dwmn3tfdK8{GR5qg^LU%Ly+uwJlk7-Qcm4RUm!lQ zQPox(`_S*Lc8y9{|C=we|4^fMOGIi~oh~Hlrb;G3ckD)_nK<5fhGe=6Q#Mp39DYb# zBz*E0x!-!>G>X2LHcKY&RwXhmyX}7lIsZwzO-z0j9pOLlPA9C>uF_ng@@A}zT=bv) zL%Q?89XXh*K(btE zj(xNKEtf%e01w-lp88^4?U^1JNV!dOy0viHmCf2s@Omn}aW{X?l+GZbLl0)g5Az{F zQPZFig`zF)&^BVu!8{GT0);zFt)NW3E(!=Jqrluwm8O03!?SP6j{-5DF8TH@TrB4M zt8>CtRFWuIC8>f9K-F1ZgE^{w7ma7xx{z5`7 zxS#C!WtsMs=|b8}?`(2;Nq=T8ec^h8So|Y*kL-8gKYq271MrMw@(eD<@6AiWI7K)m zp``WShT7S6^rka>T3b3;G9EKBmf8hqSb_yW2MD zA~}_B^*OKeqw;nKqK=049)Hc8of|K!vRTr00z=Ck4V)_I7^Wefe5fCiC;&>SXkvSd zUV}}nR|IL@a$Vb$`USn7@XqVg+2qn#Si|E2{vN9s8P~`(A|)Pm>T`i1~mnllNwqS6d_-V;aVmOv@c(@t)4|tGTa$ zy)x!80ZooXRKnLrIR*_%Mde_-ebtc7O1sXjb9^vZz}_P=5qW0ItM*}Q zKZ3!h^LnvGaA{%|TewmtX{q8IHw|i=L)>4d zhXaX+T`Jq5&!0FD5Ou+f_liTkD7AZty&djDhh!b&kNZ1M@xdg5lO80EVz$s2LrYNB zU*ybnC1|jJZ3=U>NQlHru)Gi@tZA~=qHin6!Q;*9l?=<|kEH(T3$3Is65SRep^h+O zIbKE1*F6|gtS~u#2%JPPS?f!Erk_{Ef;{XehV4BthNxv*HjCk*;w z|LNC0heN~imYQ4i*hH5ux?Huj+bM*We8VF$hG-|aADlCahIpWF%}7BbJ(~@+qa0y; zI5bjMQkrvm#6B$=I#qzkQt99NCIY*aWdlOi=Qx|jvJO%6Ht3{%PZu<%umL#8_!l{} zw%~2=(!R|+ta@BJbc9PMpmVJnYm``ee~?f%pEQ{b2Vk32TaxcZxfw*~9G6%PT0`j%9> zL6JzAHrn3WU@qYr!hlTK5^qzy-izxkjZHd2Z(xS5Y5vRTe!XUs9H&Hvu%Zr$j>R|C zlU^Qg^>!qw*=fKgXcV-Q+oo{Vhl-rLFJ@vpm4B16_8)Bma(;RO7R07j5uoQ^?*eNR z37U}2MOGYz1F({=VIZFZ`Z1Oqk=ikgfltGGHt79Ly-=ccLzo>V6Mm}2z8XCXvs}+lc+Dhq{q)RfI3CJtW7=HhrfmDVc4XOCT-3be<%#3_ zXDted$%`kkRf7_RmU?9IFx9W_W}eXzHFT7v>#!mo5)nIoD%CT?F3H-7o&Y zXYIk9iHS8`I-*l0bp^%NcJxLIO4LcMYSm5=*xKZ-{;9Dqwh@CzCU;S6G}EyHAObj)av|RcIYb`MxzQYSAc}4re$#KF|*>L*leQIVgJx z51a|i98!fli1odgNDD7EQbi+bj7cDS&pf|$f0l?uMtHvTt#`8Zkp1Mp_lSrgbXh`p z?Ys60zSL(|ta>KGX=tG<{N{3PN%%LA$tJU?-y`Djm&J%XW_V+Xyrjz@h}4+fN*@nKH~10_BLa+z=T z5)cA)0ZZ&T4g-s4qOXTIn{w)gFKU+~l#-d4=|3E+FVhN zCT7!#ULsEEPKix)Cv6LX!g#03hsNZw3IXjBeO+bmj27F=#~T9f7whpo!dkzrOJX>$ z-H{1qE6U`bhX?vdTsJ{V`ds!8^lXqyzU1sx4*pDvZk(ffQ*}^(l#QFRI83X;1qg9? zjpqk#@-Rg_E_98)q^dT#o%{J?TypFoV)D`y6)wwAuYkmU86?tsdB?qrdiRjR#ZF$P zmhhdF3rYc7z6=51Ao`ta)zh0Apwj3p7K1txqwhLunr1v4yXr`O7%TzE8&A5LD1A&` zFKBe183QH*PVrd$AwkrY5EvI*~&7Y@ZXloI0L&O^{#5t15X_UX6Vx zlJYhE{oO{XcHjW2*Q}kBRC~oJ$mRK{Sw`uQo~9ldCseDwn_^B>lOGtiUG*2a2stL2 zNVqONjO;r_dW$RKVMgbu;^fWL=Z7N z&V*Ud`TW?x_2bW!yW2mba*zBU56id*Zp0AM&8%>Zw%CoVnd>teIvm7A1fhw(GIz9g{LeZUH}

  • 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 I|7)NA2W1F|4gdfE literal 0 HcmV?d00001 diff --git a/packages/standard/audio/skill/fastchat_m19.mp3 b/packages/standard/audio/skill/fastchat_m19.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..332201f2636c50a436b8b6bdb46c2fd9011ca825 GIT binary patch literal 29511 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

    @q&)GY05#ji!$V zc9vG@xj-3m%uuN!+9I!xtK`7^0P77N;iN1nHVz_)?rc@4-8fWb15sZ1h!l*?YsXI7#CayOU&$c!`ExiQT~;CBS-rEgCQ{#1bb z1j>=*FKUy^IvW%ZEk%_bn;r$?$}+6K7rC?0-U_Wo*i_f*&{=w&Jwfibbut44u#$dW zG&JwPM8VjC$KFkrD{;OsX#m|}`0WbKlwYJTNK2|VLEo9@hjo0R*&^JdKfyk+gD3&x zI->-4m=#X8v8T3#u2qQIfi>vGXi3 zZ;YWBV4SjO1V1!diseA|K>jnBAPhs zjn>$MWot;QD9X7Z#@y+d&vbhE=jQ;iYt;I2 zZE&kzt#WOk<3U_!dauahr*fb9(}(Q$RYiF()}A}%r5wU78f7l1voG{CNcGE9 z%;GYB3dKRQUe|f-SeEwcp$;}@oeqT;;qs!N2}x7EZ&mC=<~eR3M5T>rIkGc>-?7zk zbc^vh4P#al_0v&lqeyJ^LEw6Jw&}y~SHEKt)GkOw&GlrrGJ^90he5-cbPQrDz50bI zV(fG-h|+EVxSKlp2(h3jK$i-@d)NS|dSZ@!5!COQUkZ~uH!12#a|!r$&wDD0clF-{ z>j5xa!$gZBYsCrnOi>{n}3l;~EBFi}`zYvWFjTRbO~xJ~MnJ}1Ie0!VEDKw+?y11%_ zQe^9Yj1fxz>Vf@NSD^fF%MG8?$y&yg;+k3m?o8}*+^UDdBpgBd1orH4Z$I7qO-I^F zTdFMnEdU^@5)w~Co~ZKOsfwe2>0c!Cpv=r!gl~Rt*+^{ay#2^nQDml(y9Sa*jSG(pvDWAn zJ$4_cG@7O+&#uh`48g4?U@*fm)1Q1Jj7M7Rg1Q!SO1y0;{*qm+;#FopQ4fQ;SVKsg z-vFsmK81E7t+!SO%F67y3)BpZOj@o;0}|v{W%1WVvW%D&hV)4Dln;709olAuzb`&m zb>F`kvM%H;XQPwzO3@3a!GkA;n+m2n258e&5NGr~I7HewNfQ&9qUOBFV#Ci!rqt&l zYfxJk^Ykf+afTKm7eLb;;nuWzZYmAX^varqrVCdya#wB znqJdy>0i3;@>r$4qF`*Q>-A){NxN`=dTi*&dR(rwiRahTu)hW(=C#JFMs9BZS)-m*$ZVc&8ftQ1ou4A_rmJShZM0_itD)ZU zzT{x(2E95%G2_*q3H3IGb8z_yOFpo|$rvG@u@N#NSWAd&aU!w4y2|JRb9c?+(VC$2 zULi&?-3oZTX7gGQx_X|Y80M0EHJDR%fK;hHUPfvb)>GtdnREHJrwh6z4{K^9Mqa6Ypqp-9kcIY?lIKb$imnz3dwA~rZ?{d zxg$aVPbFafjMdPMtkJ>{H6Fg(lRgp%d705xT@b0R5S|<-d}U(2o#zgS1+L6Paqdj~ z3t4~3)}Vez ze)^0}lbc&hnz4PTiDUb~N^){4!8Bw9t{>9AHqe~TX{a=p<>qs|^}{(0Wv9(LHMyI9 zA#R&XCY_IiDQPR&p(ep+K4hxX@+r;uyhGs11D}jYzb`xaw_0}O+iG)i3UY-aU!=p~u$-@X>RMBE$CVGo zv?prAJY$!-Z80~}IdN&m^?tL0@Z$|7zTPOCZS_e*zO*qs*Xo^(@n~PeYdLz{YN36r z?U2fu`lrnPD`WOQ`u+)W|40fxKK<|3Xa9{~eS+M-C%AulTuT3q_x;a#^na22H~;1T LBKL0!&ielVf|2`_ literal 0 HcmV?d00001 diff --git a/packages/standard/audio/skill/fastchat_m20.mp3 b/packages/standard/audio/skill/fastchat_m20.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..1c7a7cb79f47f7b40f1898ff383f57f878a4a263 GIT binary patch literal 25749 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 literal 0 HcmV?d00001 diff --git a/packages/standard/audio/skill/fastchat_m21.mp3 b/packages/standard/audio/skill/fastchat_m21.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..6de5e680c01fc21a59cbefd9895e466976d9c7f4 GIT binary patch literal 25436 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

    eezuMrFzRm~o8)OAoSJm^88{e;J^y-waL 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 J|2lW+-vDbd6I=iQ literal 0 HcmV?d00001 diff --git a/packages/standard/audio/skill/fastchat_m23.mp3 b/packages/standard/audio/skill/fastchat_m23.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..65cbd5cd96fd9f2f50636c9f2aab61bd8c6030d8 GIT binary patch literal 26063 zcmdSAXH-*B^Y9z0Ap{5kLa2t`F;oEoLvH~oq4(Yqkd8{|p-OK8(t9sTm)?6v0qG(N zsE7p+UU{DTzhB?=-Vb-(J8LET>~;1zXRpa`_Utn=QBxEl0Ne^2Mo&-aUrX`tgSPf_ z5W6oZDkLa`MBbkKzvSkup!a`{0csu&kNGbgX0pt!8Erl!8B zwY{tBZU4~d=*0Bg;>yaq&F#Itk4MK}&(41Sxw^T1_`k{7-cC;9w(~#2{yzu*=5Z<# z^FO`+ueARZH~4QZ1gf_aLd$6?i3mzc3X19f@8$n!@c;gH|9=~Cb5`{KP;vT-3Ls3R zTot+3T+SF4=dkBBh;(KkfaElA8Y2;7RK<;<pHPCBqIe zuHmK__NmI>_Gt(i!aHp@0pdj2ga)H%`+mRxtiV5u@ln@QY(b^Wxjs+whAZzqq|2uI zT$(9qsHWTdq>-2UWLiOb877Q!%50YN8yuf(iD3K`sh$`Q=8Cc9Aa)+KfczZoXi>b7 zBa_R6tWuS)@pFCr_QSWqq%&Z+O{+MOi0J*&NazD3xgKL)UM!7CV}fU?iTpyktvQEA zwvW6T%VU`|9$x~+^rgMn=@M&aZ62Bm!6o%k2+SHwub@?cYi!W;;*HXBl&92KS{9wpY^fUIR(0)HX%P_@xyw3t>J zpNDsP8;Dg2C9_H0z5kl3@sBVq0h#K99g%QtQG!{hbr^`DiL$VOX_n<}VfI3ikV7|J z&r|-ri9#YS@44QEav}h6hUft~0Um?5QN)xAd3Iw&Y_D8A%Pv5=2qM{6s17!=jnC1J z77ER>3#+5V_WkJ#SB30yJTccNl$I4qkkrIrsZp9)hnct)nOfnm9o7^K@^rhoRQO8x zVRaMSVZxUTBs}VZd_eZEPRfl2_=E-`EZf}ZbCgM327V(4jcZTSY{0UFVha7x(mOl4 z0gjFfs~Vzfwe4N)TqI~%ut*dDL;=0_S6xTJ9-OL&p7ICW2te8`a^Kt;X=(tQO=%Dk z#7-nm``h-c3@vKxHRm_pNhB(Pih(00RFfG*%0Hkk-+uuwEaYKPkDvlIpbA*)mk7qDTa2nItz4!H; zq1n!fym@R=G)*y}9Rv|{`N0}NjZcK2(U#IVOdv>%om*RC&TY|~?kA#jzb5)6AKGKv znu zt~j3Uf`A@!I4@%qR;b=$ovOTF0dPeJvO=E)-)r6G3CY37io9m+xua!v|I?O-b6t(= z92&-}p=h`(k8brb^EV%d|F}EYWHj`*q=;#fIrFSs{)%kF`_{X&)%CB0xLIM+gByp{i3lvkZ9&PVr zlbV--+gAyCR!>Dgq7BAy(2$mnC-e*b@KZ9VG__VyU=~4U?r!HLNI#lbFbS2IeDa3z zXm~HHnHDM>o7IbRxQFD?2e4wFr;PfaU!BQDLT z>on&hu=n^}syjBCV#A(=LoDr=vUF)=*P6*$YZVr8r)ZYmBBvr}N4B3~yC#?IPpk5R zgLA_s#Z_d=m@?g!Zuj0pD$(J@h)Y)V zfQtL&%8EtH?+sF7`nA*So>fM$u;-{jF)en_I`jz@4G2r@Qe5^23%M_L)a8IV5gaN8 zJ$R#Xl!_2VUo;0*y}wDM`yGUv$%L>?%U;fFw$yim?d&<9wdpLn=C~OMtl8y=3lx=v zgoM~rB3QW91>}!SOuTK_W4OxLSF`^$)dt`6|KD|3|F>hsL;W8*)|;4t;nUf2$B~D} zLN4@S;yDH3A%n-+4H)&?$f>$AGy2cyLnoHsY2pYF$Ozst)F zED6t-`EYZy9E(c1B7e5GGCsYky5%`NbnoWo=9A$2xA8p|&){$+!d3_U!J(<8+S?%$WZ6mP%oWHrbI(aL5{p`T+gg$1Kgb_e! zEgyv>bg(%Zd=WZu!1&ZfQTb%kz2@!h&Hl+*YWn4-{$jVtM;(J80d;cx-~lam*Y z6Ym*bKe3Gkz5+iM!#<)s68hf@{`*aSi`9`jgg479M9MCiF16P|>Q0W6xOIz8^UvsM&TN4}PjlLLe=zqQ#<{Z;LF8;D z0109cQ)cMI9sKDMe`IsT!q;LVMlC#cAve`YQi3@Z@N?qLhaGDl^GPHuB0Hwz3T9a+ z8#&?mkf-?8KXsi7#a^GB2m**{Wb`-eq6tmaIJui$4w1|D+bkyJ7 zf8O{{KW|-seERp&_$%AbS02Mlm&tLD0ZPp1(kMzwIxy5(KO3i{%mB`bgy~bzW4G*u zVqV5ZQ30~S%EEXWsen7^@0Qmg+fkN(|NT~qD&)_^Xv~dvsRY|RDfQPnf8Cqv_Dt>r zB7Pb7jYxB;V9Fl^^U~g4)LQ>zq_aDm~ z4S%l$-eeIdAzzb`yQ~7C8@vAkT4S;}78?lL%-DWbpBayIHtB^H;iHHMTnS(MrR=D?pk^2=fB3)ed`mE4c_6*l zV=|H`KuL=-PD9adrf>dwp9Zv$04INGE z_YN#d9C5?kb%Q&Tcs9xw2SnhFmyzl=x5x!Qe0ehc^#uO*v;>bZ{E3K|BDUZ!*^XRg z6RUGFFX5xf{R2Pam*}N;fAt@XOfPxPgGi^@%ATw)8KxSxJ>O=p_%@c1`3l8mp*@J1 zJWMfrE{@svn^N7u{+Z&Eb+{GB&&CdWWw+{OZISv>V&Rg7E6?(d{y^#9suW4>f>#as5EiC}TO%AlA?%p$osjGMrPomqf@u&WT(WK5E4z9Kcm+ zpL$}EAumkK-R3R-{*H(!JL;31JXqV8kB);T%Y%2C6?{5~af$%o%vc0qB;LfL2EWKO zvv}27sEkT}?!V8d8hfCK##<9pQklI)t^%b9Dk#`Y**y79O*l7gN+X|CZRZ2PDe&%a zjqPM7o$R~;fP>~hW%c$>T5(ivukXM5&l>r?ZPAvdGkqNjp&T`{5B{$mbA^{l{c8xX&5pq)Uf9`>XDS?(o1EY$vni&WS?He6s zGrGfF_{9pJn21~inHKy!JCF3<_)DG4OPP=f;qmp!e52CDSw@7`6Sb(*9|t|gH=So6 z(kYbg)S+ML-_cxyrV1oVdo`nPk(-jmz>)yVE4W*I{65{SnwL@UAjQ$PPa9M@6Vg|) zvey-TV*DMf^}g&Ao8$Z(8CT<_Ce44fv3#*U$)xYW&5Em(%dgL$4~PIG!TkIVXk|vw z;I11M3?SykmebLXyEcv4RVHctVeowK^Q_o!`9VaxH3b2fG}OW?UCOM$YHP1}kW?Lp z6kENzCmO5ykSfJ&R-H~uU~v#ih!qBe^8j+-fZl}zH=&+bn}HnsurfKrsfg4%$h&41 ze)o7{d>AJlIf7c-4!?k24Nv*$SR{}p*Lnd!#L&+2WokjB8bHhFe5DD5Q&|RYuE^B% zHc1P9NO!sXLm@JpP4)&z&%;)0P^K-8`O%W$-RgIq8~A}SuYX&c3Q~abVx;x3`NZad z{zPcN(L!GS@#g05EpmU<(#RtKo2g7suixJIKc0B{x|GKmBO2MZsg#YWlU0;2m~V_1 zi#Gel&>BqEO~Mg#Ais|(UHXsaHp!?J+1;_u5yd-w%E+gIGuBhZ@@2Bit}eq^@jlWX z2stAYMf)iZMdxJ57uB&EEanK_&K_ZKR{!Wx<9lTlKc$%DtUx*)k+Ojw9i>2k)xc2G zuqpzrn0dhA_v1agBz&KYB4+A9p+u~p7chYRGF(n^ctnFMaFN_5xhi{%8t;e|>O>CD z=0uT92%;g9N!lb>ZZwG+ei+dsq8HhC2WBQOnLM%8h)}M0xjgzO>WuPM_Pto{+)-2h z2Pc}0uWdHO?;7Zv?45;9CB_-Opf%c=6DWRb@}Zoo%<;nNy6*k)#hcoWOuo=bsIXQe7Risz(#YV%NR3wb1=new65yQ*=$z( zVhuA~4hi%(kT@#{lTWXh^&A1;8Okqrd?_j&IZVZZSFEOra5`c)pyMTl1q{rZ1&AT} z`SEe=gOZUbXMmK+j#ct&o4CqZ?T-S4bE-^hku@%E7O&4Q^W80o2`3qUU(#6Vvh>Og z#;{s*2036>pR*x;v{_6o0yId~5$!P}KS{w5)6UUWt)mv^{-~a9Vq52AS7$-_#1hJg%MB<-F4$DF=L_1&eK# zZjrmuxFR+pJhAw@)J&Xu>icuL*3F~Qt=*u0&~x*{XVKOk*J?-jYpE#K_KP>B!&`k3 zQ^bOz8w?^uf=G;zkfOivbfLJck4Uep@JQZ$r&JL)D`ZTk5MwZ$8*Bxc457IbZVm9Z zDj?6;My(E@P!eV!f--h|Rsiu)fFci#3<2=@x{Cc`+`!R==uM9fJxTVOX)#%!izP!* z5=#9K=S$uN*YWOY=bDC&7dFnqR=o}wwc7e6F_gMT3RW=-sExtfrHzy@=bjLA)~(bS zQ(q`a`Z38m@Z-2cV66--eNr@RP|BiyJpSSGTMhD6NxZ_7Z@=cw;^!YW?DGd1M{Mf1 z*T|l<-elf<7I;p3ywCeah$$RF8w8nW6o)Kiw$RsO+BvEFLaJ8;eneH?=TBN6-|y(S zRoo>S4a4A>j{-(ICn)M48(CkjT%X;{+?dsBT7pcWFLRduel}Y<{XPNyHU9H^yUnrs zqsFU|vyw)e(uvoH?)pe#BM~};3^HD!nYPafnZYgs+Il3MN_Ymq8VJ8686fF364C+M z5&-0p2?V=DTU1C2JS!(sIGji)s^HGcd8aL`sOnK#JdREuyeQqgGfWt*W~K`IRAuKY z`bmLNUU+LSL08Z)Q@R1fP7H@R;73`oI@k zN~}k<8Ma~~NgCKCYLNgF0#lwE1Rdlnw>43I2mUqVQ^_fUEY-etBt4*@!MWp2N$e8^ zP3N1ed)}F{H5sq&uyj3VZr?f1QVVT>-#wpu!S(W;sy6pb78$bbqs}fUc`L5NyZzQy z&s~4O6!G#`0UxT>*!(K8y0Ui(NP0D74Q2n1^8~UBLqxdlN)Clf+uQ-tIm5jr3h;x4 zA=qYdMtnlsNa@!y{EsmE{to=t4-T|S-tsEvZ!oIv$s1Q`VH&Yk#e4=WT_!~JNMy1# zF|ro4o&458nKM^I*P+a`oh#^N0BkD2dZ=fa!4Qifze`QZAAJhud;gr*GQ!%}&mj6; zxOdb`HGT=}P+-!;2P2ZZ&@bIsE4cy)H@#-Pk3=oPlVGpQR=^?o41^?cfdLv&EVy@1 zoTKTzHrPX;1`ZQZ1e$~dwaLYDAd)B>u#8JD`jZ77vDa1>Sw?TIEP64u)ilpAkeBW< zkGOHaV0P9CELT#5QZJ6RlV-Q4H!Imz%d%n{fQLB%Q&FGB{cGbFl;wJP5{p9RZjtjg z7lr0o=Zpd%5fX1CBE@Wt5C(Fy3Z>WRq_)alViu~?W*f6` ze3p^W`&slqn0sYd4SQwS37jgZ=b3D4EE0@9+KoyaFhXCogqn)$-d8c(qSCPIH~cOS z)aL_F5n5T33`-P8W`89F$B)WW^F#Y>BS5F!t)*U4B;&zGZ&OnjJVk*u?nJsA69S=) zA*EC`VHy-%{bF*1{c{`@NSera`MTAN8NL!4efdAUlI>5e%a6pbZTUjZUR?&*tJ#7D z3QSVfwiK<%s62cb(~NURETOqC{oQWgtpGX_dBE(YkJHZc|3U z%&Ru>y;cB$1fPcA;h5&aa@3#^DT@RFSc3hC1!BKMo$g8bgPsiV*xk8R1e17+9M8k2 zvdhx_?EcPFws}$UM>-j}~3M`+Pdv3ppO)jHDU$+hVKIfZQ!=IX;pKh+A~r3fcftY()N2%2jxy2I)+|5t{T~W^)A<^ z`*Cftb6pxA^5uzT z&y&lyH~U}wBPZMQT->Vt)0>Cu9&DHxYu*p6?o7A$o?QHCtY>eMGxH!{V%O0y@YQZj zaFpb(|I-g*FT6IXr8z^4^^UB*(aq{AE8wGsITaZW5f0lt5I0^bA(GiGNUAz`A*mOj z9VU^)gPF@!W)a%TC1i3$$O$34k*Sa|C=JYpFfuX=!{Q@9J`@MP_*hGFXwyg%eT$rt zUK$JwILN;Wr2r~r(a43NrR;TtxWECS1BDjSvI>h^VXy0$5r zz$a43!oF~*qA0iZ0e&VoBL*V<+N=~>nPY;!-~9yKUO!#!eBRNw^u*kf_QPdY<4gLb z2!RXwJeCo8edpRHmFk)85s!!Zs(at2XV)ix!kCv#1uSM{UdhIZGvwQ}h7$`|Jhh+_ za926o%q+WG_lB~^_rkc3xwke);Ijbhu5bvh;S*E)_^d;T+X?4Wll{nJm$0$~(bl|h zw-P-yVSyQX0zn~_*aDnp4uQM%oh7-uSr=zfOwLTyd23Hgv_+aZpTByi^4IJZIWb8v zEUj!Aa#dy3O<|QR!TmiCc`OpKVwRg%PIvJI|7xgK&3e@@y-7iPQTW@#nR5nnM;5E_ z*cMZ>yo|kF1+>~{{{q%i5g+!x$%xJ>myR4HaLj;F2Y)b@hQ|BHaC!m@^^nKRXZrS# zkbnE`ygJ*Hzpm!SF-Q|HYsWis?m)IU*fZzC>{yu8a89#`*aNhN=4W5x3(F5f^`d$L zV$CT|6>~#LUbTjQKU|#MZ&?t%X%?K^uvXD;WEdLg%PaWnT-l$BPj_ zemf8`Jpl@Wq0i*u9qa3rx1x`{r@wqUTE2UkJZ)`L%4Jd%u5$OtVMh7tJDJ@nMjpBB zK5y*Fnk;Vn5G-6VU`@lFsnPHT`v(5h;E%dlW?wO<_ z%t!eEa)oZvN~pL53l!4>BmVF@no3y?7xS! zm&Fsy>5$W#-0Q{0bL7TF+AE9U5IME+py2E&Ye}+?Pku4({q4vQ96w%}{!a{j5M>7Q zBr@#?`bl&T>_z>828Tl~ecwaXq^KR#lbYB0E^M>xO<5Pagjvs%n zjqE)vEq@uFth||QZmk9ZfTD6hz&4Ag4LflmH+8|%mM#LzX=jXMCFW&=TlP}1(+=V! z0~JDP!QO5PoOMRX4d(fpSqSuO195lW ztwwDTt*D7W=ytVQb{UDKeLBzF{g*>SNXpG$F#0D z3C=drOj>0Kt2;buF4TQU>V_FYCPD}yRKn-?HXd87s4X%4#}=%hf`(=47P(W$5}18V z&Mf}S%zk=DP@rA)9o7{2h}I9(8vPgI^;r`1A4Ah|5~0yhUPat*PX!n{*Et=b2{@Y( z^VGx%mBjSc4n~5Z#WiU-uNYY*%#baqZ`Qm{A(edSkm*e=UAgC07mIKbW!O9Tx5sc` z|HvIL@h#0lTPT)|8#lBwQ`Y zdXKL?YK@xjve4m9c$?0Vjy$LoSbJ5nT$}=8Adg^6eBUShUOI7yuIbn9D)&@@AJ(rw z*$ZgTs5u7&nL9+CXil*F5k7O@d2vP%ympbuQ$$yUKPSgZ3?z&P61WJE&l+J6vW>bE2cp5nx%c-_-A-<{#JX z$Lz@fj5AViJ&O|3HO!nX8^C25T?8vJ%x(Nalx#{y-JOoSl-}i(@zS32B$kUxsNdcBJ@+`PH8aF*L+3lk**%%d*j>2vi~V9eImx4FB3FZadLpG~ ziQFQl&Y>kO0ZB>B@hVrkZH_fGn@dlMlD>{7zh!6riiZMrNRqYtxOwq6xn=fT5U{i z75oho{otRv1X^p|oce&1l<*Ar^s@jOS5HNcNm!3N^K*3{~M@e8i$Z)vf5nRiDDeNpfIK zRcfvqa^$@bjlweYG0TwHyc!{n%Syn#)!Ga9thh^*kICi*28E*3ss&@Qed+xDAMbrv zD7Urs-}o4&b^V}mNYo+`O5J$yJfaA};vJB!C2a@E*wR+lMTdq$<{rd_!G!E1BQj!3 zKG%J}2-P;Hl;m{kT~C9OG75_Jq5xh=KF;@8v!v?ZgEhr|ewGmEWG+dQ`X>5CDcBWk z@A8BAP@P@fy)WBnfTUK+=+XK7<&gJfSXh;+=EovQ>6dqTZ;`vvRD_iP9PSz&9+gtD zlxdQ4V@W-`&Eup}zfP z5%Nzayi$CV@wQ?sl$0VpwAY?+y|a)+>1$W(T@s^u)}Los^J72-ossJEzmzWWJKe#AV#Zy~L@j3NewV-62n-(Hpf_ z1~Ze;VM)JyiM-%4I9PKNUN;npN~OUJeF<%dA81KDGqy+9Dwn-nj^BQ$mHK0$3N<&@ zjuz2~)RD?tv8j9N$}-UVyX70eOd=!JhOd;Jrc5)2=fB9s-^S35rDej~$TuERSFq$1 z*|4+D@AzVv%!bo^!wm%=RcW*N#ZRkOWb^00P@nG)%F6Y5WajsfGF!%>?*A~Kj>Q+0 zi}ct;-ow&hzBlC*3|f4<*A!{$2D0QY%N%t#epaeSC)uWM<*Y>1KBb@6DJG*P<|p?< zI8j&KIu7~SuJT##z@z>!ZebwRR|$<*=u>f8X2q+a?eyORn8^qbD_nbPZYzzjv{!FR zZfn>9m>i~PL?22322SGXZnGRH8UYK~jr2)G4xCyi{H6N5Gn!5?1`nl{Iwk}=vIrB{ zKK0E>W?`n@T36{WeVWj>{~;=6Wx_#-uTxZ1rK(-Qpj%=K6hFR2ZdiKaI-h-ka_E}g zw)f!cB)N=2rW3DjD!SDRO)y?riUeQwC&io`y3`KJp`4E$+KPgve7Nz>hhK+1wR9gO z|M_LyX4G@t(m~WUz4($_4O z&pid0B3vBSLPvrZnG;A5;}CElLOx1e+g61baz%6>L<(2`6zlx*aCV_EhCd!JCnl%j z4j|KqWRT>#!SelLJ%N$6=xqozH0~elnvAKv%c6kp^wn!V9S0&zSnM|+T4-m>D z`E(^`u{cqnUu>?gp+@dJD}yxcn1f2jw9vPg6Q5tj>-nEY-30sxIW0zL#Nhk2*&4I$ z^e?oBTrQGq2GbHXG?ibLF9Jpeo_Rk=<4t4v6V#8lhEyacOk?4HBrGIir7B4|9y2u& zjlO?==VVBZ^NvWaTi-Aes0g&@L)j1LP*_$>=T=-;vTlmSbk1J3{gG*md8*K$V&Bw0 zu4!#xP9Hk}IhK}Z%J-PL)OdzaN`_nU=Se2js?&W1PEN@|7a65rR6A2KN9Zw;V71lF zOO|ZHkj*48ZZzvtFh7(-*qhS+jFuLO{MyEn%S?wudFo(e)8!^%qN5RLZa@TbfC#+m zS@;*2OKhM{N>egjJN>S_EKwweNZQ1vxDlc?f>|+Hm{RmL>kk+miHr=Y*W=RtrhTrx zf_fRtLG9|NR@C^QL`we)YV6`ti$39-Y*=yf8r!6 zs|GWF-Ss^kpRmqSeAsQTsmsv$d)%hQ3rP0Y z+agjAORGf1Vva&_bHVUIAaZ=`5xNnF2+3@ieQy+jT}>-%uN+=jgbf0Kbo|{LE94MQ zlFBXdtwlI2rY0-gft{NtFh}@b8jChd#i4*C!vcsUQ})@0!SD6!0BkfuJ&KQNt8BNlTJv29OyKfL;4Pq)Zr_uGhQ@>M+(snYDzB8`AQl#O30w}#2& z);XX=YZlX6z2D4L(LPUfPm~Sk*4Ozv>?6Ma8h(3mIdy((tJhk&#JH`4{Eeq_E1Rv% zqQx0iDMVht{|b2H)WO&5-XyM$RqiZQk)LkxI89*%4Shbe!cHB=1lkN z7*_b-#35KRkKa5Nj;ZipY?vz}shUq7q9q`0z8V+Pd1l#I&6pi%I=b=xo8;ubSV+xc zbKU`x$};oAvsP+bpC0YRAw z%jy|MWBI2FSwXoFI5o;!y`POpgY+s(tILH3QZxf(>FoaP^$O|oxs+_9%J8bA4>ix@ zAuFmqDOW#U7?pG>QWl#-{@LDRYV6#ROlHFQL=WE{ak4VX8oCU3@(R^U9gIF~)R*7I(zdbZ7XQ9L;Z^J0z*--)tqs!p&SyI#B zs6;!+pv7zxE%WsVUIvy?z-tynMYZ$WP7| zzTR;js3`n-m^FdtZLl@}4#}6Y)QxtZru;$u+JPzI(Cwn8sa=X0*6B$T9zyT#HQ$#l z%Pyo?y70fgaB>2jFzQ-39?@KaspDN??6l4=>waw`eMu0v_!qY`2MC?}#< z+#0F6Vxso_*4l{P1v0n@rK6m=Oriw7@7ge3Z>LDff^Z)gC=$^U){ouKFYL2D<1A=A2IY zmR~6CE!0{(Nw#WZSPX0ps6YmekQXEa3j9fVMRtqai90*F#J}tV-u?chNo}HGE3NSB zot?Q%pIixgpd(uBM_!6XC0jG}_j8PMw1eV~ePrLAG6X&f0dy1+0u;8VfwO+=py*;L z!$MKLCGzf1;ZSP@yF&7JVi3GO-pv%!$k-le_LHM^d!vr#h6mwl?IO*(%l=>G17`7X zQJ@GqNd(4SX#^k4zS>VD*BiykZ-rk_qDKnB2c-eCjONdUw2Q$4&Vsd!>o#4&SABI0 z#L;yV#&Js&HTt)xrk9h$A#6$F?d;tmph$6!dY<*R8RCJ>J z#4pS(k!mG`A`XQox32k0d;@H44=TJ#njgP|S0qYF9;3O)bZ7pr6jTWhP%^hZ$xSHV z00g+=HrpqD%&Ov;B?yyCbVXS^DBhxtsZwu|vo|$^`I-MqKG2v=M}QcEtXXjlkN3iy zO;$~F8xqd|&t5K-b2^u0!}gbaVm&Tdzh?~l2RQ`{c_uYAo? z3R5U4{G**dCz(L-b^q-*|D6#rzjet(&q?bTRSxkw%@5ge6Z_z8ih@Ake(Ym{ z`Q`xYeUqdIxfrxv{~9)l2th0$4KqRUNnn+>%0X6mgoB7C;UX6lT2?<<6wt;Pjgk*= z|K+B_N9gnJD}m;|ckRw`*G0Walwb>0=yt*_a_p-7u(H0@0Dx&X18^AV|809-=9jwA z345Tsx(B1=#!S`_yM11P7(SrCbbCTjPP3Zy*&zM}l#B3YiVGt~wNHR!j z3`HJ_CBX7RxU%yff!JeI&6XnL**-f|Jjv_rrNVDGR3?AIpD&>-wd7nz^G^Cz#Q@|` zy-NAd^ueE&1KkFdFmp(%IfcMcXKn)Pi9jDmyxE+?AfARb)`_ z5v#iV-Z9EDn~)4*eJrck0n-gtDZpP=_#7EC_`dj2G{!uK+lNKBPQ2!8cvw;bh`^52 zm{ifqPl1{ST0sqL7XCQIV8Kr3T#diOpYDcHn>D9~X4o~eE6MA&0SbEP%2Sklo@OHt zbc6?kXb?jJ)ufeUhjlntKE73fGzFciL{nt&jA3rqP-Lb*_6k&X zW_W@Oiq`xvtP-!>B~Oj?Z3IR0SoM_13|;T{cM@6w)zKBa$g#>iA0bAXzN#ANa9P&) z7-f9z?{A@hY)O~wcXA)2zUX@S)O3{8xHsD`J6)tMIkY;|zu{Pzr>a&fT~FZmws4nU z!0m-Kyg?bY=mrwUyuSteM zszuGAa{0z+_*(+4=0jf<*k=u80+RWN8M)a+8k%%$z#}A25@09;M;)ffDN;tT57sTR zt?;P|_%qFaKea*DO<<76R7DI-t=1oFSVtddEtSbK2&Q}h-Z>`*F^=w->NcbaIVN`; zhW}yWH;Ua?8?QCwjoG7oC;t+I7!i!J?xr-duo_aZV2b?tBB(ZR!1L5`b@RgmSI-_R zz0-e5pqo>%pDkPh!6I_@uZ~*7#T(fU_NlqSwa#;$G!NNi^tzYM)%E;lUgSCo?-N$4 z;`NujOmAwaEDunuVdfPQf$Z%e2hGP0PY%9Ts!!#V`~oR&B{ap_%SRXpW%Rj42@4P}&~!rUHfiN)8L zhD!d_Fm~0~857lgl;WFJGi`#vl4L&vNN9#FL;RC@cHM-Laou+cF@^D}N{BcmEaaS0 z)QAA2-RxvFN6x=m1}t?OnIF`&P@^P$_^-z7pIp5?N^0fdkE!j_2Rl>G7Had-g|YV+ zrsMZ#u_~^lu&a8{7Sb%?lYn8UsgehH1J-ZCxP{2YXEVfgSdh=|h?@lpMTCjqD{LQR;cm9BHTm4rdk)W)*$uLseXM_Ut?%{CCq6K0vV z1EU4|&agJE(ONBcy$%703i0^cRj$y%3}#3AuMQnRNx+vUjVv(`ZusqSU*TT7X?u`o zzg%CxOXS;NeEe00kSt)0ATK_QTuTT-1!NcalnR755FQ7=t~b3%Zx7;Qb|x)cQE&BE zBi8{$%rZ|;aw7}0d&AdDGY@Kw+8#6&OlPD%#ApUT4AT5jX{!b;oV0#kStA#&N}BP~BSF(V^H z^yc1AOA59KD3+T@uSf_5GU{;wVs+NEp3ZB@)mIH5Y13~W4HiMNd<(ZMzBv3nT=un@ zFaJ(I%@U;Zdw=S~%xlR<31eCRf+PilqEaoyUB6a)@d!JZV}%PzR(4ZH*uU;+MYReD znvF2O+k|y`|5c55Fozp(X0o<&olyOmbvYJWuV{P|H1uq#*6UB*Wsg~G=j&j{lqz_& zsoM!U)`2H?av8R_j99Cr78*BJrrq~EW`9eO{>)VGSI_!Mok6ETsj19LplRT~E$OQl zVl!7EH*aq;EDc}1c*tZM74moB?RsGCu4!=TaivGaVN>}VN8_XEK<_uZyc^W9U-VLG z12ZI6PES8CCBc4M1-Tk2ay*aJ!r#^Evlrbz>csZ@@17-pB)PTpYvXS^QEQ}`iV*`mRL*zT`u3??``W^{IhbyyI4#knItWXg|wj>j#P6`OZ7BZSYQt ztK~u6!S)+LONvcz7HY^Ee=y!YJZ{I>S~cTA&$cjrLt^~j){h5n%)ar1!THtAx+NKR zhX*tbX3v?k2vMQ^_>>6Sh3B(BuYXAgZ$2A(Zks9xbhqRGMhQMRu)#}m?+3NIzO{Dv zPNQWKo z2XQNIHE6Pgdg`PXK0JPd(@5B8% z?yb+A*+WEoiV@5Bas&>5LB=tY@6pA2=D{D-G~Ob@s?ELJ7ekf>*si0d<8*YaAS56% zN~@u0`-*^^1iWQ~y%MKs-pMHQuoW&+HkG~DI~aTk)T%C_4j#tlm2w>+YnCDoWJ#)X zsDlzpIO7)#nhNdh6{PvSR9d&1q_MM81{DkLa5+}i!?wnPzIN^iX}mt`Jbe;40nxX3r>grBzIfUL@^C zk}7i27Cq1_NmiC~Hw`P6Pf}}DyRhfJwbdJqNmvy?%MvqJsc>u}<9K0a%EXahx>NdP zvZq)wo6%mmdZ+#uT1{C*>!Qn>&GkYp8CL{SkH9meI@O~fl3{}Cs$BX`A z+BUTHL}H9$WUuLcByHq~M_3n6sW&jeVQY)6AD>ucE8-vb@6TRIm8(*OFh_iHQ5TGJ zYHDT>c59;kgqdw(t0+qBJYEyw8+tcD>ZY1AxcvEDbf{uG`>K#wACx;aPPQ?-nN6FB z{*fvZ<3d`a5@&juGIFswEs_BoCuw>|J-*&EiDp9anRdp{3KyG-?J}WqM}i52eB}+)^mYczxo(lWQnw>718Ciqu%VLmyPIL8wL& z7Wl4@S7`yj@4S=*0Fe*n?4V;qADFgD4ZS0jF;k9A>sF}=EuA(*w`TJms#}WkxK-A} zQ|z*0h&E}AsRuSy!H}r?I`PhxhRDgmH!xx`V0FFDJ}gu$4D{deFWJbYH!MV2&$; zL~|&q+xSCun;zqfN%K7ss<1_ufdBme_qUoItZ-|YPP(?pd$*WUr|AnJZZMty$LB`V zM{0aFX-{C%!HpHeZnYQd zU#dTLjdZIcb(XRnKGg(S2pD~7cE)U=<$@>4nM97_hjrM52`4vAkVaW>bI~*Dc}D_< zbZ`|vi~tyVKuGwn5D#)EEWD041dAae#i?)|-}MHrJdZ49r0u3b%JL&l-WdA(7~ z0~IC5*WUXz-klcbm!eA^=H1n%EC06VftjCgf<|umZ$j#BPP1LRN0{yFC*Jw3d%n;;ml)SY?V^w<9KruKG~yH@`~?nGcVO5n=-_}V{v+_~6z!ez*@sOim-NGEMz zBHe(V_q*9e8O^+$*Uww)H*z~?HhUUe001FIFd-fuV9N>tAiz%*$7?9eN>&TQ&yi~5 zMr6?`fg{j>64!)6?12Q-)~W{e+g6J)Ay;HfA&Lx70niNSYba2*ax&Y0lNg9a@UjsC zqLg9^`q~DQ@BOd#&NHfs@7?!FNFel@KoF2#LXl9UNUtFj>Am+ZARt9Cp-Gh%AOS=O zy-F8Q5rH5AA{|8JM;A~K6|taj;D7F2=d63rTIaq$cfXlgYi8D(dFHdr^X%`lcgUX& zq;w#oRv8vB7OL|y9*tx}$k21f`MfK`8?1ORQ6~sd3^HOj0R7fGQBakTNg7|%X4jE% zw^}c$OqI#HB1u9K7eNbB_ltDmIMJKHqq_4eA*6e}mxTkz|PuXVT=8$G0bVw7HtPotzMuy+E z`Zg|*I}7fBhq)B*0N&f55R-u~`hhrJ&PyYU*97eM<=HkrerY01Q*a1EIKYrh##MlA znJ_1a4oOYNMf(Z_{)GgQG^jqF-FS90%wfJn)Gir=*K=Zl6Tfp2Lb(f zD{vwvyG+=+;~(AaLo5W073fC<7|~La;}1i?l|}$SGGGt{1hrs_;>WutZ2^FIO2hQJ z+0erKRaeDQ8L$N$hy8gW8lq5JEg`HG*au8YTTQ{I#AMR#5jf<~FSLX6SEizqZt8!t zY=+Zd2Dl0VOrHrLT6mF0#ENQa<*O_J<~kOx*tUEi1e$%B&sS!Af+PcZ&2)iWh)W=x zWK%Q=;9c8x!rc?1;b%EXXH7e=dSJ}c{HrUPd%N27bRN6l zr;4t8Ae4!XZUn$;)=WM^ZFT9qSG7h;tkG+cAd&s!Xt=zNtl+Voct^HX~FAqa3aI7u#jPd z9^tlFseEY-TDhqa)uxHb@md4eO1Da=#X7Fd$-6!Md zx-DbxP*q36tOt6W%QYUEf4ZPeY~6k3KbM!Et)`$KE_yX8%f6l#GEl!x`NFW?m{E;B zGV|{(OY+|kN8)I75ceP*m=chP55wMrMHA7>jtk`_@?xBNg+a;jHyQidFh4zIM~lsuc* zBiT;J3X!a`De4+*v2CP!k^i+YNtH6QZD!_H#h!O5_{+@qhd)TQmdVJB1`C(`%2jkM z@N_xu5y@IgTgq52FD|YbuIf2yQ{q&#KyBauDUI!JKx4&92U|zgf(y3nBHnDO8*_BJ zAB6uJ_Wjrdk9zp3zjrCq|LgV!P~1FytKg;+vAq4-&u>5MKVAK1w3b+3Iyd%b^K*+GnZU>c}CSWaf>rL=6 z!Jo(I6C35&@v0^ZuJy-tE^jl0$4Wx9DkM?1^>f>BSltfK-8$x%ew60yCv`-Vfqa{EA0 zOlNQQ_+}w=g7JswX0y@$+qcAY>)k`*HtfLs;C+x`+kxYw0D7OowO?nqngY}UzAS{k zUn*XREBFxFVEBZ1t9!4&d*8o};6*Nt_8}U{{8kNHfR`F@<3D)pyDQBuO7r=Oa&W4U z^r*Qx>PkZ>cJuCen^F-6Da837XS7nnb*yXE*Do=jGPOX5SV?-xH$}tQT|$duE!%lJ}n4igi#) zrY}ieKftL_P-WGsg+$~?4*)SBd+!8r5>G4SXXqbs6ow+Qr*Fq`2Z)9Vp_@bfErXIG z5aizUw5eVJf%ol34INY%vKQ-z6aPEE+dzM=c?XIwZPs_bzR&Ye1v|VlwEJLF&(;(s z5$GRwn&rqzOA{`6a%T-ybMTfqGxzi1i^3D^sXULg2Jw@>y2s7Wyeih}PbD!g>C;i~ z>`La~7Oi%clVvLJEb~+!oaPn8AJDz2*x#Ux1utkw^T8;=ARxe1laki2YLONPx`&y& z!_5Ns)Gls@#`E&0Qjl>2^q9OE2;CeA)d`_8(i{=)*n#F5Mt-KaKu+Cc7GCXiE^Wm% zKjxMM^qVr&i1kQhDi%D@W&lS8;Mtk->#Ij!XfIPvQLR(6yOgt( z@^nI5F@l|+1%@-+n(^%&E5K;A?4}2AZuYr|=e$c*PUk+!ka`^V`|}OFagUN&ZH8Bd z_>H@28wR2!1$Ljd>H7XgEvqY@Jr8V4Z!dIv8(8%>&R6u{jYR;paiiE)Seid#?5nua z((tLp;a0|BO>v5zP{GNWa~zpYkGn_eZvDApz+V#C;1h9D5E(16Ij&`vNZLC2`q3t6 zhAa_hC4mttxhE0(bDmqzMStYo*_9@G`Nm)irvNF5?Z%-h)1&;qk^UC1@?U2cCIM=H zFUD_pPf}7+Yoc!9dvrB)?nPSCU>F#TXGNgo@ldY$TbY1E#b}4ZZ~sD0Gw+lDd^ZA= z(vfyZwa=q9AVIQdoTa|>`m@dhW1kAMme2Aw+M!$u{nwLw57RJen1^+*xbA-k`$zn9 zho3uluo3vABGH_8^GSQhH<++@gt`6hi=aEUt=0I>UzYA1cX!qgEzNVy%*pj9V?8!6 z@xKmotI}^C`wAcusNvCGg(10^oJ<($%dCuPZOewLt>nPL9r8+f zck_9+YweV2i1FR;E@yY;EQ&5?IJ;#$tTRVO-n1ll_?Q(M+U#1X*hyL(&P1r|^1ttA z91b{PfM{$}FrcD}M>G zRa>CcXb5P&E4#~KPkjUcG`x-A_4CbP=T=WXy+u*?{qw4>yLaEZ)$i=-Fd_~e%`(`r z7TwrWoX{d*$Zczt3$9>hm93=e56yxpblsUAaJ2{wUyw^{>Cnp+gT2#5a1-FIBX``#`CYy}Q0-_x zbqz#x5KC!;AYk>}P|;Zi)q)C)LUh7(DC7~DIzdxle6~%|-gaKX-7_ZxG%U zMki8iTU!y$eg>mLvxZ-|wI+}FmN5WQ0F`AxSK1I?nxRihvmhpCLaL4oO?i&irnL~q zz4bM=h)TR1k2X$i<@Qi)ofsuD^Ap@7RRFe9?h=Ti{4&(!+RG}!0;aR$6%w)> zfz8{ZQCd1Y3OFMi@5@iVhoj&mxn25?+A_Aoc%9iHI;7VKB|8u(8=3iM6?Kz28zsb& z;)T$miBjxHh`6LDCTI$gW02u(N;f|8$mm?bz7>pqpvvU_-R$xAjlR9X=930ayq!HY zc2gXz)^Z=?kwU&wq$RLE^q}##M#8?718d^y8#bH|rSffu#XswTuH6ppP=&3_i(}bxy(oR62q%S?|qp!dgH~M><{0Vvz(~C zjwCfd)ZX{E20O}aUR$ya^~r(@6o0syauCSqYM=K!sBe2Y?xl+Hh2hSOkKjRC^Ug=I z5;ApVdN&%lw}~^w$&BBOhbj0bR`2@`e2XcCUAnAuy^(-A`DcWrK)2~kXr(0b^ioK2SHYhpHEm0?Mf4~K^>|HD;8 zDpQudFLV~oR#_9Kn`v$-to*7x2o$xOn($I456U!ZC>z}Ykq4<4Z~Kn6|$JoKY#trDEoOh z6300Y9+j``So$k&R4RSR9L+>C6ru?<;D3ZPq~fpCu}yxnzgBBdJHhU9GGsHI$6b=RIsDb50U45+Sgl|9f=lQ$%Ea`s(0Di;!GAa=RxK_O;XcBNhgN|DwoQ zj0!Ju1+?OZD*6W@zzhi{>6Vso8aDu7Qsu?K1|vXgfPy(|JT3@j#QT1*{m4p%I#Uv$ zUC4JwN-w6#zkl|Uf5v{E#{Qh6SROE=i<(vdjn)QQ4|gj=Q=(b`u5AJ}5N;U~S{ALv zpVQxk{7H!a$)6Lz=TSb$lz&WKWTTsOuAZB^vsOL)w}}e%NTgR#t}v&b0h*2+Hf4>f z&pBk$LQ)8pS9zHA#XJ_SAmIp2IzV!+t0_ zKm%?USuu`?#Ga4kF6yc$JRGoXA!lsgc6>aaKI?gy94ie48!)7Tmv;yG%j`l@IPVV_ zAu)#j==z4TuFwc(F=ZoxEjk|L-&cG+^F8(GuLjyvb45MVDEhqFT51X?qO1iB;->;< zm;+JL2myxWL+KsY7hWDLv|0)6+OUBwfSKn%Z@)ddWTYV}ke^n=d7TDKG|+k{DP1et z^}39mnQq0Jb5LOAKs->vq`pzUs6{R{(v*%wp{fxqz z`7ce_5^q@c0kY-u*B4j}8e4=3V~%K{HW|TJrRW_RO8?h553$_N-Ujj`l}|6k>-!sc zz)keeThX92E2#tNO;tk|^H0p|mopyq6nA{PQE9A1iuKFoH<=`Fv}q;KX#6a%?y|XT zwfAX7=1~z<`vr2Rm~nVu*|vqED4+3CYK&Hrbzo?9INC%CEFp?E;z?y=Di6+n8JE*B z4doSdWp`3@D%S`qybzyL(5u=Mv3YGoxjIOBfNnVa+LIrBVl$Qt2TATGK+Ev;NcT-G zeLWD1d*v_}OL(jvCJ;U9ZImoL;)AZX2A^kU90+^faI>5-yw1QWEv@s7f|e+Xh|dt| zcXVa00QNvfpSxY*0PT@UD{7?dg&%RdrIc_K5JyS9cXCn-HGX03x$)W^RRA zl_i40{WsV$Nc0x<^uVqxi3NN#@)6<}T($NrxAa8f!B^|FF@*8lJ=?$SQwlS74<<^M z=BMU1IGrw#`-5SFdugoLnbw>B-Wt`uOcZd4Q2+k;DLzb@yDae-_A+oje+SiEx2?{( z$e3;&ZQ1cw;)fNl?f!GG`?)`Y?|0g~7=3+9zD{P^%oIlf22oMssB8d&rekD??f?*- zsFRc7#b-G#*DpG((&mx&V&a1g!C#-G`BGOoM0>028uYapTQ>?a{tCz8AuMXCsKq8i z(C(Phr%q4_#6205PVUGVN2On(Y}t2Cd|FFoHuw9lq=xkBy7UNNdDc}wXAYKl(14f@ zShI{y6vtOiGxsW!#cngrLE(WAk6EFakD$eI$;7hC<}-POz&erc!BPx==KS>{z;9a> z5jvQtro_lsSlz4n1ln5Ep&NoZiON^iTrxih0#xFuMFK_2GGT-x$Je_J(tAZ(rqcXH(%F@g9pUWM<5ho{iv=SFO0?yBC1y@}uM8Pet!bq2DX&tfWoXuI_y9-7BqdbM z!|$REaUfU1)7X`yigR1Ar9T;HCdY0N9rqt{VGOd44pD9rn9y zUCb2R>IeO*))8jKlMA=2w^#}9XfaS}uW#E;5ViS=vZD z@~qSvzW-WS7m@5##Z1pEQ02{Vlna+2mBQvT zKL)wZb;ReqV@Q3eKTbs0MV63`3!29qH;>aZ0?^}zlB!-XQi~EcMcDj_PnjtU8D*J= zkwW0WIPm!7A96h1ZIlxSA0m&D8W3kdu?;~6s`cDCo*2%8A(8-5 z-~M5M{xFw}1Za$2s^xHjzM|x(fQE?h>bBszg9kk7b(%&P{(iEwrjZ%*T7w;yNhpUu61JwMW8lhYNXjH=>~OY-Dta*yATQrATZsKE@X9Em^XV164=kf} zXTs(pzFvL2nT~#h>luj0C1rOA_@h(HHoNNZpRF86s@DALeUC_cHEC}Y zuNM(Iwn0zq_XeJ(CN543^Mu<3#>>aeiSHYF3n{=JykBx2@)nwHTQQ$hcAphV+^Eu% z7ks<*M%aZ_qzP#eAu!3K2qc?UCsu45h^)@$VyT`51WH^fOw}b#UCkL96?)HUn~Gth zn~>CCTONY^LRBai>)^X2!!H*DJv-?AsRB%wsv?|OPMapX0q#YzSM=wsgJvP_&!zW% zVdi!Ij&R(}SnQ{$nTbCruQ3F1HKWThGf`i?bR~-fp=1$*{N%tnztIXdQH*w*DzJj$#Vf%hBr;fi@?#Wk~2Q=7<@D)9TW!`rmGZelC4sK2ssUPdK zchBzVl+j^j8~IgH4kh1^*>7?;&#T}>38#2-RZ5Ib zlxI#`NOYAIW_~A}Xqj9?upl3uCsNqm)|$>l;uZfINnet^ax9vX$hR(|?QM|6pTqP@ z$Dds^vt2Zs#K*~)K_?JiJ<3}vK})d-{-i)X!05%o!Uvt?p-Q4B3Tw3;HgyLGYB5Fe zFkc`ysG$Ng1q@Cv=c-Fv!l;syN0cb?cl{y&u(FC(iaEqGlqj6^>+XT7IUo&dILMTb?2@lWxYkqUj?d*3l=YXh|+aHpD zPJu~BCq>XULe^88a&`IPC0f>$G^t;X5bXIZ0q`{rP?ikvc(y>$QNl?#ZQ%p^3A@Vy z2%)?;Tr=&mCtONmL+zpcP#~GUakqGdg6a}=Ki#c32wst8EZpR-SI)UN%&f~5XsCV- zM45PgCjgXEI6na3lDOilAy9>d5W9{GqxixJ^*baQ~ zGCt>XCtq1vqrw4Q;C@Eto;=s*rF6HDkiHu+HM5Z>qWrDxWDljW=;{ianpuAVDHRkW zYx`uzqJpq8lOPN8h~FS7NAZb?>=hsk%iL2TWeIuudJeWYndHkcG{2rc(PsXOsw^Vl z_IuJs@iGLh?7n(sRvwa$FfT7gv4KD@3l^xf8h6!jb>qa*DM*uIsJ0!ypES#-!kmPL zqeq%L^K>zQJj-l9P=!Z%RE|<9hp>SvtK2REHtjA9_3JXwkw`4eIntcn+}wQeduSz$ z(M<7*FfQ$H%16X2q2T-cUn3s!l{yhqG^2$pJ0+y8apjW14m!PnRML<~e7hYg!pSUK9K z+|=JYb`@pRcpQn*E9PPZoU$<8B59W_IJQGAgfGE%KN#c<{0EHx%L~)X!H>6=RZP3d zR&y;7&6F%#Wieqzf^0Ep>0*TS9U^Shtyj_DRj9T}?kBCB)c^nzV%o}6i@{GR`|O$W zM+s;RE``O)`=p=wzQiP6h&6k*+2~GC3f{B(Re*TRoV|N&gHu!3ba=;kch^YJ zk~GxA+;^Yr^k;>2?%NM6#Tu5Y+J6*j(Gsj1zCdnBg$*_f9$a)RH8Qz<{z%{QD zauiA?Cb7z@>1L7IiLAExz8xyHAy+5wqZ~%9HMhF{|1!sa@=gB-oL(RY0JMMsfK>oM ukqkjW*N6WH!~IXU+`j>4!<*;; literal 0 HcmV?d00001 diff --git a/packages/standard/i18n/zh_CN.lua b/packages/standard/i18n/zh_CN.lua index f468b57c..645350e9 100644 --- a/packages/standard/i18n/zh_CN.lua +++ b/packages/standard/i18n/zh_CN.lua @@ -315,6 +315,13 @@ Fk:loadTranslationTable{ ["$fastchat_m14"] = "哥们,给力点行吗?", ["$fastchat_m15"] = "哥哥,交个朋友吧。", ["$fastchat_m16"] = "妹子,交个朋友吧。", + ["$fastchat_m17"] = "我从未见过如此厚颜无耻之人!", + ["$fastchat_m18"] = "你随便杀,闪不了算我输。", + ["$fastchat_m19"] = "这波,不亏。", + ["$fastchat_m20"] = "请收下我的膝盖。", + ["$fastchat_m21"] = "你咋不上天呢?", + ["$fastchat_m22"] = "放开我的队友,冲我来。", + ["$fastchat_m23"] = "见证奇迹的时刻到了。", ["$fastchat_f1"] = "能不能快一点啊,兵贵神速啊。", ["$fastchat_f2"] = "主公,别开枪,自己人!", ["$fastchat_f3"] = "小内再不跳,后面还怎么玩啊?", @@ -331,6 +338,13 @@ Fk:loadTranslationTable{ ["$fastchat_f14"] = "哥们,给力点行吗?", ["$fastchat_f15"] = "哥,交个朋友吧。", ["$fastchat_f16"] = "妹子,交个朋友吧。", + ["$fastchat_f17"] = "我从未见过如此厚颜无耻之人!", + ["$fastchat_f18"] = "你随便杀,闪不了算我输。", + ["$fastchat_f19"] = "这波,不亏。", + ["$fastchat_f20"] = "请收下我的膝盖。", + ["$fastchat_f21"] = "你咋不上天呢?", + ["$fastchat_f22"] = "放开我的队友,冲我来。", + ["$fastchat_f23"] = "见证奇迹的时刻到了。", ["aaa_role_mode"] = "身份模式", [":aaa_role_mode"] = [========================================[ From 7661cabd5838be1a906efc01503ad57f2fc888c7 Mon Sep 17 00:00:00 2001 From: notify Date: Mon, 5 Feb 2024 11:10:58 +0800 Subject: [PATCH 19/30] Changelog: v0.4.7 --- CHANGELOG.md | 2 +- CMakeLists.txt | 2 +- android/AndroidManifest.xml | 4 ++-- lua/client/i18n/zh_CN.lua | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 553d5116..391202f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # ChangeLog -## v0.4.6 +## v0.4.6 & v0.4.7 - 攻击范围状态技类新增基础值修正函数 - 伤害值在一个技能处理后小于1会终止当前事件 diff --git a/CMakeLists.txt b/CMakeLists.txt index c8656160..5df080f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16) -project(FreeKill VERSION 0.4.6) +project(FreeKill VERSION 0.4.7) add_definitions(-DFK_VERSION=\"${CMAKE_PROJECT_VERSION}\") find_package(Qt6 REQUIRED COMPONENTS diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index d6ad8892..bbac166b 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -3,8 +3,8 @@ + android:versionCode="407" + android:versionName="0.4.7"> diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index acb33f1f..2e92b714 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -317,9 +317,9 @@ FreeKill使用的是libgit2的C API,与此同时使用Git完成拓展包的下 ["Resume"] = "继续", ["Bulletin Info"] = [==[ - ## v0.4.6 + ## v0.4.7 - 底层结算优化 + 补全快捷短语 ]==], } From c3cdb8dc50db700178bf60797d92fc7a2a2ef053 Mon Sep 17 00:00:00 2001 From: notify Date: Sat, 17 Feb 2024 09:46:48 +0800 Subject: [PATCH 20/30] =?UTF-8?q?Ai=E5=B0=8F=E6=B7=BB=E5=8A=A0=20(#320)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 出牌策略仍然搞不定呀 - Qml: 新增leval函数可获得lua表达式的值 - 新增AbstractRoom类 去除冗余 --- Fk/main.qml | 9 + lua/client/client.lua | 31 +- lua/core/abstract_room.lua | 52 +++ lua/core/engine.lua | 14 +- lua/freekill.lua | 1 + lua/server/ai/smart_ai.lua | 22 +- lua/server/room.lua | 27 +- packages/standard/ai/init.lua | 286 +++-------------- packages/standard_cards/ai/init.lua | 481 +++------------------------- src/ui/qmlbackend.cpp | 23 ++ src/ui/qmlbackend.h | 1 + 11 files changed, 218 insertions(+), 729 deletions(-) create mode 100644 lua/core/abstract_room.lua diff --git a/Fk/main.qml b/Fk/main.qml index 75fe3c0b..54a913a8 100644 --- a/Fk/main.qml +++ b/Fk/main.qml @@ -269,6 +269,15 @@ Window { } } + 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); } diff --git a/lua/client/client.lua b/lua/client/client.lua index 95978400..d5ff5d07 100644 --- a/lua/client/client.lua +++ b/lua/client/client.lua @@ -1,16 +1,14 @@ -- SPDX-License-Identifier: GPL-3.0-or-later ----@class Client +---@class Client : AbstractRoom ---@field public client fk.Client ---@field public players ClientPlayer[] @ 所有参战玩家的数组 ---@field public alive_players ClientPlayer[] @ 所有存活玩家的数组 ---@field public observers ClientPlayer[] @ 观察者的数组 ---@field public current ClientPlayer @ 当前回合玩家 ---@field public discard_pile integer[] @ 弃牌堆 ----@field public status_skills Skill[] @ 状态技总和 ----@field public banners table @ 左上角显示的东西 ---@field public observing boolean -Client = class('Client') +Client = AbstractRoom:subclass('Client') -- load client classes ClientPlayer = require "client.clientplayer" @@ -26,6 +24,7 @@ local pattern_refresh_commands = { } function Client:initialize() + AbstractRoom.initialize(self) self.client = fk.ClientInstance self.notifyUI = function(self, command, jsonData) fk.Backend:emitNotifyUI(command, jsonData) @@ -49,21 +48,8 @@ function Client:initialize() end end - self.players = {} -- ClientPlayer[] - self.alive_players = {} - self.observers = {} 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_generals = {} @@ -71,7 +57,7 @@ function Client:initialize() end ---@param id integer ----@return ClientPlayer +---@return ClientPlayer? function Client:getPlayerById(id) if id == Self.id then return Self end for _, p in ipairs(self.players) do @@ -237,15 +223,6 @@ function Client:setCardNote(ids, msg) 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) local data = json.decode(jsonData) ClientInstance:setCardNote(data[1], data[2]); diff --git a/lua/core/abstract_room.lua b/lua/core/abstract_room.lua new file mode 100644 index 00000000..0a4d837b --- /dev/null +++ b/lua/core/abstract_room.lua @@ -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 @ 这个房间中含有的状态技列表 +---@field public filtered_cards table @ 见于Engine,其实在这 +---@field public printed_cards table @ 同上 +---@field public skill_costs table @ 用来存skill.cost_data +---@field public card_marks table @ 用来存实体卡的card.mark +---@field public banners table @ 全局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 diff --git a/lua/core/engine.lua b/lua/core/engine.lua index 596af613..57c41da1 100644 --- a/lua/core/engine.lua +++ b/lua/core/engine.lua @@ -78,20 +78,20 @@ function Engine:initialize() end local _foreign_keys = { - "currentResponsePattern", - "currentResponseReason", - "filtered_cards", - "printed_cards", + ["currentResponsePattern"] = true, + ["currentResponseReason"] = true, + ["filtered_cards"] = true, + ["printed_cards"] = true, } function Engine:__index(k) - if table.contains(_foreign_keys, k) then + if _foreign_keys[k] then return self:currentRoom()[k] end end function Engine:__newindex(k, v) - if table.contains(_foreign_keys, k) then + if _foreign_keys[k] then self:currentRoom()[k] = v else rawset(self, k, v) @@ -548,7 +548,7 @@ function Engine:_addPrintedCard(card) end --- 获知当前的Engine是跑在服务端还是客户端,并返回相应的实例。 ----@return Room | Client +---@return AbstractRoom function Engine:currentRoom() if RoomInstance then return RoomInstance diff --git a/lua/freekill.lua b/lua/freekill.lua index 27c169d9..24a90185 100644 --- a/lua/freekill.lua +++ b/lua/freekill.lua @@ -33,6 +33,7 @@ UsableSkill = require "core.skill_type.usable_skill" StatusSkill = require "core.skill_type.status_skill" Player = require "core.player" GameMode = require "core.game_mode" +AbstractRoom = require "core.abstract_room" UI = require "ui-util" -- 读取配置文件。 diff --git a/lua/server/ai/smart_ai.lua b/lua/server/ai/smart_ai.lua index 9d834459..857f4bf1 100644 --- a/lua/server/ai/smart_ai.lua +++ b/lua/server/ai/smart_ai.lua @@ -132,9 +132,24 @@ end -- 真的要考虑ViewAsSkill吗,害怕 --------------------------------------------------------- +-- 使用牌相关——同时见于主动使用和响应式使用。 --- 键是prompt的第一项或者牌名,优先prompt,其次name,实在不行trueName。 ---@type table -fk.ai_use_card = {} +fk.ai_use_card = setmetatable({}, { + __index = function(_, k) + -- FIXME: 感觉不妥 + local c = Fk.all_card_types[k] + if not c then return nil end + if c.type == Card.TypeEquip then + return function(self, pattern, prompt, cancelable, extra_data) + local slashes = self:getCards(k, "use", extra_data) + if #slashes == 0 then return nil end + + return self:buildUseReply(slashes[1].id) + end + end + end, +}) local defauld_use_card = function(self, pattern, _, cancelable, exdata) if cancelable then return nil end @@ -219,6 +234,9 @@ smart_cb["PlayCard"] = function(self) local card_names = {} for _, cd in ipairs(cards) do -- TODO: 视为技 + -- 视为技对应的function一般会返回一张印出来的卡,又要纳入新的考虑范围了 + -- 不过这种根据牌名判断的逻辑而言 可能需要调用多次视为技函数了 + -- 要用好空间换时间 table.insertIfNeed(card_names, cd.name) end -- TODO: 主动技 @@ -277,7 +295,7 @@ function SmartAI:isFriend(target) if Self.role == target.role then return true end local t = { "lord", "loyalist" } if table.contains(t, Self.role) and table.contains(t, target.role) then return true end - if Self.role == "renegade" or target.role == "renegade" then return math.random() < 0.5 end + if Self.role == "renegade" or target.role == "renegade" then return math.random() < 0.6 end return false end diff --git a/lua/server/room.lua b/lua/server/room.lua index d194dd6d..049a2e6a 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -3,7 +3,7 @@ --- Room是fk游戏逻辑运行的主要场所,同时也提供了许多API函数供编写技能使用。 --- --- 一个房间中只有一个Room实例,保存在RoomInstance全局变量中。 ----@class Room : Object +---@class Room : AbstractRoom ---@field public room fk.Room @ C++层面的Room类实例,别管他就是了,用不着 ---@field public id integer @ 房间的id ---@field private main_co any @ 本房间的主协程 @@ -15,7 +15,6 @@ ---@field public game_finished boolean @ 游戏是否已经结束 ---@field public timeout integer @ 出牌时长上限 ---@field public tag table @ Tag清单,其实跟Player的标记是差不多的东西 ----@field public banners table @ 左上角显示点啥好呢? ---@field public general_pile string[] @ 武将牌堆,这是可用武将名的数组 ---@field public draw_pile integer[] @ 摸牌堆,这是卡牌id的数组 ---@field public discard_pile integer[] @ 弃牌堆,也是卡牌id的数组 @@ -23,14 +22,13 @@ ---@field public void integer[] @ 从游戏中除外区,一样的是卡牌id数组 ---@field public card_place table @ 每个卡牌的id对应的区域,一张表 ---@field public owner_map table @ 每个卡牌id对应的主人,表的值是那个玩家的id,可能是nil ----@field public status_skills Skill[] @ 这个房间中含有的状态技列表 ---@field public settings table @ 房间的额外设置,差不多是json对象 ---@field public logic GameLogic @ 这个房间使用的游戏逻辑,可能根据游戏模式而变动 ---@field public request_queue table ---@field public request_self table ---@field public skill_costs table @ 存放skill.cost_data用 ---@field public card_marks table @ 存放card.mark之用 -local Room = class("Room") +local Room = AbstractRoom:subclass("Room") -- load classes used by the game GameEvent = require "server.gameevent" @@ -67,18 +65,14 @@ dofile "lua/server/ai/init.lua" --- 构造函数。别去构造 ---@param _room fk.Room function Room:initialize(_room) + AbstractRoom.initialize(self) self.room = _room self.id = _room:getId() - self.players = {} - self.alive_players = {} - self.observers = {} - self.current = nil self.game_started = false self.game_finished = false self.timeout = _room:getTimeout() self.tag = {} - self.banners = {} self.general_pile = {} self.draw_pile = {} self.discard_pile = {} @@ -86,16 +80,8 @@ function Room:initialize(_room) self.void = {} self.card_place = {} self.owner_map = {} - self.status_skills = {} - for class, skills in pairs(Fk.global_status_skill) do - self.status_skills[class] = {table.unpack(skills)} - end self.request_queue = {} self.request_self = {} - self.skill_costs = {} - self.card_marks = {} - self.filtered_cards = {} - self.printed_cards = {} self.settings = json.decode(self.room:settings()) self.disabled_packs = self.settings.disabledPack @@ -568,15 +554,10 @@ function Room:removeTag(tag_name) end function Room:setBanner(name, value) - if value == 0 then value = nil end - self.banners[name] = value + AbstractRoom.setBanner(self, name, value) self:doBroadcastNotify("SetBanner", json.encode{ name, value }) end -function Room:getBanner(name) - return self.banners[name] -end - ---@return boolean local function execGameEvent(type, ...) local event = GameEvent:new(type, ...) diff --git a/packages/standard/ai/init.lua b/packages/standard/ai/init.lua index 04a7ab47..e25c8612 100644 --- a/packages/standard/ai/init.lua +++ b/packages/standard/ai/init.lua @@ -1,266 +1,80 @@ require "packages.standard.ai.aux_skills" ---[[ -fk.ai_use_play["rende"] = function(self, skill) - for _, p in ipairs(self.friends_noself) do - if p.kingdom == "shu" and #self.player:getCardIds("h") >= self.player.hp then - self.use_id = {} - for _, cid in ipairs(self.player:getCardIds("h")) do - if #self.use_id < #self.player:getCardIds("h") / 2 then - table.insert(self.use_id, cid) - end - end - self.use_tos = { p.id } - return - end - end - for _, p in ipairs(self.friends_noself) do - if #self.player:getCardIds("h") >= self.player.hp then - self.use_id = {} - for _, cid in ipairs(self.player:getCardIds("h")) do - if #self.use_id < #self.player:getCardIds("h") / 2 then - table.insert(self.use_id, cid) - end - end - self.use_tos = { p.id } - return - end - end -end - -fk.ai_card["jijiang"] = { priority = 10 } - -fk.ai_use_play["lijian"] = function(self, skill) - local c = Fk:cloneCard("duel") - c.skillName = "lijian" - local cards = table.map( - self.player:getCardIds("he"), - function(id) - return Fk:getCardById(id) - end - ) - self:sortValue(cards) - for _, p in ipairs(self.enemies) do - for _, pt in ipairs(self.enemies) do - if p.gender == General.Male and pt.gender == General.Male and p.id ~= pt.id - and c.skill:targetFilter(pt.id, {}, p.id, c) then - self.use_id = { cards[1].id } - self.use_tos = { pt.id, p.id } - break - end - end - end - for _, p in ipairs(self.friends_noself) do - for _, pt in ipairs(self.enemies) do - if p.gender == General.Male and pt.gender == General.Male and p.id ~= pt.id - and c.skill:targetFilter(pt.id, {}, p.id, c) then - self.use_id = { cards[1].id } - self.use_tos = { pt.id, p.id } - break - end - end - end -end - -fk.ai_card["lijian"] = { priority = 2 } - -fk.ai_use_play["zhiheng"] = function(self, skill) - local card_ids = {} - local cards = table.map( - self.player:getCardIds("he"), - function(id) - return Fk:getCardById(id) - end - ) - self:sortValue(cards) - for _, h in ipairs(cards) do - if #card_ids < #cards / 2 then - table.insert(card_ids, h.id) - end - end - if #card_ids > 0 then - self.use_id = card_ids - end -end - -fk.ai_use_play["kurou"] = function(self, skill) - if #self:getActives("peach") + self.player.hp > 1 then - local slash = Fk:cloneCard("slash") - if slash.skill:canUse(self.player, slash) and not self.player:prohibitUse(slash) then - fk.ai_use_play.slash(self, slash) - if self.use_id then - self.use_id = {} - self.use_tos = {} - end - end - end -end - -fk.ai_use_play["fanjian"] = function(self, skill) - for _, p in ipairs(self.enemies) do - if #self.player:getCardIds("h") > 0 then - self.use_id = {} - table.insert(self.use_tos, p.id) - break - end - end -end - -fk.ai_use_play["jieyin"] = function(self, skill) - local cards = table.map( - self.player:getCardIds("h"), - function(id) - return Fk:getCardById(id) - end - ) - self:sortValue(cards) - for _, p in ipairs(self.friends_noself) do - if #cards > 1 and p.gender == General.Male and p:isWounded() then - self.use_id = { cards[1].id, cards[2].id } - table.insert(self.use_tos, p.id) - break - end - end -end - -fk.ai_use_play["qingnang"] = function(self, skill) - local cards = table.map( - self.player:getCardIds("h"), - function(id) - return Fk:getCardById(id) - end - ) - self:sortValue(cards) - for _, p in ipairs(self.friends) do - if #cards > 0 and p:isWounded() then - self.use_id = { cards[1].id } - table.insert(self.use_tos, p.id) - break - end - end -end +-- 魏国 fk.ai_skill_invoke["jianxiong"] = true +-- TODO: hujia +-- TODO: guicai 关于如何界定判定的好坏 需要向AI中单独说明 -fk.ai_card["hujia"] = { priority = 10 } +fk.ai_skill_invoke["fankui"] = function(self) + local room = self.room + local logic = room.logic -fk.ai_response_card["#hujia-ask"] = function(self, pattern, prompt, cancelable, data) - local to = self.room:getPlayerById(tonumber(prompt:split(":")[2])) - if to and self:isFriend(to) and (self:isWeak(to) or #self:getActives(pattern)>1) then - self:setUseId(pattern) - end + -- 询问反馈时,处于on_cost环节,当前事件必是damage且有from + local event = logic:getCurrentEvent() + local dmg = event.data[1] + return self:isEnemy(dmg.from) end -fk.ai_response_card["#jijiang-ask"] = fk.ai_response_card["#hujia-ask"] +fk.ai_skill_invoke["ganglie"] = fk.ai_skill_invoke["fankui"] -fk.ai_skill_invoke["fankui"] = function(self, data, prompt) - local damage = self:eventData("Damage") - return damage and damage.from and not self:isFriend(damage.from) -end +-- TODO: tuxi -fk.ai_response_card["#guicai-ask"] = function(self, pattern, prompt, cancelable, data) - local cards = table.map(self.player:getHandlyIds(true), function(id) - return Fk:getCardById(id) - end) - local id = self:getRetrialCardId(cards) - if id then - self.use_id = id - end -end - -fk.ai_skill_invoke["ganglie"] = function(self, data, prompt) - local damage = self:eventData("Damage") - return damage and damage.from and not self:isFriend(damage.from) -end - -fk.ai_judge["ganglie"] = { ".|.|heart", false } - -fk.ai_skill_invoke["luoyi"] = function(self, data, prompt) - for _, p in ipairs(self.enemies) do - if #self:getActives("slash") > 0 and not self:isWeak() then - return true - end - end +fk.ai_skill_invoke["luoyi"] = function(self) + return false end fk.ai_skill_invoke["tiandu"] = true -fk.ai_skill_invoke["yiji"] = true +-- TODO: yiji fk.ai_skill_invoke["luoshen"] = true -fk.ai_skill_invoke["guanxing"] = true +-- TODO: qingguo -fk.ai_skill_invoke["tieqi"] = function(self, data, prompt) - local use = self:eventData("UseCard") - for _, p in ipairs(TargetGroup:getRealTargets(use.tos)) do - p = self.room:getPlayerById(p) - if self:isEnemy(p) then - return true - end - end +-- 蜀国 +-- TODO: rende +-- TODO: jijiang +-- TODO: wusheng +-- TODO: guanxing +-- TODO: longdan + +fk.ai_skill_invoke["tieqi"] = function(self) + local room = self.room + local logic = room.logic + + -- 询问反馈时,处于on_cost环节,当前事件必是damage且有from + local event = logic:getCurrentEvent() + local use = event.data[1] ---@type CardUseStruct + return table.find(use.tos, function(t) + return self:isEnemy(room:getPlayerById(t[1])) + end) end fk.ai_skill_invoke["jizhi"] = true +-- 吴国 +-- TODO: zhiheng +-- TODO: qixi + fk.ai_skill_invoke["keji"] = true +-- TODO: kurou + fk.ai_skill_invoke["yingzi"] = true -fk.ai_skill_invoke["lianying"] = true +-- TODO: fanjian +-- TODO: guose +-- TODO: liuli +fk.ai_skill_invoke["lianying"] = true fk.ai_skill_invoke["xiaoji"] = true +-- TODO: jieyin + +-- 群雄 +-- TODO: qingnang +-- TODO: jijiu +-- TODO: wushuang +-- TODO: lijian fk.ai_skill_invoke["biyue"] = true - -fk.ai_choose_players["tuxi"] = function(self, targets, min_num, num, cancelable) - for _, pid in ipairs(targets) do - local p = self.room:getPlayerById(pid) - if self:isEnemy(p) and #self.use_tos < num then - table.insert(self.use_tos, pid) - end - end -end - -fk.ai_active_skill["yiji_active"] = function(self, prompt, cancelable, data) - for _, p in ipairs(self.friends_noself) do - for c, cid in ipairs(self.player.tag["yiji_ids"]) do - c = Fk:getCardById(cid) - if c:getMark("yiji") > 0 and c.skill:canUse(p, c) then - self.use_tos = { p.id } - self.use_id = json.encode { - skill = "yiji_active", - subcards = { cid } - } - return - end - end - end -end - -fk.ai_choose_players["liuli"] = function(self, targets, min_num, num, cancelable) - local cards = table.map( - self.player:getCardIds("he"), - function(id) - return Fk:getCardById(id) - end - ) - self:sortValue(cards) - for _, pid in ipairs(targets) do - local p = self.room:getPlayerById(pid) - if self:isEnemy(p) and #self.use_tos < num and #cards > 0 then - table.insert(self.use_tos, pid) - self.use_id = { cards[1].id } - return - end - end - for _, pid in ipairs(targets) do - local p = self.room:getPlayerById(pid) - if not self:isFriend(p) and #self.use_tos < num and #cards > 0 then - table.insert(self.use_tos, pid) - self.use_id = { cards[1].id } - return - end - end -end ---]] diff --git a/packages/standard_cards/ai/init.lua b/packages/standard_cards/ai/init.lua index 4fb90903..9c94733a 100644 --- a/packages/standard_cards/ai/init.lua +++ b/packages/standard_cards/ai/init.lua @@ -1,16 +1,50 @@ +-- TODO: 合法性的方便函数 +-- TODO: 关于如何选择多个目标 +-- TODO: 关于装备牌 + -- 基本牌:杀,闪,桃 -fk.ai_use_card["slash"] = function(self, pattern, prompt, cancelable, extra_data) - local slashes = self:getCards("slash", "use", extra_data) +---@param from ServerPlayer +---@param to ServerPlayer +---@param card Card +local function tgtValidator(from, to, card) + return not from:prohibitUse(card) and + not from:isProhibited(to, card) and + true -- feasible +end + +local function justUse(self, card_name, extra_data) + local slashes = self:getCards(card_name, "use", extra_data) + if #slashes == 0 then return nil end + + return self:buildUseReply(slashes[1].id) +end + +---@param self SmartAI +---@param card_name string +local function useToEnemy(self, card_name, extra_data) + local slashes = self:getCards(card_name, "use", extra_data) if #slashes == 0 then return nil end -- TODO: 目标合法性 local targets = {} - if self.enemies[1] then table.insert(targets, self.enemies[1].id) end + if self.enemies[1] then + table.insert(targets, self.enemies[1].id) + else + return nil + end return self:buildUseReply(slashes[1].id, targets) end +fk.ai_use_card["slash"] = function(self, pattern, prompt, cancelable, extra_data) + return useToEnemy(self, "slash", extra_data) +end + +fk.ai_use_card["jink"] = function(self, pattern, prompt, cancelable, extra_data) + return justUse(self, "jink", extra_data) +end + fk.ai_use_card["peach"] = function(self, _, _, _, extra_data) local cards = self:getCards("peach", "use", extra_data) if #cards == 0 then return nil end @@ -32,443 +66,22 @@ fk.ai_use_card["#AskForPeaches"] = function(self) return nil end ---[[ -fk.ai_card.slash = { - intention = 100, -- 身份值 - value = 4, -- 卡牌价值 - priority = 2.5 -- 使用优先值 -} -fk.ai_card.peach = { - intention = -150, - value = 10, - priority = 0.5 -} -fk.ai_card.dismantlement = { - intention = function(self, card, from) - if #self.player.player_cards[Player.Judge] < 1 then - return 80 - elseif self.ai_role[from.id] == "neutral" then - return 30 - end - end, - value = 3.5, - priority = 10.5 -} -fk.ai_card.snatch = { - intention = function(self, card, from) - if #self.player.player_cards[Player.Judge] < 1 then - return 80 - elseif self.ai_role[from.id] == "neutral" then - return 30 - end - end, - value = 4.5, - priority = 10.4 -} -fk.ai_card.duel = { - intention = 120, - value = 4.5, - priority = 3.5 -} -fk.ai_card.collateral = { - intention = 20, - value = 3, - priority = 4.5 -} -fk.ai_card.ex_nihilo = { - intention = -200, - value = 8, - priority = 10 -} -fk.ai_card.savage_assault = { - intention = 20, - value = 2, - priority = 4 -} -fk.ai_card.archery_attack = { - intention = 30, - value = 2, - priority = 3 -} -fk.ai_card.god_salvation = { - intention = function(self, card, from) - if self.player.hp ~= self.player.maxHp then - return -45 - end - end, - value = 1.5, - priority = 4 -} -fk.ai_card.amazing_grace = { - intention = -30, - value = 2, - priority = 2 -} -fk.ai_card.indulgence = { - intention = 150, - value = -1, - priority = 2 -} - -local function slashEeffect(slash, to) - for _, s in ipairs(to:getAllSkills()) do - if s.name == "#vine_skill" then - if slash.name == "slash" then - return - end - end - if s.name == "#nioh_shield_skill" then - if slash.color == Card.Black then - return - end - end - end - return true +fk.ai_use_card["dismantlement"] = function(self, pattern, prompt, cancelable, extra_data) + return useToEnemy(self, "dismantlement", extra_data) end -fk.ai_use_play["slash"] = function(self, card) - self:sort(self.enemies) - for _, p in ipairs(self.enemies) do - if card.skill:targetFilter(p.id, self.use_tos, {}, card) and slashEeffect(card, p) and self:objectiveLevel(p) > 2 then - self.use_id = card.id - table.insert(self.use_tos, p.id) - end - end +fk.ai_use_card["snatch"] = function(self, pattern, prompt, cancelable, extra_data) + return useToEnemy(self, "snatch", extra_data) end -fk.ai_use_card["#slash-jink"] = function(self, pattern, prompt, cancelable, extra_data) - local act = self:getActives(pattern) - if tonumber(prompt:split(":")[4]) > #act then - return - end - local cards = - table.map( - self.player:getCardIds("&he"), - function(id) - return Fk:getCardById(id) - end - ) - self:sortValue(cards) - for _, sth in ipairs(act) do - if sth:isInstanceOf(Card) then - self.use_id = sth.id - break - else - local selected = {} - for _, c in ipairs(cards) do - if sth.cardFilter(sth, c.id, selected) then - table.insert(selected, c.id) - end - end - local tc = sth.viewAs(sth, selected) - if tc and tc:matchPattern(pattern) then - self.use_id = - json.encode { - skill = sth.name, - subcards = selected - } - break - end - end - end +fk.ai_use_card["duel"] = function(self, pattern, prompt, cancelable, extra_data) + return useToEnemy(self, "duel", extra_data) end -fk.ai_use_card["#slash-jinks"] = fk.ai_use_card["#slash-jink"] - -fk.ai_use_play["snatch"] = function(self, card) - for _, p in ipairs(self.friends_noself) do - if card.skill:targetFilter(p.id, self.use_tos, {}, card) and #p:getCardIds("j") > 0 then - self.use_id = card.id - table.insert(self.use_tos, p.id) - end - end - self:sort(self.enemies) - for _, p in ipairs(self.enemies) do - if card.skill:targetFilter(p.id, self.use_tos, {}, card) and #p:getCardIds("he") > 0 then - self.use_id = card.id - table.insert(self.use_tos, p.id) - end - end +fk.ai_use_card["ex_nihilo"] = function(self, pattern, prompt, cancelable, extra_data) + return justUse(self, "ex_nihilo", extra_data) end -fk.ai_nullification.snatch = function(self, card, to, from, positive) - if positive then - if self:isFriend(to) and not self:isFriend(from) and self.ai_role[from.id] ~= "neutral" then - if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then - self.use_id = self.avail_cards[1] - end - end - else - if self:isEnemy(to) and self:isEnemy(from) then - if #self.avail_cards > 1 or self:isWeak(to) then - self.use_id = self.avail_cards[1] - end - end - end +fk.ai_use_card["indulgence"] = function(self, pattern, prompt, cancelable, extra_data) + return useToEnemy(self, "indulgence", extra_data) end - -fk.ai_use_play["dismantlement"] = function(self, card) - for _, p in ipairs(self.friends_noself) do - if card.skill:targetFilter(p.id, self.use_tos, {}, card) and #p:getCardIds("j") > 0 then - self.use_id = card.id - table.insert(self.use_tos, p.id) - end - end - self:sort(self.enemies) - for _, p in ipairs(self.enemies) do - if card.skill:targetFilter(p.id, self.use_tos, {}, card) and #p:getCardIds("he") > 0 then - self.use_id = card.id - table.insert(self.use_tos, p.id) - end - end -end - -fk.ai_nullification.dismantlement = function(self, card, to, from, positive) - if positive then - if self:isFriend(to) and not self:isFriend(from) and self.ai_role[from.id] ~= "neutral" then - if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then - self.use_id = self.avail_cards[1] - end - end - else - if self:isEnemy(to) and self:isEnemy(from) then - if #self.avail_cards > 1 or self:isWeak(to) then - self.use_id = self.avail_cards[1] - end - end - end -end - -fk.ai_use_play["indulgence"] = function(self, card) - self:sort(self.enemies, nil, true) - for _, p in ipairs(self.enemies) do - if card.skill:targetFilter(p.id, self.use_tos, {}, card) then - self.use_id = card.id - table.insert(self.use_tos, p.id) - end - end -end - -fk.ai_nullification.indulgence = function(self, card, to, from, positive) - if positive then - if self:isFriend(to) then - if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then - self.use_id = self.avail_cards[1] - end - end - else - if self:isEnemy(to) then - if #self.avail_cards > 1 or self:isWeak(to) then - self.use_id = self.avail_cards[1] - end - end - end -end - -fk.ai_use_play["collateral"] = function(self, card) - local max = (card.skill:getMaxTargetNum(self.player, card) - 1) * 2 - self:sort(self.enemies) - for _, p in ipairs(self.enemies) do - if #self.use_tos < max and card.skill:targetFilter(p.id, {}, {}, card) then - for _, pt in ipairs(self.enemies) do - if p ~= pt and p:inMyAttackRange(pt) then - table.insert(self.use_tos, p.id) - table.insert(self.use_tos, pt.id) - self.use_id = card.id - break - end - end - end - end - for _, p in ipairs(self.friends_noself) do - if #self.use_tos < max and card.skill:targetFilter(p.id, {}, {}, card) then - for _, pt in ipairs(self.enemies) do - if p ~= pt and p:inMyAttackRange(pt) then - table.insert(self.use_tos, p.id) - table.insert(self.use_tos, pt.id) - self.use_id = card.id - break - end - end - end - end -end - -fk.ai_nullification.collateral = function(self, card, to, from, positive) - if positive then - if self:isFriend(to) and self:isEnemy(from) then - if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then - self.use_id = self.avail_cards[1] - end - end - end -end - -fk.ai_nullification.ex_nihilo = function(self, card, to, from, positive) - if positive then - if self:isEnemy(to) then - if #self.avail_cards > 1 or self:isWeak(to) then - self.use_id = self.avail_cards[1] - end - end - else - if self:isFriend(to) then - if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then - self.use_id = self.avail_cards[1] - end - end - end -end - -fk.ai_nullification.savage_assault = function(self, card, to, from, positive) - if positive then - if self:isFriend(to) then - if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then - self.use_id = self.avail_cards[1] - end - end - else - if self:isEnemy(to) then - if #self.avail_cards > 1 or self:isWeak(to) then - self.use_id = self.avail_cards[1] - end - end - end -end - -fk.ai_nullification.archery_attack = function(self, card, to, from, positive) - if positive then - if self:isFriend(to) then - if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then - self.use_id = self.avail_cards[1] - end - end - else - if self:isEnemy(to) then - if #self.avail_cards > 1 or self:isWeak(to) then - self.use_id = self.avail_cards[1] - end - end - end -end - -fk.ai_nullification.god_salvation = function(self, card, to, from, positive) - if positive then - if self:isEnemy(to) and to.hp ~= to.maxHp then - if #self.avail_cards > 1 or self:isWeak(to) then - self.use_id = self.avail_cards[1] - end - end - else - if self:isFriend(to) and to.hp ~= to.maxHp then - if #self.avail_cards > 1 or self:isWeak(to) or to.id == self.player.id then - self.use_id = self.avail_cards[1] - end - end - end -end - -fk.ai_use_play["god_salvation"] = function(self, card) - local can = 0 - for _, p in ipairs(self.enemies) do - if p:isWounded() - then - can = can - 1 - if self:isWeak(p) - then - can = can - 1 - end - end - end - for _, p in ipairs(self.friends) do - if p:isWounded() - then - can = can + 1 - if self:isWeak(p) - then - can = can + 1 - end - end - end - self.use_id = can > 0 and card.id -end - -fk.ai_use_play["amazing_grace"] = function(self, card) - self.use_id = #self.player:getCardIds("&h") <= self.player.hp and card.id -end - -fk.ai_use_play["ex_nihilo"] = function(self, card) - self.use_id = card.id -end - -fk.ai_use_play["lightning"] = function(self, card) - self.use_id = #self.enemies > #self.friends and card.id -end - -fk.ai_use_play["peach"] = function(self, card) - if self.command == "PlayCard" then - self.use_id = self.player.hp ~= self.player.maxHp and self.player.hp < #self.player:getCardIds("h") and card.id - else - for _, p in ipairs(self.friends) do - if p.dying then - self.use_id = card.id - self.use_tos = { p.id } - break - end - end - end -end - -fk.ai_use_play["duel"] = function(self, card) - self:sort(self.enemies) - for _, p in ipairs(self.enemies) do - if card.skill:targetFilter(p.id, self.use_tos, {}, card) then - self.use_id = card.id - table.insert(self.use_tos, p.id) - end - end -end - -fk.ai_skill_invoke["#ice_sword_skill"] = function(self) - local damage = self:eventData("Damage") - return self:isFriend(damage.to) or not self:isWeak(damage.to) and #damage.to:getCardIds("e") > 1 -end - -fk.ai_skill_invoke["#double_swords_skill"] = function(self) - local use = self:eventData("UseCard") - for _, p in ipairs(TargetGroup:getRealTargets(use.tos)) do - if not self:isFriend(p) and self.room:getPlayerById(p).gender ~= self.player.gender then - return true - end - end -end - -fk.ai_discard["#double_swords_skill"] = function(self, min_num, num, include_equip, cancelable, pattern, prompt) - local use = self:eventData("UseCard") - return self:isEnemy(use.from) and { self.player:getCardIds("h")[1] } -end - -fk.ai_discard["#axe_skill"] = function(self, min_num, num, include_equip, cancelable, pattern, prompt) - local ids = {} - local effect = self:eventData("CardEffect") - for _, cid in ipairs(self.player:getCardIds("he")) do - if Fk:getCardById(cid):matchPattern(pattern) then - table.insert(ids, cid) - end - if #ids >= min_num and self:isEnemy(effect.to) - and (self:isWeak(effect.to) or #self.player:getCardIds("he") > 3) then - return ids - end - end -end - -fk.ai_skill_invoke["#kylin_bow_skill"] = function(self) - local damage = self:eventData("Damage") - return not self:isFriend(damage.to) -end - -fk.ai_skill_invoke["#eight_diagram_skill"] = function(self) - local effect = self:eventData("CardEffect") - return effect and self:isFriend(effect.to) -end ---]] diff --git a/src/ui/qmlbackend.cpp b/src/ui/qmlbackend.cpp index b4d86d66..e9ba2361 100644 --- a/src/ui/qmlbackend.cpp +++ b/src/ui/qmlbackend.cpp @@ -225,6 +225,29 @@ QString QmlBackend::callLuaFunction(const QString &func_name, return QString(result); } +QString QmlBackend::evalLuaExp(const QString &lua) { + if (!ClientInstance) return "{}"; + + lua_State *L = ClientInstance->getLuaState(); + int err; + err = luaL_loadstring(L, lua.toUtf8().constData()); + if (err != LUA_OK) { + qCritical() << lua_tostring(L, -1); + lua_pop(L, 1); + return ""; + } + err = lua_pcall(L, 0, 1, 0); + const char *result = luaL_tolstring(L, -1, NULL); + if (err) { + qCritical() << result; + lua_pop(L, 1); + return ""; + } + lua_pop(L, 1); + + return QString(result); +} + QString QmlBackend::pubEncrypt(const QString &key, const QString &data) { // 在用公钥加密口令时,也随机生成AES密钥/IV,并随着口令一起加密 // AES密钥和IV都是固定16字节的,所以可以放在开头 diff --git a/src/ui/qmlbackend.h b/src/ui/qmlbackend.h index 9c167248..2f582910 100644 --- a/src/ui/qmlbackend.h +++ b/src/ui/qmlbackend.h @@ -40,6 +40,7 @@ public: Q_INVOKABLE QString translate(const QString &src); Q_INVOKABLE QString callLuaFunction(const QString &func_name, QVariantList params); + Q_INVOKABLE QString evalLuaExp(const QString &lua); Q_INVOKABLE QString pubEncrypt(const QString &key, const QString &data); Q_INVOKABLE QString loadConf(); From 7779f06411d2bb034bb7936f567653a1333a8256 Mon Sep 17 00:00:00 2001 From: notify Date: Sat, 17 Feb 2024 09:47:17 +0800 Subject: [PATCH 21/30] Fixbug (#319) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修bug... 虽然不知道是不是修了 修那个gameOver触发时机的 注意了休整下投降仍未修复哦 先放个PR在这了 --- CMakeLists.txt | 4 ++++ Fk/Pages/GeneralsOverview.qml | 4 ++-- lua/client/client.lua | 4 +--- lua/core/card.lua | 8 -------- lua/core/player.lua | 8 -------- lua/core/skill.lua | 3 +++ lua/server/ai/smart_ai.lua | 4 +--- lua/server/room.lua | 8 +++++++- src/server/server.cpp | 2 +- 9 files changed, 19 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5df080f9..2d46cec1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,8 @@ # 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) diff --git a/Fk/Pages/GeneralsOverview.qml b/Fk/Pages/GeneralsOverview.qml index 2f7ee52e..51daf516 100644 --- a/Fk/Pages/GeneralsOverview.qml +++ b/Fk/Pages/GeneralsOverview.qml @@ -255,9 +255,9 @@ Item { const gdata = lcall("GetGeneralData", modelData); const pack = gdata.package; if (s.banPkg[pack]) { - if (!s.banPkg[pack].includes(modelData)) return 0.7; + if (!s.banPkg[pack].includes(modelData)) return 0.5; } else { - if (!!s.normalPkg[pack]?.includes(modelData)) return 0.7; + if (!!s.normalPkg[pack]?.includes(modelData)) return 0.5; } return 0; } diff --git a/lua/client/client.lua b/lua/client/client.lua index d5ff5d07..931f9ff9 100644 --- a/lua/client/client.lua +++ b/lua/client/client.lua @@ -768,9 +768,7 @@ fk.client_callback["AskForUseActiveSkill"] = function(jsonData) local data = json.decode(jsonData) local skill = Fk.skills[data[1]] local extra_data = data[4] - for k, v in pairs(extra_data) do - skill[k] = v - end + skill._extra_data = extra_data Fk.currentResponseReason = extra_data.skillName ClientInstance:notifyUI("AskForUseActiveSkill", jsonData) diff --git a/lua/core/card.lua b/lua/core/card.lua index 378d3c5c..10faad65 100644 --- a/lua/core/card.lua +++ b/lua/core/card.lua @@ -414,14 +414,6 @@ function Card:getMark(mark) return ret 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。 function Card:getMarkNames() local ret = {} diff --git a/lua/core/player.lua b/lua/core/player.lua index 5862b820..6adfa938 100644 --- a/lua/core/player.lua +++ b/lua/core/player.lua @@ -221,14 +221,6 @@ function Player:getMark(mark) return mark end ---- 判定角色是否拥有对应的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 - --- 获取角色有哪些Mark。 function Player:getMarkNames() local ret = {} diff --git a/lua/core/skill.lua b/lua/core/skill.lua index a3d18389..c03405e2 100644 --- a/lua/core/skill.lua +++ b/lua/core/skill.lua @@ -44,6 +44,7 @@ function Skill:initialize(name, frequency) self.anim_type = "" self.related_skills = {} self.attachedKingdom = {} + self._extra_data = {} local name_splited = name:split("__") self.trueName = name_splited[#name_splited] @@ -63,6 +64,8 @@ end function Skill:__index(k) if k == "cost_data" then return Fk:currentRoom().skill_costs[self.name] + else + return self._extra_data[k] end end diff --git a/lua/server/ai/smart_ai.lua b/lua/server/ai/smart_ai.lua index 857f4bf1..b1020034 100644 --- a/lua/server/ai/smart_ai.lua +++ b/lua/server/ai/smart_ai.lua @@ -114,9 +114,7 @@ smart_cb["AskForUseActiveSkill"] = function(self, jsonData) local skillName, prompt, cancelable, extra_data = table.unpack(data) local skill = Fk.skills[skillName] - for k, v in pairs(extra_data) do - skill[k] = v - end + skill._extra_data = extra_data local ret = self:callFromTable(fk.ai_active_skill, nil, skillName, self, prompt, cancelable, extra_data) diff --git a/lua/server/room.lua b/lua/server/room.lua index 049a2e6a..7f8bd5f8 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -3429,7 +3429,13 @@ end function Room:gameOver(winner) if not self.game_started then return end - self.logic:trigger(fk.GameFinished, nil, winner) + if table.contains( + { "running", "normal" }, + coroutine.status(self.main_co) + ) then + self.logic:trigger(fk.GameFinished, nil, winner) + end + self.game_started = false self.game_finished = true diff --git a/src/server/server.cpp b/src/server/server.cpp index e36880ac..9a62d90e 100644 --- a/src/server/server.cpp +++ b/src/server/server.cpp @@ -517,7 +517,7 @@ void Server::handleNameAndPassword(ClientSocket *client, const QString &name, void Server::onRoomAbandoned() { Room *room = qobject_cast(sender()); - room->gameOver(); + // room->gameOver(); // Lua会出手 rooms.remove(room->getId()); updateOnlineInfo(); // 按理说这时候就可以删除了,但是这里肯定比Lua先检测到。 From 6fbaf9d05512a8f6f70d6379ec28052e6e4194a5 Mon Sep 17 00:00:00 2001 From: YoumuKon <38815081+YoumuKon@users.noreply.github.com> Date: Sat, 17 Feb 2024 09:48:42 +0800 Subject: [PATCH 22/30] askForYiji (#318) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 从Utility那里搬运了askForYiji和doYiji两个函数,负责分配 虽然暂时没实现单烧条,但先这么用着 - 修复了askForCardAndPlayers的选择中可以选择复数张牌的bug - 为prohibitDiscard添加了输入id选项 --- lua/client/i18n/en_US.lua | 6 +- lua/client/i18n/zh_CN.lua | 2 + lua/core/player.lua | 6 +- lua/core/skill_type/active.lua | 4 +- lua/core/skill_type/view_as.lua | 4 +- lua/server/room.lua | 147 +++++++++++++++++++++++++++++++ packages/standard/aux_skills.lua | 20 ++++- packages/standard/i18n/en_US.lua | 2 + packages/standard/i18n/zh_CN.lua | 2 + packages/test/init.lua | 1 + 10 files changed, 185 insertions(+), 9 deletions(-) diff --git a/lua/client/i18n/en_US.lua b/lua/client/i18n/en_US.lua index 4e9ef76f..6c3e5af1 100644 --- a/lua/client/i18n/en_US.lua +++ b/lua/client/i18n/en_US.lua @@ -205,8 +205,10 @@ Fk:loadTranslationTable({ ["#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", - ["#AskForDiscard"] = "Please discard %arg cards (at least %arg2)", - ["#AskForCard"] = "Please choose %arg cards (at least %arg2)", + ["#AskForDiscard"] = "Please discard %arg cards (%arg2 at least)", + ["#AskForCard"] = "Please choose %arg cards (%arg2 at least)", + ["#AskForDistribution"] = "Please distribute cards (%arg at least , %arg2 total)", + ["@DistributionTo"] = "", ["#askForPindian"] = "%arg: please choose a hand card for point fight", ["#StartPindianReason"] = "%from started point fight (%arg)", ["#ShowPindianCard"] = "The point fight card of %from is %card", diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index 2e92b714..30734967 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -261,6 +261,8 @@ FreeKill使用的是libgit2的C API,与此同时使用Git完成拓展包的下 ["#AskForDiscard"] = "请弃置 %arg 张牌,最少 %arg2 张", ["#AskForCard"] = "请选择 %arg 张牌,最少 %arg2 张", + ["#AskForDistribution"] = "请分配这些牌,至少 %arg 张,至多 %arg2 张", + ["@DistributionTo"] = "", ["#askForPindian"] = "%arg:请选择一张手牌作为拼点牌", ["#StartPindianReason"] = "%from 由于 %arg 而发起拼点", ["#ShowPindianCard"] = "%from 的拼点牌是 %card", diff --git a/lua/core/player.lua b/lua/core/player.lua index 6adfa938..cf5af40d 100644 --- a/lua/core/player.lua +++ b/lua/core/player.lua @@ -929,8 +929,12 @@ function Player:prohibitResponse(card) end --- 确认玩家是否被禁止弃置特定牌。 ----@param card Card @ 特定的牌 +---@param card Card|integer @ 特定的牌 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 for _, skill in ipairs(status_skills) do if skill:prohibitDiscard(self, card) then diff --git a/lua/core/skill_type/active.lua b/lua/core/skill_type/active.lua index 4e448c03..a0d6a411 100644 --- a/lua/core/skill_type/active.lua +++ b/lua/core/skill_type/active.lua @@ -208,8 +208,8 @@ function ActiveSkill:onEffect(room, cardEffectEvent) end ---@param cardEffectEvent CardEffectEvent | SkillEffectEvent function ActiveSkill:onNullified(room, cardEffectEvent) end ----@param selected integer[] @ ids of selected players ---@param selected_cards integer[] @ ids of selected cards -function ActiveSkill:prompt(selected, selected_cards) return "" end +---@param selected_targets integer[] @ ids of selected players +function ActiveSkill:prompt(selected_cards, selected_targets) return "" end return ActiveSkill diff --git a/lua/core/skill_type/view_as.lua b/lua/core/skill_type/view_as.lua index b3798753..e87e3b09 100644 --- a/lua/core/skill_type/view_as.lua +++ b/lua/core/skill_type/view_as.lua @@ -43,8 +43,8 @@ function ViewAsSkill:beforeUse(player, cardUseStruct) end ---@param cardUseStruct CardUseStruct function ViewAsSkill:afterUse(player, cardUseStruct) end ----@param selected integer[] @ ids of selected players ---@param selected_cards integer[] @ ids of selected cards -function ViewAsSkill:prompt(selected, selected_cards) return "" end +---@param selected_targets integer[] @ ids of selected players +function ViewAsSkill:prompt(selected_cards, selected_targets) return "" end return ViewAsSkill diff --git a/lua/server/room.lua b/lua/server/room.lua index 7f8bd5f8..ac0e80c1 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -1366,6 +1366,94 @@ function Room:askForChooseCardsAndPlayers(player, minCardNum, maxCardNum, target end end +--- 询问将卡牌分配给任意角色。 +---@param player ServerPlayer @ 要询问的玩家 +---@param cards? integer[] @ 要分配的卡牌。默认拥有的所有牌 +---@param targets? ServerPlayer[] @ 可以获得卡牌的角色。默认所有存活角色 +---@param skillName? string @ 技能名,影响焦点信息。默认为“分配” +---@param minNum? integer @ 最少交出的卡牌数,默认0 +---@param maxNum? integer @ 最多交出的卡牌数,默认所有牌 +---@param prompt? string @ 询问提示信息 +---@param expand_pile? string @ 可选私人牌堆名称,如要分配你武将牌上的牌请填写 +---@param skipMove? boolean @ 是否跳过移动。默认不跳过 +---@param single_max? integer|table @ 限制每人能获得的最大牌数。输入整数或(以角色id为键以整数为值)的表 +---@return table @ 返回一个表,键为角色id,值为分配给其的牌id数组 +function Room:askForYiji(player, cards, targets, skillName, minNum, maxNum, prompt, expand_pile, skipMove, single_max) + targets = targets or self.alive_players + cards = cards or player:getCardIds("he") + local _cards = table.simpleClone(cards) + targets = table.map(targets, Util.IdMapper) + self:sortPlayersByAction(targets) + skillName = skillName or "distribution_select_skill" + minNum = minNum or 0 + maxNum = maxNum or #cards + local list = {} + for _, pid in ipairs(targets) do + list[pid] = {} + end + local toStr = function(int) return string.format("%d", int) end + local residueMap = {} + if type(single_max) == "table" then + for pid, v in pairs(single_max) do + residueMap[toStr(pid)] = v + end + end + local residue_sum = 0 + local residue_num = type(single_max) == "number" and single_max or 9999 + for _, pid in ipairs(targets) do + residueMap[toStr(pid)] = residueMap[toStr(pid)] or residue_num + residue_sum = residue_sum + residueMap[toStr(pid)] + end + minNum = math.min(minNum, #_cards, residue_sum) + local data = { + cards = _cards, + max_num = maxNum, + targets = targets, + residued_list = residueMap, + expand_pile = expand_pile + } + p(json.encode(residueMap)) + + while maxNum > 0 and #_cards > 0 do + data.max_num = maxNum + prompt = prompt or ("#AskForDistribution:::"..minNum..":"..maxNum) + local success, dat = self:askForUseActiveSkill(player, "distribution_select_skill", prompt, minNum == 0, data, true) + if success and dat then + local to = dat.targets[1] + local give_cards = dat.cards + for _, id in ipairs(give_cards) do + table.insert(list[to], id) + table.removeOne(_cards, id) + self:setCardMark(Fk:getCardById(id), "@DistributionTo", Fk:translate(self:getPlayerById(to).general)) + end + minNum = math.max(0, minNum - #give_cards) + maxNum = maxNum - #give_cards + residueMap[toStr(to)] = residueMap[toStr(to)] - #give_cards + else + break + end + end + + for _, id in ipairs(cards) do + self:setCardMark(Fk:getCardById(id), "@DistributionTo", 0) + end + for _, pid in ipairs(targets) do + if minNum == 0 or #_cards == 0 then break end + local num = math.min(residueMap[toStr(pid)] or 0, minNum, #_cards) + if num > 0 then + for i = num, 1, -1 do + local c = table.remove(_cards, i) + table.insert(list[pid], c) + minNum = minNum - 1 + end + end + end + if not skipMove then + self:doYiji(self, list, player.id, skillName) + end + + return list +end --- 抽个武将 --- --- 同getNCards,抽出来就没有了,所以记得放回去。 @@ -3009,6 +3097,65 @@ function Room:moveCardTo(card, to_place, target, reason, skill_name, special_nam self:moveCards(table.unpack(movesSplitedByOwner)) end +--- 将一些卡牌同时分配给一些角色。 +---@param room Room @ 房间 +---@param list table @ 分配牌和角色的数据表,键为角色id,值为分配给其的牌id数组 +---@param proposer? integer @ 操作者的id。默认为空 +---@param skillName? string @ 技能名。默认为“分配” +---@return table @ 返回成功分配的卡牌 +function Room:doYiji(room, list, proposer, skillName) + skillName = skillName or "distribution_skill" + local moveInfos = {} + local move_ids = {} + for to, cards in pairs(list) do + local toP = room:getPlayerById(to) + local handcards = toP:getCardIds("h") + cards = table.filter(cards, function (id) return not table.contains(handcards, id) end) + if #cards > 0 then + table.insertTable(move_ids, cards) + local moveMap = {} + local noFrom = {} + for _, id in ipairs(cards) do + local from = room.owner_map[id] + if from then + moveMap[from] = moveMap[from] or {} + table.insert(moveMap[from], id) + else + table.insert(noFrom, id) + end + end + for from, _cards in pairs(moveMap) do + table.insert(moveInfos, { + ids = _cards, + moveInfo = table.map(_cards, function(id) + return {cardId = id, fromArea = room:getCardArea(id), fromSpecialName = room:getPlayerById(from):getPileNameOfId(id)} + end), + from = from, + to = to, + toArea = Card.PlayerHand, + moveReason = fk.ReasonGive, + proposer = proposer, + skillName = skillName, + }) + end + if #noFrom > 0 then + table.insert(moveInfos, { + ids = noFrom, + to = to, + toArea = Card.PlayerHand, + moveReason = fk.ReasonGive, + proposer = proposer, + skillName = skillName, + }) + end + end + end + if #moveInfos > 0 then + room:moveCards(table.unpack(moveInfos)) + end + return move_ids +end + ------------------------------------------------------------------------ -- 其他游戏事件 ------------------------------------------------------------------------ diff --git a/packages/standard/aux_skills.lua b/packages/standard/aux_skills.lua index 9c3d6b40..916c6aa4 100644 --- a/packages/standard/aux_skills.lua +++ b/packages/standard/aux_skills.lua @@ -78,8 +78,8 @@ local chooseCardsSkill = fk.CreateActiveSkill{ local choosePlayersSkill = fk.CreateActiveSkill{ name = "choose_players_skill", - card_filter = function(self, to_select) - return self.pattern ~= "" and Exppattern:Parse(self.pattern):match(Fk:getCardById(to_select)) + card_filter = function(self, to_select, selected) + return self.pattern ~= "" and Exppattern:Parse(self.pattern):match(Fk:getCardById(to_select)) and #selected == 0 end, target_filter = function(self, to_select, selected, cards) if self.pattern ~= "" and #cards == 0 then return end @@ -137,6 +137,21 @@ local maxCardsSkill = fk.CreateMaxCardsSkill{ end, } +local distributionSelectSkill = fk.CreateActiveSkill{ + name = "distribution_select_skill", + mute = true, + min_card_num = 1, + card_filter = function(self, to_select, selected) + return #selected < self.max_num and table.contains(self.cards, to_select) + end, + target_num = 1, + target_filter = function(self, to_select, selected, selected_cards) + return #selected == 0 and #selected_cards > 0 and table.contains(self.targets, to_select) + and #selected_cards <= (self.residued_list[string.format("%d", to_select)] or 0) + end, +} + + local choosePlayersToMoveCardInBoardSkill = fk.CreateActiveSkill{ name = "choose_players_to_move_card_in_board", target_num = 2, @@ -281,6 +296,7 @@ AuxSkills = { chooseCardsSkill, choosePlayersSkill, exChooseSkill, + distributionSelectSkill, maxCardsSkill, choosePlayersToMoveCardInBoardSkill, uncompulsoryInvalidity, diff --git a/packages/standard/i18n/en_US.lua b/packages/standard/i18n/en_US.lua index 948ef599..173de1fb 100644 --- a/packages/standard/i18n/en_US.lua +++ b/packages/standard/i18n/en_US.lua @@ -192,6 +192,8 @@ Fk:loadTranslationTable({ ["discard_skill"] = "Discard", ["choose_cards_skill"] = "Choose card", ["choose_players_skill"] = "Choose players", + ["ex__choose_skill"] = "Choose", + ["distribution_select_skill"] = "Distribute", ["choose_players_to_move_card_in_board"] = "Choose players", ["reveal_skill"] = "Reveal", ["#reveal_skill"] = "Choose a character to reveal", diff --git a/packages/standard/i18n/zh_CN.lua b/packages/standard/i18n/zh_CN.lua index 645350e9..acfd0c31 100644 --- a/packages/standard/i18n/zh_CN.lua +++ b/packages/standard/i18n/zh_CN.lua @@ -519,6 +519,8 @@ Fk:loadTranslationTable{ ["discard_skill"] = "弃牌", ["choose_cards_skill"] = "选牌", ["choose_players_skill"] = "选择角色", + ["ex__choose_skill"] = "选择", + ["distribution_select_skill"] = "分配", ["choose_players_to_move_card_in_board"] = "选择角色", ["reveal_skill"] = "亮将", ["#reveal_skill"] = "选择一个武将亮将(点击左侧选择框展开)", diff --git a/packages/test/init.lua b/packages/test/init.lua index 98b9aa71..802b1535 100644 --- a/packages/test/init.lua +++ b/packages/test/init.lua @@ -80,6 +80,7 @@ local control = fk.CreateActiveSkill{ -- ok = {10, 2}, -- }) -- room:swapSeat(from, to) + -- 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:askForPoxi(from, "test", { From 380ca120e9a38b44c6e87c7454a75b4b6b86e1a3 Mon Sep 17 00:00:00 2001 From: YoumuKon <38815081+YoumuKon@users.noreply.github.com> Date: Tue, 27 Feb 2024 02:27:59 +0800 Subject: [PATCH 23/30] =?UTF-8?q?=E5=A4=9A=E5=90=8E=E7=BC=80=E4=B8=8EbugFi?= =?UTF-8?q?x=20(#321)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 正式添加对多后缀标记的支持 - 添加了一点注释 - 搬运了moveCardIntoEquip和canMoveCardIntoEquip - 为选牌的默认prompt添加了目标 - 完善了朱雀羽扇的判定 - 修复了抽选武将牌堆时未删除已选武将的bug - 修复了maxCard标记不识别“-turn”以外标记的bug - 修复了obtaincard实际不能接受id数组的bug --- Fk/Pages/RoomLogic.js | 4 +- lua/client/client.lua | 2 + lua/client/i18n/en_US.lua | 6 +- lua/client/i18n/zh_CN.lua | 6 +- lua/core/general.lua | 4 +- lua/core/player.lua | 47 ++++++++-- lua/core/skill_type/active.lua | 41 ++++++--- lua/core/skill_type/usable_skill.lua | 41 ++++++--- lua/core/util.lua | 127 ++++++++++++++------------- lua/server/events/gameflow.lua | 12 +-- lua/server/events/movecard.lua | 2 +- lua/server/room.lua | 42 ++++++++- packages/maneuvering/init.lua | 5 +- packages/standard/aux_skills.lua | 75 ++++++++++++---- packages/standard/init.lua | 24 ++++- 15 files changed, 307 insertions(+), 131 deletions(-) diff --git a/Fk/Pages/RoomLogic.js b/Fk/Pages/RoomLogic.js index 025960fe..46fb9508 100644 --- a/Fk/Pages/RoomLogic.js +++ b/Fk/Pages/RoomLogic.js @@ -1107,7 +1107,7 @@ callbacks["AskForCardChosen"] = (jsonData) => { const reason = data._reason; const prompt = data._prompt; if (prompt === "") { - roomScene.promptText = luatr("#AskForChooseCard") + roomScene.promptText = luatr(processPrompt("#AskForChooseCard:" + data._id)) .arg(luatr(reason)); } else { roomScene.setPrompt(processPrompt(prompt), true); @@ -1139,7 +1139,7 @@ callbacks["AskForCardsChosen"] = (jsonData) => { const reason = data._reason; const prompt = data._prompt; if (prompt === "") { - roomScene.promptText = luatr("#AskForChooseCards") + roomScene.promptText = luatr(processPrompt("#AskForChooseCards:" + data._id)) .arg(luatr(reason)).arg(min).arg(max); } else { roomScene.setPrompt(processPrompt(prompt), true); diff --git a/lua/client/client.lua b/lua/client/client.lua index 931f9ff9..33304613 100644 --- a/lua/client/client.lua +++ b/lua/client/client.lua @@ -378,6 +378,7 @@ fk.client_callback["AskForCardChosen"] = function(jsonData) judge = {} end ui_data = { + _id = id, _reason = reason, card_data = {}, _prompt = prompt, @@ -413,6 +414,7 @@ fk.client_callback["AskForCardsChosen"] = function(jsonData) judge = {} end ui_data = { + _id = id, _min = min, _max = max, _reason = reason, diff --git a/lua/client/i18n/en_US.lua b/lua/client/i18n/en_US.lua index 6c3e5af1..ee356e21 100644 --- a/lua/client/i18n/en_US.lua +++ b/lua/client/i18n/en_US.lua @@ -185,12 +185,13 @@ Fk:loadTranslationTable({ ["AskForKingdom"] = "Choosing kingdom", ["AskForPindian"] = "Point fight", ["AskForMoveCardInBoard"] = "Moving cards", + ["replaceEquip"] = "Replacing Equip", ["PlayCard"] = "Playing card", ["AskForCardChosen"] = "Choosing card", ["AskForCardsChosen"] = "Choosing card", - ["#AskForChooseCard"] = "%1: please choose a card", - ["#AskForChooseCards"] = "%1: please choose %2~%3 cards", + ["#AskForChooseCard"] = "%1: please choose a card from %src", + ["#AskForChooseCards"] = "%1: please choose %2~%3 cards from %src", ["$ChooseCard"] = "Choose a card", ["$ChooseCards"] = "Choose %1~%2 cards", ["$Hand"] = "Hand", @@ -209,6 +210,7 @@ Fk:loadTranslationTable({ ["#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", ["#StartPindianReason"] = "%from started point fight (%arg)", ["#ShowPindianCard"] = "The point fight card of %from is %card", diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index 30734967..59cdc393 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -239,12 +239,13 @@ FreeKill使用的是libgit2的C API,与此同时使用Git完成拓展包的下 ["AskForKingdom"] = "选择势力", ["AskForPindian"] = "拼点", ["AskForMoveCardInBoard"] = "移动卡牌", + ["replaceEquip"] = "替换装备", ["PlayCard"] = "出牌", ["AskForCardChosen"] = "选牌", ["AskForCardsChosen"] = "选牌", - ["#AskForChooseCard"] = "%1:请选择其一张卡牌", - ["#AskForChooseCards"] = "%1:请选择其%2至%3张卡牌", + ["#AskForChooseCard"] = "%1:请选择%src的一张卡牌", + ["#AskForChooseCards"] = "%1:请选择%src的%2至%3张卡牌", ["$ChooseCard"] = "请选择一张卡牌", ["$ChooseCards"] = "请选择%1至%2张卡牌", ["$Hand"] = "手牌区", @@ -263,6 +264,7 @@ FreeKill使用的是libgit2的C API,与此同时使用Git完成拓展包的下 ["#AskForCard"] = "请选择 %arg 张牌,最少 %arg2 张", ["#AskForDistribution"] = "请分配这些牌,至少 %arg 张,至多 %arg2 张", ["@DistributionTo"] = "", + ["#replaceEquip"] = "选择一张装备牌替换之", ["#askForPindian"] = "%arg:请选择一张手牌作为拼点牌", ["#StartPindianReason"] = "%from 由于 %arg 而发起拼点", ["#ShowPindianCard"] = "%from 的拼点牌是 %card", diff --git a/lua/core/general.lua b/lua/core/general.lua index 24f4beee..24008eee 100644 --- a/lua/core/general.lua +++ b/lua/core/general.lua @@ -71,7 +71,7 @@ function General:__tostring() end --- 为武将增加技能,需要注意增加其他武将技能时的处理方式。 ----@param skill Skill @ (单个)武将技能 +---@param skill Skill|string @ (单个)武将技能 function General:addSkill(skill) if (type(skill) == "string") then table.insert(self.other_skills, skill) @@ -82,7 +82,7 @@ function General:addSkill(skill) end --- 为武将增加相关技能,需要注意增加其他武将技能时的处理方式。 ----@param skill Skill @ (单个)武将技能 +---@param skill Skill|string @ (单个)武将技能 function General:addRelatedSkill(skill) if (type(skill) == "string") then table.insert(self.related_other_skills, skill) diff --git a/lua/core/player.lua b/lua/core/player.lua index cf5af40d..ceb536b7 100644 --- a/lua/core/player.lua +++ b/lua/core/player.lua @@ -475,9 +475,17 @@ end --- 获取角色是否被移除。 function Player:isRemoved() - return self:getMark(MarkEnum.PlayerRemoved) ~= 0 or table.find(MarkEnum.TempMarkSuffix, function(s) - return self:getMark(MarkEnum.PlayerRemoved .. s) ~= 0 - end) + for mark, _ in pairs(self.mark) do + if mark == MarkEnum.PlayerRemoved then return true 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 --- 修改玩家与其他角色的固定距离。 @@ -950,13 +958,21 @@ function Player:prohibitReveal(isDeputy) if type(self:getMark(MarkEnum.RevealProhibited)) == "table" and table.contains(self:getMark(MarkEnum.RevealProhibited), place) then return true 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 + + for mark, value in pairs(self.mark) do + if mark:startsWith(MarkEnum.RevealProhibited .. "-") and type(value) == "table" then + for _, suffix in ipairs(MarkEnum.TempMarkSuffix) do + if mark:find(suffix, 1, true) then return true 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 end @@ -981,6 +997,21 @@ function Player:canPindian(to, ignoreFromKong, ignoreToKong) return true 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 --转换技状态阴 diff --git a/lua/core/skill_type/active.lua b/lua/core/skill_type/active.lua index a0d6a411..7609879c 100644 --- a/lua/core/skill_type/active.lua +++ b/lua/core/skill_type/active.lua @@ -155,21 +155,38 @@ function ActiveSkill:withinDistanceLimit(player, isattack, card, to) local temp_suf = table.simpleClone(MarkEnum.TempMarkSuffix) local card_temp_suf = table.simpleClone(MarkEnum.CardTempMarkSuffix) - table.insert(temp_suf, 1, "") - table.insert(temp_suf, "-tmp") - table.insert(card_temp_suf, 1, "") + + ---@param object Card|Player + ---@param markname string + ---@param suffixes string[] + ---@return boolean + local function hasMark(object, markname, suffixes) + if not object then return false end + for mark, _ in pairs(object.mark) do + if mark == markname then return true end + if mark:startsWith(markname .. "-") then + for _, suffix in ipairs(suffixes) do + if mark:find(suffix, 1, true) then return true end + end + end + end + return false + end return (isattack and player:inMyAttackRange(to)) or (player:distanceTo(to) > 0 and player:distanceTo(to) <= self:getDistanceLimit(player, card, to)) or - (card and table.find(card_temp_suf, function(s) - return card:getMark(MarkEnum.BypassDistancesLimit .. s) ~= 0 - end)) or - (table.find(temp_suf, function(s) - return player:getMark(MarkEnum.BypassDistancesLimit .. s) ~= 0 - end)) or - (to and (table.find(temp_suf, function(s) - return to:getMark(MarkEnum.BypassDistancesLimitTo .. s) ~= 0 - end))) + hasMark(card, MarkEnum.BypassDistancesLimit, card_temp_suf) or + hasMark(player, MarkEnum.BypassDistancesLimit, temp_suf) or + hasMark(to, MarkEnum.BypassDistancesLimitTo, temp_suf) + -- (card and table.find(card_temp_suf, function(s) + -- return card:getMark(MarkEnum.BypassDistancesLimit .. s) ~= 0 + -- end)) or + -- (table.find(temp_suf, function(s) + -- return player:getMark(MarkEnum.BypassDistancesLimit .. s) ~= 0 + -- end)) or + -- (to and (table.find(temp_suf, function(s) + -- return to:getMark(MarkEnum.BypassDistancesLimitTo .. s) ~= 0 + -- end))) end --- Determine if selected cards and targets are valid for this skill diff --git a/lua/core/skill_type/usable_skill.lua b/lua/core/skill_type/usable_skill.lua index 34e9d430..7fbbb082 100644 --- a/lua/core/skill_type/usable_skill.lua +++ b/lua/core/skill_type/usable_skill.lua @@ -40,20 +40,37 @@ function UsableSkill:withinTimesLimit(player, scope, card, card_name, to) card_name = card_name or card.trueName local temp_suf = table.simpleClone(MarkEnum.TempMarkSuffix) local card_temp_suf = table.simpleClone(MarkEnum.CardTempMarkSuffix) - table.insert(temp_suf, 1, "") - table.insert(temp_suf, "-tmp") - table.insert(card_temp_suf, 1, "") + + ---@param object Card|Player + ---@param markname string + ---@param suffixes string[] + ---@return boolean + local function hasMark(object, markname, suffixes) + if not object then return false end + for mark, _ in pairs(object.mark) do + if mark == markname then return true end + if mark:startsWith(markname .. "-") then + for _, suffix in ipairs(suffixes) do + if mark:find(suffix, 1, true) then return true end + end + end + end + return false + end return player:usedCardTimes(card_name, scope) < self:getMaxUseTime(player, scope, card, to) or - (card and table.find(card_temp_suf, function(s) - return card:getMark(MarkEnum.BypassTimesLimit .. s) ~= 0 - end)) or - (table.find(temp_suf, function(s) - return player:getMark(MarkEnum.BypassTimesLimit .. s) ~= 0 - end)) or - (to and (table.find(temp_suf, function(s) - return to:getMark(MarkEnum.BypassTimesLimitTo .. s) ~= 0 - end))) + hasMark(card, MarkEnum.BypassTimesLimit, card_temp_suf) or + hasMark(player, MarkEnum.BypassTimesLimit, temp_suf) or + hasMark(to, MarkEnum.BypassTimesLimitTo, temp_suf) + -- (card and table.find(card_temp_suf, function(s) + -- return card:getMark(MarkEnum.BypassTimesLimit .. s) ~= 0 + -- end)) or + -- (table.find(temp_suf, function(s) + -- return player:getMark(MarkEnum.BypassTimesLimit .. s) ~= 0 + -- end)) or + -- (to and (table.find(temp_suf, function(s) + -- return to:getMark(MarkEnum.BypassTimesLimitTo .. s) ~= 0 + -- end))) end return UsableSkill diff --git a/lua/core/util.lua b/lua/core/util.lua index 975c5dd2..77cabc5c 100644 --- a/lua/core/util.lua +++ b/lua/core/util.lua @@ -114,6 +114,73 @@ function fk.sorted_pairs(t, val_func, reverse) return iter, nil, 1 end +-- frequenly used filter & map functions + +--- 返回ID +Util.IdMapper = function(e) return e.id end +--- 根据卡牌ID返回卡牌 +Util.Id2CardMapper = function(id) return Fk:getCardById(id) end +--- 根据玩家ID返回玩家 +Util.Id2PlayerMapper = function(id) + return Fk:currentRoom():getPlayerById(id) +end +--- 返回武将名 +Util.NameMapper = function(e) return e.name end +--- 根据武将名返回武将 +Util.Name2GeneralMapper = function(e) return Fk.generals[e] end +--- 根据技能名返回技能 +Util.Name2SkillMapper = function(e) return Fk.skills[e] end +--- 返回译文 +Util.TranslateMapper = function(str) return Fk:translate(str) end + +-- for card preset + +--- 全局卡牌(包括自己)的canUse +Util.GlobalCanUse = function(self, player, card) + local room = Fk:currentRoom() + for _, p in ipairs(room.alive_players) do + if not (card and player:isProhibited(p, card)) then + return true + end + end +end + +--- AOE卡牌(不包括自己)的canUse +Util.AoeCanUse = function(self, player, card) + local room = Fk:currentRoom() + for _, p in ipairs(room.alive_players) do + if p ~= player and not (card and player:isProhibited(p, card)) then + return true + end + end +end + +--- 全局卡牌(包括自己)的onUse +Util.GlobalOnUse = function(self, room, cardUseEvent) + if not cardUseEvent.tos or #TargetGroup:getRealTargets(cardUseEvent.tos) == 0 then + cardUseEvent.tos = {} + for _, player in ipairs(room:getAlivePlayers()) do + if not room:getPlayerById(cardUseEvent.from):isProhibited(player, cardUseEvent.card) then + TargetGroup:pushTargets(cardUseEvent.tos, player.id) + end + end + end +end + +--- AOE卡牌(不包括自己)的onUse +Util.AoeOnUse = function(self, room, cardUseEvent) + if not cardUseEvent.tos or #TargetGroup:getRealTargets(cardUseEvent.tos) == 0 then + cardUseEvent.tos = {} + for _, player in ipairs(room:getOtherPlayers(room:getPlayerById(cardUseEvent.from))) do + if not room:getPlayerById(cardUseEvent.from):isProhibited(player, cardUseEvent.card) then + TargetGroup:pushTargets(cardUseEvent.tos, player.id) + end + end + end +end + +-- Table + ---@param func fun(element, index, array) function table:forEach(func) for i, v in ipairs(self) do @@ -164,66 +231,6 @@ function table:map(func) return ret end --- frequenly used filter & map functions - ---- 返回ID -Util.IdMapper = function(e) return e.id end ---- 根据卡牌ID返回卡牌 -Util.Id2CardMapper = function(id) return Fk:getCardById(id) end ---- 根据玩家ID返回玩家 -Util.Id2PlayerMapper = function(id) - return Fk:currentRoom():getPlayerById(id) -end ---- 返回武将名 -Util.NameMapper = function(e) return e.name end ---- 根据武将名返回武将 -Util.Name2GeneralMapper = function(e) return Fk.generals[e] end ---- 根据技能名返回技能 -Util.Name2SkillMapper = function(e) return Fk.skills[e] end ---- 返回译文 -Util.TranslateMapper = function(str) return Fk:translate(str) end - --- for card preset -Util.GlobalCanUse = function(self, player, card) - local room = Fk:currentRoom() - for _, p in ipairs(room.alive_players) do - if not (card and player:isProhibited(p, card)) then - return true - end - end -end - -Util.AoeCanUse = function(self, player, card) - local room = Fk:currentRoom() - for _, p in ipairs(room.alive_players) do - if p ~= player and not (card and player:isProhibited(p, card)) then - return true - end - end -end - -Util.GlobalOnUse = function(self, room, cardUseEvent) - if not cardUseEvent.tos or #TargetGroup:getRealTargets(cardUseEvent.tos) == 0 then - cardUseEvent.tos = {} - for _, player in ipairs(room:getAlivePlayers()) do - if not room:getPlayerById(cardUseEvent.from):isProhibited(player, cardUseEvent.card) then - TargetGroup:pushTargets(cardUseEvent.tos, player.id) - end - end - end -end - -Util.AoeOnUse = function(self, room, cardUseEvent) - if not cardUseEvent.tos or #TargetGroup:getRealTargets(cardUseEvent.tos) == 0 then - cardUseEvent.tos = {} - for _, player in ipairs(room:getOtherPlayers(room:getPlayerById(cardUseEvent.from))) do - if not room:getPlayerById(cardUseEvent.from):isProhibited(player, cardUseEvent.card) then - TargetGroup:pushTargets(cardUseEvent.tos, player.id) - end - end - end -end - ---@generic T ---@param self T[] ---@return T[] diff --git a/lua/server/events/gameflow.lua b/lua/server/events/gameflow.lua index b44f1041..2c8ebea5 100644 --- a/lua/server/events/gameflow.lua +++ b/lua/server/events/gameflow.lua @@ -174,7 +174,7 @@ GameEvent.cleaners[GameEvent.Round] = function(self) p:setCardUseHistory("", 0, Player.HistoryRound) p:setSkillUseHistory("", 0, Player.HistoryRound) for name, _ in pairs(p.mark) do - if name:endsWith("-round") then + if name:find("-round", 1, true) then room:setPlayerMark(p, name, 0) end end @@ -182,7 +182,7 @@ GameEvent.cleaners[GameEvent.Round] = function(self) for cid, cmark in pairs(room.card_marks) do for name, _ in pairs(cmark) do - if name:endsWith("-round") then + if name:find("-round", 1, true) then room:setCardMark(Fk:getCardById(cid), name, 0) end end @@ -249,7 +249,7 @@ GameEvent.cleaners[GameEvent.Turn] = function(self) p:setCardUseHistory("", 0, Player.HistoryTurn) p:setSkillUseHistory("", 0, Player.HistoryTurn) for name, _ in pairs(p.mark) do - if name:endsWith("-turn") then + if name:find("-turn", 1, true) then room:setPlayerMark(p, name, 0) end end @@ -257,7 +257,7 @@ GameEvent.cleaners[GameEvent.Turn] = function(self) for cid, cmark in pairs(room.card_marks) do for name, _ in pairs(cmark) do - if name:endsWith("-turn") then + if name:find("-turn", 1, true) then room:setCardMark(Fk:getCardById(cid), name, 0) end end @@ -372,7 +372,7 @@ GameEvent.cleaners[GameEvent.Phase] = function(self) p:setCardUseHistory("", 0, Player.HistoryPhase) p:setSkillUseHistory("", 0, Player.HistoryPhase) for name, _ in pairs(p.mark) do - if name:endsWith("-phase") then + if name:find("-phase", 1, true) then room:setPlayerMark(p, name, 0) end end @@ -380,7 +380,7 @@ GameEvent.cleaners[GameEvent.Phase] = function(self) for cid, cmark in pairs(room.card_marks) do for name, _ in pairs(cmark) do - if name:endsWith("-phase") then + if name:find("-phase", 1, true) then room:setCardMark(Fk:getCardById(cid), name, 0) end end diff --git a/lua/server/events/movecard.lua b/lua/server/events/movecard.lua index 764a388d..69e7b48b 100644 --- a/lua/server/events/movecard.lua +++ b/lua/server/events/movecard.lua @@ -166,7 +166,7 @@ GameEvent.functions[GameEvent.MoveCards] = function(self) local currentCard = Fk:getCardById(info.cardId) for name, _ in pairs(currentCard.mark) do - if name:endsWith("-inhand") and + if name:find("-inhand", 1, true) and realFromArea == Player.Hand and data.from then diff --git a/lua/server/room.lua b/lua/server/room.lua index ac0e80c1..68491550 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -1501,6 +1501,7 @@ end ---@param name string @ 武将name,如找不到则查找truename,再找不到则返回nil ---@return string? @ 抽出的武将名 function Room:findGeneral(name) + if not Fk.generals[name] then return nil end for i, g in ipairs(self.general_pile) do if g == name or Fk.generals[g].trueName == Fk.generals[name].trueName then return table.remove(self.general_pile, i) @@ -1517,13 +1518,13 @@ function Room:findGenerals(func, n) n = n or 1 local ret = {} local index = 1 - repeat + while #ret < n and index <= #self.general_pile do if func(self.general_pile[index]) then table.insert(ret, table.remove(self.general_pile, index)) else index = index + 1 end - until index >= #self.general_pile or #ret >= n + end return ret end @@ -2993,8 +2994,10 @@ end ---@param proposer? integer @ 移动操作者的id function Room:obtainCard(player, cid, unhide, reason, proposer) if type(cid) ~= "number" then - assert(cid and cid:isInstanceOf(Card)) - cid = cid:isVirtual() and cid.subcards or {cid.id} + assert(cid and type(cid) == "table") + if cid:isInstanceOf(Card) then + cid = cid:isVirtual() and cid.subcards or {cid.id} + end else cid = {cid} end @@ -3156,6 +3159,37 @@ function Room:doYiji(room, list, proposer, skillName) return move_ids end +--- 将一张牌移动至某角色的装备区,若不合法则置入弃牌堆。目前没做相同副类别装备同时置入的适配(甘露神典韦) +---@param target ServerPlayer @ 接受牌的角色 +---@param cards integer|integer[] @ 移动的牌 +---@param skillName? string @ 技能名 +---@param convert? boolean @ 是否可以替换装备(默认可以) +---@param proposer? ServerPlayer @ 操作者 +function Room:moveCardIntoEquip(target, cards, skillName, convert, proposer) + convert = (convert == nil) and true or convert + skillName = skillName or "" + cards = type(cards) == "table" and cards or {cards} + local moves = {} + for _, cardId in ipairs(cards) do + local card = Fk:getCardById(cardId) + local fromId = self.owner_map[cardId] + local proposerId = proposer and proposer.id or nil + if target:canMoveCardIntoEquip(cardId, convert) then + if target:hasEmptyEquipSlot(card.sub_type) then + table.insert(moves,{ids = {cardId}, from = fromId, to = target.id, toArea = Card.PlayerEquip, moveReason = fk.ReasonPut,skillName = skillName,proposer = proposerId}) + else + local existingEquip = target:getEquipments(card.sub_type) + local throw = #existingEquip == 1 and existingEquip[1] or + self:askForCardChosen(proposer or target, target, {card_data = { {Util.convertSubtypeAndEquipSlot(card.sub_type),existingEquip} } }, "replaceEquip","#replaceEquip") + table.insert(moves,{ids = {throw}, from = target.id, toArea = Card.DiscardPile, moveReason = fk.ReasonPutIntoDiscardPile, skillName = skillName,proposer = proposerId}) + table.insert(moves,{ids = {cardId}, from = fromId, to = target.id, toArea = Card.PlayerEquip, moveReason = fk.ReasonPut,skillName = skillName,proposer = proposerId}) + end + else + table.insert(moves,{ids = {cardId}, from = fromId, toArea = Card.DiscardPile, moveReason = fk.ReasonPutIntoDiscardPile,skillName = skillName}) + end + end + self:moveCards(table.unpack(moves)) +end ------------------------------------------------------------------------ -- 其他游戏事件 ------------------------------------------------------------------------ diff --git a/packages/maneuvering/init.lua b/packages/maneuvering/init.lua index 0283fcdc..0a95e07c 100644 --- a/packages/maneuvering/init.lua +++ b/packages/maneuvering/init.lua @@ -366,9 +366,12 @@ local fanSkill = fk.CreateTriggerSkill{ card[k] = v end end - if not data.card:isVirtual() then + if data.card:isVirtual() then + card.subcards = data.card.subcards + else card.id = data.card.id end + card.skillNames = data.card.skillNames card.skillName = "fan" data.card = card end, diff --git a/packages/standard/aux_skills.lua b/packages/standard/aux_skills.lua index 916c6aa4..a3b527ab 100644 --- a/packages/standard/aux_skills.lua +++ b/packages/standard/aux_skills.lua @@ -129,11 +129,25 @@ local maxCardsSkill = fk.CreateMaxCardsSkill{ name = "max_cards_skill", global = true, correct_func = function(self, player) + local function getMark(markname) + local v = 0 + for mark, value in pairs(player.mark) do + if mark == markname then + v = v + value + elseif mark:startsWith(markname .. "-") then + for _, suffix in ipairs(MarkEnum.TempMarkSuffix) do + if mark:find(suffix, 1, true) then + v = v + value + break + end + end + end + end + return v + end return - player:getMark(MarkEnum.AddMaxCards) + - player:getMark(MarkEnum.AddMaxCardsInTurn) - - player:getMark(MarkEnum.MinusMaxCards) - - player:getMark(MarkEnum.MinusMaxCardsInTurn) + getMark(MarkEnum.AddMaxCards) - + getMark(MarkEnum.MinusMaxCards) end, } @@ -181,15 +195,32 @@ local uncompulsoryInvalidity = fk.CreateInvaliditySkill { name = "uncompulsory_invalidity", global = true, invalidity_func = function(self, from, skill) + ---@param object Card|Player + ---@param markname string + ---@param suffixes string[] + ---@return boolean + local function hasMark(object, markname, suffixes) + if not object then return false end + for mark, _ in pairs(object.mark) do + if mark == markname then return true end + if mark:startsWith(markname .. "-") then + for _, suffix in ipairs(suffixes) do + if mark:find(suffix, 1, true) then return true end + end + end + end + return false + end return (skill.frequency ~= Skill.Compulsory and skill.frequency ~= Skill.Wake) and not (skill:isEquipmentSkill() or skill.name:endsWith("&")) and - ( - from:getMark(MarkEnum.UncompulsoryInvalidity) ~= 0 or - table.find(MarkEnum.TempMarkSuffix, function(s) - return from:getMark(MarkEnum.UncompulsoryInvalidity .. s) ~= 0 - end) - ) + hasMark(from, MarkEnum.UncompulsoryInvalidity, MarkEnum.TempMarkSuffix) + -- ( + -- from:getMark(MarkEnum.UncompulsoryInvalidity) ~= 0 or + -- table.find(MarkEnum.TempMarkSuffix, function(s) + -- return from:getMark(MarkEnum.UncompulsoryInvalidity .. s) ~= 0 + -- end) + -- ) end } @@ -201,15 +232,27 @@ local revealProhibited = fk.CreateInvaliditySkill { if type(from:getMark(MarkEnum.RevealProhibited)) == "table" then generals = from:getMark(MarkEnum.RevealProhibited) end - for _, m in ipairs(table.map(MarkEnum.TempMarkSuffix, function(s) - return from:getMark(MarkEnum.RevealProhibited .. s) - end)) do - if type(m) == "table" then - for _, g in ipairs(m) do - table.insertIfNeed(generals, g) + + for mark, value in pairs(from.mark) do + if mark:startsWith(MarkEnum.RevealProhibited .. "-") and type(value) == "table" then + for _, suffix in ipairs(MarkEnum.TempMarkSuffix) do + if mark:find(suffix, 1, true) then + for _, g in ipairs(value) do + table.insertIfNeed(generals, g) + end + end end end end + -- for _, m in ipairs(table.map(MarkEnum.TempMarkSuffix, function(s) + -- return from:getMark(MarkEnum.RevealProhibited .. s) + -- end)) do + -- if type(m) == "table" then + -- for _, g in ipairs(m) do + -- table.insertIfNeed(generals, g) + -- end + -- end + -- end if #generals == 0 then return false end local sname = skill.name diff --git a/packages/standard/init.lua b/packages/standard/init.lua index 2d1d5c7e..16670af7 100644 --- a/packages/standard/init.lua +++ b/packages/standard/init.lua @@ -1132,9 +1132,17 @@ local role_getlogic = function() lord_general = lord_generals lord_generals = {lord_general} end - - generals = table.filter(generals, function(g) return not table.contains(lord_generals, g) end) room:returnToGeneralPile(generals) + local index = 1 + while index <= #room.general_pile do + local ret = {} + for _, gname in ipairs(lord_generals) do + if room.general_pile[index] == gname or Fk.generals[room.general_pile[index]].trueName == Fk.generals[gname].trueName then + table.insert(ret, table.remove(room.general_pile, index)) + end + end + if #ret == 0 then index = index + 1 end + end room:setPlayerGeneral(lord, lord_general, true) room:askForChooseKingdom({lord}) @@ -1216,14 +1224,24 @@ local role_getlogic = function() room:setPlayerGeneral(p, general, true, true) room:setDeputyGeneral(p, deputy) else + table.insertTableIfNeed(selected, p.default_reply) room:setPlayerGeneral(p, p.default_reply[1], true, true) room:setDeputyGeneral(p, p.default_reply[2]) end p.default_reply = "" end - generals = table.filter(generals, function(g) return not table.contains(selected, g) end) room:returnToGeneralPile(generals) + local index = 1 + while index <= #room.general_pile do + local ret = {} + for _, gname in ipairs(selected) do + if room.general_pile[index] == gname or Fk.generals[room.general_pile[index]].trueName == Fk.generals[gname].trueName then + table.insert(ret, table.remove(room.general_pile, index)) + end + end + if #ret == 0 then index = index + 1 end + end room:askForChooseKingdom(nonlord) end From d0eb3ba2e3ef78d64164b62d9509b48b56ecee94 Mon Sep 17 00:00:00 2001 From: notify Date: Tue, 27 Feb 2024 02:28:13 +0800 Subject: [PATCH 24/30] Dev2 (#323) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CardItem一律可长按,除了卡牌一览 - Qml Mark在QML中可获得主人的id - Qml Mark可实现某某视角完全不可见 - 隐藏#开头的pile - 可自定义interaction了 - LogMessage新增toast成员 - 修复投降杀人bug --- Fk/Pages/CardsOverview.qml | 2 ++ Fk/Pages/Room.qml | 6 ++++ Fk/PhotoElement/MarkArea.qml | 3 +- Fk/RoomElement/CardItem.qml | 4 +-- Fk/RoomElement/HandcardArea.qml | 2 -- Fk/RoomElement/InvisibleCardArea.qml | 1 - Fk/RoomElement/Photo.qml | 1 + lua/client/client.lua | 18 ++++++++-- lua/server/request.lua | 23 ++---------- lua/server/room.lua | 27 ++++++++++++++ lua/server/system_enum.lua | 54 ++++++++++++++-------------- 11 files changed, 86 insertions(+), 55 deletions(-) diff --git a/Fk/Pages/CardsOverview.qml b/Fk/Pages/CardsOverview.qml index 70c95a11..8a2fb3ab 100644 --- a/Fk/Pages/CardsOverview.qml +++ b/Fk/Pages/CardsOverview.qml @@ -63,6 +63,7 @@ Item { delegate: CardItem { autoBack: false + showDetail: false property int dupCount: 0 Text { @@ -250,6 +251,7 @@ Item { Layout.alignment: Qt.AlignHCenter cid: 1 known: false + showDetail: false property int dupCount: 0 Text { diff --git a/Fk/Pages/Room.qml b/Fk/Pages/Room.qml index fc907544..0e02cd1f 100644 --- a/Fk/Pages/Room.qml +++ b/Fk/Pages/Room.qml @@ -819,6 +819,12 @@ Item { skillInteraction.item.from = data.from; skillInteraction.item.to = data.to; break; + case "custom": + skillInteraction.sourceComponent = + Qt.createComponent(AppPath + "/" + data.qml_path + ".qml"); + skillInteraction.item.skill = skill_name; + skillInteraction.item.extra_data = data; + break; default: skillInteraction.sourceComponent = undefined; break; diff --git a/Fk/PhotoElement/MarkArea.qml b/Fk/PhotoElement/MarkArea.qml index 4b7deaa3..ba36da59 100644 --- a/Fk/PhotoElement/MarkArea.qml +++ b/Fk/PhotoElement/MarkArea.qml @@ -81,6 +81,7 @@ Item { root.parent?.playerid); if (data && data.qml_path) { params.data = JSON.parse(_data); + params.owner = root.parent?.playerid; roomScene.startCheat("../../" + data.qml_path, params); } return; @@ -94,7 +95,7 @@ Item { params.ids = data; } - // Just for using room's right drawer + // Just for using right drawer of the room roomScene.startCheat("../RoomElement/ViewPile", params); } } diff --git a/Fk/RoomElement/CardItem.qml b/Fk/RoomElement/CardItem.qml index ea4f4d0c..e4cd9453 100644 --- a/Fk/RoomElement/CardItem.qml +++ b/Fk/RoomElement/CardItem.qml @@ -51,7 +51,7 @@ Item { property bool selected: false property bool draggable: false property bool autoBack: true - property bool showDetail: false + property bool showDetail: true property int origX: 0 property int origY: 0 property int initialZ: 0 @@ -76,7 +76,7 @@ Item { signal hoverChanged(bool enter) onRightClicked: { - if (!showDetail) return; + if (!showDetail || !known) return; roomScene.startCheat("CardDetail", { card: this }); } diff --git a/Fk/RoomElement/HandcardArea.qml b/Fk/RoomElement/HandcardArea.qml index 8541d7d9..3cd0b53f 100644 --- a/Fk/RoomElement/HandcardArea.qml +++ b/Fk/RoomElement/HandcardArea.qml @@ -34,7 +34,6 @@ Item { card.autoBack = true; card.draggable = true; card.selectable = false; - card.showDetail = true; card.clicked.connect(adjustCards); } @@ -46,7 +45,6 @@ Item { card = result[i]; card.draggable = false; card.selectable = false; - // card.showDetail = false; card.selectedChanged.disconnect(adjustCards); card.prohibitReason = ""; } diff --git a/Fk/RoomElement/InvisibleCardArea.qml b/Fk/RoomElement/InvisibleCardArea.qml index 25ba6ea4..4d533f77 100644 --- a/Fk/RoomElement/InvisibleCardArea.qml +++ b/Fk/RoomElement/InvisibleCardArea.qml @@ -64,7 +64,6 @@ Item { state.y = parentPos.y; state.opacity = 0; card = component.createObject(roomScene.dynamicCardArea, state); - card.showDetail = true card.x -= card.width / 2; card.x += (i - outputs.length / 2) * 15; card.y -= card.height / 2; diff --git a/Fk/RoomElement/Photo.qml b/Fk/RoomElement/Photo.qml index cb73de73..c77d0ed3 100644 --- a/Fk/RoomElement/Photo.qml +++ b/Fk/RoomElement/Photo.qml @@ -399,6 +399,7 @@ Item { } function updatePileInfo(areaName) { + if (areaName.startsWith('#')) return; const data = lcall("GetPile", root.playerid, areaName); if (data.length === 0) { root.markArea.removeMark(areaName); diff --git a/lua/client/client.lua b/lua/client/client.lua index 33304613..80bb17ec 100644 --- a/lua/client/client.lua +++ b/lua/client/client.lua @@ -211,7 +211,11 @@ end ---@param msg LogMessage 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 ---@param msg LogMessage @@ -790,9 +794,19 @@ fk.client_callback["SetPlayerMark"] = function(jsonData) -- jsonData: [ int id, string mark, int value ] local data = json.decode(jsonData) 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 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) end end diff --git a/lua/server/request.lua b/lua/server/request.lua index 79a0a70e..2d36437d 100644 --- a/lua/server/request.lua +++ b/lua/server/request.lua @@ -159,27 +159,10 @@ end request_handlers["surrender"] = function(room, id, reqlist) local player = room:getPlayerById(id) if not player then return end - local logic = room.logic - local curEvent = logic:getCurrentEvent() - if curEvent then - curEvent:addCleaner( - function() - player.surrendered = true - room:broadcastProperty(player, "surrendered") - local mode = Fk.game_modes[room.settings.gameMode] - local winner = Pcall(mode.getWinner, mode, player) - if winner ~= nil then - room:gameOver(winner) - end - -- 以防万一 - player.surrendered = false - room.hasSurrendered = false - end - ) - room.hasSurrendered = true - room:doBroadcastNotify("CancelRequest", "") - end + room.hasSurrendered = true + player.surrendered = true + room:doBroadcastNotify("CancelRequest", "") end request_handlers["updatemini"] = function(room, pid, reqlist) diff --git a/lua/server/room.lua b/lua/server/room.lua index 68491550..68b32a3d 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -687,6 +687,29 @@ function Room:doBroadcastNotify(command, jsonData, players) end end +---@param room Room +local function surrenderCheck(room) + if not room.hasSurrendered then return end + local player = table.find(room.players, function(p) + return p.surrendered + end) + if not player then + room.hasSurrendered = false + return + end + room:broadcastProperty(player, "surrendered") + local mode = Fk.game_modes[room.settings.gameMode] + local winner = Pcall(mode.getWinner, mode, player) + if winner ~= "" then + room:gameOver(winner) + end + + -- 以防万一 + player.surrendered = false + room:broadcastProperty(player, "surrendered") + room.hasSurrendered = false +end + --- 向某个玩家发起一次Request。 ---@param player ServerPlayer @ 发出这个请求的目标玩家 ---@param command string @ 请求的类型 @@ -703,6 +726,7 @@ function Room:doRequest(player, command, jsonData, wait) local ret = player:waitForReply(self.timeout) player.serverplayer:setBusy(false) player.serverplayer:setThinking(false) + surrenderCheck(self) return ret end end @@ -731,6 +755,8 @@ function Room:doBroadcastRequest(command, players, jsonData) p.serverplayer:setBusy(false) p.serverplayer:setThinking(false) end + + surrenderCheck(self) end --- 向多名玩家发出竞争请求。 @@ -798,6 +824,7 @@ function Room:doRaceRequest(command, players, jsonData) p.serverplayer:setThinking(false) end + surrenderCheck(self) return ret end diff --git a/lua/server/system_enum.lua b/lua/server/system_enum.lua index e75d679a..d77857ef 100644 --- a/lua/server/system_enum.lua +++ b/lua/server/system_enum.lua @@ -187,8 +187,34 @@ fk.IceDamage = 4 ---@field public pattern string ---@field public result Card ----@alias CardMoveReason integer +---@class PindianStruct +---@field public from ServerPlayer +---@field public tos ServerPlayer[] +---@field public fromCard Card +---@field public results table +---@field public reason string +---@class LogMessage +---@field public type string @ log主体 +---@field public from? integer @ 要替换%from的玩家的id +---@field public to? integer[] @ 要替换%to的玩家id列表 +---@field public card? integer[] @ 要替换%card的卡牌id列表 +---@field public arg? any @ 要替换%arg的内容 +---@field public arg2? any @ 要替换%arg2的内容 +---@field public arg3? any @ 要替换%arg3的内容 +---@field public toast? boolean @ 是否顺手把消息发送一条相同的toast + +---@class SkillUseStruct +---@field public skill Skill +---@field public willUse boolean + +---@class DrawCardStruct +---@field public who ServerPlayer +---@field public num number +---@field public skillName string +---@field public fromPlace "top"|"bottom" + +---@alias CardMoveReason integer fk.ReasonJustMove = 1 fk.ReasonDraw = 2 fk.ReasonDiscard = 3 @@ -201,29 +227,3 @@ fk.ReasonUse = 9 fk.ReasonResonpse = 10 fk.ReasonJudge = 11 fk.ReasonRecast = 12 - ----@class PindianStruct ----@field public from ServerPlayer ----@field public tos ServerPlayer[] ----@field public fromCard Card ----@field public results table ----@field public reason string - ----@class LogMessage ----@field public type string ----@field public from? integer ----@field public to? integer[] ----@field public card? integer[] ----@field public arg? any ----@field public arg2? any ----@field public arg3? any - ----@class SkillUseStruct ----@field public skill Skill ----@field public willUse boolean - ----@class DrawCardStruct ----@field public who ServerPlayer ----@field public num number ----@field public skillName string ----@field public fromPlace "top"|"bottom" From 8282607ce38fc49882ab8fde2312e5cd9b3ae304 Mon Sep 17 00:00:00 2001 From: notify Date: Tue, 27 Feb 2024 02:33:49 +0800 Subject: [PATCH 25/30] Changelog: v0.4.8 --- .github/workflows/build-windows.yml | 6 +++++- CHANGELOG.md | 27 +++++++++++++++++++++++++++ CMakeLists.txt | 2 +- android/AndroidManifest.xml | 4 ++-- lua/client/i18n/zh_CN.lua | 4 ++-- 5 files changed, 37 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index a3589ed1..b0176e85 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -32,6 +32,7 @@ jobs: target: 'desktop' arch: 'win64_mingw' modules: 'qtmultimedia qt5compat qtshadertools' + tools: 'tools_opensslv3_x64' - name: Disable PCH shell: bash @@ -46,6 +47,9 @@ jobs: working-directory: ${{github.workspace}} env: 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: | 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/en_US.qm 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 - name: Upload Release diff --git a/CHANGELOG.md b/CHANGELOG.md index 391202f9..36bd161c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,32 @@ # ChangeLog +## v0.4.8 + +- 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 - 攻击范围状态技类新增基础值修正函数 diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d46cec1..98d49f82 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.16) -project(FreeKill VERSION 0.4.7) +project(FreeKill VERSION 0.4.8) add_definitions(-DFK_VERSION=\"${CMAKE_PROJECT_VERSION}\") find_package(Qt6 REQUIRED COMPONENTS diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index bbac166b..439d9d85 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -3,8 +3,8 @@ + android:versionCode="408" + android:versionName="0.4.8"> diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index 59cdc393..2a080f60 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -321,9 +321,9 @@ FreeKill使用的是libgit2的C API,与此同时使用Git完成拓展包的下 ["Resume"] = "继续", ["Bulletin Info"] = [==[ - ## v0.4.7 + ## v0.4.8 - 补全快捷短语 + 修复投降莫名奇妙把对面杀了的bug ]==], } From 59c638fa47c5102fd60221d7a2cd374a3ce5bd2c Mon Sep 17 00:00:00 2001 From: notify Date: Tue, 27 Feb 2024 03:03:51 +0800 Subject: [PATCH 26/30] remove the quote --- .github/workflows/build-windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index b0176e85..c32fb5d2 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -78,7 +78,7 @@ jobs: cp build/zh_CN.qm FreeKill-release cp build/en_US.qm FreeKill-release cp ../Qt/6.5.3/mingw_64/bin/li*.dll FreeKill-release - cp ../Qt/Tools/OpenSSLv3/Win_x64/bin/libcrypto-3-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 - name: Upload Release From 4d94ee8a938e030e5caa0b836ce1e99613254b0d Mon Sep 17 00:00:00 2001 From: Ho-spair <62695577+Ho-spair@users.noreply.github.com> Date: Tue, 27 Feb 2024 16:32:14 +0800 Subject: [PATCH 27/30] =?UTF-8?q?=E5=9B=9E=E6=9D=A5=E5=90=A7=E6=88=91?= =?UTF-8?q?=E7=9A=84=E5=8D=81=E5=B8=B8=E4=BE=8D=20(#324)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 回来吧我的十常侍,我最骄傲的信仰,历历在目的休整,投降莫名在平局。 --- Fk/RoomElement/Photo.qml | 6 ++++-- lua/core/game_mode.lua | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Fk/RoomElement/Photo.qml b/Fk/RoomElement/Photo.qml index c77d0ed3..40bc5c58 100644 --- a/Fk/RoomElement/Photo.qml +++ b/Fk/RoomElement/Photo.qml @@ -442,10 +442,12 @@ Item { // id: saveme visible: (root.dead && !root.rest) || root.dying || root.surrendered source: { - if (root.dead) { + if (root.surrendered) { + return SkinBank.DEATH_DIR + "surrender"; + } else if (root.dead) { return SkinBank.getRoleDeathPic(root.role); } - return SkinBank.DEATH_DIR + (root.surrendered ? "surrender" : "saveme") + return SkinBank.DEATH_DIR + "saveme"; } anchors.centerIn: photoMask } diff --git a/lua/core/game_mode.lua b/lua/core/game_mode.lua index d8480b98..2062a1ff 100644 --- a/lua/core/game_mode.lua +++ b/lua/core/game_mode.lua @@ -27,7 +27,7 @@ end ---@param victim ServerPlayer @ 死者 ---@return string @ 胜者阵营 function GameMode:getWinner(victim) - if victim.rest > 0 then + if not victim.surrendered and victim.rest > 0 then return "" end From 726602873e5ee664bf93979467b83be6f8d83d0f Mon Sep 17 00:00:00 2001 From: notify Date: Tue, 27 Feb 2024 16:33:00 +0800 Subject: [PATCH 28/30] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=AD=A6=E5=B0=86?= =?UTF-8?q?=E7=89=8C=E5=A0=86=E5=9B=9E=E6=B5=81=20(role=20mode=E4=B8=8B)?= =?UTF-8?q?=20(#325)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 ++++-------------- lua/server/room.lua | 11 ++++++++--- packages/standard/init.lua | 30 ++++++++++-------------------- 3 files changed, 22 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 4ab0d5d0..040e914f 100644 --- a/README.md +++ b/README.md @@ -14,21 +14,11 @@ ___ 新月杀(FreeKill)是一款开源的三国杀游戏,但其目的不在于补完官方所有武将,而是着力于提供一个最适合DIY的框架。 +___ + ## 项目文档 -[新月杀文档](https://qsgs-fans.github.io/FreeKill/usr/index.html) - -### 依赖的库 - -以下是新月杀运行所必不可少的依赖库: - -* [![](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。 +https://fkbook-all-in-one.readthedocs.io/ ___ @@ -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 ___ diff --git a/lua/server/room.lua b/lua/server/room.lua index 68b32a3d..5f6b9688 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -1508,17 +1508,22 @@ end --- 把武将牌塞回去(……) ---@param g string[] @ 武将名数组 ----@param position? string @位置,top/bottom,默认bottom +---@param position? string @位置,top/bottom/random,默认random ---@return boolean @ 是否成功 function Room:returnToGeneralPile(g, position) - position = position or "bottom" - assert(position == "top" or position == "bottom") + position = position or "random" + assert(position == "top" or position == "bottom" or position == "random") if position == "bottom" then table.insertTable(self.general_pile, g) elseif position == "top" then while #g > 0 do table.insert(self.general_pile, 1, table.remove(g)) end + elseif position == "random" then + while #g > 0 do + table.insert(self.general_pile, math.random(1, #self.general_pile), + table.remove(g)) + end end return true diff --git a/packages/standard/init.lua b/packages/standard/init.lua index 16670af7..2b5fbaba 100644 --- a/packages/standard/init.lua +++ b/packages/standard/init.lua @@ -1132,17 +1132,12 @@ local role_getlogic = function() lord_general = lord_generals lord_generals = {lord_general} end + generals = table.filter(generals, function(g) + return not table.find(lord_generals, function(lg) + return Fk.generals[lg].trueName == Fk.generals[g].trueName + end) + end) room:returnToGeneralPile(generals) - local index = 1 - while index <= #room.general_pile do - local ret = {} - for _, gname in ipairs(lord_generals) do - if room.general_pile[index] == gname or Fk.generals[room.general_pile[index]].trueName == Fk.generals[gname].trueName then - table.insert(ret, table.remove(room.general_pile, index)) - end - end - if #ret == 0 then index = index + 1 end - end room:setPlayerGeneral(lord, lord_general, true) room:askForChooseKingdom({lord}) @@ -1231,17 +1226,12 @@ local role_getlogic = function() p.default_reply = "" end + generals = table.filter(generals, function(g) + return not table.find(selected, function(lg) + return Fk.generals[lg].trueName == Fk.generals[g].trueName + end) + end) room:returnToGeneralPile(generals) - local index = 1 - while index <= #room.general_pile do - local ret = {} - for _, gname in ipairs(selected) do - if room.general_pile[index] == gname or Fk.generals[room.general_pile[index]].trueName == Fk.generals[gname].trueName then - table.insert(ret, table.remove(room.general_pile, index)) - end - end - if #ret == 0 then index = index + 1 end - end room:askForChooseKingdom(nonlord) end From dfbe59e2b6025243f8378d4417072d74a680cbe5 Mon Sep 17 00:00:00 2001 From: Nyutanislavsky Date: Tue, 27 Feb 2024 17:21:28 +0800 Subject: [PATCH 29/30] Bug fix (#326) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 修复askForCard(s)Chosen 2. table.insertIfNeed添加返回值 3. 每轮每回合结束后filter手牌并播报手牌上限 4. 修复ex__choose --------- Co-authored-by: YoumuKon <38815081+YoumuKon@users.noreply.github.com> --- lua/client/client.lua | 2 ++ lua/core/util.lua | 1 + lua/server/events/gameflow.lua | 5 +++++ packages/standard/aux_skills.lua | 4 ---- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lua/client/client.lua b/lua/client/client.lua index 80bb17ec..71ae114a 100644 --- a/lua/client/client.lua +++ b/lua/client/client.lua @@ -391,6 +391,7 @@ fk.client_callback["AskForCardChosen"] = function(jsonData) 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._id = id ui_data._reason = reason ui_data._prompt = prompt end @@ -429,6 +430,7 @@ fk.client_callback["AskForCardsChosen"] = function(jsonData) 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._id = id ui_data._min = min ui_data._max = max ui_data._reason = reason diff --git a/lua/core/util.lua b/lua/core/util.lua index 77cabc5c..1e2a823c 100644 --- a/lua/core/util.lua +++ b/lua/core/util.lua @@ -335,6 +335,7 @@ end function table:insertIfNeed(element) if not table.contains(self, element) then table.insert(self, element) + return true end end diff --git a/lua/server/events/gameflow.lua b/lua/server/events/gameflow.lua index 2c8ebea5..5f096687 100644 --- a/lua/server/events/gameflow.lua +++ b/lua/server/events/gameflow.lua @@ -385,4 +385,9 @@ GameEvent.cleaners[GameEvent.Phase] = function(self) end end end + + for _, p in ipairs(room.players) do + p:filterHandcards() + room:broadcastProperty(p, "MaxCards") + end end diff --git a/packages/standard/aux_skills.lua b/packages/standard/aux_skills.lua index a3b527ab..dcbca2a4 100644 --- a/packages/standard/aux_skills.lua +++ b/packages/standard/aux_skills.lua @@ -119,10 +119,6 @@ local exChooseSkill = fk.CreateActiveSkill{ return table.contains(self.targets, to_select) end end, - min_target_num = function(self) return self.min_target_num end, - max_target_num = function(self) return self.max_target_num end, - min_card_num = function(self) return self.min_card_num end, - max_card_num = function(self) return self.max_card_num end, } local maxCardsSkill = fk.CreateMaxCardsSkill{ From 1209ca43fa979d8919a6b03cf7b14e47ccfd1685 Mon Sep 17 00:00:00 2001 From: YoumuKon <38815081+YoumuKon@users.noreply.github.com> Date: Tue, 27 Feb 2024 17:47:56 +0800 Subject: [PATCH 30/30] redo (#327) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重新添加轮次和回合的锁视和手牌上限提醒 --------- Co-authored-by: notify --- CHANGELOG.md | 2 +- CMakeLists.txt | 2 +- android/AndroidManifest.xml | 4 ++-- lua/client/i18n/zh_CN.lua | 2 +- lua/server/events/gameflow.lua | 10 ++++++++++ 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36bd161c..4a18f2f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # ChangeLog -## v0.4.8 +## v0.4.8 & v0.4.9 - Qml: 新增leval函数可获得lua表达式的值 - 新增AbstractRoom类 去除冗余 diff --git a/CMakeLists.txt b/CMakeLists.txt index 98d49f82..dbe3a5c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.16) -project(FreeKill VERSION 0.4.8) +project(FreeKill VERSION 0.4.9) add_definitions(-DFK_VERSION=\"${CMAKE_PROJECT_VERSION}\") find_package(Qt6 REQUIRED COMPONENTS diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 439d9d85..45896229 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -3,8 +3,8 @@ + android:versionCode="409" + android:versionName="0.4.9"> diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index 2a080f60..b9400988 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -321,7 +321,7 @@ FreeKill使用的是libgit2的C API,与此同时使用Git完成拓展包的下 ["Resume"] = "继续", ["Bulletin Info"] = [==[ - ## v0.4.8 + ## v0.4.9 修复投降莫名奇妙把对面杀了的bug diff --git a/lua/server/events/gameflow.lua b/lua/server/events/gameflow.lua index 5f096687..47370508 100644 --- a/lua/server/events/gameflow.lua +++ b/lua/server/events/gameflow.lua @@ -187,6 +187,11 @@ GameEvent.cleaners[GameEvent.Round] = function(self) end end end + + for _, p in ipairs(room.players) do + p:filterHandcards() + room:broadcastProperty(p, "MaxCards") + end end GameEvent.prepare_funcs[GameEvent.Turn] = function(self) @@ -262,6 +267,11 @@ GameEvent.cleaners[GameEvent.Turn] = function(self) end end end + + for _, p in ipairs(room.players) do + p:filterHandcards() + room:broadcastProperty(p, "MaxCards") + end end GameEvent.functions[GameEvent.Phase] = function(self)