From f50fbb7951e3836032e6e1a0f124d49e57d61242 Mon Sep 17 00:00:00 2001 From: katze Date: Wed, 6 Mar 2024 15:06:51 +0800 Subject: [PATCH 001/114] modify .gitignore to ignore files automatically added by src deployer --- .gitignore | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.gitignore b/.gitignore index fcbdca7c1..f2d8ffb46 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,14 @@ config/reloadflag config/reloadalas test.py test/ +resources/ +locales/ +*.pak +*.dll +*.dat +v8_context_snapshot.bin +snapshot_blob.bin +vk_swiftshader_icd.json # Created by .ignore support plugin (hsz.mobi) From 0d4ee2640f9067bfe358cfaeccb039c99ad46667 Mon Sep 17 00:00:00 2001 From: katze Date: Wed, 6 Mar 2024 16:32:14 +0800 Subject: [PATCH 002/114] rogue farming feature: enter rogue when RogueWorld_WeeklyFarming is enabled to acquire materials from boss --- config/template.json | 4 +++- module/config/argument/args.json | 8 ++++++++ module/config/argument/argument.yaml | 2 ++ module/config/config_generated.py | 2 ++ module/config/i18n/en-US.json | 8 ++++++++ module/config/i18n/es-ES.json | 8 ++++++++ module/config/i18n/ja-JP.json | 8 ++++++++ module/config/i18n/zh-CN.json | 8 ++++++++ module/config/i18n/zh-TW.json | 8 ++++++++ tasks/rogue/entry/entry.py | 14 ++++++++++---- tasks/rogue/rogue.py | 5 +++++ 11 files changed, 70 insertions(+), 5 deletions(-) diff --git a/config/template.json b/config/template.json index 72e91bb2a..8f8fb56d9 100644 --- a/config/template.json +++ b/config/template.json @@ -193,7 +193,9 @@ "DomainStrategy": "combat", "UseImmersifier": true, "DoubleEvent": true, - "UseStamina": false + "UseStamina": false, + "WeeklyFarming": false, + "WeeklyFarmingCount": 0 }, "RogueBlessing": { "PresetBlessingFilter": "preset", diff --git a/module/config/argument/args.json b/module/config/argument/args.json index c85879ceb..0b2e25f14 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -1423,6 +1423,14 @@ "UseStamina": { "type": "checkbox", "value": false + }, + "WeeklyFarming": { + "type": "checkbox", + "value": false + }, + "WeeklyFarmingCount": { + "type": "input", + "value": 0 } }, "RogueBlessing": { diff --git a/module/config/argument/argument.yaml b/module/config/argument/argument.yaml index 871437c39..ec4bd32e4 100644 --- a/module/config/argument/argument.yaml +++ b/module/config/argument/argument.yaml @@ -237,6 +237,8 @@ RogueWorld: UseImmersifier: true DoubleEvent: true UseStamina: false + WeeklyFarming: false + WeeklyFarmingCount: 0 RogueBlessing: PresetBlessingFilter: diff --git a/module/config/config_generated.py b/module/config/config_generated.py index 8af4d7616..4f2d92239 100644 --- a/module/config/config_generated.py +++ b/module/config/config_generated.py @@ -133,6 +133,8 @@ class GeneratedConfig: RogueWorld_UseImmersifier = True RogueWorld_DoubleEvent = True RogueWorld_UseStamina = False + RogueWorld_WeeklyFarming = False + RogueWorld_WeeklyFarmingCount = 0 # Group `RogueBlessing` RogueBlessing_PresetBlessingFilter = 'preset' # preset, custom diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index b6dad37bd..c5f643d53 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -940,6 +940,14 @@ "UseStamina": { "name": "Farm Planers Using Trailblase Power", "help": "Task \"Dungeon\" will no longer run, and all trailblaze power will be used first to claim immersion rewards, except for double events." + }, + "WeeklyFarming": { + "name": "RogueWorld.WeeklyFarming.name", + "help": "RogueWorld.WeeklyFarming.help" + }, + "WeeklyFarmingCount": { + "name": "RogueWorld.WeeklyFarmingCount.name", + "help": "RogueWorld.WeeklyFarmingCount.help" } }, "RogueBlessing": { diff --git a/module/config/i18n/es-ES.json b/module/config/i18n/es-ES.json index e9a468347..473addc25 100644 --- a/module/config/i18n/es-ES.json +++ b/module/config/i18n/es-ES.json @@ -940,6 +940,14 @@ "UseStamina": { "name": "Reclamar de planers mediante poder trazacaminos", "help": "La tarea de mazmorra ya no se ejecutará y todo el poder trazacaminos se usará primero para reclamar recompensas de inmersión, excepto para eventos dobles" + }, + "WeeklyFarming": { + "name": "RogueWorld.WeeklyFarming.name", + "help": "RogueWorld.WeeklyFarming.help" + }, + "WeeklyFarmingCount": { + "name": "RogueWorld.WeeklyFarmingCount.name", + "help": "RogueWorld.WeeklyFarmingCount.help" } }, "RogueBlessing": { diff --git a/module/config/i18n/ja-JP.json b/module/config/i18n/ja-JP.json index a09ba1f54..92a388025 100644 --- a/module/config/i18n/ja-JP.json +++ b/module/config/i18n/ja-JP.json @@ -940,6 +940,14 @@ "UseStamina": { "name": "RogueWorld.UseStamina.name", "help": "RogueWorld.UseStamina.help" + }, + "WeeklyFarming": { + "name": "RogueWorld.WeeklyFarming.name", + "help": "RogueWorld.WeeklyFarming.help" + }, + "WeeklyFarmingCount": { + "name": "RogueWorld.WeeklyFarmingCount.name", + "help": "RogueWorld.WeeklyFarmingCount.help" } }, "RogueBlessing": { diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index 4f8a58c57..bc469f3c2 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -940,6 +940,14 @@ "UseStamina": { "name": "使用开拓力刷内圈遗器", "help": "每日副本任务将不再打本,所有开拓力将优先被用于领取浸器奖励,双倍活动时除外" + }, + "WeeklyFarming": { + "name": "积分满后继续刷取材料", + "help": "每周积分满后不停止,刷取完33次boss以获取形迹材料" + }, + "WeeklyFarmingCount": { + "name": "剩余材料刷取次数", + "help": "每周刷取33次boss,即11次模拟宇宙" } }, "RogueBlessing": { diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index b77f7b05f..933cb3cb1 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -940,6 +940,14 @@ "UseStamina": { "name": "用開拓力農遺器", "help": "每日副本任務將不再打本,所有開拓力將優先被用於領取浸器獎勵,雙倍活動時除外" + }, + "WeeklyFarming": { + "name": "RogueWorld.WeeklyFarming.name", + "help": "RogueWorld.WeeklyFarming.help" + }, + "WeeklyFarmingCount": { + "name": "RogueWorld.WeeklyFarmingCount.name", + "help": "RogueWorld.WeeklyFarmingCount.help" } }, "RogueBlessing": { diff --git a/tasks/rogue/entry/entry.py b/tasks/rogue/entry/entry.py index 4a7bd2410..0ec0b4009 100644 --- a/tasks/rogue/entry/entry.py +++ b/tasks/rogue/entry/entry.py @@ -332,7 +332,8 @@ class RogueEntry(RouteBase, RogueRewardHandler, RoguePathHandler, DungeonUI): """ logger.info(f'RogueWorld_UseImmersifier={self.config.RogueWorld_UseImmersifier}, ' f'RogueWorld_UseStamina={self.config.RogueWorld_UseStamina}, ' - f'RogueWorld_DoubleEvent={self.config.RogueWorld_DoubleEvent}' + f'RogueWorld_DoubleEvent={self.config.RogueWorld_DoubleEvent}, ' + f'RogueWorld_WeeklyFarming={self.config.RogueWorld_WeeklyFarming}, ' f'RogueDebug_DebugMode={self.config.RogueDebug_DebugMode}') # This shouldn't happen if self.config.RogueWorld_UseStamina and not self.config.RogueWorld_UseImmersifier: @@ -347,11 +348,16 @@ class RogueEntry(RouteBase, RogueRewardHandler, RoguePathHandler, DungeonUI): return if self.config.stored.SimulatedUniverse.is_expired(): - # Expired, do rogue - pass + # Expired, do rogue and reset weekly farming count + self.config.RogueWorld_WeeklyFarmingCount = 11 + elif self.config.stored.SimulatedUniverse.is_full(): if self.config.RogueWorld_UseImmersifier and self.config.stored.Immersifier.value > 0: - logger.info('Reached weekly point limit but still have immersifiers left, continue to use them') + logger.info( + 'Reached weekly point limit but still have immersifiers left, continue to use them') + elif self.config.RogueWorld_WeeklyFarming and self.config.RogueWorld_WeeklyFarmingCount > 0: + logger.info( + 'Reached weekly point limit but still continue to farm materials') else: raise RogueReachedWeeklyPointLimit else: diff --git a/tasks/rogue/rogue.py b/tasks/rogue/rogue.py index 7635b25b8..6ba91504f 100644 --- a/tasks/rogue/rogue.py +++ b/tasks/rogue/rogue.py @@ -28,6 +28,11 @@ class Rogue(RouteLoader, RogueEntry): self.rogue_run() self.rogue_reward_claim() + # runs when one rogue run finishes (do not handle whether the last rogue run finished or failed due to one who use this feature has less chance of failure -- would be better if check boss battle result instead of rogue result, but this version also works), and decreases rogue farming count by 1 + if self.config.RogueWorld_WeeklyFarming and self.config.RogueWorld_WeeklyFarmingCount > 0: + self.config.RogueWorld_WeeklyFarmingCount -= 1 + logger.attr("WeeklyFarmingCount", + self.config.RogueWorld_WeeklyFarmingCount) return True def run(self): From e49ec5479e76a71044ae22f7fba55c6abf028924 Mon Sep 17 00:00:00 2001 From: katze Date: Wed, 6 Mar 2024 16:57:01 +0800 Subject: [PATCH 003/114] multilanguage translation --- module/config/i18n/en-US.json | 8 ++++---- module/config/i18n/es-ES.json | 8 ++++---- module/config/i18n/ja-JP.json | 8 ++++---- module/config/i18n/zh-CN.json | 8 ++++---- module/config/i18n/zh-TW.json | 8 ++++---- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index c5f643d53..3571e9cbf 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -942,12 +942,12 @@ "help": "Task \"Dungeon\" will no longer run, and all trailblaze power will be used first to claim immersion rewards, except for double events." }, "WeeklyFarming": { - "name": "RogueWorld.WeeklyFarming.name", - "help": "RogueWorld.WeeklyFarming.help" + "name": "Boss Material Farming", + "help": "Not stop after weekly points are full, farm the boss 33 times (i.e., 11 cosmic simulations) instead to obtain trace materials" }, "WeeklyFarmingCount": { - "name": "RogueWorld.WeeklyFarmingCount.name", - "help": "RogueWorld.WeeklyFarmingCount.help" + "name": "Material Attempts Remaining This Week: X", + "help": "Resets every Monday" } }, "RogueBlessing": { diff --git a/module/config/i18n/es-ES.json b/module/config/i18n/es-ES.json index 473addc25..def4b7825 100644 --- a/module/config/i18n/es-ES.json +++ b/module/config/i18n/es-ES.json @@ -942,12 +942,12 @@ "help": "La tarea de mazmorra ya no se ejecutará y todo el poder trazacaminos se usará primero para reclamar recompensas de inmersión, excepto para eventos dobles" }, "WeeklyFarming": { - "name": "RogueWorld.WeeklyFarming.name", - "help": "RogueWorld.WeeklyFarming.help" + "name": "Recolección de Materiales de Jefes", + "help": "No detenerse después de que los puntos semanales estén llenos, farmear al jefe 33 veces (es decir, 11 simulaciones cósmicas) para obtener materiales de rastro" }, "WeeklyFarmingCount": { - "name": "RogueWorld.WeeklyFarmingCount.name", - "help": "RogueWorld.WeeklyFarmingCount.help" + "name": "Intentos de Material Restantes Esta Semana: X", + "help": "Se reinicia cada lunes" } }, "RogueBlessing": { diff --git a/module/config/i18n/ja-JP.json b/module/config/i18n/ja-JP.json index 92a388025..272429ca7 100644 --- a/module/config/i18n/ja-JP.json +++ b/module/config/i18n/ja-JP.json @@ -942,12 +942,12 @@ "help": "RogueWorld.UseStamina.help" }, "WeeklyFarming": { - "name": "RogueWorld.WeeklyFarming.name", - "help": "RogueWorld.WeeklyFarming.help" + "name": "ボス素材を収集", + "help": "週間ポイントが満点に達しても停止せず、33回のボス(つまり11回の模擬宇宙)を収集して軌跡素材を入手" }, "WeeklyFarmingCount": { - "name": "RogueWorld.WeeklyFarmingCount.name", - "help": "RogueWorld.WeeklyFarmingCount.help" + "name": "今週の素材が残りX回", + "help": "毎週月曜日にリセット" } }, "RogueBlessing": { diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index bc469f3c2..185558a84 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -942,12 +942,12 @@ "help": "每日副本任务将不再打本,所有开拓力将优先被用于领取浸器奖励,双倍活动时除外" }, "WeeklyFarming": { - "name": "积分满后继续刷取材料", - "help": "每周积分满后不停止,刷取完33次boss以获取形迹材料" + "name": "刷取Boss材料", + "help": "每周积分满后不停止,刷取33次Boss(即11次模拟宇宙)以获取形迹材料" }, "WeeklyFarmingCount": { - "name": "剩余材料刷取次数", - "help": "每周刷取33次boss,即11次模拟宇宙" + "name": "本周材料剩余X次", + "help": "每周一重置" } }, "RogueBlessing": { diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index 933cb3cb1..5f60af443 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -942,12 +942,12 @@ "help": "每日副本任務將不再打本,所有開拓力將優先被用於領取浸器獎勵,雙倍活動時除外" }, "WeeklyFarming": { - "name": "RogueWorld.WeeklyFarming.name", - "help": "RogueWorld.WeeklyFarming.help" + "name": "刷取Boss材料", + "help": "每週積分滿後不停止,刷取33次Boss(即11次模擬宇宙)以獲取形跡材料" }, "WeeklyFarmingCount": { - "name": "RogueWorld.WeeklyFarmingCount.name", - "help": "RogueWorld.WeeklyFarmingCount.help" + "name": "本週材料剩餘X次", + "help": "每週一重置" } }, "RogueBlessing": { From 7130b09540d3fb50131e90c7126db4c87c53e8d7 Mon Sep 17 00:00:00 2001 From: katze Date: Wed, 6 Mar 2024 17:11:37 +0800 Subject: [PATCH 004/114] translation bugfix --- module/config/i18n/en-US.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index 3571e9cbf..1b1e2cedb 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -943,7 +943,7 @@ }, "WeeklyFarming": { "name": "Boss Material Farming", - "help": "Not stop after weekly points are full, farm the boss 33 times (i.e., 11 cosmic simulations) instead to obtain trace materials" + "help": "Not stop after point reward is full; farm the boss 33 times (i.e., 11 Simulated Universe) instead to obtain trace materials" }, "WeeklyFarmingCount": { "name": "Material Attempts Remaining This Week: X", From 76c8f9eda62f0249c51efa3bb3a242fb0feb2d1c Mon Sep 17 00:00:00 2001 From: katze Date: Fri, 8 Mar 2024 11:31:24 +0800 Subject: [PATCH 005/114] discard modification of .gitignore cause it is less related to the current PR --- .gitignore | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.gitignore b/.gitignore index f2d8ffb46..fcbdca7c1 100644 --- a/.gitignore +++ b/.gitignore @@ -19,14 +19,6 @@ config/reloadflag config/reloadalas test.py test/ -resources/ -locales/ -*.pak -*.dll -*.dat -v8_context_snapshot.bin -snapshot_blob.bin -vk_swiftshader_icd.json # Created by .ignore support plugin (hsz.mobi) From f5bc0d0ce14f975b72600e478902e80af4e2cb93 Mon Sep 17 00:00:00 2001 From: katze Date: Sat, 9 Mar 2024 15:12:11 +0800 Subject: [PATCH 006/114] Use stored counter to count boss farming times to run --- config/template.json | 3 +-- module/config/argument/args.json | 4 ---- module/config/argument/argument.yaml | 1 - module/config/config_generated.py | 1 - module/config/i18n/en-US.json | 6 +----- module/config/i18n/es-ES.json | 6 +----- module/config/i18n/ja-JP.json | 6 +----- module/config/i18n/zh-CN.json | 6 +----- module/config/i18n/zh-TW.json | 6 +----- module/config/stored/classes.py | 15 ++++++++++++++- tasks/rogue/entry/entry.py | 8 +++++--- tasks/rogue/rogue.py | 5 ----- tasks/rogue/route/base.py | 7 +++++++ 13 files changed, 32 insertions(+), 42 deletions(-) diff --git a/config/template.json b/config/template.json index 8f8fb56d9..1ef395cea 100644 --- a/config/template.json +++ b/config/template.json @@ -194,8 +194,7 @@ "UseImmersifier": true, "DoubleEvent": true, "UseStamina": false, - "WeeklyFarming": false, - "WeeklyFarmingCount": 0 + "WeeklyFarming": false }, "RogueBlessing": { "PresetBlessingFilter": "preset", diff --git a/module/config/argument/args.json b/module/config/argument/args.json index 0b2e25f14..e06a570af 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -1427,10 +1427,6 @@ "WeeklyFarming": { "type": "checkbox", "value": false - }, - "WeeklyFarmingCount": { - "type": "input", - "value": 0 } }, "RogueBlessing": { diff --git a/module/config/argument/argument.yaml b/module/config/argument/argument.yaml index ec4bd32e4..9e0274401 100644 --- a/module/config/argument/argument.yaml +++ b/module/config/argument/argument.yaml @@ -238,7 +238,6 @@ RogueWorld: DoubleEvent: true UseStamina: false WeeklyFarming: false - WeeklyFarmingCount: 0 RogueBlessing: PresetBlessingFilter: diff --git a/module/config/config_generated.py b/module/config/config_generated.py index 4f2d92239..450762d34 100644 --- a/module/config/config_generated.py +++ b/module/config/config_generated.py @@ -134,7 +134,6 @@ class GeneratedConfig: RogueWorld_DoubleEvent = True RogueWorld_UseStamina = False RogueWorld_WeeklyFarming = False - RogueWorld_WeeklyFarmingCount = 0 # Group `RogueBlessing` RogueBlessing_PresetBlessingFilter = 'preset' # preset, custom diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index 1b1e2cedb..af3f38303 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -943,11 +943,7 @@ }, "WeeklyFarming": { "name": "Boss Material Farming", - "help": "Not stop after point reward is full; farm the boss 33 times (i.e., 11 Simulated Universe) instead to obtain trace materials" - }, - "WeeklyFarmingCount": { - "name": "Material Attempts Remaining This Week: X", - "help": "Resets every Monday" + "help": "Do not stop after point reward is full; farm the boss 100 times instead to obtain trace materials" } }, "RogueBlessing": { diff --git a/module/config/i18n/es-ES.json b/module/config/i18n/es-ES.json index def4b7825..22be96fa2 100644 --- a/module/config/i18n/es-ES.json +++ b/module/config/i18n/es-ES.json @@ -943,11 +943,7 @@ }, "WeeklyFarming": { "name": "Recolección de Materiales de Jefes", - "help": "No detenerse después de que los puntos semanales estén llenos, farmear al jefe 33 veces (es decir, 11 simulaciones cósmicas) para obtener materiales de rastro" - }, - "WeeklyFarmingCount": { - "name": "Intentos de Material Restantes Esta Semana: X", - "help": "Se reinicia cada lunes" + "help": "No detenerse después de que los puntos semanales estén llenos, farmear al jefe 100 veces para obtener materiales de rastro" } }, "RogueBlessing": { diff --git a/module/config/i18n/ja-JP.json b/module/config/i18n/ja-JP.json index 272429ca7..be59b56d2 100644 --- a/module/config/i18n/ja-JP.json +++ b/module/config/i18n/ja-JP.json @@ -943,11 +943,7 @@ }, "WeeklyFarming": { "name": "ボス素材を収集", - "help": "週間ポイントが満点に達しても停止せず、33回のボス(つまり11回の模擬宇宙)を収集して軌跡素材を入手" - }, - "WeeklyFarmingCount": { - "name": "今週の素材が残りX回", - "help": "毎週月曜日にリセット" + "help": "週間ポイントが満点に達しても停止せず、100回のボスをファームして軌跡素材を入手" } }, "RogueBlessing": { diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index 185558a84..2658d658f 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -943,11 +943,7 @@ }, "WeeklyFarming": { "name": "刷取Boss材料", - "help": "每周积分满后不停止,刷取33次Boss(即11次模拟宇宙)以获取形迹材料" - }, - "WeeklyFarmingCount": { - "name": "本周材料剩余X次", - "help": "每周一重置" + "help": "每周积分满后不停止,刷取100次Boss以获取形迹材料" } }, "RogueBlessing": { diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index 5f60af443..6af71b5f5 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -943,11 +943,7 @@ }, "WeeklyFarming": { "name": "刷取Boss材料", - "help": "每週積分滿後不停止,刷取33次Boss(即11次模擬宇宙)以獲取形跡材料" - }, - "WeeklyFarmingCount": { - "name": "本週材料剩餘X次", - "help": "每週一重置" + "help": "每週積分滿後不停止,刷取100次Boss以獲取形跡材料" } }, "RogueBlessing": { diff --git a/module/config/stored/classes.py b/module/config/stored/classes.py index 44e818826..de1c7cdf7 100644 --- a/module/config/stored/classes.py +++ b/module/config/stored/classes.py @@ -205,7 +205,20 @@ class StoredImmersifier(StoredCounter): class StoredSimulatedUniverse(StoredCounter, StoredExpiredAtMonday0400): - pass + FIXED_DEFAULT = 100 + FarmingCounter = FIXED_DEFAULT + + def farm_dec(self, value=1): + self.FarmingCounter -= value + + def farm_reset(self,): + self.FarmingCounter = self.FIXED_DEFAULT + + def farm_not_full(self) -> bool: + return self.FarmingCounter > 0 + + def farm_get_remain(self) -> int: + return self.FarmingCounter class StoredAssignment(StoredCounter): diff --git a/tasks/rogue/entry/entry.py b/tasks/rogue/entry/entry.py index 0ec0b4009..723a668d9 100644 --- a/tasks/rogue/entry/entry.py +++ b/tasks/rogue/entry/entry.py @@ -348,14 +348,16 @@ class RogueEntry(RouteBase, RogueRewardHandler, RoguePathHandler, DungeonUI): return if self.config.stored.SimulatedUniverse.is_expired(): - # Expired, do rogue and reset weekly farming count - self.config.RogueWorld_WeeklyFarmingCount = 11 + # Expired, do rogue and reset farming counter + self.config.stored.SimulatedUniverse.farm_reset() elif self.config.stored.SimulatedUniverse.is_full(): if self.config.RogueWorld_UseImmersifier and self.config.stored.Immersifier.value > 0: logger.info( 'Reached weekly point limit but still have immersifiers left, continue to use them') - elif self.config.RogueWorld_WeeklyFarming and self.config.RogueWorld_WeeklyFarmingCount > 0: + elif self.config.RogueWorld_WeeklyFarming and self.config.stored.SimulatedUniverse.farm_not_full(): + logger.attr( + "Farming Counter", self.config.stored.SimulatedUniverse.farm_get_remain()) logger.info( 'Reached weekly point limit but still continue to farm materials') else: diff --git a/tasks/rogue/rogue.py b/tasks/rogue/rogue.py index 6ba91504f..7635b25b8 100644 --- a/tasks/rogue/rogue.py +++ b/tasks/rogue/rogue.py @@ -28,11 +28,6 @@ class Rogue(RouteLoader, RogueEntry): self.rogue_run() self.rogue_reward_claim() - # runs when one rogue run finishes (do not handle whether the last rogue run finished or failed due to one who use this feature has less chance of failure -- would be better if check boss battle result instead of rogue result, but this version also works), and decreases rogue farming count by 1 - if self.config.RogueWorld_WeeklyFarming and self.config.RogueWorld_WeeklyFarmingCount > 0: - self.config.RogueWorld_WeeklyFarmingCount -= 1 - logger.attr("WeeklyFarmingCount", - self.config.RogueWorld_WeeklyFarmingCount) return True def run(self): diff --git a/tasks/rogue/route/base.py b/tasks/rogue/route/base.py index b68a4f793..fc8a34c77 100644 --- a/tasks/rogue/route/base.py +++ b/tasks/rogue/route/base.py @@ -170,6 +170,13 @@ class RouteBase(RouteBase_, RogueExit, RogueEvent, RogueReward): pass result = super().clear_enemy(*waypoints) + + # runs when one elite battle finishes (do not handle whether battle succeeded or failed), and decreases rogue farming count by 1 + if self.config.RogueWorld_WeeklyFarming and self.config.stored.SimulatedUniverse.farm_not_full(): + self.config.stored.SimulatedUniverse.farm_dec() + logger.info( + f"Cleared elite boss, decrease farming count by 1, now {self.config.stored.SimulatedUniverse.FarmingCounter}") + return result def _domain_event_expected_end(self): From 2bc797f3e2025b1c9a40686e0d88e70b2c39508c Mon Sep 17 00:00:00 2001 From: katze Date: Sat, 9 Mar 2024 15:58:37 +0800 Subject: [PATCH 007/114] Add comments in stored counter class definition --- module/config/stored/classes.py | 9 +++++++-- tasks/rogue/entry/entry.py | 2 +- tasks/rogue/route/base.py | 4 ++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/module/config/stored/classes.py b/module/config/stored/classes.py index de1c7cdf7..8f62ee857 100644 --- a/module/config/stored/classes.py +++ b/module/config/stored/classes.py @@ -205,16 +205,21 @@ class StoredImmersifier(StoredCounter): class StoredSimulatedUniverse(StoredCounter, StoredExpiredAtMonday0400): + # These variables are used in Rogue Farming feature. + + # Times of boss drop chance per week. In current version of StarRail, this value is 100. FIXED_DEFAULT = 100 + + # Times left to farm. Resets to 100 every Monday 04:00, and decreases each time the elite boss is cleared. FarmingCounter = FIXED_DEFAULT - def farm_dec(self, value=1): + def farm_dec(self, value = 1): self.FarmingCounter -= value def farm_reset(self,): self.FarmingCounter = self.FIXED_DEFAULT - def farm_not_full(self) -> bool: + def farm_not_complete(self) -> bool: return self.FarmingCounter > 0 def farm_get_remain(self) -> int: diff --git a/tasks/rogue/entry/entry.py b/tasks/rogue/entry/entry.py index 723a668d9..f81764df6 100644 --- a/tasks/rogue/entry/entry.py +++ b/tasks/rogue/entry/entry.py @@ -355,7 +355,7 @@ class RogueEntry(RouteBase, RogueRewardHandler, RoguePathHandler, DungeonUI): if self.config.RogueWorld_UseImmersifier and self.config.stored.Immersifier.value > 0: logger.info( 'Reached weekly point limit but still have immersifiers left, continue to use them') - elif self.config.RogueWorld_WeeklyFarming and self.config.stored.SimulatedUniverse.farm_not_full(): + elif self.config.RogueWorld_WeeklyFarming and self.config.stored.SimulatedUniverse.farm_not_complete(): logger.attr( "Farming Counter", self.config.stored.SimulatedUniverse.farm_get_remain()) logger.info( diff --git a/tasks/rogue/route/base.py b/tasks/rogue/route/base.py index fc8a34c77..54d1f766b 100644 --- a/tasks/rogue/route/base.py +++ b/tasks/rogue/route/base.py @@ -171,8 +171,8 @@ class RouteBase(RouteBase_, RogueExit, RogueEvent, RogueReward): result = super().clear_enemy(*waypoints) - # runs when one elite battle finishes (do not handle whether battle succeeded or failed), and decreases rogue farming count by 1 - if self.config.RogueWorld_WeeklyFarming and self.config.stored.SimulatedUniverse.farm_not_full(): + # runs when one elite battle finishes (do not handle whether the battle succeeded or failed), and decreases rogue farming count by 1 + if self.config.RogueWorld_WeeklyFarming and self.config.stored.SimulatedUniverse.farm_not_complete(): self.config.stored.SimulatedUniverse.farm_dec() logger.info( f"Cleared elite boss, decrease farming count by 1, now {self.config.stored.SimulatedUniverse.FarmingCounter}") From f262140fb6e22062e5f38b9a2db6d0e64fedf857 Mon Sep 17 00:00:00 2001 From: katze Date: Mon, 1 Apr 2024 01:58:46 +0800 Subject: [PATCH 008/114] Seperate stored counter into an independent class --- config/template.json | 3 ++- module/config/argument/args.json | 6 ++++++ module/config/argument/argument.yaml | 2 ++ module/config/argument/stored.json | 13 +++++++++++++ module/config/config_generated.py | 1 + module/config/i18n/en-US.json | 4 ++++ module/config/i18n/es-ES.json | 4 ++++ module/config/i18n/ja-JP.json | 4 ++++ module/config/i18n/zh-CN.json | 4 ++++ module/config/i18n/zh-TW.json | 4 ++++ module/config/stored/classes.py | 16 ++++++++++------ module/config/stored/stored_generated.py | 2 ++ tasks/map/control/control.py | 4 ++-- tasks/rogue/entry/entry.py | 12 ++++++++---- tasks/rogue/route/base.py | 13 +++++++------ 15 files changed, 73 insertions(+), 19 deletions(-) diff --git a/config/template.json b/config/template.json index 4c2aaf5d8..308482694 100644 --- a/config/template.json +++ b/config/template.json @@ -200,7 +200,8 @@ "UseImmersifier": true, "DoubleEvent": true, "UseStamina": false, - "WeeklyFarming": false + "WeeklyFarming": false, + "SimulatedUniverseElite": {} }, "RogueBlessing": { "PresetBlessingFilter": "preset", diff --git a/module/config/argument/args.json b/module/config/argument/args.json index 42a530a31..0b90417ca 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -1473,6 +1473,12 @@ "WeeklyFarming": { "type": "checkbox", "value": false + }, + "SimulatedUniverseElite": { + "type": "stored", + "value": {}, + "display": "hide", + "stored": "StoredSimulatedUniverseElite" } }, "RogueBlessing": { diff --git a/module/config/argument/argument.yaml b/module/config/argument/argument.yaml index 51c228e9a..9a94e5aeb 100644 --- a/module/config/argument/argument.yaml +++ b/module/config/argument/argument.yaml @@ -248,6 +248,8 @@ RogueWorld: DoubleEvent: true UseStamina: false WeeklyFarming: false + SimulatedUniverseElite: + stored: StoredSimulatedUniverseElite RogueBlessing: PresetBlessingFilter: diff --git a/module/config/argument/stored.json b/module/config/argument/stored.json index f984b47e4..37764ba0c 100644 --- a/module/config/argument/stored.json +++ b/module/config/argument/stored.json @@ -293,5 +293,18 @@ }, "order": 0, "color": "#777777" + }, + "SimulatedUniverseElite": { + "name": "SimulatedUniverseElite", + "path": "Rogue.RogueWorld.SimulatedUniverseElite", + "i18n": "RogueWorld.SimulatedUniverseElite.name", + "stored": "StoredSimulatedUniverseElite", + "attrs": { + "time": "2020-01-01 00:00:00", + "total": 0, + "value": 100 + }, + "order": 0, + "color": "#777777" } } \ No newline at end of file diff --git a/module/config/config_generated.py b/module/config/config_generated.py index 3c5aedb2c..31c800862 100644 --- a/module/config/config_generated.py +++ b/module/config/config_generated.py @@ -140,6 +140,7 @@ class GeneratedConfig: RogueWorld_DoubleEvent = True RogueWorld_UseStamina = False RogueWorld_WeeklyFarming = False + RogueWorld_SimulatedUniverseElite = {} # Group `RogueBlessing` RogueBlessing_PresetBlessingFilter = 'preset' # preset, custom diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index f42961a16..997369465 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -976,6 +976,10 @@ "WeeklyFarming": { "name": "Boss Material Farming", "help": "Do not stop after point reward is full; farm the boss 100 times instead to obtain trace materials" + }, + "SimulatedUniverseElite": { + "name": "RogueWorld.SimulatedUniverseElite.name", + "help": "RogueWorld.SimulatedUniverseElite.help" } }, "RogueBlessing": { diff --git a/module/config/i18n/es-ES.json b/module/config/i18n/es-ES.json index 2bcadc64c..b14d594d4 100644 --- a/module/config/i18n/es-ES.json +++ b/module/config/i18n/es-ES.json @@ -976,6 +976,10 @@ "WeeklyFarming": { "name": "Recolección de Materiales de Jefes", "help": "No detenerse después de que los puntos semanales estén llenos, farmear al jefe 100 veces para obtener materiales de rastro" + }, + "SimulatedUniverseElite": { + "name": "RogueWorld.SimulatedUniverseElite.name", + "help": "RogueWorld.SimulatedUniverseElite.help" } }, "RogueBlessing": { diff --git a/module/config/i18n/ja-JP.json b/module/config/i18n/ja-JP.json index 1a02dc908..fc1dada67 100644 --- a/module/config/i18n/ja-JP.json +++ b/module/config/i18n/ja-JP.json @@ -976,6 +976,10 @@ "WeeklyFarming": { "name": "ボス素材を収集", "help": "週間ポイントが満点に達しても停止せず、100回のボスをファームして軌跡素材を入手" + }, + "SimulatedUniverseElite": { + "name": "RogueWorld.SimulatedUniverseElite.name", + "help": "RogueWorld.SimulatedUniverseElite.help" } }, "RogueBlessing": { diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index 5423d4ba5..4b5a3e1de 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -976,6 +976,10 @@ "WeeklyFarming": { "name": "刷取Boss材料", "help": "每周积分满后不停止,刷取100次Boss以获取形迹材料" + }, + "SimulatedUniverseElite": { + "name": "剩余Boss材料掉落次数", + "help": "RogueWorld.SimulatedUniverseElite.help" } }, "RogueBlessing": { diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index 086ec5a46..0f541b88d 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -976,6 +976,10 @@ "WeeklyFarming": { "name": "刷取Boss材料", "help": "每週積分滿後不停止,刷取100次Boss以獲取形跡材料" + }, + "SimulatedUniverseElite": { + "name": "RogueWorld.SimulatedUniverseElite.name", + "help": "RogueWorld.SimulatedUniverseElite.help" } }, "RogueBlessing": { diff --git a/module/config/stored/classes.py b/module/config/stored/classes.py index 8f62ee857..f5ac21193 100644 --- a/module/config/stored/classes.py +++ b/module/config/stored/classes.py @@ -205,25 +205,29 @@ class StoredImmersifier(StoredCounter): class StoredSimulatedUniverse(StoredCounter, StoredExpiredAtMonday0400): + pass + + +class StoredSimulatedUniverseElite(StoredCounter, StoredExpiredAtMonday0400): # These variables are used in Rogue Farming feature. # Times of boss drop chance per week. In current version of StarRail, this value is 100. FIXED_DEFAULT = 100 # Times left to farm. Resets to 100 every Monday 04:00, and decreases each time the elite boss is cleared. - FarmingCounter = FIXED_DEFAULT + value = FIXED_DEFAULT def farm_dec(self, value = 1): - self.FarmingCounter -= value + self.value -= value - def farm_reset(self,): - self.FarmingCounter = self.FIXED_DEFAULT + def farm_reset(self): + self.value = self.FIXED_DEFAULT def farm_not_complete(self) -> bool: - return self.FarmingCounter > 0 + return self.value > 0 def farm_get_remain(self) -> int: - return self.FarmingCounter + return self.value class StoredAssignment(StoredCounter): diff --git a/module/config/stored/stored_generated.py b/module/config/stored/stored_generated.py index bc8f2c526..5a8708305 100644 --- a/module/config/stored/stored_generated.py +++ b/module/config/stored/stored_generated.py @@ -20,6 +20,7 @@ from module.config.stored.classes import ( StoredImmersifier, StoredInt, StoredSimulatedUniverse, + StoredSimulatedUniverseElite, StoredTrailblazePower, ) @@ -50,3 +51,4 @@ class StoredGenerated: Assignment = StoredAssignment("Assignment.Assignment.Assignment") Credit = StoredInt("DataUpdate.ItemStorage.Credit") StallerJade = StoredInt("DataUpdate.ItemStorage.StallerJade") + SimulatedUniverseElite = StoredSimulatedUniverseElite("Rogue.RogueWorld.SimulatedUniverseElite") diff --git a/tasks/map/control/control.py b/tasks/map/control/control.py index e9da793a5..865e0aa64 100644 --- a/tasks/map/control/control.py +++ b/tasks/map/control/control.py @@ -386,7 +386,7 @@ class MapControl(Combat, AimDetectorMixin): end_point = waypoints[-1] end_point.expected_end.append('item') - self.goto(*waypoints) + return self.goto(*waypoints) def clear_enemy(self, *waypoints): """ @@ -403,7 +403,7 @@ class MapControl(Combat, AimDetectorMixin): end_point = waypoints[-1] end_point.expected_end.append('enemy') - self.goto(*waypoints) + return self.goto(*waypoints) if __name__ == '__main__': diff --git a/tasks/rogue/entry/entry.py b/tasks/rogue/entry/entry.py index f81764df6..80820ad07 100644 --- a/tasks/rogue/entry/entry.py +++ b/tasks/rogue/entry/entry.py @@ -348,16 +348,20 @@ class RogueEntry(RouteBase, RogueRewardHandler, RoguePathHandler, DungeonUI): return if self.config.stored.SimulatedUniverse.is_expired(): - # Expired, do rogue and reset farming counter - self.config.stored.SimulatedUniverse.farm_reset() + # Expired, do rogue + pass + + if self.config.stored.SimulatedUniverseElite.is_expired(): + # Expired, reset farming counter + self.config.stored.SimulatedUniverseElite.farm_reset() elif self.config.stored.SimulatedUniverse.is_full(): if self.config.RogueWorld_UseImmersifier and self.config.stored.Immersifier.value > 0: logger.info( 'Reached weekly point limit but still have immersifiers left, continue to use them') - elif self.config.RogueWorld_WeeklyFarming and self.config.stored.SimulatedUniverse.farm_not_complete(): + elif self.config.RogueWorld_WeeklyFarming and self.config.stored.SimulatedUniverseElite.farm_not_complete(): logger.attr( - "Farming Counter", self.config.stored.SimulatedUniverse.farm_get_remain()) + "Farming Counter", self.config.stored.SimulatedUniverseElite.farm_get_remain()) logger.info( 'Reached weekly point limit but still continue to farm materials') else: diff --git a/tasks/rogue/route/base.py b/tasks/rogue/route/base.py index 7807aeda5..1007f06d2 100644 --- a/tasks/rogue/route/base.py +++ b/tasks/rogue/route/base.py @@ -170,13 +170,14 @@ class RouteBase(RouteBase_, RogueExit, RogueEvent, RogueReward): pass result = super().clear_enemy(*waypoints) + # logger.attr("result",result) - # runs when one elite battle finishes (do not handle whether the battle succeeded or failed), and decreases rogue farming count by 1 - if self.config.RogueWorld_WeeklyFarming and self.config.stored.SimulatedUniverse.farm_not_complete(): - self.config.stored.SimulatedUniverse.farm_dec() - logger.info( - f"Cleared elite boss, decrease farming count by 1, now {self.config.stored.SimulatedUniverse.FarmingCounter}") - + if 'enemy' in result: + # runs when one elite battle finishes (do not handle whether the battle succeeded or failed), and decreases rogue farming count by 1 + if self.config.RogueWorld_WeeklyFarming and self.config.stored.SimulatedUniverseElite.farm_not_complete(): + self.config.stored.SimulatedUniverseElite.farm_dec() + logger.info( + f"Cleared elite boss, decrease farming count by 1, now {self.config.stored.SimulatedUniverseElite.farm_get_remain()}") return result def _domain_event_expected_end(self): From 721ae382917b1a78d85d16e3c8cb1fd71152aa3f Mon Sep 17 00:00:00 2001 From: katze Date: Mon, 1 Apr 2024 10:33:06 +0800 Subject: [PATCH 009/114] improves readability --- module/config/stored/classes.py | 6 ++++-- tasks/rogue/entry/entry.py | 4 ++-- tasks/rogue/route/base.py | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/module/config/stored/classes.py b/module/config/stored/classes.py index f5ac21193..4ff830f0b 100644 --- a/module/config/stored/classes.py +++ b/module/config/stored/classes.py @@ -217,8 +217,10 @@ class StoredSimulatedUniverseElite(StoredCounter, StoredExpiredAtMonday0400): # Times left to farm. Resets to 100 every Monday 04:00, and decreases each time the elite boss is cleared. value = FIXED_DEFAULT - def farm_dec(self, value = 1): - self.value -= value + def farm_dec(self, delta = 1): + self.value -= delta + if self.value < 0: + self.value = 0 def farm_reset(self): self.value = self.FIXED_DEFAULT diff --git a/tasks/rogue/entry/entry.py b/tasks/rogue/entry/entry.py index 80820ad07..4304cf2a8 100644 --- a/tasks/rogue/entry/entry.py +++ b/tasks/rogue/entry/entry.py @@ -360,10 +360,10 @@ class RogueEntry(RouteBase, RogueRewardHandler, RoguePathHandler, DungeonUI): logger.info( 'Reached weekly point limit but still have immersifiers left, continue to use them') elif self.config.RogueWorld_WeeklyFarming and self.config.stored.SimulatedUniverseElite.farm_not_complete(): - logger.attr( - "Farming Counter", self.config.stored.SimulatedUniverseElite.farm_get_remain()) logger.info( 'Reached weekly point limit but still continue to farm materials') + logger.attr( + "Farming Counter", self.config.stored.SimulatedUniverseElite.farm_get_remain()) else: raise RogueReachedWeeklyPointLimit else: diff --git a/tasks/rogue/route/base.py b/tasks/rogue/route/base.py index 1007f06d2..2126852a3 100644 --- a/tasks/rogue/route/base.py +++ b/tasks/rogue/route/base.py @@ -173,11 +173,11 @@ class RouteBase(RouteBase_, RogueExit, RogueEvent, RogueReward): # logger.attr("result",result) if 'enemy' in result: - # runs when one elite battle finishes (do not handle whether the battle succeeded or failed), and decreases rogue farming count by 1 + # runs when one elite battle finishes, and decreases rogue farming count by 1 if self.config.RogueWorld_WeeklyFarming and self.config.stored.SimulatedUniverseElite.farm_not_complete(): self.config.stored.SimulatedUniverseElite.farm_dec() logger.info( - f"Cleared elite boss, decrease farming count by 1, now {self.config.stored.SimulatedUniverseElite.farm_get_remain()}") + f"Cleared elite boss, decreasing farming count by 1, now {self.config.stored.SimulatedUniverseElite.farm_get_remain()}") return result def _domain_event_expected_end(self): From 4988fb1f2f79db449e726c3a847ad1b67645db55 Mon Sep 17 00:00:00 2001 From: katze Date: Tue, 2 Apr 2024 12:23:28 +0800 Subject: [PATCH 010/114] optimize expiration judging --- tasks/rogue/entry/entry.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tasks/rogue/entry/entry.py b/tasks/rogue/entry/entry.py index 4304cf2a8..c889f23b1 100644 --- a/tasks/rogue/entry/entry.py +++ b/tasks/rogue/entry/entry.py @@ -346,15 +346,14 @@ class RogueEntry(RouteBase, RogueRewardHandler, RoguePathHandler, DungeonUI): if self.config.RogueDebug_DebugMode: # Always run return - - if self.config.stored.SimulatedUniverse.is_expired(): - # Expired, do rogue - pass if self.config.stored.SimulatedUniverseElite.is_expired(): # Expired, reset farming counter self.config.stored.SimulatedUniverseElite.farm_reset() - + + if self.config.stored.SimulatedUniverse.is_expired(): + # Expired, do rogue + pass elif self.config.stored.SimulatedUniverse.is_full(): if self.config.RogueWorld_UseImmersifier and self.config.stored.Immersifier.value > 0: logger.info( From 9b4daafcf1b5ad539c342e98bda08d9b362279e0 Mon Sep 17 00:00:00 2001 From: Zebartin <16185081+Zebartin@users.noreply.github.com> Date: Thu, 11 Apr 2024 22:24:49 +0800 Subject: [PATCH 011/114] Fix: More tolerant insight_row --- module/ui/draggable_list.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/module/ui/draggable_list.py b/module/ui/draggable_list.py index 7f8f46c92..b35616eae 100644 --- a/module/ui/draggable_list.py +++ b/module/ui/draggable_list.py @@ -160,6 +160,7 @@ class DraggableList: logger.info(f'Insight row: {row}, index={row_index}') last_buttons: set[OcrResultButton] = None + bottom_check = Timer(3, count=5).start() while 1: if skip_first_screenshot: skip_first_screenshot = False @@ -183,8 +184,11 @@ class DraggableList: 0, count=0), timeout=Timer(1.5, count=5)) skip_first_screenshot = True if self.cur_buttons and last_buttons == set(self.cur_buttons): - logger.warning(f'No more rows in {self}') - return False + if bottom_check.reached(): + logger.warning(f'No more rows in {self}') + return False + else: + bottom_check.reset() last_buttons = set(self.cur_buttons) return True From 01116336f8762cf58d4adc58c4947e5036d7f643 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Sun, 14 Apr 2024 18:36:24 +0800 Subject: [PATCH 012/114] Chore: Background worker --- module/base/base.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/module/base/base.py b/module/base/base.py index 9a1dc41cb..f7687d214 100644 --- a/module/base/base.py +++ b/module/base/base.py @@ -1,5 +1,3 @@ -from concurrent.futures import ThreadPoolExecutor - import module.config.server as server_ from module.base.button import Button, ButtonWrapper, ClickButton, match_template from module.base.timer import Timer @@ -50,11 +48,22 @@ class ModuleBase: self.interval_timer = {} @cached_class_property - def worker(self) -> ThreadPoolExecutor: + def worker(self): """ A thread pool to run things at background + + Examples: + ``` + def func(image): + logger.info('Update thread start') + with self.config.multi_set(): + self.dungeon_get_simuni_point(image) + self.dungeon_update_stamina(image) + ModuleBase.worker.submit(func, self.device.image) + ``` """ logger.hr('Creating worker') + from concurrent.futures import ThreadPoolExecutor pool = ThreadPoolExecutor(1) return pool From 96aa273f9b3fc836a9ef13feef81b8cc24e9bb55 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Sun, 14 Apr 2024 18:41:30 +0800 Subject: [PATCH 013/114] Sync: [ALAS] Emulator manager --- module/device/platform/emulator_base.py | 23 +++++++ module/device/platform/emulator_windows.py | 80 ++++++++++++++++------ module/device/platform/platform_base.py | 72 +++++++++++++------ 3 files changed, 133 insertions(+), 42 deletions(-) diff --git a/module/device/platform/emulator_base.py b/module/device/platform/emulator_base.py index ecd026ba1..94483e9f9 100644 --- a/module/device/platform/emulator_base.py +++ b/module/device/platform/emulator_base.py @@ -36,6 +36,21 @@ def get_serial_pair(serial): return None, None +def remove_duplicated_path(paths): + """ + Args: + paths (list[str]): + + Returns: + list[str]: + """ + paths = sorted(set(paths)) + dic = {} + for path in paths: + dic.setdefault(path.lower(), path) + return list(dic.values()) + + @dataclass class EmulatorInstanceBase: # Serial for adb connection @@ -205,6 +220,14 @@ class EmulatorBase: class EmulatorManagerBase: + @staticmethod + def iter_running_emulator(): + """ + Yields: + str: Path to emulator executables, may contains duplicate values + """ + return + @cached_property def all_emulators(self) -> t.List[EmulatorBase]: """ diff --git a/module/device/platform/emulator_windows.py b/module/device/platform/emulator_windows.py index 142ad3e30..f7a5e54bc 100644 --- a/module/device/platform/emulator_windows.py +++ b/module/device/platform/emulator_windows.py @@ -8,7 +8,8 @@ from dataclasses import dataclass # module/device/platform/emulator_base.py # module/device/platform/emulator_windows.py # Will be used in Alas Easy Install, they shouldn't import any Alas modules. -from module.device.platform.emulator_base import EmulatorBase, EmulatorInstanceBase, EmulatorManagerBase +from module.device.platform.emulator_base import EmulatorBase, EmulatorInstanceBase, EmulatorManagerBase, \ + remove_duplicated_path from module.device.platform.utils import cached_property, iter_folder @@ -70,7 +71,7 @@ class Emulator(EmulatorBase): def path_to_type(cls, path: str) -> str: """ Args: - path: Path to .exe file + path: Path to .exe file, case insensitive Returns: str: Emulator type, such as Emulator.NoxPlayer @@ -78,46 +79,49 @@ class Emulator(EmulatorBase): folder, exe = os.path.split(path) folder, dir1 = os.path.split(folder) folder, dir2 = os.path.split(folder) - if exe == 'Nox.exe': - if dir2 == 'Nox': + exe = exe.lower() + dir1 = dir1.lower() + dir2 = dir2.lower() + if exe == 'nox.exe': + if dir2 == 'nox': return cls.NoxPlayer - elif dir2 == 'Nox64': + elif dir2 == 'nox64': return cls.NoxPlayer64 else: return cls.NoxPlayer - if exe == 'Bluestacks.exe': - if dir1 in ['BlueStacks', 'BlueStacks_cn']: + if exe == 'bluestacks.exe': + if dir1 in ['bluestacks', 'bluestacks_cn']: return cls.BlueStacks4 - elif dir1 in ['BlueStacks_nxt', 'BlueStacks_nxt_cn']: + elif dir1 in ['bluestacks_nxt', 'bluestacks_nxt_cn']: return cls.BlueStacks5 else: return cls.BlueStacks4 - if exe == 'HD-Player.exe': - if dir1 in ['BlueStacks', 'BlueStacks_cn']: + if exe == 'hd-player.exe': + if dir1 in ['bluestacks', 'bluestacks_cn']: return cls.BlueStacks4 - elif dir1 in ['BlueStacks_nxt', 'BlueStacks_nxt_cn']: + elif dir1 in ['bluestacks_nxt', 'bluestacks_nxt_cn']: return cls.BlueStacks5 else: return cls.BlueStacks5 if exe == 'dnplayer.exe': - if dir1 == 'LDPlayer': + if dir1 == 'ldplayer': return cls.LDPlayer3 - elif dir1 == 'LDPlayer4': + elif dir1 == 'ldplayer4': return cls.LDPlayer4 - elif dir1 == 'LDPlayer9': + elif dir1 == 'ldplayer9': return cls.LDPlayer9 else: return cls.LDPlayer3 - if exe == 'NemuPlayer.exe': + if exe == 'nemuplayer.exe': if dir2 == 'nemu': return cls.MuMuPlayer elif dir2 == 'nemu9': return cls.MuMuPlayerX else: return cls.MuMuPlayer - if exe == 'MuMuPlayer.exe': + if exe == 'mumuplayer.exe': return cls.MuMuPlayer12 - if exe == 'MEmu.exe': + if exe == 'memu.exe': return cls.MEmuPlayer return '' @@ -143,7 +147,9 @@ class Emulator(EmulatorBase): elif 'NemuMultiPlayer.exe' in exe: yield exe.replace('NemuMultiPlayer.exe', 'NemuPlayer.exe') elif 'MuMuMultiPlayer.exe' in exe: - yield exe.replace('MuMuMultiPlayer.exe', 'MuMuManager.exe') + yield exe.replace('MuMuMultiPlayer.exe', 'MuMuPlayer.exe') + elif 'MuMuManager.exe' in exe: + yield exe.replace('MuMuManager.exe', 'MuMuPlayer.exe') elif 'MEmuConsole.exe' in exe: yield exe.replace('MEmuConsole.exe', 'MEmu.exe') else: @@ -316,7 +322,7 @@ class EmulatorManager(EmulatorManagerBase): Get recently executed programs in UserAssist https://github.com/forensicmatt/MonitorUserAssist - Returns: + Yields: str: Path to emulator executables, may contains duplicate values """ path = r'Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist' @@ -447,6 +453,31 @@ class EmulatorManager(EmulatorManagerBase): uninstall = res.group(1) if res else uninstall yield uninstall + @staticmethod + def iter_running_emulator(): + """ + Yields: + str: Path to emulator executables, may contains duplicate values + """ + try: + import psutil + except ModuleNotFoundError: + return + # Since this is a one-time-usage, we access psutil._psplatform.Process directly + # to bypass the call of psutil.Process.is_running(). + # This only costs about 0.017s. + for pid in psutil.pids(): + proc = psutil._psplatform.Process(pid) + try: + exe = proc.cmdline() + exe = exe[0].replace(r'\\', '/').replace('\\', '/') + except (psutil.AccessDenied, IndexError): + # psutil.AccessDenied + continue + + if Emulator.is_emulator(exe): + yield exe + @cached_property def all_emulators(self) -> t.List[Emulator]: """ @@ -474,7 +505,7 @@ class EmulatorManager(EmulatorManagerBase): exe.add(ld) # Uninstall registry - for uninstall in self.iter_uninstall_registry(): + for uninstall in EmulatorManager.iter_uninstall_registry(): # Find emulator executable from uninstaller for file in iter_folder(abspath(os.path.dirname(uninstall)), ext='.exe'): if Emulator.is_emulator(file) and os.path.exists(file): @@ -488,9 +519,14 @@ class EmulatorManager(EmulatorManagerBase): if Emulator.is_emulator(file) and os.path.exists(file): exe.add(file) + # Running + for file in EmulatorManager.iter_running_emulator(): + if os.path.exists(file): + exe.add(file) + + # De-redundancy exe = [Emulator(path).path for path in exe if Emulator.is_emulator(path)] - exe = sorted(set(exe)) - exe = [Emulator(path) for path in exe] + exe = [Emulator(path) for path in remove_duplicated_path(exe)] return exe @cached_property diff --git a/module/device/platform/platform_base.py b/module/device/platform/platform_base.py index e48da238c..02aba6698 100644 --- a/module/device/platform/platform_base.py +++ b/module/device/platform/platform_base.py @@ -6,7 +6,8 @@ from pydantic import BaseModel from module.base.decorator import cached_property, del_cached_property from module.base.utils import SelectedGrids from module.device.connection import Connection -from module.device.platform.emulator_base import EmulatorInstanceBase, EmulatorManagerBase +from module.device.method.utils import get_serial_pair +from module.device.platform.emulator_base import EmulatorInstanceBase, EmulatorManagerBase, remove_duplicated_path from module.logger import logger @@ -47,8 +48,20 @@ class PlatformBase(Connection, EmulatorManagerBase): @cached_property def emulator_info(self) -> EmulatorInfo: emulator = self.config.EmulatorInfo_Emulator - name = str(self.config.EmulatorInfo_name).strip().replace('\n', '') - path = str(self.config.EmulatorInfo_path).strip().replace('\n', '') + if emulator == 'auto': + emulator = '' + + def parse_info(value): + if isinstance(value, str): + value = value.strip().replace('\n', '') + if value in ['None', 'False', 'True']: + value = '' + return value + else: + return '' + + name = parse_info(self.config.EmulatorInfo_name) + path = parse_info(self.config.EmulatorInfo_path) return EmulatorInfo( emulator=emulator, @@ -68,8 +81,14 @@ class PlatformBase(Connection, EmulatorManagerBase): path=data.path, name=data.name, ) + # Redirect emulator-5554 to 127.0.0.1:5555 + serial = self.serial + port_serial, _ = get_serial_pair(self.serial) + if port_serial is not None: + serial = port_serial + instance = self.find_emulator_instance( - serial=str(self.config.Emulator_Serial).strip(), + serial=serial, name=data.name, path=data.path, emulator=data.emulator, @@ -117,7 +136,7 @@ class PlatformBase(Connection, EmulatorManagerBase): # Search by serial select = instances.select(**search_args) if select.count == 0: - logger.warning(f'No emulator instance with {search_args}') + logger.warning(f'No emulator instance with {search_args}, serial invalid') return None if select.count == 1: instance = select[0] @@ -130,9 +149,9 @@ class PlatformBase(Connection, EmulatorManagerBase): search_args['name'] = name select = instances.select(**search_args) if select.count == 0: - logger.warning(f'No emulator instances with {search_args}') - return None - if select.count == 1: + logger.warning(f'No emulator instances with {search_args}, name invalid') + search_args.pop('name') + elif select.count == 1: instance = select[0] logger.hr('Emulator instance', level=2) logger.info(f'Found emulator instance: {instance}') @@ -143,9 +162,9 @@ class PlatformBase(Connection, EmulatorManagerBase): search_args['path'] = path select = instances.select(**search_args) if select.count == 0: - logger.warning(f'No emulator instances with {search_args}') - return None - if select.count == 1: + logger.warning(f'No emulator instances with {search_args}, path invalid') + search_args.pop('path') + elif select.count == 1: instance = select[0] logger.hr('Emulator instance', level=2) logger.info(f'Found emulator instance: {instance}') @@ -156,9 +175,28 @@ class PlatformBase(Connection, EmulatorManagerBase): search_args['type'] = emulator select = instances.select(**search_args) if select.count == 0: - logger.warning(f'No emulator instances with {search_args}') - return None - if select.count == 1: + logger.warning(f'No emulator instances with {search_args}, type invalid') + search_args.pop('type') + elif select.count == 1: + instance = select[0] + logger.hr('Emulator instance', level=2) + logger.info(f'Found emulator instance: {instance}') + return instance + + # Still too many instances, search from running emulators + running = remove_duplicated_path(list(self.iter_running_emulator())) + logger.info('Running emulators') + for exe in running: + logger.info(exe) + if len(running) == 1: + logger.info('Only one running emulator') + # Same as searching path + search_args['path'] = running[0] + select = instances.select(**search_args) + if select.count == 0: + logger.warning(f'No emulator instances with {search_args}, path invalid') + search_args.pop('path') + elif select.count == 1: instance = select[0] logger.hr('Emulator instance', level=2) logger.info(f'Found emulator instance: {instance}') @@ -167,9 +205,3 @@ class PlatformBase(Connection, EmulatorManagerBase): # Still too many instances logger.warning(f'Found multiple emulator instances with {search_args}') return None - - -if __name__ == '__main__': - self = PlatformBase('alas') - d = self.emulator_instance - print(d) From 03ed78f905df2c6c9b685e2dff6a803364be5076 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Sun, 14 Apr 2024 19:05:14 +0800 Subject: [PATCH 014/114] Sync: [ALAS] Add nemu_ipc --- module/config/argument/args.json | 3 +- module/config/argument/argument.yaml | 13 +- module/config/config_generated.py | 2 +- module/config/i18n/en-US.json | 3 +- module/config/i18n/es-ES.json | 3 +- module/config/i18n/ja-JP.json | 3 +- module/config/i18n/zh-CN.json | 3 +- module/config/i18n/zh-TW.json | 3 +- module/device/connection.py | 141 +++++-- module/device/connection_attr.py | 22 +- module/device/control.py | 18 +- module/device/device.py | 24 +- module/device/method/adb.py | 2 +- module/device/method/droidcast.py | 57 ++- module/device/method/nemu_ipc.py | 541 ++++++++++++++++++++++++++ module/device/method/uiautomator_2.py | 9 +- module/device/screenshot.py | 11 +- 17 files changed, 802 insertions(+), 56 deletions(-) create mode 100644 module/device/method/nemu_ipc.py diff --git a/module/config/argument/args.json b/module/config/argument/args.json index 75c4cba04..8135a9226 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -48,7 +48,8 @@ "aScreenCap_nc", "DroidCast", "DroidCast_raw", - "scrcpy" + "scrcpy", + "nemu_ipc" ], "display": "hide" }, diff --git a/module/config/argument/argument.yaml b/module/config/argument/argument.yaml index 2f976f2ef..9f40d9a9e 100644 --- a/module/config/argument/argument.yaml +++ b/module/config/argument/argument.yaml @@ -29,7 +29,18 @@ Emulator: option: [ auto, cn, en ] ScreenshotMethod: value: auto - option: [ auto, ADB, ADB_nc, uiautomator2, aScreenCap, aScreenCap_nc, DroidCast, DroidCast_raw, scrcpy ] + option: [ + auto, + ADB, + ADB_nc, + uiautomator2, + aScreenCap, + aScreenCap_nc, + DroidCast, + DroidCast_raw, + scrcpy, + nemu_ipc, + ] ControlMethod: value: MaaTouch option: [ minitouch, MaaTouch ] diff --git a/module/config/config_generated.py b/module/config/config_generated.py index bd79039f3..6771444a4 100644 --- a/module/config/config_generated.py +++ b/module/config/config_generated.py @@ -20,7 +20,7 @@ class GeneratedConfig: Emulator_GameClient = 'android' # android, cloud_android Emulator_PackageName = 'auto' # auto, CN-Official, CN-Bilibili, OVERSEA-America, OVERSEA-Asia, OVERSEA-Europe, OVERSEA-TWHKMO Emulator_GameLanguage = 'auto' # auto, cn, en - Emulator_ScreenshotMethod = 'auto' # auto, ADB, ADB_nc, uiautomator2, aScreenCap, aScreenCap_nc, DroidCast, DroidCast_raw, scrcpy + Emulator_ScreenshotMethod = 'auto' # auto, ADB, ADB_nc, uiautomator2, aScreenCap, aScreenCap_nc, DroidCast, DroidCast_raw, scrcpy, nemu_ipc Emulator_ControlMethod = 'MaaTouch' # minitouch, MaaTouch Emulator_AdbRestart = False diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index 70e7ebb8e..730146df8 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -131,7 +131,8 @@ "aScreenCap_nc": "aScreenCap_nc", "DroidCast": "DroidCast", "DroidCast_raw": "DroidCast_raw", - "scrcpy": "scrcpy" + "scrcpy": "scrcpy", + "nemu_ipc": "nemu_ipc" }, "ControlMethod": { "name": "Control Method", diff --git a/module/config/i18n/es-ES.json b/module/config/i18n/es-ES.json index 5357226e1..93da78a16 100644 --- a/module/config/i18n/es-ES.json +++ b/module/config/i18n/es-ES.json @@ -131,7 +131,8 @@ "aScreenCap_nc": "aScreenCap_nc", "DroidCast": "DroidCast", "DroidCast_raw": "DroidCast_raw", - "scrcpy": "scrcpy" + "scrcpy": "scrcpy", + "nemu_ipc": "nemu_ipc" }, "ControlMethod": { "name": "Método de control", diff --git a/module/config/i18n/ja-JP.json b/module/config/i18n/ja-JP.json index 0a40b8d2f..d7efc1275 100644 --- a/module/config/i18n/ja-JP.json +++ b/module/config/i18n/ja-JP.json @@ -131,7 +131,8 @@ "aScreenCap_nc": "aScreenCap_nc", "DroidCast": "DroidCast", "DroidCast_raw": "DroidCast_raw", - "scrcpy": "scrcpy" + "scrcpy": "scrcpy", + "nemu_ipc": "nemu_ipc" }, "ControlMethod": { "name": "Emulator.ControlMethod.name", diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index a6e7e78c4..4819a409f 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -131,7 +131,8 @@ "aScreenCap_nc": "aScreenCap_nc", "DroidCast": "DroidCast", "DroidCast_raw": "DroidCast_raw", - "scrcpy": "scrcpy" + "scrcpy": "scrcpy", + "nemu_ipc": "nemu_ipc" }, "ControlMethod": { "name": "模拟器控制方案", diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index a3f21c18e..c679ece1e 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -131,7 +131,8 @@ "aScreenCap_nc": "aScreenCap_nc", "DroidCast": "DroidCast", "DroidCast_raw": "DroidCast_raw", - "scrcpy": "scrcpy" + "scrcpy": "scrcpy", + "nemu_ipc": "nemu_ipc" }, "ControlMethod": { "name": "模擬器控制方案", diff --git a/module/device/connection.py b/module/device/connection.py index 2bc838e70..0a5f8aa2f 100644 --- a/module/device/connection.py +++ b/module/device/connection.py @@ -1,9 +1,9 @@ import ipaddress import logging -import platform import re import socket import subprocess +import sys import time from functools import wraps @@ -12,7 +12,8 @@ from adbutils import AdbClient, AdbDevice, AdbTimeout, ForwardItem, ReverseItem from adbutils.errors import AdbError import module.config.server as server_ -from module.base.decorator import Config, cached_property, del_cached_property +import platform +from module.base.decorator import Config, cached_property, del_cached_property, run_once from module.base.utils import SelectedGrids, ensure_time from module.device.connection_attr import ConnectionAttr from module.device.method.utils import ( @@ -84,10 +85,17 @@ class AdbDeviceWithStatus(AdbDevice): def __bool__(self): return True + @cached_property + def port(self) -> int: + try: + return int(self.serial.split(':')[1]) + except (IndexError, ValueError): + return 0 + @cached_property def may_mumu12_family(self): # 127.0.0.1:16XXX - return len(self.serial) == 15 and self.serial.startswith('127.0.0.1:16') + return 16384 <= self.port <= 17408 class Connection(ConnectionAttr): @@ -276,6 +284,7 @@ class Connection(ConnectionAttr): @cached_property def nemud_app_keep_alive(self) -> str: res = self.adb_getprop('nemud.app_keep_alive') + logger.attr('nemud.app_keep_alive', res) return res @retry @@ -284,7 +293,6 @@ class Connection(ConnectionAttr): return False res = self.nemud_app_keep_alive - logger.attr('nemud.app_keep_alive', res) if res == '': # Empty property, probably MuMu6 or MuMu12 version < 3.5.6 return True @@ -299,6 +307,15 @@ class Connection(ConnectionAttr): logger.warning(f'Invalid nemud.app_keep_alive value: {res}') return False + @cached_property + def is_mumu_over_version_356(self) -> bool: + """ + Returns: + bool: If MuMu12 version >= 3.5.6, + which has nemud.app_keep_alive and always be a vertical device + """ + return self.nemud_app_keep_alive != '' + @cached_property def _nc_server_host_port(self): """ @@ -549,14 +566,14 @@ class Connection(ConnectionAttr): # Disconnect offline device before connecting for device in self.list_device(): if device.status == 'offline': - logger.warning(f'Device {serial} is offline, disconnect it before connecting') - self.adb_disconnect(serial) + logger.warning(f'Device {device.serial} is offline, disconnect it before connecting') + self.adb_disconnect(device.serial) elif device.status == 'unauthorized': - logger.error(f'Device {serial} is unauthorized, please accept ADB debugging on your device') + logger.error(f'Device {device.serial} is unauthorized, please accept ADB debugging on your device') elif device.status == 'device': pass else: - logger.warning(f'Device {serial} is is having a unknown status: {device.status}') + logger.warning(f'Device {device.serial} is is having a unknown status: {device.status}') # Skip for emulator-5554 if 'emulator-' in serial: @@ -764,23 +781,45 @@ class Connection(ConnectionAttr): If serial=='auto' and only 1 device detected, use it """ logger.hr('Detect device') - logger.info('Here are the available devices, ' - 'copy to Alas.Emulator.Serial to use it or set Alas.Emulator.Serial="auto"') - devices = self.list_device() + available = SelectedGrids([]) + devices = SelectedGrids([]) - # Show available devices - available = devices.select(status='device') - for device in available: - logger.info(device.serial) - if not len(available): - logger.info('No available devices') + @run_once + def brute_force_connect(): + logger.info('Brute force connect') + from deploy.Windows.emulator import EmulatorManager + manager = EmulatorManager() + manager.brute_force_connect() - # Show unavailable devices if having any - unavailable = devices.delete(available) - if len(unavailable): - logger.info('Here are the devices detected but unavailable') - for device in unavailable: - logger.info(f'{device.serial} ({device.status})') + for _ in range(2): + logger.info('Here are the available devices, ' + 'copy to Alas.Emulator.Serial to use it or set Alas.Emulator.Serial="auto"') + devices = self.list_device() + + # Show available devices + available = devices.select(status='device') + for device in available: + logger.info(device.serial) + if not len(available): + logger.info('No available devices') + + # Show unavailable devices if having any + unavailable = devices.delete(available) + if len(unavailable): + logger.info('Here are the devices detected but unavailable') + for device in unavailable: + logger.info(f'{device.serial} ({device.status})') + + # brute_force_connect + if self.config.Emulator_Serial == 'auto' and available.count == 0: + logger.warning(f'No available device found') + if sys.platform == 'win32': + brute_force_connect() + continue + else: + break + else: + break # Auto device detection if self.config.Emulator_Serial == 'auto': @@ -790,7 +829,7 @@ class Connection(ConnectionAttr): raise RequestHumanTakeover elif available.count == 1: logger.info(f'Auto device detection found only one device, using it') - self.serial = available[0].serial + self.config.Emulator_Serial = self.serial = available[0].serial del_cached_property(self, 'adb') elif available.count == 2 \ and available.select(serial='127.0.0.1:7555') \ @@ -799,7 +838,7 @@ class Connection(ConnectionAttr): # For MuMu12 serials like 127.0.0.1:7555 and 127.0.0.1:16384 # ignore 7555 use 16384 remain = available.select(may_mumu12_family=True).first_or_none() - self.serial = remain.serial + self.config.Emulator_Serial = self.serial = remain.serial del_cached_property(self, 'adb') else: logger.critical('Multiple devices found, auto device detection cannot decide which to choose, ' @@ -808,6 +847,7 @@ class Connection(ConnectionAttr): # Handle LDPlayer # LDPlayer serial jumps between `127.0.0.1:5555+{X}` and `emulator-5554+{X}` + # No config write since it's dynamic port_serial, emu_serial = get_serial_pair(self.serial) if port_serial and emu_serial: # Might be LDPlayer, check connected devices @@ -834,6 +874,57 @@ class Connection(ConnectionAttr): f'Using serial: {emu_serial}') self.serial = emu_serial + # Redirect MuMu12 from 127.0.0.1:7555 to 127.0.0.1:16xxx + if self.serial == '127.0.0.1:7555': + for _ in range(2): + mumu12 = available.select(may_mumu12_family=True) + if mumu12.count == 1: + emu_serial = mumu12.first_or_none().serial + logger.warning(f'Redirect MuMu12 {self.serial} to {emu_serial}') + self.config.Emulator_Serial = self.serial = emu_serial + break + elif mumu12.count >= 2: + logger.warning(f'Multiple MuMu12 serial found, cannot redirect') + break + else: + # Only 127.0.0.1:7555 + if self.is_mumu_over_version_356: + # is_mumu_over_version_356 and nemud_app_keep_alive was cached + # Acceptable since it's the same device + logger.warning(f'Device {self.serial} is MuMu12 but corresponding port not found') + brute_force_connect() + devices = self.list_device() + # Show available devices + available = devices.select(status='device') + for device in available: + logger.info(device.serial) + if not len(available): + logger.info('No available devices') + continue + else: + # MuMu6 + break + + # MuMu12 uses 127.0.0.1:16385 if port 16384 is occupied, auto redirect + # No config write since it's dynamic + if self.is_mumu12_family: + matched = False + for device in available.select(may_mumu12_family=True): + if device.port == self.port: + # Exact match + matched = True + break + if not matched: + for device in available.select(may_mumu12_family=True): + if -2 <= device.port - self.port <= 2: + # Port switched + logger.info(f'MuMu12 port switches from {self.serial} to {device.serial}') + del_cached_property(self, 'port') + del_cached_property(self, 'is_mumu12_family') + del_cached_property(self, 'is_mumu_family') + self.serial = device.serial + break + @retry def list_package(self, show_log=True): """ diff --git a/module/device/connection_attr.py b/module/device/connection_attr.py index 622020960..97c914dbe 100644 --- a/module/device/connection_attr.py +++ b/module/device/connection_attr.py @@ -7,7 +7,6 @@ from adbutils import AdbClient, AdbDevice from module.base.decorator import cached_property from module.config.config import AzurLaneConfig -from module.config.utils import deep_iter from module.exception import RequestHumanTakeover from module.logger import logger @@ -49,7 +48,6 @@ class ConnectionAttr: self.serial_check() self.config.DEVICE_OVER_HTTP = self.is_over_http - @staticmethod def revise_serial(serial): serial = serial.replace(' ', '') @@ -123,6 +121,18 @@ class ConnectionAttr: def is_wsa(self): return bool(re.match(r'^wsa', self.serial)) + @cached_property + def port(self) -> int: + try: + return int(self.serial.split(':')[1]) + except (IndexError, ValueError): + return 0 + + @cached_property + def is_mumu12_family(self): + # 127.0.0.1:16XXX + return 16384 <= self.port <= 17408 + @cached_property def is_mumu_family(self): # 127.0.0.1:7555 @@ -130,9 +140,8 @@ class ConnectionAttr: return self.serial == '127.0.0.1:7555' or self.is_mumu12_family @cached_property - def is_mumu12_family(self): - # 127.0.0.1:16384 + 32*n - return len(self.serial) == 15 and self.serial.startswith('127.0.0.1:16') + def is_nox_family(self): + return 62001 <= self.port <= 63025 @cached_property def is_emulator(self): @@ -178,7 +187,8 @@ class ConnectionAttr: rf"SOFTWARE\BlueStacks_bgp64_hyperv\Guests\{folder_name}\Config") as key: port = QueryValueEx(key, "BstAdbPort")[0] except FileNotFoundError: - logger.error(rf'Unable to find registry HKEY_LOCAL_MACHINE\SOFTWARE\BlueStacks_bgp64_hyperv\Guests\{folder_name}\Config') + logger.error( + rf'Unable to find registry HKEY_LOCAL_MACHINE\SOFTWARE\BlueStacks_bgp64_hyperv\Guests\{folder_name}\Config') logger.error('Please confirm that your are using BlueStack 4 hyper-v and not regular BlueStacks 4') logger.error(r'Please check if there is any other emulator instances under ' r'registry HKEY_LOCAL_MACHINE\SOFTWARE\BlueStacks_bgp64_hyperv\Guests') diff --git a/module/device/control.py b/module/device/control.py index cd9eb1865..b8f61260e 100644 --- a/module/device/control.py +++ b/module/device/control.py @@ -5,11 +5,12 @@ from module.base.utils import * from module.device.method.hermit import Hermit from module.device.method.maatouch import MaaTouch from module.device.method.minitouch import Minitouch +from module.device.method.nemu_ipc import NemuIpc from module.device.method.scrcpy import Scrcpy from module.logger import logger -class Control(Hermit, Minitouch, Scrcpy, MaaTouch): +class Control(Hermit, Minitouch, Scrcpy, MaaTouch, NemuIpc): def handle_control_check(self, button): # Will be overridden in Device pass @@ -22,6 +23,7 @@ class Control(Hermit, Minitouch, Scrcpy, MaaTouch): 'minitouch': self.click_minitouch, 'Hermit': self.click_hermit, 'MaaTouch': self.click_maatouch, + 'nemu_ipc': self.click_nemu_ipc, } def click(self, button, control_check=True): @@ -78,6 +80,8 @@ class Control(Hermit, Minitouch, Scrcpy, MaaTouch): self.long_click_scrcpy(x, y, duration) elif method == 'MaaTouch': self.long_click_maatouch(x, y, duration) + elif method == 'nemu_ipc': + self.long_click_nemu_ipc(x, y, duration) else: self.swipe_adb((x, y), (x, y), duration) @@ -86,13 +90,9 @@ class Control(Hermit, Minitouch, Scrcpy, MaaTouch): p1, p2 = ensure_int(p1, p2) duration = ensure_time(duration) method = self.config.Emulator_ControlMethod - if method == 'minitouch': - logger.info('Swipe %s -> %s' % (point2str(*p1), point2str(*p2))) - elif method == 'uiautomator2': + if method == 'uiautomator2': logger.info('Swipe %s -> %s, %s' % (point2str(*p1), point2str(*p2), duration)) - elif method == 'scrcpy': - logger.info('Swipe %s -> %s' % (point2str(*p1), point2str(*p2))) - elif method == 'MaaTouch': + elif method in ['minitouch', 'MaaTouch', 'scrcpy', 'nemu_ipc']: logger.info('Swipe %s -> %s' % (point2str(*p1), point2str(*p2))) else: # ADB needs to be slow, or swipe doesn't work @@ -114,6 +114,8 @@ class Control(Hermit, Minitouch, Scrcpy, MaaTouch): self.swipe_scrcpy(p1, p2) elif method == 'MaaTouch': self.swipe_maatouch(p1, p2) + elif method == 'nemu_ipc': + self.swipe_nemu_ipc(p1, p2) else: self.swipe_adb(p1, p2, duration=duration) @@ -163,6 +165,8 @@ class Control(Hermit, Minitouch, Scrcpy, MaaTouch): self.drag_scrcpy(p1, p2, point_random=point_random) elif method == 'MaaTouch': self.drag_maatouch(p1, p2, point_random=point_random) + elif method == 'nemu_ipc': + self.drag_nemu_ipc(p1, p2, point_random=point_random) else: logger.warning(f'Control method {method} does not support drag well, ' f'falling back to ADB swipe may cause unexpected behaviour') diff --git a/module/device/device.py b/module/device/device.py index cbc45be7c..9f40da974 100644 --- a/module/device/device.py +++ b/module/device/device.py @@ -4,7 +4,6 @@ import itertools from module.base.timer import Timer from module.device.app_control import AppControl from module.device.control import Control -from module.device.platform import Platform from module.device.screenshot import Screenshot from module.exception import ( EmulatorNotRunningError, @@ -56,7 +55,7 @@ def show_function_call(): logger.info('Function calls:' + ''.join(func_list)) -class Device(Screenshot, Control, AppControl, Platform): +class Device(Screenshot, Control, AppControl): _screen_size_checked = False detect_record = set() click_record = collections.deque(maxlen=30) @@ -83,6 +82,7 @@ class Device(Screenshot, Control, AppControl, Platform): _ = self.emulator_instance self.screenshot_interval_set() + self.method_check() # Auto-select the fastest screenshot method if not self.config.is_template_config and self.config.Emulator_ScreenshotMethod == 'auto': @@ -101,7 +101,23 @@ class Device(Screenshot, Control, AppControl, Platform): bench = Benchmark(config=self.config, device=self) method = bench.run_simple_screenshot_benchmark() # Set - self.config.Emulator_ScreenshotMethod = method + with self.config.multi_set(): + self.config.Emulator_ScreenshotMethod = method + # if method == 'nemu_ipc': + # self.config.Emulator_ControlMethod = 'nemu_ipc' + + def method_check(self): + """ + Check combinations of screenshot method and control methods + """ + # nemu_ipc should be together + # if self.config.Emulator_ScreenshotMethod == 'nemu_ipc' and self.config.Emulator_ControlMethod != 'nemu_ipc': + # logger.warning('When using nemu_ipc, both screenshot and control should use nemu_ipc') + # self.config.Emulator_ControlMethod = 'nemu_ipc' + # if self.config.Emulator_ScreenshotMethod != 'nemu_ipc' and self.config.Emulator_ControlMethod == 'nemu_ipc': + # logger.warning('When not using nemu_ipc, both screenshot and control should not use nemu_ipc') + # self.config.Emulator_ControlMethod = 'minitouch' + pass def screenshot(self): """ @@ -127,6 +143,8 @@ class Device(Screenshot, Control, AppControl, Platform): # stop it during wait if self.config.Emulator_ScreenshotMethod == 'scrcpy': self._scrcpy_server_stop() + if self.config.Emulator_ScreenshotMethod == 'nemu_ipc': + self.nemu_ipc_release() def stuck_record_add(self, button): self.detect_record.add(str(button)) diff --git a/module/device/method/adb.py b/module/device/method/adb.py index 48324fd5d..bd3397edf 100644 --- a/module/device/method/adb.py +++ b/module/device/method/adb.py @@ -128,7 +128,7 @@ class Adb(Connection): if image is None: raise ImageTruncated('Empty image after cv2.imdecode') - image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB, dst=image) + cv2.cvtColor(image, cv2.COLOR_BGR2RGB, dst=image) if image is None: raise ImageTruncated('Empty image after cv2.cvtColor') diff --git a/module/device/method/droidcast.py b/module/device/method/droidcast.py index 1a5e71f62..3c7a97762 100644 --- a/module/device/method/droidcast.py +++ b/module/device/method/droidcast.py @@ -95,6 +95,8 @@ class DroidCast(Uiautomator2): """ _droidcast_port: int = 0 + droidcast_width: int = 0 + droidcast_height: int = 0 @cached_property def droidcast_session(self): @@ -112,15 +114,37 @@ class DroidCast(Uiautomator2): - /preview To get PNG screenshots. """ + def droidcast_url(self, url='/preview'): + if self.is_mumu_over_version_356: + w, h = self.droidcast_width, self.droidcast_height + if self.orientation == 0: + return f'http://127.0.0.1:{self._droidcast_port}{url}?width={w}&height={h}' + elif self.orientation == 1: + return f'http://127.0.0.1:{self._droidcast_port}{url}?width={h}&height={w}' + else: + # logger.warning('DroidCast receives invalid device orientation') + pass + return f'http://127.0.0.1:{self._droidcast_port}{url}' def droidcast_raw_url(self, url='/screenshot'): + if self.is_mumu_over_version_356: + w, h = self.droidcast_width, self.droidcast_height + if self.orientation == 0: + return f'http://127.0.0.1:{self._droidcast_port}{url}?width={w}&height={h}' + elif self.orientation == 1: + return f'http://127.0.0.1:{self._droidcast_port}{url}?width={h}&height={w}' + else: + # logger.warning('DroidCast receives invalid device orientation') + pass + return f'http://127.0.0.1:{self._droidcast_port}{url}' def droidcast_init(self): logger.hr('DroidCast init') self.droidcast_stop() + self._droidcast_update_resolution() logger.info('Pushing DroidCast apk') self.adb_push(self.config.DROIDCAST_FILEPATH_LOCAL, self.config.DROIDCAST_FILEPATH_REMOTE) @@ -150,10 +174,25 @@ class DroidCast(Uiautomator2): else: logger.error(f'Unknown DROIDCAST_VERSION: {self.config.DROIDCAST_VERSION}') + def _droidcast_update_resolution(self): + if self.is_mumu_over_version_356: + logger.info('Update droidcast resolution') + w, h = self.resolution_uiautomator2(cal_rotation=False) + self.get_orientation() + # 720, 1280 + # mumu12 > 3.5.6 is always a vertical device + self.droidcast_width, self.droidcast_height = w, h + logger.info(f'Droicast resolution: {(w, h)}') + @retry def screenshot_droidcast(self): self.config.DROIDCAST_VERSION = 'DroidCast' + if self.is_mumu_over_version_356: + if not self.droidcast_width or not self.droidcast_height: + self._droidcast_update_resolution() + resp = self.droidcast_session.get(self.droidcast_url(), timeout=3) + if resp.status_code == 404: raise DroidCastVersionIncompatible('DroidCast server does not have /preview') image = resp.content @@ -173,16 +212,27 @@ class DroidCast(Uiautomator2): if image is None: raise ImageTruncated('Empty image after cv2.cvtColor') + if self.is_mumu_over_version_356: + if self.orientation == 1: + image = cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE) + return image @retry def screenshot_droidcast_raw(self): self.config.DROIDCAST_VERSION = 'DroidCast_raw' + shape = (720, 1280) + if self.is_mumu_over_version_356: + if not self.droidcast_width or not self.droidcast_height: + self._droidcast_update_resolution() + if self.droidcast_height and self.droidcast_width: + shape = (self.droidcast_height, self.droidcast_width) + image = self.droidcast_session.get(self.droidcast_raw_url(), timeout=3).content # DroidCast_raw returns a RGB565 bitmap try: - arr = np.frombuffer(image, dtype=np.uint16).reshape((720, 1280)) + arr = np.frombuffer(image, dtype=np.uint16).reshape(shape) except ValueError as e: if len(image) < 500: logger.warning(f'Unexpected screenshot: {image}') @@ -230,6 +280,11 @@ class DroidCast(Uiautomator2): cv2.add(b, m, dst=b) image = cv2.merge([r, g, b]) + + if self.is_mumu_over_version_356: + if self.orientation == 1: + image = cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE) + return image def droidcast_wait_startup(self): diff --git a/module/device/method/nemu_ipc.py b/module/device/method/nemu_ipc.py new file mode 100644 index 000000000..b79e1c322 --- /dev/null +++ b/module/device/method/nemu_ipc.py @@ -0,0 +1,541 @@ +import asyncio +import ctypes +import os +import sys +from functools import partial, wraps + +import cv2 +import numpy as np + +from module.base.decorator import cached_property, del_cached_property, has_cached_property +from module.base.utils import ensure_time +from module.device.method.minitouch import insert_swipe, random_rectangle_point +from module.device.method.utils import RETRY_TRIES, retry_sleep +from module.device.platform import Platform +from module.exception import RequestHumanTakeover +from module.logger import logger + + +class NemuIpcIncompatible(Exception): + pass + + +class NemuIpcError(Exception): + pass + + +class CaptureStd: + """ + Capture stdout and stderr from both python and C library + https://stackoverflow.com/questions/5081657/how-do-i-prevent-a-c-shared-library-to-print-on-stdout-in-python/17954769 + + ``` + with CaptureStd() as capture: + # String wasn't printed + print('whatever') + # But captured in ``capture.stdout`` + print(f'Got stdout: "{capture.stdout}"') + print(f'Got stderr: "{capture.stderr}"') + ``` + """ + + def __init__(self): + self.stdout = b'' + self.stderr = b'' + + def _redirect_stdout(self, to): + sys.stdout.close() + os.dup2(to, self.fdout) + sys.stdout = os.fdopen(self.fdout, 'w') + + def _redirect_stderr(self, to): + sys.stderr.close() + os.dup2(to, self.fderr) + sys.stderr = os.fdopen(self.fderr, 'w') + + def __enter__(self): + self.fdout = sys.stdout.fileno() + self.fderr = sys.stderr.fileno() + self.reader_out, self.writer_out = os.pipe() + self.reader_err, self.writer_err = os.pipe() + self.old_stdout = os.dup(self.fdout) + self.old_stderr = os.dup(self.fderr) + + file_out = os.fdopen(self.writer_out, 'w') + file_err = os.fdopen(self.writer_err, 'w') + self._redirect_stdout(to=file_out.fileno()) + self._redirect_stderr(to=file_err.fileno()) + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self._redirect_stdout(to=self.old_stdout) + self._redirect_stderr(to=self.old_stderr) + os.close(self.old_stdout) + os.close(self.old_stderr) + + self.stdout = self.recvall(self.reader_out) + self.stderr = self.recvall(self.reader_err) + os.close(self.reader_out) + os.close(self.reader_err) + + @staticmethod + def recvall(reader, length=1024) -> bytes: + fragments = [] + while 1: + chunk = os.read(reader, length) + if chunk: + fragments.append(chunk) + else: + break + output = b''.join(fragments) + return output + + +class CaptureNemuIpc(CaptureStd): + instance = None + + def is_capturing(self): + """ + Only capture at the topmost wrapper to avoid nested capturing + If a capture is ongoing, this instance does nothing + """ + cls = self.__class__ + return isinstance(cls.instance, cls) and cls.instance != self + + def __enter__(self): + if self.is_capturing(): + return self + + super().__enter__() + CaptureNemuIpc.instance = self + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + if self.is_capturing(): + return + + CaptureNemuIpc.instance = None + super().__exit__(exc_type, exc_val, exc_tb) + + self.check_stdout() + self.check_stderr() + + def check_stdout(self): + if not self.stdout: + return + logger.info(f'NemuIpc stdout: {self.stdout}') + + def check_stderr(self): + if not self.stderr: + return + logger.error(f'NemuIpc stderr: {self.stderr}') + + # Calling an old MuMu12 player + # Tested on 3.4.0 + # b'nemu_capture_display rpc error: 1783\r\n' + # Tested on 3.7.3 + # b'nemu_capture_display rpc error: 1745\r\n' + if b'error: 1783' in self.stderr or b'error: 1745' in self.stderr: + raise NemuIpcIncompatible( + f'NemuIpc requires MuMu12 version >= 3.8.13, please check your version') + # contact_id incorrect + # b'nemu_capture_display cannot find rpc connection\r\n' + if b'cannot find rpc connection' in self.stderr: + raise NemuIpcError(self.stderr) + # Emulator died + # b'nemu_capture_display rpc error: 1722\r\n' + # MuMuVMMSVC.exe died + # b'nemu_capture_display rpc error: 1726\r\n' + # No idea how to handle yet + if b'error: 1722' in self.stderr or b'error: 1726' in self.stderr: + raise NemuIpcError('Emulator instance is probably dead') + + +def retry(func): + @wraps(func) + def retry_wrapper(self, *args, **kwargs): + """ + Args: + self (NemuIpcImpl): + """ + init = None + for _ in range(RETRY_TRIES): + try: + if callable(init): + retry_sleep(_) + init() + return func(self, *args, **kwargs) + # Can't handle + except RequestHumanTakeover: + break + # Can't handle + except NemuIpcIncompatible as e: + logger.error(e) + break + # Function call timeout + except asyncio.TimeoutError: + logger.warning(f'Func {func.__name__}() call timeout, retrying: {_}') + + def init(): + self.reconnect() + # NemuIpcError + except NemuIpcError as e: + logger.error(e) + + def init(): + self.reconnect() + # Unknown, probably a trucked image + except Exception as e: + logger.exception(e) + + def init(): + pass + + logger.critical(f'Retry {func.__name__}() failed') + raise RequestHumanTakeover + + return retry_wrapper + + +class NemuIpcImpl: + def __init__(self, nemu_folder: str, instance_id: int, display_id: int = 0): + """ + Args: + nemu_folder: Installation path of MuMu12, e.g. E:/ProgramFiles/MuMuPlayer-12.0 + instance_id: Emulator instance ID, starting from 0 + display_id: Always 0 if keep app alive was disabled + """ + self.nemu_folder: str = nemu_folder + self.instance_id: int = instance_id + self.display_id: int = display_id + + ipc_dll = os.path.abspath(os.path.join(nemu_folder, './shell/sdk/external_renderer_ipc.dll')) + logger.info( + f'NemuIpcImpl init, ' + f'nemu_folder={nemu_folder}, ' + f'ipc_dll={ipc_dll}, ' + f'instance_id={instance_id}, ' + f'display_id={display_id}' + ) + + try: + self.lib = ctypes.CDLL(ipc_dll) + except OSError as e: + logger.error(e) + # OSError: [WinError 126] 找不到指定的模块。 + if not os.path.exists(ipc_dll): + raise NemuIpcIncompatible( + f'ipc_dll={ipc_dll} does not exist, ' + f'NemuIpc requires MuMu12 version >= 3.8.13, please check your version') + else: + raise NemuIpcIncompatible( + f'ipc_dll={ipc_dll} exists, but cannot be loaded') + self.connect_id: int = 0 + self.width = 0 + self.height = 0 + + def connect(self): + if self.connect_id > 0: + return + + connect_id = self.ev_run_sync( + self.lib.nemu_connect, + self.nemu_folder, self.instance_id + ) + if connect_id == 0: + raise NemuIpcError( + 'Connection failed, please check if nemu_folder is correct and emulator is running' + ) + + self.connect_id = connect_id + # logger.info(f'NemuIpc connected: {self.connect_id}') + + def disconnect(self): + if self.connect_id == 0: + return + + self.ev_run_sync( + self.lib.nemu_disconnect, + self.connect_id + ) + + # logger.info(f'NemuIpc disconnected: {self.connect_id}') + self.connect_id = 0 + + def reconnect(self): + self.disconnect() + self.connect() + + def __enter__(self): + self.connect() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.disconnect() + + @cached_property + def _ev(self): + return asyncio.new_event_loop() + + async def ev_run_async(self, func, *args, **kwargs): + """ + Args: + func: Sync function to call + *args: + **kwargs: + + Raises: + asyncio.TimeoutError: If function call timeout + """ + func_wrapped = partial(func, *args, **kwargs) + # Increased timeout for slow PCs + # Default screenshot interval is 0.2s, so a 0.15s timeout would have a fast retry without extra time costs + result = await asyncio.wait_for(self._ev.run_in_executor(None, func_wrapped), timeout=0.15) + return result + + def ev_run_sync(self, func, *args, **kwargs): + """ + Args: + func: Sync function to call + *args: + **kwargs: + + Raises: + asyncio.TimeoutError: If function call timeout + NemuIpcIncompatible: + NemuIpcError + """ + result = self._ev.run_until_complete(self.ev_run_async(func, *args, **kwargs)) + + err = False + if func.__name__ == 'nemu_connect': + if result == 0: + err = True + else: + if result > 0: + err = True + # Get to actual error message printed in std + if err: + logger.warning(f'Failed to call {func.__name__}, result={result}') + with CaptureNemuIpc(): + result = self._ev.run_until_complete(self.ev_run_async(func, *args, **kwargs)) + + return result + + def get_resolution(self): + """ + Get emulator resolution, `self.width` and `self.height` will be set + """ + if self.connect_id == 0: + self.connect() + + width_ptr = ctypes.pointer(ctypes.c_int(0)) + height_ptr = ctypes.pointer(ctypes.c_int(0)) + nullptr = ctypes.POINTER(ctypes.c_int)() + + ret = self.ev_run_sync( + self.lib.nemu_capture_display, + self.connect_id, self.display_id, 0, width_ptr, height_ptr, nullptr + ) + if ret > 0: + raise NemuIpcError('nemu_capture_display failed during get_resolution()') + self.width = width_ptr.contents.value + self.height = height_ptr.contents.value + + @retry + def screenshot(self): + """ + Returns: + np.ndarray: Image array in RGBA color space + Note that image is upside down + """ + if self.connect_id == 0: + self.connect() + + self.get_resolution() + + width_ptr = ctypes.pointer(ctypes.c_int(self.width)) + height_ptr = ctypes.pointer(ctypes.c_int(self.height)) + length = self.width * self.height * 4 + pixels_pointer = ctypes.pointer((ctypes.c_ubyte * length)()) + + ret = self.ev_run_sync( + self.lib.nemu_capture_display, + self.connect_id, self.display_id, length, width_ptr, height_ptr, pixels_pointer + ) + if ret > 0: + raise NemuIpcError('nemu_capture_display failed during screenshot()') + + # image = np.ctypeslib.as_array(pixels_pointer, shape=(self.height, self.width, 4)) + image = np.ctypeslib.as_array(pixels_pointer.contents).reshape((self.height, self.width, 4)) + return image + + def convert_xy(self, x, y): + """ + Convert classic ADB coordinates to Nemu's + `self.height` must be updated before calling this method + + Returns: + int, int + """ + x, y = int(x), int(y) + x, y = self.height - y, x + return x, y + + @retry + def down(self, x, y): + """ + Contact down, continuous contact down will be considered as swipe + """ + if self.connect_id == 0: + self.connect() + if self.height == 0: + self.get_resolution() + + x, y = self.convert_xy(x, y) + + ret = self.ev_run_sync( + self.lib.nemu_input_event_touch_down, + self.connect_id, self.display_id, x, y + ) + if ret > 0: + raise NemuIpcError('nemu_input_event_touch_down failed') + + @retry + def up(self): + """ + Contact up + """ + if self.connect_id == 0: + self.connect() + + ret = self.ev_run_sync( + self.lib.nemu_input_event_touch_up, + self.connect_id, self.display_id + ) + if ret > 0: + raise NemuIpcError('nemu_input_event_touch_up failed') + + +def serial_to_id(serial: str): + """ + Predict instance ID from serial + E.g. + "127.0.0.1:16384" -> 0 + "127.0.0.1:16416" -> 1 + + Returns: + int: instance_id, or None if failed to predict + """ + try: + port = int(serial.split(':')[1]) + except (IndexError, ValueError): + return None + index, offset = divmod(port - 16384, 32) + if 0 <= index < 32 and offset in [0, 1, 2]: + return index + else: + return None + + +class NemuIpc(Platform): + @cached_property + def nemu_ipc(self) -> NemuIpcImpl: + """ + Initialize a nemu ipc implementation + """ + # Try existing settings first + if self.config.EmulatorInfo_path: + folder = os.path.abspath(os.path.join(self.config.EmulatorInfo_path, '../../')) + index = serial_to_id(self.serial) + if index is not None: + try: + return NemuIpcImpl( + nemu_folder=folder, + instance_id=index, + display_id=0 + ).__enter__() + except (NemuIpcIncompatible, NemuIpcError) as e: + logger.error(e) + logger.error('Emulator info incorrect') + + # Search emulator instance + # with E:\ProgramFiles\MuMuPlayer-12.0\shell\MuMuPlayer.exe + # installation path is E:\ProgramFiles\MuMuPlayer-12.0 + if self.emulator_instance is None: + logger.error('Unable to use NemuIpc because emulator instance not found') + raise RequestHumanTakeover + try: + return NemuIpcImpl( + nemu_folder=self.emulator_instance.emulator.abspath('../'), + instance_id=self.emulator_instance.MuMuPlayer12_id, + display_id=0 + ).__enter__() + except (NemuIpcIncompatible, NemuIpcError) as e: + logger.error(e) + logger.error('Unable to initialize NemuIpc') + raise RequestHumanTakeover + + def nemu_ipc_available(self) -> bool: + if not self.is_mumu_family: + return False + if self.nemud_app_keep_alive == '': + return False + try: + _ = self.nemu_ipc + except RequestHumanTakeover: + return False + return True + + def nemu_ipc_release(self): + if has_cached_property(self, 'nemu_ipc'): + self.nemu_ipc.disconnect() + del_cached_property(self, 'nemu_ipc') + logger.info('nemu_ipc released') + + def screenshot_nemu_ipc(self): + image = self.nemu_ipc.screenshot() + + image = cv2.cvtColor(image, cv2.COLOR_BGRA2BGR) + cv2.flip(image, 0, dst=image) + return image + + def click_nemu_ipc(self, x, y): + down = ensure_time((0.010, 0.020)) + self.nemu_ipc.down(x, y) + self.sleep(down) + self.nemu_ipc.up() + self.sleep(0.050 - down) + + def long_click_nemu_ipc(self, x, y, duration=1.0): + self.nemu_ipc.down(x, y) + self.sleep(duration) + self.nemu_ipc.up() + self.sleep(0.050) + + def swipe_nemu_ipc(self, p1, p2): + points = insert_swipe(p0=p1, p3=p2) + + for point in points: + self.nemu_ipc.down(*point) + self.sleep(0.010) + + self.nemu_ipc.up() + self.sleep(0.050) + + def drag_nemu_ipc(self, p1, p2, point_random=(-10, -10, 10, 10)): + p1 = np.array(p1) - random_rectangle_point(point_random) + p2 = np.array(p2) - random_rectangle_point(point_random) + points = insert_swipe(p0=p1, p3=p2, speed=20) + + for point in points: + self.nemu_ipc.down(*point) + self.sleep(0.010) + + self.nemu_ipc.down(*p2) + self.sleep(0.140) + self.nemu_ipc.down(*p2) + self.sleep(0.140) + + self.nemu_ipc.up() + self.sleep(0.050) diff --git a/module/device/method/uiautomator_2.py b/module/device/method/uiautomator_2.py index 69520f0a2..765f7dde6 100644 --- a/module/device/method/uiautomator_2.py +++ b/module/device/method/uiautomator_2.py @@ -243,7 +243,7 @@ class Uiautomator2(Connection): return hierarchy @retry - def resolution_uiautomator2(self) -> t.Tuple[int, int]: + def resolution_uiautomator2(self, cal_rotation=True) -> t.Tuple[int, int]: """ Faster u2.window_size(), cause that calls `dumpsys display` twice. @@ -252,9 +252,10 @@ class Uiautomator2(Connection): """ info = self.u2.http.get('/info').json() w, h = info['display']['width'], info['display']['height'] - rotation = self.get_orientation() - if (w > h) != (rotation % 2 == 1): - w, h = h, w + if cal_rotation: + rotation = self.get_orientation() + if (w > h) != (rotation % 2 == 1): + w, h = h, w return w, h def resolution_check_uiautomator2(self): diff --git a/module/device/screenshot.py b/module/device/screenshot.py index 9101a1d3f..2dcd6fe88 100644 --- a/module/device/screenshot.py +++ b/module/device/screenshot.py @@ -13,13 +13,14 @@ from module.base.utils import get_color, image_size, limit_in, save_image from module.device.method.adb import Adb from module.device.method.ascreencap import AScreenCap from module.device.method.droidcast import DroidCast +from module.device.method.nemu_ipc import NemuIpc from module.device.method.scrcpy import Scrcpy from module.device.method.wsa import WSA from module.exception import RequestHumanTakeover, ScriptError from module.logger import logger -class Screenshot(Adb, WSA, DroidCast, AScreenCap, Scrcpy): +class Screenshot(Adb, WSA, DroidCast, AScreenCap, Scrcpy, NemuIpc): _screen_size_checked = False _screen_black_checked = False _minicap_uninstalled = False @@ -38,6 +39,7 @@ class Screenshot(Adb, WSA, DroidCast, AScreenCap, Scrcpy): 'DroidCast': self.screenshot_droidcast, 'DroidCast_raw': self.screenshot_droidcast_raw, 'scrcpy': self.screenshot_scrcpy, + 'nemu_ipc': self.screenshot_nemu_ipc, } def screenshot(self): @@ -70,6 +72,10 @@ class Screenshot(Adb, WSA, DroidCast, AScreenCap, Scrcpy): return self.image + @property + def has_cached_image(self): + return hasattr(self, 'image') and self.image is not None + def _handle_orientated_image(self, image): """ Args: @@ -159,6 +165,9 @@ class Screenshot(Adb, WSA, DroidCast, AScreenCap, Scrcpy): if interval != origin: logger.warning(f'Optimization.ScreenshotInterval {origin} is revised to {interval}') self.config.Optimization_ScreenshotInterval = interval + # Allow nemu_ipc to have a lower default + if self.config.Emulator_ScreenshotMethod == 'nemu_ipc': + interval = limit_in(origin, 0.1, 0.2) elif interval == 'combat': origin = self.config.Optimization_CombatScreenshotInterval interval = limit_in(origin, 0.3, 1.0) From d26534ab14b27b456c081a3f169137356c3d6898 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Sun, 14 Apr 2024 19:11:53 +0800 Subject: [PATCH 015/114] Add: Use nemu_ipc if available --- module/device/device.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/module/device/device.py b/module/device/device.py index 9f40da974..948fefe03 100644 --- a/module/device/device.py +++ b/module/device/device.py @@ -88,6 +88,12 @@ class Device(Screenshot, Control, AppControl): if not self.config.is_template_config and self.config.Emulator_ScreenshotMethod == 'auto': self.run_simple_screenshot_benchmark() + # SRC only, use nemu_ipc if available + available = self.nemu_ipc_available() + logger.attr('nemu_ipc_available', available) + if available: + self.config.override(Emulator_ScreenshotMethod='nemu_ipc') + def run_simple_screenshot_benchmark(self): """ Perform a screenshot method benchmark, test 3 times on each method. From a12763b94b2fec747f50f466d4f09690d6ebbb1e Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Sun, 14 Apr 2024 23:04:00 +0800 Subject: [PATCH 016/114] Fix: Stuck in the corner of Jarilo_CorridorofFadingEchoes_F1_X369Y439 --- .../Jarilo_CorridorofFadingEchoes_F1.py | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/route/rogue/Combat/Jarilo_CorridorofFadingEchoes_F1.py b/route/rogue/Combat/Jarilo_CorridorofFadingEchoes_F1.py index 562f5c057..c2d83ca9d 100644 --- a/route/rogue/Combat/Jarilo_CorridorofFadingEchoes_F1.py +++ b/route/rogue/Combat/Jarilo_CorridorofFadingEchoes_F1.py @@ -1,3 +1,4 @@ +from module.logger import logger from tasks.map.control.waypoint import Waypoint from tasks.map.keywords.plane import Jarilo_CorridorofFadingEchoes from tasks.rogue.route.base import RouteBase @@ -167,12 +168,23 @@ class Route(RouteBase): enemy2right.straight_run(), enemy2left.straight_run().set_threshold(5), ) - self.clear_enemy( - enemy2left.set_threshold(5), - node3.straight_run(), - node4.set_threshold(3).straight_run(), - enemy4.straight_run(), - ) + if self.minimap.is_position_near(enemy2right, threshold=30): + logger.info('Near enemy2right') + self.clear_enemy( + enemy2left.set_threshold(5), + node3.straight_run(), + node4.set_threshold(3).straight_run(), + enemy4.straight_run(), + ) + else: + logger.info('Not near enemy2right') + self.clear_enemy( + enemy2right.set_threshold(5), + enemy2left.set_threshold(5), + node3.straight_run(), + node4.set_threshold(3).straight_run(), + enemy4.straight_run(), + ) def Jarilo_CorridorofFadingEchoes_F1_X437Y122(self): """ From 1d57777b32cef479593d99d5f52481d26a189ab0 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Sun, 14 Apr 2024 23:05:35 +0800 Subject: [PATCH 017/114] Fix: Check rogue entry button available --- route/rogue/Combat/Jarilo_CorridorofFadingEchoes_F1.py | 2 +- tasks/rogue/entry/entry.py | 2 +- tasks/rogue/entry/path.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/route/rogue/Combat/Jarilo_CorridorofFadingEchoes_F1.py b/route/rogue/Combat/Jarilo_CorridorofFadingEchoes_F1.py index c2d83ca9d..004217fb9 100644 --- a/route/rogue/Combat/Jarilo_CorridorofFadingEchoes_F1.py +++ b/route/rogue/Combat/Jarilo_CorridorofFadingEchoes_F1.py @@ -168,7 +168,7 @@ class Route(RouteBase): enemy2right.straight_run(), enemy2left.straight_run().set_threshold(5), ) - if self.minimap.is_position_near(enemy2right, threshold=30): + if self.minimap.is_position_near(enemy2left.position, threshold=30): logger.info('Near enemy2right') self.clear_enemy( enemy2left.set_threshold(5), diff --git a/tasks/rogue/entry/entry.py b/tasks/rogue/entry/entry.py index c889f23b1..58dc40a66 100644 --- a/tasks/rogue/entry/entry.py +++ b/tasks/rogue/entry/entry.py @@ -249,7 +249,7 @@ class RogueEntry(RouteBase, RogueRewardHandler, RoguePathHandler, DungeonUI): self.device.click(WORLD_ENTER) self.interval_reset(REWARD_ENTER, interval=2) continue - if self.appear(LEVEL_CONFIRM, interval=2): + if self.match_template_color(LEVEL_CONFIRM, interval=2): self.dungeon_update_stamina() self.check_stop_condition() self.device.click(LEVEL_CONFIRM) diff --git a/tasks/rogue/entry/path.py b/tasks/rogue/entry/path.py index 8ab17b50b..2d3af237c 100644 --- a/tasks/rogue/entry/path.py +++ b/tasks/rogue/entry/path.py @@ -205,7 +205,7 @@ class RoguePathHandler(RogueUI): logger.info('rogue_path_select ended at page_main') break - if self.appear(ROGUE_LAUNCH, interval=2): + if self.match_template_color(ROGUE_LAUNCH, interval=2): if not self._is_team_prepared(): raise RogueTeamNotPrepared self.device.click(ROGUE_LAUNCH) From aa700b81db820782361e0f0ed6d375a37510195b Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 15 Apr 2024 00:38:20 +0800 Subject: [PATCH 018/114] I18n: WeeklyFarming description --- config/template.json | 2 +- module/config/argument/args.json | 4 ++-- module/config/argument/argument.yaml | 2 +- module/config/config_generated.py | 2 +- module/config/i18n/en-US.json | 8 ++++---- module/config/i18n/es-ES.json | 8 ++++---- module/config/i18n/ja-JP.json | 8 ++++---- module/config/i18n/zh-CN.json | 8 ++++---- module/config/i18n/zh-TW.json | 8 ++++---- module/config/stored/classes.py | 14 +++++++------- 10 files changed, 32 insertions(+), 32 deletions(-) diff --git a/config/template.json b/config/template.json index 308482694..a177646a8 100644 --- a/config/template.json +++ b/config/template.json @@ -199,8 +199,8 @@ "DomainStrategy": "combat", "UseImmersifier": true, "DoubleEvent": true, - "UseStamina": false, "WeeklyFarming": false, + "UseStamina": false, "SimulatedUniverseElite": {} }, "RogueBlessing": { diff --git a/module/config/argument/args.json b/module/config/argument/args.json index ed8122978..61de75e65 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -1467,11 +1467,11 @@ "type": "checkbox", "value": true }, - "UseStamina": { + "WeeklyFarming": { "type": "checkbox", "value": false }, - "WeeklyFarming": { + "UseStamina": { "type": "checkbox", "value": false }, diff --git a/module/config/argument/argument.yaml b/module/config/argument/argument.yaml index 51003fb26..206d023c0 100644 --- a/module/config/argument/argument.yaml +++ b/module/config/argument/argument.yaml @@ -257,8 +257,8 @@ RogueWorld: option: [ combat, occurrence ] UseImmersifier: true DoubleEvent: true - UseStamina: false WeeklyFarming: false + UseStamina: false SimulatedUniverseElite: stored: StoredSimulatedUniverseElite diff --git a/module/config/config_generated.py b/module/config/config_generated.py index af35a4cfa..31363dddb 100644 --- a/module/config/config_generated.py +++ b/module/config/config_generated.py @@ -138,8 +138,8 @@ class GeneratedConfig: RogueWorld_DomainStrategy = 'combat' # combat, occurrence RogueWorld_UseImmersifier = True RogueWorld_DoubleEvent = True - RogueWorld_UseStamina = False RogueWorld_WeeklyFarming = False + RogueWorld_UseStamina = False RogueWorld_SimulatedUniverseElite = {} # Group `RogueBlessing` diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index 66c4073a9..a062ac5ff 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -970,14 +970,14 @@ "name": "Participate in Double Planer Event", "help": "" }, + "WeeklyFarming": { + "name": "Farm 100 Elites Weekly", + "help": "" + }, "UseStamina": { "name": "Farm Planers Using Trailblase Power", "help": "Task \"Dungeon\" will no longer run, and all trailblaze power will be used first to claim immersion rewards, except for double events." }, - "WeeklyFarming": { - "name": "Boss Material Farming", - "help": "Do not stop after point reward is full; farm the boss 100 times instead to obtain trace materials" - }, "SimulatedUniverseElite": { "name": "RogueWorld.SimulatedUniverseElite.name", "help": "RogueWorld.SimulatedUniverseElite.help" diff --git a/module/config/i18n/es-ES.json b/module/config/i18n/es-ES.json index 18e32fe4b..6130a4cce 100644 --- a/module/config/i18n/es-ES.json +++ b/module/config/i18n/es-ES.json @@ -970,14 +970,14 @@ "name": "Participa en doble planer evento", "help": "" }, + "WeeklyFarming": { + "name": "Granja 100 élites semanalmente", + "help": "" + }, "UseStamina": { "name": "Reclamar de planers mediante poder trazacaminos", "help": "La tarea de mazmorra ya no se ejecutará y todo el poder trazacaminos se usará primero para reclamar recompensas de inmersión, excepto para eventos dobles" }, - "WeeklyFarming": { - "name": "Recolección de Materiales de Jefes", - "help": "No detenerse después de que los puntos semanales estén llenos, farmear al jefe 100 veces para obtener materiales de rastro" - }, "SimulatedUniverseElite": { "name": "RogueWorld.SimulatedUniverseElite.name", "help": "RogueWorld.SimulatedUniverseElite.help" diff --git a/module/config/i18n/ja-JP.json b/module/config/i18n/ja-JP.json index af3efe861..b2ff361a2 100644 --- a/module/config/i18n/ja-JP.json +++ b/module/config/i18n/ja-JP.json @@ -970,14 +970,14 @@ "name": "RogueWorld.DoubleEvent.name", "help": "RogueWorld.DoubleEvent.help" }, + "WeeklyFarming": { + "name": "RogueWorld.WeeklyFarming.name", + "help": "RogueWorld.WeeklyFarming.help" + }, "UseStamina": { "name": "RogueWorld.UseStamina.name", "help": "RogueWorld.UseStamina.help" }, - "WeeklyFarming": { - "name": "ボス素材を収集", - "help": "週間ポイントが満点に達しても停止せず、100回のボスをファームして軌跡素材を入手" - }, "SimulatedUniverseElite": { "name": "RogueWorld.SimulatedUniverseElite.name", "help": "RogueWorld.SimulatedUniverseElite.help" diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index 0ad1e991c..cf3a86232 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -970,14 +970,14 @@ "name": "参与双倍内圈仪器活动", "help": "" }, + "WeeklyFarming": { + "name": "每周刷100精英怪", + "help": "" + }, "UseStamina": { "name": "使用开拓力刷内圈遗器", "help": "每日副本任务将不再打本,所有开拓力将优先被用于领取浸器奖励,双倍活动时除外" }, - "WeeklyFarming": { - "name": "刷取Boss材料", - "help": "每周积分满后不停止,刷取100次Boss以获取形迹材料" - }, "SimulatedUniverseElite": { "name": "剩余Boss材料掉落次数", "help": "RogueWorld.SimulatedUniverseElite.help" diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index 2535a576b..a9afefdfb 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -970,14 +970,14 @@ "name": "參與雙倍內圈儀器活動", "help": "" }, + "WeeklyFarming": { + "name": "每週農100精英怪", + "help": "" + }, "UseStamina": { "name": "用開拓力農遺器", "help": "每日副本任務將不再打本,所有開拓力將優先被用於領取浸器獎勵,雙倍活動時除外" }, - "WeeklyFarming": { - "name": "刷取Boss材料", - "help": "每週積分滿後不停止,刷取100次Boss以獲取形跡材料" - }, "SimulatedUniverseElite": { "name": "RogueWorld.SimulatedUniverseElite.name", "help": "RogueWorld.SimulatedUniverseElite.help" diff --git a/module/config/stored/classes.py b/module/config/stored/classes.py index 4ff830f0b..83c6ca9ef 100644 --- a/module/config/stored/classes.py +++ b/module/config/stored/classes.py @@ -210,24 +210,24 @@ class StoredSimulatedUniverse(StoredCounter, StoredExpiredAtMonday0400): class StoredSimulatedUniverseElite(StoredCounter, StoredExpiredAtMonday0400): # These variables are used in Rogue Farming feature. - + # Times of boss drop chance per week. In current version of StarRail, this value is 100. FIXED_DEFAULT = 100 - + # Times left to farm. Resets to 100 every Monday 04:00, and decreases each time the elite boss is cleared. value = FIXED_DEFAULT - - def farm_dec(self, delta = 1): + + def farm_dec(self, delta=1): self.value -= delta if self.value < 0: self.value = 0 - + def farm_reset(self): self.value = self.FIXED_DEFAULT - + def farm_not_complete(self) -> bool: return self.value > 0 - + def farm_get_remain(self) -> int: return self.value From d4fe1959a80b0e0aa2e6e0b1830de37e78f94c05 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 15 Apr 2024 02:38:04 +0800 Subject: [PATCH 019/114] Fix: No items enroute in Luofu_StargazerNavalia_F1_X521Y595 --- .../Occurrence/Luofu_StargazerNavalia_F1.py | 5 +++++ tasks/map/control/waypoint.py | 4 ++++ tasks/rogue/route/base.py | 17 ++++++++--------- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/route/rogue/Occurrence/Luofu_StargazerNavalia_F1.py b/route/rogue/Occurrence/Luofu_StargazerNavalia_F1.py index 3e1af7a7a..fa8f6f8fd 100644 --- a/route/rogue/Occurrence/Luofu_StargazerNavalia_F1.py +++ b/route/rogue/Occurrence/Luofu_StargazerNavalia_F1.py @@ -28,3 +28,8 @@ class Route(RouteBase): self.clear_item(item_X504Y610) self.clear_event(event_X510Y626) # ===== End of generated waypoints ===== + + def clear_event(self, *waypoints): + # Too many clicks on A_BUTTON, so no items enroute in Luofu_StargazerNavalia_F1_X521Y595 + self.enroute_add_item = False + return super().clear_event(*waypoints) diff --git a/tasks/map/control/waypoint.py b/tasks/map/control/waypoint.py index 829a4c05f..0a058a463 100644 --- a/tasks/map/control/waypoint.py +++ b/tasks/map/control/waypoint.py @@ -116,6 +116,10 @@ class Waypoint: return list(same) + def enroute_add_item(self): + if 'item' not in self.expected_enroute: + self.expected_enroute.append('item') + def ensure_waypoint(point) -> Waypoint: """ diff --git a/tasks/rogue/route/base.py b/tasks/rogue/route/base.py index 2126852a3..d41522075 100644 --- a/tasks/rogue/route/base.py +++ b/tasks/rogue/route/base.py @@ -17,6 +17,7 @@ from tasks.rogue.route.exit import RogueExit class RouteBase(RouteBase_, RogueExit, RogueEvent, RogueReward): registered_domain_exit = None + enroute_add_item = True def combat_expected_end(self): if self.is_page_choose_blessing(): @@ -140,10 +141,9 @@ class RouteBase(RouteBase_, RogueExit, RogueEvent, RogueReward): def clear_enemy(self, *waypoints): waypoints = ensure_waypoints(waypoints) - if self.plane.is_rogue_combat: + if self.enroute_add_item and self.plane.is_rogue_combat: for point in waypoints: - if 'item' not in point.expected_enroute: - point.expected_enroute.append('item') + point.enroute_add_item() return super().clear_enemy(*waypoints) def clear_item(self, *waypoints): @@ -199,10 +199,9 @@ class RouteBase(RouteBase_, RogueExit, RogueEvent, RogueReward): end_point.endpoint_threshold = 1.5 end_point.interact_radius = 7 end_point.expected_end.append(self._domain_event_expected_end) - if self.plane.is_rogue_occurrence: + if self.enroute_add_item and self.plane.is_rogue_occurrence: for point in waypoints: - if 'item' not in point.expected_enroute: - point.expected_enroute.append('item') + point.enroute_add_item() result = self.goto(*waypoints) self.clear_occurrence() @@ -302,9 +301,9 @@ class RouteBase(RouteBase_, RogueExit, RogueEvent, RogueReward): logger.hr('Domain single exit', level=1) waypoints = ensure_waypoints(waypoints) - for point in waypoints: - if 'item' not in point.expected_enroute: - point.expected_enroute.append('item') + if self.enroute_add_item: + for point in waypoints: + point.enroute_add_item() end_point = waypoints[-1] end_point.min_speed = 'run' From a0fd30193cde9c11371055dbd3d1006e565b9522 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 15 Apr 2024 03:45:50 +0800 Subject: [PATCH 020/114] Fix: Special match Combat_Luofu_DivinationCommission_F1_X737Y237 --- tasks/rogue/route/loader.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tasks/rogue/route/loader.py b/tasks/rogue/route/loader.py index b60be3d64..6c6eff19a 100644 --- a/tasks/rogue/route/loader.py +++ b/tasks/rogue/route/loader.py @@ -164,6 +164,11 @@ class RouteLoader(RogueUI, MinimapWrapper, RouteLoader_, CharacterSwitch): # if route.name == 'Occurrence_Herta_StorageZone_F2_X363Y166' and similarity > 0.05: # return True + # Before Combat_Herta_SupplyZone_F2_X45Y369 + if route.name in [ + 'Combat_Luofu_DivinationCommission_F1_X737Y237', + ] and similarity > 0.25: + return True # Before Combat_Luofu_Cloudford_F1_X281Y873 if route.name in [ 'Occurrence_Jarilo_BackwaterPass_F1_X553Y643', From 39af23657e2140452d0fff50ab2b124b1f17642e Mon Sep 17 00:00:00 2001 From: Matrix_Cain Date: Mon, 15 Apr 2024 19:07:45 +0800 Subject: [PATCH 021/114] Feature: Migrate notify.py from AzurLaneAutoScript and upgrade dep onepush ver. to 1.3.0 (#418) Co-authored-by: LmeSzinc --- module/notify.py | 75 +++++++++++++++++++++++++++++++++++++++++++-- requirements-in.txt | 1 + requirements.txt | 4 ++- 3 files changed, 77 insertions(+), 3 deletions(-) diff --git a/module/notify.py b/module/notify.py index 4f8b470ac..9186e5c4a 100644 --- a/module/notify.py +++ b/module/notify.py @@ -1,4 +1,75 @@ +import onepush.core +import yaml +from onepush import get_notifier +from onepush.core import Provider +from onepush.exceptions import OnePushException +from onepush.providers.custom import Custom +from requests import Response + from module.logger import logger -def handle_notify(*args, **kwargs): - logger.error('Error notify is not supported yet') +onepush.core.log = logger + + +def handle_notify(_config: str, **kwargs) -> bool: + try: + config = {} + for item in yaml.safe_load_all(_config): + config.update(item) + except Exception: + logger.error("Fail to load onepush config, skip sending") + return False + try: + provider_name: str = config.pop("provider", None) + if provider_name is None: + logger.info("No provider specified, skip sending") + return False + notifier: Provider = get_notifier(provider_name) + required: list[str] = notifier.params["required"] + config.update(kwargs) + + # pre check + for key in required: + if key not in config: + logger.warning( + f"Notifier {notifier.name} require param '{key}' but not provided" + ) + + if isinstance(notifier, Custom): + if "method" not in config or config["method"] == "post": + config["datatype"] = "json" + if not ("data" in config or isinstance(config["data"], dict)): + config["data"] = {} + if "title" in kwargs: + config["data"]["title"] = kwargs["title"] + if "content" in kwargs: + config["data"]["content"] = kwargs["content"] + + if provider_name.lower() == "gocqhttp": + access_token = config.get("access_token") + if access_token: + config["token"] = access_token + + resp = notifier.notify(**config) + if isinstance(resp, Response): + if resp.status_code != 200: + logger.warning("Push notify failed!") + logger.warning(f"HTTP Code:{resp.status_code}") + return False + else: + if provider_name.lower() == "gocqhttp": + return_data: dict = resp.json() + if return_data["status"] == "failed": + logger.warning("Push notify failed!") + logger.warning( + f"Return message:{return_data['wording']}") + return False + except OnePushException: + logger.exception("Push notify failed") + return False + except Exception as e: + logger.exception(e) + return False + + logger.info("Push notify success") + return True \ No newline at end of file diff --git a/requirements-in.txt b/requirements-in.txt index a518216e4..656bef808 100644 --- a/requirements-in.txt +++ b/requirements-in.txt @@ -21,6 +21,7 @@ pyyaml inflection prettytable==2.2.1 pydantic>=2.4 +onepush==1.3.0 # OCR pponnxcr==2.0 diff --git a/requirements.txt b/requirements.txt index 21042b646..0c79b502b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -37,6 +37,7 @@ markdown-it-py==2.2.0 # via rich mdurl==0.1.2 # via markdown-it-py mpmath==1.3.0 # via sympy numpy==1.24.3 # via -r requirements-in.txt, onnxruntime, opencv-python, pponnxcr, scipy, shapely +onepush==1.3.0 # via -r requirements-in.txt onnxruntime==1.14.1 # via pponnxcr opencv-python==4.7.0.72 # via -r requirements-in.txt, pponnxcr packaging==20.9 # via deprecation, onnxruntime, uiautomator2 @@ -48,6 +49,7 @@ protobuf==4.23.0 # via onnxruntime psutil==5.9.3 # via -r requirements-in.txt py==1.11.0 # via retry pyclipper==1.3.0.post4 # via pponnxcr +pycryptodome==3.20.0 # via onepush pydantic==2.4.2 # via -r requirements-in.txt pydantic-core==2.10.1 # via pydantic pyelftools==0.29 # via apkutils2 @@ -58,7 +60,7 @@ pyreadline3==3.4.1 # via humanfriendly python-dotenv==1.0.0 # via uvicorn pywebio==1.8.3 # via -r requirements-in.txt pyyaml==6.0 # via -r requirements-in.txt, uvicorn -requests==2.30.0 # via adbutils, uiautomator2 +requests==2.30.0 # via adbutils, onepush, uiautomator2 retry==0.9.2 # via adbutils, uiautomator2 rich==13.3.5 # via -r requirements-in.txt scipy==1.10.1 # via -r requirements-in.txt From 6ec8df807668bdf74fe470d46833b5ec630671d0 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 15 Apr 2024 19:27:03 +0800 Subject: [PATCH 022/114] Opt: Early init minitouch and MaaTouch for faster startup --- module/config/config.py | 4 +++ module/device/device.py | 7 +++++ module/device/method/maatouch.py | 43 +++++++++++++++++++++++++------ module/device/method/minitouch.py | 40 +++++++++++++++++++++++----- 4 files changed, 79 insertions(+), 15 deletions(-) diff --git a/module/config/config.py b/module/config/config.py index 6ba4e6d5a..26e54e287 100644 --- a/module/config/config.py +++ b/module/config/config.py @@ -176,6 +176,10 @@ class AzurLaneConfig(ConfigUpdater, ManualConfig, GeneratedConfig, ConfigWatcher self.data, keys="Alas.Optimization.CloseGameDuringWait", default=False ) + @property + def is_actual_task(self): + return self.task.command.lower() not in ['alas', 'template'] + @property def is_cloud_game(self): return deep_get( diff --git a/module/device/device.py b/module/device/device.py index 948fefe03..9e882194f 100644 --- a/module/device/device.py +++ b/module/device/device.py @@ -88,6 +88,13 @@ class Device(Screenshot, Control, AppControl): if not self.config.is_template_config and self.config.Emulator_ScreenshotMethod == 'auto': self.run_simple_screenshot_benchmark() + # Early init + if self.config.is_actual_task: + if self.config.Emulator_ControlMethod == 'MaaTouch': + self.early_maatouch_init() + if self.config.Emulator_ControlMethod == 'minitouch': + self.early_minitouch_init() + # SRC only, use nemu_ipc if available available = self.nemu_ipc_available() logger.attr('nemu_ipc_available', available) diff --git a/module/device/method/maatouch.py b/module/device/method/maatouch.py index 8122505bc..300e8ff9c 100644 --- a/module/device/method/maatouch.py +++ b/module/device/method/maatouch.py @@ -1,14 +1,15 @@ import socket +import threading from functools import wraps from adbutils.errors import AdbError -from module.base.decorator import cached_property, del_cached_property +from module.base.decorator import cached_property, del_cached_property, has_cached_property from module.base.timer import Timer from module.base.utils import * from module.device.connection import Connection from module.device.method.minitouch import CommandBuilder, insert_swipe -from module.device.method.utils import RETRY_TRIES, retry_sleep, handle_adb_error +from module.device.method.utils import RETRY_TRIES, handle_adb_error, retry_sleep from module.exception import RequestHumanTakeover from module.logger import logger @@ -36,20 +37,20 @@ def retry(func): def init(): self.adb_reconnect() - del_cached_property(self, 'maatouch_builder') + del_cached_property(self, '_maatouch_builder') # Emulator closed except ConnectionAbortedError as e: logger.error(e) def init(): self.adb_reconnect() - del_cached_property(self, 'maatouch_builder') + del_cached_property(self, '_maatouch_builder') # AdbError except AdbError as e: if handle_adb_error(e): def init(): self.adb_reconnect() - del_cached_property(self, 'maatouch_builder') + del_cached_property(self, '_maatouch_builder') else: break # MaaTouchNotInstalledError: Received "Aborted" from MaaTouch @@ -58,12 +59,12 @@ def retry(func): def init(): self.maatouch_install() - del_cached_property(self, 'maatouch_builder') + del_cached_property(self, '_maatouch_builder') except BrokenPipeError as e: logger.error(e) def init(): - del_cached_property(self, 'maatouch_builder') + del_cached_property(self, '_maatouch_builder') # Unknown, probably a trucked image except Exception as e: logger.exception(e) @@ -103,12 +104,38 @@ class MaaTouch(Connection): max_y: int _maatouch_stream = socket.socket _maatouch_stream_storage = None + _maatouch_init_thread = None @cached_property - def maatouch_builder(self): + def _maatouch_builder(self): self.maatouch_init() return MaatouchBuilder(self) + @property + def maatouch_builder(self): + # Wait init thread + if self._maatouch_init_thread is not None: + self._maatouch_init_thread.join() + del self._maatouch_init_thread + self._maatouch_init_thread = None + + return self._maatouch_builder + + def early_maatouch_init(self): + """ + Start a thread to init maatouch connection while the Alas instance just starting to take screenshots + This would speed up the first click 0.2 ~ 0.4s. + """ + if has_cached_property(self, '_maatouch_builder'): + return + + def early_maatouch_init_func(): + _ = self._maatouch_builder + + thread = threading.Thread(target=early_maatouch_init_func, daemon=True) + self._maatouch_init_thread = thread + thread.start() + def maatouch_init(self): logger.hr('MaaTouch init') max_x, max_y = 1280, 720 diff --git a/module/device/method/minitouch.py b/module/device/method/minitouch.py index 405bc0299..18a09009f 100644 --- a/module/device/method/minitouch.py +++ b/module/device/method/minitouch.py @@ -1,7 +1,7 @@ import asyncio import json -import re import socket +import threading import time from functools import wraps from typing import List @@ -10,11 +10,11 @@ import websockets from adbutils.errors import AdbError from uiautomator2 import _Service -from module.base.decorator import Config, cached_property, del_cached_property +from module.base.decorator import Config, cached_property, del_cached_property, has_cached_property from module.base.timer import Timer from module.base.utils import * from module.device.connection import Connection -from module.device.method.utils import RETRY_TRIES, retry_sleep, handle_adb_error +from module.device.method.utils import RETRY_TRIES, handle_adb_error, retry_sleep from module.exception import RequestHumanTakeover, ScriptError from module.logger import logger @@ -328,7 +328,7 @@ def retry(func): self.install_uiautomator2() if self._minitouch_port: self.adb_forward_remove(f'tcp:{self._minitouch_port}') - del_cached_property(self, 'minitouch_builder') + del_cached_property(self, '_minitouch_builder') # MinitouchOccupiedError: Timeout when connecting to minitouch except MinitouchOccupiedError as e: logger.error(e) @@ -337,7 +337,7 @@ def retry(func): self.restart_atx() if self._minitouch_port: self.adb_forward_remove(f'tcp:{self._minitouch_port}') - del_cached_property(self, 'minitouch_builder') + del_cached_property(self, '_minitouch_builder') # AdbError except AdbError as e: if handle_adb_error(e): @@ -349,7 +349,7 @@ def retry(func): logger.error(e) def init(): - del_cached_property(self, 'minitouch_builder') + del_cached_property(self, '_minitouch_builder') # Unknown, probably a trucked image except Exception as e: logger.exception(e) @@ -370,12 +370,38 @@ class Minitouch(Connection): _minitouch_ws: websockets.WebSocketClientProtocol max_x: int max_y: int + _minitouch_init_thread = None @cached_property - def minitouch_builder(self): + def _minitouch_builder(self): self.minitouch_init() return CommandBuilder(self) + @property + def minitouch_builder(self): + # Wait init thread + if self._minitouch_init_thread is not None: + self._minitouch_init_thread.join() + del self._minitouch_init_thread + self._minitouch_init_thread = None + + return self._minitouch_builder + + def early_minitouch_init(self): + """ + Start a thread to init minitouch connection while the Alas instance just starting to take screenshots + This would speed up the first click 0.05s. + """ + if has_cached_property(self, '_minitouch_builder'): + return + + def early_minitouch_init_func(): + _ = self._minitouch_builder + + thread = threading.Thread(target=early_minitouch_init_func, daemon=True) + self._minitouch_init_thread = thread + thread.start() + @Config.when(DEVICE_OVER_HTTP=False) def minitouch_init(self): logger.hr('MiniTouch init') From 9449bf693b182803155c15dd74ae1a9a3d00f5f7 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 15 Apr 2024 21:04:52 +0800 Subject: [PATCH 023/114] Opt: Patch pkg_resources for faster startup --- module/device/device.py | 6 ++ module/device/pkg_resources/__init__.py | 82 +++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 module/device/pkg_resources/__init__.py diff --git a/module/device/device.py b/module/device/device.py index 9e882194f..d9cdbf1f8 100644 --- a/module/device/device.py +++ b/module/device/device.py @@ -1,6 +1,12 @@ import collections import itertools +# Patch pkg_resources before importing adbutils and uiautomator2 +from module.device.pkg_resources import get_distribution + +# Just avoid being removed by import optimization +_ = get_distribution + from module.base.timer import Timer from module.device.app_control import AppControl from module.device.control import Control diff --git a/module/device/pkg_resources/__init__.py b/module/device/pkg_resources/__init__.py new file mode 100644 index 000000000..61014ef79 --- /dev/null +++ b/module/device/pkg_resources/__init__.py @@ -0,0 +1,82 @@ +import os +import re +import sys + +from module.base.decorator import cached_property + +""" +Importing pkg_resources is so slow, like 0.4 ~ 1.0s, just google it you will find it indeed really slow. +Since it was some kind of standard library there is no way to modify it or speed it up. +So here's a poor but fast implementation of pkg_resources returning the things in need. + +To patch: +``` +# Patch pkg_resources before importing adbutils and uiautomator2 +from module.device.pkg_resources import get_distribution +# Just avoid being removed by import optimization +_ = get_distribution +``` +""" +# Inject sys.modules, pretend we have pkg_resources imported +sys.modules['pkg_resources'] = sys.modules['module.device.pkg_resources'] + + +class FakeDistributionObject: + def __init__(self, dist, version): + self.dist = dist + self.version = version + + def __str__(self): + return f'{self.__class__.__name__}({self.dist}={self.version})' + + __repr__ = __str__ + + +class PackageCache: + @cached_property + def site_packages(self): + # Just whatever library to locate the `site-packages` directory + import requests + path = os.path.abspath(os.path.join(requests.__file__, '../../')) + return path + + @cached_property + def dict_installed_packages(self): + """ + Returns: + dict: Key: str, package name + Value: FakeDistributionObject + """ + dic = {} + for file in os.listdir(self.site_packages): + # mxnet_cu101-1.6.0.dist-info + res = re.match(r'^(.+)-(.+)\.dist-info$', file) + if res: + obj = FakeDistributionObject( + dist=res.group(1), + version=res.group(2), + ) + dic[obj.dist] = obj + + return dic + + +PACKAGE_CACHE = PackageCache() + + +def resource_filename(*args): + if args == ("adbutils", "binaries"): + path = os.path.abspath(os.path.join(PACKAGE_CACHE.site_packages, *args)) + return path + + +def get_distribution(dist): + """Return a current distribution object for a Requirement or string""" + if dist == 'adbutils': + return PACKAGE_CACHE.dict_installed_packages.get('adbutils', '0.11.0') + if dist == 'uiautomator2': + return PACKAGE_CACHE.dict_installed_packages.get('uiautomator2', '2.16.17') + + +class DistributionNotFound(Exception): + pass From 47b8f4a37d04996cdf2788d0d219d02533a0a904 Mon Sep 17 00:00:00 2001 From: Zero <98764734+X-Zero-L@users.noreply.github.com> Date: Thu, 18 Apr 2024 21:32:58 +0800 Subject: [PATCH 024/114] Upd: Character (#422) Co-authored-by: LmeSzinc --- assets/character/Aventurine.png | Bin 0 -> 17297 bytes module/config/argument/args.json | 2 ++ module/config/config_generated.py | 2 +- module/config/config_updater.py | 2 +- module/config/i18n/en-US.json | 1 + module/config/i18n/es-ES.json | 1 + module/config/i18n/ja-JP.json | 1 + module/config/i18n/zh-CN.json | 1 + module/config/i18n/zh-TW.json | 1 + 9 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 assets/character/Aventurine.png diff --git a/assets/character/Aventurine.png b/assets/character/Aventurine.png new file mode 100644 index 0000000000000000000000000000000000000000..2e1c1de4585027942473dc0884c8d79e1424c91f GIT binary patch literal 17297 zcmW+-1yodB7k$78Lpp?j3eq@qhjc0_Eg&T=E#2wRjf8-JfS`bYbeA-PpaLS@4bolz z{rwbonsX5Kkx@3Z&5k?PN%5#l|-gCK}dQ9(`<{AmJy>BBL>@8h0MZ4e}@rzj__ z?e!tF+50_}t~X~u>Pz3b&(uociv;Zi+tWd$5C!`IKm7;_EY4Y$v`pEjjc>JazDTY$QHAAFB^DF{!5Wq4@UqaR*fFha%UhX?3~$tfK2brHyPYzh zh!}=JC&yBd%-ch0c%=cS^D1}PxE%8*ATFpzYC=-d*fAAUM0`DPj2IqxR{;eQg5Y!z zj|?0WCKK`N_EwFzC)IDBdW*Zq%_z+>9f3*5Pk==?Iz2u8`}d;oP{&&wVwn^rc^w^8 zLZMA#*;FkDeqJ6jpnSM?$6oLuUZ}*u{B?!ipx|J7db-K=V!ulR72&gH<8p^7N^>X( z8AZgj>m4@6w=mwY%n9BF)=}}Wa9k%PseCBUQ!4dqJadA8wN4sZ6l7rYPXn%QjD%)j+C1qhR%`_s85tkP@6uka-R_oH4`&-u+e*P% z$VufZLLexJ4hMqn-+^dPhSJrQN|?RICuAY0oeYBH=(&5M^sHi5=rEv9+QiUDIt&@) zBxS%U1I?#oJd)Cv;q+@+Tdh4Jh{|GjgpAJCUX^K#h7x`#CiH4b+*4D@J9%H&*52{p z@>=f&mrxp;q5*J?kE0}kt|Z&fdVA2-0aZE}B;1LqP^E-|?voEQWE67Tu;z!*suQcJ zs`kfLw6;p5&DmuzLsF?Lsj1{TI?>&E$SBA1yXJ<-G-KZ#0hGEh)l;}pv)BH@CCyiD z)_Za~Is&Ss<|=p@HW}WfAGaDA6;)ML#r^$nZ~mG;i+|+cNifeS#En!=UC|FyD$wlbFpp8rYkp15P9m=XchODRBisRFJBuqi?sQTNn@X0US8g%k(mDVOT)y3X8)|kxExziBFD37rntEAzMv|f z?vQnrKDrWF;Ogp1bMZ5rH+|4r@^T_e)sCkJu?|5UwGb3IC^G&ul@?BEu9I{}8j)^J z8D<`VO1Q&LEMr9_o4S%m+p#-)7~qI0uki(Gn}MBX#SDj_be9S}_EZNgGWqS_DkIw4 zJ?hBiyYCS)X}Gv_KRLjY-cFXOBR_F}vU%xmpscH#<}mg4?OQD^t;Vvn6XXc_F z+YqIEz6R>&-@ke;62B%UBITqt$fT2~dWq!|5%kK%c}VimjKcIbSmS=NwHMD&M@5~ z?TmOY*o^?Xl2K6xu9##oVRbB(QD|8;VOa1L8HJ2;cXt%I|6fzA0yFYEbdCd4-f z(M{Z^`5Gpku1GRW$f9Z@Y{-(DntJ$ZF)gET#$(E1+$2!koPe&2k}XAv2&{UL3|B+44wCDZQj)%8@ zuoNu<54`uHA{Mx*tX96m)WRz`H1FJSvOO^}K0ZD+W=R<~wNrb9cgmiIW`fIrva_?4 zMtoBi_rEwgaS#o>-ZC-qxe2tjv!nYduK~N|qCDd4)wiWazS2*<&SOhgDON!tvQdv< zY4*`3Z>zOAKOHngIY`Ge2hg0Jha5MHx+HH_K8s=Pm@CRX#1QJKPKpn9>*G;kA*Kvj zM$wl|L`LT{-*#rrsNQ2he>6sNP;CbOSdq?+PCEkaUX%wNJW>J09Ls3zXerGNP#c1$yE zc+I>1Nt~8}f$!x`-NP^*0Rc;W@@J{wrBlM1Ja!IzT0jp$L8OnLOTm48d8jb$yQiNuib&klBYOqhmC_wYNvhe8uF7&GtHCf75iBkCMEr8qAiTWIlx#qFtt-wUOvwML-0rTKS#OOwvNW0>>=%LX8%fq>xQ#MO7AIiDx$h72&mO%s0~sI zYEzk?+M|D_dAdIexR&B4DUL)nO{R$-txHm6=WrKcF<~f$EWh9&U?;GUhb1&(Xv0Gz zq`U0*vnbj;U@WjMD`v9$3Ar7E@DG8y7W*9ODgc7BThCWBcUw+pwOLaX7+2TUybY?q zeVg}anhUseP)zdpH@&g%MJAt{92XZi8*sUkYKa^25vfF)=ExV)sDa_bcr`{;;UQ_|rawf|!x`iij!KE^oEAx8%&kS4RYtFPS$K&T} zYjYy6yWcvFC`xx6gklD?-G;~C?hW5;55p^-m^HI@>}UM&jRR1m!c5pX@6W8}3PCx; zYsWqn$oSaU0^W2$08~f3KL2xRs8&)$pkAvUEuMEQIZ+$dIU0$)Q(=A=c-++%Yt(u* zD0JgsVKz-oD9F$MsrPYoVO?+G`7WNvZ zp8A4?AIY?ft=buiy#v`K|%UGf`-KoBPl7vC^GmbI}B3UP1Vk7UgYHDg43^~c8 zb(FcFcIOX6LqmqSZ?4Yvv$G#PH&aMjKK3E(>|1dWntsy>kc0thpRBRV@m{^p++J&m zZaw&GesScM_nw11`pt@ap^d~;TaGmoik$A(@4z%GV;Oi`)~@XoIUi>{SegAzA#Xl84o3FUOX4I;KJr|98 zr=q5!s#;{*JL5UG6W@^Se=?!<`MFKTi-*7EG{~CYynem@-CNJVVC(Qa;A)Ac?xeQ5 zww4z!wNc7_#9jPR{O(MXr#J3I6B=L470Mb0NWaty!E3+ytcFR-Rr=SJge}XnwmPcU$`51CTrs%@gO#;5sR)m zbx&0Jx}b6R_ER!FG=EA{ADyq4Gi)uJtH++5)4DE9gz*UcbA+sbpGxO;k?UshX!N#g zX@AwqW;k2i2t9r;x;xlB&r+ZM5G6yWLnI_9*kdO)VSn=HbmoInffxK0qgjl%NoOEH zRPpoWcVJZJsz-RT0_LSxW1E7E8W|Z$*6idKZnh%GmhgX>JO&uDMyl$_@_ADNT0lU+ z6l1csG)(}KVkGHbadDz~giKMQxv9cA{Wl}(>!pBz<$n_3=O2yuqRq+^j0th)>$v^} z+;Ex*8~W?@O9(RwGI!J4yqM2UP-Pbsv*1*)lyd()t77jj<1fta#Ih{Z{$d zbjt?U4s+GNl$303e9npMT|yxglkh^nH@L1LFhO(N%+Gil)!AC!8sG#T9r63mA1g|1oYi;l-Z`}#zu=|>(9IpQiL<)8`pExCBfeLQ zHH8)hh@#)&568MG{vT!)dfft4(o%5z&=6X<@5y$<;gIO(&z~jtBh{qf$^}WtD0nW| z30xYmv9UL|wv0H_kWuL>Pj$wMii-HGhdybtXpm)QX66K3Y=nk}f;)wu`}<#SmA8RO zO(w73>UTar|IsExTwEM5ZBQmZ`C}QNf5aaajOE&QO(Q&4vWnGmJzMa(VSm@?G49%( zhL2VfIq+e3ws|dUtRgx*|Fs~ zEFYf#`SYjkdaA7j3@bm0x%v55NHP((^`C&_%gVmZLk3IDU!5PK5>5DupeR~aS)=!;Q3w=)(L>+3CB4*p@x9`Y z)GG91y3Go0Kz+>4&aR--!nZaZ>ld)o&?|@Mwzjs&0`O@L4-Y{T@TpcAa!%Fm9Q$~S zW9#}*P1R;*Wc&yE|Ni~cWB)TV^8OTcGI>DVj2`gP(r)7{HpXS^Ifzk3r><%Ro}NFuIr;U& zJ&OT~cbb{GN;7G;8w*3A&f-M>efoM3e0Mg3>yx~oh&cn`3z8{eE;&Nh1uG7Lzk+991Bh2VUEdOOCJ&x)e7+|{UJ zr25|O6wPhKQtMGmE$Ny!vgJbIDmpItd&)1+dM3wXEYRh$P|a~>@8cqi+8~ulofTn) z0~BPbKk6(O;S-M>$V0(Z#5 zOIE&4zS-AW=(e-6>e)S|i0*c2@Y@_M+BBu^1;j-nM=f>5Ua-U_1HCd?X&Frm2RMH9 zr_^9LOSISfFDuil3@dKR<-RR8lXJYsl)~ach(+y-8q1PNFM1S$jJrKTMv1QAXYNnp zgtxhb$l#b%v}GDv=gUV0hu+b?m_7>()4I(lIjq8hED>>bHtDHvbfH8{8OGA>{nqb@ zs>B04n_ID!6%s5=XwOdCe~JEUM(-T+L{Lf*nG%`S%04;x7^D~#BiCWdpQ2FuvSD+2 z!_{@fHN*6T=6;0GC_QdnU0s1D{<+yK&C%uCoBe+0Xmv&yWd+j>rGp*kOEX?Qo;*ahQ_f8Xf%WIJ_Q4xXJ1Jt(oUed@eYR~EYpR#vT&?_As9d07x z0s=K)(=+dlBbKqy|F^vj&|dBHh!~C2Wc9}SuY5i}KF}PIa^F{6oIeenD`x*v%Mx)P z7C-%@S%gmG0+LrJH=|->DxcxYhB9s6I80g;;sxtHX@s1PGlWkf%}!adLd}G$v8lc( z1qZwglufSsYg^j^3to&I8jQXvOQUgbht&(bY~Jwja03Ga!cN4UbP_~l7g@JFOAtbo z$epT~)e&|_BZ2f#u~hp7V?#mFeGCP-_uS>I5T6JqmK+?YBLKR}+in{3khKoIW)kVV2nUS+JZl{f#MnV~*9uPSo0b490XeP)WtmHpu&4~syXO=q|J z(ThLHY3Aq2${gm&BIFw6cz@6Mh7R>1+($R8^^d1lPa>nDMsh_(1>lHruc>+~9Gbuu z2v$BIh9Ts6($L;wkDk}>ckZjXv<}qnC>123R{&bO8Lb%#v~hr;IP>on_d4LO0g~m+ zAC4AvMa9K`jEtO}oq4t%W}TdzXcl!zN3o-U>15`el3Q?0^- z7*ybNG<Cea)ISnDA)tkLq$qLeA-IgarnQx+ z@#~nBmL&DEdWXEs+FlBE!+%SU)#T}UrkFR}$Ylo0aj=k5D9VIyK^XjOA{F+y_%ACM z@$<}IUIkK*@K*EIa40CYZ`pi_HE*XHsdSTt!T-t)-;3@a1k6YzD~zC8u&(PWv0?u{ zhe>hl=-;qdjM-+JLuIb!WgYD_WGI%jt!M;>PSSAJlT5{ed+c?iLXX$T->VkM2X(?w z^6W=P9>tuA{fx6DY(JhC_HynRw+xu4$C(qGxsq5F*^tL}+llVRc+DnSPSoy1Mn=}w z3J#58K>Vt9c6JXRJaB2631~AJ;U982i226v;Jo{#7Cs-f+Tl#q$*oyL7S7mAd!S9P zND{u9hkQz(7)}##_OEGoRaM{Ie5t*?y{`}MRDV7!ASh^D|0Y=W^Bdto>jjA%CpWib z-FpFkib-xpbGx5#Bn5a=Q4ggXSdAyZpBySwqdOHp~RV%DtJW*3?sd8=%KkZiSo;ag3=$~ z*p2eNT3<=XNtN_JRl#x1e4v+vDvrmJ8=!bkJp z$%sD2xHJ?NuAWRem6wz>`JEpCEDbzunsW}AtpyU*#l;0s)A>Y2+0ZWh5AHy(Vt~D2 zlYvSAT=#IZCol`~edHZb+Olm^*7aY0o=r))PfsSImyAI|^_1^?x+3@2?6HAr1efZM zKDf8V1nZg@iCfdM868`bwo|)FLCu+AN3BC2^pQXv`~E!FRJF&!z_Eb%c_0)0unsJr+R)v50voDJ& z;~@mA7b-j^@2PU$uurY+^Y&l{OZ8Xs_dfA{{joVA+PCu&aw|cq4lLmi6fHqS z9nmK$cem9n&X!{hOY$b%CZj-;B}FMVKAy{kQEDPCQJy{;kOd)mC&5|sy3Z8`dm;kVX!oG*Z zm%1J-_Y2MW@nt;+6hfEadMB8H@6fflmykOTL93Brpzwk~cI4PEmkb`p@(RVV8CK$z zYfC#-JF-!Yi~_C>mE2xCGfFR2rxdGMtCZQk=kFp}G9sJ30ZWM}fvR1*$U`4JCw5VD z{p=FIc1WA&UH|rM%jIa1)`y9C^~2J-*+sF9yMcRe-M9X1G!B_={w7}1ndz*-(D4sU zbZA`&dk888CJn^MRLkR|qrLl2dRczG=1)=THmfkJpoGJ`1dGi2SmU))MBcE3GAgF4 z6zZ|NU?$W#HA}W`GM*8Ie^1u@w%j*{sAWf2e*ME%|EJbLKUKK^>;qGw_!I*xF5Hx2 zfj3tat$U&Nf|&}8wN7)KQS{ay8^*QYp=-kG2Q+uP#_ur%XJowA)$OgcES*u^4}wLQ zrmsXfV$pu-T;hx3QbF$F$b2X-s(cZ{gLXlXsEjb9q$4n-Qlg|;xcT4eEie9*uUo{1 zSfcRFHAdX(VOd-=YYuqk2sjA@hhRZa(5vXbbF|3CDJC}jEw=+@8KZv=GJUP zr+;$u@f~(^1?(q2dlcDic31MCPnG8O45R{X_QECKgvlj+%5-dGc@PWiDDeL%{$5^Q zUSEGrivadtb20Nf#k#2AtMfegKD-0m8}qOKS>tG`OiUuQHj;!?_@AQ{NKFM&Z@w(LRh6`40-#{2ibfqL-Kk0l(jX!c!> z9s~MMVmK{;^R-(j#26ulcE#*j25>suyIzY$9#yZwz(!}zl z{;;~{ybHDStE#gDeZa%Z>+Em8R&nvo_1vw$gDwR^nkVNmrVg3>N5QVMxYM@Vw!n)K zhG%hZ?(P`y#-^t5xU5(?`WfmOz>I0GesbXB}Os^Yx^~xyT)yEG;o+^(P-2?wrsxx#pk&iO4k8_K*?-x2VI@{ z!-d9!s+URWJ#YRe|7*6uz=QRQ;G0gG=EJhvvt98T{~a=It2O4ZXG0JBV=Vsmr}yWG z-{iyx-p)CcZojvm!!Ji5@3DCt9@M5h)TI`6{VUzrRQ1{F2lk5*br&2fl6)Hf`F}-8 z)6JVbH8j@)vU35eH%?c6GMg%-Y8hTgSl$6=sD``m-RtMavMEXh;3_K{dM*s7GxiZq zt#Fuf{&0ToT{i{1Xux{wf7d3XSCr3{Kgidz+qt;>r{a=zBqk>6vA6&4qzoe_3K!KaYu+krqJnUYeM>ELUz8c%C%^`>KX{cu18(IO z*FlmYOlhvypS4}-nviH8uW?C;3JBbbKeMy71#|tM@X&0FueG&xq}9>U(K4PYSXG`q zw;IZkR2w2GAD_~gI!h_{TS@P|^_0lD3qx=fj>jR%O}8|qVmauEX^GVpKE0@RuZL<6 z8V4BTiiDTJY9+?gyR;wL;N4ptVVQYvri>D!?sT!CV`i%M@b&TKwLz`D_t`>t;7L`E zGwZA~lFojuY{z_w;V@EP#zEpiR%Yf~#Gj{TIhTXh;H84gBY&8NX?({7hpkrNGvYuF=}YxC{thmYgR zb~bFwVq-o?j{N;~+LOqaG`tfVW=O(6&=9P(cC!{+_hQ<|>B-NpHV@WZ=Q>69GDmr>&kq(4Tc5 z_n*7|cUN`f>gY2~Sy9nMwarN9UCBWKjSfq+S1LaNL)P=%>4w8#oJW6a>?pXrXJ%&n zvd8}ZeVwtoyv(@EDyMOD?pJ7I+~@`t>y4UmA1*!t@OcfW*VfmPE^G=OFHV<)!8C`jdk7!Rp!e^i|&x zL(bLFxWr{&;`prJZK>zgp6{&&O=Kh)2u%Q-n-a7aKlp9;&AxYr?)!Y`0-8x~R>YH+ z3~wgz_BzeV`rGhjg;C(aPMOD zjgz$#`^5S0MSZQfN#eTmT3xNcML|MG78Jp#<1%xz z5}1Ba-Rkqincu-S1f9^Lvd7zDC%du9bPs|QlK`sorN@Bz-F$!TqQNV?GhB)`L4{dI zCo4PK(Z=S9>ncH;5m(F6?|~Gy@vr(z9!+3*T3cHKebxyT z9vU778H&@B6CxraO{J~PP4MX@4ie+-XPeK{+O8~tQrvcX*>(#+YFYl(NTDhY5!Knn zcpIm;n3~vr*P7(*`Nf6Lo6vWQqQI2urZ5rn*rvSg268{3_qv~trv-#V*N=Vtbp^}E zn?Sq<7?}AWBOog)3#5&=v{29+ow+-77BO8*6%EybLPB;K&hi=-`eXdmP`l1kLrwb( zD~TKmm^67oYN1u%`xmSlBZY-6o*;SfP|#4=qc~}>FlD}2RcKTncx2^AyKQb~+D3Lu z!vT9sIFh%taBQyln9$8UHgkdA=Q*t{rHcE$*VM5<__TFp6^h3l1D~eJ90(gb zCrHeb;+R;-<8%*ue)D6dgJ=zT0`1t$`8SK2yh3mq-PT*|!!QYC)(_9MbvP*A0VU{mBk*==C`&Z|7qU_5 z1Pe+agDN=sUt7){yy=R+^Yn}3dwC5qFonI7id~-fTWBVI0st`O*i_oudV@B(aiK-% zH8lIq6mR)7`UpBrU!~REZan)ln_3iEJ?()8T;^2#7PCi~B+}4x#=StZsI6^o?J%@+ z!CtV*c`CKPYr11C(4^!{sgK7f#Kf7)?zQo+-xUo`JB6on%@?aXY>lbW&=WE#_6&GB zyn!jFAn|kC^$dRbKdzWmr#Y|37_e8+7kXTYKEoI!IS-)$1)wAFn0@M-@5z{p)^T?s z12XCQ++Q^1*CWF>yTMiIf5r^v@~t>5^&F?E#7S)T|+FGFK09YIOtmc~4 z={|0hrc`iqQGPqQdwQ3U(0@G`=jPbt_P9M5tI2aW;O2a-?YPtluton}-?=3Q6v&;% zK7IJ`Az-^e?tin`6{(!Y39>KjlmxB&JrA$1uh0Fmg+TD~jetUd=0by7eWkOKubG+I zM5Se_rA>y%(Yi(q&#sj)P2V~5?EZHqvYDE*I1?vsN-#i6OH09*H*OZ++V`5aXZ(c> zykTI-bd;Vb=M02d8$^x9brhTZ_GOfnLE#u=WM)#3lUGjI1ChPK@X><@QSwW@aoJus zx^4S^Mt7*iua&G8S8}d4ORbFqZl6i=>JIjZPI5lB66j^gSRF0ccV~OCJ6m7Fo%wR? z525nv;IzH)0~W*V2UoIzPjXz*NxHl<3h(YHle6n^lYNc;$QVTO?am!vnA%=nzw479 z!a`#7b=9rj-g3m4adhi(!71`zqva%1F(K0zI)t3w8tjj#Cphsmsp&}YqQ3d)25SFQ* zxGaMRg5=f0U6Wm!GZ1o^sx-X>pMTTce(GfJZ=$Y=|8d9g^%Ez30oB8Z#h*Wa7Ud>p ziFyk13kvcJVMPQXF{3dQc_V?r96?J43%0B1SR}D(*NPukw!Z{+P`CNo)a0bZ(Oze~ z*a4hoO-t!X%3FlX*<#FTYI=y9NGu{rjzZ@3bx$l77FM#lyoSVH@LgI(7$q^11WCdu z6%kCYh$#cl$eWKyvXe+GBZOHf9Znh==uVGD6-ThQkR3HVRU^E}Wu!$A;>ac{Fp^(( ztL11XG<&So2Hp(QRGZYT4%3_;L={>-Ogp`E@u10RVjeF{$Z?8COyIdC-SWv$OdQwN z_P2uajEj#5CP_U4!LX%X=8dDyzKaGW_)6*&@v7>lD8n?eYC3NpV+{7&qkaZs;ruB#nHw8Bo^~A;zd2h1Pn*ox zU+d6Hf~vY`K1)?%YSqphVG(;bP&qv{WuU8DXEVYKryH)Cs&!ZzOoZs(A~Y3f=?QqT z(|ea>;E@p%aT0t&k6EJh2LRnf&TNO3o%gojxgc8!jL&m9C1^a({jAs# z6&4njbcf+VY>(~ae|h*co*shA*4EZv{Ttu`U1enfRZITe8Gg6ixCs~wo^Eb~!^5ZG zqo1CZa5cXfH}Wh6X6(v9O0iYiq4#pUX^f@4*zIVUTb+V=5QM!l*^^pkOoK|G!;><0hRg>$)HTG? zR5)_VPz>(*=6tc#4hrPMM|b)8`Kyml+=QwvrVEZm>u7w>LkIWfFGM}9dDC_LAHqyx z&5ID#Wi2kyitE)9}w1}0p zwY;pWman_I&qe?3yR*&Cq9{&01(KppR(cX~e!0UT6{ZHCV_Uds%DZL3mp~M_qvPycFj-1x8(U!S@G+w@$xmFI$| zl&tRlZS$B^fzk@0iK4hSh*=6j_P-!VYSA=C5gE7?*ZMZltEIH4NP_Cf%yjPn0$_#wv%hJeLhY2u(xL@wBWbjrOES`FND3zYHPK&iX;y% zw&q4Gh*5q-cOZ*z{m{;r-5x#6AZ>R(9gaW0Prk1mg3SeFeliNr%Ovn+V|cI$Xi;*~ zUy~ND5MpvH7zmjuXE}}O7T;6AjMoo+L6QG!-ZdARanlO0Ez~Tcto>NLS;Zw*6L5V| z?!RLfFMjf##0i{nA%l(Zr@xBKqa?uA{i-LuR(W~4yE{`WPKKBAL#!Ut`c|(21lH{? zf$~EKLjNKn!outOURMFPgM$R&8cB>8BOg9|m!;IpUGQdbyE zejy?P;^D<_e{6=q2zWvgXyq{8KyN6?AQB8*h-6-ngu{T2pMJ0{&@OuN4}b5rxNl(7 zZE1d3iDJYtY%9ZP;q2I7XxNz5ajN0?@$I#FM}MRGgq0uyYICBL%9$dyb80=$jUSGW z*R7m*rG<6mT|7I~+jSLE!fMZ{sxr80#jGXJ;_pOY3_rKjf0JbQVmbdtkJ ze4+v>FfBgiY>*WC#$VpS#zy2#QsMaOVBXJ~;PZmhaX}qR(OKt35pLk=XJ?*t7?S~aHDlqj{Xa6V(>*d<^U$r() zW2NtdNg-CK1rr&uWatsSq2f|58m;S6xnzv*HvGA^4;!03=X~S;{q5p^?{L5Urn@|+ zl@rl)wz(y2ax!86+Rb);@Z7$brXBrq_p5bUezu=4X;BOXkdN*>AlL7*G9ELy3*xof zq<4DWmxqz^SSE^<4#k>k#7=&X;`pM1DJZp8kCQ22BRAZs*?BxxXts|b`Nd$w10}-` z53<lPWJxVm^oogJ_X> zRXh9p`(Tea6|6Rkxo-)Ac-%4eg7}yYR2b)etsrL$Udz501c8vyf2;Uj+AavA*03wU z6B&g*hZhgh=_;7G7g3s*DdQ_{(&$|7>C|D$Vai2lbRne>AG^PH(Z0_}zgf0^l&)x3 zhylIxE?Xgfj9#3c9(;4#{%3{Fpz~(AulvHNTRuUcX_+f6#qnz zn|2`6=hv4{HU-HH>0^R#gOWnS`MWP8^)Kk-tZwAY=qudK0wq>O)+Hj92!cT zvFJd~7KCi$EL((x^k!y9-hg1bN#OOY?=FzimL|}Z>=L@boygD64=18teE)arI9u5H zJ2wI6=Hkm}pg#aaa|BruP&4z@bUCDZFAd~3$D*>83Lg1xSNtbV?3SWed^U?d*VD%a zlE{2PDoNy+=;?u+t5hlm1fopiZ8z@1R413$%_!&BooqJlPk#R1`j4%4J@fjWcYv#j z%_5&nKj4*a4Nl>K6L{d52{h_#kNz#Yehi-Uwlm&{Qy<$^OQSn^fCM`MFHa@X&cfnv z=^_Z?4fMTcC(r!A+HJ-Yb#IJg5h z?p?}5W)yB5`+!7Pp^YJ7kMC>!REEU9`T2Rk_4an00w2~JcDzbhBi2vlGOXWIyFF4f zsB>Jg3pleOARzcOV)HLXXH3m~0$+Fa(of=fI7pP22OO6)mPP03v6G*}>xv^Wb_1de z-Iv(wygF5oYr3PqM9;lT)c^X77Mf?WwhLxV%f?s^>&(tBshJCv9eNx6s`8{yN z5=mxjW3#!jVccX|D!E)oMQS#$>0c;Kh0R)cFRq6!)yLqWyg|-*tXL4GO>N2(lcp2x z*WEu2C|XZ`&TR)SoSKdquqts~mEF#J-mIwU9x;E?%nQs2rsuzc$^z#+r z6$`rxNrkfIsC0skJs)K>BM#@u7iWI2PX)qjUUp09@!R<(lUN+Of@I~m^8-%fujLt8 zx)jfye_JQcVFY%Wq&foU2sCv4VO)79Th_yT6%8`|4#amoRYvk?Ma)+*6LG0Z1@@iG zY|Xss1mouJ)$U#`vjFiXCiLHgel?BJFJy@VT|6B}EqZZz3DVoqDX&1-7MvmlGJL&A z2#=tE063A!R6f4t(Zo{FSmX+h;QdvNlWow`rkOcI@umxDNw34Z+L+9CY4E-hJuXpIwj0aO9 zi`##vaB}g9pYp&Vmhi}@xS9Gbhpy#B+h6K#_i1h`ggq2fS3;F=QFjvQtU@ZE-2UFp z&erqbgP`R5IQR3AFQe8Y^k@nGg0zYiza7Rr?hj~U_Y@tOM31a})M`3|^E}(Dw4k87 zb_Y1>V{O25EH%~TUk#NBMl(SWD&h2fK7L5Mt^j_>f7;9A96w-JRF;&mkw?z}r`=Fw z(3DBnqOQ97jh-RXG+t;(9&-9nR8+KK(oknig}Jk&$H;R=fQQF)O7K|;l{jAy;CXO5 zlZ)SJXPq1EDLCQ)j{z$0%X=vc5tWzSlRC6C%qVo$M+`FnH3Aq+S3CmIgwc80-hG=j zTp(T1`#!j!WV~2Zjwmg{$3?hlZ9-^PB$vZrf1%Ox^pW`EVT-gI<+^Jt_r*2orWnOPCba2n* z*Rhy3>7(NGt7|HLRv!=?58WHFd}V4SZAPZfV#*xqwcEVImtaQQEzR6P4rwRBz9^gM5$dV0t2Q*PBn0 z%~j8Rzt9pui>N0@x-QIaZ;KU>nbSh+>OFEeN^x19Vop?I=c^SPQ;wI_az8glNcsB7 zdg?#v0Z*xg@Rthn+xK70#w?z4Q{kbmYaO&~&10x~u^}n!`)^UfL;qIvk~z?e-;Fxi z8Bp3|4pWTObJvGj6DHKR`o6xG;b~2P&%#>ZP(9^3IWeXGaUH0MlHK#l;kEomML3<6 z{s?~^A598g_LrAPvX2_fC|q~KF!UWt%a}JIVGwPTcB0}!Vi)Py8aSM|eFRP$F#J&fNlW=*key>y zoFCxhKf{!kv8mA8e))%#0Q<393$UX{Ya3|u0A&?!1@X|swPDw5ev?gwi6%p}RLi!x z;eJACVyNmkcYj(-P2BUcR&vDsdN7d`q(lhnI~VrS_ailnjt+NkDcC0BKJoTY$w1_p znJ+OQA|p47I9%=?Oi~D7J49z;P+OL%jw+=t4WAX88!2Ip!j~bK#)wYQQh_iP-o8Xi z&oK6dlKDO@EG$J;)>0nyk*J%9`1$n4Q6F|Eq3_LJw{jHP6waMFkf~-9eZ?*dVlRnX z+UveAXsM3KiV9AE3zkx1OLb!wjGp-L>&ujVVK~DTI2SR}Ap1{~oQfXZBe7)ptd127 zjSJJl7l3wB9(X-n>#+5h`M>Lo-X*^G@Q;BL2Gn{~z_o8+p-&$8(aY0QsQd*L%r&Vr zV;lnl$K>g{4s&SXfB_p)5Jz{PO+NQpsR;1X(y4iQ-z(|A0}F+!nIN?RI?m1LZg?jZ zqNcif@AnIFnVLU)cLITgzjpsyBYSpv!Ms@9_*M!dID)-T;=MD%c24WIQp{k}D zA0KZL*tu$}RQ3KX;=4MMTmajq`zO+!lJv#t4pq|!TSwc2OS{jwhuw?)T_K+a`l>OY zK4#v0xueOyJBdt68+rp0y!@BHGV7Yp7BN)Wj2eY4c$r8dr+-tY4=x{+q{e<=uhz2C z_-+x&Q#j!fIyob5)Wlsui6FVr1>%ZKWakXd!SZ%0HJYlX(^LKSFC|+5i9m literal 0 HcmV?d00001 diff --git a/module/config/argument/args.json b/module/config/argument/args.json index 61de75e65..7e5fc8b74 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -427,6 +427,7 @@ "Argenti", "Arlan", "Asta", + "Aventurine", "Bailu", "BlackSwan", "Blade", @@ -1341,6 +1342,7 @@ "Argenti", "Arlan", "Asta", + "Aventurine", "Bailu", "BlackSwan", "Blade", diff --git a/module/config/config_generated.py b/module/config/config_generated.py index 31363dddb..54781a597 100644 --- a/module/config/config_generated.py +++ b/module/config/config_generated.py @@ -59,7 +59,7 @@ class GeneratedConfig: # Group `DungeonSupport` DungeonSupport_Use = 'when_daily' # always_use, when_daily, do_not_use - DungeonSupport_Character = 'FirstCharacter' # FirstCharacter, Acheron, Argenti, Arlan, Asta, Bailu, BlackSwan, Blade, Bronya, Clara, DanHeng, DanHengImbibitorLunae, DrRatio, FuXuan, Gallagher, Gepard, Guinaifen, Hanya, Herta, Himeko, Hook, Huohuo, JingYuan, Jingliu, Kafka, Luka, Luocha, Lynx, March7th, Misha, Natasha, Pela, Qingque, RuanMei, Sampo, Seele, Serval, SilverWolf, Sparkle, Sushang, Tingyun, TopazNumby, TrailblazerDestruction, TrailblazerPreservation, Welt, Xueyi, Yanqing, Yukong + DungeonSupport_Character = 'FirstCharacter' # FirstCharacter, Acheron, Argenti, Arlan, Asta, Aventurine, Bailu, BlackSwan, Blade, Bronya, Clara, DanHeng, DanHengImbibitorLunae, DrRatio, FuXuan, Gallagher, Gepard, Guinaifen, Hanya, Herta, Himeko, Hook, Huohuo, JingYuan, Jingliu, Kafka, Luka, Luocha, Lynx, March7th, Misha, Natasha, Pela, Qingque, RuanMei, Sampo, Seele, Serval, SilverWolf, Sparkle, Sushang, Tingyun, TopazNumby, TrailblazerDestruction, TrailblazerPreservation, Welt, Xueyi, Yanqing, Yukong # Group `DungeonStorage` DungeonStorage_TrailblazePower = {} diff --git a/module/config/config_updater.py b/module/config/config_updater.py index 3b83ed950..b5e9c8e13 100644 --- a/module/config/config_updater.py +++ b/module/config/config_updater.py @@ -100,7 +100,7 @@ class ConfigGenerator: options=[dungeon.name for dungeon in DungeonList.instances.values() if dungeon.is_Echo_of_War]) # Insert characters from tasks.character.keywords import CharacterList - unsupported_characters = ['Aventurine'] + unsupported_characters = [] characters = [character.name for character in CharacterList.instances.values() if character.name not in unsupported_characters] option_add(keys='DungeonSupport.Character.option', options=characters) diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index a062ac5ff..4639d1ff6 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -441,6 +441,7 @@ "Argenti": "Argenti", "Arlan": "Arlan", "Asta": "Asta", + "Aventurine": "Aventurine", "Bailu": "Bailu", "BlackSwan": "Black Swan", "Blade": "Blade", diff --git a/module/config/i18n/es-ES.json b/module/config/i18n/es-ES.json index 6130a4cce..d3c378f0c 100644 --- a/module/config/i18n/es-ES.json +++ b/module/config/i18n/es-ES.json @@ -441,6 +441,7 @@ "Argenti": "Argenti", "Arlan": "Arlan", "Asta": "Asta", + "Aventurine": "Aventurino", "Bailu": "Bailu", "BlackSwan": "Cisne Negro", "Blade": "Blade", diff --git a/module/config/i18n/ja-JP.json b/module/config/i18n/ja-JP.json index b2ff361a2..00da6dd94 100644 --- a/module/config/i18n/ja-JP.json +++ b/module/config/i18n/ja-JP.json @@ -441,6 +441,7 @@ "Argenti": "アルジェンティ", "Arlan": "アーラン", "Asta": "アスター", + "Aventurine": "アベンチュリン", "Bailu": "白露", "BlackSwan": "ブラックスワン", "Blade": "刃", diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index cf3a86232..e93f56d34 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -441,6 +441,7 @@ "Argenti": "银枝", "Arlan": "阿兰", "Asta": "艾丝妲", + "Aventurine": "砂金", "Bailu": "白露", "BlackSwan": "黑天鹅", "Blade": "刃", diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index a9afefdfb..75d160610 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -441,6 +441,7 @@ "Argenti": "銀枝", "Arlan": "阿蘭", "Asta": "艾絲妲", + "Aventurine": "砂金", "Bailu": "白露", "BlackSwan": "黑天鵝", "Blade": "刃", From 82f5e8b6ab929b645f1572d96ccef90184352e91 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Thu, 18 Apr 2024 22:55:53 +0800 Subject: [PATCH 025/114] Fix: site-packages detection on manual package builds --- module/device/method/utils.py | 2 +- module/device/pkg_resources/__init__.py | 37 +++++++++++++++++++++---- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/module/device/method/utils.py b/module/device/method/utils.py index 36d6bbd49..50b7ecc70 100644 --- a/module/device/method/utils.py +++ b/module/device/method/utils.py @@ -253,7 +253,7 @@ def remove_suffix(s, suffix): Returns: str, bytes: """ - return s[:len(suffix)] if s.endswith(suffix) else s + return s[:-len(suffix)] if s.endswith(suffix) else s def remove_shell_warning(s): diff --git a/module/device/pkg_resources/__init__.py b/module/device/pkg_resources/__init__.py index 61014ef79..b685a6d9a 100644 --- a/module/device/pkg_resources/__init__.py +++ b/module/device/pkg_resources/__init__.py @@ -3,6 +3,7 @@ import re import sys from module.base.decorator import cached_property +from module.logger import logger """ Importing pkg_resources is so slow, like 0.4 ~ 1.0s, just google it you will find it indeed really slow. @@ -18,7 +19,24 @@ _ = get_distribution ``` """ # Inject sys.modules, pretend we have pkg_resources imported -sys.modules['pkg_resources'] = sys.modules['module.device.pkg_resources'] +try: + sys.modules['pkg_resources'] = sys.modules['module.device.pkg_resources'] +except KeyError: + logger.error('Patch pkg_resources failed, patch module does not exists') + + +def remove_suffix(s, suffix): + """ + Remove suffix of a string or bytes like `string.removesuffix(suffix)`, which is on Python3.9+ + + Args: + s (str, bytes): + suffix (str, bytes): + + Returns: + str, bytes: + """ + return s[:-len(suffix)] if s.endswith(suffix) else s class FakeDistributionObject: @@ -50,11 +68,14 @@ class PackageCache: dic = {} for file in os.listdir(self.site_packages): # mxnet_cu101-1.6.0.dist-info - res = re.match(r'^(.+)-(.+)\.dist-info$', file) + # adbutils-0.11.0-py3.7.egg-info + res = re.match(r'^([a-zA-Z0-9._]+)-([a-zA-Z0-9._]+)-', file) if res: + version = remove_suffix(res.group(2), '.dist') + # version = res.group(2) obj = FakeDistributionObject( dist=res.group(1), - version=res.group(2), + version=version, ) dic[obj.dist] = obj @@ -73,9 +94,15 @@ def resource_filename(*args): def get_distribution(dist): """Return a current distribution object for a Requirement or string""" if dist == 'adbutils': - return PACKAGE_CACHE.dict_installed_packages.get('adbutils', '0.11.0') + return PACKAGE_CACHE.dict_installed_packages.get( + 'adbutils', + FakeDistributionObject('adbutils', '0.11.0'), + ) if dist == 'uiautomator2': - return PACKAGE_CACHE.dict_installed_packages.get('uiautomator2', '2.16.17') + return PACKAGE_CACHE.dict_installed_packages.get( + 'uiautomator2', + FakeDistributionObject('uiautomator2', '2.16.17'), + ) class DistributionNotFound(Exception): From fa209e1cf517ecc08479bce4c7255a48de7b2748 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Thu, 18 Apr 2024 23:00:49 +0800 Subject: [PATCH 026/114] Fix: Character trial ended at page_main --- route/daily/HimekoTrial.py | 8 +++++++- tasks/combat/skill.py | 6 +++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/route/daily/HimekoTrial.py b/route/daily/HimekoTrial.py index d5f77b433..8aee25967 100644 --- a/route/daily/HimekoTrial.py +++ b/route/daily/HimekoTrial.py @@ -16,7 +16,13 @@ class Route(RouteBase, Combat, CharacterTrial): def wait_next_skill(self, expected_end=None, skip_first_screenshot=True): # Ended at START_TRIAL def combat_end(): - return self.match_template_color(START_TRIAL) + if self.match_template_color(START_TRIAL): + logger.info('Trial ended at START_TRIAL') + return True + if self.is_in_main(): + logger.warning('Trial ended at is_in_main()') + return True + return False return super().wait_next_skill(expected_end=combat_end, skip_first_screenshot=skip_first_screenshot) diff --git a/tasks/combat/skill.py b/tasks/combat/skill.py index 35e8c05fa..081c9b44d 100644 --- a/tasks/combat/skill.py +++ b/tasks/combat/skill.py @@ -13,7 +13,7 @@ class CombatSkill(UI): if not self.appear(IN_SKILL): return False - if not self.image_color_count(IN_SKILL, color=(255, 255, 255), threshold=221, count=50): + if not self.image_color_count(IN_SKILL, color=(255, 255, 255), threshold=180, count=50): return False return True @@ -51,6 +51,10 @@ class CombatSkill(UI): logger.info(f'Skill used: {button} (icon changed)') break + if self.is_in_main(): + logger.warning('_skill_click ended at is_in_main') + break + def _is_skill_active(self, button): flag = self.image_color_count(button, color=(220, 196, 145), threshold=221, count=50) return flag From 13199daeb7c8c068317357f9ba4611f98a439096 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Thu, 18 Apr 2024 23:15:00 +0800 Subject: [PATCH 027/114] Fix: Special match Combat_Herta_SupplyZone_F2_X543Y255 --- tasks/rogue/route/loader.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tasks/rogue/route/loader.py b/tasks/rogue/route/loader.py index 6c6eff19a..f482633a3 100644 --- a/tasks/rogue/route/loader.py +++ b/tasks/rogue/route/loader.py @@ -166,6 +166,7 @@ class RouteLoader(RogueUI, MinimapWrapper, RouteLoader_, CharacterSwitch): # Before Combat_Herta_SupplyZone_F2_X45Y369 if route.name in [ + 'Combat_Herta_SupplyZone_F2_X543Y255', # 0.462, (543.3, 255.4) 'Combat_Luofu_DivinationCommission_F1_X737Y237', ] and similarity > 0.25: return True From 88b199fd6a671749bcf0cb22fa7851f4d3c56642 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Fri, 19 Apr 2024 00:08:49 +0800 Subject: [PATCH 028/114] Fix: Add route copy Occurrence_Luofu_Cloudford_F1_X244Y951 --- route/rogue/Occurrence/Luofu_Cloudford_F1.py | 26 ++++++++++++++++++++ route/rogue/route.json | 11 +++++++++ 2 files changed, 37 insertions(+) diff --git a/route/rogue/Occurrence/Luofu_Cloudford_F1.py b/route/rogue/Occurrence/Luofu_Cloudford_F1.py index c57a7ea90..5930d0de9 100644 --- a/route/rogue/Occurrence/Luofu_Cloudford_F1.py +++ b/route/rogue/Occurrence/Luofu_Cloudford_F1.py @@ -27,6 +27,32 @@ class Route(RouteBase): self.clear_event(event) # ===== End of generated waypoints ===== + @locked_rotation(270) + def Luofu_Cloudford_F1_X244Y951(self): + """ + | Waypoint | Position | Direction | Rotation | + | -------- | ------------------------- | --------- | -------- | + | spawn | Waypoint((241.4, 947.5)), | 274.2 | 274 | + | event | Waypoint((199.0, 940.8)), | 300.1 | 294 | + | exit_ | Waypoint((193.1, 947.2)), | 12.8 | 274 | + | exit1 | Waypoint((179.0, 956.4)), | 279.8 | 278 | + | exit2 | Waypoint((184.1, 940.2)), | 282.9 | 278 | + """ + self.map_init(plane=Luofu_Cloudford, floor="F1", position=(244, 951)) + self.register_domain_exit( + Waypoint((193.1, 947.2)), end_rotation=274, + left_door=Waypoint((179.0, 956.4)), right_door=Waypoint((184.1, 940.2))) + event = Waypoint((199.0, 940.8)) + + self.clear_event(event) + # ===== End of generated waypoints ===== + + """ + Notes + Luofu_Cloudford_F1_X244Y951 is the same as Luofu_Cloudford_F1_X241Y947 + but for wrong spawn point detected + """ + @locked_position @locked_rotation(0) def Luofu_Cloudford_F1_X281Y873(self): diff --git a/route/rogue/route.json b/route/rogue/route.json index 198c9be88..8df482bc9 100644 --- a/route/rogue/route.json +++ b/route/rogue/route.json @@ -1781,6 +1781,17 @@ ], "domain": "Occurrence" }, + { + "name": "Occurrence_Luofu_Cloudford_F1_X244Y951", + "route": "route.rogue.Occurrence.Luofu_Cloudford_F1:Luofu_Cloudford_F1_X244Y951", + "plane": "Luofu_Cloudford", + "floor": "F1", + "position": [ + 244.0, + 951.0 + ], + "domain": "Occurrence" + }, { "name": "Occurrence_Luofu_Cloudford_F1_X281Y873", "route": "route.rogue.Occurrence.Luofu_Cloudford_F1:Luofu_Cloudford_F1_X281Y873", From d4d1b46dfd2164a38737cd9a8eda2c0d9a0134b5 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Fri, 19 Apr 2024 00:34:05 +0800 Subject: [PATCH 029/114] Fix: screen2direction gets high DomainDoor when going downhills --- tasks/rogue/route/exit.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tasks/rogue/route/exit.py b/tasks/rogue/route/exit.py index 7f385dedd..f8ee3c319 100644 --- a/tasks/rogue/route/exit.py +++ b/tasks/rogue/route/exit.py @@ -112,6 +112,9 @@ class RogueExit(CombatInteract): distant_point = np.array((1509.46, 247.34)) name_y = 77.60 foot_y = 621.82 + if point < 80: + logger.warning(f'screen2direction: Point {point} to high') + point[1] = 80 door_projection_bottom = ( Points([point]).link(vanish_point).get_x(name_y)[0], @@ -129,6 +132,8 @@ class RogueExit(CombatInteract): door_projection_bottom[0] - screen_middle[0], door_projection_bottom[0] - door_distant[0], ) + if planar_door[1] < 0: + logger.warning('screen2direction: planer_door at back') if abs(planar_door[0]) < 5: direction = 0 else: From 9216d8bb1232b9777ba67a02e8d2f6a6759d3cb2 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Fri, 19 Apr 2024 00:41:38 +0800 Subject: [PATCH 030/114] Fix: Touch builders have no retries if called directly --- module/device/method/maatouch.py | 6 ++++++ module/device/method/minitouch.py | 1 + 2 files changed, 7 insertions(+) diff --git a/module/device/method/maatouch.py b/module/device/method/maatouch.py index 300e8ff9c..d1a97d04b 100644 --- a/module/device/method/maatouch.py +++ b/module/device/method/maatouch.py @@ -107,6 +107,7 @@ class MaaTouch(Connection): _maatouch_init_thread = None @cached_property + @retry def _maatouch_builder(self): self.maatouch_init() return MaatouchBuilder(self) @@ -272,3 +273,8 @@ class MaaTouch(Connection): builder.up().commit() builder.send() + + +if __name__ == '__main__': + self = MaaTouch('src') + self.maatouch_uninstall() \ No newline at end of file diff --git a/module/device/method/minitouch.py b/module/device/method/minitouch.py index 18a09009f..339c17cc1 100644 --- a/module/device/method/minitouch.py +++ b/module/device/method/minitouch.py @@ -373,6 +373,7 @@ class Minitouch(Connection): _minitouch_init_thread = None @cached_property + @retry def _minitouch_builder(self): self.minitouch_init() return CommandBuilder(self) From fc5dc22dfd448dab26c50c5dd67c554fb8bc3664 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Fri, 19 Apr 2024 18:03:55 +0800 Subject: [PATCH 031/114] Fix: Point in tuple cannot be compared to int --- tasks/rogue/route/exit.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasks/rogue/route/exit.py b/tasks/rogue/route/exit.py index f8ee3c319..d3fc611c4 100644 --- a/tasks/rogue/route/exit.py +++ b/tasks/rogue/route/exit.py @@ -112,9 +112,9 @@ class RogueExit(CombatInteract): distant_point = np.array((1509.46, 247.34)) name_y = 77.60 foot_y = 621.82 - if point < 80: + if point[1] < 80: logger.warning(f'screen2direction: Point {point} to high') - point[1] = 80 + point = (point[0], 80) door_projection_bottom = ( Points([point]).link(vanish_point).get_x(name_y)[0], From 2e7c39dcf4af88d3fe7025d39294e39b49ef81bc Mon Sep 17 00:00:00 2001 From: Small_Ku Date: Fri, 19 Apr 2024 20:10:07 +0800 Subject: [PATCH 032/114] Fix: Global MuMu 12 id (#424) --- module/device/platform/emulator_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/device/platform/emulator_base.py b/module/device/platform/emulator_base.py index 94483e9f9..394becc24 100644 --- a/module/device/platform/emulator_base.py +++ b/module/device/platform/emulator_base.py @@ -105,7 +105,7 @@ class EmulatorInstanceBase: Returns: int: Instance ID, or None if this is not a MuMu 12 instance """ - res = re.search(r'MuMuPlayer-12.0-(\d+)', self.name) + res = re.search(r'MuMuPlayer(?:Global)?-12.0-(\d+)', self.name) if res: return int(res.group(1)) res = re.search(r'YXArkNights-12.0-(\d+)', self.name) From cf1e61b06792a9ebcb6a1c282e0093835cd3fccd Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Fri, 19 Apr 2024 23:01:02 +0800 Subject: [PATCH 033/114] Fix: Cloud client should also be known packages --- module/device/connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/device/connection.py b/module/device/connection.py index 0a5f8aa2f..b95b4c5da 100644 --- a/module/device/connection.py +++ b/module/device/connection.py @@ -955,7 +955,7 @@ class Connection(ConnectionAttr): list[str]: List of package names """ packages = self.list_package(show_log=show_log) - packages = [p for p in packages if p in server_.VALID_PACKAGE] + packages = [p for p in packages if p in server_.VALID_PACKAGE or p in server_.VALID_CLOUD_PACKAGE] return packages def detect_package(self, set_config=True): From 62f7f38f102e83a5b5463b0f84cdfe724a30dcfa Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Sun, 21 Apr 2024 23:31:49 +0800 Subject: [PATCH 034/114] Fix: Special match Occurrence_Jarilo_RivetTown_F1_X157Y435 --- tasks/rogue/route/loader.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tasks/rogue/route/loader.py b/tasks/rogue/route/loader.py index f482633a3..f8a82813f 100644 --- a/tasks/rogue/route/loader.py +++ b/tasks/rogue/route/loader.py @@ -177,6 +177,11 @@ class RouteLoader(RogueUI, MinimapWrapper, RouteLoader_, CharacterSwitch): 'Combat_Herta_SupplyZone_F2_X45Y369', ] and similarity > 0.20: return True + # Before Occurrence_Luofu_DivinationCommission_F2_X425Y791 + if route.name in [ + 'Occurrence_Jarilo_RivetTown_F1_X157Y435', + ] and similarity > 0.15: + return True if route.name in [ 'Combat_Herta_StorageZone_F1_X273Y92', 'Occurrence_Herta_StorageZone_F1_X273Y93', From aab70b3808d63ab865a28c5230bae90402de65de Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Sun, 21 Apr 2024 23:35:43 +0800 Subject: [PATCH 035/114] Dep: [ALAS] Upgrade to MaaTouch 1.1.0 (cherry picked from commit 670541f51539d4f7ad29c4516277d09d91201684) --- bin/MaaTouch/maatouch | Bin 11535 -> 13775 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bin/MaaTouch/maatouch b/bin/MaaTouch/maatouch index 3673f3ff0aa57b2802d7af2666b0ed29512a1b4e..e164cdb89963d21fb08defb35f9759760eeb1944 100644 GIT binary patch delta 13502 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 C2gL6H delta 11241 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 From 96926412f65784e1af409b1604af1a4035de0de6 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Sun, 21 Apr 2024 23:36:23 +0800 Subject: [PATCH 036/114] Fix: [ALAS] Re-init MaaTouch when orientation changed (cherry picked from commit 3757510ecd22223daf94ae6bee91656af1190601) --- module/device/device.py | 10 ++++++++++ module/device/method/maatouch.py | 31 ++++++++++++++++++++++++++++++- module/device/method/minitouch.py | 9 +++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/module/device/device.py b/module/device/device.py index d9cdbf1f8..2435074ef 100644 --- a/module/device/device.py +++ b/module/device/device.py @@ -165,6 +165,16 @@ class Device(Screenshot, Control, AppControl): if self.config.Emulator_ScreenshotMethod == 'nemu_ipc': self.nemu_ipc_release() + def get_orientation(self): + """ + Callbacks when orientation changed. + """ + o = super().get_orientation() + + self.on_orientation_change_maatouch() + + return o + def stuck_record_add(self, button): self.detect_record.add(str(button)) diff --git a/module/device/method/maatouch.py b/module/device/method/maatouch.py index d1a97d04b..c61e82d24 100644 --- a/module/device/method/maatouch.py +++ b/module/device/method/maatouch.py @@ -102,9 +102,10 @@ class MaaTouch(Connection): """ max_x: int max_y: int - _maatouch_stream = socket.socket + _maatouch_stream: socket.socket = None _maatouch_stream_storage = None _maatouch_init_thread = None + _maatouch_orientation: int = None @cached_property @retry @@ -137,12 +138,40 @@ class MaaTouch(Connection): self._maatouch_init_thread = thread thread.start() + def on_orientation_change_maatouch(self): + """ + MaaTouch caches devices orientation at its startup + A restart is required when orientation changed + """ + if self._maatouch_orientation is None: + return + if self.orientation == self._maatouch_orientation: + return + + logger.info(f'Orientation changed {self._maatouch_orientation} => {self.orientation}, re-init MaaTouch') + del_cached_property(self, '_maatouch_builder') + self.early_maatouch_init() + def maatouch_init(self): logger.hr('MaaTouch init') max_x, max_y = 1280, 720 max_contacts = 2 max_pressure = 50 + # Try to close existing stream + if self._maatouch_stream is not None: + try: + self._maatouch_stream.close() + except Exception as e: + logger.error(e) + del self._maatouch_stream + if self._maatouch_stream_storage is not None: + del self._maatouch_stream_storage + + # MaaTouch caches devices orientation at its startup + super(MaaTouch, self).get_orientation() + self._maatouch_orientation = self.orientation + # CLASSPATH=/data/local/tmp/maatouch app_process / com.shxyke.MaaTouch.App stream = self.adb_shell( ['CLASSPATH=/data/local/tmp/maatouch', 'app_process', '/', 'com.shxyke.MaaTouch.App'], diff --git a/module/device/method/minitouch.py b/module/device/method/minitouch.py index 339c17cc1..2b9958a6f 100644 --- a/module/device/method/minitouch.py +++ b/module/device/method/minitouch.py @@ -409,6 +409,15 @@ class Minitouch(Connection): max_x, max_y = 1280, 720 max_contacts = 2 max_pressure = 50 + + # Try to close existing stream + if self._minitouch_client is not None: + try: + self._minitouch_client.close() + except Exception as e: + logger.error(e) + del self._minitouch_client + self.get_orientation() self._minitouch_port = self.adb_forward("localabstract:minitouch") From ef7214c5f056c1eccf3da338dd5e9e58b22ad5d2 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 22 Apr 2024 00:15:44 +0800 Subject: [PATCH 037/114] Fix: [ALAS] _minitouch_client is not default value (cherry picked from commit 1ddc7d936577281c2d9ed09ea04ac628d63ae590) --- module/device/method/minitouch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/device/method/minitouch.py b/module/device/method/minitouch.py index 2b9958a6f..32d9441d4 100644 --- a/module/device/method/minitouch.py +++ b/module/device/method/minitouch.py @@ -365,7 +365,7 @@ def retry(func): class Minitouch(Connection): _minitouch_port: int = 0 - _minitouch_client: socket.socket + _minitouch_client: socket.socket = None _minitouch_pid: int _minitouch_ws: websockets.WebSocketClientProtocol max_x: int From a8e2dab328b9671dbadeee7c171c5b43512f78b1 Mon Sep 17 00:00:00 2001 From: Jerry Lingjie Mei Date: Mon, 22 Apr 2024 10:17:55 -0500 Subject: [PATCH 038/114] Fix custom resonance filter errors. (#430) Co-authored-by: LmeSzinc --- tasks/rogue/blessing/blessing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/rogue/blessing/blessing.py b/tasks/rogue/blessing/blessing.py index a83db202f..42fdeb381 100644 --- a/tasks/rogue/blessing/blessing.py +++ b/tasks/rogue/blessing/blessing.py @@ -306,7 +306,7 @@ class RogueBlessingSelector(RogueSelector): "strategy_config": self.main.config.RogueBlessing_SelectionStrategy, "preset_values": { 'preset': RESONANCE_PRESET[self.main.config.RogueWorld_Path], - 'custom': self.main.config.RogueBlessing_PresetResonanceFilter, + 'custom': self.main.config.RogueBlessing_CustomResonanceFilter, }, } } From eb7086e8099bfe9e479813d10dc45ff89b2279ea Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 22 Apr 2024 23:32:53 +0800 Subject: [PATCH 039/114] Fix: [ALAS] Deploy config wasn't redirected after Updater.delay() --- deploy/Windows/config.py | 18 ++++++++++++------ module/webui/config.py | 2 ++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/deploy/Windows/config.py b/deploy/Windows/config.py index adf91ee67..6f42abba4 100644 --- a/deploy/Windows/config.py +++ b/deploy/Windows/config.py @@ -80,12 +80,6 @@ class DeployConfig(ConfigModel): self.config_template = {} self.read() - # Bypass webui.config.DeployConfig.__setattr__() - # Don't write these into deploy.yaml - super().__setattr__('GitOverCdn', self.Repository in ['cn']) - if self.Repository in ['global', 'cn']: - super().__setattr__('Repository', 'https://github.com/LmeSzinc/StarRailCopilot') - self.write() self.show_config() @@ -109,9 +103,21 @@ class DeployConfig(ConfigModel): if hasattr(self, key): super().__setattr__(key, value) + self.config_redirect() + def write(self): poor_yaml_write(self.config, self.file) + def config_redirect(self): + """ + Redirect deploy config, must be called after each `read()` + """ + # Bypass webui.config.DeployConfig.__setattr__() + # Don't write these into deploy.yaml + super().__setattr__('GitOverCdn', self.Repository in ['cn']) + if self.Repository in ['global', 'cn']: + super().__setattr__('Repository', 'https://github.com/LmeSzinc/StarRailCopilot') + def filepath(self, path): """ Args: diff --git a/module/webui/config.py b/module/webui/config.py index fd4d6ef12..0df5f0527 100644 --- a/module/webui/config.py +++ b/module/webui/config.py @@ -37,6 +37,8 @@ class DeployConfig(_DeployConfig): if hasattr(self, key): super().__setattr__(key, value) + self.config_redirect() + def write(self): """ Write `self.config` into deploy config. From 2f5dc378949d79ab65a2d83d961de8802ac7c7aa Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 22 Apr 2024 23:47:37 +0800 Subject: [PATCH 040/114] Fix: [ALAS] Use current python executable if the deploy.yaml one does not exist --- deploy/Windows/config.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/deploy/Windows/config.py b/deploy/Windows/config.py index 6f42abba4..5c10ab0a8 100644 --- a/deploy/Windows/config.py +++ b/deploy/Windows/config.py @@ -1,6 +1,7 @@ import copy import os import subprocess +import sys from typing import Optional, Union from deploy.Windows.logger import logger @@ -149,7 +150,7 @@ class DeployConfig(ConfigModel): if os.path.exists(exe): return exe - logger.warning(f'AdbExecutable: {exe} does not exists, use `adb` instead') + logger.warning(f'AdbExecutable: {exe} does not exist, use `adb` instead') return 'adb' @cached_property @@ -158,12 +159,18 @@ class DeployConfig(ConfigModel): if os.path.exists(exe): return exe - logger.warning(f'GitExecutable: {exe} does not exists, use `git` instead') + logger.warning(f'GitExecutable: {exe} does not exist, use `git` instead') return 'git' @cached_property def python(self) -> str: - return self.filepath(self.PythonExecutable) + exe = self.filepath(self.PythonExecutable) + if os.path.exists(exe): + return exe + + current = sys.executable.replace("\\", "/") + logger.warning(f'PythonExecutable: {exe} does not exist, use current python instead: {current}') + return current @cached_property def requirements_file(self) -> str: From b31724c92f5a8ce252c8b8e2bca169a7ea3b303b Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Tue, 23 Apr 2024 00:01:48 +0800 Subject: [PATCH 041/114] Fix: [ALAS] Old minitouch conn does not response to inputs after adb restarted --- module/device/method/minitouch.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/module/device/method/minitouch.py b/module/device/method/minitouch.py index 32d9441d4..30fdcc88c 100644 --- a/module/device/method/minitouch.py +++ b/module/device/method/minitouch.py @@ -314,12 +314,18 @@ def retry(func): def init(): self.adb_reconnect() + if self._minitouch_port: + self.adb_forward_remove(f'tcp:{self._minitouch_port}') + del_cached_property(self, '_minitouch_builder') # Emulator closed except ConnectionAbortedError as e: logger.error(e) def init(): self.adb_reconnect() + if self._minitouch_port: + self.adb_forward_remove(f'tcp:{self._minitouch_port}') + del_cached_property(self, '_minitouch_builder') # MinitouchNotInstalledError: Received empty data from minitouch except MinitouchNotInstalledError as e: logger.error(e) @@ -343,6 +349,9 @@ def retry(func): if handle_adb_error(e): def init(): self.adb_reconnect() + if self._minitouch_port: + self.adb_forward_remove(f'tcp:{self._minitouch_port}') + del_cached_property(self, '_minitouch_builder') else: break except BrokenPipeError as e: From 7c8d41614f2be056f8bc4b2108cf49c4b2055f81 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Thu, 25 Apr 2024 12:05:04 +0800 Subject: [PATCH 042/114] Fix: [CN] OCR error on Stagnant_Shadow_Puppetry --- tasks/dungeon/ui.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tasks/dungeon/ui.py b/tasks/dungeon/ui.py index 8bf8bd27c..c9eec1434 100644 --- a/tasks/dungeon/ui.py +++ b/tasks/dungeon/ui.py @@ -90,6 +90,8 @@ class OcrDungeonList(Ocr): # 燔灼之形•凝滞虚影 result = result.replace('熠', '燔') result = re.sub('^灼之形', '燔灼之形', result) + # 偃偶之形•凝滞虚影 + result = re.sub('^偶之形', '偃偶之形', result) # 蛀星的旧·历战余响 result = re.sub(r'蛀星的旧.*?历战', '蛀星的旧靥•历战', result) From d5f641aeb4c3455a9d1a367018b65ba80839aa7f Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Thu, 25 Apr 2024 19:05:22 +0800 Subject: [PATCH 043/114] Fix: [ALAS] Allow removing non-existent forwards and reverses --- module/device/connection.py | 41 ++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/module/device/connection.py b/module/device/connection.py index b95b4c5da..1fd5eb911 100644 --- a/module/device/connection.py +++ b/module/device/connection.py @@ -513,30 +513,51 @@ class Connection(ConnectionAttr): def adb_forward_remove(self, local): """ Equivalent to `adb -s forward --remove ` + No error raised when removing a non-existent forward + More about the commands send to ADB server, see: https://cs.android.com/android/platform/superproject/+/master:packages/modules/adb/SERVICES.TXT Args: local (str): Such as 'tcp:2437' """ - with self.adb_client._connect() as c: - list_cmd = f"host-serial:{self.serial}:killforward:{local}" - c.send_command(list_cmd) - c.check_okay() + try: + with self.adb_client._connect() as c: + list_cmd = f"host-serial:{self.serial}:killforward:{local}" + c.send_command(list_cmd) + c.check_okay() + except AdbError as e: + # No error raised when removing a non-existed forward + # adbutils.errors.AdbError: listener 'tcp:8888' not found + msg = str(e) + if re.search(r'listener .*? not found', msg): + logger.warning(f'{type(e).__name__}: {msg}') + else: + raise def adb_reverse_remove(self, local): """ Equivalent to `adb -s reverse --remove ` + No error raised when removing a non-existent reverse Args: local (str): Such as 'tcp:2437' """ - with self.adb_client._connect() as c: - c.send_command(f"host:transport:{self.serial}") - c.check_okay() - list_cmd = f"reverse:killforward:{local}" - c.send_command(list_cmd) - c.check_okay() + try: + with self.adb_client._connect() as c: + c.send_command(f"host:transport:{self.serial}") + c.check_okay() + list_cmd = f"reverse:killforward:{local}" + c.send_command(list_cmd) + c.check_okay() + except AdbError as e: + # No error raised when removing a non-existed forward + # adbutils.errors.AdbError: listener 'tcp:8888' not found + msg = str(e) + if re.search(r'listener .*? not found', msg): + logger.warning(f'{type(e).__name__}: {msg}') + else: + raise def adb_push(self, local, remote): """ From 871ea9a8094c7398a53d2b0e17d21066d1fe79b6 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Sat, 27 Apr 2024 00:24:15 +0800 Subject: [PATCH 044/114] Upd: Anniversary 3x relic event (#433) --- tasks/dungeon/dungeon.py | 2 +- tasks/dungeon/event.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tasks/dungeon/dungeon.py b/tasks/dungeon/dungeon.py index 94fb93bef..a2affe666 100644 --- a/tasks/dungeon/dungeon.py +++ b/tasks/dungeon/dungeon.py @@ -215,7 +215,7 @@ class Dungeon(DungeonStamina, DungeonEvent, Combat): calyx = self.get_double_event_remain() if self.has_double_relic_event(): self._dungeon_nav_goto(KEYWORDS_DUNGEON_NAV.Cavern_of_Corrosion) - relic = self.get_double_event_remain() + relic = self.get_double_rogue_remain() with self.config.multi_set(): self.config.stored.DungeonDouble.calyx = calyx self.config.stored.DungeonDouble.relic = relic diff --git a/tasks/dungeon/event.py b/tasks/dungeon/event.py index cde2c8951..bb93f1dce 100644 --- a/tasks/dungeon/event.py +++ b/tasks/dungeon/event.py @@ -32,6 +32,8 @@ class DungeonEvent(UI): """ has = self.image_color_count(DOUBLE_CALYX_EVENT_TAG, color=(252, 209, 123), threshold=221, count=50) has |= self.image_color_count(DOUBLE_CALYX_EVENT_TAG, color=(252, 251, 140), threshold=221, count=50) + # Anniversary 3x rogue event + has |= self.image_color_count(DOUBLE_CALYX_EVENT_TAG, color=(229, 62, 44), threshold=221, count=50) logger.attr('Double calyx', has) return has @@ -42,6 +44,8 @@ class DungeonEvent(UI): """ has = self.image_color_count(DOUBLE_RELIC_EVENT_TAG, color=(252, 209, 123), threshold=221, count=50) has |= self.image_color_count(DOUBLE_RELIC_EVENT_TAG, color=(252, 251, 140), threshold=221, count=50) + # Anniversary 3x rogue event + has |= self.image_color_count(DOUBLE_RELIC_EVENT_TAG, color=(229, 62, 44), threshold=221, count=50) logger.attr('Double relic', has) return has From c295919a019de6c34bd44ccc10481f3884dc1bcb Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Sat, 27 Apr 2024 00:26:42 +0800 Subject: [PATCH 045/114] Fix: Clear item enroute in Luofu_StargazerNavalia_F2_X627Y179 --- route/rogue/Combat/Luofu_StargazerNavalia_F2.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/route/rogue/Combat/Luofu_StargazerNavalia_F2.py b/route/rogue/Combat/Luofu_StargazerNavalia_F2.py index 597304409..90af3c630 100644 --- a/route/rogue/Combat/Luofu_StargazerNavalia_F2.py +++ b/route/rogue/Combat/Luofu_StargazerNavalia_F2.py @@ -70,6 +70,7 @@ class Route(RouteBase): # 1, enemy first self.clear_enemy(enemy1) - self.clear_item(item1) + # item1 is cleared on the way to enemy2, or will get stuck at corner + # self.clear_item(item1) # 2, ignore item2, bad way self.clear_enemy(enemy2) From a3ce30dc96c7bcd0ee242abc03867671b2d7b991 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Tue, 30 Apr 2024 00:50:58 +0800 Subject: [PATCH 046/114] Fix: VALID_CLOUD_PACKAGE uses wrong package list causing cloud game client undetected during emulator start --- module/config/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/config/server.py b/module/config/server.py index ee3fc23f0..899284329 100644 --- a/module/config/server.py +++ b/module/config/server.py @@ -18,7 +18,7 @@ VALID_PACKAGE = set(list(VALID_SERVER.values())) VALID_CLOUD_SERVER = { 'CN-Official': 'com.miHoYo.cloudgames.hkrpg', } -VALID_CLOUD_PACKAGE = set(list(VALID_SERVER.values())) +VALID_CLOUD_PACKAGE = set(list(VALID_CLOUD_SERVER.values())) def set_lang(lang_: str): From 3179cdc81d6b0064c2a231dcc08e7e06a99efa47 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Sun, 28 Apr 2024 17:12:03 +0800 Subject: [PATCH 047/114] Opt: [ALAS] Lazy import onepush (cherry picked from commit 63fd8b63f6574137a0ec0840440cab951f4f40df) --- module/notify/__init__.py | 4 ++++ module/{ => notify}/notify.py | 0 2 files changed, 4 insertions(+) create mode 100644 module/notify/__init__.py rename module/{ => notify}/notify.py (100%) diff --git a/module/notify/__init__.py b/module/notify/__init__.py new file mode 100644 index 000000000..58df2006e --- /dev/null +++ b/module/notify/__init__.py @@ -0,0 +1,4 @@ +def handle_notify(*args, **kwargs): + # Lazy import onepush + from module.notify.notify import handle_notify + return handle_notify(*args, **kwargs) diff --git a/module/notify.py b/module/notify/notify.py similarity index 100% rename from module/notify.py rename to module/notify/notify.py From 6818a9d86cb88d00fa90059c1191a6657b83494c Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 29 Apr 2024 23:41:32 +0800 Subject: [PATCH 048/114] Fix: [ALAS] Patch u2.init.appdir in runtime --- module/device/method/utils.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/module/device/method/utils.py b/module/device/method/utils.py index 50b7ecc70..b4845a382 100644 --- a/module/device/method/utils.py +++ b/module/device/method/utils.py @@ -1,3 +1,4 @@ +import os import random import re import socket @@ -5,6 +6,7 @@ import time import typing as t import uiautomator2 as u2 +import uiautomator2cache from adbutils import AdbTimeout from lxml import etree @@ -51,6 +53,9 @@ from module.logger import logger RETRY_TRIES = 5 RETRY_DELAY = 3 +# Patch uiautomator2 appdir +u2.init.appdir = os.path.dirname(uiautomator2cache.__file__) + def is_port_using(port_num): """ if port is using by others, return True. else return False """ From 24fbff17ef86d094195c69718908363c5834d237 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 29 Apr 2024 23:43:54 +0800 Subject: [PATCH 049/114] Chore: [ALAS] Add method uninstall_uiautomator2 --- module/device/method/uiautomator_2.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/module/device/method/uiautomator_2.py b/module/device/method/uiautomator_2.py index 765f7dde6..c116bce94 100644 --- a/module/device/method/uiautomator_2.py +++ b/module/device/method/uiautomator_2.py @@ -242,6 +242,17 @@ class Uiautomator2(Connection): hierarchy = etree.fromstring(content.encode('utf-8')) return hierarchy + def uninstall_uiautomator2(self): + logger.info('Removing uiautomator2') + for file in [ + 'app-uiautomator.apk', + 'app-uiautomator-test.apk', + 'minitouch', + 'minitouch.so', + 'atx-agent', + ]: + self.adb_shell(["rm", f"/data/local/tmp/{file}"]) + @retry def resolution_uiautomator2(self, cal_rotation=True) -> t.Tuple[int, int]: """ From f959b090f0fc86e72992980e6cb855671257300e Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Tue, 30 Apr 2024 00:01:33 +0800 Subject: [PATCH 050/114] Chore: [ALAS] Patch uiautomator2 loggers so they can be logged in Alas --- module/device/method/utils.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/module/device/method/utils.py b/module/device/method/utils.py index b4845a382..352d4361b 100644 --- a/module/device/method/utils.py +++ b/module/device/method/utils.py @@ -56,6 +56,22 @@ RETRY_DELAY = 3 # Patch uiautomator2 appdir u2.init.appdir = os.path.dirname(uiautomator2cache.__file__) +# Patch uiautomator2 logger +u2_logger = u2.logger +u2_logger.debug = logger.info +u2_logger.info = logger.info +u2_logger.warning = logger.warning +u2_logger.error = logger.error +u2_logger.critical = logger.critical + + +def setup_logger(*args, **kwargs): + return u2_logger + + +u2.setup_logger = setup_logger +u2.init.setup_logger = setup_logger + def is_port_using(port_num): """ if port is using by others, return True. else return False """ From aa40215cb8bb73da7abe5f9f07a4eabbd802658b Mon Sep 17 00:00:00 2001 From: Yinhr <110515845+Yinhaoran1128@users.noreply.github.com> Date: Thu, 2 May 2024 01:02:34 +0800 Subject: [PATCH 051/114] Fix: Anniversary 3x get remain at combat (#438) * Fix: Anniversary 3x get remain at combat * Fix: Anniversary 3x get remain at combat --- tasks/dungeon/event.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tasks/dungeon/event.py b/tasks/dungeon/event.py index bb93f1dce..291baec5e 100644 --- a/tasks/dungeon/event.py +++ b/tasks/dungeon/event.py @@ -71,6 +71,12 @@ class DungeonEvent(UI): color=(231, 188, 103), threshold=240, count=1000 ) + # Anniversary 3x event + has |= self.image_color_count( + OCR_DOUBLE_EVENT_REMAIN_AT_COMBAT, + color=(229, 62, 44), + threshold=221, count=50 + ) logger.attr('Double event at combat', has) return has From 0f33ba733e330d72c846dc89925a4f37a149cdaf Mon Sep 17 00:00:00 2001 From: Asxcvbn <63904802+Asxcvbn@users.noreply.github.com> Date: Tue, 7 May 2024 12:14:01 +0800 Subject: [PATCH 052/114] Update data_update.py for removing whitespace in credit ocr result (#441) * Update data_update.py for removing whitespace in credit ocr result sometimes ocr provides a str like '1 2261000' and conversion to int int('1 2261000') fails. we do a post process here, removing the whitespace and allow the conversion to be successful * Update data_update.py for removing whitespace in credit ocr result sometimes ocr provides a str like '1 2261000' and conversion to int int('1 2261000') fails. we do a post process here, removing the whitespace and allow the conversion to be successful edited to align with standards of this repo --- tasks/item/data_update.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tasks/item/data_update.py b/tasks/item/data_update.py index 40174d836..bd87efdb6 100644 --- a/tasks/item/data_update.py +++ b/tasks/item/data_update.py @@ -1,3 +1,5 @@ +import re + from module.base.timer import Timer from module.logger import logger from module.ocr.ocr import Digit @@ -19,7 +21,7 @@ class DataUpdate(UI): while 1: data = ocr.detect_and_ocr(self.device.image) if len(data) == 2: - credit, jade = [int(d.ocr_text) for d in data] + credit, jade = [int(re.sub(r'\s', '', d.ocr_text)) for d in data] if credit > 0 or jade > 0: break From 6a8a7d18c8232cfcb705e2e225dee6a98a015252 Mon Sep 17 00:00:00 2001 From: Zebartin <16185081+Zebartin@users.noreply.github.com> Date: Mon, 6 May 2024 21:36:29 +0800 Subject: [PATCH 053/114] Fix: Add handle_blessing_popup in blessing choosing --- tasks/rogue/blessing/blessing.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tasks/rogue/blessing/blessing.py b/tasks/rogue/blessing/blessing.py index 42fdeb381..416723e31 100644 --- a/tasks/rogue/blessing/blessing.py +++ b/tasks/rogue/blessing/blessing.py @@ -187,6 +187,9 @@ class RogueBlessingSelector(RogueSelector): else: self.main.device.screenshot() + if self.main.handle_blessing_popup(): + logger.warning('Mistakenly recognized current page as blessing choosing page, quit') + return if is_card_selected(self.main, target, confirm_button=BLESSING_CONFIRM): if enforce: logger.info("Buff selected (enforce)") From 74a2ccf2dc9b71d255d39c8b1a2de7f745ccbbef Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:22:36 +0800 Subject: [PATCH 054/114] Opt: [ALAS] Improve memory performance in utils --- module/base/utils/utils.py | 89 ++++++++++++++++++++++++++++++-------- 1 file changed, 70 insertions(+), 19 deletions(-) diff --git a/module/base/utils/utils.py b/module/base/utils/utils.py index 4f2abea70..c3d919136 100644 --- a/module/base/utils/utils.py +++ b/module/base/utils/utils.py @@ -625,17 +625,29 @@ def image_paste(image, background, origin): def rgb2gray(image): """ + gray = ( MAX(r, g, b) + MIN(r, g, b)) / 2 + Args: image (np.ndarray): Shape (height, width, channel) Returns: np.ndarray: Shape (height, width) """ + # r, g, b = cv2.split(image) + # return cv2.add( + # cv2.multiply(cv2.max(cv2.max(r, g), b), 0.5), + # cv2.multiply(cv2.min(cv2.min(r, g), b), 0.5) + # ) r, g, b = cv2.split(image) - return cv2.add( - cv2.multiply(cv2.max(cv2.max(r, g), b), 0.5), - cv2.multiply(cv2.min(cv2.min(r, g), b), 0.5) - ) + maximum = cv2.max(r, g) + cv2.max(maximum, b, dst=maximum) + cv2.convertScaleAbs(maximum, alpha=0.5, dst=maximum) + cv2.min(r, g, dst=r) + cv2.min(r, b, dst=r) + cv2.convertScaleAbs(r, alpha=0.5, dst=r) + # minimum = r + cv2.add(maximum, r, dst=maximum) + return maximum def rgb2hsv(image): @@ -791,11 +803,24 @@ def color_similarity_2d(image, color): Returns: np.ndarray: uint8 """ - r, g, b = cv2.split(cv2.subtract(image, (*color, 0))) - positive = cv2.max(cv2.max(r, g), b) - r, g, b = cv2.split(cv2.subtract((*color, 0), image)) - negative = cv2.max(cv2.max(r, g), b) - return cv2.subtract(255, cv2.add(positive, negative)) + # r, g, b = cv2.split(cv2.subtract(image, (*color, 0))) + # positive = cv2.max(cv2.max(r, g), b) + # r, g, b = cv2.split(cv2.subtract((*color, 0), image)) + # negative = cv2.max(cv2.max(r, g), b) + # return cv2.subtract(255, cv2.add(positive, negative)) + diff = cv2.subtract(image, (*color, 0)) + r, g, b = cv2.split(diff) + cv2.max(r, g, dst=r) + cv2.max(r, b, dst=r) + positive = r + cv2.subtract((*color, 0), image, dst=diff) + r, g, b = cv2.split(diff) + cv2.max(r, g, dst=r) + cv2.max(r, b, dst=r) + negative = r + cv2.add(positive, negative, dst=positive) + cv2.subtract(255, positive, dst=positive) + return positive def extract_letters(image, letter=(255, 255, 255), threshold=128): @@ -809,11 +834,24 @@ def extract_letters(image, letter=(255, 255, 255), threshold=128): Returns: np.ndarray: Shape (height, width) """ - r, g, b = cv2.split(cv2.subtract(image, (*letter, 0))) - positive = cv2.max(cv2.max(r, g), b) - r, g, b = cv2.split(cv2.subtract((*letter, 0), image)) - negative = cv2.max(cv2.max(r, g), b) - return cv2.multiply(cv2.add(positive, negative), 255.0 / threshold) + # r, g, b = cv2.split(cv2.subtract(image, (*letter, 0))) + # positive = cv2.max(cv2.max(r, g), b) + # r, g, b = cv2.split(cv2.subtract((*letter, 0), image)) + # negative = cv2.max(cv2.max(r, g), b) + # return cv2.multiply(cv2.add(positive, negative), 255.0 / threshold) + diff = cv2.subtract(image, (*letter, 0)) + r, g, b = cv2.split(diff) + cv2.max(r, g, dst=r) + cv2.max(r, b, dst=r) + positive = r + cv2.subtract((*letter, 0), image, dst=diff) + r, g, b = cv2.split(diff) + cv2.max(r, g, dst=r) + cv2.max(r, b, dst=r) + negative = r + cv2.add(positive, negative, dst=positive) + cv2.convertScaleAbs(positive, alpha=255.0 / threshold, dst=positive) + return positive def extract_white_letters(image, threshold=128): @@ -827,10 +865,21 @@ def extract_white_letters(image, threshold=128): Returns: np.ndarray: Shape (height, width) """ + # minimum = cv2.min(cv2.min(r, g), b) + # maximum = cv2.max(cv2.max(r, g), b) + # return cv2.multiply(cv2.add(maximum, cv2.subtract(maximum, minimum)), 255.0 / threshold) r, g, b = cv2.split(cv2.subtract((255, 255, 255, 0), image)) - minimum = cv2.min(cv2.min(r, g), b) - maximum = cv2.max(cv2.max(r, g), b) - return cv2.multiply(cv2.add(maximum, cv2.subtract(maximum, minimum)), 255.0 / threshold) + maximum = cv2.max(r, g) + cv2.max(maximum, b, dst=maximum) + cv2.convertScaleAbs(maximum, alpha=0.5, dst=maximum) + cv2.min(r, g, dst=r) + cv2.min(r, b, dst=r) + cv2.convertScaleAbs(r, alpha=0.5, dst=r) + minimum = r + cv2.subtract(maximum, minimum, dst=minimum) + cv2.add(maximum, minimum, dst=maximum) + cv2.convertScaleAbs(maximum, alpha=255.0 / threshold, dst=maximum) + return maximum def color_mapping(image, max_multiply=2): @@ -849,7 +898,9 @@ def color_mapping(image, max_multiply=2): low, high = np.min(image), np.max(image) multiply = min(255 / (high - low), max_multiply) add = (255 - multiply * (low + high)) / 2 - image = cv2.add(cv2.multiply(image, multiply), add) + # image = cv2.add(cv2.multiply(image, multiply), add) + cv2.multiply(image, multiply, dst=image) + cv2.add(image, add, dst=image) image[image > 255] = 255 image[image < 0] = 0 return image.astype(np.uint8) @@ -909,7 +960,7 @@ def color_bar_percentage(image, area, prev_color, reverse=False, starter=0, thre Returns: float: 0 to 1. """ - image = crop(image, area) + image = crop(image, area, copy=False) image = image[:, ::-1, :] if reverse else image length = image.shape[1] prev_index = starter From 2ae0f5e4b9b48a4d600e993c15b40b1c159c2d30 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Wed, 1 May 2024 23:40:17 +0800 Subject: [PATCH 055/114] Fix: [ALAS] insert_swipe() may return a length=1 list after deleting nearing points (cherry picked from commit 75333193ea561bd1f4241364c8660f6526bb5431) --- module/device/method/minitouch.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/module/device/method/minitouch.py b/module/device/method/minitouch.py index 30fdcc88c..356976c21 100644 --- a/module/device/method/minitouch.py +++ b/module/device/method/minitouch.py @@ -86,6 +86,8 @@ def insert_swipe(p0, p3, speed=15, min_distance=10): distance = np.linalg.norm(np.subtract(points[1:], points[0]), axis=1) mask = np.append(True, distance > min_distance) points = np.array(points)[mask].tolist() + if len(points) <= 1: + points = [p0, p3] else: points = [p0, p3] From 22dc2cfc1c815d21e4c373c0e0e0b6e00473b9c5 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Wed, 1 May 2024 23:59:09 +0800 Subject: [PATCH 056/114] Opt: [ALAS] Faster DroidCast_raw image parsing --- module/device/method/droidcast.py | 35 ++++++++++++++++++------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/module/device/method/droidcast.py b/module/device/method/droidcast.py index 3c7a97762..73c0f22b9 100644 --- a/module/device/method/droidcast.py +++ b/module/device/method/droidcast.py @@ -228,11 +228,21 @@ class DroidCast(Uiautomator2): if self.droidcast_height and self.droidcast_width: shape = (self.droidcast_height, self.droidcast_width) + rotate = self.is_mumu_over_version_356 and self.orientation == 1 + image = self.droidcast_session.get(self.droidcast_raw_url(), timeout=3).content # DroidCast_raw returns a RGB565 bitmap try: - arr = np.frombuffer(image, dtype=np.uint16).reshape(shape) + arr = np.frombuffer(image, dtype=np.uint16) + if rotate: + arr = arr.reshape(shape) + # arr = cv2.rotate(arr, cv2.ROTATE_90_CLOCKWISE) + # A little bit faster? + arr = cv2.transpose(arr) + cv2.flip(arr, 1, dst=arr) + else: + arr = arr.reshape(shape) except ValueError as e: if len(image) < 500: logger.warning(f'Unexpected screenshot: {image}') @@ -260,31 +270,26 @@ class DroidCast(Uiautomator2): # b = b.astype(np.uint8) # image = cv2.merge([r, g, b]) - # The same as the code above but costs about 5ms instead of 10ms. + # The same as the code above but costs about 3~4ms instead of 10ms. + # Note that cv2.convertScaleAbs is 5x fast as cv2.multiply, cv2.add is 8x fast as cv2.convertScaleAbs + # Note that cv2.convertScaleAbs includes rounding r = cv2.bitwise_and(arr, 0b1111100000000000) - cv2.multiply(r, 0.00390625, dst=r) - r = np.uint8(r) - m = cv2.multiply(r, 0.03125) + r = cv2.convertScaleAbs(r, alpha=0.00390625) + m = cv2.convertScaleAbs(r, alpha=0.03125) cv2.add(r, m, dst=r) g = cv2.bitwise_and(arr, 0b0000011111100000) - cv2.multiply(g, 0.125, dst=g) - g = np.uint8(g) - m = cv2.multiply(g, 0.015625) + g = cv2.convertScaleAbs(g, alpha=0.125) + m = cv2.convertScaleAbs(g, alpha=0.015625, dst=m) cv2.add(g, m, dst=g) b = cv2.bitwise_and(arr, 0b0000000000011111) - cv2.multiply(b, 8, dst=b) - b = np.uint8(b) - m = cv2.multiply(b, 0.03125) + b = cv2.convertScaleAbs(b, alpha=8) + m = cv2.convertScaleAbs(b, alpha=0.03125, dst=m) cv2.add(b, m, dst=b) image = cv2.merge([r, g, b]) - if self.is_mumu_over_version_356: - if self.orientation == 1: - image = cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE) - return image def droidcast_wait_startup(self): From 6e703ffb666fe9e2c4e7d42eee9fbd69f26451c7 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Tue, 7 May 2024 21:47:46 +0800 Subject: [PATCH 057/114] Fix: [ALAS] socket timeout on scrcpy server startup --- module/device/method/scrcpy/core.py | 41 ++++++++++++++++++++------- module/device/method/scrcpy/scrcpy.py | 17 ++++++++--- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/module/device/method/scrcpy/core.py b/module/device/method/scrcpy/core.py index aadb49bbd..499338b36 100644 --- a/module/device/method/scrcpy/core.py +++ b/module/device/method/scrcpy/core.py @@ -65,6 +65,8 @@ class ScrcpyCore(Connection): Raises: ScrcpyError: + adbutils.AdbTimeout: + socket.timeout: """ logger.hr('Scrcpy server start') commands = ScrcpyOptions.command_v120(jar_path=self.config.SCRCPY_FILEPATH_REMOTE) @@ -72,6 +74,7 @@ class ScrcpyCore(Connection): commands, stream=True, ) + self._scrcpy_server_stream.conn.settimeout(3) logger.info('Create server stream') ret = self._scrcpy_server_stream.read(10) @@ -104,6 +107,7 @@ class ScrcpyCore(Connection): self._scrcpy_video_socket = self.adb.create_connection( Network.LOCAL_ABSTRACT, "scrcpy" ) + self._scrcpy_video_socket.settimeout(3) break except AdbError: sleep(0.1) @@ -115,6 +119,7 @@ class ScrcpyCore(Connection): self._scrcpy_control_socket = self.adb.create_connection( Network.LOCAL_ABSTRACT, "scrcpy" ) + self._scrcpy_control_socket.settimeout(3) logger.info('Fetch device info') device_name = self._scrcpy_video_socket.recv(64).decode("utf-8").rstrip("\x00") @@ -151,23 +156,35 @@ class ScrcpyCore(Connection): # logger.error(err) self._scrcpy_alive = False - if self._scrcpy_server_stream is not None: - try: - self._scrcpy_server_stream.close() - except Exception: - pass + + if self._scrcpy_stream_loop_thread is not None: + self._scrcpy_stream_loop_thread.join(1) + del self._scrcpy_stream_loop_thread + self._scrcpy_stream_loop_thread = None if self._scrcpy_control_socket is not None: try: self._scrcpy_control_socket.close() - except Exception: - pass + except Exception as e: + logger.error(e) + del self._scrcpy_control_socket + self._scrcpy_control_socket = None if self._scrcpy_video_socket is not None: try: self._scrcpy_video_socket.close() - except Exception: - pass + except Exception as e: + logger.error(e) + del self._scrcpy_video_socket + self._scrcpy_video_socket = None + + if self._scrcpy_server_stream is not None: + try: + self._scrcpy_server_stream.close() + except Exception as e: + logger.error(e) + del self._scrcpy_server_stream + self._scrcpy_server_stream = None logger.info('Scrcpy server stopped') @@ -195,7 +212,8 @@ class ScrcpyCore(Connection): try: raw_h264 = self._scrcpy_video_socket.recv(0x10000) if raw_h264 == b"": - raise ScrcpyError("Video stream is disconnected") + if self._scrcpy_alive: + raise ScrcpyError("_scrcpy_stream_loop_thread: Video stream disconnected") packets = codec.parse(raw_h264) for packet in packets: frames = codec.decode(packet) @@ -212,5 +230,8 @@ class ScrcpyCore(Connection): if self._scrcpy_alive: logger.error(f'_scrcpy_stream_loop_thread: {repr(e)}') raise + except Exception as e: + logger.error(f'_scrcpy_stream_loop_thread exception: {repr(e)}') + raise raise ScrcpyError('_scrcpy_stream_loop stopped') diff --git a/module/device/method/scrcpy/scrcpy.py b/module/device/method/scrcpy/scrcpy.py index 50eff74f8..0730e9bac 100644 --- a/module/device/method/scrcpy/scrcpy.py +++ b/module/device/method/scrcpy/scrcpy.py @@ -1,15 +1,16 @@ +import socket import time from functools import wraps import numpy as np -from adbutils.errors import AdbError +from adbutils.errors import AdbError, AdbTimeout import module.device.method.scrcpy.const as const from module.base.utils import random_rectangle_point from module.device.method.minitouch import insert_swipe from module.device.method.scrcpy.core import ScrcpyCore, ScrcpyError from module.device.method.uiautomator_2 import Uiautomator2 -from module.device.method.utils import RETRY_TRIES, retry_sleep, handle_adb_error +from module.device.method.utils import RETRY_TRIES, handle_adb_error, retry_sleep from module.exception import RequestHumanTakeover from module.logger import logger @@ -19,7 +20,7 @@ def retry(func): def retry_wrapper(self, *args, **kwargs): """ Args: - self (Minitouch): + self (ScrcpyCore): """ init = None for _ in range(RETRY_TRIES): @@ -47,6 +48,13 @@ def retry(func): except ScrcpyError as e: logger.error(e) + def init(): + self.scrcpy_init() + # AdbTimeout + # socket.timeout + except (AdbTimeout, socket.timeout) as e: + logger.error(e) + def init(): self.scrcpy_init() # AdbError @@ -85,7 +93,8 @@ class Scrcpy(ScrcpyCore, Uiautomator2): now = time.time() while 1: time.sleep(0.001) - if self._scrcpy_stream_loop_thread is None or not self._scrcpy_stream_loop_thread.is_alive(): + thread = self._scrcpy_stream_loop_thread + if thread is None or not thread.is_alive(): raise ScrcpyError('_scrcpy_stream_loop_thread died') if self._scrcpy_last_frame_time > now: screenshot = self._scrcpy_last_frame.copy() From 9cf388b8e0a04c77549b1ff8d7765d5c19dc5f55 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Tue, 7 May 2024 22:28:54 +0800 Subject: [PATCH 058/114] Fix: Special match Occurrence_Luofu_DivinationCommission_F2_X149Y659 should before Occurrence_Luofu_DivinationCommission_F2_X425Y791 --- tasks/rogue/route/loader.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tasks/rogue/route/loader.py b/tasks/rogue/route/loader.py index f8a82813f..e8888c04f 100644 --- a/tasks/rogue/route/loader.py +++ b/tasks/rogue/route/loader.py @@ -180,6 +180,9 @@ class RouteLoader(RogueUI, MinimapWrapper, RouteLoader_, CharacterSwitch): # Before Occurrence_Luofu_DivinationCommission_F2_X425Y791 if route.name in [ 'Occurrence_Jarilo_RivetTown_F1_X157Y435', + # ('Occurrence_Luofu_DivinationCommission_F2_X149Y659', 0.237, (148.9, 658.8)), + # ('Occurrence_Luofu_DivinationCommission_F2_X425Y791', 0.11, (425.2, 793.8)) + 'Occurrence_Luofu_DivinationCommission_F2_X149Y659', ] and similarity > 0.15: return True if route.name in [ From 348c22cd35d542c81051b0479de371e6c17985a6 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Tue, 7 May 2024 23:03:40 +0800 Subject: [PATCH 059/114] Fix: Handle enemy during rotation_set() --- tasks/map/control/control.py | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/tasks/map/control/control.py b/tasks/map/control/control.py index 865e0aa64..90d752fa3 100644 --- a/tasks/map/control/control.py +++ b/tasks/map/control/control.py @@ -59,8 +59,11 @@ class MapControl(Combat, AimDetectorMixin): skip_first_screenshot: Returns: - bool: If swiped rotation + list[str]: A list of walk result + Enemy may attack character during rotation_set, function returns walk result """ + logger.hr('Rotation set') + result = [] interval = Timer(1, count=2) while 1: if skip_first_screenshot: @@ -70,15 +73,27 @@ class MapControl(Combat, AimDetectorMixin): self.minimap.update_rotation(self.device.image) self.minimap.log_minimap() - # End - if self.minimap.is_rotation_near(target, threshold=threshold): - logger.info(f'Rotation is now at: {target}') - break + # Additional + if self.is_combat_executing(): + logger.info('Walk result add: enemy') + result.append('enemy') + logger.hr('Combat', level=2) + self.combat_execute() + if self.walk_additional(): + continue - if interval.reached(): - if self.handle_rotation_set(target, threshold=threshold): - interval.reset() - continue + if self.is_in_main(): + # End + if self.minimap.is_rotation_near(target, threshold=threshold): + logger.info(f'Rotation is now at: {target}') + break + # Swipe + if interval.reached(): + if self.handle_rotation_set(target, threshold=threshold): + interval.reset() + continue + + return result def walk_additional(self) -> bool: """ From 66aa3c4848edce4b53a3e39f36a462a1ec8c26c8 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Tue, 7 May 2024 23:09:29 +0800 Subject: [PATCH 060/114] Fix: Special match Occurrence_Luofu_Cloudford_F1_X241Y947 --- tasks/rogue/route/loader.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tasks/rogue/route/loader.py b/tasks/rogue/route/loader.py index e8888c04f..cd9d2a197 100644 --- a/tasks/rogue/route/loader.py +++ b/tasks/rogue/route/loader.py @@ -168,6 +168,11 @@ class RouteLoader(RogueUI, MinimapWrapper, RouteLoader_, CharacterSwitch): if route.name in [ 'Combat_Herta_SupplyZone_F2_X543Y255', # 0.462, (543.3, 255.4) 'Combat_Luofu_DivinationCommission_F1_X737Y237', + # ('Occurrence_Luofu_Cloudford_F1_X241Y947', 0.307, (236.5, 949.6)), + # ('Occurrence_Luofu_Cloudford_F1_X244Y951', 0.307, (236.5, 949.6)), + # ('Occurrence_Jarilo_SilvermaneGuardRestrictedZone_F1_X509Y541', 0.154, (507.8, 515.2)) + 'Occurrence_Luofu_Cloudford_F1_X241Y947', + 'Occurrence_Luofu_Cloudford_F1_X244Y951', ] and similarity > 0.25: return True # Before Combat_Luofu_Cloudford_F1_X281Y873 From c581f427b565203e4b117aa8f49ed175938f706b Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Tue, 7 May 2024 23:18:59 +0800 Subject: [PATCH 061/114] Fix: Add copy of Herta_SupplyZone_F2_X550Y248 and Luofu_Cloudford_F1Rogue_X44Y405 for wrong spawn point detected --- route/rogue/Combat/Luofu_Cloudford_F1Rogue.py | 59 +++++++++++++++++++ route/rogue/Respite/Herta_SupplyZone_F2.py | 26 ++++++++ route/rogue/route.json | 22 +++++++ 3 files changed, 107 insertions(+) diff --git a/route/rogue/Combat/Luofu_Cloudford_F1Rogue.py b/route/rogue/Combat/Luofu_Cloudford_F1Rogue.py index e737dab7c..9b0f5e6b0 100644 --- a/route/rogue/Combat/Luofu_Cloudford_F1Rogue.py +++ b/route/rogue/Combat/Luofu_Cloudford_F1Rogue.py @@ -105,3 +105,62 @@ class Route(RouteBase): Luofu_Cloudford_F1Rogue_X49Y405 is the same as Luofu_Cloudford_F1Rogue_X59Y405 but for wrong spawn point detected """ + + def Luofu_Cloudford_F1Rogue_X44Y405(self): + """ + | Waypoint | Position | Direction | Rotation | + | ------------ | ------------------------- | --------- | -------- | + | spawn | Waypoint((59.3, 405.6)), | 96.7 | 91 | + | item1 | Waypoint((96.9, 393.0)), | 87.7 | 84 | + | enemy1 | Waypoint((126.2, 402.5)), | 96.8 | 101 | + | node2 | Waypoint((142.9, 413.0)), | 96.8 | 101 | + | enemy2top | Waypoint((214.6, 432.8)), | 94.1 | 87 | + | enemy2bottom | Waypoint((211.4, 483.3)), | 191.8 | 174 | + | enemy3 | Waypoint((288.0, 452.2)), | 87.7 | 260 | + | exit_ | Waypoint((291.8, 454.4)), | 5.7 | 91 | + | exit1 | Waypoint((295.0, 451.4)), | 96.7 | 89 | + | exit2 | Waypoint((296.0, 460.2)), | 96.9 | 89 | + """ + self.map_init(plane=Luofu_Cloudford, floor="F1Rogue", position=(43.8, 405.0)) + self.register_domain_exit( + Waypoint((291.8, 454.4)), end_rotation=91, + left_door=Waypoint((295.0, 451.4)), right_door=Waypoint((296.0, 460.2))) + item1 = Waypoint((96.9, 393.0)) + enemy1 = Waypoint((126.2, 402.5)) + node2 = Waypoint((142.9, 413.0)) + enemy2top = Waypoint((214.6, 432.8)) + enemy2bottom = Waypoint((211.4, 483.3)) + enemy3 = Waypoint((288.0, 452.2)) + # ===== End of generated waypoints ===== + + self.rotation_set(120) + self.minimap.lock_rotation(120) + # 1, ignore item1, which position may cause detection error + # self.clear_item(item1) + self.clear_enemy(enemy1) + # 2 moving enemy + # Ignore enemy2, it might be a pig, you can never catch it. + # self.clear_enemy( + # enemy2top, + # enemy2bottom.straight_run(), + # ) + # 3 + self.clear_enemy( + node2.set_threshold(3), + enemy3, + ) + if self.minimap.position_diff(enemy3.position) > 60: + logger.info('Cleared an enemy but have not reached enemy3') + self.clear_enemy(enemy3) + + """ + Notes + Luofu_Cloudford_F1Rogue_X44Y405 is the same as Luofu_Cloudford_F1Rogue_X59Y405 + but for wrong spawn point detected + """ + # Best 3 predictions: [ + # ('Combat_Luofu_Cloudford_F1Rogue_X59Y405', 0.349, (43.8, 405.0)), + # ('Combat_Luofu_Cloudford_F1Rogue_X49Y405', 0.349, (43.8, 405.0)), + # ('Combat_Herta_SupplyZone_F2_X594Y247', 0.262, (641.3, 250.5)) + # ] + # Best 3 nearby predictions: [('Combat_Jarilo_RivetTown_F1_X181Y439', 0.109, (178.5, 435.4))] diff --git a/route/rogue/Respite/Herta_SupplyZone_F2.py b/route/rogue/Respite/Herta_SupplyZone_F2.py index 93668ee33..9fe8041f0 100644 --- a/route/rogue/Respite/Herta_SupplyZone_F2.py +++ b/route/rogue/Respite/Herta_SupplyZone_F2.py @@ -23,3 +23,29 @@ class Route(RouteBase): self.domain_herta(herta) self.domain_single_exit(exit_) # ===== End of generated waypoints ===== + + def Herta_SupplyZone_F2_X550Y248(self): + """ + | Waypoint | Position | Direction | Rotation | + | -------- | ------------------------- | --------- | -------- | + | spawn | Waypoint((554.6, 245.0)), | 274.2 | 274 | + | item | Waypoint((541.9, 238.4)), | 308.0 | 301 | + | herta | Waypoint((506.8, 238.4)), | 283.0 | 281 | + | exit_ | Waypoint((495.0, 244.8)), | 283.0 | 274 | + """ + self.map_init(plane=Herta_SupplyZone, floor="F2", position=(550.7, 248.7)) + item = Waypoint((541.9, 238.4)) + herta = Waypoint((506.8, 238.4)) + exit_ = Waypoint((495.0, 244.8)) + + self.clear_item(item) + self.domain_herta(herta) + self.domain_single_exit(exit_) + # ===== End of generated waypoints ===== + + # Best 3 predictions: [('Respite_Herta_SupplyZone_F2_X554Y245', 0.257, (550.7, 248.7)), + """ + Notes + Herta_SupplyZone_F2_X550Y248 is the same as Herta_SupplyZone_F2_X554Y245 + but for wrong spawn point detected + """ diff --git a/route/rogue/route.json b/route/rogue/route.json index 8df482bc9..8d6cb46fe 100644 --- a/route/rogue/route.json +++ b/route/rogue/route.json @@ -912,6 +912,17 @@ ], "domain": "Combat" }, + { + "name": "Combat_Luofu_Cloudford_F1Rogue_X44Y405", + "route": "route.rogue.Combat.Luofu_Cloudford_F1Rogue:Luofu_Cloudford_F1Rogue_X44Y405", + "plane": "Luofu_Cloudford", + "floor": "F1Rogue", + "position": [ + 43.8, + 405.0 + ], + "domain": "Combat" + }, { "name": "Combat_Luofu_Cloudford_F2_X425Y111", "route": "route.rogue.Combat.Luofu_Cloudford_F2:Luofu_Cloudford_F2_X425Y111", @@ -1946,6 +1957,17 @@ ], "domain": "Respite" }, + { + "name": "Respite_Herta_SupplyZone_F2_X550Y248", + "route": "route.rogue.Respite.Herta_SupplyZone_F2:Herta_SupplyZone_F2_X550Y248", + "plane": "Herta_SupplyZone", + "floor": "F2", + "position": [ + 550.7, + 248.7 + ], + "domain": "Respite" + }, { "name": "Respite_Jarilo_BackwaterPass_F1_X581Y403", "route": "route.rogue.Respite.Jarilo_BackwaterPass_F1:Jarilo_BackwaterPass_F1_X581Y403", From 3679f00c13b0d3af36b3fc053344ab1b2a1d64b6 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Wed, 8 May 2024 00:03:10 +0800 Subject: [PATCH 062/114] Fix: Add route copy Luofu_ArtisanshipCommission_F1_X481Y920 --- .../Combat/Luofu_ArtisanshipCommission_F1.py | 45 +++++++++++++++++++ route/rogue/route.json | 11 +++++ 2 files changed, 56 insertions(+) diff --git a/route/rogue/Combat/Luofu_ArtisanshipCommission_F1.py b/route/rogue/Combat/Luofu_ArtisanshipCommission_F1.py index 54ee2e7f3..1be60db11 100644 --- a/route/rogue/Combat/Luofu_ArtisanshipCommission_F1.py +++ b/route/rogue/Combat/Luofu_ArtisanshipCommission_F1.py @@ -105,6 +105,51 @@ class Route(RouteBase): if self.minimap.position_diff(enemy3.position) > 25: self.clear_enemy(enemy3) + def Luofu_ArtisanshipCommission_F1_X481Y920(self): + """ + | Waypoint | Position | Direction | Rotation | + | ----------- | ------------------------- | --------- | -------- | + | spawn | Waypoint((473.5, 920.9)), | 4.5 | 4 | + | enemy1left | Waypoint((475.0, 848.4)), | 4.4 | 4 | + | enemy2right | Waypoint((493.5, 807.4)), | 157.1 | 48 | + | enemy3 | Waypoint((528.9, 782.9)), | 198.5 | 91 | + | exit_ | Waypoint((528.9, 782.9)), | 198.5 | 91 | + | exit1 | Waypoint((537.0, 773.2)), | 99.0 | 89 | + | exit2 | Waypoint((537.5, 790.6)), | 101.1 | 91 | + """ + self.map_init(plane=Luofu_ArtisanshipCommission, floor="F1", position=(481.5, 920.9)) + self.register_domain_exit( + Waypoint((528.9, 782.9)), end_rotation=91, + left_door=Waypoint((537.0, 773.2)), right_door=Waypoint((537.5, 790.6))) + enemy1left = Waypoint((475.0, 848.4)) + enemy2right = Waypoint((493.5, 807.4)) + enemy3 = Waypoint((528.9, 782.9)) + # ===== End of generated waypoints ===== + + self.rotation_set(30) + self.clear_enemy( + enemy1left, + enemy2right, + ) + self.clear_enemy(enemy3) + if self.minimap.position_diff(enemy3.position) > 25: + self.clear_enemy(enemy3) + + """ + Notes + Luofu_ArtisanshipCommission_F1_X481Y920 is the same as Luofu_ArtisanshipCommission_F1_X473Y920 + but for wrong spawn point detected + """ + # Best 3 predictions: [ + # ('Combat_Luofu_ArtisanshipCommission_F1_X473Y920', 0.168, (481.0, 920.9)), + # ('Combat_Luofu_ArtisanshipCommission_F1_X41Y640', 0.163, (40.8, 664.0)), + # ('Combat_Luofu_ArtisanshipCommission_F1_X667Y189', 0.14, (705.0, 193.3)) + # ] + # Best 3 nearby predictions: [ + # ('Combat_Herta_SupplyZone_F2_X45Y369', 0.128, (47.6, 369.4)), + # ('Combat_Herta_SupplyZone_F2Rogue_X219Y112', 0.093, (219.6, 108.7)) + # ] + def Luofu_ArtisanshipCommission_F1_X543Y269(self): """ | Waypoint | Position | Direction | Rotation | diff --git a/route/rogue/route.json b/route/rogue/route.json index 8d6cb46fe..11a629cd0 100644 --- a/route/rogue/route.json +++ b/route/rogue/route.json @@ -769,6 +769,17 @@ ], "domain": "Combat" }, + { + "name": "Combat_Luofu_ArtisanshipCommission_F1_X481Y920", + "route": "route.rogue.Combat.Luofu_ArtisanshipCommission_F1:Luofu_ArtisanshipCommission_F1_X481Y920", + "plane": "Luofu_ArtisanshipCommission", + "floor": "F1", + "position": [ + 481.5, + 920.9 + ], + "domain": "Combat" + }, { "name": "Combat_Luofu_ArtisanshipCommission_F1_X543Y269", "route": "route.rogue.Combat.Luofu_ArtisanshipCommission_F1:Luofu_ArtisanshipCommission_F1_X543Y269", From 4d086d1753b7f4219a369c2c721910c6d18942b1 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Wed, 8 May 2024 00:47:42 +0800 Subject: [PATCH 063/114] Opt: Wait rogue buttons loaded before clicking to prevent retries --- assets/share/rogue/path/PATH_LOADED_CHECK.png | Bin 0 -> 13145 bytes tasks/rogue/assets/assets_rogue_path.py | 10 ++++++++++ tasks/rogue/blessing/ui.py | 2 +- tasks/rogue/entry/entry.py | 3 +++ tasks/rogue/entry/path.py | 13 ++++++++++++- 5 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 assets/share/rogue/path/PATH_LOADED_CHECK.png diff --git a/assets/share/rogue/path/PATH_LOADED_CHECK.png b/assets/share/rogue/path/PATH_LOADED_CHECK.png new file mode 100644 index 0000000000000000000000000000000000000000..14e7eae5655dd32e97f2702685b17f49962244f8 GIT binary patch literal 13145 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 Date: Wed, 8 May 2024 00:49:55 +0800 Subject: [PATCH 064/114] Chore: [ALAS] Add Button.is_offset_in() --- module/base/button.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/module/base/button.py b/module/base/button.py index b0e057ee5..cae5fc18e 100644 --- a/module/base/button.py +++ b/module/base/button.py @@ -34,6 +34,23 @@ class Button(Resource): def clear_offset(self): self._button_offset = (0, 0) + def is_offset_in(self, x=0, y=0): + """ + Args: + x: + y: + + Returns: + bool: If _button_offset is in (-x, -y, x, y) + """ + if x: + if self._button_offset[0] < -x or self._button_offset[0] > x: + return False + if y: + if self._button_offset[1] < -y or self._button_offset[1] > y: + return False + return True + @cached_property def image(self): return load_image(self.file, self.area) @@ -295,6 +312,17 @@ class ButtonWrapper(Resource): for b in self.iter_buttons(): b.clear_offset() + def is_offset_in(self, x=0, y=0): + """ + Args: + x: + y: + + Returns: + bool: If _button_offset is in (-x, -y, x, y) + """ + return self.matched_button.is_offset_in(x=x, y=y) + def load_search(self, area): """ Set `search` attribute. From 043f07dbf04c31b37506b95392c9a6916870638a Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Wed, 8 May 2024 02:27:53 +0800 Subject: [PATCH 065/114] Upd: Add Aventurine adn ranged character --- tasks/character/keywords/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tasks/character/keywords/__init__.py b/tasks/character/keywords/__init__.py index cde9badfe..346ed08ad 100644 --- a/tasks/character/keywords/__init__.py +++ b/tasks/character/keywords/__init__.py @@ -9,6 +9,7 @@ DICT_SORTED_RANGES = { RuanMei, DanHengImbibitorLunae, Welt, + Aventurine, FuXuan, # Longer precast BlackSwan, From 7d9ae07e5eec2a150a1bc2cf36748538e35f53b0 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Wed, 8 May 2024 02:54:12 +0800 Subject: [PATCH 066/114] Fix: Retry ranged character switch if the first time failed --- tasks/character/switch.py | 28 +++++++++++++++++++++++----- tasks/rogue/route/loader.py | 4 ++-- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/tasks/character/switch.py b/tasks/character/switch.py index 6c625d9e9..7b36b6806 100644 --- a/tasks/character/switch.py +++ b/tasks/character/switch.py @@ -32,6 +32,7 @@ class CharacterSwitch(UI): characters: list[CharacterList] = [] character_current: CharacterList | None = None character_buttons: list[OcrResultButton] = [] + character_is_ranged: t.Optional[bool] = None def character_update(self, skip_first_screenshot=True) -> list[CharacterList]: """ @@ -187,7 +188,7 @@ class CharacterSwitch(UI): skip_first_screenshot: Returns: - bool: If chose + bool: If chose success """ logger.info(f'Character choose: {character}') if isinstance(character, int): @@ -220,8 +221,12 @@ class CharacterSwitch(UI): # End selected = self._update_current_character() if index in selected: - logger.info('Character chose') - return True + if len(selected) > 1: + logger.warning('Multiple selected characters found, cannot guarantee character selected') + return False + else: + logger.info('Character chose') + return True if count > 3: logger.warning('Failed to choose character, assume chose') return False @@ -255,22 +260,35 @@ class CharacterSwitch(UI): logger.info('No ranged characters in team') return False - def character_switch_to_ranged(self, update=True) -> bool: + def character_switch_to_ranged(self, update=True) -> bool | None: """ Args: update: If update characters before switching Returns: bool: If using a ranged character now + or None if failed to switch """ + if self.character_is_ranged is not None: + return self.character_is_ranged + logger.hr('Character switch to ranged') if update: self.character_update() character = self._get_ranged_character() if character is True: + self.character_is_ranged = True return True elif character is False: + self.character_is_ranged = False return False + + success = self.character_switch(character) + if success: + self.character_is_ranged = True + return True else: - return self.character_switch(character) + # Cannot switch, keep None to retry at next call + self.character_is_ranged = None + return None diff --git a/tasks/rogue/route/loader.py b/tasks/rogue/route/loader.py index cd9d2a197..7fa9e4f86 100644 --- a/tasks/rogue/route/loader.py +++ b/tasks/rogue/route/loader.py @@ -285,6 +285,7 @@ class RouteLoader(RogueUI, MinimapWrapper, RouteLoader_, CharacterSwitch): """ base = RouteBase(config=self.config, device=self.device, task=self.config.task.command) count = 1 + self.character_is_ranged = None while 1: if skip_first_screenshot: skip_first_screenshot = False @@ -293,8 +294,7 @@ class RouteLoader(RogueUI, MinimapWrapper, RouteLoader_, CharacterSwitch): logger.hr(f'Route run: {count}', level=1) base.clear_blessing() - if count == 1: - self.character_switch_to_ranged(update=True) + self.character_switch_to_ranged(update=True) self.route_run() # if not success: From 816c787ed01af2d3581dd35b5ec0fe31be0ccbc8 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Wed, 8 May 2024 13:32:47 +0800 Subject: [PATCH 067/114] Upd: ASSIGNMENT_CHECK --- assets/cn/base/page/ASSIGNMENT_CHECK.png | Bin 0 -> 7082 bytes assets/en/base/page/ASSIGNMENT_CHECK.png | Bin 0 -> 6242 bytes assets/share/base/page/ASSIGNMENT_CHECK.png | Bin 4742 -> 0 bytes tasks/base/assets/assets_base_page.py | 19 +++++++++++++------ 4 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 assets/cn/base/page/ASSIGNMENT_CHECK.png create mode 100644 assets/en/base/page/ASSIGNMENT_CHECK.png delete mode 100644 assets/share/base/page/ASSIGNMENT_CHECK.png diff --git a/assets/cn/base/page/ASSIGNMENT_CHECK.png b/assets/cn/base/page/ASSIGNMENT_CHECK.png new file mode 100644 index 0000000000000000000000000000000000000000..a063e299c91907ffc815cd94ec951b6aeb64a9e5 GIT binary patch literal 7082 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 literal 0 HcmV?d00001 diff --git a/assets/en/base/page/ASSIGNMENT_CHECK.png b/assets/en/base/page/ASSIGNMENT_CHECK.png new file mode 100644 index 0000000000000000000000000000000000000000..cc84e201c89d6145d5fe51dbb6ed8c7389c982c7 GIT binary patch literal 6242 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 diff --git a/tasks/base/assets/assets_base_page.py b/tasks/base/assets/assets_base_page.py index 7480b5ddf..0bb0d9dde 100644 --- a/tasks/base/assets/assets_base_page.py +++ b/tasks/base/assets/assets_base_page.py @@ -5,12 +5,19 @@ from module.base.button import Button, ButtonWrapper ASSIGNMENT_CHECK = ButtonWrapper( name='ASSIGNMENT_CHECK', - share=Button( - file='./assets/share/base/page/ASSIGNMENT_CHECK.png', - area=(45, 21, 70, 53), - search=(25, 1, 90, 73), - color=(162, 145, 112), - button=(45, 21, 70, 53), + cn=Button( + file='./assets/cn/base/page/ASSIGNMENT_CHECK.png', + area=(535, 165, 587, 181), + search=(515, 145, 607, 201), + color=(207, 199, 181), + button=(535, 165, 587, 181), + ), + en=Button( + file='./assets/en/base/page/ASSIGNMENT_CHECK.png', + area=(535, 165, 581, 180), + search=(515, 145, 601, 200), + color=(204, 195, 176), + button=(535, 165, 581, 180), ), ) BACK = ButtonWrapper( From d61924a42be1196872cb54652d6e4c97302d825f Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Wed, 8 May 2024 13:36:35 +0800 Subject: [PATCH 068/114] Upd: [CN] Assignment ui tab the font just changed --- .../ui/CHARACTER_MATERIALS_CHECK.png | Bin 8419 -> 8739 bytes .../ui/CHARACTER_MATERIALS_CLICK.png | Bin 8166 -> 8580 bytes .../ui/EXP_MATERIALS_CREDITS_CHECK.png | Bin 9332 -> 9056 bytes .../ui/EXP_MATERIALS_CREDITS_CLICK.png | Bin 9471 -> 8858 bytes .../ui/SYNTHESIS_MATERIALS_CHECK.png | Bin 8354 -> 8229 bytes .../ui/SYNTHESIS_MATERIALS_CLICK.png | Bin 8753 -> 8122 bytes .../assignment/assets/assets_assignment_ui.py | 48 +++++++++--------- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/assets/cn/assignment/ui/CHARACTER_MATERIALS_CHECK.png b/assets/cn/assignment/ui/CHARACTER_MATERIALS_CHECK.png index d35f8d96d80e57833daababc121a649bd710c1b4..d88e9ea6b1a9cc3f4d4a70478ecf8612998c26c7 100644 GIT binary patch literal 8739 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 bi;N7K^y6;h1RH-{lNXL0iix5hOu71RO5?)r diff --git a/assets/cn/assignment/ui/CHARACTER_MATERIALS_CLICK.png b/assets/cn/assignment/ui/CHARACTER_MATERIALS_CLICK.png index 41c3a87de224cfc0dc3362c99b7dbb2ee73968cd..542ef36bb3e52325a967fda1e0405b936753cc7b 100644 GIT binary patch literal 8580 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 diff --git a/assets/cn/assignment/ui/EXP_MATERIALS_CREDITS_CHECK.png b/assets/cn/assignment/ui/EXP_MATERIALS_CREDITS_CHECK.png index 19245e7cf3e805f31afd5286a9566a31ced4f365..bf97a78c98e57b3516fc4365592f936867ce230e 100644 GIT binary patch literal 9056 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 diff --git a/assets/cn/assignment/ui/EXP_MATERIALS_CREDITS_CLICK.png b/assets/cn/assignment/ui/EXP_MATERIALS_CREDITS_CLICK.png index 05b9782ab6321b90b57cb07644ef59318b9c5985..11f5ac0200ed3973b87de06e9c7cfce0ee5b779d 100644 GIT binary patch literal 8858 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 Cm9E18 literal 9471 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 Z_oFXX!}fmhSZ=VKIeqpQ^3UP7{s(RJL^%Ke diff --git a/assets/cn/assignment/ui/SYNTHESIS_MATERIALS_CHECK.png b/assets/cn/assignment/ui/SYNTHESIS_MATERIALS_CHECK.png index 26999de56ddb637f49d67cd1b844944c1ecb0263..cdd4856ae010352a8a76951837fced30b1c06809 100644 GIT binary patch literal 8229 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 literal 8354 zcmeHM`&UzEy54{S)*@O?WiUWo!>BX0YAq8YM4+}6bSXqpK)`^OmRo>;5CVh*M1}DJ zSZP561T7=CKmtODB!pP8fFcA05=aO^ZqbA!V6NuINxEj8e__^Hiy!uS_WQ%P_qX5u zeDCeKc_ui};gemT007`{^2GNc0I=D7YdvajYX&w>5M~CejF7-@0rH6Jd$VDa@$Jd; z_V)Jtx9~Z$xheiw&@ljLFWYLkY+1P_?L`|l^eKXyJlS}Mp< zhNEi+)}Z(L=i6>pRJdG9eef$}d_E-U;ic*Mdy#*YCCWbeK^9kcGVIpYxvA>d^6t|I zo0hIWSUO#?$hlwf?N0+|o&kr?-24fzG4+12C#<2#Kl@{Ln(QN9Zk&}H*Yt7>QPiPq zY+gdxnKuJ$PoR$M|MTVqu*r-Cm#lYN%uP;!l^J&52kb58@0-4{fc;*;+6>=(WwqU6 z7H%o9!1mJ{OE=u~U!{cVolgaZwHYV~C+1|tV`kSXKXvU47P$d!UmUhJbJbM%(@@3a zOm_zI?eiNo``7&y%flq$x=AC|Z{E6dqJ!`LguK<$K|enn4EO{_D(dp z=e*NDXwz8?A4Ny4`PCZjlehkYelTK2r)L7Ygk#?vthVN2N+n$yi|ic=tjX zL~|gfw49fF=N(Q% z!O9uyKLxHpj>-E$D6)jiz&{?i=WlqleDvdwh+hk29PUHosj)^T&O0Ugd2XaDg;UHjsuPLX8pmH%-`O}CB+!UJ|=B!lIW}lOqe@% z&C%~dF*rTbR{_2A8^|X!A&8iHc9`hfP*PS>ds=_en6>Y7S>M&wi*FW^i}p z(Xnp72PguKOO^T5qEB5y_^BOehrfLFP(71TM(|dw=@$L;oOt$8W-o3ipXvdyjfQFT z^DVVa?XhL8(k^U1B71BPdOQfqSLT5_jWEHSbn^Fq2AV%8GQ)uV*x)guPPga<^}oD9|9`-s`UWh^}=q~)kW!#l?d%Y0KWf*{b@>^d(1 zzvuxbSD{Q{IDHhf%fqp%X{0hbH$smso5o}!zD|5%X38;3N{AjK08@8XkcCq(ZaQ-d z8=NPSNUD~lAs?hnm0xa{If-;ovblf|yU`xN0i&d0-!B(p@y_7~Ohx@cE zN}P>zc9@AR3;yRS5nhkI_?hEs-f?GmGu751DNqU_BtC=tQXkfkv7^7&CIuQ0=p`;4 zA1CzU`F>n1Pnki!CO}Ats|`sm(y zkisd^kSk^%v=88;Y;2XNz$1Q!RS#+Di{d=7C9f4;urZ%T`su1H49r?y2R5Dv>X|!5 z)w?q$|FxqJ`C`?vMLCuJ;$QSR!=cId4KUTn1xp)a#J)j8e!CH zNSFJHx;%7*g`eM#^R?V}8n}^%WDl@Vc?)#iAAOG84z_PNORAebRqFEWb@ULTS`C^%wd?D}67=GZ+SI;S&-qdN zZ-Bwen|1?0W}=rWVP1I0h1Ig3Lf29JQkg1-G*cj2h>2aB|AU%5{b85)l?M99dJr$R z(h=8&@JK6xa2kmSiaA218npwMNu`TtQWDpb(59((C`J=U-9D$V-d3d;Ks}r}HP1>f z57*3KV}i8Lb5h!>A=oUVWV#mBATM?Cq%%eEpd*?Ux{QYuS9kbx`&r8(sA%|b>Pk9# z^qAemgLon*cn5LxLBOZLk2}mMiTgwhp@{ z+)q^=Kyr5`TVL!bm)nAR9tmE=zS(n*_j55r-iri%AVP6w8m;wLC23b3Cw4~lJZ5?& zO$1Xlv)C?%!OLp3aO=V328Da}Gn1+a+9I4)G~_DZorU`wGi%cOBcvbSJRVA#e{q}6 zJ-E_>dOFOBn!KLDZgPSjePrt>D=q1u6R~ofhKm@lr;31Q&8N%-T*0qLv=410=6D~i z);_<^<`!7l_+ZxCZ`|HF`DAIRIfj)lzStDmnRZsSGVNkD)e)bftcOx1fN8ZD!`TK) z4(F#bf*2r_lZ3!sCQAeZ1Ar`C!xk)4uS~`KW%klF2k|N#%$wyVTVI(;U*P?nCK>RHG{+|tEHdr5Lc~Tj z7-w;8CRH&~Cw8ixA}LPzqC1Mh#=>RPc%akD2Lr9eNTsOS-vXpmqp}&MuSL)YQ zk|%ti6XYhD&Z&6Z-Y(-TmX?ch%UivQ9=&SU`G1~gb=ZOP8gV#5u7oO48t0uIxeI0f zWBoL9=Gzjvy>)5F^NVB}QrP|DHOXEv-O@W)sbA~KBPvFl|u1HaayLSLX+L( zL=%mp=CfGAy4PAUpN0~RR}%c!n|zdQ9z0Rd{nd|IHGTcjSX_@}s-x6io9PxCjgmC(97^tgJ05_Hha~ z!!a`a*^aJR0Y-QZgnE*0PO=Ph@Hp+3H~2`p7bKs$GNj|sP)&7Z-elWtu4fho%8<3` zrp->4DonM&s1Ie~jY(1}H?=|ORaMlIF!$`LYOE~psT$OQ0;fj~Y!S9>O=| zuj8rfcs=KEAUFb-tC3xlMmw*L!rd-@SG=vs5;JQd+^{@%;O=*Sl36>9az{cp<#gA% zOwxXQd2d7@AT%+D`*)iwKMGT$%T9Una z@+3sVoiIac*ZX=%(!`*TuF*p*WL-&2dHS|U ziXpsz3f9dvdbxXXOMS$vS><(9Nnezk!SxpkU&gWw!`vPk-}=mSA*uTp;5Pj`s?5B^tMf{osEqlMoX zS8R-CHYQjb)4GkJ=EeYbV_3a0)8Ei9Hnf@zNozyC+fX?-6y5)S>h%wyMzBEHdC<`5 TSZ$Wwfs@CBzbAitsqlXRg9W80 diff --git a/assets/cn/assignment/ui/SYNTHESIS_MATERIALS_CLICK.png b/assets/cn/assignment/ui/SYNTHESIS_MATERIALS_CLICK.png index 4031171e0ec8f9ec723c520d957bbe0f94ec0312..b7a0ebbda527af2506c23b6ab09f0904d33b772c 100644 GIT binary patch literal 8122 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 literal 8753 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 diff --git a/tasks/assignment/assets/assets_assignment_ui.py b/tasks/assignment/assets/assets_assignment_ui.py index 572411124..da093ac84 100644 --- a/tasks/assignment/assets/assets_assignment_ui.py +++ b/tasks/assignment/assets/assets_assignment_ui.py @@ -7,10 +7,10 @@ CHARACTER_MATERIALS_CHECK = ButtonWrapper( name='CHARACTER_MATERIALS_CHECK', cn=Button( file='./assets/cn/assignment/ui/CHARACTER_MATERIALS_CHECK.png', - area=(346, 97, 421, 117), - search=(326, 77, 441, 137), - color=(177, 176, 173), - button=(346, 97, 421, 117), + area=(190, 98, 265, 120), + search=(170, 78, 285, 140), + color=(195, 194, 191), + button=(190, 98, 265, 120), ), en=[ Button( @@ -33,10 +33,10 @@ CHARACTER_MATERIALS_CLICK = ButtonWrapper( name='CHARACTER_MATERIALS_CLICK', cn=Button( file='./assets/cn/assignment/ui/CHARACTER_MATERIALS_CLICK.png', - area=(347, 97, 421, 117), - search=(327, 77, 441, 137), - color=(60, 60, 60), - button=(347, 97, 421, 117), + area=(190, 98, 265, 119), + search=(170, 78, 285, 139), + color=(64, 63, 61), + button=(190, 98, 265, 119), ), en=[ Button( @@ -86,10 +86,10 @@ EXP_MATERIALS_CREDITS_CHECK = ButtonWrapper( name='EXP_MATERIALS_CREDITS_CHECK', cn=Button( file='./assets/cn/assignment/ui/EXP_MATERIALS_CREDITS_CHECK.png', - area=(514, 97, 614, 117), - search=(494, 77, 634, 137), - color=(178, 177, 174), - button=(514, 97, 614, 117), + area=(420, 100, 518, 118), + search=(400, 80, 538, 138), + color=(170, 169, 166), + button=(420, 100, 518, 118), ), en=[ Button( @@ -112,10 +112,10 @@ EXP_MATERIALS_CREDITS_CLICK = ButtonWrapper( name='EXP_MATERIALS_CREDITS_CLICK', cn=Button( file='./assets/cn/assignment/ui/EXP_MATERIALS_CREDITS_CLICK.png', - area=(514, 97, 614, 117), - search=(494, 77, 634, 137), - color=(61, 60, 60), - button=(514, 97, 614, 117), + area=(420, 100, 518, 118), + search=(400, 80, 538, 138), + color=(64, 65, 64), + button=(420, 100, 518, 118), ), en=[ Button( @@ -229,10 +229,10 @@ SYNTHESIS_MATERIALS_CHECK = ButtonWrapper( name='SYNTHESIS_MATERIALS_CHECK', cn=Button( file='./assets/cn/assignment/ui/SYNTHESIS_MATERIALS_CHECK.png', - area=(708, 97, 783, 117), - search=(688, 77, 803, 137), - color=(180, 179, 176), - button=(708, 97, 783, 117), + area=(676, 98, 749, 119), + search=(656, 78, 769, 139), + color=(182, 181, 178), + button=(676, 98, 749, 119), ), en=[ Button( @@ -255,10 +255,10 @@ SYNTHESIS_MATERIALS_CLICK = ButtonWrapper( name='SYNTHESIS_MATERIALS_CLICK', cn=Button( file='./assets/cn/assignment/ui/SYNTHESIS_MATERIALS_CLICK.png', - area=(709, 97, 783, 117), - search=(689, 77, 803, 137), - color=(68, 66, 65), - button=(709, 97, 783, 117), + area=(676, 99, 749, 119), + search=(656, 79, 769, 139), + color=(57, 58, 57), + button=(676, 99, 749, 119), ), en=[ Button( From 5ce334ca8c72537aad541a09827a2eb62dfd96d4 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Wed, 8 May 2024 13:47:29 +0800 Subject: [PATCH 069/114] Fix: [EN] Handle omitted assignment names --- tasks/assignment/keywords/classes.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tasks/assignment/keywords/classes.py b/tasks/assignment/keywords/classes.py index cbb2cd008..566a5f29f 100644 --- a/tasks/assignment/keywords/classes.py +++ b/tasks/assignment/keywords/classes.py @@ -19,6 +19,16 @@ class AssignmentEntry(Keyword): def __hash__(self) -> int: return super().__hash__() + @classmethod + def _compare(cls, name, keyword): + # 2024.05.08 Assignment names are omitted in EN + if name == keyword: + return True + # namelesslandnameless.. Nameless Land, Nameless People + if name[:17] == keyword[:17]: + return True + return False + @dataclass(repr=False) class AssignmentEntryDetailed(Keyword): From a271bf6ba5206ae63883cff5504f4d87b30405b3 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Wed, 8 May 2024 13:55:14 +0800 Subject: [PATCH 070/114] Upd: Support reward assets --- .../support_reward/CAN_GET_REWARD.png | Bin 6845 -> 6836 bytes .../freebies/support_reward/IN_PROFILE.png | Bin 6899 -> 6896 bytes .../freebies/support_reward/REWARD_POPUP.png | Bin 0 -> 7735 bytes .../assets/assets_freebies_support_reward.py | 24 +++++++++++++----- tasks/freebies/support_reward.py | 14 +++++++--- 5 files changed, 28 insertions(+), 10 deletions(-) create mode 100644 assets/share/freebies/support_reward/REWARD_POPUP.png diff --git a/assets/share/freebies/support_reward/CAN_GET_REWARD.png b/assets/share/freebies/support_reward/CAN_GET_REWARD.png index afaf00938738b46dd44003cc2bf6fd66a93a2063..a7c64e3800f1488589a628451745633092380622 100644 GIT binary patch delta 1524 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 VEXOSN-n0M!002ovPDHLkV1h7bEK>jg diff --git a/assets/share/freebies/support_reward/REWARD_POPUP.png b/assets/share/freebies/support_reward/REWARD_POPUP.png new file mode 100644 index 0000000000000000000000000000000000000000..a4a0edcc93b08993259ad1278683c52eff1103bd GIT binary patch literal 7735 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 Date: Wed, 8 May 2024 16:03:34 +0800 Subject: [PATCH 071/114] Fix: Add route copy of Jarilo_BackwaterPass_F1_X611Y761 and Jarilo_SilvermaneGuardRestrictedZone_F1_X435Y233 --- .../Occurrence/Jarilo_BackwaterPass_F1.py | 32 +++++++++++++++++++ ...Jarilo_SilvermaneGuardRestrictedZone_F1.py | 32 +++++++++++++++++++ route/rogue/route.json | 22 +++++++++++++ 3 files changed, 86 insertions(+) diff --git a/route/rogue/Occurrence/Jarilo_BackwaterPass_F1.py b/route/rogue/Occurrence/Jarilo_BackwaterPass_F1.py index 35d322a9b..75fd8c9d6 100644 --- a/route/rogue/Occurrence/Jarilo_BackwaterPass_F1.py +++ b/route/rogue/Occurrence/Jarilo_BackwaterPass_F1.py @@ -67,3 +67,35 @@ class Route(RouteBase): self.clear_item(item) self.clear_event(event) # ===== End of generated waypoints ===== + + def Jarilo_BackwaterPass_F1_X611Y761(self): + """ + | Waypoint | Position | Direction | Rotation | + | -------- | ------------------------- | --------- | -------- | + | spawn | Waypoint((613.3, 755.7)), | 319.8 | 318 | + | item | Waypoint((603.0, 734.6)), | 342.6 | 343 | + | event | Waypoint((586.8, 724.7)), | 318.0 | 315 | + | exit_ | Waypoint((576.9, 728.6)), | 126.2 | 304 | + | exit1 | Waypoint((567.0, 732.7)), | 311.8 | 306 | + | exit2 | Waypoint((576.2, 722.0)), | 308.1 | 306 | + """ + self.map_init(plane=Jarilo_BackwaterPass, floor="F1", position=(611.4, 761.2)) + self.register_domain_exit( + Waypoint((576.9, 728.6)), end_rotation=304, + left_door=Waypoint((567.0, 732.7)), right_door=Waypoint((576.2, 722.0))) + item = Waypoint((603.0, 734.6)) + event = Waypoint((586.8, 724.7)) + + self.clear_item(item) + self.clear_event(event) + # ===== End of generated waypoints ===== + # Best 3 predictions: [ + # ('Occurrence_Jarilo_BackwaterPass_F1_X613Y755', 0.203, (611.4, 761.2)), + # ('Occurrence_Herta_SupplyZone_F2Rogue_X397Y223', 0.148, (381.4, 207.5)), + # ('Occurrence_Herta_SupplyZone_F2Rogue_X397Y227', 0.148, (381.4, 207.5)) + # ] + """ + Notes + Jarilo_BackwaterPass_F1_X611Y761 is the same as Jarilo_BackwaterPass_F1_X613Y755 + but for wrong spawn point detected + """ diff --git a/route/rogue/Occurrence/Jarilo_SilvermaneGuardRestrictedZone_F1.py b/route/rogue/Occurrence/Jarilo_SilvermaneGuardRestrictedZone_F1.py index 53bd2c1ea..3e91c22c6 100644 --- a/route/rogue/Occurrence/Jarilo_SilvermaneGuardRestrictedZone_F1.py +++ b/route/rogue/Occurrence/Jarilo_SilvermaneGuardRestrictedZone_F1.py @@ -49,6 +49,38 @@ class Route(RouteBase): self.clear_event(event) # ===== End of generated waypoints ===== + def Jarilo_SilvermaneGuardRestrictedZone_F1_X435Y233(self): + """ + | Waypoint | Position | Direction | Rotation | + | -------- | ------------------------- | --------- | -------- | + | spawn | Waypoint((439.3, 237.1)), | 354.1 | 348 | + | item | Waypoint((440.8, 215.2)), | 15.6 | 11 | + | event | Waypoint((434.8, 192.4)), | 355.9 | 359 | + | exit_ | Waypoint((428.6, 190.4)), | 76.4 | 338 | + | exit1 | Waypoint((416.8, 184.4)), | 337.5 | 334 | + | exit2 | Waypoint((428.8, 180.4)), | 339.1 | 336 | + """ + self.map_init(plane=Jarilo_SilvermaneGuardRestrictedZone, floor="F1", position=(435.5, 233.5)) + self.register_domain_exit( + Waypoint((428.6, 190.4)), end_rotation=338, + left_door=Waypoint((416.8, 184.4)), right_door=Waypoint((428.8, 180.4))) + item = Waypoint((440.8, 215.2)) + event = Waypoint((434.8, 192.4)) + + self.clear_item(item) + self.clear_event(event) + # ===== End of generated waypoints ===== + # Best 3 predictions: [ + # ('Occurrence_Jarilo_SilvermaneGuardRestrictedZone_F1_X439Y237', 0.194, (435.5, 233.5)), + # ('Occurrence_Luofu_ScalegorgeWaterscape_F1_X619Y387', 0.118, (593.5, 412.0)), + # ('Occurrence_Luofu_Cloudford_F1_X244Y951', 0.098, (193.8, 931.7)) + # ] + """ + Notes + Jarilo_SilvermaneGuardRestrictedZone_F1_X435Y233 is the same as Jarilo_SilvermaneGuardRestrictedZone_F1_X439Y237 + but for wrong spawn point detected + """ + def Jarilo_SilvermaneGuardRestrictedZone_F1_X509Y541(self): """ | Waypoint | Position | Direction | Rotation | diff --git a/route/rogue/route.json b/route/rogue/route.json index 11a629cd0..2987b3112 100644 --- a/route/rogue/route.json +++ b/route/rogue/route.json @@ -1594,6 +1594,17 @@ ], "domain": "Occurrence" }, + { + "name": "Occurrence_Jarilo_BackwaterPass_F1_X611Y761", + "route": "route.rogue.Occurrence.Jarilo_BackwaterPass_F1:Jarilo_BackwaterPass_F1_X611Y761", + "plane": "Jarilo_BackwaterPass", + "floor": "F1", + "position": [ + 611.4, + 761.2 + ], + "domain": "Occurrence" + }, { "name": "Occurrence_Jarilo_CorridorofFadingEchoes_F1_X236Y903", "route": "route.rogue.Occurrence.Jarilo_CorridorofFadingEchoes_F1:Jarilo_CorridorofFadingEchoes_F1_X236Y903", @@ -1715,6 +1726,17 @@ ], "domain": "Occurrence" }, + { + "name": "Occurrence_Jarilo_SilvermaneGuardRestrictedZone_F1_X435Y233", + "route": "route.rogue.Occurrence.Jarilo_SilvermaneGuardRestrictedZone_F1:Jarilo_SilvermaneGuardRestrictedZone_F1_X435Y233", + "plane": "Jarilo_SilvermaneGuardRestrictedZone", + "floor": "F1", + "position": [ + 435.5, + 233.5 + ], + "domain": "Occurrence" + }, { "name": "Occurrence_Jarilo_SilvermaneGuardRestrictedZone_F1_X509Y541", "route": "route.rogue.Occurrence.Jarilo_SilvermaneGuardRestrictedZone_F1:Jarilo_SilvermaneGuardRestrictedZone_F1_X509Y541", From 50ae72d17a7678b3d9aa44cc9976a5e9418d446d Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Wed, 8 May 2024 16:22:47 +0800 Subject: [PATCH 072/114] Upd: 2.2 Keyword extract --- dev_tools/keywords/base.py | 3 +- dev_tools/keywords/dungeon_list.py | 7 +- module/ocr/keyword.py | 2 +- tasks/assignment/keywords/__init__.py | 48 +-- tasks/assignment/keywords/event_entry.py | 336 ++++++++++----------- tasks/character/keywords/character_list.py | 107 ++++--- tasks/dungeon/keywords/dungeon.py | 214 +++++++------ tasks/dungeon/keywords/dungeon_detailed.py | 9 + tasks/map/keywords/plane.py | 33 ++ tasks/rogue/keywords/event_option.py | 8 +- 10 files changed, 436 insertions(+), 331 deletions(-) diff --git a/dev_tools/keywords/base.py b/dev_tools/keywords/base.py index e44f0ac61..f9c147b3f 100644 --- a/dev_tools/keywords/base.py +++ b/dev_tools/keywords/base.py @@ -63,7 +63,8 @@ class TextMap: def text_to_variable(text): text = re.sub("'s |s' ", '_', text) - text = re.sub(r'[ \-—:\'/•.]+', '_', text) + text = re.sub(r'[ \-—–:\'/•.™]+', '_', text) + text = re.sub(r'[█]+', '', text) text = re.sub(r'[(),#"?!&%*]|', '', text) # text = re.sub(r'[#_]?\d+(_times?)?', '', text) text = re.sub(r'', '', text) diff --git a/dev_tools/keywords/dungeon_list.py b/dev_tools/keywords/dungeon_list.py index d18451619..35221f9da 100644 --- a/dev_tools/keywords/dungeon_list.py +++ b/dev_tools/keywords/dungeon_list.py @@ -16,6 +16,8 @@ def dungeon_name(name: str) -> str: name = f'Echo_of_War_{name}' if name in ['The_Swarm_Disaster', 'Gold_and_Gears']: name = f'Simulated_Universe_{name}' + name = name.replace('Stagnant_Shadow_Stagnant_Shadow', 'Stagnant_Shadow') + name = name.replace('Cavern_of_Corrosion_Cavern_of_Corrosion', 'Cavern_of_Corrosion') return name @@ -70,7 +72,10 @@ class GenerateDungeonList(GenerateKeyword): if text.startswith('Calyx_Crimson'): plane = MapPlane.find_plane_id(keyword['plane_id']) - text = f'{text}_{plane.name}' + if plane is not None: + text = f'{text}_{plane.name}' + else: + text = f'{text}_unknown_plane' return text def convert_keyword(self, text: str, lang: str) -> str: diff --git a/module/ocr/keyword.py b/module/ocr/keyword.py index 3593b1d46..82fcdf369 100644 --- a/module/ocr/keyword.py +++ b/module/ocr/keyword.py @@ -7,7 +7,7 @@ import module.config.server as server from module.exception import ScriptError # ord('.') = 65294 -REGEX_PUNCTUATION = re.compile(r'[ ,..\'"“”,。…::;;!!??·・•●〇°*※\-—-/\\\n\t()\[\]()「」『』【】《》[]]') +REGEX_PUNCTUATION = re.compile(r'[ ,..\'"“”,。…::;;!!??·・•●〇°*※\-—–-/\\\n\t()\[\]()「」『』【】《》[]]') def parse_name(n): diff --git a/tasks/assignment/keywords/__init__.py b/tasks/assignment/keywords/__init__.py index ab95755a7..eec743922 100644 --- a/tasks/assignment/keywords/__init__.py +++ b/tasks/assignment/keywords/__init__.py @@ -31,30 +31,30 @@ KEYWORDS_ASSIGNMENT_GROUP.Synthesis_Materials.entries = ( KEYWORDS_ASSIGNMENT_ENTRY.Scalpel_and_Screwdriver, ) KEYWORDS_ASSIGNMENT_EVENT_GROUP.Space_Station_Task_Force.entries = ( - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Repulsion_Bridge_Errors, - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Meal_Delivery_Robot_Check_Up, - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Noise_Complaint, - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Interior_Temperature_Modulator, - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Researcher_Health_Reports, - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Confidential_Investigation, - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Borrowed_Equipment, - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Booking_System, - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Non_Digital_Documents, - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Drip_Feed_Errors, - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Pet_Movement_Route_Planning, - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Food_Improvement_Plan, - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Curio_Distribution, - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Super_Urgent_Waiting_Online, - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Ventilation_Problem, - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Unstable_Connection, - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Chronology_Checks, - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Supply_Chain_Management, - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Malicious_Occupation_of_Public_Space, - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Uniform_Material, - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Virus_Re_creation_Report, - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Abnormal_Signal, - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Flexible_Working_Approval, - KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Lighting_Issue, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Activate_Genetic_Samples, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Reproduce_Experimental_Data, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Burned_Warehouse, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Car_Thief, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Synesthesia_Beacon_Function_Iteration, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Hunters_Wanted_No_Newbies_Please, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Peaceful_Life_for_Good_People, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Closed_Beta_Test_Recruitment, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Protect_Digital_Exhibits, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Final_Survivor, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Water_Pollution_Control, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Hook_Line_and_Sinker, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Investigation_Order_Boothill, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Volunteers_Wanted, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Burn_Treatment, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.I_Want_to_Speak_to_Your_Manager, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Licensed_Product_Damage_Assessment, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Annoying_Flies, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Urgent_Protection_Services, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.A_Dream_Is_Born, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Wanted_Boothill, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Casual_Cowboy, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Dangerous_Journey, + KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Crossing_the_Fire_Line, ) for group in ( KEYWORDS_ASSIGNMENT_GROUP.Character_Materials, diff --git a/tasks/assignment/keywords/event_entry.py b/tasks/assignment/keywords/event_entry.py index d6dec29c9..9dd113804 100644 --- a/tasks/assignment/keywords/event_entry.py +++ b/tasks/assignment/keywords/event_entry.py @@ -3,219 +3,219 @@ from .classes import AssignmentEventEntry # This file was auto-generated, do not modify it manually. To generate: # ``` python -m dev_tools.keyword_extract ``` -Repulsion_Bridge_Errors = AssignmentEventEntry( +Activate_Genetic_Samples = AssignmentEventEntry( id=1, - name='Repulsion_Bridge_Errors', - cn='斥力桥报错', - cht='斥力橋錯誤', - en='Repulsion Bridge Errors', - jp='斥力ブリッジエラー', - es='Reporte de error del puente de rechazo', + name='Activate_Genetic_Samples', + cn='激活基因样本', + cht='激發基因樣本', + en='Activate Genetic Samples', + jp='遺伝子サンプルの活性化', + es='Activación de muestras genéticas', ) -Meal_Delivery_Robot_Check_Up = AssignmentEventEntry( +Reproduce_Experimental_Data = AssignmentEventEntry( id=2, - name='Meal_Delivery_Robot_Check_Up', - cn='送餐机器人检修', - cht='送餐機器人檢修', - en='Meal-Delivery Robot Check-Up', - jp='配膳ロボット点検修理', - es='Mantenimiento de los robots de reparto de comida', + name='Reproduce_Experimental_Data', + cn='复现实验数据', + cht='重現實驗數據', + en='Reproduce Experimental Data', + jp='実験データの復元', + es='Recreación de datos experimentales', ) -Noise_Complaint = AssignmentEventEntry( +Burned_Warehouse = AssignmentEventEntry( id=3, - name='Noise_Complaint', - cn='噪音投诉问题', - cht='噪音投訴問題', - en='Noise Complaint', - jp='騒音苦情問題', - es='Quejas por ruidos', + name='Burned_Warehouse', + cn='仓库被烧', + cht='倉庫被燒', + en='Burned Warehouse', + jp='燃えた倉庫', + es='Almacén incinerado', ) -Interior_Temperature_Modulator = AssignmentEventEntry( +Car_Thief = AssignmentEventEntry( id=4, - name='Interior_Temperature_Modulator', - cn='室内温度调节器', - cht='室內溫度調節器', - en='Interior Temperature Modulator', - jp='室内温度調節器', - es='Regulador de temperatura ambiental', + name='Car_Thief', + cn='███的偷车贼!', + cht='███的偷車賊!', + en='███████ Car Thief!', + jp='███の車泥棒!', + es='¡████ ladrón de autos!', ) -Researcher_Health_Reports = AssignmentEventEntry( +Synesthesia_Beacon_Function_Iteration = AssignmentEventEntry( id=5, - name='Researcher_Health_Reports', - cn='科员的体检报告', - cht='組員的體檢報告', - en="Researchers' Health Reports", - jp='スタッフの健康診断報告', - es='Informe médico de los investigadores', + name='Synesthesia_Beacon_Function_Iteration', + cn='联觉信标功能迭代', + cht='聯覺信標功能更新', + en='Synesthesia Beacon Function Iteration', + jp='共感覚ビーコン機能アップデート', + es='Iteración de funciones de la baliza sinestésica', ) -Confidential_Investigation = AssignmentEventEntry( +Hunters_Wanted_No_Newbies_Please = AssignmentEventEntry( id=6, - name='Confidential_Investigation', - cn='秘密调查行动', - cht='秘密調查行動', - en='Confidential Investigation', - jp='秘密裏の調査', - es='Investigación encubierta', + name='Hunters_Wanted_No_Newbies_Please', + cn='来猎人,菜鸟勿扰', + cht='來獵人,菜鳥勿擾', + en='Hunters Wanted, No Newbies Please', + jp='プロハンター歓迎、初心者お断り', + es='Se necesitan cazadores. Novatos abstenerse', ) -Borrowed_Equipment = AssignmentEventEntry( +Peaceful_Life_for_Good_People = AssignmentEventEntry( id=7, - name='Borrowed_Equipment', - cn='实验器械借用', - cht='實驗器械借用', - en='Borrowed Equipment', - jp='実験機器借用', - es='Préstamo de instrumentos de laboratorio', + name='Peaceful_Life_for_Good_People', + cn='好人一生平安', + cht='好人一生平安', + en='Peaceful Life for Good People', + jp='善人は一生安泰', + es='Vida pacífica para la gente de bien', ) -Booking_System = AssignmentEventEntry( +Closed_Beta_Test_Recruitment = AssignmentEventEntry( id=8, - name='Booking_System', - cn='会议室预约系统', - cht='會議室預約系統', - en='Booking System', - jp='会議室予約システム', - es='Sistema de reserva de las salas de reuniones', + name='Closed_Beta_Test_Recruitment', + cn='征集内测玩家', + cht='徵求封測玩家', + en='Closed Beta Test Recruitment', + jp='テスター募集', + es='Reclutamiento para beta cerrada', ) -Non_Digital_Documents = AssignmentEventEntry( +Protect_Digital_Exhibits = AssignmentEventEntry( id=9, - name='Non_Digital_Documents', - cn='非电子版文件', - cht='非電子版文件', - en='Non-Digital Documents', - jp='非デジタル版ファイル', - es='Documentos no electrónicos', + name='Protect_Digital_Exhibits', + cn='保护数字展品', + cht='保護數位展品', + en='Protect Digital Exhibits', + jp='デジタル展示品を守れ', + es='Protección de objetos de exposición digitales', ) -Drip_Feed_Errors = AssignmentEventEntry( +Final_Survivor = AssignmentEventEntry( id=10, - name='Drip_Feed_Errors', - cn='液滴系统报错', - cht='液滴系統錯誤', - en='Drip-Feed Errors', - jp='水やりシステムエラー', - es='Reporte de error del sistema de goteo', + name='Final_Survivor', + cn='最后的生还者', + cht='最後的生還者', + en='Final Survivor', + jp='最後の生還者', + es='Última superviviente', ) -Pet_Movement_Route_Planning = AssignmentEventEntry( +Water_Pollution_Control = AssignmentEventEntry( id=11, - name='Pet_Movement_Route_Planning', - cn='宠物行动路线规划', - cht='寵物行動路線規劃', - en='Pet Movement Route Planning', - jp='ペットの行動ルート規制', - es='Planificación de las rutas de paseo de mascotas', + name='Water_Pollution_Control', + cn='水域排污', + cht='水域排汙', + en='Water Pollution Control', + jp='水質汚染', + es='Control de contaminación acuática', ) -Food_Improvement_Plan = AssignmentEventEntry( +Hook_Line_and_Sinker = AssignmentEventEntry( id=12, - name='Food_Improvement_Plan', - cn='餐饮优化方案', - cht='餐飲改良方案', - en='Food Improvement Plan', - jp='飲食優良化法案', - es='Programa de mejora de comida', + name='Hook_Line_and_Sinker', + cn='愿者上钩', + cht='願者上鉤', + en='Hook, Line, and Sinker', + jp='いずれ捕まる', + es='Quien muerde el anzuelo es porque quiere', ) -Curio_Distribution = AssignmentEventEntry( +Investigation_Order_Boothill = AssignmentEventEntry( id=13, - name='Curio_Distribution', - cn='奇物借用问题', - cht='奇物借用問題', - en='Curio Distribution', - jp='奇物借用問題', - es='Problemas con el préstamo de objetos raros', + name='Investigation_Order_Boothill', + cn='追查令:波提欧', + cht='追查令:波提歐', + en='Investigation Order: Boothill', + jp='調査命令:ブートヒル', + es='Orden de investigación: Boothill', ) -Super_Urgent_Waiting_Online = AssignmentEventEntry( +Volunteers_Wanted = AssignmentEventEntry( id=14, - name='Super_Urgent_Waiting_Online', - cn='来活人很急在线等', - cht='急,線上等', - en='Super Urgent, Waiting Online', - jp='緊急助っ人求むオンラインにて待つ', - es='Muy urgente, esperando en línea', + name='Volunteers_Wanted', + cn='寻找志愿者', + cht='尋找志向者', + en='Volunteers Wanted', + jp='ボランティアを探せ', + es='Se necesitan voluntarios', ) -Ventilation_Problem = AssignmentEventEntry( +Burn_Treatment = AssignmentEventEntry( id=15, - name='Ventilation_Problem', - cn='空气流通问题', - cht='空氣流通問題', - en='Ventilation Problem', - jp='換気問題', - es='Problemas con la circulación del aire', + name='Burn_Treatment', + cn='烧伤治疗', + cht='燒傷治療', + en='Burn Treatment', + jp='火傷の治療', + es='Tratamiento cauterizador', ) -Unstable_Connection = AssignmentEventEntry( +I_Want_to_Speak_to_Your_Manager = AssignmentEventEntry( id=16, - name='Unstable_Connection', - cn='连接不稳定问题', - cht='連線不穩定問題', - en='Unstable Connection', - jp='接続不安定問題', - es='Conexión inestable', + name='I_Want_to_Speak_to_Your_Manager', + cn='去把你们经理喊过来', + cht='叫你們經理出來', + en='I Want to Speak to Your Manager', + jp='責任者を呼べ', + es='Quiero hablar con su gerente', ) -Chronology_Checks = AssignmentEventEntry( +Licensed_Product_Damage_Assessment = AssignmentEventEntry( id=17, - name='Chronology_Checks', - cn='编年史校对', - cht='編年史校對', - en='Chronology Checks', - jp='編年史校正', - es='Corrección de registros', + name='Licensed_Product_Damage_Assessment', + cn='授权产品定损', + cht='授權產品損失鑑定', + en='Licensed Product Damage Assessment', + jp='ライセンス製品の損失額評価', + es='Evaluación de pérdidas de productos autorizados', ) -Supply_Chain_Management = AssignmentEventEntry( +Annoying_Flies = AssignmentEventEntry( id=18, - name='Supply_Chain_Management', - cn='物流供应链管理', - cht='物流供應鏈管理', - en='Supply Chain Management', - jp='物流供給路線管理', - es='Gestión de pedidos', + name='Annoying_Flies', + cn='烦人的苍蝇', + cht='煩人的蒼蠅', + en='Annoying Flies', + jp='鬱陶しいハエ', + es='Moscas molestas', ) -Malicious_Occupation_of_Public_Space = AssignmentEventEntry( +Urgent_Protection_Services = AssignmentEventEntry( id=19, - name='Malicious_Occupation_of_Public_Space', - cn='公共区域被恶意侵占', - cht='公共區域被惡意侵佔', - en='Malicious Occupation of Public Space', - jp='公共区域の悪意による独占', - es='Invasión de espacios públicos', + name='Urgent_Protection_Services', + cn='紧急护卫', + cht='緊急護衛', + en='Urgent Protection Services', + jp='緊急護衛', + es='Se necesita protección urgente', ) -Uniform_Material = AssignmentEventEntry( +A_Dream_Is_Born = AssignmentEventEntry( id=20, - name='Uniform_Material', - cn='科室服装面料', - cht='科室服裝材質', - en='Uniform Material', - jp='スタッフ制服の素材', - es='Material del uniforme', + name='A_Dream_Is_Born', + cn='星梦起航', + cht='星夢啟航', + en='A Dream Is Born', + jp='星の夢の門出', + es='Ha nacido una estrella', ) -Virus_Re_creation_Report = AssignmentEventEntry( +Wanted_Boothill = AssignmentEventEntry( id=21, - name='Virus_Re_creation_Report', - cn='病毒溯源报告', - cht='病毒溯源報告', - en='Virus Re-creation Report', - jp='ウイルス根源報告', - es='Informe de rastreo de virus', + name='Wanted_Boothill', + cn='通缉令:波提欧', + cht='通緝令:波提歐', + en='Wanted: Boothill', + jp='指名手配:ブートヒル', + es='Se busca: Boothill', ) -Abnormal_Signal = AssignmentEventEntry( +Casual_Cowboy = AssignmentEventEntry( id=22, - name='Abnormal_Signal', - cn='舱段信号异常', - cht='艙段訊號異常', - en='Abnormal Signal', - jp='部分の信号異常', - es='Mala señal en las cabinas', + name='Casual_Cowboy', + cn='牛仔不忙', + cht='牛仔不忙', + en='Casual Cowboy', + jp='暇を持て余したカウボーイ', + es='Vaquero ocioso', ) -Flexible_Working_Approval = AssignmentEventEntry( +Dangerous_Journey = AssignmentEventEntry( id=23, - name='Flexible_Working_Approval', - cn='轮休审批流程', - cht='輪休審批流程', - en='Flexible Working Approval', - jp='交代休み審査フロー', - es='Aprobación de días de descanso', + name='Dangerous_Journey', + cn='危险之旅', + cht='危險之旅', + en='Dangerous Journey', + jp='危険な旅', + es='Viaje peligroso', ) -Lighting_Issue = AssignmentEventEntry( +Crossing_the_Fire_Line = AssignmentEventEntry( id=24, - name='Lighting_Issue', - cn='灯光照明问题', - cht='燈光照明問題', - en='Lighting Issue', - jp='照明の色問題', - es='Problemas de iluminación', + name='Crossing_the_Fire_Line', + cn='穿越火线', + cht='穿越火線', + en='Crossing the Fire Line', + jp='クロスファイア', + es='Cruzando la línea de fuego', ) diff --git a/tasks/character/keywords/character_list.py b/tasks/character/keywords/character_list.py index fefdb2a7e..f65273e05 100644 --- a/tasks/character/keywords/character_list.py +++ b/tasks/character/keywords/character_list.py @@ -75,8 +75,17 @@ Blade = CharacterList( jp='刃', es='Blade', ) -Bronya = CharacterList( +Boothill = CharacterList( id=9, + name='Boothill', + cn='波提欧', + cht='波提歐', + en='Boothill', + jp='ブートヒル', + es='Boothill', +) +Bronya = CharacterList( + id=10, name='Bronya', cn='布洛妮娅', cht='布洛妮婭', @@ -85,7 +94,7 @@ Bronya = CharacterList( es='Bronya', ) Clara = CharacterList( - id=10, + id=11, name='Clara', cn='克拉拉', cht='克拉拉', @@ -94,7 +103,7 @@ Clara = CharacterList( es='Clara', ) DanHeng = CharacterList( - id=11, + id=12, name='DanHeng', cn='丹恒', cht='丹恆', @@ -103,7 +112,7 @@ DanHeng = CharacterList( es='Dan Heng', ) DanHengImbibitorLunae = CharacterList( - id=12, + id=13, name='DanHengImbibitorLunae', cn='丹恒•饮月', cht='丹恆•飲月', @@ -112,7 +121,7 @@ DanHengImbibitorLunae = CharacterList( es='Dan Heng - Imbibitor Lunae', ) DrRatio = CharacterList( - id=13, + id=14, name='DrRatio', cn='真理医生', cht='真理醫生', @@ -121,7 +130,7 @@ DrRatio = CharacterList( es='Dr. Ratio', ) FuXuan = CharacterList( - id=14, + id=15, name='FuXuan', cn='符玄', cht='符玄', @@ -130,7 +139,7 @@ FuXuan = CharacterList( es='Fu Xuan', ) Gallagher = CharacterList( - id=15, + id=16, name='Gallagher', cn='加拉赫', cht='加拉赫', @@ -139,7 +148,7 @@ Gallagher = CharacterList( es='Gallagher', ) Gepard = CharacterList( - id=16, + id=17, name='Gepard', cn='杰帕德', cht='傑帕德', @@ -148,7 +157,7 @@ Gepard = CharacterList( es='Gepard', ) Guinaifen = CharacterList( - id=17, + id=18, name='Guinaifen', cn='桂乃芬', cht='桂乃芬', @@ -157,7 +166,7 @@ Guinaifen = CharacterList( es='Guinaifen', ) Hanya = CharacterList( - id=18, + id=19, name='Hanya', cn='寒鸦', cht='寒鴉', @@ -166,7 +175,7 @@ Hanya = CharacterList( es='Hanya', ) Herta = CharacterList( - id=19, + id=20, name='Herta', cn='黑塔', cht='黑塔', @@ -175,7 +184,7 @@ Herta = CharacterList( es='Herta', ) Himeko = CharacterList( - id=20, + id=21, name='Himeko', cn='姬子', cht='姬子', @@ -184,7 +193,7 @@ Himeko = CharacterList( es='Himeko', ) Hook = CharacterList( - id=21, + id=22, name='Hook', cn='虎克', cht='虎克', @@ -193,7 +202,7 @@ Hook = CharacterList( es='Hook', ) Huohuo = CharacterList( - id=22, + id=23, name='Huohuo', cn='藿藿', cht='藿藿', @@ -202,7 +211,7 @@ Huohuo = CharacterList( es='Huohuo', ) JingYuan = CharacterList( - id=23, + id=24, name='JingYuan', cn='景元', cht='景元', @@ -211,7 +220,7 @@ JingYuan = CharacterList( es='Jing Yuan', ) Jingliu = CharacterList( - id=24, + id=25, name='Jingliu', cn='镜流', cht='鏡流', @@ -220,7 +229,7 @@ Jingliu = CharacterList( es='Jingliu', ) Kafka = CharacterList( - id=25, + id=26, name='Kafka', cn='卡芙卡', cht='卡芙卡', @@ -229,7 +238,7 @@ Kafka = CharacterList( es='Kafka', ) Luka = CharacterList( - id=26, + id=27, name='Luka', cn='卢卡', cht='盧卡', @@ -238,7 +247,7 @@ Luka = CharacterList( es='Luka', ) Luocha = CharacterList( - id=27, + id=28, name='Luocha', cn='罗刹', cht='羅剎', @@ -247,7 +256,7 @@ Luocha = CharacterList( es='Luocha', ) Lynx = CharacterList( - id=28, + id=29, name='Lynx', cn='玲可', cht='玲可', @@ -256,7 +265,7 @@ Lynx = CharacterList( es='Lynx', ) March7th = CharacterList( - id=29, + id=30, name='March7th', cn='三月七', cht='三月七', @@ -265,7 +274,7 @@ March7th = CharacterList( es='Siete de Marzo', ) Misha = CharacterList( - id=30, + id=31, name='Misha', cn='米沙', cht='米沙', @@ -274,7 +283,7 @@ Misha = CharacterList( es='Misha', ) Natasha = CharacterList( - id=31, + id=32, name='Natasha', cn='娜塔莎', cht='娜塔莎', @@ -283,7 +292,7 @@ Natasha = CharacterList( es='Natasha', ) Pela = CharacterList( - id=32, + id=33, name='Pela', cn='佩拉', cht='佩拉', @@ -292,7 +301,7 @@ Pela = CharacterList( es='Pela', ) Qingque = CharacterList( - id=33, + id=34, name='Qingque', cn='青雀', cht='青雀', @@ -300,8 +309,17 @@ Qingque = CharacterList( jp='青雀', es='Qingque', ) +Robin = CharacterList( + id=35, + name='Robin', + cn='知更鸟', + cht='知更鳥', + en='Robin', + jp='ロビン', + es='Robin', +) RuanMei = CharacterList( - id=34, + id=36, name='RuanMei', cn='阮•梅', cht='阮•梅', @@ -310,7 +328,7 @@ RuanMei = CharacterList( es='Ruan Mei', ) Sampo = CharacterList( - id=35, + id=37, name='Sampo', cn='桑博', cht='桑博', @@ -319,7 +337,7 @@ Sampo = CharacterList( es='Sampo', ) Seele = CharacterList( - id=36, + id=38, name='Seele', cn='希儿', cht='希兒', @@ -328,7 +346,7 @@ Seele = CharacterList( es='Seele', ) Serval = CharacterList( - id=37, + id=39, name='Serval', cn='希露瓦', cht='希露瓦', @@ -337,7 +355,7 @@ Serval = CharacterList( es='Serval', ) SilverWolf = CharacterList( - id=38, + id=40, name='SilverWolf', cn='银狼', cht='銀狼', @@ -346,7 +364,7 @@ SilverWolf = CharacterList( es='Silver Wolf', ) Sparkle = CharacterList( - id=39, + id=41, name='Sparkle', cn='花火', cht='花火', @@ -355,7 +373,7 @@ Sparkle = CharacterList( es='Sparkle', ) Sushang = CharacterList( - id=40, + id=42, name='Sushang', cn='素裳', cht='素裳', @@ -364,7 +382,7 @@ Sushang = CharacterList( es='Sushang', ) Tingyun = CharacterList( - id=41, + id=43, name='Tingyun', cn='停云', cht='停雲', @@ -373,7 +391,7 @@ Tingyun = CharacterList( es='Tingyun', ) TopazNumby = CharacterList( - id=42, + id=44, name='TopazNumby', cn='托帕&账账', cht='托帕&帳帳', @@ -382,7 +400,7 @@ TopazNumby = CharacterList( es='Topaz y Conti', ) TrailblazerDestruction = CharacterList( - id=43, + id=45, name='TrailblazerDestruction', cn='Trailblazer•毁灭', cht='Trailblazer•毀滅', @@ -390,8 +408,17 @@ TrailblazerDestruction = CharacterList( jp='Trailblazer・壊滅', es='Trailblazer: Destrucción', ) +TrailblazerHarmony = CharacterList( + id=46, + name='TrailblazerHarmony', + cn='Trailblazer•同谐', + cht='Trailblazer•同諧', + en='Trailblazer: Harmony', + jp='Trailblazer・調和', + es='Trailblazer: Armonía', +) TrailblazerPreservation = CharacterList( - id=44, + id=47, name='TrailblazerPreservation', cn='Trailblazer•存护', cht='Trailblazer•存護', @@ -400,7 +427,7 @@ TrailblazerPreservation = CharacterList( es='Trailblazer: Conservación', ) Welt = CharacterList( - id=45, + id=48, name='Welt', cn='瓦尔特', cht='瓦爾特', @@ -409,7 +436,7 @@ Welt = CharacterList( es='Welt', ) Xueyi = CharacterList( - id=46, + id=49, name='Xueyi', cn='雪衣', cht='雪衣', @@ -418,7 +445,7 @@ Xueyi = CharacterList( es='Xueyi', ) Yanqing = CharacterList( - id=47, + id=50, name='Yanqing', cn='彦卿', cht='彥卿', @@ -427,7 +454,7 @@ Yanqing = CharacterList( es='Yanqing', ) Yukong = CharacterList( - id=48, + id=51, name='Yukong', cn='驭空', cht='馭空', diff --git a/tasks/dungeon/keywords/dungeon.py b/tasks/dungeon/keywords/dungeon.py index 35ee6a29a..c4301cba0 100644 --- a/tasks/dungeon/keywords/dungeon.py +++ b/tasks/dungeon/keywords/dungeon.py @@ -98,7 +98,7 @@ Calyx_Crimson_Destruction_Herta_StorageZone = DungeonList( name='Calyx_Crimson_Destruction_Herta_StorageZone', cn='毁灭之蕾•拟造花萼(赤)', cht='毀滅之蕾•擬造花萼(赤)', - en='Bud of Destruction', + en='Calyx (Crimson): Bud of Destruction', jp='疑似花萼(赤)・壊滅の蕾', es='Flor de la Destrucción', plane_id=2000201, @@ -108,7 +108,7 @@ Calyx_Crimson_Destruction_Luofu_ScalegorgeWaterscape = DungeonList( name='Calyx_Crimson_Destruction_Luofu_ScalegorgeWaterscape', cn='毁灭之蕾•拟造花萼(赤)', cht='毀滅之蕾•擬造花萼(赤)', - en='Bud of Destruction', + en='Calyx (Crimson): Bud of Destruction', jp='疑似花萼(赤)・壊滅の蕾', es='Flor de la Destrucción', plane_id=2023201, @@ -118,7 +118,7 @@ Calyx_Crimson_Preservation_Herta_SupplyZone = DungeonList( name='Calyx_Crimson_Preservation_Herta_SupplyZone', cn='存护之蕾•拟造花萼(赤)', cht='存護之蕾•擬造花萼(赤)', - en='Bud of Preservation', + en='Calyx (Crimson): Bud of Preservation', jp='疑似花萼(赤)・存護の蕾', es='Flor de la Conservación', plane_id=2000301, @@ -128,7 +128,7 @@ Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark = DungeonList( name='Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark', cn='存护之蕾•拟造花萼(赤)', cht='存護之蕾•擬造花萼(赤)', - en='Bud of Preservation', + en='Calyx (Crimson): Bud of Preservation', jp='疑似花萼(赤)・存護の蕾', es='Flor de la Conservación', plane_id=2032101, @@ -138,383 +138,413 @@ Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains = DungeonList( name='Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains', cn='巡猎之蕾•拟造花萼(赤)', cht='巡獵之蕾•擬造花萼(赤)', - en='Bud of The Hunt', + en='Calyx (Crimson): Bud of The Hunt', jp='疑似花萼(赤)・巡狩の蕾', es='Flor de la Cacería', plane_id=2010101, ) -Calyx_Crimson_Abundance_Jarilo_BackwaterPass = DungeonList( +Calyx_Crimson_The_Hunt_Penacony_SoulGladScorchsandAuditionVenue = DungeonList( id=15, + name='Calyx_Crimson_The_Hunt_Penacony_SoulGladScorchsandAuditionVenue', + cn='巡猎之蕾•拟造花萼(赤)', + cht='巡獵之蕾•擬造花萼(赤)', + en='Calyx (Crimson): Bud of The Hunt', + jp='疑似花萼(赤)・巡狩の蕾', + es='Cáliz (carmesí): Flor de la Cacería', + plane_id=2033101, +) +Calyx_Crimson_Abundance_Jarilo_BackwaterPass = DungeonList( + id=16, name='Calyx_Crimson_Abundance_Jarilo_BackwaterPass', cn='丰饶之蕾•拟造花萼(赤)', cht='豐饒之蕾•擬造花萼(赤)', - en='Bud of Abundance', + en='Calyx (Crimson): Bud of Abundance', jp='疑似花萼(赤)・豊穣の蕾', es='Flor de la Abundancia', plane_id=2011101, ) Calyx_Crimson_Abundance_Luofu_FyxestrollGarden = DungeonList( - id=16, + id=17, name='Calyx_Crimson_Abundance_Luofu_FyxestrollGarden', cn='丰饶之蕾•拟造花萼(赤)', cht='豐饒之蕾•擬造花萼(赤)', - en='Bud of Abundance', + en='Calyx (Crimson): Bud of Abundance', jp='疑似花萼(赤)・豊穣の蕾', es='Flor de la Abundancia', plane_id=2022301, ) Calyx_Crimson_Erudition_Jarilo_RivetTown = DungeonList( - id=17, + id=18, name='Calyx_Crimson_Erudition_Jarilo_RivetTown', cn='智识之蕾•拟造花萼(赤)', cht='智識之蕾•擬造花萼(赤)', - en='Bud of Erudition', + en='Calyx (Crimson): Bud of Erudition', jp='疑似花萼(赤)・知恵の蕾', es='Flor de la Erudición', plane_id=2012201, ) Calyx_Crimson_Harmony_Jarilo_RobotSettlement = DungeonList( - id=18, + id=19, name='Calyx_Crimson_Harmony_Jarilo_RobotSettlement', cn='同谐之蕾•拟造花萼(赤)', cht='同諧之蕾•擬造花萼(赤)', - en='Bud of Harmony', + en='Calyx (Crimson): Bud of Harmony', jp='疑似花萼(赤)・調和の蕾', es='Flor de la Armonía', plane_id=2012301, ) Calyx_Crimson_Harmony_Penacony_TheReverieDreamscape = DungeonList( - id=19, + id=20, name='Calyx_Crimson_Harmony_Penacony_TheReverieDreamscape', cn='同谐之蕾•拟造花萼(赤)', cht='同諧之蕾•擬造花萼(赤)', - en='Bud of Harmony', + en='Calyx (Crimson): Bud of Harmony', jp='疑似花萼(赤)・調和の蕾', es='Flor de la Armonía', plane_id=2031101, ) Calyx_Crimson_Nihility_Jarilo_GreatMine = DungeonList( - id=20, + id=21, name='Calyx_Crimson_Nihility_Jarilo_GreatMine', cn='虚无之蕾•拟造花萼(赤)', cht='虛無之蕾•擬造花萼(赤)', - en='Bud of Nihility', + en='Calyx (Crimson): Bud of Nihility', jp='疑似花萼(赤)・虚無の蕾', es='Flor de la Nihilidad', plane_id=2012101, ) Calyx_Crimson_Nihility_Luofu_AlchemyCommission = DungeonList( - id=21, + id=22, name='Calyx_Crimson_Nihility_Luofu_AlchemyCommission', cn='虚无之蕾•拟造花萼(赤)', cht='虛無之蕾•擬造花萼(赤)', - en='Bud of Nihility', + en='Calyx (Crimson): Bud of Nihility', jp='疑似花萼(赤)・虚無の蕾', es='Flor de la Nihilidad', plane_id=2023101, ) Stagnant_Shadow_Quanta = DungeonList( - id=22, + id=23, name='Stagnant_Shadow_Quanta', cn='空海之形•凝滞虚影', cht='空海之形•凝滯虛影', - en='Shape of Quanta', + en='Stagnant Shadow: Shape of Quanta', jp='凝結虚影・虚海の形', es='Forma del cuanto', plane_id=2000101, ) Stagnant_Shadow_Gust = DungeonList( - id=23, + id=24, name='Stagnant_Shadow_Gust', cn='巽风之形•凝滞虚影', cht='巽風之形•凝滯虛影', - en='Shape of Gust', + en='Stagnant Shadow: Shape of Gust', jp='凝結虚影・薫風の形', es='Forma del aire', plane_id=2012201, ) Stagnant_Shadow_Fulmination = DungeonList( - id=24, + id=25, name='Stagnant_Shadow_Fulmination', cn='鸣雷之形•凝滞虚影', cht='鳴雷之形•凝滯虛影', - en='Shape of Fulmination', + en='Stagnant Shadow: Shape of Fulmination', jp='凝結虚影・鳴雷の形', es='Forma del trueno', plane_id=2013201, ) Stagnant_Shadow_Blaze = DungeonList( - id=25, + id=26, name='Stagnant_Shadow_Blaze', cn='炎华之形•凝滞虚影', cht='炎華之形•凝滯虛影', - en='Shape of Blaze', + en='Stagnant Shadow: Shape of Blaze', jp='凝結虚影・炎華の形', es='Forma de las llamas', plane_id=2013101, ) Stagnant_Shadow_Spike = DungeonList( - id=26, + id=27, name='Stagnant_Shadow_Spike', cn='锋芒之形•凝滞虚影', cht='鋒芒之形•凝滯虛影', - en='Shape of Spike', + en='Stagnant Shadow: Shape of Spike', jp='凝結虚影・切先の形', es='Forma afilada', plane_id=2012101, ) Stagnant_Shadow_Rime = DungeonList( - id=27, + id=28, name='Stagnant_Shadow_Rime', cn='霜晶之形•凝滞虚影', cht='霜晶之形•凝滯虛影', - en='Shape of Rime', + en='Stagnant Shadow: Shape of Rime', jp='凝結虚影・霜晶の形', es='Forma de la escarcha', plane_id=2013201, ) Stagnant_Shadow_Mirage = DungeonList( - id=28, + id=29, name='Stagnant_Shadow_Mirage', cn='幻光之形•凝滞虚影', cht='幻光之形•凝滯虛影', - en='Shape of Mirage', + en='Stagnant Shadow: Shape of Mirage', jp='凝結虚影・幻光の形', es='Forma del espejismo', plane_id=2011101, ) Stagnant_Shadow_Icicle = DungeonList( - id=29, + id=30, name='Stagnant_Shadow_Icicle', cn='冰棱之形•凝滞虚影', cht='冰稜之形•凝滯虛影', - en='Shape of Icicle', + en='Stagnant Shadow: Shape of Icicle', jp='凝結虚影・氷柱の形', es='Forma del témpano', plane_id=2021101, ) Stagnant_Shadow_Doom = DungeonList( - id=30, + id=31, name='Stagnant_Shadow_Doom', cn='震厄之形•凝滞虚影', cht='震厄之形•凝滯虛影', - en='Shape of Doom', + en='Stagnant Shadow: Shape of Doom', jp='凝結虚影・震厄の形', es='Forma de la perdición', plane_id=2021201, ) Stagnant_Shadow_Puppetry = DungeonList( - id=31, + id=32, name='Stagnant_Shadow_Puppetry', cn='偃偶之形•凝滞虚影', cht='偃偶之形•凝滯虛影', - en='Shape of Puppetry', + en='Stagnant Shadow: Shape of Puppetry', jp='凝結虚影・傀儡の形', es='Forma de las marionetas', plane_id=2022201, ) Stagnant_Shadow_Abomination = DungeonList( - id=32, + id=33, name='Stagnant_Shadow_Abomination', cn='孽兽之形•凝滞虚影', cht='孽獸之形•凝滯虛影', - en='Shape of Abomination', + en='Stagnant Shadow: Shape of Abomination', jp='凝結虚影・厄獣の形', es='Forma de la abominación', plane_id=2023201, ) Stagnant_Shadow_Scorch = DungeonList( - id=33, + id=34, name='Stagnant_Shadow_Scorch', cn='燔灼之形•凝滞虚影', cht='燔灼之形•凝滯虛影', - en='Shape of Scorch', + en='Stagnant Shadow: Shape of Scorch', jp='凝結虚影・燔灼の形', es='Forma abrasada', plane_id=2012101, ) Stagnant_Shadow_Celestial = DungeonList( - id=34, + id=35, name='Stagnant_Shadow_Celestial', cn='天人之形•凝滞虚影', cht='天人之形•凝滯虛影', - en='Shape of Celestial', + en='Stagnant Shadow: Shape of Celestial', jp='凝結虚影・天人の形', es='Forma de lo celestial', plane_id=2023101, ) Stagnant_Shadow_Perdition = DungeonList( - id=35, + id=36, name='Stagnant_Shadow_Perdition', cn='幽府之形•凝滞虚影', cht='幽府之形•凝滯虛影', - en='Shape of Perdition', + en='Stagnant Shadow: Shape of Perdition', jp='凝結虚影・幽府の形', es='Forma del aislamiento', plane_id=2022301, ) Stagnant_Shadow_Nectar = DungeonList( - id=36, + id=37, name='Stagnant_Shadow_Nectar', cn='冰酿之形•凝滞虚影', cht='冰釀之形•凝滯虛影', - en='Shape of Nectar', + en='Stagnant Shadow: Shape of Nectar', jp='凝結虚影・氷醸の形', es='Forma del néctar', plane_id=2031101, ) Stagnant_Shadow_Roast = DungeonList( - id=37, + id=38, name='Stagnant_Shadow_Roast', cn='焦炙之形•凝滞虚影', cht='焦炙之形•凝滯虛影', - en='Shape of Roast', + en='Stagnant Shadow: Shape of Roast', jp='凝結虚影・焦灼の形', es='Forma del agostamiento', plane_id=2031301, ) Stagnant_Shadow_Ire = DungeonList( - id=38, + id=39, name='Stagnant_Shadow_Ire', cn='嗔怒之形•凝滞虚影', cht='嗔怒之形•凝滯虛影', - en='Shape of Ire', + en='Stagnant Shadow: Shape of Ire', jp='凝結虚影・憤怒の形', es='Forma de la ira', plane_id=2032201, ) +Stagnant_Shadow_Duty = DungeonList( + id=40, + name='Stagnant_Shadow_Duty', + cn='职司之形•凝滞虚影', + cht='職司之形•凝滯虛影', + en='Stagnant Shadow: Shape of Duty', + jp='凝結虚影・職掌の形', + es='Sombra paralizada: Forma del deber', + plane_id=2032101, +) Cavern_of_Corrosion_Path_of_Gelid_Wind = DungeonList( - id=39, + id=41, name='Cavern_of_Corrosion_Path_of_Gelid_Wind', cn='霜风之径•侵蚀隧洞', cht='霜風之徑•侵蝕隧洞', - en='Path of Gelid Wind', + en='Cavern of Corrosion: Path of Gelid Wind', jp='侵蝕トンネル・霜風の路', es='Senda del viento gélido', plane_id=2000201, ) Cavern_of_Corrosion_Path_of_Jabbing_Punch = DungeonList( - id=40, + id=42, name='Cavern_of_Corrosion_Path_of_Jabbing_Punch', cn='迅拳之径•侵蚀隧洞', cht='迅拳之徑•侵蝕隧洞', - en='Path of Jabbing Punch', + en='Cavern of Corrosion: Path of Jabbing Punch', jp='侵蝕トンネル・迅拳の路', es='Senda de los puños rápidos', plane_id=2013101, ) Cavern_of_Corrosion_Path_of_Drifting = DungeonList( - id=41, + id=43, name='Cavern_of_Corrosion_Path_of_Drifting', cn='漂泊之径•侵蚀隧洞', cht='漂泊之徑•侵蝕隧洞', - en='Path of Drifting', + en='Cavern of Corrosion: Path of Drifting', jp='侵蝕トンネル・漂泊の路', es='Senda de la deriva', plane_id=2013201, ) Cavern_of_Corrosion_Path_of_Providence = DungeonList( - id=42, + id=44, name='Cavern_of_Corrosion_Path_of_Providence', cn='睿治之径•侵蚀隧洞', cht='睿治之徑•侵蝕隧洞', - en='Path of Providence', + en='Cavern of Corrosion: Path of Providence', jp='侵蝕トンネル・睿治の路', es='Senda de la providencia', plane_id=2013401, ) Cavern_of_Corrosion_Path_of_Holy_Hymn = DungeonList( - id=43, + id=45, name='Cavern_of_Corrosion_Path_of_Holy_Hymn', cn='圣颂之径•侵蚀隧洞', cht='聖頌之徑•侵蝕隧洞', - en='Path of Holy Hymn', + en='Cavern of Corrosion: Path of Holy Hymn', jp='侵蝕トンネル・聖頌の路', es='Senda del himno sagrado', plane_id=2021101, ) Cavern_of_Corrosion_Path_of_Conflagration = DungeonList( - id=44, + id=46, name='Cavern_of_Corrosion_Path_of_Conflagration', cn='野焰之径•侵蚀隧洞', cht='野焰之徑•侵蝕隧洞', - en='Path of Conflagration', + en='Cavern of Corrosion: Path of Conflagration', jp='侵蝕トンネル・野焔の路', es='Senda de la conflagración', plane_id=2021201, ) Cavern_of_Corrosion_Path_of_Elixir_Seekers = DungeonList( - id=45, + id=47, name='Cavern_of_Corrosion_Path_of_Elixir_Seekers', cn='药使之径•侵蚀隧洞', cht='藥使之徑•侵蝕隧洞', - en='Path of Elixir Seekers', + en='Cavern of Corrosion: Path of Elixir Seekers', jp='侵蝕トンネル・薬使の路', es='Senda de los elixires', plane_id=2023101, ) Cavern_of_Corrosion_Path_of_Darkness = DungeonList( - id=46, + id=48, name='Cavern_of_Corrosion_Path_of_Darkness', cn='幽冥之径•侵蚀隧洞', cht='幽冥之徑•侵蝕隧洞', - en='Path of Darkness', + en='Cavern of Corrosion: Path of Darkness', jp='侵蝕トンネル・幽冥の路', es='Senda de la oscuridad', plane_id=2022301, ) Cavern_of_Corrosion_Path_of_Dreamdive = DungeonList( - id=47, + id=49, name='Cavern_of_Corrosion_Path_of_Dreamdive', cn='梦潜之径•侵蚀隧洞', cht='夢潛之徑•侵蝕隧洞', - en='Path of Dreamdive', + en='Cavern of Corrosion: Path of Dreamdive', jp='侵蝕トンネル・夢潜の路', es='Senda de los sueños', plane_id=2031101, ) Echo_of_War_Destruction_Beginning = DungeonList( - id=48, + id=50, name='Echo_of_War_Destruction_Beginning', cn='毁灭的开端•历战余响', cht='毀滅的開端•歷戰餘響', - en="Destruction's Beginning", + en="Echo of War: Destruction's Beginning", jp='歴戦余韻・壊滅の始まり', es='El principio de la Destrucción', plane_id=2000301, ) Echo_of_War_End_of_the_Eternal_Freeze = DungeonList( - id=49, + id=51, name='Echo_of_War_End_of_the_Eternal_Freeze', cn='寒潮的落幕•历战余响', cht='寒潮的落幕•歷戰餘響', - en='End of the Eternal Freeze', + en='Echo of War: End of the Eternal Freeze', jp='歴戦余韻・寒波の幕切れ', es='El fin del Hielo Eterno', plane_id=2013401, ) Echo_of_War_Divine_Seed = DungeonList( - id=50, + id=52, name='Echo_of_War_Divine_Seed', cn='不死的神实•历战余响', cht='不死的神實•歷戰餘響', - en='Divine Seed', + en='Echo of War: Divine Seed', jp='歴戦余韻・不死の神実', es='Semilla divina', plane_id=2023201, ) Echo_of_War_Borehole_Planet_Old_Crater = DungeonList( - id=51, + id=53, name='Echo_of_War_Borehole_Planet_Old_Crater', cn='蛀星的旧靥•历战余响', cht='蛀星的舊靨•歷戰餘響', - en="Borehole Planet's Old Crater", + en="Echo of War: Borehole Planet's Old Crater", jp='歴戦余韻・星を蝕む往日の面影', es='Cráter del planeta devorado', plane_id=2000401, ) +Echo_of_War_Salutations_of_Ashen_Dreams = DungeonList( + id=54, + name='Echo_of_War_Salutations_of_Ashen_Dreams', + cn='尘梦的赞礼•历战余响', + cht='塵夢的讚禮•歷戰餘響', + en='Echo of War: Salutations of Ashen Dreams', + jp='歴戦余韻・現世の夢の礼賛', + es='Ecos de la guerra: Tributo del sueño ceniciento', + plane_id=2033201, +) Simulated_Universe_World_1 = DungeonList( - id=52, + id=55, name='Simulated_Universe_World_1', cn='第一世界•模拟宇宙', cht='第一世界•模擬宇宙', @@ -524,7 +554,7 @@ Simulated_Universe_World_1 = DungeonList( plane_id=100000104, ) Simulated_Universe_World_3 = DungeonList( - id=53, + id=56, name='Simulated_Universe_World_3', cn='第三世界•模拟宇宙', cht='第三世界•模擬宇宙', @@ -534,7 +564,7 @@ Simulated_Universe_World_3 = DungeonList( plane_id=100000104, ) Simulated_Universe_World_4 = DungeonList( - id=54, + id=57, name='Simulated_Universe_World_4', cn='第四世界•模拟宇宙', cht='第四世界•模擬宇宙', @@ -544,7 +574,7 @@ Simulated_Universe_World_4 = DungeonList( plane_id=100000104, ) Simulated_Universe_World_5 = DungeonList( - id=55, + id=58, name='Simulated_Universe_World_5', cn='第五世界•模拟宇宙', cht='第五世界•模擬宇宙', @@ -554,7 +584,7 @@ Simulated_Universe_World_5 = DungeonList( plane_id=100000104, ) Simulated_Universe_World_6 = DungeonList( - id=56, + id=59, name='Simulated_Universe_World_6', cn='第六世界•模拟宇宙', cht='第六世界•模擬宇宙', @@ -564,7 +594,7 @@ Simulated_Universe_World_6 = DungeonList( plane_id=100000104, ) Simulated_Universe_World_7 = DungeonList( - id=57, + id=60, name='Simulated_Universe_World_7', cn='第七世界•模拟宇宙', cht='第七世界•模擬宇宙', @@ -574,7 +604,7 @@ Simulated_Universe_World_7 = DungeonList( plane_id=100000104, ) Simulated_Universe_World_8 = DungeonList( - id=58, + id=61, name='Simulated_Universe_World_8', cn='第八世界•模拟宇宙', cht='第八世界•模擬宇宙', @@ -584,7 +614,7 @@ Simulated_Universe_World_8 = DungeonList( plane_id=100000104, ) Simulated_Universe_World_9 = DungeonList( - id=59, + id=62, name='Simulated_Universe_World_9', cn='第九世界•模拟宇宙', cht='第九世界•模擬宇宙', @@ -594,7 +624,7 @@ Simulated_Universe_World_9 = DungeonList( plane_id=100000104, ) Simulated_Universe_The_Swarm_Disaster = DungeonList( - id=60, + id=63, name='Simulated_Universe_The_Swarm_Disaster', cn='寰宇蝗灾', cht='寰宇蝗災', @@ -604,7 +634,7 @@ Simulated_Universe_The_Swarm_Disaster = DungeonList( plane_id=-1, ) Simulated_Universe_Gold_and_Gears = DungeonList( - id=61, + id=64, name='Simulated_Universe_Gold_and_Gears', cn='黄金与机械', cht='黃金與機械', @@ -614,7 +644,7 @@ Simulated_Universe_Gold_and_Gears = DungeonList( plane_id=-1, ) Memory_of_Chaos = DungeonList( - id=62, + id=65, name='Memory_of_Chaos', cn='混沌回忆', cht='混沌回憶', @@ -624,7 +654,7 @@ Memory_of_Chaos = DungeonList( plane_id=-1, ) The_Voyage_of_Navis_Astriger = DungeonList( - id=63, + id=66, name='The_Voyage_of_Navis_Astriger', cn='天艟求仙迷航录', cht='天艟求仙迷航錄', @@ -634,7 +664,7 @@ The_Voyage_of_Navis_Astriger = DungeonList( plane_id=-1, ) The_Last_Vestiges_of_Towering_Citadel = DungeonList( - id=64, + id=67, name='The_Last_Vestiges_of_Towering_Citadel', cn='永屹之城遗秘', cht='永屹之城遺秘', diff --git a/tasks/dungeon/keywords/dungeon_detailed.py b/tasks/dungeon/keywords/dungeon_detailed.py index 4258f6e68..a81442fd5 100644 --- a/tasks/dungeon/keywords/dungeon_detailed.py +++ b/tasks/dungeon/keywords/dungeon_detailed.py @@ -156,3 +156,12 @@ Stagnant_Shadow_Ire = DungeonDetailed( jp='キャラクター昇格素材:炎(ギャラガー)', es='Ascension: Fuego (Gallagher)', ) +Stagnant_Shadow_Duty = DungeonDetailed( + id=18, + name='Stagnant_Shadow_Duty', + cn='角色晋阶材料:物理(波提欧 / 知更鸟)', + cht='角色晉階材料:物理(波提歐 / 知更鳥)', + en='Ascension: Physical (Boothill / Robin)', + jp='キャラクター昇格素材:物理(ブートヒル / ロビン)', + es='Ascension: Físico (Boothill / Robin)', +) diff --git a/tasks/map/keywords/plane.py b/tasks/map/keywords/plane.py index b77c5adc6..bca8da0b5 100644 --- a/tasks/map/keywords/plane.py +++ b/tasks/map/keywords/plane.py @@ -487,3 +487,36 @@ Penacony_ClockStudiosThemePark = MapPlane( world_id=3, plane_id=2032101, ) +Penacony_DreamfluxReef = MapPlane( + id=45, + name='Penacony_DreamfluxReef', + cn='流梦礁', + cht='流夢礁', + en='Dreamflux Reef', + jp='ドリームリーフ', + es='Arrecife Flujosueño', + world_id=3, + plane_id=1030401, +) +Penacony_SoulGladScorchsandAuditionVenue = MapPlane( + id=46, + name='Penacony_SoulGladScorchsandAuditionVenue', + cn='苏乐达™热砂海选会场', + cht='蘇樂達™熱砂海選會場', + en='SoulGlad™ Scorchsand Audition Venue', + jp='スラーダ™熱砂オーディション会場', + es='Recinto de las Audiciones FelizAlma™ en la Arena Ardiente', + world_id=3, + plane_id=2033101, +) +Penacony_PenaconyGrandTheater = MapPlane( + id=47, + name='Penacony_PenaconyGrandTheater', + cn='匹诺康尼大剧院', + cht='匹諾康尼大劇院', + en='Penacony Grand Theater', + jp='ピノコニー大劇場', + es='Gran Teatro de Colonipenal', + world_id=3, + plane_id=2033201, +) diff --git a/tasks/rogue/keywords/event_option.py b/tasks/rogue/keywords/event_option.py index f391cec2c..4400d553d 100644 --- a/tasks/rogue/keywords/event_option.py +++ b/tasks/rogue/keywords/event_option.py @@ -162,7 +162,7 @@ Jim_Hulk_collection = RogueEventOption( cn='杰姆·哈克的藏品。', cht='傑姆•哈克的收藏。', en="Jim Hulk's collection.", - jp='ジェム・ハックの所蔵品', + jp='ジャック・ハックの所蔵品', es='Colección de Jim Hulk.', ) Walk_away = RogueEventOption( @@ -1097,7 +1097,7 @@ Hurry_and_delete_the_Cyclic_Demon_Lord_life_algorithm = RogueEventOption( name='Hurry_and_delete_the_Cyclic_Demon_Lord_life_algorithm', cn='抓紧时间,删除周期性魔王的生命方程。', cht='把握時間,刪除週期性魔王的生命方程式。', - en="Hurry and delete the Cyclic Demon Lord's life algorithm", + en="Hurry and delete the Cyclic Demon Lord's life algorithm.", jp='事態は一刻を争う、周期性魔王の生命方程式を削除する', es='Date prisa y borra el algoritmo vital del Rey Demonio Cíclico.', ) @@ -1187,7 +1187,7 @@ Purchase_a_1_2_star_Blessing = RogueEventOption( name='Purchase_a_1_2_star_Blessing', cn='购买1个1-2星祝福', cht='購買1個一至二星祝福', - en='Purchase a 1-2 star Blessing', + en='Purchase a 1–2 star Blessing', jp='★1~2の祝福を1個購入する', es='Compra 1 bendición de 1-2 estrellas.', ) @@ -1196,7 +1196,7 @@ Purchase_a_1_3_star_Blessing = RogueEventOption( name='Purchase_a_1_3_star_Blessing', cn='购买1个1-3星祝福', cht='購買1個一至三星祝福', - en='Purchase a 1-3 star Blessing', + en='Purchase a 1–3 star Blessing', jp='★1~3の祝福を1個購入する', es='Compra 1 bendición de 1-3 estrellas.', ) From bad70f415aa6dcdf6c0138a7c0a6a1b8077d81d9 Mon Sep 17 00:00:00 2001 From: Zero <98764734+X-Zero-L@users.noreply.github.com> Date: Wed, 8 May 2024 16:46:11 +0800 Subject: [PATCH 073/114] Upd: Character (#445) Co-authored-by: LmeSzinc --- assets/character/Robin.png | Bin 0 -> 20233 bytes module/config/argument/args.json | 2 ++ module/config/config_generated.py | 2 +- module/config/config_updater.py | 2 +- module/config/i18n/en-US.json | 1 + module/config/i18n/es-ES.json | 1 + module/config/i18n/ja-JP.json | 1 + module/config/i18n/zh-CN.json | 1 + module/config/i18n/zh-TW.json | 1 + 9 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 assets/character/Robin.png diff --git a/assets/character/Robin.png b/assets/character/Robin.png new file mode 100644 index 0000000000000000000000000000000000000000..17046c663195a30302bcb2560b5056f309aad7cf GIT binary patch literal 20233 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 Date: Wed, 8 May 2024 18:41:42 +0800 Subject: [PATCH 074/114] Fix: Clear blessings after executing a battle so blessings can be handled in rotation_set() --- tasks/rogue/route/base.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/tasks/rogue/route/base.py b/tasks/rogue/route/base.py index d41522075..6137166b3 100644 --- a/tasks/rogue/route/base.py +++ b/tasks/rogue/route/base.py @@ -33,7 +33,8 @@ class RouteBase(RouteBase_, RogueExit, RogueEvent, RogueReward): return False def combat_execute(self, expected_end=None): - return super().combat_execute(expected_end=self.combat_expected_end) + super().combat_execute(expected_end=self.combat_expected_end) + self.clear_blessing() def walk_additional(self) -> bool: if self.handle_blessing_popup(): @@ -126,12 +127,6 @@ class RouteBase(RouteBase_, RogueExit, RogueEvent, RogueReward): if self.handle_event_option(): continue - def _goto(self, *args, **kwargs): - result = super()._goto(*args, **kwargs) - if 'enemy' in result: - self.clear_blessing() - return result - def wait_until_minimap_stabled(self): logger.info('Wait until minimap stabled') radius = self.minimap.MINIMAP_RADIUS From 80d1fbf9e5f885cdf56637cc1ada909883aa2570 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Wed, 8 May 2024 18:42:21 +0800 Subject: [PATCH 075/114] Fix: Consider multiple rogue runs as separated tasks --- tasks/rogue/rogue.py | 50 ++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/tasks/rogue/rogue.py b/tasks/rogue/rogue.py index 7635b25b8..5e2f56750 100644 --- a/tasks/rogue/rogue.py +++ b/tasks/rogue/rogue.py @@ -37,31 +37,35 @@ class Rogue(RouteLoader, RogueEntry): self.config.task_call('Dungeon') self.config.task_stop() - while 1: - # Run - success = self.rogue_once() + # Run + success = self.rogue_once() - # Scheduler - with self.config.multi_set(): - # Task switched - if self.config.task_switched(): + # Scheduler + with self.config.multi_set(): + # Task switched + if self.config.task_switched(): + self.config.task_stop() + # Archived daily quest + if success: + quests = self.config.stored.DailyQuest.load_quests() + if KEYWORDS_DAILY_QUEST.Complete_Simulated_Universe_1_times in quests: + logger.info('Achieve daily quest Complete_Simulated_Universe_1_times') + self.config.task_call('DailyQuest') self.config.task_stop() - # Archived daily quest - if success: - quests = self.config.stored.DailyQuest.load_quests() - if KEYWORDS_DAILY_QUEST.Complete_Simulated_Universe_1_times in quests: - logger.info('Achieve daily quest Complete_Simulated_Universe_1_times') - self.config.task_call('DailyQuest') - self.config.task_stop() - quests = self.config.stored.BattlePassWeeklyQuest.load_quests() - if KEYWORDS_BATTLE_PASS_QUEST.Complete_Simulated_Universe_1_times in quests: - logger.info('Achieve battle pass quest Complete_Simulated_Universe_1_times') - self.config.task_call('BattlePass') - self.config.task_stop() - # End - if not success: - self.config.task_delay(server_update=True) - break + quests = self.config.stored.BattlePassWeeklyQuest.load_quests() + if KEYWORDS_BATTLE_PASS_QUEST.Complete_Simulated_Universe_1_times in quests: + logger.info('Achieve battle pass quest Complete_Simulated_Universe_1_times') + self.config.task_call('BattlePass') + self.config.task_stop() + # End + if success: + logger.info('Rogue run success') + # Call rogue itself, so multiple rogue runs are considered as separated tasks + # which won't trigger failure count >= 3 when clearing 100 elites + self.config.task_call('Rogue') + else: + logger.info('Rogue run failed') + self.config.task_delay(server_update=True) if __name__ == '__main__': From 9b9e85990ae46fffb5981e7f3c7c7e5add93fb4c Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Wed, 8 May 2024 20:38:34 +0800 Subject: [PATCH 076/114] Upd: Combat support assets --- .../combat/support/COMBAT_SUPPORT_LIST.png | Bin 6569 -> 6222 bytes .../support/COMBAT_SUPPORT_LIST_GRID.png | Bin 95304 -> 93810 bytes .../support/COMBAT_SUPPORT_LIST_SCROLL.png | Bin 6359 -> 7731 bytes .../support/SUPPORT_SELECTED.SEARCH.png | Bin 11754 -> 16582 bytes tasks/combat/assets/assets_combat_support.py | 32 +++++++++--------- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/assets/share/combat/support/COMBAT_SUPPORT_LIST.png b/assets/share/combat/support/COMBAT_SUPPORT_LIST.png index 9917bea76ec83ffa7db716e3893ff5d430841d0a..f728912e2407ba0dca0b0d94c5d74fbcfc3729b9 100644 GIT binary patch literal 6222 zcmeI0=TpT_fl77Oi3Qd6#XEFdg`2KBL8m$NN@kc|*VV0QlY+@O!<(|L| zXyQ$kNdc4zq`SW(hME&HPbS3*8>0~8f0ixV57znRRaQ`2K>gZU1IB9IE`;J z6|)2cGk|$vHasI$Iuz);X5PmE^l<}?X0Pru09gRwjf~J123|4&>%aBY>43?a%o#8+ zS$?4g1U#Vv^2GHnQER>f9AU9gDQfHopcJhse2o@cPZeYRMoy19rh^U$PP0m9(R2i2 z^0cJ}06}GHK<6U+AdR3QO{vUi*L;nGUm^xV1z>U6gSmG`SB(uYWdXR)?c2#u)0f_3_!73Cic~(*yjWaj+IN%x z0s|xChNyE{hs;u{`lwM4<&mt9Yz8k-su$!+^03vBoa z1OT^6W3M!7ojv!S1C3=ae9zyG1yehnxutpT680vm_L=3F3k+r4Ia)mCSd~fI*Nrs1 zrRVb(SX|4%_w@N!QrBpEX4pYQwy*kg+<0QEd5}KcCn)#m=ux|#Y*uXZU7ByyL7H!0 z2=G4oR2nc6sK8V>itQ0L!ig13b>%p9S*kUIl=n{2m4Es9BK7sXvcU*e9o|U1M&I{3XkruW#kR^S5 zg6xOO!kI5jT13Ox_Pax!RhrVRbkSm)LKlUKMeQ#$vpZx$E;08%T{dPA5>1!WXVmwC zOhc|#npLWgvU(I6iV@O%`hP5gnq$y{xf!kLEa@gs5BonZw{ocTJX|!SFTVt>%wjW8 zbkSI$ttyez&MSR+cd(}T8|dXjm6Ch znF%?*Jhx{o&k!)HD}Sy;aAtgF%T~#@$d1a!zchyhz%<5?FB#2iVJx-w7Lm_&VXsV= zYUsal)sEHh!`{^>)ek{KpeWPfyABnd4##l^aHi&@MBUOPNQF7P^%Kt%A+AqyE?ZXG z zze;me<7(5mXPs+ZRNeKdu&K@|=MDc2t_{KzG03k-vq-9FSH(w#97Nxy)ONQmv28kN z_?P-uI03mvzx#QYd2Mz?Z`bE{#V+_4ahy0Ja9u_Lq?FF%EvpPC4Wz?=dOh>NjF1u* zUYy6dw`Fv@SB&UaT*ioPCc7;uhJw{y(TEGqXoVjxGhR2HoIT{GE4wRumOCW|v%K_Z!392Z9E%(%iBXANGv6uM^`mu$ z_5AfS8#n$q{Sp1+^JkcU-~t=}xN4wkgnwYrK(M!8JE3wx;bTpA&BrxMH$JzrfTA5m z@bf$AIJVs2T(Fr>^`mOvk}zEM3h!py=xryAG`d#6*KhYx zji~jTM7COdFtAiqT~PgDCmkbJld+WigZut-kp&?r_QN;j-JU{z&{g#1#k21!{uk(r1D2NKZA-m@9`bT42T(C z96z6yGz^+!jY2%%H(VQd19~{G=lu{P;K7pz-jC#vxFYxR+ONAUW`Pd^$x`F!W$Qz? zj3*LSz{$$P}@|R+hnEM*NC5eJE0)a zUdFu9A|OOv}hspU-@m$)Cx~BKrY5)12Okh9ea%(0Ws5 zE<@=TlA$)-HlY|u2jt#2`8n55ZnC##fV=Q0uLE;jY{7xI|LTz#-RU z?VxSpeUQM}H4ZC$HTvlzV)lb&K!cvbk> zKevel&p?FtOhZ>!GrbQ~>B&Wiir-2>3}UKaBR}M|-@Pl$AGB89U!Hp&5x!*lvux<& z%*VmGyy(_l5CMXe998KGLSL=$DOD@$ApU$@QN(Z0uh-dn8by>{f(P{~qAZI`i^SEo z_Lt)_m#Xhq7Z(|8sC~RyF12+v@9mxGVKkz6!QoG-8VY2hI zXzQIiwR$zZD8J(cNP`W6;6lc2Sl+A;{B1-~9U*>=KJdhj1x)NsT(c`db`U?U^R@bl z&0fHWw(af(|JeYCFl-vm*oC1&8;&}sj(?p!VEGl@a?<32^_kyu#5xXtv}PeP(Iuc6?uXx5)j~H;_cYJKa{yp-CWH+{5i_v6iEoE#m^lt9^M(YI+`Rg z?xzlS)gxi#d(xt9C5F%VmYk>F+2(N=kRmIDUhSh}zXMI?~*l zjbXHHEaz4nRo5!$j3EJ+UXz&`^2LjsQ2=2&!F=hl3K9?H4SqIOIh1cPO}@W5w+ zoekC&J|?4-uD{C2NJ&XK`JJ*Qt3|0zk~~FdtY1%W)HgKDy$ioy#i8BT?0tFTdIBr_ z0v{hALO~m}5&TY^;XGxoOgkM$3U{IlgEh!TPSo2Wi+j0Mi_(}WWi_nvw=>P29rh^M zPuz!OnXV*mc*Tf&>oi3IN(%sv5JtbpP4*4s)?6+yezpFjVohHDa$$c3t(M2WE2d^gx9pp{bxW8)LjHKh;p}xLX zy{PD}$iwcG;c*K~idtS?rj2o4!MnB=45s0Gd+ zln{0p2VLcdcdAxazmu>FPI=>HnY5W0a{vmkt1_XaJjugQDW)(R!` zq6Emvly!!S)!DU`6;)MdJ^Lv;kGYNkl39(TPI^XaYO=FcE$eM19+jWQ{VW&f;##$| z*t}4bf3TNnLU)EjiO(TMPfxGw=og_}ynfK`cm-c_pc;*d*dOglt*F?&liNAGW9vvU z+P+1noZz=dS_r^PC$Q#lfz1qDtbep`*?<;i5_b;sjXWp?Wl|2uJnM($<@=yAsah#U z80@)u66-eoL=+_#`i{ZR0S0TP4Cgnbu@uQtfQI6hnqnESsbSiB>$3PaH@C<@`49l7 zjPY-&3!#r>eQ&h55r4pYQN+0}4P5ifD!*FI4M&f0RGy1@coohkGmVHf&<0 zGoil%RV1o(E1D3)zJI6uf48HvCzK0udP)PJS0q;jn(Fa?_oIK?_*T8PW88lLL*V81 literal 6569 zcmeHLZBr9h6kZS&P(Tq2iX@D!qD7@*B_IllC^8~cu|f?m+K5z95)2xFBv7qYi-6TC zNwpSqvU&b@b^d*->%x%ZrN z{+q-&5BG)c004NzZ;Rap0Mn6A=hvFN__05-6z>P*{0I;EKd6%ch|Q8 z{+D(K?V+vt`aVu-m+uqmAixNzs|oK&KK;z9lU{Gh--dAP;rQr$&V)u(GV|8(2Ga51~$HRD@Ha4 zfKT3l6Ji1r02joReC0F;9sSc~BWhgdVUrjMm=dsXKs11nfRTW`4%ickHg>e>a(A|%^Ky{NB+fk=VEi3zKv5Fh zN`l+WmSK3Ju9ON#a&1W@5{bbMZ(KdIy^$d31MMbpCeJ9L4U#QdA4eF$>(pSMz^^hS z6IXen{z4JdN;{H!&nALqB4{O!oLr}jMjo#y_`?oK@++27K3-aN8!%1Shu3r$bUo=N zg`VN72h@3z4WaVn@&pdX`kyYX@YdXl|@XoeOwHFf8NrSa1}V?@X809bpW}z(+m^e6osm$>8nD=5%ab!q zWn8c@haFvPS7=!|t{gvtK>DzsX4{K06OMCfpI^zh9!ulrjc9sPrMQo+CeWX6HZ};2 zns=&pQ!lAP#B~eFabDFBZTWO7Vh(40!Lb(WXqQr5Thq4IqxlFvbb9;!`&8Ok=oI2s ztuy_lkLGH1u~2l}#-WdP8s2UX5pgFI_d{cnXa^Y4UC+;?8#=6DzPL1`Eu2$0e1`}% z=y7yj^-m~-0@?zkHQrNkpNi$jv}RQezGD68OL)9HNm$p=wk~N};%ai0op(al*YdYK zyM_^lW5R=m0jfl_lOjzx&~rF^s-WQ$)*kLS3=@|cuc{tqOUhF+3bNP{)PrqH;4%Vu zjRg8wK>C=-(3jItmRy@ClBmQ11Mhi_c@H{Abz$zPCndK6f%(L}b7v(a2=0O0S1aa$7F=L77=>tTS}iIOrJ=85+vIt<2GA#w6FA zm0+r4W;4<+27vNeP9vUsf}DMQSEc!}Sgf(4t^@M?$PpiReBq}X1;b?XfG)}lT^Sb- zSn*Xi<$pYchO2(;(x^9ZttD5&fzB?a%TF znrD(;mV1dTHPZ?+$_kQwT9OBd?`zcmuDImZKHt%=Zozw-5&#sC@*5(AIt{#|qW?*H zJF#^Me3prZ9k6J+6QyHkg6cXj6$#6}n?~tClpz!dX#7iAwelY0WD}PwoR_qIwVv;= zn|mMO6sb}hO}gadqN>?!F^f5&*Xz@HQ7BkB9s%pAOA+2lt`%4;mirgE3EGuhJtw-D z*AXCKS`W7TG$`6Q`^|E-ib$;eD@Edi}Ia`Y@X;YbuK55>@2Yt%QIY zTr$jx8N3plGPHJvq&kwoY-(ks)T3urqKuoOsN{izc_R|_M&VeAbyxoD?#f+(pJRII W)qriSy4KH8*5kJ%#@5H|JM|A&ZpYvN diff --git a/assets/share/combat/support/COMBAT_SUPPORT_LIST_GRID.png b/assets/share/combat/support/COMBAT_SUPPORT_LIST_GRID.png index a9839d3716e3d3f0fff58fd87e73f78469ba087f..d9d6f7597b54aadab44b4285e284a6d58397e495 100644 GIT binary patch literal 93810 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 z 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 diff --git a/assets/share/combat/support/COMBAT_SUPPORT_LIST_SCROLL.png b/assets/share/combat/support/COMBAT_SUPPORT_LIST_SCROLL.png index 1e4779af371bfc47fb6491cdc29cb9658c905e39..8e7040fec5da0a01238e9c28716c52f63f354041 100644 GIT binary patch literal 7731 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 literal 6359 zcmeHLZBSI_6@K>vSWpPX!B91@rVQf@E#0vl>#8i9RIQtA#AZ5tv?A*?$mJkzV`4HBHKIgr6?|!&A znKsiu;r`$*``-7w=RD^*&pCI0y25)&3AFvb#1>sCL`SPVWY-_yk6=B()}U@Wc4 zwECf+>}tHx_gKrKlE19Eu&fQLyTtf&d6(zuRY-?Ri7T8JepYko5jUBd-ESD z==sohw({gA=N#Bz>pB)?)4d{Ipj&blQ{rZ9U>f>&vrGOHr2B&u9{brNMwg(#$D#qvlpg@J@)F8nvcsTzZjUF zupdIgwdb(qjUN5LH1_}guc~ChfEW;I<)6YS&-t7~Ka~X4A zxlEwl)dyEH;IYxsjS%JqdliM%> z|3nO)DcURWHNx0AJOxRpidcO{TpA$X@+kUa&cTLAaXDU|0_iAsU8#t!qJmB_>~og^4a zE!n6g`@K)AfK1wADua%>l#GSdK4PL6+wgn~@OTb*G?({B3rE%DdQy{qH#<1Dz#cK6tySSJNeac8iE4u-iGIX#6K8p9 zbW!_|4o9&A%LuYXrAOOfl|6Ut28Gg0v32D9kk}qOV8b_JmDbx%s?WY_1X9bQ*MNjF zw&RK{5K^Z#p2pUrhMo*aIk2Ip0_}lk9;ponule$EIP@*zf|&9mN<1l?hu7Js30R7OBL_k zLiWlciuhi=x$VBabGnWUi8|Qb4nED8;J4LkNOz;tVF-uko~#I8l-hb9rjg~S26?WC z;5pUVD#uABh~m;_3j77Y->M@4=r_Ta?o! zwG67_k)iK&^~XZ!NRIYB!wq&*0}D1EE-#&2Uw#o$a}oZdmFNcj!zpM!mOY`2iH`Gm zoKxR(E9`M;g5pd4u4`AKj9iG(ENQHC#+Mnp#1u4oM=!Q}!K&U6>SmR+LIA?Y6wdt) zgI)EvbPtN+MCgE6HT{;)nJS=PHHJGN26Wsvhq0~bZSIO~aoB67HunUz(IRRYCPqTN zi)vPyEDxBB;KX{W+naEWx5XVL+ssQ?Y9KiB?U$KTQL?QY zhGy`7P(SW;*qR)B@yI^_Zo`OAeRzuz45wiqm*~K&Q_!naf}Nz2LzPJhF1kw41;v=x zRLc9>jh6N3U_+e(R)OCSh%ZwQiO(oaT(rM`ST$@#5wuF%n> zPh&Z!V2CTEntkIzjv5TM#O}@kYX(xLeUuPww+?J_ZLgX3Ix_6HgH9b{6&YGs#cx`= za~DaQPyb9%Gz7`e?5VOD#=NTxkO1S0IOjU%b@)qVk$C}HLF2Yy>TLD@l;WWG4V`I3 zCBA0_x_YR3+dA!rH}Bt#pq}Kg)Y*72OG8uLna^Zvcu=14u>n^te`9cc@&mvTDge5{ zQ!<_Lm|WENYN~;uibz@F8b$4+X=(FG@PZQ;E%uH3G00p+V3m>=q(xF@2JKu~iURLI zHOn`$`3K8SP2YMDSRL*OAD6!B-wy+)<&E+;($}Rv;hugcJw0wNIr&)hVFq#Gnqf$s vUHl4vBniBveMu_LX60+bzSlRky6lQicO1EMY4q=Fs2WXc)~{}P*s}XSVOf1r diff --git a/assets/share/combat/support/SUPPORT_SELECTED.SEARCH.png b/assets/share/combat/support/SUPPORT_SELECTED.SEARCH.png index 10a1f29b5e548dd650f1d3d22ef4c3a548bf3f9b..b7966bf7c3f1c80ce001547d44bdf12cc88d282e 100644 GIT binary patch delta 13067 zcmZ9zc|6qHA3r__w*^-!$#&z@mAxqY(lT)`H!d2J7*v)bTZ|d&d%A8#DYqL*7{!&E zVI=#Et&p;fvP||t*0Jw~nfadgsPFgp`2E$xnRCwjoY#51p3mp&bxu>xA8rY}soVvf zT>2e(`U1XZy5Bd^^SYDD`gnIlhLmJj{vT5!zoYC9?8B(N|9Z7F^IGY>U-H^2(_Y^2 zbZO6;`9-|Rsl=!~rX<#=Qtm(6IT6uIz1ev>tN*Tc>G-HO`c<^`A-^g8X}?d{X|2M$ zu6r|GLNX;xsnzh2WJ-`u==-_SOr_Os@44NY$@-3J68yYK!PI%Zp* zN3LFW;)QHgrInfLHkgIa;E%mp)6m2~oEH#pF8Zj_ENxC#a#Y`S_21LF6INj-BurLh zAeON9hHE_nfpCw0hS?ncm1~mLLh4TgyuI6%w5dU4`v@3Pter~o_-sD z)XqDqM84Q><+fWSI%Jms=mtfij5)W;Z;!;rj(GPZC`4%ZhxsTNEq=_gA?*N@2Nze0 z&)@Ny>)2l{Jv_VN7(|>C%3o?RpTCMquU=pUL=JyWHQ$Q6ixNa22nxbX`DLhjY}_Wp zG-CCuaszj`>RrttcQDTwOGoimYjZ`}Ap`9q2%VtIU{{f)M>in-4;ZL+WMHkz+;?$( z;HF-7LiPM^4R@_C?OWur$jHe{@)q5q25())^$z%a_2}SlFcO7k-Pc=<$tc5*f37dIuC)*Y!zbJj#L8KH zM}25Z-rT%vGhI%I^DY6Jphn$Cp)3n8>I)@-UhWji8KF@2C8l#iV)Hf9wO%{pOLp+$ z*!u~kWwUF;LzfisS9h;Jai;nFlch&GZ7L_uH$?d})*)ug*eMB-a+7->KdP}v_S<`+ zIXn-pJ$<8R(^N4gI8z`E1$_uSh{=I1?IITJQ#p2ozQ-!W?>Z^GgNPCVtz$Fyx)AvR z>o!EQ>N5uV7DIa_?VeF(9$2)z&(s0G9}A$Btm~z^Tt_=r^Wu%*7VqS85&Vs<;M{Y&q%;i!ek?;_k@4W^(%p#G&D0*o$1EDoeWJ5a{jt~``x#bn(oQmotEaDp?qMcvs=T{2l$sff;??G|O#Og19!??tj#;w+p*Jyao(e)lGZt(C6e2 zu4ShBn^X+dQcGrD=81t5CCD>eHZ1#s8a^j;?nB{FZ=ODnJICV|Ty1dOXhcW%mPYm7 zcU{i3h1l(iTTIdFVfCuvZ0)GY`)*sqIZ;FMtbcJR36U6zA8vvNf)1fq*l+oqu@Wtz z@O3j2T1;bbmn9;<)z0yz^0`yAxvEWg@NT#D_n)sV6W1!nBdW$Do;-Q-P~Ld;ZE__v zy+Mb#QEUA)wq(q^UzDs={d|8q0H!q+s~HaHaIT^&5J_E!bW}FHhbK-qi$84)!xCa zG5taO$OEzpG^`Rm>=zK>jGP{U^NFmZ(I}uSGn*Y zAU;;QdcEF!tJd7)(1WU3R@JOFZ&BM?RWfL;A6LDYl<#u!Vr=YPh43Pl71H5kdE{*0 zn}??%(`~eyy0p=t88$byY!&$h!q1H^I~E*2=vZ3{W~uhb_uz%M9tTkS=vxN z-HbCC!1!|>&a-U04_TUhPfAaC+6 z1?qdZz@kye*7t2*B%8)q?X!W(KQy*!+loM)VA$YBXtx zdE{oQ!UO6@b?4YUV3ol?)fi?An5+&@e9poaE}u2uqLx;UMENFL*vv?0gw3zgO(N;T z?}A&ZpphKgG!zWZ;rj{t!L!qgsk{jcZ`_u9w{ZN4cG!YBzE$56iMB#U{xT@PV0{m4 zyW)8-{~q3FJ-Lmf$8J9tpMI3$2OyzU9Zw> z?nAFvZdRRw0k6za5DF~u`?Nrue`zdNxk6VAo62*W#}U^{tH-S8!q;^r z_#|w)t0p^f!Nu5k=t(iJb+FiH>mE9?A;GdvWv+6rDgnANH*i)BURZ+QeEpG{^5cTu ztyIr&8}53}tlMnQ*-ddx4~KK)@4PwpJUcwFKT!3plh(`|2*W3|@B;+KleLG%Se4Xj zDbCO*i$JVia-sZeK)!KVb)E=+a=PHWcv#zJ5j`DCVv`h*0EQ<8mc+sAaZWw)ukM0hL0YfNoE+B~_i_J<+7%;lDm1qS4GqhzP zDqe6>bk^%{(l!1qkbCFZ|w>^`jDRZPfPCnxkl zW8YF5AagF2rC@Fkb1hq%fcvQHwtiMJX!w!CT;;fO!a&4*D|m%Dh_vKsj{i_364`f? zz$9WSO@?Rk)GzW18joD$PiU#2^{7*F@OtRk5suog5ETvs@{bQED z4V_;DVC}}`K=)#6ZK=QWtqGH?i<+>lF*-BxAgvf#=WDhldske+Wp|4rJC;8aFl+Zf zm9yIlZFT3QnSBv1e}#>b}Pw`rdEjLd&!tjM8c`@#H`oku+on18LB-4C7s>M=kBNE{y&JANu?~S zr8i*kQ`(HgyPnWF9D6TNEU4Ug3oJ$*M(U-KCb|OS!SXgh1#d!#fSXOZWNQx()%u?` z;Ljc);4YzW2!b%K=?v`NMA+(u24i9jly#JYhbtdUk-i{+wV&ez@G8~DOC_ExY1Bg>BO^39$8wznuGN@A|x4tUl`%!i+IKm9tl+)q$> z0`Y8qFV9AL?eir7OFS*aOdv}AdP`;&MjBE{6dm2SKejg$O3=Tt!z61hRkrn8ZUX2` zh!$bW|H;7iVgv9S42r*i3tCbVS2>gS10!6A^PJ|A?X~n03J;y=?6%8ABJ2KA0()#L zMYvdXZw-w)wL`fiwWbX_w0{3`S**d1e;`PerdkbGyNc%6dZJg{`N(;fT*Sd(z6e z=s%nfM=spP`~yZz;1C0c>D>{wb&a=^dFNC?)6*P3GeOg0PHK5c%3M$W?(v4`U$qi0 z%0L}4ow_maGQUT8MV3YXwLc~Jq*H?mEKfM)cIEw2Hr=~JCOh%w{JdDU!%tu~whSTv zlwih@`|NNNn)t@kWZgzlox!$(*Z6!Rk*Tb?(O|s9L(BwJ+dR9YfRfgsHC= zT~xa-ppYd^Fc#ND22ZSN(KR#TyQN;xGyI-STkW}9+i39{=)bIvn0rOJzxNR;8#dyy zCL%v$+N67=MMgS7(aQq)+A5^~ImNR79zT760%%=PEwQIDI82TZ937}P9LwjWMXZ1u zLy?^psLu2K9q>WcRLr=F8eH}45PwGUHTDE8?gJ&J!r@&?gAXP{Kg>vTJ;tiC-<2nY zx_t<|ZubN5cV;KXPEd8GdM-O8+drXyXW9b<@0r<%xTO(oD+qZ4i_&lTq}wDqMV0xU zJ@piytig1Ok^HY8R?2L0geQASp1~4yN9;`pWW0a1@vO6A6q6IqmI@vNzeVk;F@Dt= z`|6q6-Ic&=%KrmR0KB|c@WIx}7D$gtt4S*~wZ!T+yy&V>Ke52{l~=NzC=4TJXc%(c zPV`D>5`q3J9(B_L0Z63k`6SP5%=O@+$S!{J3ZwzH>+gfiG4vzlsU z{T=w~2zdNg_2`*A#rxS=mKpPPss0tw*ZEZRlpJPrYhZbp<~F|)k)^0%3cc2KPeGtja`3VyO5Zw;D+kFrP9Zq z;ADia%vGH~4tDi41LGXI*^y-_5_1^JI25q4Qdm6_+$)fo7u9|ZPXiZ|aP~@VN zV*d6Sc;X06)H-8@sGRzN=8t0Ih3xFXD7VsaHrr)0u@}oHsC=)|s&xi${w!mra%jSV z7_zjY-?x%z%r85*iZJ^Il01iBhVzD^?uyG9R*ZKU`*T)!>xZ_zJk)R3^85GiKaHqJ z$Wdpd6!DwTpiy`_3^Rcbb};>H{-#Jvry7* zZBKYc|2fx5>V>XS7BVq*(}tmbH-HyVy;yErHu>_dqA3M7p-YSaem9r9yK3~DcI1%C z9Pe8IZ#6)B#)*GIZ1DNp^%dxwoKL#r+(Glmr2uXZ6fh9V@@r7QU)UK$ z@spO8HeW7~=nvbJ=PxZoaDF+A?)iAl3*!K?Lq=0m6K)`Sd^odz`(S2F1un zd9z3ZRv)dXl)@5K)Y$=uo?r%C@O=ulkvg)s$vav33hxo?M{q=eLe9hZj|XREJTQEO z2rgDnIF%fGP-73tDC=HfPOB;Yy)vvuBfEr|&GE6e{k)5)(JD?1fCl{Jz=#^G|7wc& z5>!;o1N3&YKMqAn7U&qQ4MixhSrUC&e5aCo7< z_X@$g^0-feoJoi~#N+Db(9?STH({|grT{X4p<2d_bC0a#n(5;F`3*^ahO;JMFrs5r zW{pKzj1O!MlvXm;iEMY=wqoBzL^?e*-AYjwHCq~Wi)>J=TGLhOIr5!Y426yK!?1Dp z8^A{E^(#kfZpG{ZpQ`D!gVhZL%bSul0ass^m;X>^%Ya!R(_J+k*cTCqo%RwfL;f!N z3pgVD{@>8W6P;oviq zv?#T##D7!1d>3h+Dheo9&Oi269=Y+3Q&dL?I38Ti)E2)*T=gFAv6d(!rS`DhKr7`h zO!St4^`RXg0SB@G)Pd{vGSfoO)jiS8+n|FEF7h;U0;$nz7ff9PT32^1*JoXy8Ayw2 zVzDrF{S@|6=1Qi+{y&L}T8_yFh#6VG#Vx201*{8H*@S^E zUjT2}EdZQ{KonsX)ENbx$Wt{Lao8#Z*$&VN(29S2SB z(Im{e%_JlZMw0z$zJ%b=*mUZti+f-skPgii(x3w;X!Azv2^;1A@8_I0zBtp(I9a1` z3-01w!Vpjh5U?Hl8kbJcP73mL2a^KXQ@S^*^xqrj-~(jK5f~>4!R$%RbA9onS5>xH zU`>IYF0F@{@gL*k@0&Z@RzoSiYftt{7WBc#fXe&cpugsNYvX%h&qFwC4{0aQ_7g2a5+=`K+yEtd zH#J52$9ILrerS{eU*&rX7zE4ihE#0wdk-08b=XlM3a!H%Y_tX?QPX?A+R*oNpnn+{ zj9~901n)K-a2i+naK;;*N%A=f8sW4rbUFREGTm0U*t#Q`jUTqW;l2k6Ik}AVg2B15 zao{VK*)8*XPJnjK?wZ1^xv#9gW$IqLOysBOJ6Pn2&&awTYCp};7(z7Uskk7a!m&oILk}oG}y(L`=h7(#)G_I;6R+c9rBDtB(6VzAFUP{8aKjDlRSd?}6 zcd^cWqFJpc>5UY;bx_+ZUqPg$x+F?ESy^0IaW6*!Ux5kG+iO54T1`9clT5jxZXrn& z>U`hWDD!=928`QZ)_89DS7168O4cCp=vgnC`gvc>F{}bXR9H-o+}u$F4w( z|DioK<#`Ab)*lug;&-r}p?-D8rubO{s92bAahb9vVZVC2JeJJ9UlOmQn|UvnkF4OH zW+^W8YG2EYD8wX@Ec}cQeiZ^DhQJkOrs=i%Sp247UB{v)#(q4ClDTl6fyK(G!pVDv zm;kfUKOW#$~S+{S( zDM;VjHF(I@t9Ugm$0mInjrS26gdo4M0~D9)mCyCMDMvCX&zd#-rk(yT9fnTyM`|rE z%Y4p8sumQz`6>ga`fx6FF)ckku7U1hQ)#BGLwYGwGV}HjSYR6s2=iDPxmu4Re*$j2UJ}<%Z{^r_y^3~xNl-oR{p$yOwr;0dF%N_ylhm{QS#;gY6;S3r?4~S_=G|yRnx$@tn$-k8*oxCGw%WU%JC+QVP6d2h-^m6c(w-EQqk4 zq?Y)St)yTa0V-~x&G^Xr!<>-SL+)TKFbpH1y77T3{^r-tjSg8U@?X9F*?#C}UdNThr%Ofns}g3$9lpqN znDG+850`adAE$iQP%wY-GZ?_3QFZdW@+$#uo z={Mr>lSWyIp)XvIW+Us`e$jxxyPp8%tY91d3i_jtSwPcE`xvX?S*+-ia|Zkf*W7$Z za=k*LR5BNff<8+t{H>+F9itfnWzUZ9@YU#T-BT&8wA^!udC}!YCadvE-Ib?;aJd+0 zwZuq-iw`Z{Bfe6PS&&=RkD=*|A2NFk#aM&9%1XIU}l}LMzo$6 zn!jUxm3@f0v{BkvY$BDnt@Stt#*GIKTNwz*XVcRaMn@)_;3!lVj+ zKS=i9v>;n%TK7KU-a8V5Hydu9W#lPnC$#b{Yr3aKHZy?2qXqTPbx1lXt}o%d>H^ym z?Gu616NtjhkJva|=tMd=*QnNUXhCeeq@ejp-aZ0jPAKwtTWcgUENVi&dW?s!w}mt6?(qHK)-F=`6D{@e$p?~dz zu31c^zg3yv7^#mp&e*DJXw5wlGgiB1k_)3}GYad3^F&(29UV_-XdUm#Ks_oCb@9C5 ztF2s!>=h?Zt-sb)vFXDzMyfDJK>rClgn~q|@8{6|`%W)iE*w~c7GFp~tG5!Ox(A~_ z-x~T+uE%SBpp-;4JVZZ+W|l{uhI5B}c(l2w-2UK+M^X}6de++_4bx>-GvXVrt?+@3 zs*-HSiMSdEO}9(PMQybtL~juzKz-gkVqBYl5}*jKHIt1lmA+hY*lubFOABQ8KezPDL$(a`J%&k#(hYSlUC#(vYEhvRe^5)X z5_@d9N-}QeQ)Wh3_d*+&KMsop1dgi>^*k{M)T_k zSLrAf(Nnf=KUD-tGu6l+!F`&$D#jx*6V5F_&2RjJ>BE;Z;YG)fjem$}@<}bbZ+~w8 zVF6f_79!H59%tm-9_Z9HT!H@Gty+eBUFhFiLLyT_V9Nl04Q!32J${7OQE!Run(J7g zbLln9N(THgfkqHdZiI1CWlK&sst#>bP+x~YaQp#sQWzA)uWC&6Jx%tm8;SjBpLF!Q z$^~R|c}$fI<@`MmhofgqzUK?R@PmY43j|RzTz_|Sd5J(^I;uDB9lUW>M8MO(iYIB3 zl4QD`clmngCwoWRb%_2q{V-bmF)DcO9ssfk4Oiq(V-(bw;H zx{=Mv`Hb{W3%cNg45C4FMJsq|@yGV%Lg|c|U}d!Y99#M^*pRF4>O++b{{B4?18@Gp z`TX6|++yqDD6>!;@i$)BTp#zgpUPMiAM>on-1dNb_ zg7eb?=&RaSKBTOSygfi%NLJ4p1vOWj?Ydxvbje&}!RypD6bd*1DXXHL0SE>+LVJO|>t<86wc` z23K$F3UReT5F;;(@g0134J36P+nBPl6ThhZQsoL5S5_hz*!)wCl&p|#gdD8t*pdVl zrP;(ism?!DI+(!-05Um-WtVmSNQ)ClFX5@_0%di}`$0Wnwu(j17(9gc>1nyL+a2@` zmmvg?&7LzniLiAknC;B{0DpHn6hUl$SFq9+u>cLk8?3%`i@#_G+Tq^bxjpG8dwhHl zTE4bs`Fp0`S<&`_ml4}31Mm#3F8{E_C8{W~VL%?6b=vgeb<0LD65LP+dd*%yw7$}{ zQXORu$y(+744MJR+t&4`DCaKjo%8Ak&tzU(_D^(VZPylEg_(VjBjVxtHBk!7NJ->= z?N9tf(hShnBsnan>S;Lp{bfg)tcgtZY?m%84=~|VX9Q74nEgeiOLMmZggbUH1e2T+ zGsG{)0rY=(RQ9ZY_dkI-9)lVVi$M;f<&-_Cq;o zc}IoIt#QdQRDYU{C@c_x;O%2?#LYbVwMz``cLCMkaX#;R8Xe&GXs&)uKkSN;uA^g5 zlyxm-hR@H@!c3@h2xSaXy}>WcJ}a8`)v_Y~?Jjt56L6Ai(`OQWo{?n75KpBhzKtBq zg~M>(=^#Nl^EH`z-G=C7_9vqyaC^j2gzd}TKARm13{?M>PEAcDGNgN3BkZaPGn7?0 zZ=Cu_QXRtp*)B#M0Rv$UqM{x;-lm&5N9~=8k0rgnc~Ro7-Wj0Fo}&fu>b`{1EvIbt zY<0|JkJ^6FN&p9wccfPngMy#YiA|lxNZh#}!LPi6Uo^D^s=nRMK7o3Y6q?=gdC`d< z^ktzSlvpu3&q#5x)ayTX-I8-^TyNmxwI^B71&pfXaTgt7ex*;Cxo|YqxjN*TQSgP# z*ChMK#S{lVBm;?NbN*IqY>9ddb<*l}aPKCc!y<*|AO5eilm3bnA5YP&{k2xA4OnPd z12LYI!@S9rdS*nkS%NDJTw!KKrZwaCc<$Y7B&zeXMpp~}1;PXbZsmbwwH9W+Wv^h1 zW?sqX{irAfd7gdK)$d}7YIya)=TC{d+o_PHfI&Wfm*Ycu&4f&n5;>RrnB8o)UA`snI!U;B>oGw z?FVd6$(UZ~C|PdLo&KqV6Q2!XjR~IbT~lN>&4_p-L$X;GP1ts1o%{9t?^rHpOubU- z15JH=`&Gk~`hQ=b4S4LF56uj+K=yacl$R*z`GASG-^}%X5nJ73o%jj`qi1<JNs%jBiFj zt+(M+vORQ3-W~i6U$+06?}glxYM_6~>Bf^HJ}dAl1AY{Ca}soK8obgSDTb=qv!gjt{6jg0Hm(AGYNp;eqm(n9v#Yd)p=jRh3Mc)LED?hx>=lz>yXN5u~&_-Xc zLCc)##f(lkcmadjYpvbCS$p>c*5M|0$MP>ex8eFLD7zb6Raq5y7$0j-O$t46?OE(W zt?3^!5@(+-Q(p0Q@A(VcU;<=r`Gdkj^ZzL;k#CTwmkyDD^tWNLe($ah&*%)iX zY>w@Xp(+$BL<3VYeZDS;&mTCjGkLG1(>V*LSLAw&O*o>#P=UiXHc?_(}AeUg9oi#_4xT82uZH%b6 zMV>4n@ zrB=Y8^zFqmWD=^k7Z^@N{yKHG z1>2RKm}?KyaM`Nhyk6!_Rk$b*@^8jXgxR2ItC+bTqXZMNRkvWo>RfjAJr}xc@A9{7*QPK?y|L z*-7zEA!(|ax({w)7J~XtWAhuKW8e>Tlh%!x3wqEcSels`^Y7nbJ0gC8DYfRLG$C&X z?6$#5{J%Sk+IxTT^Sqj+vjQLU*l&EPK;LQ15NK5|vsXR{_D?Wuhp)ew!S8KC2unBp z!$&2mK2)25?&ub+5JJp2t6#?7sfS?D3ik9^pOC@A%aVNHxb_8d9XD`-yUXLvs9t^U#vX5bX|OI`~@=9nm#zp z?XPq^yyy21_`M7g5uB$cfg-XqcyB`Wq3K;YxP7-f5rnAvce?MKvqg%G9b72_gI#8j ziyAUVN;&nYPHZ*_375<$clucfwCA5E0dLg*0&kb{0$~#D&79QHRU(lN?K$KL+CY+k zxAf9W?)=Y0BWv`f0fNFO;VRGR+9ffA9gs?7&n>WvVO6 z%Bk|d|9@fljO(YC|8GsWCT*ri3n_zY*T|NKiMLEG)K75fm~!Tc6iFg{$K^g7nlC`3 z+LRr71%5`L-+{~arCrr{=uMeH?h}jt?(i3fG-kFR_k*Vn9Yjt9nS%M4ecLp~bW_+J z3^CgT3LiP$Uwtt-{^QWFJH1L6CLU&?7Qzx)$BnvgcQQL9LV8v1EA9tZX-S6FlOeR` zjI2c$X7a%5p4DmHpFrTiLQ=k{C?Ei7VKe4#TUZvbElL2=dMLF z*DAn5Lco}-)6PdzWD4G#C|iiLPea&%E@Rjgb&s+CgAo%Bor4%_zsLYt@aK(&koP+tkUPHgn&p+PtdOcsy*YkWlpRd>JMI7Oq$D?XlOmofN zUE6<)_&CT8cxdc*PJLMDUdXAx!gJ-8u1Gfw+-{uq!1$oX2kkAJH*Ar0a9-nVtF1*3 zvi>c>@l~zCp)a*k}&fEX2EY;k8G4JkWrJoaZ-c9YyJLtSYBVdPCT<)rZ71CE% ze(CLI%{^lH`Gt9fO_=qb>$z>j%o^++ly7?d!H+jB=)eku4oiy;EmM`i0HAJp?uw0Y z%6ns&ljEG`Yc8Ch{5hM#=GnIsQyq6b@C=Q9BAOk&$36rAaM!or!{zmtS!UHyJK*m} zr_KDQlJ=O<_gvGUr#hANuxl>c)+gESNyi8P^qII5RXtAvsh7qu9fu9BVvY2P2Ok}{oWo_vvA^wt2<3X|G_-L6`VcKP9rtuJblNykVE z4NN#8_ekr@>0&zP>z$0PtANt@yz1?18BH(t>H)%~qzyW=JHx_Aw!_;GD_tDV#os zcUuhM(5)AzsiX!gvJvGsjlk;hq=|MCUFk8FFs?AY1{y(vNQr6X)M#@iHr>{AOza+g z@LZ#yCbGg+@4w8Y0N7k6L@8ReuW_u~Ujy z_MByxbUbvh8hfpNqS*#2)e=xFRem}phaY;otS-2zq5JceF;X#R$$^Wy%U({Q{jwJ!bK{S0ZXx_@EG)d~ela0bSC7vs}> z7LNDP324>o^xe@{%8ReOLyF0X$811N)l5cEX$!Ks-_J-_JgrEtzoB+R7IYUicAXEL zIC0dCMe;YUuIPTW^eFtjJZFKQZ6Gx`*ZAS<{7L*=FdPR`Zx*ceyb-u$VqFOV+i4~> zaGYk-FHdQjR%eQas%EBXDcQ}jZs)c=s^Xv z)|f~sTZXOqC3^IFR&1N2Fy+Loy`^${MPG%4_MEYmR0lK*s0IWcW`E=46LG?Q)>EW1`0!$K~E_w@2ng7f(fsYXS>vY zBU#!jrWLNzre{s}XU$$?#14NvU1$}~XRSPdZ5w%_5Nk+RQY>SKb^2M2{q4e>X=}C; zvKmR8KOJYRMU#I=r+SY+un_JY)N4ZuYkdynX0p{aaGM18LeJ6xdOUuzRxTfVq>QP7R(Oq zs2Zy&o1M#=eaxB3FJuKbTlO~H^0>tqd7)xGS<%dn74lN7=cjfmSQeYd4&;|vjlXk3 z(mubkMl_X@71gav4zq(wpL9NacIGZ~B4fVso?7hKt-_cN2A?V9Gg4Z~_Whll`H5TM zUDG&Gp$eKhdZz#0Q27@uIrS}xIo-(MR}Sn{pJ|yM>tzlJ11T1e-wj*fpuK+mdU|Sl zgKvi-bii`_O-ig_z*`h)-{0;*o*VSDj>3ezGG+vyRn#0?C(hJ&fHnMd z87FEsBbHZ&^{NcbUyiDoX)BDKY0a8{ch73Py{u+DW+;j?)@+T5<|eWEv8)mJ#TtVnAxMwDEfX59hvrt4kPObf8dr$r&Z}1pNEcR5++5?0 zMUTANlodVvMB2$duQ$tj;&Dp#CwcNzj&&q|Rblz&4^j~PsWJ6z%f18iUoU1&ZxYR@ z&pu0`r}CkPm3!xuZ4^9I%vwS{xg?0mX~1=(?8Bvhc37-iwieiQ;!;a+)>qI;yrc1d!|?P znFFgykv2Vbvuo1y23y66qR|+dH5;itY zo|`P+g0`?)gi=-vb*$#|*0L&hrqeX?L?r3SA zu<^6lyqg#8{#aH}Db44sEEhs`z~EvHko_QSI;r-`vIFjj|M0zBw1oe=ACo9Ed8)ti zXr%)TZpzh!ri|E9M}k4dK5;GEAkDo_?`>?_tEl)X5L)G+4@gcu zcJFgQ*8H#ftxU6f(WxnKHwxxIczAvCLTGus-Uz?e1Q8(iUlDYAcE|e^n?je^K(+o( zVfbU$uQ}1!{OrXwUJ)an*wD4iz~~Q22AG?bc@RolEfb)AGegtS-pr>Egr zqE^@3p_n078FS2245V9#l)%4%+F&32(`w3M4zt2Vg6Upw*I#(O&?7c?2vfOmnDWzS z*7TS*J)zi83f3N*enPp~(yG#^K1{1aDkj*fOGeu}#0YNMw{?0`yvtHkmw%8mj8~1y zgmlGt@-A2iKeh@AUdnm{HbV0YgO&BkZF7vMr)j%@C9;c$zAM~j!2q2 zCf?T*(1MTm_qK%&{L6h`({H&PlKi@`{HR-#%PBX>%D4RXyI9_)j`)WX5KwH?7kj-f zot8rZlwkj%(6SD)P8Y9>sc(UNVk5mkaPX}hh36M44Rd>USNoH;G%PFA6CGIopAILe zD#45j-c80(?{W4U-p7@FER;U&Uo5xT2A{HTYGJn&+4lu$uUk{1=s|OwHZOh%%Tyy zUt$=2h&{+>MC~m&vNkGg8H)O_MeScpnTC1y)V~Q(XR*}FH)e_jW4=}KX(mA}DLOAa zpaBOR1X~Y>dbvDpQ=)lT$w1~4i|}-n1Jbfg%bL6P((wU%SL#{pK%9xIgsWD>H%xw8i6+al)*E!=WN%pKE7Dm4DE#1y2P!H zflIZvVW98?b&Nhg(X*4a9a73F;F}(j2bi?rFat!e1!PfH^&J(;pk*%!Q`}|4MG4sF9FYGh9r2TzV_PITXH2Wo}a;-h~c#m1x@9)z=)uxrbG+!Uc z!d?!4GkQO`xui4eO_;gIl4a1CJBXB8jbpBW-&Esac5w-U*K>raKa;4FC5&s^MCkV& zLPUvdh}Dn!;e{7A%tHAOo&kX=|J{n8!B#&>{Eg!Pn=DYj6j#i-;@bWa&>|1Vsr^9BQT#h!1|@wQ!PLZ$8XPWQ7Y#e>6}VSs zAU?`e7bE-}sd1u^R~7ZV#KMa*pe(RR0+z;xP{AKTiW0R$2zp~iOqTdSMjKVBPR&+$fXe!kjqE=WUTbXw zLmX*W0JD_4e+l>5m(zAW$(C_zA@PeW!fHff`h`$#QM#ObN#TesL_$D_xUhs}M$|(l z#3&~uCa#kO6){f-<1fa$bVc=_ZN%2a{}PJHAfolF2jpOG%m9(fMQdv)F%! zs3=0~hY|PY98JfX{Krp=&a9(`2HtYvgZqnusKUF89V=>gjDY4a^p%WA) zF49}Tkawk7xT9Ytx{2#&C25Lwd+(DjgT ztJ8s=D&>1TWhdP8U?iydimmk_AVfMP$!*)xl37CvztEY_!jPL}c5#QGM9rg<8)P0CL&M-7WbkVaRdU6is7bi712~*w zTsvg^M?jZ)$j(bcq{18<=*EJS!!p6#|zf_z(o*h z`m>$0=^pU=?mFs#6JFs+D0>a;G$ni6_rbyJN1)1G()>yx{HBe7s_T)#@0#hMuQFBQ zb;;Y-q6{5@8Py>-JnWiBLNOo!w13(Oh0C}{l1k3b)|s#9)x!{K8a@W91J2nBk%?}# zD3*cmv4?H6x%cpgZv~YflB4$ zYq@NCR*u_LVhfy5Ql)mDpbFJbtPVjwJOZqi7hfx@*yK5Q30LWHks#Q+>=_gf@(1u*!^qH+Q35t@lXqxCaUq|C1=uf zA+Nj2RF6?IU#{X>n0eRCZQM-V7b8HVrfR^tl~`;cshTl+ioXF;Zh+OVSf$!;?Di5p zBGTClbe$ox(%r|X?>$7-&vz_V>1Y~t;1r4UG+?l%K5B&#T&bi)J-*P$>q&D{zX>M` zTtWkFKhq{iRezvdq)YiMw4yS2UJ}&_Gv~)fg@YywY5#T$pdvoA;z0H27($tz71Yzx z$=+>wthwibU+*KVK>Qm4l*sTrpgF$Fi1T`tM2z7Dc1@!a{#z(;+HyYLpqv>K1Bx;g!c}iq!nP^v;}s#|PH8hBkMcGWC-1RYw-a(A zS3`~vLC?O8q0*5q8x^T#!HhRDCFe+=YzL&zWPVC`>Grb})CszX=I{}eMc?j(I=~Ds z?;2>Aip!bVxV~KuN^7&1VCuj?oi23+aac!^)+5@07iz9Kk|Dl9Fx`8+t0r(NOD=J#a{40z%lWvgPK^x#Ov2ZjXlrzd3*RR>l zue9=05EmEN8uhW4BBwOj0=^21cji*wrl7e|ZG_51TLwk#+shq?;tN%bs;UL;xet6;wYagV5SY3Up zbV8GEe4m{LPjx^GmiC{;Te;L9Tjy=`)XEN$(P|@%aP8ge)J~7xz7=bN!y!y?a}TlV zRfD+u{-yDT5q>sNVe4s>kbNaL-jyYdtH1pP(n;TJhu!^N?#XCW8bdOD#0VR&LSEI^ zCO3RkN1h^nMKZ;ISMiBM*4%m3lo}0nbS8i*E`0k4c%{OLgR9 zG4pPSm5sqdRy%^#1kx)hDBdx~4d5x>m$HR#wnp078u?S4mNUP>^E zyS_UM93$5CSeE9zR+eU-X{>`XPXrW?t;3aYHx7aPnakUec?JmpKs`cxK9;`i# zjMTDkaSFcP`RhsCk{%*f3HR=mL?*UgRPD1IC0D7$EBpX~wG9i9MzUR6JiF!$5n0Z{ z9>N;i?i~vbTYRi&aAdycUiQ9XgP>>eQpMGwH*fiEg!NRZMb{z9TgU=K^v>};vJ6LcTJjEuyT+)j zXTshD2*s@6{h#&_@yzi5c)i}CC_ntub=dT=RRfycuMIM#(f+GZw+v1wQN@d1i|?ew#{nQd@}(+KgYC}^?G^{{ zjYu(=>7FvR`Aj>$i7)*WKX?uy?n^u7VvtfBYZfszA^H diff --git a/tasks/combat/assets/assets_combat_support.py b/tasks/combat/assets/assets_combat_support.py index 10dc25671..0eb09775a 100644 --- a/tasks/combat/assets/assets_combat_support.py +++ b/tasks/combat/assets/assets_combat_support.py @@ -17,30 +17,30 @@ COMBAT_SUPPORT_LIST = ButtonWrapper( name='COMBAT_SUPPORT_LIST', share=Button( file='./assets/share/combat/support/COMBAT_SUPPORT_LIST.png', - area=(67, 645, 93, 669), - search=(47, 625, 113, 689), - color=(204, 205, 205), - button=(67, 645, 93, 669), + area=(39, 645, 65, 669), + search=(19, 625, 85, 689), + color=(204, 206, 206), + button=(39, 645, 65, 669), ), ) COMBAT_SUPPORT_LIST_GRID = ButtonWrapper( name='COMBAT_SUPPORT_LIST_GRID', share=Button( file='./assets/share/combat/support/COMBAT_SUPPORT_LIST_GRID.png', - area=(64, 158, 155, 627), - search=(44, 138, 175, 647), - color=(117, 107, 124), - button=(64, 158, 155, 627), + area=(36, 148, 128, 629), + search=(16, 128, 148, 649), + color=(135, 124, 133), + button=(36, 148, 128, 629), ), ) COMBAT_SUPPORT_LIST_SCROLL = ButtonWrapper( name='COMBAT_SUPPORT_LIST_SCROLL', share=Button( file='./assets/share/combat/support/COMBAT_SUPPORT_LIST_SCROLL.png', - area=(448, 156, 452, 592), - search=(428, 136, 472, 612), - color=(130, 133, 153), - button=(448, 156, 452, 592), + area=(472, 162, 476, 598), + search=(452, 142, 496, 618), + color=(150, 151, 166), + button=(472, 162, 476, 598), ), ) COMBAT_SUPPORT_SELECTED = ButtonWrapper( @@ -59,28 +59,28 @@ SUPPORT_SELECTED = ButtonWrapper( Button( file='./assets/share/combat/support/SUPPORT_SELECTED.png', area=(54, 202, 60, 220), - search=(40, 146, 68, 628), + search=(15, 148, 45, 629), color=(125, 126, 131), button=(54, 202, 60, 220), ), Button( file='./assets/share/combat/support/SUPPORT_SELECTED.2.png', area=(53, 547, 59, 565), - search=(40, 146, 68, 628), + search=(15, 148, 45, 629), color=(144, 146, 147), button=(53, 547, 59, 565), ), Button( file='./assets/share/combat/support/SUPPORT_SELECTED.3.png', area=(54, 432, 61, 450), - search=(40, 146, 68, 628), + search=(15, 148, 45, 629), color=(139, 139, 141), button=(54, 432, 61, 450), ), Button( file='./assets/share/combat/support/SUPPORT_SELECTED.4.png', area=(52, 417, 59, 435), - search=(40, 146, 68, 628), + search=(15, 148, 45, 629), color=(140, 138, 145), button=(52, 417, 59, 435), ), From 9d514f4e9d2001966016da81166acb29992ecd55 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Wed, 8 May 2024 21:46:53 +0800 Subject: [PATCH 077/114] Fix: Matching dungeon from map interact --- tasks/base/main_page.py | 1 + tasks/dungeon/keywords/classes.py | 44 ++++++++++++++++++++++++++++- tasks/dungeon/ui.py | 47 +++++++++++++++++++++++-------- 3 files changed, 79 insertions(+), 13 deletions(-) diff --git a/tasks/base/main_page.py b/tasks/base/main_page.py index 4ea355919..acd41fdcd 100644 --- a/tasks/base/main_page.py +++ b/tasks/base/main_page.py @@ -54,6 +54,7 @@ class OcrPlaneName(OcrWhiteLetterOnComplexBackground): result = result.replace('累塔', '黑塔') if '星港' in result: result = '迴星港' + result = result.replace('太司', '太卜司') result = result.replace(' ', '') diff --git a/tasks/dungeon/keywords/classes.py b/tasks/dungeon/keywords/classes.py index 88bccc895..5b9da5a36 100644 --- a/tasks/dungeon/keywords/classes.py +++ b/tasks/dungeon/keywords/classes.py @@ -3,7 +3,7 @@ from functools import cached_property from typing import ClassVar from module.exception import ScriptError -from module.ocr.keyword import Keyword +from module.ocr.keyword import Keyword, parse_name @dataclass(repr=False) @@ -183,6 +183,48 @@ class DungeonList(Keyword): else: return '' + @classmethod + def find_dungeon_by_string(cls, cn='', en='', **kwargs): + """ + Args: + cn: Any substring in dungeon name + en: + **kwargs: Filter properties, e.g. is_Echo_of_War=True + + Returns: + DungeonList: or None + """ + if cn: + string = parse_name(cn) + lang = 'cn' + elif en: + string = parse_name(en) + lang = 'en' + else: + return None + + def find(obj: "DungeonList"): + name = obj._keywords_to_find(lang=lang, ignore_punctuation=True)[0] + if string in name: + return True + return False + + # From SelectedGrids + def matched(obj): + flag = True + for k, v in kwargs.items(): + obj_v = obj.__getattribute__(k) + if type(obj_v) != type(v) or obj_v != v: + flag = False + return flag + + dungeons = [grid for grid in cls.instances.values() if find(grid) and matched(grid)] + if len(dungeons) == 1: + return dungeons[0] + else: + return None + + @dataclass(repr=False) class DungeonEntrance(Keyword): diff --git a/tasks/dungeon/ui.py b/tasks/dungeon/ui.py index c9eec1434..f9d91a412 100644 --- a/tasks/dungeon/ui.py +++ b/tasks/dungeon/ui.py @@ -612,23 +612,46 @@ class DungeonUI(DungeonState): logger.info('No dungeon interact') return None + self.acquire_lang_checked() + ocr = OcrDungeonList(DUNGEON_COMBAT_INTERACT_TEXT) result = ocr.detect_and_ocr(self.device.image) + dungeon = None + # Special match names in English + # Second row must have at least 3 characters which is the shortest name "Ire" + # Stangnant Shadow: Shape of + # Quanta + if len(result) == 2 and len(result[1].ocr_text) >= 3: + first, second = result[0].ocr_text, result[1].ocr_text + if re.search(r'Stagnant\s*Shadow', first): + dungeon = DungeonList.find_dungeon_by_string(en=second, is_Stagnant_Shadow=True) + elif re.search(r'Cavern\s*of\s*Corrosion', first): + dungeon = DungeonList.find_dungeon_by_string(en=second, is_Cavern_of_Corrosion=True) + elif re.search(r'Echo\s*of\s*War', first): + dungeon = DungeonList.find_dungeon_by_string(en=second, is_Echo_of_War=True) + elif re.search(r'Calyx[\s(]+Golden', first): + dungeon = DungeonList.find_dungeon_by_string(en=second, is_Calyx_Golden=True, world=self.plane.world) + elif re.search(r'Calyx[\s(]+Crimson', first): + dungeon = DungeonList.find_dungeon_by_string(en=second, is_Calyx_Crimson=True, plane=self.plane) + if dungeon is not None: + logger.attr('DungeonInteract', dungeon) + return dungeon + + # Join result = ' '.join([row.ocr_text for row in result]) - # Calyx (Crimson): Bud of XXX -> Bud of XXX - result = re.sub(r'Calyx\s*\(.*?\):*', '', result) - # Stagnant Shadow: Shap XXX -> Shape of XXX - result = re.sub(r'Stagnant\s*Shadow[:\s]*\w*', 'Shape of', result) - # Cavern of Corrosion: Pa XXX -> Path of XXX - result = re.sub(r'Cavern\s*of\s*Corrosion[:\s]*\w*', 'Path of', result) - # Echo of War: XXX -> XXX - result = re.sub(r'Echo\s*of\s*War:*', '', result) - # Divine See -> Divine Seed - result = re.sub(r'Divine\s*\w*', 'Divine Seed', result) - # Destructio Beginning -> Destruction's Beginning - result = re.sub(r"Destruct[a-zA-Z0-9_']*", "Destruction's", result) + # Special match names in Chinese + # Only calyxes need spacial match + if res := re.search(r'(^.+之蕾)', result): + dungeon = DungeonList.find_dungeon_by_string(cn=res.group(1), is_Calyx_Crimson=True, plane=self.plane) + if dungeon is not None: + logger.attr('DungeonInteract', dungeon) + return dungeon + dungeon = DungeonList.find_dungeon_by_string(cn=res.group(1), is_Calyx_Golden=True, world=self.plane.world) + if dungeon is not None: + logger.attr('DungeonInteract', dungeon) + return dungeon # Dungeons try: From a4d6c94a466c17db9152e1edf8321f10a777510e Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Wed, 8 May 2024 22:00:41 +0800 Subject: [PATCH 078/114] Fix: Food_Improvement_Plan does not exist anymore --- tasks/assignment/ui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasks/assignment/ui.py b/tasks/assignment/ui.py index 895fe0c38..efbeb0f67 100644 --- a/tasks/assignment/ui.py +++ b/tasks/assignment/ui.py @@ -40,8 +40,8 @@ class AssignmentOcr(Ocr): (KEYWORDS_ASSIGNMENT_ENTRY.The_Wages_of_Humanity.name, '[赠]养人类'), ], 'en': [ - (KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Food_Improvement_Plan.name, - 'Food\s*[I]{0}mprovement Plan'), + # (KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Food_Improvement_Plan.name, + # 'Food\s*[I]{0}mprovement Plan'), ] } From 880bb2a911b8e874512e538bfab42a643b65201b Mon Sep 17 00:00:00 2001 From: Schwarze-Katze <68176965+Schwarze-Katze@users.noreply.github.com> Date: Thu, 9 May 2024 15:36:52 +0800 Subject: [PATCH 079/114] Apply StoredCounter for counting farm times (#419) * Apply StoredCounter in counting farm times * i18n for SimulatedUniverseElite counter (probably used for display in future) * change display style to "counter" style * Configure ConfigUpdater.redirect and rename `SimulatedUniverseElite` config item to `SimulatedUniverseFarm` * show `Rogue.RogueWorld.SimulatedUniverseFarm` in disabled mode only when `Rogue.RogueWorld.WeeklyFarming` is checked * treat Rogue_RogueWorld_SimulatedUniverseFarm specially to display value only; i18n for SimulatedUniverseFarm item; bugfix on counter convert * Set display style of stored counter * update counter despite whether farming is enabled --- assets/gui/css/dark-alas.css | 5 +++ assets/gui/css/light-alas.css | 5 +++ config/template.json | 4 +- module/config/argument/args.json | 12 +++--- module/config/argument/argument.yaml | 5 ++- module/config/argument/stored.json | 12 +++--- module/config/config_generated.py | 2 +- module/config/config_updater.py | 3 ++ module/config/convert.py | 7 ++++ module/config/i18n/en-US.json | 8 ++-- module/config/i18n/es-ES.json | 8 ++-- module/config/i18n/ja-JP.json | 8 ++-- module/config/i18n/zh-CN.json | 8 ++-- module/config/i18n/zh-TW.json | 8 ++-- module/config/stored/classes.py | 21 ++-------- module/config/stored/stored_generated.py | 2 +- module/webui/widgets.py | 53 +++++++++++++++++------- tasks/rogue/entry/entry.py | 8 ++-- tasks/rogue/route/base.py | 8 ++-- 19 files changed, 107 insertions(+), 80 deletions(-) diff --git a/assets/gui/css/dark-alas.css b/assets/gui/css/dark-alas.css index 7cdc1e819..fc6962752 100644 --- a/assets/gui/css/dark-alas.css +++ b/assets/gui/css/dark-alas.css @@ -83,6 +83,11 @@ select.state-light { background-color: transparent !important; } +[id^="pywebio-scope-arg_stored-stored-value-"] { + border-bottom: .125rem solid #7a77bb; + background-color: #343a40; +} + textarea { border: 1px solid #21262d; } diff --git a/assets/gui/css/light-alas.css b/assets/gui/css/light-alas.css index ed295b88b..d9de9a2bd 100644 --- a/assets/gui/css/light-alas.css +++ b/assets/gui/css/light-alas.css @@ -84,6 +84,11 @@ select.state-light { background-color: transparent !important; } +[id^="pywebio-scope-arg_stored-stored-value-"] { + border-bottom: .125rem solid #4e4c97; + background-color: #e9ecef; +} + textarea { border: 1px solid lightgrey; } diff --git a/config/template.json b/config/template.json index a177646a8..5c4e3cc7c 100644 --- a/config/template.json +++ b/config/template.json @@ -200,8 +200,8 @@ "UseImmersifier": true, "DoubleEvent": true, "WeeklyFarming": false, - "UseStamina": false, - "SimulatedUniverseElite": {} + "SimulatedUniverseFarm": {}, + "UseStamina": false }, "RogueBlessing": { "PresetBlessingFilter": "preset", diff --git a/module/config/argument/args.json b/module/config/argument/args.json index fdf6bbc68..f72d0ed47 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -1475,15 +1475,15 @@ "type": "checkbox", "value": false }, + "SimulatedUniverseFarm": { + "type": "stored", + "value": {}, + "display": "disabled", + "stored": "StoredSimulatedUniverseElite" + }, "UseStamina": { "type": "checkbox", "value": false - }, - "SimulatedUniverseElite": { - "type": "stored", - "value": {}, - "display": "hide", - "stored": "StoredSimulatedUniverseElite" } }, "RogueBlessing": { diff --git a/module/config/argument/argument.yaml b/module/config/argument/argument.yaml index 206d023c0..9c9efd878 100644 --- a/module/config/argument/argument.yaml +++ b/module/config/argument/argument.yaml @@ -258,9 +258,10 @@ RogueWorld: UseImmersifier: true DoubleEvent: true WeeklyFarming: false - UseStamina: false - SimulatedUniverseElite: + SimulatedUniverseFarm: stored: StoredSimulatedUniverseElite + display: disabled + UseStamina: false RogueBlessing: PresetBlessingFilter: diff --git a/module/config/argument/stored.json b/module/config/argument/stored.json index 37764ba0c..b9c86336a 100644 --- a/module/config/argument/stored.json +++ b/module/config/argument/stored.json @@ -294,15 +294,15 @@ "order": 0, "color": "#777777" }, - "SimulatedUniverseElite": { - "name": "SimulatedUniverseElite", - "path": "Rogue.RogueWorld.SimulatedUniverseElite", - "i18n": "RogueWorld.SimulatedUniverseElite.name", + "SimulatedUniverseFarm": { + "name": "SimulatedUniverseFarm", + "path": "Rogue.RogueWorld.SimulatedUniverseFarm", + "i18n": "RogueWorld.SimulatedUniverseFarm.name", "stored": "StoredSimulatedUniverseElite", "attrs": { "time": "2020-01-01 00:00:00", - "total": 0, - "value": 100 + "total": 100, + "value": 0 }, "order": 0, "color": "#777777" diff --git a/module/config/config_generated.py b/module/config/config_generated.py index bd2a1f641..49c89f6e8 100644 --- a/module/config/config_generated.py +++ b/module/config/config_generated.py @@ -139,8 +139,8 @@ class GeneratedConfig: RogueWorld_UseImmersifier = True RogueWorld_DoubleEvent = True RogueWorld_WeeklyFarming = False + RogueWorld_SimulatedUniverseFarm = {} RogueWorld_UseStamina = False - RogueWorld_SimulatedUniverseElite = {} # Group `RogueBlessing` RogueBlessing_PresetBlessingFilter = 'preset' # preset, custom diff --git a/module/config/config_updater.py b/module/config/config_updater.py index b0c85c473..38111c606 100644 --- a/module/config/config_updater.py +++ b/module/config/config_updater.py @@ -653,6 +653,7 @@ class ConfigUpdater: ('Dungeon.Dungeon.NameAtDoubleCalyx', 'Dungeon.Dungeon.NameAtDoubleCalyx', convert_20_dungeon), ('Dungeon.DungeonDaily.CalyxGolden', 'Dungeon.DungeonDaily.CalyxGolden', convert_20_dungeon), ('Dungeon.DungeonDaily.CalyxCrimson', 'Dungeon.DungeonDaily.CalyxCrimson', convert_20_dungeon), + ('Rogue.RogueWorld.SimulatedUniverseElite', 'Rogue.RogueWorld.SimulatedUniverseFarm', convert_rogue_farm), ] @cached_property @@ -863,6 +864,8 @@ class ConfigUpdater: yield 'Rogue.RogueBlessing.CustomResonanceFilter' if deep_get(data, 'Rogue.RogueBlessing.PresetCurioFilter') != 'custom': yield 'Rogue.RogueBlessing.CustomCurioFilter' + if deep_get(data, 'Rogue.RogueWorld.WeeklyFarming') != True: + yield 'Rogue.RogueWorld.SimulatedUniverseFarm' def get_hidden_args(self, data) -> t.Set[str]: """ diff --git a/module/config/convert.py b/module/config/convert.py index aadc72df4..799a5ac03 100644 --- a/module/config/convert.py +++ b/module/config/convert.py @@ -30,3 +30,10 @@ def convert_20_dungeon(value): return 'Calyx_Crimson_Abundance_Jarilo_BackwaterPass' return value + + +def convert_rogue_farm(value): + if isinstance(value, dict) and 'value' in value.keys(): + value['value'] = 100 - value['value'] + value['total'] = 100 + return value diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index 8097ae3d7..8000fe0d1 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -976,13 +976,13 @@ "name": "Farm 100 Elites Weekly", "help": "" }, + "SimulatedUniverseFarm": { + "name": "Progress of elite boss farmed", + "help": "" + }, "UseStamina": { "name": "Farm Planers Using Trailblase Power", "help": "Task \"Dungeon\" will no longer run, and all trailblaze power will be used first to claim immersion rewards, except for double events." - }, - "SimulatedUniverseElite": { - "name": "RogueWorld.SimulatedUniverseElite.name", - "help": "RogueWorld.SimulatedUniverseElite.help" } }, "RogueBlessing": { diff --git a/module/config/i18n/es-ES.json b/module/config/i18n/es-ES.json index 4be840c8a..f02621f79 100644 --- a/module/config/i18n/es-ES.json +++ b/module/config/i18n/es-ES.json @@ -976,13 +976,13 @@ "name": "Granja 100 élites semanalmente", "help": "" }, + "SimulatedUniverseFarm": { + "name": "Progreso de élites derrotadas", + "help": "" + }, "UseStamina": { "name": "Reclamar de planers mediante poder trazacaminos", "help": "La tarea de mazmorra ya no se ejecutará y todo el poder trazacaminos se usará primero para reclamar recompensas de inmersión, excepto para eventos dobles" - }, - "SimulatedUniverseElite": { - "name": "RogueWorld.SimulatedUniverseElite.name", - "help": "RogueWorld.SimulatedUniverseElite.help" } }, "RogueBlessing": { diff --git a/module/config/i18n/ja-JP.json b/module/config/i18n/ja-JP.json index 68d47cc78..ae9002956 100644 --- a/module/config/i18n/ja-JP.json +++ b/module/config/i18n/ja-JP.json @@ -976,13 +976,13 @@ "name": "RogueWorld.WeeklyFarming.name", "help": "RogueWorld.WeeklyFarming.help" }, + "SimulatedUniverseFarm": { + "name": "RogueWorld.SimulatedUniverseFarm.name", + "help": "RogueWorld.SimulatedUniverseFarm.help" + }, "UseStamina": { "name": "RogueWorld.UseStamina.name", "help": "RogueWorld.UseStamina.help" - }, - "SimulatedUniverseElite": { - "name": "RogueWorld.SimulatedUniverseElite.name", - "help": "RogueWorld.SimulatedUniverseElite.help" } }, "RogueBlessing": { diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index 46ec76f7a..63eaccea1 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -976,13 +976,13 @@ "name": "每周刷100精英怪", "help": "" }, + "SimulatedUniverseFarm": { + "name": "刷精英怪进度", + "help": "" + }, "UseStamina": { "name": "使用开拓力刷内圈遗器", "help": "每日副本任务将不再打本,所有开拓力将优先被用于领取浸器奖励,双倍活动时除外" - }, - "SimulatedUniverseElite": { - "name": "剩余Boss材料掉落次数", - "help": "RogueWorld.SimulatedUniverseElite.help" } }, "RogueBlessing": { diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index c8522459b..5b781c922 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -976,13 +976,13 @@ "name": "每週農100精英怪", "help": "" }, + "SimulatedUniverseFarm": { + "name": "農精英怪進度", + "help": "" + }, "UseStamina": { "name": "用開拓力農遺器", "help": "每日副本任務將不再打本,所有開拓力將優先被用於領取浸器獎勵,雙倍活動時除外" - }, - "SimulatedUniverseElite": { - "name": "RogueWorld.SimulatedUniverseElite.name", - "help": "RogueWorld.SimulatedUniverseElite.help" } }, "RogueBlessing": { diff --git a/module/config/stored/classes.py b/module/config/stored/classes.py index 83c6ca9ef..e66cb299a 100644 --- a/module/config/stored/classes.py +++ b/module/config/stored/classes.py @@ -211,25 +211,10 @@ class StoredSimulatedUniverse(StoredCounter, StoredExpiredAtMonday0400): class StoredSimulatedUniverseElite(StoredCounter, StoredExpiredAtMonday0400): # These variables are used in Rogue Farming feature. - # Times of boss drop chance per week. In current version of StarRail, this value is 100. - FIXED_DEFAULT = 100 + # FIXED_TOTAL --- Times of boss drop chance per week. In current version of StarRail, this value is 100. + FIXED_TOTAL = 100 - # Times left to farm. Resets to 100 every Monday 04:00, and decreases each time the elite boss is cleared. - value = FIXED_DEFAULT - - def farm_dec(self, delta=1): - self.value -= delta - if self.value < 0: - self.value = 0 - - def farm_reset(self): - self.value = self.FIXED_DEFAULT - - def farm_not_complete(self) -> bool: - return self.value > 0 - - def farm_get_remain(self) -> int: - return self.value + # value --- Times left to farm. Resets to 100 every Monday 04:00, and decreases each time the elite boss is cleared. class StoredAssignment(StoredCounter): diff --git a/module/config/stored/stored_generated.py b/module/config/stored/stored_generated.py index 5a8708305..bcb853b5c 100644 --- a/module/config/stored/stored_generated.py +++ b/module/config/stored/stored_generated.py @@ -51,4 +51,4 @@ class StoredGenerated: Assignment = StoredAssignment("Assignment.Assignment.Assignment") Credit = StoredInt("DataUpdate.ItemStorage.Credit") StallerJade = StoredInt("DataUpdate.ItemStorage.StallerJade") - SimulatedUniverseElite = StoredSimulatedUniverseElite("Rogue.RogueWorld.SimulatedUniverseElite") + SimulatedUniverseFarm = StoredSimulatedUniverseElite("Rogue.RogueWorld.SimulatedUniverseFarm") diff --git a/module/webui/widgets.py b/module/webui/widgets.py index ef729a882..37fbdec9a 100644 --- a/module/webui/widgets.py +++ b/module/webui/widgets.py @@ -325,11 +325,11 @@ def put_arg_input(kwargs: T_Output_Kwargs) -> Output: ) -def product_stored_row(kwargs: T_Output_Kwargs, key, value): +def product_stored_row(kwargs: T_Output_Kwargs, key, value, style = "--input--"): kwargs = copy.copy(kwargs) kwargs["name"] += f'_{key}' kwargs["value"] = value - return put_input(**kwargs).style("--input--") + return put_input(**kwargs).style(style) def put_arg_stored(kwargs: T_Output_Kwargs) -> Output: @@ -338,20 +338,41 @@ def put_arg_stored(kwargs: T_Output_Kwargs) -> Output: values = kwargs.pop("value", {}) time_ = values.pop("time", "") - - rows = [product_stored_row(kwargs, key, value) for key, value in values.items() if value] - if time_: - rows += [product_stored_row(kwargs, "time", time_)] - return put_scope( - f"arg_container-stored-{name}", - [ - get_title_help(kwargs), - put_scope( - f"arg_stored-stored-value-{name}", - rows, - ) - ] - ) + + if "value" in values and "total" in values: + # display as counter style + counter = f'{values["value"]} / {values["total"]}' + rows = [product_stored_row( + kwargs, "counter", counter)] + if time_: + rows += [product_stored_row(kwargs, "time", time_)] + return put_scope( + f"arg_container-stored-{name}", + [ + get_title_help(kwargs), + put_scope( + f"arg_stored-stored-value-{name}", + rows, + ) + ] + ) + else: + # display per key + rows = [product_stored_row(kwargs, key, value) + for key, value in values.items() if value] + if time_: + rows += [product_stored_row(kwargs, "time", time_)] + + return put_scope( + f"arg_container-stored-{name}", + [ + get_title_help(kwargs), + put_scope( + f"arg_stored-stored-value-{name}", + rows, + ) + ] + ) def put_arg_select(kwargs: T_Output_Kwargs) -> Output: diff --git a/tasks/rogue/entry/entry.py b/tasks/rogue/entry/entry.py index 3b4b2a72a..35b647919 100644 --- a/tasks/rogue/entry/entry.py +++ b/tasks/rogue/entry/entry.py @@ -350,9 +350,9 @@ class RogueEntry(RouteBase, RogueRewardHandler, RoguePathHandler, DungeonUI): # Always run return - if self.config.stored.SimulatedUniverseElite.is_expired(): + if self.config.stored.SimulatedUniverseFarm.is_expired(): # Expired, reset farming counter - self.config.stored.SimulatedUniverseElite.farm_reset() + self.config.stored.SimulatedUniverseFarm.set(0) if self.config.stored.SimulatedUniverse.is_expired(): # Expired, do rogue @@ -361,11 +361,11 @@ class RogueEntry(RouteBase, RogueRewardHandler, RoguePathHandler, DungeonUI): if self.config.RogueWorld_UseImmersifier and self.config.stored.Immersifier.value > 0: logger.info( 'Reached weekly point limit but still have immersifiers left, continue to use them') - elif self.config.RogueWorld_WeeklyFarming and self.config.stored.SimulatedUniverseElite.farm_not_complete(): + elif self.config.RogueWorld_WeeklyFarming and not self.config.stored.SimulatedUniverseFarm.is_full(): logger.info( 'Reached weekly point limit but still continue to farm materials') logger.attr( - "Farming Counter", self.config.stored.SimulatedUniverseElite.farm_get_remain()) + "Farming Counter", self.config.stored.SimulatedUniverseFarm.to_counter()) else: raise RogueReachedWeeklyPointLimit else: diff --git a/tasks/rogue/route/base.py b/tasks/rogue/route/base.py index 6137166b3..859c6e7d8 100644 --- a/tasks/rogue/route/base.py +++ b/tasks/rogue/route/base.py @@ -168,11 +168,11 @@ class RouteBase(RouteBase_, RogueExit, RogueEvent, RogueReward): # logger.attr("result",result) if 'enemy' in result: - # runs when one elite battle finishes, and decreases rogue farming count by 1 - if self.config.RogueWorld_WeeklyFarming and self.config.stored.SimulatedUniverseElite.farm_not_complete(): - self.config.stored.SimulatedUniverseElite.farm_dec() + # runs when one elite battle finishes, and increases rogue farming count by 1 + if not self.config.stored.SimulatedUniverseFarm.is_full(): + self.config.stored.SimulatedUniverseFarm.add() logger.info( - f"Cleared elite boss, decreasing farming count by 1, now {self.config.stored.SimulatedUniverseElite.farm_get_remain()}") + f"Cleared elite boss, increasing farming count by 1, now " + self.config.stored.SimulatedUniverseFarm.to_counter()) return result def _domain_event_expected_end(self): From 4e05aa37a33857b6ed015ca1644f79870d52c1e0 Mon Sep 17 00:00:00 2001 From: Yinhr <110515845+Yinhaoran1128@users.noreply.github.com> Date: Thu, 9 May 2024 17:40:41 +0800 Subject: [PATCH 080/114] Upd: Add new dungeon and weekly in gui by config updater (#447) * Fix: Food_Improvement_Plan does not exist anymore * Upd: Add new dungeon and weekly in gui by config updater * Fix: OCR of TM in dungeon * Fix: remove TM in keywords * Fix: remove TM in GUI * Fix: Remove redundant i18n in en-US --------- Co-authored-by: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> --- dev_tools/keywords/map_plane.py | 4 ++++ module/config/argument/args.json | 8 +++++++- module/config/config_generated.py | 10 +++++----- module/config/config_updater.py | 4 ++-- module/config/i18n/en-US.json | 8 +++++++- module/config/i18n/es-ES.json | 8 +++++++- module/config/i18n/ja-JP.json | 8 +++++++- module/config/i18n/zh-CN.json | 8 +++++++- module/config/i18n/zh-TW.json | 8 +++++++- tasks/assignment/ui.py | 4 ++-- tasks/dungeon/ui.py | 3 +++ tasks/map/keywords/plane.py | 10 +++++----- 12 files changed, 63 insertions(+), 20 deletions(-) diff --git a/dev_tools/keywords/map_plane.py b/dev_tools/keywords/map_plane.py index dec382cff..0d365dc50 100644 --- a/dev_tools/keywords/map_plane.py +++ b/dev_tools/keywords/map_plane.py @@ -68,3 +68,7 @@ class GenerateMapPlane(GenerateKeyword): return f'Special_{text}' else: return f'{world.short_name}_{text}' + + def convert_keyword(self, text: str, lang: str) -> str: + text = text.replace('™', '') + return super().convert_keyword(text, lang=lang) diff --git a/module/config/argument/args.json b/module/config/argument/args.json index f72d0ed47..01ba4ecaa 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -238,6 +238,7 @@ "Calyx_Crimson_Preservation_Herta_SupplyZone", "Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark", "Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains", + "Calyx_Crimson_The_Hunt_Penacony_SoulGladScorchsandAuditionVenue", "Calyx_Crimson_Abundance_Jarilo_BackwaterPass", "Calyx_Crimson_Abundance_Luofu_FyxestrollGarden", "Calyx_Crimson_Erudition_Jarilo_RivetTown", @@ -247,6 +248,7 @@ "Calyx_Crimson_Nihility_Luofu_AlchemyCommission", "Stagnant_Shadow_Spike", "Stagnant_Shadow_Perdition", + "Stagnant_Shadow_Duty", "Stagnant_Shadow_Blaze", "Stagnant_Shadow_Scorch", "Stagnant_Shadow_Ire", @@ -291,6 +293,7 @@ "Calyx_Crimson_Preservation_Herta_SupplyZone", "Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark", "Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains", + "Calyx_Crimson_The_Hunt_Penacony_SoulGladScorchsandAuditionVenue", "Calyx_Crimson_Abundance_Jarilo_BackwaterPass", "Calyx_Crimson_Abundance_Luofu_FyxestrollGarden", "Calyx_Crimson_Erudition_Jarilo_RivetTown", @@ -358,6 +361,7 @@ "Calyx_Crimson_Preservation_Herta_SupplyZone", "Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark", "Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains", + "Calyx_Crimson_The_Hunt_Penacony_SoulGladScorchsandAuditionVenue", "Calyx_Crimson_Abundance_Jarilo_BackwaterPass", "Calyx_Crimson_Abundance_Luofu_FyxestrollGarden", "Calyx_Crimson_Erudition_Jarilo_RivetTown", @@ -374,6 +378,7 @@ "do_not_achieve", "Stagnant_Shadow_Spike", "Stagnant_Shadow_Perdition", + "Stagnant_Shadow_Duty", "Stagnant_Shadow_Blaze", "Stagnant_Shadow_Scorch", "Stagnant_Shadow_Ire", @@ -1306,7 +1311,8 @@ "Echo_of_War_Destruction_Beginning", "Echo_of_War_End_of_the_Eternal_Freeze", "Echo_of_War_Divine_Seed", - "Echo_of_War_Borehole_Planet_Old_Crater" + "Echo_of_War_Borehole_Planet_Old_Crater", + "Echo_of_War_Salutations_of_Ashen_Dreams" ] }, "Team": { diff --git a/module/config/config_generated.py b/module/config/config_generated.py index 49c89f6e8..b4f3226b7 100644 --- a/module/config/config_generated.py +++ b/module/config/config_generated.py @@ -46,15 +46,15 @@ class GeneratedConfig: CloudStorage_CloudRemainFree = {} # Group `Dungeon` - Dungeon_Name = 'Calyx_Golden_Treasures' # Calyx_Golden_Memories_Jarilo_VI, Calyx_Golden_Memories_The_Xianzhou_Luofu, Calyx_Golden_Memories_Penacony, Calyx_Golden_Aether_Jarilo_VI, Calyx_Golden_Aether_The_Xianzhou_Luofu, Calyx_Golden_Aether_Penacony, Calyx_Golden_Treasures_Jarilo_VI, Calyx_Golden_Treasures_The_Xianzhou_Luofu, Calyx_Golden_Treasures_Penacony, Calyx_Crimson_Destruction_Herta_StorageZone, Calyx_Crimson_Destruction_Luofu_ScalegorgeWaterscape, Calyx_Crimson_Preservation_Herta_SupplyZone, Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark, Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains, Calyx_Crimson_Abundance_Jarilo_BackwaterPass, Calyx_Crimson_Abundance_Luofu_FyxestrollGarden, Calyx_Crimson_Erudition_Jarilo_RivetTown, Calyx_Crimson_Harmony_Jarilo_RobotSettlement, Calyx_Crimson_Harmony_Penacony_TheReverieDreamscape, Calyx_Crimson_Nihility_Jarilo_GreatMine, Calyx_Crimson_Nihility_Luofu_AlchemyCommission, Stagnant_Shadow_Spike, Stagnant_Shadow_Perdition, Stagnant_Shadow_Blaze, Stagnant_Shadow_Scorch, Stagnant_Shadow_Ire, Stagnant_Shadow_Rime, Stagnant_Shadow_Icicle, Stagnant_Shadow_Nectar, Stagnant_Shadow_Fulmination, Stagnant_Shadow_Doom, Stagnant_Shadow_Gust, Stagnant_Shadow_Celestial, Stagnant_Shadow_Quanta, Stagnant_Shadow_Abomination, Stagnant_Shadow_Roast, Stagnant_Shadow_Mirage, Stagnant_Shadow_Puppetry, Cavern_of_Corrosion_Path_of_Gelid_Wind, Cavern_of_Corrosion_Path_of_Jabbing_Punch, Cavern_of_Corrosion_Path_of_Drifting, Cavern_of_Corrosion_Path_of_Providence, Cavern_of_Corrosion_Path_of_Holy_Hymn, Cavern_of_Corrosion_Path_of_Conflagration, Cavern_of_Corrosion_Path_of_Elixir_Seekers, Cavern_of_Corrosion_Path_of_Darkness, Cavern_of_Corrosion_Path_of_Dreamdive - Dungeon_NameAtDoubleCalyx = 'Calyx_Golden_Treasures' # Calyx_Golden_Memories_Jarilo_VI, Calyx_Golden_Memories_The_Xianzhou_Luofu, Calyx_Golden_Memories_Penacony, Calyx_Golden_Aether_Jarilo_VI, Calyx_Golden_Aether_The_Xianzhou_Luofu, Calyx_Golden_Aether_Penacony, Calyx_Golden_Treasures_Jarilo_VI, Calyx_Golden_Treasures_The_Xianzhou_Luofu, Calyx_Golden_Treasures_Penacony, Calyx_Crimson_Destruction_Herta_StorageZone, Calyx_Crimson_Destruction_Luofu_ScalegorgeWaterscape, Calyx_Crimson_Preservation_Herta_SupplyZone, Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark, Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains, Calyx_Crimson_Abundance_Jarilo_BackwaterPass, Calyx_Crimson_Abundance_Luofu_FyxestrollGarden, Calyx_Crimson_Erudition_Jarilo_RivetTown, Calyx_Crimson_Harmony_Jarilo_RobotSettlement, Calyx_Crimson_Harmony_Penacony_TheReverieDreamscape, Calyx_Crimson_Nihility_Jarilo_GreatMine, Calyx_Crimson_Nihility_Luofu_AlchemyCommission + Dungeon_Name = 'Calyx_Golden_Treasures' # Calyx_Golden_Memories_Jarilo_VI, Calyx_Golden_Memories_The_Xianzhou_Luofu, Calyx_Golden_Memories_Penacony, Calyx_Golden_Aether_Jarilo_VI, Calyx_Golden_Aether_The_Xianzhou_Luofu, Calyx_Golden_Aether_Penacony, Calyx_Golden_Treasures_Jarilo_VI, Calyx_Golden_Treasures_The_Xianzhou_Luofu, Calyx_Golden_Treasures_Penacony, Calyx_Crimson_Destruction_Herta_StorageZone, Calyx_Crimson_Destruction_Luofu_ScalegorgeWaterscape, Calyx_Crimson_Preservation_Herta_SupplyZone, Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark, Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains, Calyx_Crimson_The_Hunt_Penacony_SoulGladScorchsandAuditionVenue, Calyx_Crimson_Abundance_Jarilo_BackwaterPass, Calyx_Crimson_Abundance_Luofu_FyxestrollGarden, Calyx_Crimson_Erudition_Jarilo_RivetTown, Calyx_Crimson_Harmony_Jarilo_RobotSettlement, Calyx_Crimson_Harmony_Penacony_TheReverieDreamscape, Calyx_Crimson_Nihility_Jarilo_GreatMine, Calyx_Crimson_Nihility_Luofu_AlchemyCommission, Stagnant_Shadow_Spike, Stagnant_Shadow_Perdition, Stagnant_Shadow_Duty, Stagnant_Shadow_Blaze, Stagnant_Shadow_Scorch, Stagnant_Shadow_Ire, Stagnant_Shadow_Rime, Stagnant_Shadow_Icicle, Stagnant_Shadow_Nectar, Stagnant_Shadow_Fulmination, Stagnant_Shadow_Doom, Stagnant_Shadow_Gust, Stagnant_Shadow_Celestial, Stagnant_Shadow_Quanta, Stagnant_Shadow_Abomination, Stagnant_Shadow_Roast, Stagnant_Shadow_Mirage, Stagnant_Shadow_Puppetry, Cavern_of_Corrosion_Path_of_Gelid_Wind, Cavern_of_Corrosion_Path_of_Jabbing_Punch, Cavern_of_Corrosion_Path_of_Drifting, Cavern_of_Corrosion_Path_of_Providence, Cavern_of_Corrosion_Path_of_Holy_Hymn, Cavern_of_Corrosion_Path_of_Conflagration, Cavern_of_Corrosion_Path_of_Elixir_Seekers, Cavern_of_Corrosion_Path_of_Darkness, Cavern_of_Corrosion_Path_of_Dreamdive + Dungeon_NameAtDoubleCalyx = 'Calyx_Golden_Treasures' # Calyx_Golden_Memories_Jarilo_VI, Calyx_Golden_Memories_The_Xianzhou_Luofu, Calyx_Golden_Memories_Penacony, Calyx_Golden_Aether_Jarilo_VI, Calyx_Golden_Aether_The_Xianzhou_Luofu, Calyx_Golden_Aether_Penacony, Calyx_Golden_Treasures_Jarilo_VI, Calyx_Golden_Treasures_The_Xianzhou_Luofu, Calyx_Golden_Treasures_Penacony, Calyx_Crimson_Destruction_Herta_StorageZone, Calyx_Crimson_Destruction_Luofu_ScalegorgeWaterscape, Calyx_Crimson_Preservation_Herta_SupplyZone, Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark, Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains, Calyx_Crimson_The_Hunt_Penacony_SoulGladScorchsandAuditionVenue, Calyx_Crimson_Abundance_Jarilo_BackwaterPass, Calyx_Crimson_Abundance_Luofu_FyxestrollGarden, Calyx_Crimson_Erudition_Jarilo_RivetTown, Calyx_Crimson_Harmony_Jarilo_RobotSettlement, Calyx_Crimson_Harmony_Penacony_TheReverieDreamscape, Calyx_Crimson_Nihility_Jarilo_GreatMine, Calyx_Crimson_Nihility_Luofu_AlchemyCommission Dungeon_NameAtDoubleRelic = 'Cavern_of_Corrosion_Path_of_Providence' # Cavern_of_Corrosion_Path_of_Gelid_Wind, Cavern_of_Corrosion_Path_of_Jabbing_Punch, Cavern_of_Corrosion_Path_of_Drifting, Cavern_of_Corrosion_Path_of_Providence, Cavern_of_Corrosion_Path_of_Holy_Hymn, Cavern_of_Corrosion_Path_of_Conflagration, Cavern_of_Corrosion_Path_of_Elixir_Seekers, Cavern_of_Corrosion_Path_of_Darkness, Cavern_of_Corrosion_Path_of_Dreamdive Dungeon_Team = 1 # 1, 2, 3, 4, 5, 6, 7, 8, 9 # Group `DungeonDaily` DungeonDaily_CalyxGolden = 'Calyx_Golden_Treasures_Jarilo_VI' # do_not_achieve, Calyx_Golden_Memories_Jarilo_VI, Calyx_Golden_Memories_The_Xianzhou_Luofu, Calyx_Golden_Memories_Penacony, Calyx_Golden_Aether_Jarilo_VI, Calyx_Golden_Aether_The_Xianzhou_Luofu, Calyx_Golden_Aether_Penacony, Calyx_Golden_Treasures_Jarilo_VI, Calyx_Golden_Treasures_The_Xianzhou_Luofu, Calyx_Golden_Treasures_Penacony - DungeonDaily_CalyxCrimson = 'Calyx_Crimson_Destruction_Herta_StorageZone' # do_not_achieve, Calyx_Crimson_Destruction_Herta_StorageZone, Calyx_Crimson_Destruction_Luofu_ScalegorgeWaterscape, Calyx_Crimson_Preservation_Herta_SupplyZone, Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark, Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains, Calyx_Crimson_Abundance_Jarilo_BackwaterPass, Calyx_Crimson_Abundance_Luofu_FyxestrollGarden, Calyx_Crimson_Erudition_Jarilo_RivetTown, Calyx_Crimson_Harmony_Jarilo_RobotSettlement, Calyx_Crimson_Harmony_Penacony_TheReverieDreamscape, Calyx_Crimson_Nihility_Jarilo_GreatMine, Calyx_Crimson_Nihility_Luofu_AlchemyCommission - DungeonDaily_StagnantShadow = 'Stagnant_Shadow_Quanta' # do_not_achieve, Stagnant_Shadow_Spike, Stagnant_Shadow_Perdition, Stagnant_Shadow_Blaze, Stagnant_Shadow_Scorch, Stagnant_Shadow_Ire, Stagnant_Shadow_Rime, Stagnant_Shadow_Icicle, Stagnant_Shadow_Nectar, Stagnant_Shadow_Fulmination, Stagnant_Shadow_Doom, Stagnant_Shadow_Gust, Stagnant_Shadow_Celestial, Stagnant_Shadow_Quanta, Stagnant_Shadow_Abomination, Stagnant_Shadow_Roast, Stagnant_Shadow_Mirage, Stagnant_Shadow_Puppetry + DungeonDaily_CalyxCrimson = 'Calyx_Crimson_Destruction_Herta_StorageZone' # do_not_achieve, Calyx_Crimson_Destruction_Herta_StorageZone, Calyx_Crimson_Destruction_Luofu_ScalegorgeWaterscape, Calyx_Crimson_Preservation_Herta_SupplyZone, Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark, Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains, Calyx_Crimson_The_Hunt_Penacony_SoulGladScorchsandAuditionVenue, Calyx_Crimson_Abundance_Jarilo_BackwaterPass, Calyx_Crimson_Abundance_Luofu_FyxestrollGarden, Calyx_Crimson_Erudition_Jarilo_RivetTown, Calyx_Crimson_Harmony_Jarilo_RobotSettlement, Calyx_Crimson_Harmony_Penacony_TheReverieDreamscape, Calyx_Crimson_Nihility_Jarilo_GreatMine, Calyx_Crimson_Nihility_Luofu_AlchemyCommission + DungeonDaily_StagnantShadow = 'Stagnant_Shadow_Quanta' # do_not_achieve, Stagnant_Shadow_Spike, Stagnant_Shadow_Perdition, Stagnant_Shadow_Duty, Stagnant_Shadow_Blaze, Stagnant_Shadow_Scorch, Stagnant_Shadow_Ire, Stagnant_Shadow_Rime, Stagnant_Shadow_Icicle, Stagnant_Shadow_Nectar, Stagnant_Shadow_Fulmination, Stagnant_Shadow_Doom, Stagnant_Shadow_Gust, Stagnant_Shadow_Celestial, Stagnant_Shadow_Quanta, Stagnant_Shadow_Abomination, Stagnant_Shadow_Roast, Stagnant_Shadow_Mirage, Stagnant_Shadow_Puppetry DungeonDaily_CavernOfCorrosion = 'Cavern_of_Corrosion_Path_of_Providence' # do_not_achieve, Cavern_of_Corrosion_Path_of_Gelid_Wind, Cavern_of_Corrosion_Path_of_Jabbing_Punch, Cavern_of_Corrosion_Path_of_Drifting, Cavern_of_Corrosion_Path_of_Providence, Cavern_of_Corrosion_Path_of_Holy_Hymn, Cavern_of_Corrosion_Path_of_Conflagration, Cavern_of_Corrosion_Path_of_Elixir_Seekers, Cavern_of_Corrosion_Path_of_Darkness, Cavern_of_Corrosion_Path_of_Dreamdive # Group `DungeonSupport` @@ -72,7 +72,7 @@ class GeneratedConfig: SupportReward_Collect = True # Group `Weekly` - Weekly_Name = 'Echo_of_War_Divine_Seed' # Echo_of_War_Destruction_Beginning, Echo_of_War_End_of_the_Eternal_Freeze, Echo_of_War_Divine_Seed, Echo_of_War_Borehole_Planet_Old_Crater + Weekly_Name = 'Echo_of_War_Divine_Seed' # Echo_of_War_Destruction_Beginning, Echo_of_War_End_of_the_Eternal_Freeze, Echo_of_War_Divine_Seed, Echo_of_War_Borehole_Planet_Old_Crater, Echo_of_War_Salutations_of_Ashen_Dreams Weekly_Team = 1 # 1, 2, 3, 4, 5, 6, 7, 8, 9 # Group `AchievableQuest` diff --git a/module/config/config_updater.py b/module/config/config_updater.py index 38111c606..543e9ce4a 100644 --- a/module/config/config_updater.py +++ b/module/config/config_updater.py @@ -430,7 +430,7 @@ class ConfigGenerator: value=i18n_crimson[ingame_lang].format(path=path, plane=plane)) if dungeon.is_Cavern_of_Corrosion: value = deep_get(new, keys=['Dungeon', 'Name', dungeon.name], default='') - suffix = i18n_relic[ingame_lang].format(dungeon=dungeon_name) + suffix = i18n_relic[ingame_lang].format(dungeon=dungeon_name).replace('Cavern of Corrosion: ', '') if not value.endswith(suffix): deep_set(new, keys=['Dungeon', 'Name', dungeon.name], value=f'{value}{suffix}') @@ -489,7 +489,7 @@ class ConfigGenerator: for dungeon in dungeons: world = dungeon.plane.world world_name = world.__getattribute__(ingame_lang) - dungeon_name = dungeon.__getattribute__(ingame_lang) + dungeon_name = dungeon.__getattribute__(ingame_lang).replace('Echo of War: ', '') value = f'{dungeon_name} ({world_name})' deep_set(new, keys=['Weekly', 'Name', dungeon.name], value=value) # Rogue worlds diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index 8000fe0d1..0f170e54b 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -262,6 +262,7 @@ "Calyx_Crimson_Preservation_Herta_SupplyZone": "Trace: Preservation (Supply Zone)", "Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark": "Trace: Preservation (Clock Studios Theme Park)", "Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains": "Trace: The Hunt (Outlying Snow Plains)", + "Calyx_Crimson_The_Hunt_Penacony_SoulGladScorchsandAuditionVenue": "Trace: The Hunt (SoulGlad Scorchsand Audition Venue)", "Calyx_Crimson_Abundance_Jarilo_BackwaterPass": "Trace: Abundance (Backwater Pass)", "Calyx_Crimson_Abundance_Luofu_FyxestrollGarden": "Trace: Abundance (Fyxestroll Garden)", "Calyx_Crimson_Erudition_Jarilo_RivetTown": "Trace: Erudition (Rivet Town)", @@ -271,6 +272,7 @@ "Calyx_Crimson_Nihility_Luofu_AlchemyCommission": "Trace: Nihility (Alchemy Commission)", "Stagnant_Shadow_Spike": "Ascension: Physical (Natasha / Clara / Luka / Sushang)", "Stagnant_Shadow_Perdition": "Ascension: Physical (Hanya / Argenti)", + "Stagnant_Shadow_Duty": "Ascension: Physical (Boothill / Robin)", "Stagnant_Shadow_Blaze": "Ascension: Fire (Himeko / Asta / Hook)", "Stagnant_Shadow_Scorch": "Ascension: Fire (Guinaifen / Topaz & Numby)", "Stagnant_Shadow_Ire": "Ascension: Fire (Gallagher)", @@ -313,6 +315,7 @@ "Calyx_Crimson_Preservation_Herta_SupplyZone": "Trace: Preservation (Supply Zone)", "Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark": "Trace: Preservation (Clock Studios Theme Park)", "Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains": "Trace: The Hunt (Outlying Snow Plains)", + "Calyx_Crimson_The_Hunt_Penacony_SoulGladScorchsandAuditionVenue": "Trace: The Hunt (SoulGlad Scorchsand Audition Venue)", "Calyx_Crimson_Abundance_Jarilo_BackwaterPass": "Trace: Abundance (Backwater Pass)", "Calyx_Crimson_Abundance_Luofu_FyxestrollGarden": "Trace: Abundance (Fyxestroll Garden)", "Calyx_Crimson_Erudition_Jarilo_RivetTown": "Trace: Erudition (Rivet Town)", @@ -376,6 +379,7 @@ "Calyx_Crimson_Preservation_Herta_SupplyZone": "Trace: Preservation (Supply Zone)", "Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark": "Trace: Preservation (Clock Studios Theme Park)", "Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains": "Trace: The Hunt (Outlying Snow Plains)", + "Calyx_Crimson_The_Hunt_Penacony_SoulGladScorchsandAuditionVenue": "Trace: The Hunt (SoulGlad Scorchsand Audition Venue)", "Calyx_Crimson_Abundance_Jarilo_BackwaterPass": "Trace: Abundance (Backwater Pass)", "Calyx_Crimson_Abundance_Luofu_FyxestrollGarden": "Trace: Abundance (Fyxestroll Garden)", "Calyx_Crimson_Erudition_Jarilo_RivetTown": "Trace: Erudition (Rivet Town)", @@ -390,6 +394,7 @@ "do_not_achieve": "Don't Do This Quest", "Stagnant_Shadow_Spike": "Ascension: Physical (Natasha / Clara / Luka / Sushang)", "Stagnant_Shadow_Perdition": "Ascension: Physical (Hanya / Argenti)", + "Stagnant_Shadow_Duty": "Ascension: Physical (Boothill / Robin)", "Stagnant_Shadow_Blaze": "Ascension: Fire (Himeko / Asta / Hook)", "Stagnant_Shadow_Scorch": "Ascension: Fire (Guinaifen / Topaz & Numby)", "Stagnant_Shadow_Ire": "Ascension: Fire (Gallagher)", @@ -535,7 +540,8 @@ "Echo_of_War_Destruction_Beginning": "Destruction's Beginning (Herta Space Station)", "Echo_of_War_End_of_the_Eternal_Freeze": "End of the Eternal Freeze (Jarilo-VI)", "Echo_of_War_Divine_Seed": "Divine Seed (The Xianzhou Luofu)", - "Echo_of_War_Borehole_Planet_Old_Crater": "Borehole Planet's Old Crater (Herta Space Station)" + "Echo_of_War_Borehole_Planet_Old_Crater": "Borehole Planet's Old Crater (Herta Space Station)", + "Echo_of_War_Salutations_of_Ashen_Dreams": "Salutations of Ashen Dreams (Penacony)" }, "Team": { "name": "Dungeon Team", diff --git a/module/config/i18n/es-ES.json b/module/config/i18n/es-ES.json index f02621f79..c3dd7752d 100644 --- a/module/config/i18n/es-ES.json +++ b/module/config/i18n/es-ES.json @@ -262,6 +262,7 @@ "Calyx_Crimson_Preservation_Herta_SupplyZone": "Rastros: Conservación (Zona de suministros)", "Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark": "Rastros: Conservación (Parque temático de los Estudios Reloj)", "Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains": "Rastros: Cacería (Llanuras nevadas de las afueras)", + "Calyx_Crimson_The_Hunt_Penacony_SoulGladScorchsandAuditionVenue": "Rastros: Cacería (Recinto de las Audiciones FelizAlma en la Arena Ardiente)", "Calyx_Crimson_Abundance_Jarilo_BackwaterPass": "Rastros: Abundancia (Paso del Remanso)", "Calyx_Crimson_Abundance_Luofu_FyxestrollGarden": "Rastros: Abundancia (Jardín del Sosiego)", "Calyx_Crimson_Erudition_Jarilo_RivetTown": "Rastros: Erudición (Villarremache)", @@ -271,6 +272,7 @@ "Calyx_Crimson_Nihility_Luofu_AlchemyCommission": "Rastros: Nihilidad (Comisión de Alquimia)", "Stagnant_Shadow_Spike": "Ascension: Físico (Natasha / Clara / Luka / Sushang)", "Stagnant_Shadow_Perdition": "Ascension: Físico (Hanya / Argenti)", + "Stagnant_Shadow_Duty": "Ascension: Físico (Boothill / Robin)", "Stagnant_Shadow_Blaze": "Ascension: Fuego (Himeko / Asta / Hook)", "Stagnant_Shadow_Scorch": "Ascension: Fuego (Guinaifen / Topaz y Conti)", "Stagnant_Shadow_Ire": "Ascension: Fuego (Gallagher)", @@ -313,6 +315,7 @@ "Calyx_Crimson_Preservation_Herta_SupplyZone": "Rastros: Conservación (Zona de suministros)", "Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark": "Rastros: Conservación (Parque temático de los Estudios Reloj)", "Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains": "Rastros: Cacería (Llanuras nevadas de las afueras)", + "Calyx_Crimson_The_Hunt_Penacony_SoulGladScorchsandAuditionVenue": "Rastros: Cacería (Recinto de las Audiciones FelizAlma en la Arena Ardiente)", "Calyx_Crimson_Abundance_Jarilo_BackwaterPass": "Rastros: Abundancia (Paso del Remanso)", "Calyx_Crimson_Abundance_Luofu_FyxestrollGarden": "Rastros: Abundancia (Jardín del Sosiego)", "Calyx_Crimson_Erudition_Jarilo_RivetTown": "Rastros: Erudición (Villarremache)", @@ -376,6 +379,7 @@ "Calyx_Crimson_Preservation_Herta_SupplyZone": "Rastros: Conservación (Zona de suministros)", "Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark": "Rastros: Conservación (Parque temático de los Estudios Reloj)", "Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains": "Rastros: Cacería (Llanuras nevadas de las afueras)", + "Calyx_Crimson_The_Hunt_Penacony_SoulGladScorchsandAuditionVenue": "Rastros: Cacería (Recinto de las Audiciones FelizAlma en la Arena Ardiente)", "Calyx_Crimson_Abundance_Jarilo_BackwaterPass": "Rastros: Abundancia (Paso del Remanso)", "Calyx_Crimson_Abundance_Luofu_FyxestrollGarden": "Rastros: Abundancia (Jardín del Sosiego)", "Calyx_Crimson_Erudition_Jarilo_RivetTown": "Rastros: Erudición (Villarremache)", @@ -390,6 +394,7 @@ "do_not_achieve": "No hacer esta misión", "Stagnant_Shadow_Spike": "Ascension: Físico (Natasha / Clara / Luka / Sushang)", "Stagnant_Shadow_Perdition": "Ascension: Físico (Hanya / Argenti)", + "Stagnant_Shadow_Duty": "Ascension: Físico (Boothill / Robin)", "Stagnant_Shadow_Blaze": "Ascension: Fuego (Himeko / Asta / Hook)", "Stagnant_Shadow_Scorch": "Ascension: Fuego (Guinaifen / Topaz y Conti)", "Stagnant_Shadow_Ire": "Ascension: Fuego (Gallagher)", @@ -535,7 +540,8 @@ "Echo_of_War_Destruction_Beginning": "El principio de la Destrucción (Estación Espacial Herta)", "Echo_of_War_End_of_the_Eternal_Freeze": "El fin del Hielo Eterno (Jarilo-VI)", "Echo_of_War_Divine_Seed": "Semilla divina (El Luofu de Xianzhou)", - "Echo_of_War_Borehole_Planet_Old_Crater": "Cráter del planeta devorado (Estación Espacial Herta)" + "Echo_of_War_Borehole_Planet_Old_Crater": "Cráter del planeta devorado (Estación Espacial Herta)", + "Echo_of_War_Salutations_of_Ashen_Dreams": "Ecos de la guerra: Tributo del sueño ceniciento (Colonipenal)" }, "Team": { "name": "Equipo de mazmorra", diff --git a/module/config/i18n/ja-JP.json b/module/config/i18n/ja-JP.json index ae9002956..e179a8a78 100644 --- a/module/config/i18n/ja-JP.json +++ b/module/config/i18n/ja-JP.json @@ -262,6 +262,7 @@ "Calyx_Crimson_Preservation_Herta_SupplyZone": "軌跡素材:存護(サポート部分)", "Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark": "軌跡素材:存護(クラークフィルムランド)", "Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains": "軌跡素材:巡狩(郊外雪原)", + "Calyx_Crimson_The_Hunt_Penacony_SoulGladScorchsandAuditionVenue": "軌跡素材:巡狩(スラーダ熱砂オーディション会場)", "Calyx_Crimson_Abundance_Jarilo_BackwaterPass": "軌跡素材:豊穣(外縁通路)", "Calyx_Crimson_Abundance_Luofu_FyxestrollGarden": "軌跡素材:豊穣(綏園)", "Calyx_Crimson_Erudition_Jarilo_RivetTown": "軌跡素材:知恵(リベットタウン)", @@ -271,6 +272,7 @@ "Calyx_Crimson_Nihility_Luofu_AlchemyCommission": "軌跡素材:虚無(丹鼎司)", "Stagnant_Shadow_Spike": "キャラクター昇格素材:物理(ナターシャ / クラーラ / ルカ / 素裳)", "Stagnant_Shadow_Perdition": "キャラクター昇格素材:物理(寒鴉 / アルジェンティ)", + "Stagnant_Shadow_Duty": "キャラクター昇格素材:物理(ブートヒル / ロビン)", "Stagnant_Shadow_Blaze": "キャラクター昇格素材:炎(姫子 / アスター / フック)", "Stagnant_Shadow_Scorch": "キャラクター昇格素材:炎(桂乃芬 / トパーズ&カブ)", "Stagnant_Shadow_Ire": "キャラクター昇格素材:炎(ギャラガー)", @@ -313,6 +315,7 @@ "Calyx_Crimson_Preservation_Herta_SupplyZone": "軌跡素材:存護(サポート部分)", "Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark": "軌跡素材:存護(クラークフィルムランド)", "Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains": "軌跡素材:巡狩(郊外雪原)", + "Calyx_Crimson_The_Hunt_Penacony_SoulGladScorchsandAuditionVenue": "軌跡素材:巡狩(スラーダ熱砂オーディション会場)", "Calyx_Crimson_Abundance_Jarilo_BackwaterPass": "軌跡素材:豊穣(外縁通路)", "Calyx_Crimson_Abundance_Luofu_FyxestrollGarden": "軌跡素材:豊穣(綏園)", "Calyx_Crimson_Erudition_Jarilo_RivetTown": "軌跡素材:知恵(リベットタウン)", @@ -376,6 +379,7 @@ "Calyx_Crimson_Preservation_Herta_SupplyZone": "軌跡素材:存護(サポート部分)", "Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark": "軌跡素材:存護(クラークフィルムランド)", "Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains": "軌跡素材:巡狩(郊外雪原)", + "Calyx_Crimson_The_Hunt_Penacony_SoulGladScorchsandAuditionVenue": "軌跡素材:巡狩(スラーダ熱砂オーディション会場)", "Calyx_Crimson_Abundance_Jarilo_BackwaterPass": "軌跡素材:豊穣(外縁通路)", "Calyx_Crimson_Abundance_Luofu_FyxestrollGarden": "軌跡素材:豊穣(綏園)", "Calyx_Crimson_Erudition_Jarilo_RivetTown": "軌跡素材:知恵(リベットタウン)", @@ -390,6 +394,7 @@ "do_not_achieve": "do_not_achieve", "Stagnant_Shadow_Spike": "キャラクター昇格素材:物理(ナターシャ / クラーラ / ルカ / 素裳)", "Stagnant_Shadow_Perdition": "キャラクター昇格素材:物理(寒鴉 / アルジェンティ)", + "Stagnant_Shadow_Duty": "キャラクター昇格素材:物理(ブートヒル / ロビン)", "Stagnant_Shadow_Blaze": "キャラクター昇格素材:炎(姫子 / アスター / フック)", "Stagnant_Shadow_Scorch": "キャラクター昇格素材:炎(桂乃芬 / トパーズ&カブ)", "Stagnant_Shadow_Ire": "キャラクター昇格素材:炎(ギャラガー)", @@ -535,7 +540,8 @@ "Echo_of_War_Destruction_Beginning": "歴戦余韻・壊滅の始まり (宇宙ステーション「ヘルタ」)", "Echo_of_War_End_of_the_Eternal_Freeze": "歴戦余韻・寒波の幕切れ (ヤリーロ-VI)", "Echo_of_War_Divine_Seed": "歴戦余韻・不死の神実 (仙舟「羅浮」)", - "Echo_of_War_Borehole_Planet_Old_Crater": "歴戦余韻・星を蝕む往日の面影 (宇宙ステーション「ヘルタ」)" + "Echo_of_War_Borehole_Planet_Old_Crater": "歴戦余韻・星を蝕む往日の面影 (宇宙ステーション「ヘルタ」)", + "Echo_of_War_Salutations_of_Ashen_Dreams": "歴戦余韻・現世の夢の礼賛 (ピノコニー)" }, "Team": { "name": "Weekly.Team.name", diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index 63eaccea1..8032f072a 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -262,6 +262,7 @@ "Calyx_Crimson_Preservation_Herta_SupplyZone": "行迹材料:存护(支援舱段)", "Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark": "行迹材料:存护(克劳克影视乐园)", "Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains": "行迹材料:巡猎(城郊雪原)", + "Calyx_Crimson_The_Hunt_Penacony_SoulGladScorchsandAuditionVenue": "行迹材料:巡猎(苏乐达热砂海选会场)", "Calyx_Crimson_Abundance_Jarilo_BackwaterPass": "行迹材料:丰饶(边缘通路)", "Calyx_Crimson_Abundance_Luofu_FyxestrollGarden": "行迹材料:丰饶(绥园)", "Calyx_Crimson_Erudition_Jarilo_RivetTown": "行迹材料:智识(铆钉镇)", @@ -271,6 +272,7 @@ "Calyx_Crimson_Nihility_Luofu_AlchemyCommission": "行迹材料:虚无(丹鼎司)", "Stagnant_Shadow_Spike": "角色晋阶材料:物理(娜塔莎 / 克拉拉 / 卢卡 / 素裳)", "Stagnant_Shadow_Perdition": "角色晋阶材料:物理(寒鸦 / 银枝)", + "Stagnant_Shadow_Duty": "角色晋阶材料:物理(波提欧 / 知更鸟)", "Stagnant_Shadow_Blaze": "角色晋阶材料:火(姬子 / 艾丝妲 / 虎克)", "Stagnant_Shadow_Scorch": "角色晋阶材料:火(桂乃芬 / 托帕&账账)", "Stagnant_Shadow_Ire": "角色晋阶材料:火(加拉赫)", @@ -313,6 +315,7 @@ "Calyx_Crimson_Preservation_Herta_SupplyZone": "行迹材料:存护(支援舱段)", "Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark": "行迹材料:存护(克劳克影视乐园)", "Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains": "行迹材料:巡猎(城郊雪原)", + "Calyx_Crimson_The_Hunt_Penacony_SoulGladScorchsandAuditionVenue": "行迹材料:巡猎(苏乐达热砂海选会场)", "Calyx_Crimson_Abundance_Jarilo_BackwaterPass": "行迹材料:丰饶(边缘通路)", "Calyx_Crimson_Abundance_Luofu_FyxestrollGarden": "行迹材料:丰饶(绥园)", "Calyx_Crimson_Erudition_Jarilo_RivetTown": "行迹材料:智识(铆钉镇)", @@ -376,6 +379,7 @@ "Calyx_Crimson_Preservation_Herta_SupplyZone": "行迹材料:存护(支援舱段)", "Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark": "行迹材料:存护(克劳克影视乐园)", "Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains": "行迹材料:巡猎(城郊雪原)", + "Calyx_Crimson_The_Hunt_Penacony_SoulGladScorchsandAuditionVenue": "行迹材料:巡猎(苏乐达热砂海选会场)", "Calyx_Crimson_Abundance_Jarilo_BackwaterPass": "行迹材料:丰饶(边缘通路)", "Calyx_Crimson_Abundance_Luofu_FyxestrollGarden": "行迹材料:丰饶(绥园)", "Calyx_Crimson_Erudition_Jarilo_RivetTown": "行迹材料:智识(铆钉镇)", @@ -390,6 +394,7 @@ "do_not_achieve": "不完成这个任务", "Stagnant_Shadow_Spike": "角色晋阶材料:物理(娜塔莎 / 克拉拉 / 卢卡 / 素裳)", "Stagnant_Shadow_Perdition": "角色晋阶材料:物理(寒鸦 / 银枝)", + "Stagnant_Shadow_Duty": "角色晋阶材料:物理(波提欧 / 知更鸟)", "Stagnant_Shadow_Blaze": "角色晋阶材料:火(姬子 / 艾丝妲 / 虎克)", "Stagnant_Shadow_Scorch": "角色晋阶材料:火(桂乃芬 / 托帕&账账)", "Stagnant_Shadow_Ire": "角色晋阶材料:火(加拉赫)", @@ -535,7 +540,8 @@ "Echo_of_War_Destruction_Beginning": "毁灭的开端•历战余响 (空间站「黑塔」)", "Echo_of_War_End_of_the_Eternal_Freeze": "寒潮的落幕•历战余响 (雅利洛-Ⅵ)", "Echo_of_War_Divine_Seed": "不死的神实•历战余响 (仙舟「罗浮」)", - "Echo_of_War_Borehole_Planet_Old_Crater": "蛀星的旧靥•历战余响 (空间站「黑塔」)" + "Echo_of_War_Borehole_Planet_Old_Crater": "蛀星的旧靥•历战余响 (空间站「黑塔」)", + "Echo_of_War_Salutations_of_Ashen_Dreams": "尘梦的赞礼•历战余响 (匹诺康尼)" }, "Team": { "name": "打本队伍", diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index 5b781c922..d8e7c77a7 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -262,6 +262,7 @@ "Calyx_Crimson_Preservation_Herta_SupplyZone": "行跡材料:存護(支援艙段)", "Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark": "行跡材料:存護(克勞克影視樂園)", "Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains": "行跡材料:巡獵(城郊雪原)", + "Calyx_Crimson_The_Hunt_Penacony_SoulGladScorchsandAuditionVenue": "行跡材料:巡獵(蘇樂達熱砂海選會場)", "Calyx_Crimson_Abundance_Jarilo_BackwaterPass": "行跡材料:豐饒(邊緣通道)", "Calyx_Crimson_Abundance_Luofu_FyxestrollGarden": "行跡材料:豐饒(綏園)", "Calyx_Crimson_Erudition_Jarilo_RivetTown": "行跡材料:智識(鉚釘鎮)", @@ -271,6 +272,7 @@ "Calyx_Crimson_Nihility_Luofu_AlchemyCommission": "行跡材料:虛無(丹鼎司)", "Stagnant_Shadow_Spike": "角色晉階材料:物理(娜塔莎 / 克拉拉 / 盧卡 / 素裳)", "Stagnant_Shadow_Perdition": "角色晉階材料:物理(寒鴉 / 銀枝)", + "Stagnant_Shadow_Duty": "角色晉階材料:物理(波提歐 / 知更鳥)", "Stagnant_Shadow_Blaze": "角色晉階材料:火(姬子 / 艾絲妲 / 虎克)", "Stagnant_Shadow_Scorch": "角色晉階材料:火(桂乃芬 / 托帕&帳帳)", "Stagnant_Shadow_Ire": "角色晉階材料:火(加拉赫)", @@ -313,6 +315,7 @@ "Calyx_Crimson_Preservation_Herta_SupplyZone": "行跡材料:存護(支援艙段)", "Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark": "行跡材料:存護(克勞克影視樂園)", "Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains": "行跡材料:巡獵(城郊雪原)", + "Calyx_Crimson_The_Hunt_Penacony_SoulGladScorchsandAuditionVenue": "行跡材料:巡獵(蘇樂達熱砂海選會場)", "Calyx_Crimson_Abundance_Jarilo_BackwaterPass": "行跡材料:豐饒(邊緣通道)", "Calyx_Crimson_Abundance_Luofu_FyxestrollGarden": "行跡材料:豐饒(綏園)", "Calyx_Crimson_Erudition_Jarilo_RivetTown": "行跡材料:智識(鉚釘鎮)", @@ -376,6 +379,7 @@ "Calyx_Crimson_Preservation_Herta_SupplyZone": "行跡材料:存護(支援艙段)", "Calyx_Crimson_Preservation_Penacony_ClockStudiosThemePark": "行跡材料:存護(克勞克影視樂園)", "Calyx_Crimson_The_Hunt_Jarilo_OutlyingSnowPlains": "行跡材料:巡獵(城郊雪原)", + "Calyx_Crimson_The_Hunt_Penacony_SoulGladScorchsandAuditionVenue": "行跡材料:巡獵(蘇樂達熱砂海選會場)", "Calyx_Crimson_Abundance_Jarilo_BackwaterPass": "行跡材料:豐饒(邊緣通道)", "Calyx_Crimson_Abundance_Luofu_FyxestrollGarden": "行跡材料:豐饒(綏園)", "Calyx_Crimson_Erudition_Jarilo_RivetTown": "行跡材料:智識(鉚釘鎮)", @@ -390,6 +394,7 @@ "do_not_achieve": "不完成這個任務", "Stagnant_Shadow_Spike": "角色晉階材料:物理(娜塔莎 / 克拉拉 / 盧卡 / 素裳)", "Stagnant_Shadow_Perdition": "角色晉階材料:物理(寒鴉 / 銀枝)", + "Stagnant_Shadow_Duty": "角色晉階材料:物理(波提歐 / 知更鳥)", "Stagnant_Shadow_Blaze": "角色晉階材料:火(姬子 / 艾絲妲 / 虎克)", "Stagnant_Shadow_Scorch": "角色晉階材料:火(桂乃芬 / 托帕&帳帳)", "Stagnant_Shadow_Ire": "角色晉階材料:火(加拉赫)", @@ -535,7 +540,8 @@ "Echo_of_War_Destruction_Beginning": "毀滅的開端•歷戰餘響 (太空站「黑塔」)", "Echo_of_War_End_of_the_Eternal_Freeze": "寒潮的落幕•歷戰餘響 (雅利洛-Ⅵ)", "Echo_of_War_Divine_Seed": "不死的神實•歷戰餘響 (仙舟「羅浮」)", - "Echo_of_War_Borehole_Planet_Old_Crater": "蛀星的舊靨•歷戰餘響 (太空站「黑塔」)" + "Echo_of_War_Borehole_Planet_Old_Crater": "蛀星的舊靨•歷戰餘響 (太空站「黑塔」)", + "Echo_of_War_Salutations_of_Ashen_Dreams": "塵夢的讚禮•歷戰餘響 (匹諾康尼)" }, "Team": { "name": "打本隊伍", diff --git a/tasks/assignment/ui.py b/tasks/assignment/ui.py index 895fe0c38..efbeb0f67 100644 --- a/tasks/assignment/ui.py +++ b/tasks/assignment/ui.py @@ -40,8 +40,8 @@ class AssignmentOcr(Ocr): (KEYWORDS_ASSIGNMENT_ENTRY.The_Wages_of_Humanity.name, '[赠]养人类'), ], 'en': [ - (KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Food_Improvement_Plan.name, - 'Food\s*[I]{0}mprovement Plan'), + # (KEYWORDS_ASSIGNMENT_EVENT_ENTRY.Food_Improvement_Plan.name, + # 'Food\s*[I]{0}mprovement Plan'), ] } diff --git a/tasks/dungeon/ui.py b/tasks/dungeon/ui.py index f9d91a412..1ded01cdd 100644 --- a/tasks/dungeon/ui.py +++ b/tasks/dungeon/ui.py @@ -82,6 +82,9 @@ class OcrDungeonList(Ocr): # 乙太之蕾•雅利洛-Ⅵ result = re.sub(r'-[VⅤ][IⅠ]', '-Ⅵ', result) + # 苏乐达™热砂海选会场 + result = re.sub(r'(苏乐达|蘇樂達|SoulGlad|スラーダ|FelizAlma)[rtT]*M', r'\1', result) + result = super().after_process(result) if self.lang == 'cn': diff --git a/tasks/map/keywords/plane.py b/tasks/map/keywords/plane.py index bca8da0b5..075ad9d77 100644 --- a/tasks/map/keywords/plane.py +++ b/tasks/map/keywords/plane.py @@ -501,11 +501,11 @@ Penacony_DreamfluxReef = MapPlane( Penacony_SoulGladScorchsandAuditionVenue = MapPlane( id=46, name='Penacony_SoulGladScorchsandAuditionVenue', - cn='苏乐达™热砂海选会场', - cht='蘇樂達™熱砂海選會場', - en='SoulGlad™ Scorchsand Audition Venue', - jp='スラーダ™熱砂オーディション会場', - es='Recinto de las Audiciones FelizAlma™ en la Arena Ardiente', + cn='苏乐达热砂海选会场', + cht='蘇樂達熱砂海選會場', + en='SoulGlad Scorchsand Audition Venue', + jp='スラーダ熱砂オーディション会場', + es='Recinto de las Audiciones FelizAlma en la Arena Ardiente', world_id=3, plane_id=2033101, ) From f58aec2f6a9d3964a9253cc92718b5564eae57df Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Thu, 9 May 2024 17:44:39 +0800 Subject: [PATCH 081/114] Opt: [ALAS] Improve ui appearance of stored object --- assets/gui/css/alas.css | 5 ++- assets/gui/css/dark-alas.css | 5 --- assets/gui/css/light-alas.css | 5 --- module/webui/widgets.py | 82 +++++++++++++++++++---------------- 4 files changed, 48 insertions(+), 49 deletions(-) diff --git a/assets/gui/css/alas.css b/assets/gui/css/alas.css index 91c5649fa..5c7610226 100644 --- a/assets/gui/css/alas.css +++ b/assets/gui/css/alas.css @@ -441,10 +441,13 @@ pre.rich-traceback-code { [id^="pywebio-scope-dashboard-value-"] { display: flex; - align-items: flex-end; + align-items: baseline; height: 1.5rem; } +[id^="pywebio-scope-arg_stored-stored-value-"] p { + margin-bottom: 0; +} #pywebio-scope-log { line-height: 1.2; diff --git a/assets/gui/css/dark-alas.css b/assets/gui/css/dark-alas.css index fc6962752..7cdc1e819 100644 --- a/assets/gui/css/dark-alas.css +++ b/assets/gui/css/dark-alas.css @@ -83,11 +83,6 @@ select.state-light { background-color: transparent !important; } -[id^="pywebio-scope-arg_stored-stored-value-"] { - border-bottom: .125rem solid #7a77bb; - background-color: #343a40; -} - textarea { border: 1px solid #21262d; } diff --git a/assets/gui/css/light-alas.css b/assets/gui/css/light-alas.css index d9de9a2bd..ed295b88b 100644 --- a/assets/gui/css/light-alas.css +++ b/assets/gui/css/light-alas.css @@ -84,11 +84,6 @@ select.state-light { background-color: transparent !important; } -[id^="pywebio-scope-arg_stored-stored-value-"] { - border-bottom: .125rem solid #4e4c97; - background-color: #e9ecef; -} - textarea { border: 1px solid lightgrey; } diff --git a/module/webui/widgets.py b/module/webui/widgets.py index 37fbdec9a..97fe7aad4 100644 --- a/module/webui/widgets.py +++ b/module/webui/widgets.py @@ -325,11 +325,17 @@ def put_arg_input(kwargs: T_Output_Kwargs) -> Output: ) -def product_stored_row(kwargs: T_Output_Kwargs, key, value, style = "--input--"): - kwargs = copy.copy(kwargs) - kwargs["name"] += f'_{key}' - kwargs["value"] = value - return put_input(**kwargs).style(style) +def product_stored_row(key, value): + if key[-1].isdigit(): + # quest1, quest2, quest3 + return [put_text(value).style("--dashboard-time--")] + else: + # calyx, relic + # 3 (relic) + return [ + put_text(value).style("--dashboard-value--"), + put_text(f" ({key})").style("--dashboard-time--"), + ] def put_arg_stored(kwargs: T_Output_Kwargs) -> Output: @@ -337,43 +343,43 @@ def put_arg_stored(kwargs: T_Output_Kwargs) -> Output: kwargs["disabled"] = True values = kwargs.pop("value", {}) + value = values.pop("value", "") + total = values.pop("total", "") time_ = values.pop("time", "") - - if "value" in values and "total" in values: - # display as counter style - counter = f'{values["value"]} / {values["total"]}' - rows = [product_stored_row( - kwargs, "counter", counter)] - if time_: - rows += [product_stored_row(kwargs, "time", time_)] - return put_scope( - f"arg_container-stored-{name}", - [ - get_title_help(kwargs), - put_scope( - f"arg_stored-stored-value-{name}", - rows, - ) - ] - ) + + if value != "" and total != "": + rows = [put_scope(f"dashboard-value-{name}", [ + put_text(value).style("--dashboard-value--"), + put_text(f" / {total}").style("--dashboard-time--"), + ])] + elif value != "": + rows = [put_scope(f"dashboard-value-{name}", [ + put_text(value).style("--dashboard-value--") + ])] else: - # display per key - rows = [product_stored_row(kwargs, key, value) - for key, value in values.items() if value] - if time_: - rows += [product_stored_row(kwargs, "time", time_)] - - return put_scope( - f"arg_container-stored-{name}", - [ - get_title_help(kwargs), - put_scope( - f"arg_stored-stored-value-{name}", - rows, - ) - ] + rows = [] + if values: + rows += [ + put_scope(f"dashboard-value-{name}-{key}", product_stored_row(key, value)) + for key, value in values.items() if value != "" + ] + + if time_: + rows.append( + put_text(time_).style("--dashboard-time--") ) + return put_scope( + f"arg_container-stored-{name}", + [ + get_title_help(kwargs), + put_scope( + f"arg_stored-stored-value-{name}", + rows, + ) + ] + ) + def put_arg_select(kwargs: T_Output_Kwargs) -> Output: name: str = kwargs["name"] From 56af575a5aa0175a527aa89e2aa7b53b28986632 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Thu, 9 May 2024 17:45:36 +0800 Subject: [PATCH 082/114] Chore: SimulatedUniverseFarm should be the last --- config/template.json | 4 ++-- module/config/argument/args.json | 8 ++++---- module/config/argument/argument.yaml | 2 +- module/config/config_generated.py | 2 +- module/config/config_updater.py | 2 +- module/config/i18n/en-US.json | 8 ++++---- module/config/i18n/es-ES.json | 8 ++++---- module/config/i18n/ja-JP.json | 8 ++++---- module/config/i18n/zh-CN.json | 8 ++++---- module/config/i18n/zh-TW.json | 8 ++++---- 10 files changed, 29 insertions(+), 29 deletions(-) diff --git a/config/template.json b/config/template.json index 5c4e3cc7c..9d666a02e 100644 --- a/config/template.json +++ b/config/template.json @@ -200,8 +200,8 @@ "UseImmersifier": true, "DoubleEvent": true, "WeeklyFarming": false, - "SimulatedUniverseFarm": {}, - "UseStamina": false + "UseStamina": false, + "SimulatedUniverseFarm": {} }, "RogueBlessing": { "PresetBlessingFilter": "preset", diff --git a/module/config/argument/args.json b/module/config/argument/args.json index 01ba4ecaa..b37292f5e 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -1481,15 +1481,15 @@ "type": "checkbox", "value": false }, + "UseStamina": { + "type": "checkbox", + "value": false + }, "SimulatedUniverseFarm": { "type": "stored", "value": {}, "display": "disabled", "stored": "StoredSimulatedUniverseElite" - }, - "UseStamina": { - "type": "checkbox", - "value": false } }, "RogueBlessing": { diff --git a/module/config/argument/argument.yaml b/module/config/argument/argument.yaml index 9c9efd878..0dc814a8f 100644 --- a/module/config/argument/argument.yaml +++ b/module/config/argument/argument.yaml @@ -258,10 +258,10 @@ RogueWorld: UseImmersifier: true DoubleEvent: true WeeklyFarming: false + UseStamina: false SimulatedUniverseFarm: stored: StoredSimulatedUniverseElite display: disabled - UseStamina: false RogueBlessing: PresetBlessingFilter: diff --git a/module/config/config_generated.py b/module/config/config_generated.py index b4f3226b7..798eff74a 100644 --- a/module/config/config_generated.py +++ b/module/config/config_generated.py @@ -139,8 +139,8 @@ class GeneratedConfig: RogueWorld_UseImmersifier = True RogueWorld_DoubleEvent = True RogueWorld_WeeklyFarming = False - RogueWorld_SimulatedUniverseFarm = {} RogueWorld_UseStamina = False + RogueWorld_SimulatedUniverseFarm = {} # Group `RogueBlessing` RogueBlessing_PresetBlessingFilter = 'preset' # preset, custom diff --git a/module/config/config_updater.py b/module/config/config_updater.py index 543e9ce4a..1a1f06ed6 100644 --- a/module/config/config_updater.py +++ b/module/config/config_updater.py @@ -864,7 +864,7 @@ class ConfigUpdater: yield 'Rogue.RogueBlessing.CustomResonanceFilter' if deep_get(data, 'Rogue.RogueBlessing.PresetCurioFilter') != 'custom': yield 'Rogue.RogueBlessing.CustomCurioFilter' - if deep_get(data, 'Rogue.RogueWorld.WeeklyFarming') != True: + if deep_get(data, 'Rogue.RogueWorld.WeeklyFarming', default=False) is False: yield 'Rogue.RogueWorld.SimulatedUniverseFarm' def get_hidden_args(self, data) -> t.Set[str]: diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index 0f170e54b..a01b95725 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -982,13 +982,13 @@ "name": "Farm 100 Elites Weekly", "help": "" }, - "SimulatedUniverseFarm": { - "name": "Progress of elite boss farmed", - "help": "" - }, "UseStamina": { "name": "Farm Planers Using Trailblase Power", "help": "Task \"Dungeon\" will no longer run, and all trailblaze power will be used first to claim immersion rewards, except for double events." + }, + "SimulatedUniverseFarm": { + "name": "Progress of elite boss farmed", + "help": "" } }, "RogueBlessing": { diff --git a/module/config/i18n/es-ES.json b/module/config/i18n/es-ES.json index c3dd7752d..66c665879 100644 --- a/module/config/i18n/es-ES.json +++ b/module/config/i18n/es-ES.json @@ -982,13 +982,13 @@ "name": "Granja 100 élites semanalmente", "help": "" }, - "SimulatedUniverseFarm": { - "name": "Progreso de élites derrotadas", - "help": "" - }, "UseStamina": { "name": "Reclamar de planers mediante poder trazacaminos", "help": "La tarea de mazmorra ya no se ejecutará y todo el poder trazacaminos se usará primero para reclamar recompensas de inmersión, excepto para eventos dobles" + }, + "SimulatedUniverseFarm": { + "name": "Progreso de élites derrotadas", + "help": "" } }, "RogueBlessing": { diff --git a/module/config/i18n/ja-JP.json b/module/config/i18n/ja-JP.json index e179a8a78..82f69b042 100644 --- a/module/config/i18n/ja-JP.json +++ b/module/config/i18n/ja-JP.json @@ -982,13 +982,13 @@ "name": "RogueWorld.WeeklyFarming.name", "help": "RogueWorld.WeeklyFarming.help" }, - "SimulatedUniverseFarm": { - "name": "RogueWorld.SimulatedUniverseFarm.name", - "help": "RogueWorld.SimulatedUniverseFarm.help" - }, "UseStamina": { "name": "RogueWorld.UseStamina.name", "help": "RogueWorld.UseStamina.help" + }, + "SimulatedUniverseFarm": { + "name": "RogueWorld.SimulatedUniverseFarm.name", + "help": "RogueWorld.SimulatedUniverseFarm.help" } }, "RogueBlessing": { diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index 8032f072a..37b6e7e84 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -982,13 +982,13 @@ "name": "每周刷100精英怪", "help": "" }, - "SimulatedUniverseFarm": { - "name": "刷精英怪进度", - "help": "" - }, "UseStamina": { "name": "使用开拓力刷内圈遗器", "help": "每日副本任务将不再打本,所有开拓力将优先被用于领取浸器奖励,双倍活动时除外" + }, + "SimulatedUniverseFarm": { + "name": "刷精英怪进度", + "help": "" } }, "RogueBlessing": { diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index d8e7c77a7..b7e6c08ca 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -982,13 +982,13 @@ "name": "每週農100精英怪", "help": "" }, - "SimulatedUniverseFarm": { - "name": "農精英怪進度", - "help": "" - }, "UseStamina": { "name": "用開拓力農遺器", "help": "每日副本任務將不再打本,所有開拓力將優先被用於領取浸器獎勵,雙倍活動時除外" + }, + "SimulatedUniverseFarm": { + "name": "農精英怪進度", + "help": "" } }, "RogueBlessing": { From 9dcc3793be4ae14118a8bae2da58a56da16d73d0 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Sat, 11 May 2024 22:40:23 +0800 Subject: [PATCH 083/114] Fix: [CN] Handle OCR error on Stagnant_Shadow_Ire --- tasks/dungeon/ui.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tasks/dungeon/ui.py b/tasks/dungeon/ui.py index 1ded01cdd..4d02ff30c 100644 --- a/tasks/dungeon/ui.py +++ b/tasks/dungeon/ui.py @@ -95,6 +95,8 @@ class OcrDungeonList(Ocr): result = re.sub('^灼之形', '燔灼之形', result) # 偃偶之形•凝滞虚影 result = re.sub('^偶之形', '偃偶之形', result) + # 嗔怒之形•凝滞虚影 + result = re.sub('^怒之形', '嗔怒之形', result) # 蛀星的旧·历战余响 result = re.sub(r'蛀星的旧.*?历战', '蛀星的旧靥•历战', result) From fc480674959752f5034e4b4f6fb82818d3ace5d0 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Sun, 12 May 2024 00:28:33 +0800 Subject: [PATCH 084/114] Fix: Set nemu_ipc before screenshot_interval_set --- module/device/device.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/module/device/device.py b/module/device/device.py index 2435074ef..494914176 100644 --- a/module/device/device.py +++ b/module/device/device.py @@ -87,6 +87,12 @@ class Device(Screenshot, Control, AppControl): if self.config.EmulatorInfo_Emulator == 'auto': _ = self.emulator_instance + # SRC only, use nemu_ipc if available + available = self.nemu_ipc_available() + logger.attr('nemu_ipc_available', available) + if available: + self.config.override(Emulator_ScreenshotMethod='nemu_ipc') + self.screenshot_interval_set() self.method_check() @@ -101,12 +107,6 @@ class Device(Screenshot, Control, AppControl): if self.config.Emulator_ControlMethod == 'minitouch': self.early_minitouch_init() - # SRC only, use nemu_ipc if available - available = self.nemu_ipc_available() - logger.attr('nemu_ipc_available', available) - if available: - self.config.override(Emulator_ScreenshotMethod='nemu_ipc') - def run_simple_screenshot_benchmark(self): """ Perform a screenshot method benchmark, test 3 times on each method. From 0135bf916792efef10ab89e12f8a9397c7aaf87e Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Sun, 12 May 2024 00:45:17 +0800 Subject: [PATCH 085/114] Refactor: Set team using next prev buttons instead (#450) --- assets/share/combat/team/TEAM_2_CLICK.png | Bin 6316 -> 0 bytes assets/share/combat/team/TEAM_3_CLICK.png | Bin 6363 -> 0 bytes assets/share/combat/team/TEAM_4_CLICK.png | Bin 6263 -> 0 bytes assets/share/combat/team/TEAM_5_CLICK.png | Bin 6206 -> 0 bytes assets/share/combat/team/TEAM_7_CLICK.png | Bin 6174 -> 0 bytes assets/share/combat/team/TEAM_8_CLICK.png | Bin 6466 -> 0 bytes assets/share/combat/team/TEAM_9_CLICK.png | Bin 6393 -> 0 bytes .../team/{TEAM_6_CLICK.png => TEAM_NEXT.png} | Bin 6402 -> 8960 bytes .../team/{TEAM_1_CLICK.png => TEAM_PREV.png} | Bin 6696 -> 9623 bytes tasks/combat/assets/assets_combat_team.py | 104 +++------------ tasks/combat/team.py | 126 ++++++------------ 11 files changed, 56 insertions(+), 174 deletions(-) delete mode 100644 assets/share/combat/team/TEAM_2_CLICK.png delete mode 100644 assets/share/combat/team/TEAM_3_CLICK.png delete mode 100644 assets/share/combat/team/TEAM_4_CLICK.png delete mode 100644 assets/share/combat/team/TEAM_5_CLICK.png delete mode 100644 assets/share/combat/team/TEAM_7_CLICK.png delete mode 100644 assets/share/combat/team/TEAM_8_CLICK.png delete mode 100644 assets/share/combat/team/TEAM_9_CLICK.png rename assets/share/combat/team/{TEAM_6_CLICK.png => TEAM_NEXT.png} (56%) rename assets/share/combat/team/{TEAM_1_CLICK.png => TEAM_PREV.png} (52%) diff --git a/assets/share/combat/team/TEAM_2_CLICK.png b/assets/share/combat/team/TEAM_2_CLICK.png deleted file mode 100644 index 7a20b4e6281256d0e57c6a7f94783cc767c5f5da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6316 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 diff --git a/assets/share/combat/team/TEAM_3_CLICK.png b/assets/share/combat/team/TEAM_3_CLICK.png deleted file mode 100644 index 1d4a6130db787f4ef437a8fdeedb586f8a4eb3f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6363 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 diff --git a/assets/share/combat/team/TEAM_7_CLICK.png b/assets/share/combat/team/TEAM_7_CLICK.png deleted file mode 100644 index 9bdb478c9a34ff5298f5b131ed78e0a889f2f130..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6174 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 C&DDDV diff --git a/assets/share/combat/team/TEAM_8_CLICK.png b/assets/share/combat/team/TEAM_8_CLICK.png deleted file mode 100644 index 8e2dbcf656bfde616af0d8dfe6ae5d136073fdf0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6466 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 diff --git a/assets/share/combat/team/TEAM_9_CLICK.png b/assets/share/combat/team/TEAM_9_CLICK.png deleted file mode 100644 index 018a0efa769c100f9cb071f4c310a96e6dd487e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6393 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 diff --git a/assets/share/combat/team/TEAM_6_CLICK.png b/assets/share/combat/team/TEAM_NEXT.png similarity index 56% rename from assets/share/combat/team/TEAM_6_CLICK.png rename to assets/share/combat/team/TEAM_NEXT.png index 4fcd4b8c9a1cb620f32404aab19a4c0be43e16d8..52ee873a1fdec21b9de627d002571a9dc2c8425a 100644 GIT binary patch delta 3813 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 Y-l&3zNXe5r{Gf9l7u_q){`m9%0J7-<+W-In delta 1382 zcmV-s1)2JnOQXB7wVbDno5H@Qg@)6}Gj{gpt2;$$Q+Zt3vN~&lLN~5%0XlYDrtRgC!hWs#Ta_^n@IWFG0H_67O zOv&fNEbhF_oyA!Ud^yi^P6U%t3wD3N|3@kTu(FOFJN~IpKJxsRpPA2xsB!yoRb*A3 zJ$vC-r{CPzj2;d8+Z&5Tn&dp%+;a>~HHB3do>AK3H!i7!6%(1U-~$EaQr zrJ4W$0WPGo;=oKtM{>@BBFm+Z>a^q`i&3tegOawfPqz))gx+aG5+S)Z)C1z z?#i51%s;rfxb*(jiioI>9zHboo4t>{FUE0ERZ-O|s@f~Byms;jKUoetC_xwi;9lnl zG_bm=dPHn&Y>s`b9Cn`c82?eQp zi|aSWXzP)FfPoK*-m9utEP7Q%_1>#$9LI`i84)?mhs->D_#-cV<@m!7@9otp zGTJQDrZ)fpZgT`0SY3aaEwU=ADk^Jdb$L~d<6^q&8&R`1M8vuC7tftPJq*jw^l597 z{YL-*2*ALHD+y<2L`FqLWVR{$#`4{?1UNFI_ln569f7N&G=c#D5P*S|R#7|Co2#ZJ zzZo^Xo1SMxWMMgQoWv__Ltep|i%QP(i0s!3etU&@+ zH~?=?fUc;jzVm-PBZv7AQB!kwTx`YiW^h$iMU8!2EVkCy=f@uZ_%i!C0Sy3f&(i@0 zRy_d^*}olHAcj)*?juI*lX-PY8i&7xmi3@bc%;&SA4Xril7XSbO7+7^1 zo0}DpQ7^sp)%|;C^R_u}Tf2tEi7$O&KATPVf-k)L-ngi$MfF8RR82v+N5pJ4%!Z+5 zlYapK2zP&gfmQa-JLf<9^i!F&zGwG0zx9nv?_XZ@eqjH;ef!oUdR1M$cH?(%{JtWp zFXuvMw#cZ6vG;bzPNbco&;S4kzyK@fwb#xZ*uU@S(MK|CKFbFmJQNX;)41ojd2{23 zKYn#%V^J~BT4vm;9@Vq8vA3#4ue-)a0|0O@8+r7OI zDQ@1{{L5ee_VUX=y>#jF81vRzWM;I;y#0#I9A>k2=z;sMUcGkt^7S)kPAe(1fgt<@ ovydRP1Rucv_Fn)10RR630BKo@m(}I>;{X5v07*qoM6N<$g3ANG>;M1& diff --git a/tasks/combat/assets/assets_combat_team.py b/tasks/combat/assets/assets_combat_team.py index 757daa624..01fd9adf8 100644 --- a/tasks/combat/assets/assets_combat_team.py +++ b/tasks/combat/assets/assets_combat_team.py @@ -50,16 +50,6 @@ TEAM_1_CHECK = ButtonWrapper( button=(391, 29, 424, 56), ), ) -TEAM_1_CLICK = ButtonWrapper( - name='TEAM_1_CLICK', - share=Button( - file='./assets/share/combat/team/TEAM_1_CLICK.png', - area=(395, 32, 421, 54), - search=(375, 12, 441, 74), - color=(80, 79, 96), - button=(395, 32, 421, 54), - ), -) TEAM_2_CHECK = ButtonWrapper( name='TEAM_2_CHECK', share=Button( @@ -70,16 +60,6 @@ TEAM_2_CHECK = ButtonWrapper( button=(491, 29, 528, 56), ), ) -TEAM_2_CLICK = ButtonWrapper( - name='TEAM_2_CLICK', - share=Button( - file='./assets/share/combat/team/TEAM_2_CLICK.png', - area=(512, 32, 525, 54), - search=(492, 12, 545, 74), - color=(86, 89, 108), - button=(512, 32, 525, 54), - ), -) TEAM_3_CHECK = ButtonWrapper( name='TEAM_3_CHECK', share=Button( @@ -90,16 +70,6 @@ TEAM_3_CHECK = ButtonWrapper( button=(591, 29, 629, 56), ), ) -TEAM_3_CLICK = ButtonWrapper( - name='TEAM_3_CLICK', - share=Button( - file='./assets/share/combat/team/TEAM_3_CLICK.png', - area=(612, 32, 625, 54), - search=(592, 12, 645, 74), - color=(78, 82, 95), - button=(612, 32, 625, 54), - ), -) TEAM_4_CHECK = ButtonWrapper( name='TEAM_4_CHECK', share=Button( @@ -110,16 +80,6 @@ TEAM_4_CHECK = ButtonWrapper( button=(691, 29, 730, 56), ), ) -TEAM_4_CLICK = ButtonWrapper( - name='TEAM_4_CLICK', - share=Button( - file='./assets/share/combat/team/TEAM_4_CLICK.png', - area=(712, 32, 726, 54), - search=(692, 12, 746, 74), - color=(67, 69, 82), - button=(712, 32, 726, 54), - ), -) TEAM_5_CHECK = ButtonWrapper( name='TEAM_5_CHECK', share=Button( @@ -130,16 +90,6 @@ TEAM_5_CHECK = ButtonWrapper( button=(791, 29, 829, 56), ), ) -TEAM_5_CLICK = ButtonWrapper( - name='TEAM_5_CLICK', - share=Button( - file='./assets/share/combat/team/TEAM_5_CLICK.png', - area=(813, 32, 825, 54), - search=(793, 12, 845, 74), - color=(83, 83, 93), - button=(813, 32, 825, 54), - ), -) TEAM_6_CHECK = ButtonWrapper( name='TEAM_6_CHECK', share=Button( @@ -150,16 +100,6 @@ TEAM_6_CHECK = ButtonWrapper( button=(551, 29, 589, 56), ), ) -TEAM_6_CLICK = ButtonWrapper( - name='TEAM_6_CLICK', - share=Button( - file='./assets/share/combat/team/TEAM_6_CLICK.png', - area=(572, 32, 586, 54), - search=(552, 12, 606, 74), - color=(86, 91, 109), - button=(572, 32, 586, 54), - ), -) TEAM_7_CHECK = ButtonWrapper( name='TEAM_7_CHECK', share=Button( @@ -170,16 +110,6 @@ TEAM_7_CHECK = ButtonWrapper( button=(651, 29, 689, 56), ), ) -TEAM_7_CLICK = ButtonWrapper( - name='TEAM_7_CLICK', - share=Button( - file='./assets/share/combat/team/TEAM_7_CLICK.png', - area=(673, 32, 685, 54), - search=(653, 12, 705, 74), - color=(63, 66, 76), - button=(673, 32, 685, 54), - ), -) TEAM_8_CHECK = ButtonWrapper( name='TEAM_8_CHECK', share=Button( @@ -190,16 +120,6 @@ TEAM_8_CHECK = ButtonWrapper( button=(751, 29, 789, 56), ), ) -TEAM_8_CLICK = ButtonWrapper( - name='TEAM_8_CLICK', - share=Button( - file='./assets/share/combat/team/TEAM_8_CLICK.png', - area=(772, 32, 786, 54), - search=(752, 12, 806, 74), - color=(92, 92, 100), - button=(772, 32, 786, 54), - ), -) TEAM_9_CHECK = ButtonWrapper( name='TEAM_9_CHECK', share=Button( @@ -210,14 +130,24 @@ TEAM_9_CHECK = ButtonWrapper( button=(851, 29, 889, 56), ), ) -TEAM_9_CLICK = ButtonWrapper( - name='TEAM_9_CLICK', +TEAM_NEXT = ButtonWrapper( + name='TEAM_NEXT', share=Button( - file='./assets/share/combat/team/TEAM_9_CLICK.png', - area=(872, 32, 886, 54), - search=(852, 12, 906, 74), - color=(73, 73, 83), - button=(872, 32, 886, 54), + file='./assets/share/combat/team/TEAM_NEXT.png', + area=(1202, 313, 1234, 375), + search=(1182, 293, 1254, 395), + color=(52, 56, 77), + button=(1202, 313, 1234, 375), + ), +) +TEAM_PREV = ButtonWrapper( + name='TEAM_PREV', + share=Button( + file='./assets/share/combat/team/TEAM_PREV.png', + area=(46, 314, 78, 375), + search=(26, 294, 98, 395), + color=(108, 113, 136), + button=(46, 314, 78, 375), ), ) TEAM_SEARCH = ButtonWrapper( diff --git a/tasks/combat/team.py b/tasks/combat/team.py index babd4d29e..2282c178c 100644 --- a/tasks/combat/team.py +++ b/tasks/combat/team.py @@ -1,7 +1,6 @@ import re from module.base.timer import Timer -from module.base.utils import random_rectangle_vector_opted from module.logger import logger from tasks.base.ui import UI from tasks.combat.assets.assets_combat_team import * @@ -13,74 +12,32 @@ def button_to_index(button: ButtonWrapper) -> int: return int(res.group(1)) else: logger.warning(f'Cannot convert team button to index: {button}') - return 1 - - -def index_to_button(index: int) -> ButtonWrapper: - match index: - case 1: - return TEAM_1_CLICK - case 2: - return TEAM_2_CLICK - case 3: - return TEAM_3_CLICK - case 4: - return TEAM_4_CLICK - case 5: - return TEAM_5_CLICK - case 6: - return TEAM_6_CLICK - case 7: - return TEAM_7_CLICK - case 8: - return TEAM_8_CLICK - case 9: - return TEAM_9_CLICK - case _: - logger.warning(f'Invalid team index: {index}') - return TEAM_1_CLICK + return 0 class CombatTeam(UI): - def _get_team(self) -> tuple[list[int], int]: + def _get_team(self) -> int: """ Returns: - list[str]: List of displayed team index. - int: Current team index, or None if current team is not insight + int: Current team index, or 0 if current team is not insight """ - list_team = [] - for button in [ - TEAM_1_CLICK, TEAM_2_CLICK, TEAM_3_CLICK, TEAM_4_CLICK, TEAM_5_CLICK, - TEAM_6_CLICK, TEAM_7_CLICK, TEAM_8_CLICK, TEAM_9_CLICK - ]: - button.load_search(TEAM_SEARCH.area) - if self.appear(button): - list_team.append(button_to_index(button)) - current_team = None + team = 0 for button in [ TEAM_1_CHECK, TEAM_2_CHECK, TEAM_3_CHECK, TEAM_4_CHECK, TEAM_5_CHECK, TEAM_6_CHECK, TEAM_7_CHECK, TEAM_8_CHECK, TEAM_9_CHECK ]: button.load_search(TEAM_SEARCH.area) - if self.appear(button): - current_team = button_to_index(button) - list_team.append(button_to_index(button)) - list_team = list(sorted(list_team)) + if self.appear(button, similarity=0.92): + if self.image_color_count(button.button, color=(255, 234, 191), threshold=180, count=50): + team = button_to_index(button) + break - def show(index): - if index == current_team: - return f'*0{index}*' - else: - return f'0{index}' + return team - # [Team] 01 02 *03* 04 05 06 - logger.attr('Team', ' '.join([show(i) for i in list_team])) - return list_team, current_team - - def team_set(self, team: int = 1, skip_first_screenshot=True) -> bool: + def team_set(self, index: int = 1, skip_first_screenshot=True) -> bool: """ Args: - team: Team index, 1 to 9. + index: Team index, 1 to 9. skip_first_screenshot: Returns: @@ -89,7 +46,7 @@ class CombatTeam(UI): Pages: in: page_team """ - logger.info(f'Team set: {team}') + logger.info(f'Team set: {index}') # Wait teams show up timeout = Timer(1, count=5).start() while 1: @@ -102,18 +59,18 @@ class CombatTeam(UI): if timeout.reached(): logger.warning('Wait current team timeout') break - _, current = self._get_team() - if current: - if current == team: - logger.info(f'Selected to the correct team') - return False - else: - break + current = self._get_team() + if current == index: + logger.attr('Team', current) + logger.info(f'Already selected to the correct team') + return False + else: + break # Set team - click_interval = Timer(2) - swipe_interval = Timer(2) + retry = Timer(2, count=10) skip_first_screenshot = True + clicked = False while 1: if skip_first_screenshot: skip_first_screenshot = False @@ -121,31 +78,26 @@ class CombatTeam(UI): self.device.screenshot() # End - list_team, current = self._get_team() - if current and current == team: + current = self._get_team() + logger.attr('Team', current) + if current == index: logger.info(f'Selected to the correct team') - return True - + return clicked # Click - if team in list_team: - if click_interval.reached(): - self.device.click(index_to_button(team)) - click_interval.reset() - continue - # At left - elif team < min(list_team): - if swipe_interval.reached(): - p1, p2 = random_rectangle_vector_opted( - (350, 0), box=TEAM_SEARCH.area, random_range=(-20, -10, 20, 10)) - self.device.drag(p1, p2, name=f'TEAM_DRAG') - swipe_interval.reset() - # At right - elif team > max(list_team): - if swipe_interval.reached(): - p1, p2 = random_rectangle_vector_opted( - (-350, 0), box=TEAM_SEARCH.area, random_range=(-20, -10, 20, 10)) - self.device.drag(p1, p2, name=f'TEAM_DRAG') - swipe_interval.reset() + if retry.reached(): + diff = index - current + right = diff % 9 + left = -diff % 9 + if right <= left: + self.device.multi_click(TEAM_NEXT, right) + clicked = True + else: + self.device.multi_click(TEAM_PREV, left) + clicked = True + retry.reset() + continue + + return clicked def handle_combat_team_prepare(self, team: int = 1) -> bool: """ From 1288b38b3b1d4132ab224bbe3e7adde18523eb30 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 13 May 2024 02:32:30 +0800 Subject: [PATCH 086/114] Upd: Assignment character assets --- .../share/assignment/dispatch/CHARACTER_1.png | Bin 29910 -> 29901 bytes .../dispatch/CHARACTER_1_SELECTED.png | Bin 5914 -> 5914 bytes .../share/assignment/dispatch/CHARACTER_2.png | Bin 28841 -> 28829 bytes .../dispatch/CHARACTER_2_SELECTED.png | Bin 6168 -> 6172 bytes .../assets/assets_assignment_dispatch.py | 24 +++++++++--------- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/assets/share/assignment/dispatch/CHARACTER_1.png b/assets/share/assignment/dispatch/CHARACTER_1.png index a642340049708f9d01fb42427cf1b7e5673122a8..91d01ca2bf10e7665141a652a0c4b8169a68131f 100644 GIT binary patch delta 24070 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 delta 24103 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 E0v*}S!vFvP diff --git a/assets/share/assignment/dispatch/CHARACTER_1_SELECTED.png b/assets/share/assignment/dispatch/CHARACTER_1_SELECTED.png index 2cf101dc3619d87c6e417e4a56f5f998c41215b5..954517450bfc8cd8929ade5fc6601bfc10a6d11c 100644 GIT binary patch delta 567 zcmV-70?7TEE}AZ|#|ltAYZXBhfZ;QDuM)&gurZaQovBr9LkdZ!|3Lf+B8U<#EbIjv zv5K}XIx<3}4LD(1r^o9uOWY=d5t3kKZ8=V7~fB&7T zTG{-puP@Kez8xDI9T_?K^5yjArHyK}+$hWSvZ|__vvdFe0PWP3U=Wj&53gUpIX5>K z5sQmUzpSRnIiharZZ5O}003yGjs%0abLVbd|2T2t`0q<8awj#NPL34i+Ok#v000Ky zU2J^(+|<}L@^vvs zv$j#KZB)_$006X;E71KFF-1`n2Npif)lGfy(BVkEh{%y5qKIzf{OsMwXHRFIKYFn` zF?#IYn^dG;SsgoiNIJ8z4@w7`U3y}002ovPDHLk FV1iD_2EqUU delta 567 zcmV-70?7TEE}AZ|#|lrsOIA@B!13q2*FvIAXsId5t>y|>afr71528OIf=H~Pp_piKi3^w-SH{fUX2%gf6V zvAVkU-D--QBkHCe$c0t_003?3NYGzXQ`2?*b!6nkk4q_XCpDc;juhqQx>f)H00!W{ z*!lD4W@jIsIdl3Kds5^qJOBWHK)YOl?z3yxuKs#MikwnDIB-BK004kCIf&dZNdf=> z00XcA7ytkO1MokhkpKVyzyJ&Y004dFBUo+A5lxP6_p=lL003=r0=z$7ynHPJ002On zoB+2cM>IL7$SLRf`8R?9000<(|9YdNXM4Tgv13Pny87tx{L0EFl>h*L0NUhdJbP7^ zWl_|-DV-ylCcj_l-nexyMd!}+74-lB09xY&c&~b2qbPQ}dHqXyee!PVbRyT%0RRBB z$w#pETrkyjjynH-=i1uF{DgFK?`ICjE3l}Fpd|b`Z zY;ISZ+m&;3dp!U2!LyCY zvEe%lsYt!D8a_TG9ka0yO9zuM3kM&-f8ZMc009600|57yyA@%KAG-hm002ovPDHLk FV1miT0?GgY diff --git a/assets/share/assignment/dispatch/CHARACTER_2.png b/assets/share/assignment/dispatch/CHARACTER_2.png index 96ca4fe2a6dd1cec75b5befd243f211efbc24ccb..c68a4daa417516152c040201c3ddfdefd36baa95 100644 GIT binary patch delta 22263 zcmYhjhd_dedc^X|TFt*YK?$0}+T)rQutO+?h*l!z6)-feG{m_<+{)Ce^~Z6Tqp z(OOYKQY%`sW@^UvP2c15`}qU%cwNtPp67YabzMivk3X8r% zasC`Sx!+J~^iz2-Wsp3l{q0|8xK$pxvK!nIHWd3e|_#< zND0en$aMqk5OnrGPMoNDc+uuG@ZnfM?C5mk>mWwt)G2|T?`rh5?W++#=fN`|``ty` z$vtxJZU*!Cqrd@uw>i{=US`>RxO6;~Xa%x}u*XXjVC$x=?APg)N4My2KByJPSJ?!u zeptQId77>^pDm{cCiGtCPmh)(pNn>P%N* zkQ8;$)s|>?wgDZLC2g=5*J+WMHadN3Z)>_?oCtU9d%|SbrDR}pJQcI(%{>ivS`H^z ze?0d;HwZYXj4fz&N$u52_WLR$~KMKWLV2dxD(DkhA>`JNWzjsKnXqJxHDy!tX zQQq$F>+RULU?ysd7qMWS`3Wd*+==NIdV`&WTsOD*dT(H4F?+1_n9$YLB>>57-P8xf z1R)-5x^|;d)&=n>lv*=MZB+tRJ#_jxm%m`X5c_RG>R3=_AT(LW!}!>m@yi(lig{ds zHFqi1`B5PT0sE}UY0Y~u;uKRS;Uk2iCVJj;gnenkVb z0*_k-^U2vcNA}8QtKLTjEyq8hbE9ttYW3`R756)lRG06RX zH4zxUJ#3IoW_Tgxa{}q9>0r9R$}1l>HeIqSFfq=Y>}2{`ISS(I5P?3 zW3je-W5yBkmtN}2*zukI@%5`%>hE~}=KvQIP856W*|u&802@ljZTJTIL5ECa{S&c+ zhx?QMpOG;gEn*Tm$8RgMBZ{oVaTs<1R@6gr)vKTht0fzCO%2u11q(ueHU^;RwZ~db zr8;8^3B>Q^7Fs`BndX(e3~)Qr-zc~;`L0!U5Il6%+{>d%X_+=E)&*|5dg)PqZAinO ziD^bib7p??Mr|Dmr8vrquesJ@A@v2$PsYuadEt1g+^E@eq*L3_b{n1+A`kJtXPPXU`s!F;G z-3_fQDmbMLn()9&W~+RAgI$ohe9i#|Zg+7MtU~|14;4}6^arNO#e}<2sEzzOs*X6W z*3yJ`Ybz~GFVridIOXzXD~+9x!B?(WM!s$3`HkVEtaS1TB9fVdWJF}$nTxeQSWdNC zJ;-YgWt(?#4cm_Q+YxWR{y;dFv9cs!rKd7aWIFJw&V$=ssbU2fX0qh4+g<~-eyuwT z430yEEOY|;!VZ7M@XSM9Yxbs0`#g>hiCwuDi8}=d3ek=g#DLBsCw}`jfYFK>25^wW6eu!wnQ56EI!BZKCXvc$>vXIHAn9gWbbsFW*Ql0dJ# z3|957zQylj;yG*h{+X8g?o3Mb75*!2zvBJEV^zm27hCq}d(Ex;V-vX~)CLS~MUU75 zU5#Ffn0|hBUUPHc*OCmclcgp1#f})Nwo|Qg^PckiG|%eyvx`{}8$lSC#7jcBhW&EV zee`3OkaDD}u%3BvAZ@>Qv=?z*1#d)}^Y(m@vQ!enz+8#vs7z&chZc7rg*E>4Nef|P zv->IFYr*r$w120r8-U3|fJkH1IE_C$`Xjy}!NAj$&afy;6@Oau8`=SX>uv}L__ z|9Zq3r;|?Wsd-|7`Iz%B;MjNVL2h9EcLmCEV4*%J?_A6*jFHWxSeMjY*O${J=XRp01w8&_V{AsmhV7AV z*|1{gPF}!Vm&(Oc_k?u8;dil1t zr81ShJhH6-e6ZM{-W|I=g)O6vl2wF_h;TB*t$n}TZi`%m4XQ97fT%4InI&+WG;#jM zpypwZksv#yzjT=tdC0Apg)hO!lpKrVy-n>NsURDPvzfGvl|j&A06x`H;!lGrDDU-! z5*oydmT)lFx+}_gmWAZmAF?OoE_TOS9S7StOG?oq_YRNq2X!&1n);((mFaZmqo8?J zbenIsPq`%L1(NEeEbQ#%29lNsK714csdu=iKv4YiVS3Og*79jw(i3sv=i5_0gY~5w zpyAzj#g*flg~T2Jf*8nizwOwL%MW~Ai9#hLr|vF1z$KSF0fYH)!uq301G5hZ_`*T& z7*q9AUQ>>YE|Rf~mD~PaCm39wdh4|$hZ9+&FLkdl7vaTr@Hja1%lY6IF1$V+VkzGU=6r#Xo%dTX27onzh+53-tq6{ zb=0QS2Q7NBs0~XnaeFr>$Fgp8u^e-j=FaivmV(GLJlE2w181?W;~*G31r0r@1|@1T zCtAAS0b;_PC?@;qscbo&Y+L?4gq{Z*sFQctPS83ueZ)~CUFe$jf1dBa=bgt1mn4s( zTh^m0HP?%1t;d__Yf=#6u--LXKM zbC-gM&yq~^_&Cz_v?`R5o4ecH`Hv+eIGoBk%EI6DXDPEwx;KT-(qY>XK>+$zF$5_d=FVaX4r6sL~NcLrJdRX#V<_KU=11jWfOqpt_J<$!=t% z8*w;ye|41^{?q<=Pv&s@QzdC&*FYRcWcSKC-o_S<#7JCY$M14A>m-*8vS$u?>DG?ePg zvmQ)Da89+UARUCm06ZM0wKy}xtgeljW7SE@5eDrs>`TJ>gS|x(f41|Qu>a#<0ysM_ zkr&Klb|q6JmtYUdF9ed&TXZyveL(NbDC%(y+pmu5-!kqoye~>M6rjt%_M%1FWWANFhz(>{L5Oo*AKYB> z^X-B@9iR?+lhX&>uHuDFG#Efu(N!7oav3!`OCSEBs|*N2&~x&CvOqy^{q7#u>7h10 z88^(gt5|J*=3GXyv$vu+D`T_%$1d&uUY~WN5|QeX+%!`#|&V7=v>ytVpCA zKdHP2(%ddezX|vT6`4uQ#O#hQj$obp3V}P#Os6JXI4zefDVQ| zNi1dx3d3B#F)@>k?=uE6#8x%h%1@;rR0Uz;E*Q3?oB@Q{#<^xuR+5D*;hC@l&&kTw z96FLga=a|}-+D&)9>{wQtCY-fzQ9z8Yf(VUFJZSSrzNja6YlPQOW^brF@$YJ9-w&=Ty6PEq>XsOx*fscrNV>B zX!un2Hv=L^I{FCqgSmOtkbfk2b$jalSa?q?<;c5iQTB?(J5`(FA+_3Pd+&l2Wg$=z zL6O|0TE3}lQ&p}M&?O5h!+pq2kn5G|C4@W1B%jC>bOa;k@pbv9IA(}o$R|`L3&{y- z&Ky9q{Y>hvf%DzXP=?OghU}a$8{(HL!gKsFQ7xeP{c%Ig@sa+(seUDm&CKUM0;{WK zV6N^xJH{cpfjEn|r$;>Pd&XF%=rv4N4>)3crG)ZHs0KwdJv2&D4_}{kM1TEWadCN= z1h4oe|HL*sh#d4NJ~dgHM~Uw;KljY?0Kl5B{+xmudDQtXZC!Lr;MP~qVA4Z{C1jK8 zY=R$w*i%7n3HNLzTe2di z`9v`s#xg%uPQ<;^{AJ@e{<)PLJ&=!2Q5_q@o5E%?F=vx^erzRGtQXBAN=BYv-|ZY< z2ncOwjU#ashzHJGDJ|=R7#{d^OEUeUmV5SwvBu$vsDD2-Cw|{`Ilb6_^1%j$K8r-W zd8fQT*0W%vekoko?TW#?MzbwA=6F?qSeVjPUJHqD`M7pJbRl6aOjNL}{|0iY8vF_PKF-u|;B7K8MO}zy zYE*AimOj0E-DK{yqOGdySv_;Wh>p9XiZC%2j?3RQi@q>R?+ln;)^{kY$?rFElQ!>k zEjAM44-&Nl4d|gajL>qOA;QlSFg1B;PnIy!Uw>!WAG|ze!zHbdP)6+)aM#!GbCjOp zk!S%Eiroq8Sbmf#q<`p>F{4W38jR=oDECMk*E;_(I=8$*bwWBN_m=A)^C@Tl{u@C4 z{;8eg>%aT!P{yg-IRVYeZ(etc+7q%0EgI+2KH(%tx{u6Ckq=VGvC|0ca zRF{Q>`t1T;xU{*n`8@S#Yh@eItCEA-DQEVa8x^09^LMO<2>IYSIF#{L!HUnNJ37Js zd2*Jv*&HbgkHPJ>e4lpHE8NtRU>tOQ5O};GWP4Dx@3(Yw ze|a^%ysj=4(mcq0ek%NLk9xgRsYW%p+HRhWWJPV~XMxp02L_yv@UCWHj42_UeA=y8 zeO{jse#cPNBMtqSlLrzH$@?mwTQu)WQAr1-)hO89iwsSfAWh3mu zSrx?TA&@ao*j}?Pps`Y`mzq>aa-1SkRmrI@#2P4&WvNRc!uhAx6cv&-sC$yXAQ+yt zn98>-NdLNs0wSkBoqdYW7gLq-tf=@{hGAdRi4xR+V^<$E)0=hndnYQJK!$^fz;ciA zO5_jRspuMoK8iU;e=Ht1!r=k(ksRiW?eRz!6OMYIg#k`GJgp5-9MFj(ytkI@G!cTe z>*sxQdm(Ej))86IV$g5sxH5SG5dwGP4$Bvvwq_H{$1~< zfuGzgkx~*_>U)lFdK$)Tl9i{~)0@bj9su^a?)JUWI%wmuj*odzg3^!{XS<_zIWPa+ z>phM_((MkMa~Gpb32vc3oIRd}Z-1*26YvDETbC?8LI0Lc195n98w*(E*U_F|u-tdT zn|Bag5x&lC)V*HuH5fQwELUjdmb5UwiNh_m@m8jthJ(;0A-{4(=>Wm}I+N#Y=P{Nd z0jkT^F(+BzZk?E(n#~R-gIvJih5HyberpCXSjWpL?#^>+=WgehpC2ynU%7X*0|#(9 zN2|~LGi}(`=ux`|Ol+-Lx@3lCXvmKl2e$LIg?%n;RUT8&U@gI<)YR-daM1(=B;J)& z5`U%O>zlX5ayL2&Vpgb&yWz%(%$5Obqd}t*zjt-D<(O_&tPvh!Yh5lQ?dtIvSqxT5 z=h}bO^m2Igbp1qJ3`ts_1fP)V0J^^}Pnod!8I&WwLrW22dg23_KkR>J8f&~Dey?4F z)lwkhUTU4~km%8Wy7k{aG#QN*bE#F-E9rJupgqY2mfN3~%Y+6y2whM4F<%9E#O~2x2DIMvQ|ADg|DWwCtou4Y!C;Hcag5o%veKnBx z-K{5icL)kTsNm?y1}h3(wxD21WRg`lPCM}_$&d}ukApi36g+^~jVWSb35u7aE~{>JB%tF# z0W2}Vg&~&}1TK6cR*$HsK(^_Stm|cQgQFfY4vxaV*gY)bZkPTy%LEHCR6P%zcKe$T zZytlIB{%5jv}QDNz2p0w9n?wvh*8y8^m zTIL^Mqo$a8h5HKs2s4p>a6Pp!$Y1q6xzI*jqSp9kLr;%KM3;G4r`j}jJ;QW_f_`}> z>BZw2lD?|+LObDw&t1>r1pS(8)@449-js4_PeypL!U5yj!(O`xv#W*Vxg z=}v%TU^X-p$2O?Ap=$oad0cRK8#;A!X*z$m=;cK{2M^tik37Z=j5Vf{aLRjK*3Kpd}_) z8O%Nyw1l;ScvwJdj)vA-WWRXu|6Em9P8UHlb8zcM9 z1UXLakEvOTA!{7dPV@HCUNmsw8=3l1yzjwkQ0^ku+RQ$OjZsI@;ltORpOz)ZK5n+D zlwj20=aaRaxXHYsTONdjT^GV87Z3@l2_$N6m~HQOQ|L(%cL zU3x=Drz!Eo#bZKU_U)FUuHMiM8M98GN*D zE~4X|mNpeGoy+6bG3M=G+Tf==l;l}#Y6r=R)gL_9u%!*`xdD3_#=ucEHp|V8=V4Xe zH~IrV0HBg%H@T-|Yq(a1DCQbH0m({uXLn~jla4wqs3TO14T7LDRVDC)sP%5KyxFN}Sj<;%P1FAFb?*0TtL+BHgnWD$E!y9!|a9s>6pP zNJa+;#~J_jcNfSaTpe}d+-08o+H$E1AOU`57wF93*FyF}MXx?!XEOXdfhSn==-WeE zzfk=77jx?mDu%qk50XBNKpC%5h59x1HgK~?=faRYOT%HLqg7_hK1uVa{^;l?gA`0X zon$=r@jDo~_D>x(w78aAxaRYA|LUQU*AIh=Vn*sC4FMG^cJWxw>2S_}A~;g;i$Joe z#MkFk2(+QKCP+~qa(N!gI~#XCRg9(Dh~rZ%+gv@W+`hP?#m;|%BDM>e71lSs?{W*T z1Wtx{cptWyB`OzZ59a5$FWfIvenOCaEAczySQxiu%O40sz_nsBFmaAoQCXAWv)^Vu zJUUu8_KVz5%kInW%iP#dKUDy*BA!orbL(|E$D5}|9iR6_mFmid?L&_DBR3-$F&irL z2e=IzAQLJ z*GOCaFv6yp4SibYTqml(T^}mZcT@AP)R_3ud{aF>DUCtBcC-m(zZWP5<)yhsPzjct z(HT9}mg-+iEByrRN}}{)(|biurP~jZ7X|Y%ym;+)l}TSW^WSCf{)tZQ$KrURWquus znkR;{c#EMxMC&a^+<}An$f;?87i z!Hi}Y?LII`xYhxrwgeOmTrzm89R<;mAf1|Ztf?}W-d37-gQkfk$&%A0Zab;fGcUzw zht6dUbDXmjpmGh5X*$1nuXfi4jS3coflK9!u{~g+m4u%;O2`r9ymwk4WJSk<&*{bc zK@V!WTSs$F9{N@A1spK{xqiOk@wu}}Pc6_z7`*Z>>z17RsVnuG+E^jDnXo(}; z-b5W}0v4+(C3EhHDVmVA9o#Xd6DO9-*-bnau}ec6;erL-ZXaL`whnP6BU*&D=!LX>j^a^HC6TzQetjWc+M;9PuN`a2t0;Qmx$ zq#m{0F_xi+x>rRyhQrH|Hsk$lUIPJj9CLQ_Wf{)4x zrSiMs4%Q8f#@P$}E{>`h`ts?jv+a*7G;BAe4Xzt$Z}`|l)xKp}rf|r_yKAaI#g&R< z`a1Ne*S}c33X4|4V;cjP=BDmDgx6NfA)d|lR=ppTc;pYuu#T(N6>@}9&3={u{OULO z221;*pDT5v`@mofXDmriJk?b_!YLVYCwVZr-c!G(xbUxNfAgOL-ot6le$;<(MM6{Z zCPjRCZD0P)jQ_6ie>*wi>fD9d�mTg`~gahFD99u}mmq&Bg7m)!cS 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 h)eiyTSfknS&AAtwN;i8$lzrzW_V9#ywJC7 z%5XUo2a+)T@LmY~`tk>OjmAa zu50a>(+-YiSu^xtdSR;-Cg{M5HFm42!DB}Q1HHCh!P~x{!HC?63UU68maEk5I*C2i zqR!79Ldy;473W&G3p4zlJ4RA*vyxkHyMnIU#fNT+{$8ud#pV2nmJk3OPiwfRhEiJ@ zBz}=zs7RqUSYV-oj|=nk@_X@12t?fKc+za(96sjW;~4{CeYc>nvPrKnn(R!_wTq|N zAog@Q_Rjy@3Y>IN#~rO73?8W}E6af*A|hrRbFNgS$t@P${#_eTz8XBsEke;=9aB!T zA|p%-w>_^YJroB)q5-P0J4yl4m}>d8L110SR($FM_c=ddQ+^$Yg!6BH{<2;Osz`h* z(oFLAaOG4Ow>wwz+8V~4_kF;mO;#;mfc-fTsC&s$$|E=hit*c;UrRN)4|G+=IWT)+ zXU|Fb8a&G&QYFa4{ zN(eGTsa6NB{A)t!$BM_k5jTb={A;nPq+b@mynXAeANu~sb^&jzQyY_f`#`l_jCgw7 zV_LYUcX={^{5JGtAv$u8cdF5sCt2EHFjo`nb0m8GEc>uOlXN)ckL7+2vY2v>Pz-0p zQs(iiZ7I-7|MD^`L16*EI5>|;!n;(C7-kwexXd(^pAC})rG)8E#USexZgCo(P0+zM zvRa)fW5)~k1UJo4nxShGwW9YTn8Q93SGkhsy#V4s1kF2~79=7{fUb&PIFJ4Kte@Aw zFMDDVi*7%fSMHE++N0DzynN|m0^th#On+s*zUkO%2uJ)iqIi@ryEoW=yvkZ$%<(vD47;T_Chl5(SNOQO@vC_=P0@hS^qM9G1VMmhDXu9zGSWL1d- z%%CwLn~>I&id4l+zTA4r-;aXbc49Y;!L5AMK!&7sfCx-ElWJU_aTW0;5|fZ(_(A=- z$`y21%UHCr3uWCib|Z0w2m+DZo$H8U|1_4^;>A$XMl%x=pJsepJ*fD&RtB}(|0Gp! z|1$T8<5T{@G#~kBgJJL2wZKjifDeMEuwJmrt0If6x)HPE?Mj(a&=zP}FMq0wrNl7w zz^6+>gcL#xSGV(+5>~#iQVq8n{4{|*2P)}8R7372FCIFjD1H%7++xcA-eTRD8t5hE zM^>ZO^dCrbQNiF=ceFYtJxbx6K&`?N`p515IAoKdp|BE{AhHh}4FZgFdC}l&Tz>X- zM3Oi^q$lFx%>HZFJp}H8ymxeU%Jm+`74-44q(jq-suki z!I6(W+(K!+RF*@X2q_i5@Mx2g+vjEJV7qcn1(>gGN;I;;2*d50?LK88g|pyFaDCmF z)1nZyd$gk->P^}o2!E_S7gr9Xk<~Dj;PqQ|*3yGkJ4`P+jczYs3W*l|{kIYGUd~Gv zHKVFj-{w#%=h2YeX-D?)zoQ+&BKF0_C2l7nXqZ};y9_YttS9>#)PVeU-yboozf*H~ zgoL`rOiRO8h7@)dCif}RTWi}r5rdGLk2{*7l*!c7 zywbpY>R`UwJ%j&V^H|3pE-TevZ>k3_z2tva1svs!PS%()rs<2b$Lq6exLpt)Az~lO zNSD*PT**j4Xr-~f>vmLWz~Zf~t)+RSpnM6#l(m&WN13q~&->Dq>uSKQk``wJnBlky zp;uRudw)J-^h44<`p_yFS@l_~imFgl*udoIThVdzO|pdGbuoJ>S~}f0jsXe3Op@LN zftrW3rpsjCqVqQGl#R3)^NubFe}F{7;Kr#-ydc1a^S<_fj{f_nre6}`!*^HPsZm%tL-r>T;rG3$xQj3>H&{4sjjSowb5zBaNd4P9*pyO&|XzFN@= zX-EPjZh2S0jq)^2nbc%Vfj%`;!lUPAm(_Rb)-CbcWF0IPPHS#MnPNt1?ns zuU-R&B-wFg7X^u^kSsMo@cXR^|2AYmh7l4i>lB@x0n+d9~#&G#l{S?NyuVIxG7hvt}SFEXyR`&_? zO7*t~ck)+T;w|ApkQn6F40H?v_Q2GcJ#>s+=or#7X(K{gg679rGg?K@*+70_xo`h6 z=h7Qz#VWt$o{<65V&Z_YWnoCCwGHsy%7DfcY6(DfK3B#oSzbLKWiJT48^CY9v#~F! zv()sKk(rXenQbfZw!lDr-B?P#Tkm69$^*n^gNMx+Gkqm|bi``omKoUv)4cSgj_VG^ z%v9~06&S4B(|Z_LVAf+H4k}C_q=>A4TkDb&AHny^cAEZ{ znlF@>H?HFw7QHM)X7-CL{OAd1efuniTd47JeEFu92wiRF0GN_8AiNFNCW(p&gN}KY z*P30oEi!U^J@o!ly=4U~+B8=*#)z9MCSYOHKvwH`J9uBRs4$IRh-`2f8PLn`=TpBr zE`ipr;CHqNj&WSDbB$x#ro2!*0r4IE&v`|A^0jA&oZ(cu1LEh!s)*TTZUtyvNO}5Q z$U@}u+-G2XcVETAKuXgsZL2rVGHhgbU{x6-x4Y|wT_KW(hlAjzG22NO`4EV-^Uws> zl9`n}B3jlPO>TaHUQ|OFsr$NHmXCbRliS&q!f1P#LSe(uIpoI3k%WiXJVF2`Fv$RVJP@O+$wiEXl%0xK%itOE#1tGeduY!q#VcHE&?Frbm=LM=XiJN5(}= z4{vNCI4z_~U<|Ed#luBw>uU`W!Pz)NJ9|GZ?bpqe$qt;f#~Hs~NK8monwh=f<XO$FkQYRMs0F(N_? z1OZF&x(k7{$u0{D^t7Jxs8LbCREfP>_Zf5U{Qgs5vyzu zJs8b-KfRTjf!7$n8o4tsHmKwf~VaMx}!6Qz2`{LGMdrJe+=!f`y>GyVA$=$v<=g?W-GfB6I zC09pm09jK=X!17EV5d}@K3z&Wp7lCjoYHR1D~<7ARZcu6B(?rv>gMI4mebB-oirQ6 zg2b#9Jo;r}yrjBRYm{2GajpT%zzXqK8($6>`t_lT%6;5@glOVixTc#$BabCfkV~c) zxnz5rsgf~j;xmy|5AEY9DSY%HjjqJKDQf`a!ig>Y)~oBEmR@|3tG^ycR^|G`aHuVt zuPTk$8D^r*u0g+;EJG~0OQA2BKI z$ALjR#Xiz?R=(25^md=)BnF&QrhT~``9ZyEddrtJSEX^>krgJiEZQIN2ALXE0+{=T zQCkFB1ITO+5h6De*G$WLDbS6zELy?OetcYAisbVKOK`;$isKiwOFs`A;U`IlX)%3?E4t z-`>6$vEK9e@hRr*%hawo_k65n-@Pf49^9{a$o#4h#at#U!!Vbd+UZI`H5qOP-%8Fq zE+`c`z|Yv<2#WI!H6`WCJQeINIR8{^H7zXqs`0Hc{8^p2K5*xJ0Dua5oNEp#aq^EU z=-n4g*f;#p@g%z7Qo>zG8p9qO?@nv2w+wWAotvp=z&|X?hVobDJ8O2Y>+xSfa?sp)KSFa4)ciQAgpuu1Pv}K%4xQJJptWA%egBPA(Ne@4)0?iKn zo5+f<*1(jbT9PJZzDTtn2uaHOw8~8>rp@^4)9uEm&G|{q@To!>R4%4LeVGC=fc^2v zC4Oae%35p(l2Rww?Z@+VW$;*#~pZ_Xf*-Z(RMK0_s(U++Th zA>(Fz0P;p@9f9Zy71+d!3UZCf{@BZJyH!~e+zf*JNmKyE6JqF+cJ&YKnD=^9qoFpX@op5gF&(PR_#Om* z<2^pYwGqj~`odRRa1+so4*IZ`kx{h=%>P*32tD{V|4vRLQabT!vKHO0CC1`{LFeqG%!eT{ zbmN%2-60~c64Po6GM=oaNe;0A{qie6U)fkeJ2aG5cM04x^nH=QF*bSbrUk+7muKv1 z0396dFj=qGyk^hGBIh4#quZ&tb2rNy)n+79Q*OKd`Zbk#`%ggg%A}U+pLblv=(L7sAJ8ivRwIbkdH05{6F2 zXG(}j8kl1~w1gxRUj-P4R;6Du#67uS&{ePj!QxARWP*Of9-F$N5o_ix`He)PrRTb_ zjWCiP^U`le1XkTB|2-XfsdH)wjJH{>{RA_$@05zHIH8C3caKdI zGGV_EC1VTeg^MfKTOkr$z82yXh4E$Cy45yV-QYAcR-K@DG@;wRy|>|(uk}*JU_{zT z8(TN?EL$VFfzWN%R^7y&+04wudBA}F<+D0cZrt7$?>XYSoNj1Sv|D*%BSe1>h%(EK z(*{dO@IW9;TU_gXt)e=s-i0k}cy(h5AZ(z9cYVmixUju@VK)QOONq!3l`^P>2rP7p zlp@`^RcMB3jn>(doot3wF~gqZ?}$NS3%Vzt1IALOPzfQ@p14J* zOMRoHLCq)Tr^#>SxpyU3-C^80hop8E8V8v@m304jzV;w@@T`tlxNH4*Va;UGU=%jI z1tk)rAnRAUDlNUu*ooQg3U^Q2tFyatqci;~6b09f&T8ZG;R@yY;paTpxwkFeeNo|CIPdpo zK5coEtbD!CgCwzYu|6Xs-ORdVr1U9_BW|awOL{$omL^WEahiS7SPRYh)R1FZJ?>H( zT-kyKo?XkfyDv#eh^fDnYfyhlTS5dna>T(#aQQF4SIS1-t$dg|%U)9AooZr%Qo+`h zr6*~-AjmaLf?3(!>jU>jsW&zK96hl(d7IyHEz-l37;e*QtC))-F&1i{K|~j8gv%t{vhLYJ?nJ-XfH2HW|4JF~k4Aeg+M1wGkcN}e_s`imV@GX%sv!~Q zS})ZGiNS(Fg07ZoePea0FYqN<iz&9VTZj>WyIcsfQA zhVdPAdnS0G(-`FU;__!U?c;`fk?tZ8`hPADJ18G>>8~!G;yo>?D4JP(aZ?s}QgDaa zZ|&?P)~)J|@rJJix9%p2Czf-JfXs3xU2Aw-!>_2a`PR|VkTq+ zRkzoUUUQ3GSrS~!l_6=OfVZE0Zl)qHmvdoIEm6a!@|pYSs%hn#o$E*GWI9rx<9Ua2k5JO}XOaILidP1IxE-J?`x97Lql zF85M}f`?`7`SsMV>s$U%hr45CoA{8D@M4RGK#JqT54W#`(ozx))ch(PZ4yYOa|Exm zM9&=l9L4fDlKQO2M3Bwh;GiCm90i=~Us->@)^!ALY{p;{ZX!HwLf#6Jnz%F+x-2;R z(&>2>9kB1jRPMok!vwx?LZ=0jRdxRd?3#Dl>6!HAih#o3&_DAIgkv_zD&?p#s0XIg zW}!=5&?7a@pCBV9W1p*_b{XT95xU-0Zd*C}5zp5fQJB7im%`LZR$2p|p$`1>b(aDe zH**bAI}t*X{Wnac*cXY-3ZwKw<=FgWm8_$_gD*<5X3|9=6zsi4A?bqKXe1O zW^D#seOW($M3{6l9|aGRw!AbQu5zI*GDLNEH7zSIXq&!d7GJbK?A)7RX7-WpaY-!3 zVS>s9+FQm-!o-N9nQom3Ox>^!%)3J+o(>}T5Mdk&I{2CFXF11T@^$NNcE|-WD{I%d z?mQg*C>usQkH)!r1KlT-PvF{8`8~IEade$22`L$!oV>x~m}kj8`-#d>>+NX@1sBS% zST>u@{1}4himnkuE8WZidTCYU4>}OK|1Gb#6FERI%N<;xQ!pVpGR8Z*7o*k8)-B2m zU;%y}``;UIOD3!}6Ro4tLA#m&`r4Y4xDwBl&C)tBND56RKf2-CP>6zN_qtgdZxyFs zNT}E$3nV}2M80i!Dx1ln>5d;<^bh5_Q|NE(Z@(p)Rv}|;1CI5(5vxGH?yG$TgW_^& zy%c{nSsoei5MMts1jgEahOf+UddFuOl{8niG3;B-Af~@xXCq>3#J{TX ztN8h_ef#jA0m%U($u)X+@VXBtl} z(+FxF_q^EJ-8-)gWXkRSr*0-;wAh8umd>UGW5%1zZX!*6=bwOHRT)W0?xnieQ|TP1 zplAM@aW~&R{RoajUR~&ta_RW@<(z=rt(+TsKOK&j6MVySYO`vs10r#5j=-q6H@J?E z`*aHYH|~Bdh8}CaIH-Otapxk>bh;DjN|qbWk_Uaa^9c3T(t3-uHvY19^g z3X?fs^kHTcQb(X1t$Zmq;sunk-5 zDn*afV$ha1qN|a{=Hu>*fr|WJP2r;Xau-&2y_M~Xiwq6L0z4MYN^<#o#@yn_QTks> z-Er%2uycqJPk#!NYh5f_YGKi$8vCpVdWzZmg!Z$zo}15i#`cDIe3GSQ@ukXK)vH0! z1#!F-cbutG3^=|o?7Rk0Uli@^46SF%|I|^f&O#znoT{xP(1eN-Uqdq~9e*gn(%WM< zjfWzic>d@OhUCxqlKt;!8aQ0EH&7^$|Jwh*Z)d)}f94?rjRC*91QLwlqluWuRbi?4 zq~l?}r_ID1Oa3T~js`w+rLUH0R)w5U)Q2W)eGBnk0YW17UjV@D=;-!7JXI>LZ)`G( zt*jU51`pm)6$POoRZ0&_CV9B>_vJ5CHEo`Ud1ut0U(l!Jmg7Sm+=on;Ee+0sPzK|U)hhtRzjcWw zl9YC`88{%Yrp+waT9h&pRC~W+^LwdoLPbWqG57yDEo{5ur9g=psH{cS$K6S8YLs;Kc%XAUy*^t$slI&=1x&xG=s;eepXMy!gbT4YIHx)`D3G7X?BD9 zadup4p`1NXa*`7v`p=%7Ve!2rpHcf}dv_|_GkgZd+MYdxi}QNFzKz_yzyF}9`fiij zpk<8SdpUy)nVX@m2Lr)3fhBvbhGV8Ku9ihcFnFBU2A7}7W{SEGZUy7JqIlgzP(5Tr zQo+qtDLzxSJ?LO-G!Pzy^9>)EcbxUFSIUWH0QNB}GMUE41KSrW+Ey#2G+ABeQe6$e zzi~fv>vFHQF1No(cj59QHsna9)S;5J!CtzC!l3Tc&vW~%!t6XV@wpO=iwIg80iE|M zid#2Y$?GPmmD$N{604W3I3>l=MNVE`=FV|>6O32Ae!|#(rVN$5%`APgoNH#! z+`j15zR?`xt=^#`%eWYrQHIbwZp`0X&d)T2uk>N_aMHD+7n0UAB)h#Pn*C;qH`lG~ zi1zPQ>sZXb2H$T2xAapQvLU+AuL$5~gwJyykG@5?r1v8Hs=EmJ8uc-{o8-KAEQsed^* z6f`ux6pFkgFX_b%SI#`kyM<*wU0*4DL4=e1fCaeLF1Jul@EVKQw$S@YA^%{zqf{ua zByp_d>KBz=%|0`MOvGxO!j*TRt=`7z9kkZL_QJUD(Mq%y+#zCPJC!s2E0L7<>g7CT z>u2}yz4VfspL<@9>o$sq)=LJ!u|kdyC+xR2|+Mbnp`HmynQLYS$ga<5N!3wiU8^|mrJ|~cJpcQ z%wd^To~Hkh`gpd3=JwF3Q#s1gRIM(E-0c`_ST49XQVIGnR3F;q%6#e7;;#;((y)jc#l>Hn$;1$p9>D zwP9nM$7T9ot-P~CG9;^cp}CQ|<>xv^aZ(?8=o%qEI(e|8%aJ;uAjTcnU<(-e{UaPU zDP}IE3xicQho_!X6h58qi~X7^rzrB<*ZovWMLod9V{rqR_jTuIf45Is*CQYyS^jv* zdNs`nL`i*V{C4Kf3;uZpH|vTo(6D&pCDIjy}NUnH`J zEjYizSlRB*`IdV@ry3)jrlfo^sH`M(+3CPcGd@g z6~b8K?2PB=s+0krZ@cSY@J0@~QEXh9fAKs4V%~G9{gJw1`RCKh zHKuBK(aA}=lD1dy0GT;ihR^+AU2r)uiP5cd^I$Q+X=Fp*h4-xF`6(%bluB@0I+~OP zvuAZ%weN~i{C|{9+1r>!ZX6H3AGM}p0;~xW^ighQTP!`o7tsatZ2`LDV2YL&m*Lrc z4NrMREN#mztwb*v;){v%amKAkutqv@>orZso!aQo`eRF~QH$AG+p9kAfnIM$z8C+! zE2{2~lZ0n81I1rpTvgz@E30=!8cfSWX^*_q*n34rJn!{{^+r0wta8**E~R{eZ%te3 z*D4YWcysFJoM>}E3u9+zJ2%)z4uo=TxqHVlr`(=SD(@J&kho0>N>S5LZfjCAv66DB zGn#T5nVMy#_GbH|eP^DkpCuZTcWP+@v!P$+cdE?s2vh5pA71+kT1WW&z2TNj0gfu{ zdiLxw5UFW2ayz~)SazMCZFZ5C--{E~^az~@KP3)IF5o^_Gn(I4?WBpL6ITr#W6{SI z^&pUv2fQW`IcI$`K(kw(yo-`~AwKHIgRg7>Wn%tH&U^VSd?>XO!PS!zh&RAXihl4&ilGgD11Ak%WP;nZ@r!MMe{FzpvkePDP2N$Z(r z6Ts@9ZO=Ncj)^0O&rPgnCeB1IH;m6LR}84_G~Ja1jg1WIcHwMgNjPshnU6C*=~94w`no91o7Kj(#0s`J zRuf>jXtS{N4aV49!lO$1ob_`5tC$Z83+v791G7g-CPw-1t(WVBf`zicY7$X^Th?{~?Hh zS+6_F+=a^*Rg1TV)dK{mVZd{HFhnC2+eGlW2gQUTJY=o-?bQNYJ^VxmuAt|;s7X~V z?nmXU<(4-p(!2(syKq-~M8pne>ij}0vhMmtu4Ta+dTJ=?p2W77Yv-o_HY-ef{{VbR zYsX7PMI2f}4{**=hC>GgmF;M#3&!ybhq`-}+X)vh4Gzk68Ku1QD|mMEa~wgI@8MwSJC5Km}UTxp(p z+2COqHte!GH=$9Rs-`9)ZnZvbk?*_c&&xHUaG#)Dy=#KQDYri#-!e0|5Em7D=z|_> zUF#m#ON~~$4U|{nnkPN+hJr(Cv(*DlCPe$@y#Jq8fBHiM#U(Fu!L&=8E_2=^C=^`M zjv+wUtAM;?e=W(&Yr92E^dL65VdH}hoRzcw)qiy05^ThLur2lS^|YMXTs1#ZoFXjY zoaWBX<_>oi^M_U3l}(&_@`PI!vZCE9z@_PrK`5~HJ?-PI@KibVn}F`E(B|g4++yoS zU#m1os&Osu2Dlnomzimk;91_F_^)fZUT}(kCU9Ug`wv?ni}DNHY%tJ%G1s8JV&Im0 z8jtXPRxj{>pYrv*70gx}`(X7`v!Y_CO!db^_TmKra;`i6sce~y-06pxvs6aA9@z4{ zy%HQ7RcHx`>wF9ptvb;0F>wuR2fOCy^2m%}fZk=4>5E-Sgc&;44VOzO%c~`7OJGKu zF#=s8Umfn>fjfom=-`Vj+-A%j4)Faw*QmC$Lk~(zT7Brs#wSBsMp41G57jjQ*lxxrV!Ou)9fszv2rOHy2Jo@T(Nwm==y zG9~UyO#Q0VsW+yT^IZ}c&YR(Wj*frpoZS3Yo?jh)wJ%OP4N*m>SVWWFI+D26V?h&h)V9Oterg%)xyayrH<+cCBq^QI5hXPi^3xVflS^u6iLtfLkNwKrnCeJjH# z(|%j5ebrQ3zPBw*tR9|K+=t=5{o*M@H?Y$31mCGzKK)Z5qwqKhK@lY>Cdq~9x46bRt z>`J#95@%yRDF6?2U<++mMc9@Ks4%?&oqOAZ(uqeN6?Zvv9vKY-n{Bs*2W+~Cf( ze_H9AyZEDn3NPK#_>RwekdB}serCz9*d0y9JmW&__S?6@DV?6a_2v-FY;)*KClqfj zb=YlnXK{|9v809xj{M%n9*_dv|2?EUo^9R7=kz`nLf|$Bf(qQO1)+x|x6BcBqJe3!vx}36Ix}2)a z?jjcBE!FY|`3x3pbJZg#-;(FV#XB_sZ$D27Vu^nvODl zuI(zw-JEWs`;VxH{~Z2Gv~O^BpVS;Y|g*|m(9C7y#nO)zG!^&zLP=#9zknUekg2TcSq+Mm#+e~jkDwSP_k)Z+*q9K zOvrqH>7(i`%I6Z<=It%v+S&l~-qVntpFe-%A_nacU1*{U2n4yb5qdbPOdG-L_>Jb} za~6I>P%-zOi-Vx6ak5iQHKQsKYZlr2$(s)`>Egh$;EF?OF+r;6IHEO0NhS@x=y*AWX1`BkX7Fy;CA9pAPO)hZ1DD{g`Oc@k@$Bir2JsUWYG z+qVGjJg0Wg)X#n00oX}ap2dy0%h)&A1&?gBnb=IJ9d&-enz?u~oKf5X*jJyASA#El zk-c8u&L5oJYF?$WI)n~_wE%~l{FdF_@_?w;#?pbBPofV$q&KO|(mUd!-v!&xZjzO* z_i@k-YD*BJauVqWnVxh%Toa=-I%ut_G-DtrMRB0@Kqy)9!-BTb?*|L>L zFrpn*-|{x_>{4G%)BvPwCm{h6_=W8Cu{E(7y)jJ;V zb#>`em>(XRKA@CGZ>v{srRN#9gfC9SFekA0i3xdt*f=>T6kdHimIPEQ z-D}+G?(01R%$W(q4MTPPgza;K-4t46G7b)OiI$ix@;T9u<5%U#VNdJT#;4R3t*TaU z|7%?cUm9Ea(>epURjH2CIzTiMo^d6+)F3j| z>l7aP5=WGQsc*wQd00Z}!J#v2AWXsKYp@ug4vUiNju!jUbQ%A)BC8t(954h`vilgA zoar=00Y5^k(^tHBto#48OUxpL} zO8Hx53fPaicy438BK!bIn?3v+D33Sv=cn91GY~%W)YIGxnLqEnKW>i)esO(U-__ zWJZ>8`PkaTtAUmpX*!-%H=@xCl`4FFTP0$v3&ILg6i-n>c7eYE}w_5n^QC++T;E0~V$}w>aErj|g;{8!@@R zboZKFEgqOp2r<>%${M5|nDi?ee9dU|iPaW1haiSQ(B<&bG}^ieI4yIcobP62$ZTp$ zWBf&Zo-=G$tgfocp9=HAZB$x?Q;PEQv$O5L)rcb(?Zbltzi6);Lw!fz+@>07F4vun z9!II-Sfjm#F&xIrPPCUrSlbmrUM{~%?;I=+C|FsJ5KFo>S?A~6*k}Vc%Xf*+Q-444 z;-&jzGZc2CxqKsR%nlk~{6dh8`ebc$K%I7}l<$RpgyU8KjCk*z{ZVO$c2#<}Z&;{0Zs4G#9-jVP`Qd$|ARi(*i6cLB-p z?H)ejMQ_>Nj8-JK>DJ6-D~OUZKd)eNj>5R9vXW9kFef)Sr}E9ZH(a`@w)yGQpj%cP zBx3m6TLNj`z@XM#-*wIjxIs0^RVBGg$67I+(7x^oD2;4knN_0#MQO-5pD~}*7rZOo z?UK7--u)o|zTyP8s+CLkSsltW0B~H}nj(C7>f7Hwhw_zJd@KU3_Ei2aRtcUS0q@79 zJ(NoU!$xz!eN{_=qk<$|XZ1mjuamtaw`>kcRd3($@ybTp1s5eEAnE`(>zU?}b8yzy z@@77FGB#@Nlu){`L3%A&y^6gTsCs}-(hMIwaQL%u40~q?q!a*@1k*H`{pM!kSI;jb zq^@MW(e;-NS&88T&v#)b6H>WR7S{yhtFCFa+V*9JEf$*)sjkuE4#JWll;J8cAvLbI zc85>dr)_x&r^^8&x>=juoGwheO3SJ5+WG``{;{_;9Wx*xs}(w*HZ1|7w9x|9SSwb? zWAO3mko%i)=0J4hkriuU*5RKMjp?K$^RVA0<)%j7YpUE^uaAn>o!xX-teHEE@Y$5Bm>5gr_YR$+BjkZL)k^>%;n{1?H8LYHOiGR!&-=V;aaFjWwV(=cc9wN{wrec zlGedqn=e3L-7?LM&8(nPaC{~knwGZhZOJw*fd5KV7gl>jUGa{5buMJ2r>E#^-Lh)^ zgv5me5wgZn2M#-lCVcxS3oZ@3q{*!Bn)KlE;L#z6t`5fNO$|o6{`gGTQ<~EEx0Fck z?lrUcSTUOMdNlOubc4(KbVpWD%ROZ+R@7hwlZHD^C;WUOWo57F_dS9&-*#pG%k7tV z0O2C7@#(+N-GAG#d_^=JBHi1H!sW2E6~iVOYu~-rH?l655S=`?N0uYb#ps41v_^;LQnqtX_e#tjW(SRBR=;sGbB$%bQw*} z9se;4jocVUP!|^yF*juS&ietCAK`z?@frTj+3~Lu+_ui2$Q%7mfUXcn7h5qHd4Bn6 znB;;P(@;vW_pJ`h;hN1zCL9`3vfsscS9TxQgRru(#x3>=*d#2zVE}er`%LPk zr?4^GYF|fp%F?TnFoX^rJ3aBTJpeaioMH?yIhj39oc8N~2_=66q}r+v^-70iX<;-* zTYi806JTqwr7D7Gec0VDVY?NEqNxM)OdFb3O1QRFiOG zU~AfklpQ57IP|IX8SFhN5FvwjIO==c;?SRAY+u6`j%cmsX}wV^%#z?>(m?Q0YH+26 z)vgwO?bXpi5zz1ZzC0}Xqwkw-?~tuexTqk)M;Sw7djatUjC#o7s*nKrLq&#RSwa@M zq4KF)f3eH}X{$@%UA#lCZOdvy3z?+<0^8hVfii&hEW$Gr?eAxE1`OV>W~}rDxsuPQ z@`}%5c20z<6BW+Ld-m&DA0R)&tENe;edFwif*)|)qUk*V>_#+TU-}p{1O^^+&~7zT z<#s{>lqP`8Z1V1oP4+3{;9)-lyA-)?B$U6 zgdn9qKF35wEtgj|n}c25OsHE?133+R2KzrhKgNf$NgPUQ-YeyQ;?4sh>k{~4R1*;L zZ|jq$ZMU>P2IJasp-ZrJZvLe3**^zPF+>#xyXIQ(v(Ly(yxo}AQEzIHfzY$kuV=rC zRW_|PGqG7+%X3n-0dKg4w02rpeqP~EJa|8VKvEc9^ypr9eL=>ilq~kJ)Oq%pc&a7o zW~FdVP)IoX4l7Fo2-$RLycjytGv`qf3T>zC8)Ui4s2lg?7!vu+lR_M{G@hLg8pyG9v4q!mQo|! zRIii5j(58Q@_q8dx6j@99^uS#v(d$6&4#dzXs%&q>kI2mn|E;)$b^ zE-CTDWCYhe`TI|#GmA|Ot=#yOeMyAz5iv@@a|=|y@BO3q7H3`glEf;H%A{B>Xg8zm z;skt$*XOgn()rV@W|Y>>(y$zBZKpkC{`+*t=q6q3Eph*16~H*f0E(ZjcqnEz+SeGt zY6%~CIYP5f|6lbv;|}1=6W%-vmoBtg)sheaEmM-a5Wq6v|9$I-_2>1;=dq8ezPXhe z<3M;JZ@pN+6kW|evn7c2*>{_>A~b9;SjoYkncRlBUXOlouGY<%jR_c75<# z73z>gPh$1WvM@&KvUdxH3s3PPk#AFqJ+fUP7Ci4(l`1h80n1A_)m3>ewmYr6S2(CD zwC_=BlZ-SP#PU*K%k>4^vTofHGx+>z+^s!rwrOTCKV)vGW$U>Kx{)#ec_c^43$giK zpQ}_xS^#BGA0lGB>gYtL58lt-*+qCz1DKz9Y%o{MZ zlW-OgSsXr~1s@3I8dD2*Xnv^&2Im|Ws~3HaYcuLZ*r-Ry{vtMluzT(m^1{3;F)x{q zy^nV%>DJK)InrXU)AOeGYBN-hx=5CH=#`G_JYn4>xMzHOyDr@^k?z(WKC z^)M~0znZM4OJ=5%AlbzzW3l*-1Od@RK`mg!172Z(=t_oFmwQH*7f-;y8a z3*IDIlfy$Jb|>mTFQ7kuUHZPi*~sd;b?9Oa>=CWn77sKJzHo+Pd9!V1cX0~mc3WRA z{jBV3&^661=J|XgyRvP8%cUJJal`Iz#WNSCblZ7%F*F}JpNIgz)toK_t!B~$IDD!6 ziZ9XI=Jbpv}zFG zR*BmWYJWC{A%5SlH$kbgpO`p=4kY2w&t%W4U6zBwF!c=fT1Z+iuRr+!T2J<5?|5dW z{o!B0#TTSjiZnEiAKZwD{AmYNWwR+TUDw1Y*jP-dF>rz=?wv1fM`?BUD-8(tK8%@d zVdZNu4_c~7@S}~B}lBq(b;y|pRPg$9gvBjLD_6yg|`BfrrCAMG{ zW0ibIr!@R>$YxdhMq6s;EZp67lRfA3e9e#ish%{gUN=YV@m4Ng|1jbX<;zvkU>qjT zfl~Sw{zusU`KM-~YuCSBvccF$l&+hSMa9WYAJcMQxX{E}FSU<&9kecAIOqlT;ZePR zn?b=91Brr@n0og6{8Nv=I7dXhfs|59IE{A`25+hcZCAS~&HM`sdGQSV`u6Xy|54v# z(xey`M2v&)qdAmG>ZmGvV1!$F%k45+Db!g6v%yUO2R z5>2=qT8L4KNTzq2*FRNNC4|)2wteEN-~Pcp2?P5zK(*UIu|-W9JiUbj{=rj7kxTN!b^|Glb4#8c4M5)uM_kzCO%{K zQ8B@oVpH7AM2OBGZu%vre#JM#@0yYhmOAa~z(7>PK^JG^{(oeI6i;a!m~Ot5GKC8N zwqrNvl^1Yqjx!v?^~4?qCCVBmA|;`7CW(%G(p|*Htp4ZUwZ7)${Fp!ace?2N9scP@w>MR zqmoeeOLS1F5fH=*#Yyy`FrVDy3){6ZsJOLu34RrgR}FlYAdpw>{Y*`_V)P~auAj^G z2CZ$uNDkaRwh~HTEdOt)UE|z0@i7=lWzW3}a`g+I|GeVX5;T>HSybw_^N{#Wn&Wi! zh&FTj-BT72?NdF-)1^V|U*H=L*vE|nHXhI1Gq9j}Ys;B2Ia5Yu>Zy3;avN2C8F5yl zqV)b|t$;>h|3hHKq;acj#!4C;`8)S+T$+<2Ho@-_%!4&YbvM6y7LmADJ265O6QrEc zRVd`7z{c=dj6Y4L$?DaGpM2J41D(m8>RRYr7*me5wzg(J-Y;)ovaWv^1Q<~5drQ41 z2wHbOxwpq3IyMtkSIvs}ylJ1I-gwQ4SqsN>e#1aX1EGn!Raf!f=H3<7g*51kh8vmm zWycX~pufvLH6l#Ju8{Z|MXEa%m!zca2&$vOuz@rZ9IcSv#5cy=V07_3Us4l9pJv)C zJ-3`&>y(1?9e|VER?riFH2+=yR=P=$H!7cRT6!x5#r)!9}C3Ts@$VLj94-{9at#5b%)TC?N&BA=60!aQ!;}sbY86J$Kv<%+?H^yTNx?TwfZ{CR!)H;?mzh{qJjH zlDCFDdY0doanN$A!fd~G{$K0Uv~`2!(;+xAvhBC6y5GHvLsXj}%4}EpMHpQdj|iSa zT#<#P){a|Oprxcf_xc^^Ing*>B{jo^zckXuK&P;6ZlaaeFF^kl;s+zln~O2kVX)+D zA`|p6CJZB->vuLWI45ttJ5ifuhLLMzZo*W&AIWd;0=K^*o;;~( zr0&01SPKLeG5e37cQlLosRaXZhZBedPr7qkAG$AHJ?AN zmX5D29LFcCa&34G!1!8k$q834LK4dZ<#T2fcBwYojA?pb{0pj^A5Bhfs=hSVa2dGz zQ@KNod>NuxG;*qcO>)$|k^7s4@br&pj$INfg^@x4{;KvV*{uhwKZYb6wS9r|Igv9N z5O&A4NoxK-dJE_uwn4j0(NN!2&ct$%Ojyag^ZOy&ZW?w}m1?;rkz(_}54d^{V`B<( zBkPPCvAq;p$qOR1it3wq5Mphl_0ZU-gl}u;_h#`1{frvRU=skO6%iK*t0DM-a2-MpH+ClrJq` zNBh6soRy_}X7{J-NTB611f9aX zNmzHe)Dnkaf-#v*5T>tpAYWYYtGvnVPzA~!0#n2Q2(48f0%$In@1hCHKN*d#QZkhv zI{^3W1Y&U&U9=U5-%&mIunM)Q<$NfbArP-7lLOZ3A?lcao^sXdrUk$*ZXUpVR;qKE zm6awmTE1Aed)MxHk;|>q z$Tzz{ha+;lH^+@C8?qOa0z>1EwZTGRIs&MbRou_g+1C|{N9{7$t(-1jo9227SLdoB zCh$ZN(t-wA)PY3Fm!}^p`L2!CY>*jKSPL3xl;mhQylt3)xt=iWAgp(?qBZBlXcB;RHn*y1b+CdZ%dRHw zgV)Bi-xR6H7c(fqv(PuvE~F5R=yR~TkhTsAS{LD3zsdqdrLzR+OxRFu76O zPF9RZskbqlYU|~~jbmHfb#<{EPh2fH=im5g$>CvguyB-!@t!VBT`g>^egVZAsaHCD zp)+nZv!LoGH7smyARF|3+E72uTUgRhg`%rM5RAi{J<6TL6g3|K!xbcqG$c+cQkPQT z*`SuKQCs8f;y>}~n%>}*L98h0US9F$4Rix{a>O^PO-(6Jyw zCh{Ayzq&kmPeBE+%F{cwTV6Ns< zE4;f7XipmfU+9t{|MC=R5k8sUUF}gu6_k+-?cy&G3>;FI-GfwDs#zniqJA~#pddtj zuY2r_`0q$GRd_E{90M|r!bv>o6p6~W0VZu`EG*9C88{_1z;TgdggQK4RA0V&wzvwL zPb$soY|#P~)4}CCtsLU#eq+VTdkEY3G+-8Rlx^O!MKw~cnr>1lqRC?#kcAioH<3NR zzpONJ_z{o;VCDAA}k{{g@=VH3xJZy#I`_u z02~evITq2x-zlJaM+*!Qfhm&XX-7mUBBUs(&7w(}Z-ur`Ik4%S*}Y8}clu|FLtloF zIK-sz-s1NW+S4tWT54&eTKe|a%+~h>hjc-(&PighCk-y+X75%d!Ap)x<#C|5ko`yxOHJDbZ%gIU^H&F?|M6v`6$3rxA8V-NXSD}&%4!JXcXzo zvZ1DjySux2-GjbCF)It76!b)T*7lsXGVjFOzXP|UE=MIK*lxa~mHaW?-95)h{vp%( z85GODW%cEJq6lPH`CbZzh({P%Ss>$SBQCCZ@5JduKH9VXZ0Pm-)6&KG7Mz)vOB@C1 zs~J>Nk8TEU3Hvw|v~i()?|t)6r2X=`Xx386L0`lU&K&f^Q=bpY8AA@01l@7q&>jqB z@Hy2CyGzn8TI&-f@Z0VkeR1AQsbr{5)sHBx=rRgdIY-kq=`xGt(m(_q4@pl_2oYz` zo=u*151Na$H}*c5|IVy&?loE=6)N5d=;zILikcMjFx=1~uE96$Iixo#)GWmq2d@!L_GV-)_f)+%-tYRp87%pDRj=>lD$H&zU zbQMJq=?{a;(;+LW401+VS{l1)ifFXj8RVo^ig~NaKa~Rv0k|uOY`CJx6Oea4GKsRZ zIdWu?RIYq>)UCGrsD)eXU#9!n1>JmwNL@A5WoK1^^5$+Fa;vEa?wK)RNTIK_Fp=y_ zt(8V1G=mcH(9D$XRa%pP-Mii_TAwPm#!Z&X-~?|dRR_k1rJa z(oKCk)!_dMPd!~&AkkBpZMt-UE3k}To56zz+c;9WvglLonGGt8G2nm>Gyl?Ie%m8Q zq|{bG=!a9qSj43A)(r_4R|NQYfG5);H{(Ggt%-Z<5|^Jt!5W3`H?G)t-8z_{Z6&bU z{-5K_LY*X_ij8@So*O*HyzCnuafBz4E!csi#NXO2R!bXlBKD>uZd_=X6~0(u$a&?u)Qr`4#C7eJ#QPkvXHH_fA`6F<>#8mfQd;9jppd-wEDTHr^f+o z(R4>~*0fsioJTLg_p6@4YM;oFhn?HVJc{-x^X4{wCAs%r%vpvIj+c#-h26jm+0;iQ< ztR~uIGFde`mc4@l^35LXc1)TZt}BUXS_SXUE>*e0@)T)XVe9i~S$Tn!c=LU-H7@P( z_t(q;*1sqj_}nGzRSNC!3P5hC{&;--84-t1ArLAD^$qbuN-GGu=?Sku;rs zfJ}lU4&fWbJHX_CJ&@1yOkUDqu~;XFuvw!-Hf1SetYb}nm1}t&gOVnxsw9QNVRA}` zl*i+xFdPw*< z938K`s_8|Y4t$e*rW+AhCnBeK8Q}a$po`hk$|~o1#DV$tn}dc!VQr!KMs*0LR31UI zu)_ZO7y8w%|BT+wSYU`C#S24VnhrQE^0J*Ab_`|9cs~NT)2(*w+*acXR5Z_GUmrO# zc~W0PYj;wIL+VN#9;qs9Ql5@FP~IIr{Oal8EpuFcpzvY}8_6Svvhy`HHFGpI*R$hN zmxU*Ye}h2OQj+>Z96M0f`q*WfVay z2A#R*{-UTpxYdAR&YB&+$y3Fcx2SOTivo)|YP1{So6F~c&K*3-JR)+Gz(khsole(EndRYm9NZIrslJ}U=KCRU zN^)JqeGVe304F=S(5&(eNIk80w|wRE{&*1&jq%%l8o^6s>(8gQ%yTJ_rAPFh*9SLM zetPr?hJ#`Up{$yqKqfoK$Uu*(n5T5Kx0m3iNb|tQn&7hj3WjAIFc3ICoRd}WfqK#$ zyWbAs>VznF#}S#Pn<@`)rkKlHfYrid|B3X&8%P{<5-lQps@6RZr2C_4~2e z?3*d6DK!xrpcTne4B}$i+cdKWt?v8J6ciRT9>*w02J;)UFNEx9WA5{}Tn~ppe8Vib z+lotoa`8mq6lmwT#d#Bs_7HS3O|Xg_`$FF~cf{cMU^KMC*_~txMT~qJ9ODGmo>@1m zh2l`&PU3B4ftwnTw-I4)hOl4R7Frc{1B;)uSpVqE?*2S^{lMmyc#7fOv^c2qw`p}A zt%=%RX;DMBaFm$q`P&4lk=16nGl0@9@zZ%CF5Cix!R>AC4LjgbGnSUML?_*U;Sgtv z=eTrVXD3AwBR>7P?f-2QmWp}c2YZk+%1cmXU_rD;$^!lz3U;Vs;38w+losA(dXQs^ zPJT}y_T^|@nFQ10==9)46sq4%|0=3KZHu$})<6D>Xv@3TatTtJu-}k9WPh0VTx3}P zkxn|iL`E>eA;7Ce^_&|Z`dM3B49q`PwvY@{gI+jts=2-WZWnIe*|T}0puFsh^+*zZ)A#bezMN{&X!S7%{LaqLK!@ZtOCH@OVt?Sp%oc3^jdFuB& F{|1UMJIVk6 diff --git a/assets/share/assignment/dispatch/CHARACTER_2_SELECTED.png b/assets/share/assignment/dispatch/CHARACTER_2_SELECTED.png index 0980134cc15140b809335dc871ca2903078616e7..1bfd1964aca5db060ad90bb1e2880f6f18776340 100644 GIT binary patch delta 851 zcmV-Z1FZa*Fq|;3z6yUyNklps)~m3H$>J^rA$O1Paw5q{Jj$7a~{?BEwBrU3Vol+@0B(o!{%Q zwaMD9mtkt+^Wk^+4e!n6%e#EvgZB#o000000000000000006T;3(f+6;D0QNfEK9N z=a%17N|Te5Q&Tq=7Z-baHVzCtS1OhM9t|;1SxBLf6^g}TjFu<>003=OfR~^YiycM-OFLwr$(X_wUcH7=8E=9RL6TZCrp)p8TX-E^poX z(%ri=YqVbl7ytl(HZH&yFMik6wV|hHez@~p%F000G;c9KGhA%+mTySsPp+_7SGU|`_UzZ^e)`s(DA=l}qK zX7QOBTA@;@6pD@b*!31e3L($at(!Bi?s}(Ess8-aIq3m^002$lJHWI;eKCX-Qc5YL z$4nZ*`5Ia02E-_ZJotNBZWo^A%(R&@SWM;-W(peas5^nvzS?*004jj zOj{|J%Q60Spu2hpKK=E%9k1-WaqV^%vXB~CbOsCn05pwDuxN!sp-?DvOkJPO^Tn?2 z^&!?n3`_eSFNIJHDV@4B{`se0Onh@`{{5kj(a*A2jCo9XLv*vT5J?Aa delta 833 zcmV-H1HSy6Fqkl~z6x(eNkl;XKj`S#nPpj)Wo>QGojiGB zeY93vtk#y6tL4gaxw2eIDHk08002!{fTyN@+Oeapx3~A)xlhiX{kW&6r&KCky*j>s zu>-Hw;7YYJKmSm4002OJMG?@Ls?}FI*R00005n5ML6&+aQ%zF+^jcE;P**7jukjWIp@%u^Br008Q9ZxD@T<0V+@=H}-6 z`(MoS{Kk!+ySuypcO{oft0(|k}XDux)`}V!EurODr{VKo!001;`0ls$a zhmMY&U0u5-Cx6_k_f~6*mFnW}w{M9K005}ZEzmTm+1Uq2kG@l>RK~_G@811?QvJ!7 zMlVYY002;cX-X3l6E|=ERw|Va95@`~Kex3VIWlnO%tveQDTF+w7*b66H(!rQ3;+O7 zfN3Tvq!?ldp|i8|(4m8yM*I5up8U)C^P{(>XG8}80MzH>F*KHPxm+l&#%)(-3@L;> zPxtT5y?*$;a=G&BFXPe!008QL@Eu?pOKl~D6jDklq-`fJJo@w9W2csvt2S}~000G; zW?PYQbv1?66haF1JMhE#Ki(c1zI*3>7PFXH$p8R=0!&kBX=#b^-viyPJMieY;|E_m zcK7y!EMy_AX3-fi006KJmtfIY3WY+U&^mKxHqTc&I@?36g&5X$Y6_to7*e`)ee%oC zznc2)`qGDkts`G#u^981@>S6Rv#}6O2a`Yx1s)CQZvX%Q|NjF3d-#%PfH8t<00000 LNkvXXu0mjfo0*n5 diff --git a/tasks/assignment/assets/assets_assignment_dispatch.py b/tasks/assignment/assets/assets_assignment_dispatch.py index 25758c969..c1965a3f3 100644 --- a/tasks/assignment/assets/assets_assignment_dispatch.py +++ b/tasks/assignment/assets/assets_assignment_dispatch.py @@ -34,40 +34,40 @@ CHARACTER_1 = ButtonWrapper( name='CHARACTER_1', share=Button( file='./assets/share/assignment/dispatch/CHARACTER_1.png', - area=(110, 202, 202, 309), - search=(90, 182, 222, 329), + area=(96, 200, 188, 307), + search=(76, 180, 208, 327), color=(153, 141, 159), - button=(110, 202, 202, 309), + button=(96, 200, 188, 307), ), ) CHARACTER_1_SELECTED = ButtonWrapper( name='CHARACTER_1_SELECTED', share=Button( file='./assets/share/assignment/dispatch/CHARACTER_1_SELECTED.png', - area=(107, 199, 126, 217), - search=(87, 179, 146, 237), + area=(93, 197, 112, 215), + search=(73, 177, 132, 235), color=(217, 218, 216), - button=(107, 199, 126, 217), + button=(93, 197, 112, 215), ), ) CHARACTER_2 = ButtonWrapper( name='CHARACTER_2', share=Button( file='./assets/share/assignment/dispatch/CHARACTER_2.png', - area=(222, 202, 314, 309), - search=(202, 182, 334, 329), + area=(208, 200, 300, 307), + search=(188, 180, 320, 327), color=(120, 120, 138), - button=(222, 202, 314, 309), + button=(208, 200, 300, 307), ), ) CHARACTER_2_SELECTED = ButtonWrapper( name='CHARACTER_2_SELECTED', share=Button( file='./assets/share/assignment/dispatch/CHARACTER_2_SELECTED.png', - area=(219, 199, 238, 217), - search=(199, 179, 258, 237), + area=(205, 197, 224, 215), + search=(185, 177, 244, 235), color=(206, 207, 204), - button=(219, 199, 238, 217), + button=(205, 197, 224, 215), ), ) CHARACTER_LIST = ButtonWrapper( From 092b2e60db5bd199dad15787ccb23be84a59eae5 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 13 May 2024 03:01:32 +0800 Subject: [PATCH 087/114] Upd: Assignment resorted in 2.2 (#453) --- dev_tools/keywords/assignment.py | 20 ++ module/config/argument/args.json | 112 ++++---- module/config/config_generated.py | 8 +- module/config/i18n/en-US.json | 112 ++++---- module/config/i18n/es-ES.json | 112 ++++---- module/config/i18n/ja-JP.json | 112 ++++---- module/config/i18n/zh-CN.json | 112 ++++---- module/config/i18n/zh-TW.json | 112 ++++---- tasks/assignment/keywords/entry.py | 274 ++++++++++---------- tasks/assignment/keywords/entry_detailed.py | 274 ++++++++++---------- 10 files changed, 634 insertions(+), 614 deletions(-) diff --git a/dev_tools/keywords/assignment.py b/dev_tools/keywords/assignment.py index 30dadcb25..25d53a3e3 100644 --- a/dev_tools/keywords/assignment.py +++ b/dev_tools/keywords/assignment.py @@ -5,6 +5,22 @@ from dev_tools.keywords.base import UI_LANGUAGES, GenerateKeyword from module.config.utils import deep_get +def resort(dic: dict): + # Poor assigment sort for 2.2 + order = [ + 1008, 1007, 1006, 1005, 1004, 1003, 1002, 1001, + 3001, 2001, 4001, + 5008, 5006, 5005, 5003, 5002, 5007, 5004, 5001, + ] + out = {} + for index in order: + value = dic.pop(index) + out[index] = value + for k, v, in dic.items(): + out[k] = v + return out + + @cache def get_assignment_entry_data(): """ @@ -16,6 +32,9 @@ def get_assignment_entry_data(): deep_get(expedition, 'Name.Hash'): deep_get(expedition, 'ExpeditionID') for expedition in GenerateKeyword.read_file('./ExcelOutput/ExpeditionData.json').values() } + rev = {v: k for k, v in expedition_namehash_to_id.items()} + rev = resort(rev) + expedition_namehash_to_id = {v: k for k, v in rev.items()} expedition_id_to_reward_id = { deep_get(expedition, '4.2.ExpeditionID'): deep_get(expedition, '4.2.RewardID') for expedition in GenerateKeyword.read_file('./ExcelOutput/ExpeditionReward.json').values() @@ -119,5 +138,6 @@ class GenerateAssignmentEventEntry(GenerateKeyword): if __name__ == "__main__": from dev_tools.keywords.base import TextMap + TextMap.DATA_FOLDER = '../StarRailData' GenerateAssignment()() diff --git a/module/config/argument/args.json b/module/config/argument/args.json index b37292f5e..64f0ee68d 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -1076,100 +1076,100 @@ "type": "select", "value": "Nameless_Land_Nameless_People", "option": [ - "Nine_Billion_Names", - "Destruction_of_the_Destroyer", - "Winter_Soldiers", - "Born_to_Obey", - "Root_Out_the_Turpitude", - "Fire_Lord_Inflames_Blades_of_War", - "A_Startling_Night_Terror", "Tranquility_of_Vimala_bhumi", - "Nameless_Land_Nameless_People", + "A_Startling_Night_Terror", + "Fire_Lord_Inflames_Blades_of_War", + "Root_Out_the_Turpitude", + "Born_to_Obey", + "Winter_Soldiers", + "Destruction_of_the_Destroyer", + "Nine_Billion_Names", "Akashic_Records", + "Nameless_Land_Nameless_People", "The_Invisible_Hand", - "Abandoned_and_Insulted", - "Spring_of_Life", - "The_Land_of_Gold", - "The_Blossom_in_the_Storm", - "Legend_of_the_Puppet_Master", + "Scalpel_and_Screwdriver", "The_Wages_of_Humanity", + "Legend_of_the_Puppet_Master", + "The_Land_of_Gold", + "Spring_of_Life", "Fragments_of_Illusory_Dreams", - "Scalpel_and_Screwdriver" + "The_Blossom_in_the_Storm", + "Abandoned_and_Insulted" ] }, "Name_2": { "type": "select", "value": "Akashic_Records", "option": [ - "Nine_Billion_Names", - "Destruction_of_the_Destroyer", - "Winter_Soldiers", - "Born_to_Obey", - "Root_Out_the_Turpitude", - "Fire_Lord_Inflames_Blades_of_War", - "A_Startling_Night_Terror", "Tranquility_of_Vimala_bhumi", - "Nameless_Land_Nameless_People", + "A_Startling_Night_Terror", + "Fire_Lord_Inflames_Blades_of_War", + "Root_Out_the_Turpitude", + "Born_to_Obey", + "Winter_Soldiers", + "Destruction_of_the_Destroyer", + "Nine_Billion_Names", "Akashic_Records", + "Nameless_Land_Nameless_People", "The_Invisible_Hand", - "Abandoned_and_Insulted", - "Spring_of_Life", - "The_Land_of_Gold", - "The_Blossom_in_the_Storm", - "Legend_of_the_Puppet_Master", + "Scalpel_and_Screwdriver", "The_Wages_of_Humanity", + "Legend_of_the_Puppet_Master", + "The_Land_of_Gold", + "Spring_of_Life", "Fragments_of_Illusory_Dreams", - "Scalpel_and_Screwdriver" + "The_Blossom_in_the_Storm", + "Abandoned_and_Insulted" ] }, "Name_3": { "type": "select", "value": "The_Invisible_Hand", "option": [ - "Nine_Billion_Names", - "Destruction_of_the_Destroyer", - "Winter_Soldiers", - "Born_to_Obey", - "Root_Out_the_Turpitude", - "Fire_Lord_Inflames_Blades_of_War", - "A_Startling_Night_Terror", "Tranquility_of_Vimala_bhumi", - "Nameless_Land_Nameless_People", + "A_Startling_Night_Terror", + "Fire_Lord_Inflames_Blades_of_War", + "Root_Out_the_Turpitude", + "Born_to_Obey", + "Winter_Soldiers", + "Destruction_of_the_Destroyer", + "Nine_Billion_Names", "Akashic_Records", + "Nameless_Land_Nameless_People", "The_Invisible_Hand", - "Abandoned_and_Insulted", - "Spring_of_Life", - "The_Land_of_Gold", - "The_Blossom_in_the_Storm", - "Legend_of_the_Puppet_Master", + "Scalpel_and_Screwdriver", "The_Wages_of_Humanity", + "Legend_of_the_Puppet_Master", + "The_Land_of_Gold", + "Spring_of_Life", "Fragments_of_Illusory_Dreams", - "Scalpel_and_Screwdriver" + "The_Blossom_in_the_Storm", + "Abandoned_and_Insulted" ] }, "Name_4": { "type": "select", "value": "Nine_Billion_Names", "option": [ - "Nine_Billion_Names", - "Destruction_of_the_Destroyer", - "Winter_Soldiers", - "Born_to_Obey", - "Root_Out_the_Turpitude", - "Fire_Lord_Inflames_Blades_of_War", - "A_Startling_Night_Terror", "Tranquility_of_Vimala_bhumi", - "Nameless_Land_Nameless_People", + "A_Startling_Night_Terror", + "Fire_Lord_Inflames_Blades_of_War", + "Root_Out_the_Turpitude", + "Born_to_Obey", + "Winter_Soldiers", + "Destruction_of_the_Destroyer", + "Nine_Billion_Names", "Akashic_Records", + "Nameless_Land_Nameless_People", "The_Invisible_Hand", - "Abandoned_and_Insulted", - "Spring_of_Life", - "The_Land_of_Gold", - "The_Blossom_in_the_Storm", - "Legend_of_the_Puppet_Master", + "Scalpel_and_Screwdriver", "The_Wages_of_Humanity", + "Legend_of_the_Puppet_Master", + "The_Land_of_Gold", + "Spring_of_Life", "Fragments_of_Illusory_Dreams", - "Scalpel_and_Screwdriver" + "The_Blossom_in_the_Storm", + "Abandoned_and_Insulted" ] }, "Duration": { diff --git a/module/config/config_generated.py b/module/config/config_generated.py index 798eff74a..efb301bda 100644 --- a/module/config/config_generated.py +++ b/module/config/config_generated.py @@ -119,10 +119,10 @@ class GeneratedConfig: BattlePassStorage_BattlePassQuestTrailblazePower = {} # Group `Assignment` - Assignment_Name_1 = 'Nameless_Land_Nameless_People' # Nine_Billion_Names, Destruction_of_the_Destroyer, Winter_Soldiers, Born_to_Obey, Root_Out_the_Turpitude, Fire_Lord_Inflames_Blades_of_War, A_Startling_Night_Terror, Tranquility_of_Vimala_bhumi, Nameless_Land_Nameless_People, Akashic_Records, The_Invisible_Hand, Abandoned_and_Insulted, Spring_of_Life, The_Land_of_Gold, The_Blossom_in_the_Storm, Legend_of_the_Puppet_Master, The_Wages_of_Humanity, Fragments_of_Illusory_Dreams, Scalpel_and_Screwdriver - Assignment_Name_2 = 'Akashic_Records' # Nine_Billion_Names, Destruction_of_the_Destroyer, Winter_Soldiers, Born_to_Obey, Root_Out_the_Turpitude, Fire_Lord_Inflames_Blades_of_War, A_Startling_Night_Terror, Tranquility_of_Vimala_bhumi, Nameless_Land_Nameless_People, Akashic_Records, The_Invisible_Hand, Abandoned_and_Insulted, Spring_of_Life, The_Land_of_Gold, The_Blossom_in_the_Storm, Legend_of_the_Puppet_Master, The_Wages_of_Humanity, Fragments_of_Illusory_Dreams, Scalpel_and_Screwdriver - Assignment_Name_3 = 'The_Invisible_Hand' # Nine_Billion_Names, Destruction_of_the_Destroyer, Winter_Soldiers, Born_to_Obey, Root_Out_the_Turpitude, Fire_Lord_Inflames_Blades_of_War, A_Startling_Night_Terror, Tranquility_of_Vimala_bhumi, Nameless_Land_Nameless_People, Akashic_Records, The_Invisible_Hand, Abandoned_and_Insulted, Spring_of_Life, The_Land_of_Gold, The_Blossom_in_the_Storm, Legend_of_the_Puppet_Master, The_Wages_of_Humanity, Fragments_of_Illusory_Dreams, Scalpel_and_Screwdriver - Assignment_Name_4 = 'Nine_Billion_Names' # Nine_Billion_Names, Destruction_of_the_Destroyer, Winter_Soldiers, Born_to_Obey, Root_Out_the_Turpitude, Fire_Lord_Inflames_Blades_of_War, A_Startling_Night_Terror, Tranquility_of_Vimala_bhumi, Nameless_Land_Nameless_People, Akashic_Records, The_Invisible_Hand, Abandoned_and_Insulted, Spring_of_Life, The_Land_of_Gold, The_Blossom_in_the_Storm, Legend_of_the_Puppet_Master, The_Wages_of_Humanity, Fragments_of_Illusory_Dreams, Scalpel_and_Screwdriver + Assignment_Name_1 = 'Nameless_Land_Nameless_People' # Tranquility_of_Vimala_bhumi, A_Startling_Night_Terror, Fire_Lord_Inflames_Blades_of_War, Root_Out_the_Turpitude, Born_to_Obey, Winter_Soldiers, Destruction_of_the_Destroyer, Nine_Billion_Names, Akashic_Records, Nameless_Land_Nameless_People, The_Invisible_Hand, Scalpel_and_Screwdriver, The_Wages_of_Humanity, Legend_of_the_Puppet_Master, The_Land_of_Gold, Spring_of_Life, Fragments_of_Illusory_Dreams, The_Blossom_in_the_Storm, Abandoned_and_Insulted + Assignment_Name_2 = 'Akashic_Records' # Tranquility_of_Vimala_bhumi, A_Startling_Night_Terror, Fire_Lord_Inflames_Blades_of_War, Root_Out_the_Turpitude, Born_to_Obey, Winter_Soldiers, Destruction_of_the_Destroyer, Nine_Billion_Names, Akashic_Records, Nameless_Land_Nameless_People, The_Invisible_Hand, Scalpel_and_Screwdriver, The_Wages_of_Humanity, Legend_of_the_Puppet_Master, The_Land_of_Gold, Spring_of_Life, Fragments_of_Illusory_Dreams, The_Blossom_in_the_Storm, Abandoned_and_Insulted + Assignment_Name_3 = 'The_Invisible_Hand' # Tranquility_of_Vimala_bhumi, A_Startling_Night_Terror, Fire_Lord_Inflames_Blades_of_War, Root_Out_the_Turpitude, Born_to_Obey, Winter_Soldiers, Destruction_of_the_Destroyer, Nine_Billion_Names, Akashic_Records, Nameless_Land_Nameless_People, The_Invisible_Hand, Scalpel_and_Screwdriver, The_Wages_of_Humanity, Legend_of_the_Puppet_Master, The_Land_of_Gold, Spring_of_Life, Fragments_of_Illusory_Dreams, The_Blossom_in_the_Storm, Abandoned_and_Insulted + Assignment_Name_4 = 'Nine_Billion_Names' # Tranquility_of_Vimala_bhumi, A_Startling_Night_Terror, Fire_Lord_Inflames_Blades_of_War, Root_Out_the_Turpitude, Born_to_Obey, Winter_Soldiers, Destruction_of_the_Destroyer, Nine_Billion_Names, Akashic_Records, Nameless_Land_Nameless_People, The_Invisible_Hand, Scalpel_and_Screwdriver, The_Wages_of_Humanity, Legend_of_the_Puppet_Master, The_Land_of_Gold, Spring_of_Life, Fragments_of_Illusory_Dreams, The_Blossom_in_the_Storm, Abandoned_and_Insulted Assignment_Duration = 20 # 4, 8, 12, 20 Assignment_Event = True Assignment_Assignment = {} diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index a01b95725..2c6f0adc4 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -809,94 +809,94 @@ "Name_1": { "name": "Assignment 1 Preference", "help": "", - "Nine_Billion_Names": "Extinguished Core (Nine Billion Names)", - "Destruction_of_the_Destroyer": "Thief's Instinct (Destruction of the Destroyer)", - "Winter_Soldiers": "Silvermane Badge (Winter Soldiers)", - "Born_to_Obey": "Ancient Part (Born to Obey)", - "Root_Out_the_Turpitude": "Immortal Scionette (Root Out the Turpitude)", - "Fire_Lord_Inflames_Blades_of_War": "Artifex's Module (Fire Lord Inflames Blades of War)", - "A_Startling_Night_Terror": "Dream Collection Component (A Startling Night Terror)", "Tranquility_of_Vimala_bhumi": "Tatters of Thought (Tranquility of Vimala-bhumi)", - "Nameless_Land_Nameless_People": "Character EXP Material (Nameless Land, Nameless People)", + "A_Startling_Night_Terror": "Dream Collection Component (A Startling Night Terror)", + "Fire_Lord_Inflames_Blades_of_War": "Artifex's Module (Fire Lord Inflames Blades of War)", + "Root_Out_the_Turpitude": "Immortal Scionette (Root Out the Turpitude)", + "Born_to_Obey": "Ancient Part (Born to Obey)", + "Winter_Soldiers": "Silvermane Badge (Winter Soldiers)", + "Destruction_of_the_Destroyer": "Thief's Instinct (Destruction of the Destroyer)", + "Nine_Billion_Names": "Extinguished Core (Nine Billion Names)", "Akashic_Records": "Light Cone EXP Material (Akashic Records)", + "Nameless_Land_Nameless_People": "Character EXP Material (Nameless Land, Nameless People)", "The_Invisible_Hand": "Credit (The Invisible Hand)", - "Abandoned_and_Insulted": "Phlogiston & Metal (Abandoned and Insulted)", - "Spring_of_Life": "Solid Water & Virtual Particle (Spring of Life)", - "The_Land_of_Gold": "Basic Ingredients & Protein Rice (The Land of Gold)", - "The_Blossom_in_the_Storm": "Gaseous Liquid & Seed (The Blossom in the Storm)", - "Legend_of_the_Puppet_Master": "Discarded Ingenium Parts & Jade Abacus Unit (Legend of the Puppet Master)", + "Scalpel_and_Screwdriver": "Rusty Gear & Old Molar (Scalpel and Screwdriver)", "The_Wages_of_Humanity": "Human-Height Auspicious Crops & Extract of Medicinal Herbs (The Wages of Humanity)", + "Legend_of_the_Puppet_Master": "Discarded Ingenium Parts & Jade Abacus Unit (Legend of the Puppet Master)", + "The_Land_of_Gold": "Basic Ingredients & Protein Rice (The Land of Gold)", + "Spring_of_Life": "Solid Water & Virtual Particle (Spring of Life)", "Fragments_of_Illusory_Dreams": "Tranquility & Broken Dreams (Fragments of Illusory Dreams)", - "Scalpel_and_Screwdriver": "Rusty Gear & Old Molar (Scalpel and Screwdriver)" + "The_Blossom_in_the_Storm": "Gaseous Liquid & Seed (The Blossom in the Storm)", + "Abandoned_and_Insulted": "Phlogiston & Metal (Abandoned and Insulted)" }, "Name_2": { "name": "Assignment 2 Preference", "help": "", - "Nine_Billion_Names": "Extinguished Core (Nine Billion Names)", - "Destruction_of_the_Destroyer": "Thief's Instinct (Destruction of the Destroyer)", - "Winter_Soldiers": "Silvermane Badge (Winter Soldiers)", - "Born_to_Obey": "Ancient Part (Born to Obey)", - "Root_Out_the_Turpitude": "Immortal Scionette (Root Out the Turpitude)", - "Fire_Lord_Inflames_Blades_of_War": "Artifex's Module (Fire Lord Inflames Blades of War)", - "A_Startling_Night_Terror": "Dream Collection Component (A Startling Night Terror)", "Tranquility_of_Vimala_bhumi": "Tatters of Thought (Tranquility of Vimala-bhumi)", - "Nameless_Land_Nameless_People": "Character EXP Material (Nameless Land, Nameless People)", + "A_Startling_Night_Terror": "Dream Collection Component (A Startling Night Terror)", + "Fire_Lord_Inflames_Blades_of_War": "Artifex's Module (Fire Lord Inflames Blades of War)", + "Root_Out_the_Turpitude": "Immortal Scionette (Root Out the Turpitude)", + "Born_to_Obey": "Ancient Part (Born to Obey)", + "Winter_Soldiers": "Silvermane Badge (Winter Soldiers)", + "Destruction_of_the_Destroyer": "Thief's Instinct (Destruction of the Destroyer)", + "Nine_Billion_Names": "Extinguished Core (Nine Billion Names)", "Akashic_Records": "Light Cone EXP Material (Akashic Records)", + "Nameless_Land_Nameless_People": "Character EXP Material (Nameless Land, Nameless People)", "The_Invisible_Hand": "Credit (The Invisible Hand)", - "Abandoned_and_Insulted": "Phlogiston & Metal (Abandoned and Insulted)", - "Spring_of_Life": "Solid Water & Virtual Particle (Spring of Life)", - "The_Land_of_Gold": "Basic Ingredients & Protein Rice (The Land of Gold)", - "The_Blossom_in_the_Storm": "Gaseous Liquid & Seed (The Blossom in the Storm)", - "Legend_of_the_Puppet_Master": "Discarded Ingenium Parts & Jade Abacus Unit (Legend of the Puppet Master)", + "Scalpel_and_Screwdriver": "Rusty Gear & Old Molar (Scalpel and Screwdriver)", "The_Wages_of_Humanity": "Human-Height Auspicious Crops & Extract of Medicinal Herbs (The Wages of Humanity)", + "Legend_of_the_Puppet_Master": "Discarded Ingenium Parts & Jade Abacus Unit (Legend of the Puppet Master)", + "The_Land_of_Gold": "Basic Ingredients & Protein Rice (The Land of Gold)", + "Spring_of_Life": "Solid Water & Virtual Particle (Spring of Life)", "Fragments_of_Illusory_Dreams": "Tranquility & Broken Dreams (Fragments of Illusory Dreams)", - "Scalpel_and_Screwdriver": "Rusty Gear & Old Molar (Scalpel and Screwdriver)" + "The_Blossom_in_the_Storm": "Gaseous Liquid & Seed (The Blossom in the Storm)", + "Abandoned_and_Insulted": "Phlogiston & Metal (Abandoned and Insulted)" }, "Name_3": { "name": "Assignment 3 Preference", "help": "", - "Nine_Billion_Names": "Extinguished Core (Nine Billion Names)", - "Destruction_of_the_Destroyer": "Thief's Instinct (Destruction of the Destroyer)", - "Winter_Soldiers": "Silvermane Badge (Winter Soldiers)", - "Born_to_Obey": "Ancient Part (Born to Obey)", - "Root_Out_the_Turpitude": "Immortal Scionette (Root Out the Turpitude)", - "Fire_Lord_Inflames_Blades_of_War": "Artifex's Module (Fire Lord Inflames Blades of War)", - "A_Startling_Night_Terror": "Dream Collection Component (A Startling Night Terror)", "Tranquility_of_Vimala_bhumi": "Tatters of Thought (Tranquility of Vimala-bhumi)", - "Nameless_Land_Nameless_People": "Character EXP Material (Nameless Land, Nameless People)", + "A_Startling_Night_Terror": "Dream Collection Component (A Startling Night Terror)", + "Fire_Lord_Inflames_Blades_of_War": "Artifex's Module (Fire Lord Inflames Blades of War)", + "Root_Out_the_Turpitude": "Immortal Scionette (Root Out the Turpitude)", + "Born_to_Obey": "Ancient Part (Born to Obey)", + "Winter_Soldiers": "Silvermane Badge (Winter Soldiers)", + "Destruction_of_the_Destroyer": "Thief's Instinct (Destruction of the Destroyer)", + "Nine_Billion_Names": "Extinguished Core (Nine Billion Names)", "Akashic_Records": "Light Cone EXP Material (Akashic Records)", + "Nameless_Land_Nameless_People": "Character EXP Material (Nameless Land, Nameless People)", "The_Invisible_Hand": "Credit (The Invisible Hand)", - "Abandoned_and_Insulted": "Phlogiston & Metal (Abandoned and Insulted)", - "Spring_of_Life": "Solid Water & Virtual Particle (Spring of Life)", - "The_Land_of_Gold": "Basic Ingredients & Protein Rice (The Land of Gold)", - "The_Blossom_in_the_Storm": "Gaseous Liquid & Seed (The Blossom in the Storm)", - "Legend_of_the_Puppet_Master": "Discarded Ingenium Parts & Jade Abacus Unit (Legend of the Puppet Master)", + "Scalpel_and_Screwdriver": "Rusty Gear & Old Molar (Scalpel and Screwdriver)", "The_Wages_of_Humanity": "Human-Height Auspicious Crops & Extract of Medicinal Herbs (The Wages of Humanity)", + "Legend_of_the_Puppet_Master": "Discarded Ingenium Parts & Jade Abacus Unit (Legend of the Puppet Master)", + "The_Land_of_Gold": "Basic Ingredients & Protein Rice (The Land of Gold)", + "Spring_of_Life": "Solid Water & Virtual Particle (Spring of Life)", "Fragments_of_Illusory_Dreams": "Tranquility & Broken Dreams (Fragments of Illusory Dreams)", - "Scalpel_and_Screwdriver": "Rusty Gear & Old Molar (Scalpel and Screwdriver)" + "The_Blossom_in_the_Storm": "Gaseous Liquid & Seed (The Blossom in the Storm)", + "Abandoned_and_Insulted": "Phlogiston & Metal (Abandoned and Insulted)" }, "Name_4": { "name": "Assignment 4 Preference", "help": "", - "Nine_Billion_Names": "Extinguished Core (Nine Billion Names)", - "Destruction_of_the_Destroyer": "Thief's Instinct (Destruction of the Destroyer)", - "Winter_Soldiers": "Silvermane Badge (Winter Soldiers)", - "Born_to_Obey": "Ancient Part (Born to Obey)", - "Root_Out_the_Turpitude": "Immortal Scionette (Root Out the Turpitude)", - "Fire_Lord_Inflames_Blades_of_War": "Artifex's Module (Fire Lord Inflames Blades of War)", - "A_Startling_Night_Terror": "Dream Collection Component (A Startling Night Terror)", "Tranquility_of_Vimala_bhumi": "Tatters of Thought (Tranquility of Vimala-bhumi)", - "Nameless_Land_Nameless_People": "Character EXP Material (Nameless Land, Nameless People)", + "A_Startling_Night_Terror": "Dream Collection Component (A Startling Night Terror)", + "Fire_Lord_Inflames_Blades_of_War": "Artifex's Module (Fire Lord Inflames Blades of War)", + "Root_Out_the_Turpitude": "Immortal Scionette (Root Out the Turpitude)", + "Born_to_Obey": "Ancient Part (Born to Obey)", + "Winter_Soldiers": "Silvermane Badge (Winter Soldiers)", + "Destruction_of_the_Destroyer": "Thief's Instinct (Destruction of the Destroyer)", + "Nine_Billion_Names": "Extinguished Core (Nine Billion Names)", "Akashic_Records": "Light Cone EXP Material (Akashic Records)", + "Nameless_Land_Nameless_People": "Character EXP Material (Nameless Land, Nameless People)", "The_Invisible_Hand": "Credit (The Invisible Hand)", - "Abandoned_and_Insulted": "Phlogiston & Metal (Abandoned and Insulted)", - "Spring_of_Life": "Solid Water & Virtual Particle (Spring of Life)", - "The_Land_of_Gold": "Basic Ingredients & Protein Rice (The Land of Gold)", - "The_Blossom_in_the_Storm": "Gaseous Liquid & Seed (The Blossom in the Storm)", - "Legend_of_the_Puppet_Master": "Discarded Ingenium Parts & Jade Abacus Unit (Legend of the Puppet Master)", + "Scalpel_and_Screwdriver": "Rusty Gear & Old Molar (Scalpel and Screwdriver)", "The_Wages_of_Humanity": "Human-Height Auspicious Crops & Extract of Medicinal Herbs (The Wages of Humanity)", + "Legend_of_the_Puppet_Master": "Discarded Ingenium Parts & Jade Abacus Unit (Legend of the Puppet Master)", + "The_Land_of_Gold": "Basic Ingredients & Protein Rice (The Land of Gold)", + "Spring_of_Life": "Solid Water & Virtual Particle (Spring of Life)", "Fragments_of_Illusory_Dreams": "Tranquility & Broken Dreams (Fragments of Illusory Dreams)", - "Scalpel_and_Screwdriver": "Rusty Gear & Old Molar (Scalpel and Screwdriver)" + "The_Blossom_in_the_Storm": "Gaseous Liquid & Seed (The Blossom in the Storm)", + "Abandoned_and_Insulted": "Phlogiston & Metal (Abandoned and Insulted)" }, "Duration": { "name": "Dispatch Duration", diff --git a/module/config/i18n/es-ES.json b/module/config/i18n/es-ES.json index 66c665879..058d97844 100644 --- a/module/config/i18n/es-ES.json +++ b/module/config/i18n/es-ES.json @@ -809,94 +809,94 @@ "Name_1": { "name": "Preferencia de Encargo 1", "help": "", - "Nine_Billion_Names": "Núcleo apagado (Nueve mil millones de nombres)", - "Destruction_of_the_Destroyer": "Instinto del ladrón (La destrucción del destructor)", - "Winter_Soldiers": "Pin del guardia (Los guerreros del invierno)", - "Born_to_Obey": "Componente antiguo (Creados para obedecer)", - "Root_Out_the_Turpitude": "Brote verde inmortal (La raíz del mal)", - "Fire_Lord_Inflames_Blades_of_War": "Componente artificial mecánico (Prendan los fuelles, fundan las armas)", - "A_Startling_Night_Terror": "Componente del acumulador de sueños (Pesadilla aterradora)", "Tranquility_of_Vimala_bhumi": "Jirones de pensamientos (Limpieza y purificación)", - "Nameless_Land_Nameless_People": "Material de EXP de personaje (Lugar anónimo, personas anónimas)", + "A_Startling_Night_Terror": "Componente del acumulador de sueños (Pesadilla aterradora)", + "Fire_Lord_Inflames_Blades_of_War": "Componente artificial mecánico (Prendan los fuelles, fundan las armas)", + "Root_Out_the_Turpitude": "Brote verde inmortal (La raíz del mal)", + "Born_to_Obey": "Componente antiguo (Creados para obedecer)", + "Winter_Soldiers": "Pin del guardia (Los guerreros del invierno)", + "Destruction_of_the_Destroyer": "Instinto del ladrón (La destrucción del destructor)", + "Nine_Billion_Names": "Núcleo apagado (Nueve mil millones de nombres)", "Akashic_Records": "Material de EXP de conos de luz (Los Registros de Akasha)", + "Nameless_Land_Nameless_People": "Material de EXP de personaje (Lugar anónimo, personas anónimas)", "The_Invisible_Hand": "Crédito (La mano invisible)", - "Abandoned_and_Insulted": "Flogisto & Metal (Abandonado e insultado)", - "Spring_of_Life": "Agua sólida & Partícula virtual (La fuente de la vida)", - "The_Land_of_Gold": "Ingredientes básicos & Arroz proteico (Tierra de oportunidades)", - "The_Blossom_in_the_Storm": "Líquido gaseoso & Semilla (Flores en la tormenta)", - "Legend_of_the_Puppet_Master": "Componentes mecánicos abandonados & Unidad de ábaco de jade (La leyenda del titiritero)", + "Scalpel_and_Screwdriver": "Engranaje oxidado & Muela vieja (Bisturí y destornillador)", "The_Wages_of_Humanity": "Cosecha tan alta como una persona & Extracto de hierbas medicinales (La paga de la humanidad)", + "Legend_of_the_Puppet_Master": "Componentes mecánicos abandonados & Unidad de ábaco de jade (La leyenda del titiritero)", + "The_Land_of_Gold": "Ingredientes básicos & Arroz proteico (Tierra de oportunidades)", + "Spring_of_Life": "Agua sólida & Partícula virtual (La fuente de la vida)", "Fragments_of_Illusory_Dreams": "Tranquilidad & Sueños rotos (Fragmentos de sueños ilusorios)", - "Scalpel_and_Screwdriver": "Engranaje oxidado & Muela vieja (Bisturí y destornillador)" + "The_Blossom_in_the_Storm": "Líquido gaseoso & Semilla (Flores en la tormenta)", + "Abandoned_and_Insulted": "Flogisto & Metal (Abandonado e insultado)" }, "Name_2": { "name": "Preferencia de Encargo 2", "help": "", - "Nine_Billion_Names": "Núcleo apagado (Nueve mil millones de nombres)", - "Destruction_of_the_Destroyer": "Instinto del ladrón (La destrucción del destructor)", - "Winter_Soldiers": "Pin del guardia (Los guerreros del invierno)", - "Born_to_Obey": "Componente antiguo (Creados para obedecer)", - "Root_Out_the_Turpitude": "Brote verde inmortal (La raíz del mal)", - "Fire_Lord_Inflames_Blades_of_War": "Componente artificial mecánico (Prendan los fuelles, fundan las armas)", - "A_Startling_Night_Terror": "Componente del acumulador de sueños (Pesadilla aterradora)", "Tranquility_of_Vimala_bhumi": "Jirones de pensamientos (Limpieza y purificación)", - "Nameless_Land_Nameless_People": "Material de EXP de personaje (Lugar anónimo, personas anónimas)", + "A_Startling_Night_Terror": "Componente del acumulador de sueños (Pesadilla aterradora)", + "Fire_Lord_Inflames_Blades_of_War": "Componente artificial mecánico (Prendan los fuelles, fundan las armas)", + "Root_Out_the_Turpitude": "Brote verde inmortal (La raíz del mal)", + "Born_to_Obey": "Componente antiguo (Creados para obedecer)", + "Winter_Soldiers": "Pin del guardia (Los guerreros del invierno)", + "Destruction_of_the_Destroyer": "Instinto del ladrón (La destrucción del destructor)", + "Nine_Billion_Names": "Núcleo apagado (Nueve mil millones de nombres)", "Akashic_Records": "Material de EXP de conos de luz (Los Registros de Akasha)", + "Nameless_Land_Nameless_People": "Material de EXP de personaje (Lugar anónimo, personas anónimas)", "The_Invisible_Hand": "Crédito (La mano invisible)", - "Abandoned_and_Insulted": "Flogisto & Metal (Abandonado e insultado)", - "Spring_of_Life": "Agua sólida & Partícula virtual (La fuente de la vida)", - "The_Land_of_Gold": "Ingredientes básicos & Arroz proteico (Tierra de oportunidades)", - "The_Blossom_in_the_Storm": "Líquido gaseoso & Semilla (Flores en la tormenta)", - "Legend_of_the_Puppet_Master": "Componentes mecánicos abandonados & Unidad de ábaco de jade (La leyenda del titiritero)", + "Scalpel_and_Screwdriver": "Engranaje oxidado & Muela vieja (Bisturí y destornillador)", "The_Wages_of_Humanity": "Cosecha tan alta como una persona & Extracto de hierbas medicinales (La paga de la humanidad)", + "Legend_of_the_Puppet_Master": "Componentes mecánicos abandonados & Unidad de ábaco de jade (La leyenda del titiritero)", + "The_Land_of_Gold": "Ingredientes básicos & Arroz proteico (Tierra de oportunidades)", + "Spring_of_Life": "Agua sólida & Partícula virtual (La fuente de la vida)", "Fragments_of_Illusory_Dreams": "Tranquilidad & Sueños rotos (Fragmentos de sueños ilusorios)", - "Scalpel_and_Screwdriver": "Engranaje oxidado & Muela vieja (Bisturí y destornillador)" + "The_Blossom_in_the_Storm": "Líquido gaseoso & Semilla (Flores en la tormenta)", + "Abandoned_and_Insulted": "Flogisto & Metal (Abandonado e insultado)" }, "Name_3": { "name": "Preferencia de Encargo 3", "help": "", - "Nine_Billion_Names": "Núcleo apagado (Nueve mil millones de nombres)", - "Destruction_of_the_Destroyer": "Instinto del ladrón (La destrucción del destructor)", - "Winter_Soldiers": "Pin del guardia (Los guerreros del invierno)", - "Born_to_Obey": "Componente antiguo (Creados para obedecer)", - "Root_Out_the_Turpitude": "Brote verde inmortal (La raíz del mal)", - "Fire_Lord_Inflames_Blades_of_War": "Componente artificial mecánico (Prendan los fuelles, fundan las armas)", - "A_Startling_Night_Terror": "Componente del acumulador de sueños (Pesadilla aterradora)", "Tranquility_of_Vimala_bhumi": "Jirones de pensamientos (Limpieza y purificación)", - "Nameless_Land_Nameless_People": "Material de EXP de personaje (Lugar anónimo, personas anónimas)", + "A_Startling_Night_Terror": "Componente del acumulador de sueños (Pesadilla aterradora)", + "Fire_Lord_Inflames_Blades_of_War": "Componente artificial mecánico (Prendan los fuelles, fundan las armas)", + "Root_Out_the_Turpitude": "Brote verde inmortal (La raíz del mal)", + "Born_to_Obey": "Componente antiguo (Creados para obedecer)", + "Winter_Soldiers": "Pin del guardia (Los guerreros del invierno)", + "Destruction_of_the_Destroyer": "Instinto del ladrón (La destrucción del destructor)", + "Nine_Billion_Names": "Núcleo apagado (Nueve mil millones de nombres)", "Akashic_Records": "Material de EXP de conos de luz (Los Registros de Akasha)", + "Nameless_Land_Nameless_People": "Material de EXP de personaje (Lugar anónimo, personas anónimas)", "The_Invisible_Hand": "Crédito (La mano invisible)", - "Abandoned_and_Insulted": "Flogisto & Metal (Abandonado e insultado)", - "Spring_of_Life": "Agua sólida & Partícula virtual (La fuente de la vida)", - "The_Land_of_Gold": "Ingredientes básicos & Arroz proteico (Tierra de oportunidades)", - "The_Blossom_in_the_Storm": "Líquido gaseoso & Semilla (Flores en la tormenta)", - "Legend_of_the_Puppet_Master": "Componentes mecánicos abandonados & Unidad de ábaco de jade (La leyenda del titiritero)", + "Scalpel_and_Screwdriver": "Engranaje oxidado & Muela vieja (Bisturí y destornillador)", "The_Wages_of_Humanity": "Cosecha tan alta como una persona & Extracto de hierbas medicinales (La paga de la humanidad)", + "Legend_of_the_Puppet_Master": "Componentes mecánicos abandonados & Unidad de ábaco de jade (La leyenda del titiritero)", + "The_Land_of_Gold": "Ingredientes básicos & Arroz proteico (Tierra de oportunidades)", + "Spring_of_Life": "Agua sólida & Partícula virtual (La fuente de la vida)", "Fragments_of_Illusory_Dreams": "Tranquilidad & Sueños rotos (Fragmentos de sueños ilusorios)", - "Scalpel_and_Screwdriver": "Engranaje oxidado & Muela vieja (Bisturí y destornillador)" + "The_Blossom_in_the_Storm": "Líquido gaseoso & Semilla (Flores en la tormenta)", + "Abandoned_and_Insulted": "Flogisto & Metal (Abandonado e insultado)" }, "Name_4": { "name": "Preferencia de Encargo 4", "help": "", - "Nine_Billion_Names": "Núcleo apagado (Nueve mil millones de nombres)", - "Destruction_of_the_Destroyer": "Instinto del ladrón (La destrucción del destructor)", - "Winter_Soldiers": "Pin del guardia (Los guerreros del invierno)", - "Born_to_Obey": "Componente antiguo (Creados para obedecer)", - "Root_Out_the_Turpitude": "Brote verde inmortal (La raíz del mal)", - "Fire_Lord_Inflames_Blades_of_War": "Componente artificial mecánico (Prendan los fuelles, fundan las armas)", - "A_Startling_Night_Terror": "Componente del acumulador de sueños (Pesadilla aterradora)", "Tranquility_of_Vimala_bhumi": "Jirones de pensamientos (Limpieza y purificación)", - "Nameless_Land_Nameless_People": "Material de EXP de personaje (Lugar anónimo, personas anónimas)", + "A_Startling_Night_Terror": "Componente del acumulador de sueños (Pesadilla aterradora)", + "Fire_Lord_Inflames_Blades_of_War": "Componente artificial mecánico (Prendan los fuelles, fundan las armas)", + "Root_Out_the_Turpitude": "Brote verde inmortal (La raíz del mal)", + "Born_to_Obey": "Componente antiguo (Creados para obedecer)", + "Winter_Soldiers": "Pin del guardia (Los guerreros del invierno)", + "Destruction_of_the_Destroyer": "Instinto del ladrón (La destrucción del destructor)", + "Nine_Billion_Names": "Núcleo apagado (Nueve mil millones de nombres)", "Akashic_Records": "Material de EXP de conos de luz (Los Registros de Akasha)", + "Nameless_Land_Nameless_People": "Material de EXP de personaje (Lugar anónimo, personas anónimas)", "The_Invisible_Hand": "Crédito (La mano invisible)", - "Abandoned_and_Insulted": "Flogisto & Metal (Abandonado e insultado)", - "Spring_of_Life": "Agua sólida & Partícula virtual (La fuente de la vida)", - "The_Land_of_Gold": "Ingredientes básicos & Arroz proteico (Tierra de oportunidades)", - "The_Blossom_in_the_Storm": "Líquido gaseoso & Semilla (Flores en la tormenta)", - "Legend_of_the_Puppet_Master": "Componentes mecánicos abandonados & Unidad de ábaco de jade (La leyenda del titiritero)", + "Scalpel_and_Screwdriver": "Engranaje oxidado & Muela vieja (Bisturí y destornillador)", "The_Wages_of_Humanity": "Cosecha tan alta como una persona & Extracto de hierbas medicinales (La paga de la humanidad)", + "Legend_of_the_Puppet_Master": "Componentes mecánicos abandonados & Unidad de ábaco de jade (La leyenda del titiritero)", + "The_Land_of_Gold": "Ingredientes básicos & Arroz proteico (Tierra de oportunidades)", + "Spring_of_Life": "Agua sólida & Partícula virtual (La fuente de la vida)", "Fragments_of_Illusory_Dreams": "Tranquilidad & Sueños rotos (Fragmentos de sueños ilusorios)", - "Scalpel_and_Screwdriver": "Engranaje oxidado & Muela vieja (Bisturí y destornillador)" + "The_Blossom_in_the_Storm": "Líquido gaseoso & Semilla (Flores en la tormenta)", + "Abandoned_and_Insulted": "Flogisto & Metal (Abandonado e insultado)" }, "Duration": { "name": "Duración del encargo", diff --git a/module/config/i18n/ja-JP.json b/module/config/i18n/ja-JP.json index 82f69b042..436e36592 100644 --- a/module/config/i18n/ja-JP.json +++ b/module/config/i18n/ja-JP.json @@ -809,94 +809,94 @@ "Name_1": { "name": "依頼 1", "help": "", - "Nine_Billion_Names": "消滅した原核(九十億の御名)", - "Destruction_of_the_Destroyer": "略奪の本能(壊滅者の覆没)", - "Winter_Soldiers": "シルバーメインの釦(寒冬の戦士たち)", - "Born_to_Obey": "古代パーツ(生まれながらに服従する)", - "Root_Out_the_Turpitude": "永寿の萌芽(悪孽を根絶やしに)", - "Fire_Lord_Inflames_Blades_of_War": "工造機関(剣戟を焼却する火帝炉)", - "A_Startling_Night_Terror": "ドリームコレクションパーツ(魂震える悪夢)", "Tranquility_of_Vimala_bhumi": "思考の粉末(離垢清浄)", - "Nameless_Land_Nameless_People": "キャラクター経験値素材(無名の地、無名の人)", + "A_Startling_Night_Terror": "ドリームコレクションパーツ(魂震える悪夢)", + "Fire_Lord_Inflames_Blades_of_War": "工造機関(剣戟を焼却する火帝炉)", + "Root_Out_the_Turpitude": "永寿の萌芽(悪孽を根絶やしに)", + "Born_to_Obey": "古代パーツ(生まれながらに服従する)", + "Winter_Soldiers": "シルバーメインの釦(寒冬の戦士たち)", + "Destruction_of_the_Destroyer": "略奪の本能(壊滅者の覆没)", + "Nine_Billion_Names": "消滅した原核(九十億の御名)", "Akashic_Records": "光円錐経験値素材(アーカーシャの記録)", + "Nameless_Land_Nameless_People": "キャラクター経験値素材(無名の地、無名の人)", "The_Invisible_Hand": "信用ポイント(見えざる手)", - "Abandoned_and_Insulted": "燃素 & 金属(捨てられしものと傷つけられしもの)", - "Spring_of_Life": "固形純水 & 仮想粒子(生命の泉)", - "The_Land_of_Gold": "基本食材 & タンパク米(黄金の大地)", - "The_Blossom_in_the_Storm": "気態流体 & 種子(嵐の中で咲き誇る花)", - "Legend_of_the_Puppet_Master": "廃棄された機巧部品 & 玉兆単元(傀儡師伝説)", + "Scalpel_and_Screwdriver": "錆びた歯車 & 古びた大臼歯(メスとスクリュードライバー)", "The_Wages_of_Humanity": "一人稲 & 薬草抽出物(人類扶養)", + "Legend_of_the_Puppet_Master": "廃棄された機巧部品 & 玉兆単元(傀儡師伝説)", + "The_Land_of_Gold": "基本食材 & タンパク米(黄金の大地)", + "Spring_of_Life": "固形純水 & 仮想粒子(生命の泉)", "Fragments_of_Illusory_Dreams": "安逸 & 砕けた夢(幻夢の残片)", - "Scalpel_and_Screwdriver": "錆びた歯車 & 古びた大臼歯(メスとスクリュードライバー)" + "The_Blossom_in_the_Storm": "気態流体 & 種子(嵐の中で咲き誇る花)", + "Abandoned_and_Insulted": "燃素 & 金属(捨てられしものと傷つけられしもの)" }, "Name_2": { "name": "依頼 2", "help": "", - "Nine_Billion_Names": "消滅した原核(九十億の御名)", - "Destruction_of_the_Destroyer": "略奪の本能(壊滅者の覆没)", - "Winter_Soldiers": "シルバーメインの釦(寒冬の戦士たち)", - "Born_to_Obey": "古代パーツ(生まれながらに服従する)", - "Root_Out_the_Turpitude": "永寿の萌芽(悪孽を根絶やしに)", - "Fire_Lord_Inflames_Blades_of_War": "工造機関(剣戟を焼却する火帝炉)", - "A_Startling_Night_Terror": "ドリームコレクションパーツ(魂震える悪夢)", "Tranquility_of_Vimala_bhumi": "思考の粉末(離垢清浄)", - "Nameless_Land_Nameless_People": "キャラクター経験値素材(無名の地、無名の人)", + "A_Startling_Night_Terror": "ドリームコレクションパーツ(魂震える悪夢)", + "Fire_Lord_Inflames_Blades_of_War": "工造機関(剣戟を焼却する火帝炉)", + "Root_Out_the_Turpitude": "永寿の萌芽(悪孽を根絶やしに)", + "Born_to_Obey": "古代パーツ(生まれながらに服従する)", + "Winter_Soldiers": "シルバーメインの釦(寒冬の戦士たち)", + "Destruction_of_the_Destroyer": "略奪の本能(壊滅者の覆没)", + "Nine_Billion_Names": "消滅した原核(九十億の御名)", "Akashic_Records": "光円錐経験値素材(アーカーシャの記録)", + "Nameless_Land_Nameless_People": "キャラクター経験値素材(無名の地、無名の人)", "The_Invisible_Hand": "信用ポイント(見えざる手)", - "Abandoned_and_Insulted": "燃素 & 金属(捨てられしものと傷つけられしもの)", - "Spring_of_Life": "固形純水 & 仮想粒子(生命の泉)", - "The_Land_of_Gold": "基本食材 & タンパク米(黄金の大地)", - "The_Blossom_in_the_Storm": "気態流体 & 種子(嵐の中で咲き誇る花)", - "Legend_of_the_Puppet_Master": "廃棄された機巧部品 & 玉兆単元(傀儡師伝説)", + "Scalpel_and_Screwdriver": "錆びた歯車 & 古びた大臼歯(メスとスクリュードライバー)", "The_Wages_of_Humanity": "一人稲 & 薬草抽出物(人類扶養)", + "Legend_of_the_Puppet_Master": "廃棄された機巧部品 & 玉兆単元(傀儡師伝説)", + "The_Land_of_Gold": "基本食材 & タンパク米(黄金の大地)", + "Spring_of_Life": "固形純水 & 仮想粒子(生命の泉)", "Fragments_of_Illusory_Dreams": "安逸 & 砕けた夢(幻夢の残片)", - "Scalpel_and_Screwdriver": "錆びた歯車 & 古びた大臼歯(メスとスクリュードライバー)" + "The_Blossom_in_the_Storm": "気態流体 & 種子(嵐の中で咲き誇る花)", + "Abandoned_and_Insulted": "燃素 & 金属(捨てられしものと傷つけられしもの)" }, "Name_3": { "name": "依頼 3", "help": "", - "Nine_Billion_Names": "消滅した原核(九十億の御名)", - "Destruction_of_the_Destroyer": "略奪の本能(壊滅者の覆没)", - "Winter_Soldiers": "シルバーメインの釦(寒冬の戦士たち)", - "Born_to_Obey": "古代パーツ(生まれながらに服従する)", - "Root_Out_the_Turpitude": "永寿の萌芽(悪孽を根絶やしに)", - "Fire_Lord_Inflames_Blades_of_War": "工造機関(剣戟を焼却する火帝炉)", - "A_Startling_Night_Terror": "ドリームコレクションパーツ(魂震える悪夢)", "Tranquility_of_Vimala_bhumi": "思考の粉末(離垢清浄)", - "Nameless_Land_Nameless_People": "キャラクター経験値素材(無名の地、無名の人)", + "A_Startling_Night_Terror": "ドリームコレクションパーツ(魂震える悪夢)", + "Fire_Lord_Inflames_Blades_of_War": "工造機関(剣戟を焼却する火帝炉)", + "Root_Out_the_Turpitude": "永寿の萌芽(悪孽を根絶やしに)", + "Born_to_Obey": "古代パーツ(生まれながらに服従する)", + "Winter_Soldiers": "シルバーメインの釦(寒冬の戦士たち)", + "Destruction_of_the_Destroyer": "略奪の本能(壊滅者の覆没)", + "Nine_Billion_Names": "消滅した原核(九十億の御名)", "Akashic_Records": "光円錐経験値素材(アーカーシャの記録)", + "Nameless_Land_Nameless_People": "キャラクター経験値素材(無名の地、無名の人)", "The_Invisible_Hand": "信用ポイント(見えざる手)", - "Abandoned_and_Insulted": "燃素 & 金属(捨てられしものと傷つけられしもの)", - "Spring_of_Life": "固形純水 & 仮想粒子(生命の泉)", - "The_Land_of_Gold": "基本食材 & タンパク米(黄金の大地)", - "The_Blossom_in_the_Storm": "気態流体 & 種子(嵐の中で咲き誇る花)", - "Legend_of_the_Puppet_Master": "廃棄された機巧部品 & 玉兆単元(傀儡師伝説)", + "Scalpel_and_Screwdriver": "錆びた歯車 & 古びた大臼歯(メスとスクリュードライバー)", "The_Wages_of_Humanity": "一人稲 & 薬草抽出物(人類扶養)", + "Legend_of_the_Puppet_Master": "廃棄された機巧部品 & 玉兆単元(傀儡師伝説)", + "The_Land_of_Gold": "基本食材 & タンパク米(黄金の大地)", + "Spring_of_Life": "固形純水 & 仮想粒子(生命の泉)", "Fragments_of_Illusory_Dreams": "安逸 & 砕けた夢(幻夢の残片)", - "Scalpel_and_Screwdriver": "錆びた歯車 & 古びた大臼歯(メスとスクリュードライバー)" + "The_Blossom_in_the_Storm": "気態流体 & 種子(嵐の中で咲き誇る花)", + "Abandoned_and_Insulted": "燃素 & 金属(捨てられしものと傷つけられしもの)" }, "Name_4": { "name": "依頼 4", "help": "", - "Nine_Billion_Names": "消滅した原核(九十億の御名)", - "Destruction_of_the_Destroyer": "略奪の本能(壊滅者の覆没)", - "Winter_Soldiers": "シルバーメインの釦(寒冬の戦士たち)", - "Born_to_Obey": "古代パーツ(生まれながらに服従する)", - "Root_Out_the_Turpitude": "永寿の萌芽(悪孽を根絶やしに)", - "Fire_Lord_Inflames_Blades_of_War": "工造機関(剣戟を焼却する火帝炉)", - "A_Startling_Night_Terror": "ドリームコレクションパーツ(魂震える悪夢)", "Tranquility_of_Vimala_bhumi": "思考の粉末(離垢清浄)", - "Nameless_Land_Nameless_People": "キャラクター経験値素材(無名の地、無名の人)", + "A_Startling_Night_Terror": "ドリームコレクションパーツ(魂震える悪夢)", + "Fire_Lord_Inflames_Blades_of_War": "工造機関(剣戟を焼却する火帝炉)", + "Root_Out_the_Turpitude": "永寿の萌芽(悪孽を根絶やしに)", + "Born_to_Obey": "古代パーツ(生まれながらに服従する)", + "Winter_Soldiers": "シルバーメインの釦(寒冬の戦士たち)", + "Destruction_of_the_Destroyer": "略奪の本能(壊滅者の覆没)", + "Nine_Billion_Names": "消滅した原核(九十億の御名)", "Akashic_Records": "光円錐経験値素材(アーカーシャの記録)", + "Nameless_Land_Nameless_People": "キャラクター経験値素材(無名の地、無名の人)", "The_Invisible_Hand": "信用ポイント(見えざる手)", - "Abandoned_and_Insulted": "燃素 & 金属(捨てられしものと傷つけられしもの)", - "Spring_of_Life": "固形純水 & 仮想粒子(生命の泉)", - "The_Land_of_Gold": "基本食材 & タンパク米(黄金の大地)", - "The_Blossom_in_the_Storm": "気態流体 & 種子(嵐の中で咲き誇る花)", - "Legend_of_the_Puppet_Master": "廃棄された機巧部品 & 玉兆単元(傀儡師伝説)", + "Scalpel_and_Screwdriver": "錆びた歯車 & 古びた大臼歯(メスとスクリュードライバー)", "The_Wages_of_Humanity": "一人稲 & 薬草抽出物(人類扶養)", + "Legend_of_the_Puppet_Master": "廃棄された機巧部品 & 玉兆単元(傀儡師伝説)", + "The_Land_of_Gold": "基本食材 & タンパク米(黄金の大地)", + "Spring_of_Life": "固形純水 & 仮想粒子(生命の泉)", "Fragments_of_Illusory_Dreams": "安逸 & 砕けた夢(幻夢の残片)", - "Scalpel_and_Screwdriver": "錆びた歯車 & 古びた大臼歯(メスとスクリュードライバー)" + "The_Blossom_in_the_Storm": "気態流体 & 種子(嵐の中で咲き誇る花)", + "Abandoned_and_Insulted": "燃素 & 金属(捨てられしものと傷つけられしもの)" }, "Duration": { "name": "派遣時間", diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index 37b6e7e84..a0a857a7a 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -809,94 +809,94 @@ "Name_1": { "name": "第1个委托选择", "help": "", - "Nine_Billion_Names": "熄灭原核(九十亿个名字)", - "Destruction_of_the_Destroyer": "掠夺的本能(毁灭者的覆灭)", - "Winter_Soldiers": "铁卫扣饰(寒冬的战士们)", - "Born_to_Obey": "古代零件(生而服从)", - "Root_Out_the_Turpitude": "永寿幼芽(根除恶孽)", - "Fire_Lord_Inflames_Blades_of_War": "工造机杼(火帝动炉销剑戟)", - "A_Startling_Night_Terror": "蓄梦元件(劫梦惊魂)", "Tranquility_of_Vimala_bhumi": "思绪末屑(离垢清净)", - "Nameless_Land_Nameless_People": "角色经验材料(无名之地,无名之人)", + "A_Startling_Night_Terror": "蓄梦元件(劫梦惊魂)", + "Fire_Lord_Inflames_Blades_of_War": "工造机杼(火帝动炉销剑戟)", + "Root_Out_the_Turpitude": "永寿幼芽(根除恶孽)", + "Born_to_Obey": "古代零件(生而服从)", + "Winter_Soldiers": "铁卫扣饰(寒冬的战士们)", + "Destruction_of_the_Destroyer": "掠夺的本能(毁灭者的覆灭)", + "Nine_Billion_Names": "熄灭原核(九十亿个名字)", "Akashic_Records": "光锥经验材料(阿卡夏记录)", + "Nameless_Land_Nameless_People": "角色经验材料(无名之地,无名之人)", "The_Invisible_Hand": "信用点(看不见的手)", - "Abandoned_and_Insulted": "燃素 & 金属(被废弃与损害的)", - "Spring_of_Life": "固态净水 & 虚粒子(生命之泉)", - "The_Land_of_Gold": "基本食材 & 蛋白米(黄金大地)", - "The_Blossom_in_the_Storm": "气态流体 & 种子(风暴中怒放的花)", - "Legend_of_the_Puppet_Master": "废弃机巧零件 & 玉兆单元(偃师传说)", + "Scalpel_and_Screwdriver": "锈迹齿轮 & 老旧臼齿(手术刀与螺丝刀)", "The_Wages_of_Humanity": "一人嘉禾 & 药草提取物(赡养人类)", + "Legend_of_the_Puppet_Master": "废弃机巧零件 & 玉兆单元(偃师传说)", + "The_Land_of_Gold": "基本食材 & 蛋白米(黄金大地)", + "Spring_of_Life": "固态净水 & 虚粒子(生命之泉)", "Fragments_of_Illusory_Dreams": "安逸 & 碎梦(幻梦的残片)", - "Scalpel_and_Screwdriver": "锈迹齿轮 & 老旧臼齿(手术刀与螺丝刀)" + "The_Blossom_in_the_Storm": "气态流体 & 种子(风暴中怒放的花)", + "Abandoned_and_Insulted": "燃素 & 金属(被废弃与损害的)" }, "Name_2": { "name": "第2个委托选择", "help": "", - "Nine_Billion_Names": "熄灭原核(九十亿个名字)", - "Destruction_of_the_Destroyer": "掠夺的本能(毁灭者的覆灭)", - "Winter_Soldiers": "铁卫扣饰(寒冬的战士们)", - "Born_to_Obey": "古代零件(生而服从)", - "Root_Out_the_Turpitude": "永寿幼芽(根除恶孽)", - "Fire_Lord_Inflames_Blades_of_War": "工造机杼(火帝动炉销剑戟)", - "A_Startling_Night_Terror": "蓄梦元件(劫梦惊魂)", "Tranquility_of_Vimala_bhumi": "思绪末屑(离垢清净)", - "Nameless_Land_Nameless_People": "角色经验材料(无名之地,无名之人)", + "A_Startling_Night_Terror": "蓄梦元件(劫梦惊魂)", + "Fire_Lord_Inflames_Blades_of_War": "工造机杼(火帝动炉销剑戟)", + "Root_Out_the_Turpitude": "永寿幼芽(根除恶孽)", + "Born_to_Obey": "古代零件(生而服从)", + "Winter_Soldiers": "铁卫扣饰(寒冬的战士们)", + "Destruction_of_the_Destroyer": "掠夺的本能(毁灭者的覆灭)", + "Nine_Billion_Names": "熄灭原核(九十亿个名字)", "Akashic_Records": "光锥经验材料(阿卡夏记录)", + "Nameless_Land_Nameless_People": "角色经验材料(无名之地,无名之人)", "The_Invisible_Hand": "信用点(看不见的手)", - "Abandoned_and_Insulted": "燃素 & 金属(被废弃与损害的)", - "Spring_of_Life": "固态净水 & 虚粒子(生命之泉)", - "The_Land_of_Gold": "基本食材 & 蛋白米(黄金大地)", - "The_Blossom_in_the_Storm": "气态流体 & 种子(风暴中怒放的花)", - "Legend_of_the_Puppet_Master": "废弃机巧零件 & 玉兆单元(偃师传说)", + "Scalpel_and_Screwdriver": "锈迹齿轮 & 老旧臼齿(手术刀与螺丝刀)", "The_Wages_of_Humanity": "一人嘉禾 & 药草提取物(赡养人类)", + "Legend_of_the_Puppet_Master": "废弃机巧零件 & 玉兆单元(偃师传说)", + "The_Land_of_Gold": "基本食材 & 蛋白米(黄金大地)", + "Spring_of_Life": "固态净水 & 虚粒子(生命之泉)", "Fragments_of_Illusory_Dreams": "安逸 & 碎梦(幻梦的残片)", - "Scalpel_and_Screwdriver": "锈迹齿轮 & 老旧臼齿(手术刀与螺丝刀)" + "The_Blossom_in_the_Storm": "气态流体 & 种子(风暴中怒放的花)", + "Abandoned_and_Insulted": "燃素 & 金属(被废弃与损害的)" }, "Name_3": { "name": "第3个委托选择", "help": "", - "Nine_Billion_Names": "熄灭原核(九十亿个名字)", - "Destruction_of_the_Destroyer": "掠夺的本能(毁灭者的覆灭)", - "Winter_Soldiers": "铁卫扣饰(寒冬的战士们)", - "Born_to_Obey": "古代零件(生而服从)", - "Root_Out_the_Turpitude": "永寿幼芽(根除恶孽)", - "Fire_Lord_Inflames_Blades_of_War": "工造机杼(火帝动炉销剑戟)", - "A_Startling_Night_Terror": "蓄梦元件(劫梦惊魂)", "Tranquility_of_Vimala_bhumi": "思绪末屑(离垢清净)", - "Nameless_Land_Nameless_People": "角色经验材料(无名之地,无名之人)", + "A_Startling_Night_Terror": "蓄梦元件(劫梦惊魂)", + "Fire_Lord_Inflames_Blades_of_War": "工造机杼(火帝动炉销剑戟)", + "Root_Out_the_Turpitude": "永寿幼芽(根除恶孽)", + "Born_to_Obey": "古代零件(生而服从)", + "Winter_Soldiers": "铁卫扣饰(寒冬的战士们)", + "Destruction_of_the_Destroyer": "掠夺的本能(毁灭者的覆灭)", + "Nine_Billion_Names": "熄灭原核(九十亿个名字)", "Akashic_Records": "光锥经验材料(阿卡夏记录)", + "Nameless_Land_Nameless_People": "角色经验材料(无名之地,无名之人)", "The_Invisible_Hand": "信用点(看不见的手)", - "Abandoned_and_Insulted": "燃素 & 金属(被废弃与损害的)", - "Spring_of_Life": "固态净水 & 虚粒子(生命之泉)", - "The_Land_of_Gold": "基本食材 & 蛋白米(黄金大地)", - "The_Blossom_in_the_Storm": "气态流体 & 种子(风暴中怒放的花)", - "Legend_of_the_Puppet_Master": "废弃机巧零件 & 玉兆单元(偃师传说)", + "Scalpel_and_Screwdriver": "锈迹齿轮 & 老旧臼齿(手术刀与螺丝刀)", "The_Wages_of_Humanity": "一人嘉禾 & 药草提取物(赡养人类)", + "Legend_of_the_Puppet_Master": "废弃机巧零件 & 玉兆单元(偃师传说)", + "The_Land_of_Gold": "基本食材 & 蛋白米(黄金大地)", + "Spring_of_Life": "固态净水 & 虚粒子(生命之泉)", "Fragments_of_Illusory_Dreams": "安逸 & 碎梦(幻梦的残片)", - "Scalpel_and_Screwdriver": "锈迹齿轮 & 老旧臼齿(手术刀与螺丝刀)" + "The_Blossom_in_the_Storm": "气态流体 & 种子(风暴中怒放的花)", + "Abandoned_and_Insulted": "燃素 & 金属(被废弃与损害的)" }, "Name_4": { "name": "第4个委托选择", "help": "", - "Nine_Billion_Names": "熄灭原核(九十亿个名字)", - "Destruction_of_the_Destroyer": "掠夺的本能(毁灭者的覆灭)", - "Winter_Soldiers": "铁卫扣饰(寒冬的战士们)", - "Born_to_Obey": "古代零件(生而服从)", - "Root_Out_the_Turpitude": "永寿幼芽(根除恶孽)", - "Fire_Lord_Inflames_Blades_of_War": "工造机杼(火帝动炉销剑戟)", - "A_Startling_Night_Terror": "蓄梦元件(劫梦惊魂)", "Tranquility_of_Vimala_bhumi": "思绪末屑(离垢清净)", - "Nameless_Land_Nameless_People": "角色经验材料(无名之地,无名之人)", + "A_Startling_Night_Terror": "蓄梦元件(劫梦惊魂)", + "Fire_Lord_Inflames_Blades_of_War": "工造机杼(火帝动炉销剑戟)", + "Root_Out_the_Turpitude": "永寿幼芽(根除恶孽)", + "Born_to_Obey": "古代零件(生而服从)", + "Winter_Soldiers": "铁卫扣饰(寒冬的战士们)", + "Destruction_of_the_Destroyer": "掠夺的本能(毁灭者的覆灭)", + "Nine_Billion_Names": "熄灭原核(九十亿个名字)", "Akashic_Records": "光锥经验材料(阿卡夏记录)", + "Nameless_Land_Nameless_People": "角色经验材料(无名之地,无名之人)", "The_Invisible_Hand": "信用点(看不见的手)", - "Abandoned_and_Insulted": "燃素 & 金属(被废弃与损害的)", - "Spring_of_Life": "固态净水 & 虚粒子(生命之泉)", - "The_Land_of_Gold": "基本食材 & 蛋白米(黄金大地)", - "The_Blossom_in_the_Storm": "气态流体 & 种子(风暴中怒放的花)", - "Legend_of_the_Puppet_Master": "废弃机巧零件 & 玉兆单元(偃师传说)", + "Scalpel_and_Screwdriver": "锈迹齿轮 & 老旧臼齿(手术刀与螺丝刀)", "The_Wages_of_Humanity": "一人嘉禾 & 药草提取物(赡养人类)", + "Legend_of_the_Puppet_Master": "废弃机巧零件 & 玉兆单元(偃师传说)", + "The_Land_of_Gold": "基本食材 & 蛋白米(黄金大地)", + "Spring_of_Life": "固态净水 & 虚粒子(生命之泉)", "Fragments_of_Illusory_Dreams": "安逸 & 碎梦(幻梦的残片)", - "Scalpel_and_Screwdriver": "锈迹齿轮 & 老旧臼齿(手术刀与螺丝刀)" + "The_Blossom_in_the_Storm": "气态流体 & 种子(风暴中怒放的花)", + "Abandoned_and_Insulted": "燃素 & 金属(被废弃与损害的)" }, "Duration": { "name": "派遣时长", diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index b7e6c08ca..f3eb18c55 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -809,94 +809,94 @@ "Name_1": { "name": "第1個委託選擇", "help": "", - "Nine_Billion_Names": "熄滅原核(九十億個名字)", - "Destruction_of_the_Destroyer": "掠奪的本能(毀滅者的覆滅)", - "Winter_Soldiers": "鐵衛扣飾(寒冬的戰士們)", - "Born_to_Obey": "古代零件(生而服從)", - "Root_Out_the_Turpitude": "永壽幼芽(根除惡孽)", - "Fire_Lord_Inflames_Blades_of_War": "工造機杼(火帝動爐銷劍戟)", - "A_Startling_Night_Terror": "蓄夢元件(劫夢驚魂)", "Tranquility_of_Vimala_bhumi": "思緒末屑(離垢清淨)", - "Nameless_Land_Nameless_People": "角色經驗素材(無名之地,無名之人)", + "A_Startling_Night_Terror": "蓄夢元件(劫夢驚魂)", + "Fire_Lord_Inflames_Blades_of_War": "工造機杼(火帝動爐銷劍戟)", + "Root_Out_the_Turpitude": "永壽幼芽(根除惡孽)", + "Born_to_Obey": "古代零件(生而服從)", + "Winter_Soldiers": "鐵衛扣飾(寒冬的戰士們)", + "Destruction_of_the_Destroyer": "掠奪的本能(毀滅者的覆滅)", + "Nine_Billion_Names": "熄滅原核(九十億個名字)", "Akashic_Records": "光錐經驗素材(阿卡夏紀錄)", + "Nameless_Land_Nameless_People": "角色經驗素材(無名之地,無名之人)", "The_Invisible_Hand": "信用點(看不見的手)", - "Abandoned_and_Insulted": "燃素 & 金屬(被廢棄與損害的)", - "Spring_of_Life": "固態淨水 & 虛粒子(生命之泉)", - "The_Land_of_Gold": "基本食材 & 蛋白米(黃金大地)", - "The_Blossom_in_the_Storm": "氣態流體 & 種子(風暴中怒放的花)", - "Legend_of_the_Puppet_Master": "廢棄機巧零件 & 玉兆單元(偃師傳說)", + "Scalpel_and_Screwdriver": "鏽跡齒輪 & 老舊臼齒(手術刀與螺絲起子)", "The_Wages_of_Humanity": "一人嘉禾 & 藥草萃取物(贍養人類)", + "Legend_of_the_Puppet_Master": "廢棄機巧零件 & 玉兆單元(偃師傳說)", + "The_Land_of_Gold": "基本食材 & 蛋白米(黃金大地)", + "Spring_of_Life": "固態淨水 & 虛粒子(生命之泉)", "Fragments_of_Illusory_Dreams": "安逸 & 碎夢(幻夢的殘片)", - "Scalpel_and_Screwdriver": "鏽跡齒輪 & 老舊臼齒(手術刀與螺絲起子)" + "The_Blossom_in_the_Storm": "氣態流體 & 種子(風暴中怒放的花)", + "Abandoned_and_Insulted": "燃素 & 金屬(被廢棄與損害的)" }, "Name_2": { "name": "第2個委託選擇", "help": "", - "Nine_Billion_Names": "熄滅原核(九十億個名字)", - "Destruction_of_the_Destroyer": "掠奪的本能(毀滅者的覆滅)", - "Winter_Soldiers": "鐵衛扣飾(寒冬的戰士們)", - "Born_to_Obey": "古代零件(生而服從)", - "Root_Out_the_Turpitude": "永壽幼芽(根除惡孽)", - "Fire_Lord_Inflames_Blades_of_War": "工造機杼(火帝動爐銷劍戟)", - "A_Startling_Night_Terror": "蓄夢元件(劫夢驚魂)", "Tranquility_of_Vimala_bhumi": "思緒末屑(離垢清淨)", - "Nameless_Land_Nameless_People": "角色經驗素材(無名之地,無名之人)", + "A_Startling_Night_Terror": "蓄夢元件(劫夢驚魂)", + "Fire_Lord_Inflames_Blades_of_War": "工造機杼(火帝動爐銷劍戟)", + "Root_Out_the_Turpitude": "永壽幼芽(根除惡孽)", + "Born_to_Obey": "古代零件(生而服從)", + "Winter_Soldiers": "鐵衛扣飾(寒冬的戰士們)", + "Destruction_of_the_Destroyer": "掠奪的本能(毀滅者的覆滅)", + "Nine_Billion_Names": "熄滅原核(九十億個名字)", "Akashic_Records": "光錐經驗素材(阿卡夏紀錄)", + "Nameless_Land_Nameless_People": "角色經驗素材(無名之地,無名之人)", "The_Invisible_Hand": "信用點(看不見的手)", - "Abandoned_and_Insulted": "燃素 & 金屬(被廢棄與損害的)", - "Spring_of_Life": "固態淨水 & 虛粒子(生命之泉)", - "The_Land_of_Gold": "基本食材 & 蛋白米(黃金大地)", - "The_Blossom_in_the_Storm": "氣態流體 & 種子(風暴中怒放的花)", - "Legend_of_the_Puppet_Master": "廢棄機巧零件 & 玉兆單元(偃師傳說)", + "Scalpel_and_Screwdriver": "鏽跡齒輪 & 老舊臼齒(手術刀與螺絲起子)", "The_Wages_of_Humanity": "一人嘉禾 & 藥草萃取物(贍養人類)", + "Legend_of_the_Puppet_Master": "廢棄機巧零件 & 玉兆單元(偃師傳說)", + "The_Land_of_Gold": "基本食材 & 蛋白米(黃金大地)", + "Spring_of_Life": "固態淨水 & 虛粒子(生命之泉)", "Fragments_of_Illusory_Dreams": "安逸 & 碎夢(幻夢的殘片)", - "Scalpel_and_Screwdriver": "鏽跡齒輪 & 老舊臼齒(手術刀與螺絲起子)" + "The_Blossom_in_the_Storm": "氣態流體 & 種子(風暴中怒放的花)", + "Abandoned_and_Insulted": "燃素 & 金屬(被廢棄與損害的)" }, "Name_3": { "name": "第3個委託選擇", "help": "", - "Nine_Billion_Names": "熄滅原核(九十億個名字)", - "Destruction_of_the_Destroyer": "掠奪的本能(毀滅者的覆滅)", - "Winter_Soldiers": "鐵衛扣飾(寒冬的戰士們)", - "Born_to_Obey": "古代零件(生而服從)", - "Root_Out_the_Turpitude": "永壽幼芽(根除惡孽)", - "Fire_Lord_Inflames_Blades_of_War": "工造機杼(火帝動爐銷劍戟)", - "A_Startling_Night_Terror": "蓄夢元件(劫夢驚魂)", "Tranquility_of_Vimala_bhumi": "思緒末屑(離垢清淨)", - "Nameless_Land_Nameless_People": "角色經驗素材(無名之地,無名之人)", + "A_Startling_Night_Terror": "蓄夢元件(劫夢驚魂)", + "Fire_Lord_Inflames_Blades_of_War": "工造機杼(火帝動爐銷劍戟)", + "Root_Out_the_Turpitude": "永壽幼芽(根除惡孽)", + "Born_to_Obey": "古代零件(生而服從)", + "Winter_Soldiers": "鐵衛扣飾(寒冬的戰士們)", + "Destruction_of_the_Destroyer": "掠奪的本能(毀滅者的覆滅)", + "Nine_Billion_Names": "熄滅原核(九十億個名字)", "Akashic_Records": "光錐經驗素材(阿卡夏紀錄)", + "Nameless_Land_Nameless_People": "角色經驗素材(無名之地,無名之人)", "The_Invisible_Hand": "信用點(看不見的手)", - "Abandoned_and_Insulted": "燃素 & 金屬(被廢棄與損害的)", - "Spring_of_Life": "固態淨水 & 虛粒子(生命之泉)", - "The_Land_of_Gold": "基本食材 & 蛋白米(黃金大地)", - "The_Blossom_in_the_Storm": "氣態流體 & 種子(風暴中怒放的花)", - "Legend_of_the_Puppet_Master": "廢棄機巧零件 & 玉兆單元(偃師傳說)", + "Scalpel_and_Screwdriver": "鏽跡齒輪 & 老舊臼齒(手術刀與螺絲起子)", "The_Wages_of_Humanity": "一人嘉禾 & 藥草萃取物(贍養人類)", + "Legend_of_the_Puppet_Master": "廢棄機巧零件 & 玉兆單元(偃師傳說)", + "The_Land_of_Gold": "基本食材 & 蛋白米(黃金大地)", + "Spring_of_Life": "固態淨水 & 虛粒子(生命之泉)", "Fragments_of_Illusory_Dreams": "安逸 & 碎夢(幻夢的殘片)", - "Scalpel_and_Screwdriver": "鏽跡齒輪 & 老舊臼齒(手術刀與螺絲起子)" + "The_Blossom_in_the_Storm": "氣態流體 & 種子(風暴中怒放的花)", + "Abandoned_and_Insulted": "燃素 & 金屬(被廢棄與損害的)" }, "Name_4": { "name": "第4個委託選擇", "help": "", - "Nine_Billion_Names": "熄滅原核(九十億個名字)", - "Destruction_of_the_Destroyer": "掠奪的本能(毀滅者的覆滅)", - "Winter_Soldiers": "鐵衛扣飾(寒冬的戰士們)", - "Born_to_Obey": "古代零件(生而服從)", - "Root_Out_the_Turpitude": "永壽幼芽(根除惡孽)", - "Fire_Lord_Inflames_Blades_of_War": "工造機杼(火帝動爐銷劍戟)", - "A_Startling_Night_Terror": "蓄夢元件(劫夢驚魂)", "Tranquility_of_Vimala_bhumi": "思緒末屑(離垢清淨)", - "Nameless_Land_Nameless_People": "角色經驗素材(無名之地,無名之人)", + "A_Startling_Night_Terror": "蓄夢元件(劫夢驚魂)", + "Fire_Lord_Inflames_Blades_of_War": "工造機杼(火帝動爐銷劍戟)", + "Root_Out_the_Turpitude": "永壽幼芽(根除惡孽)", + "Born_to_Obey": "古代零件(生而服從)", + "Winter_Soldiers": "鐵衛扣飾(寒冬的戰士們)", + "Destruction_of_the_Destroyer": "掠奪的本能(毀滅者的覆滅)", + "Nine_Billion_Names": "熄滅原核(九十億個名字)", "Akashic_Records": "光錐經驗素材(阿卡夏紀錄)", + "Nameless_Land_Nameless_People": "角色經驗素材(無名之地,無名之人)", "The_Invisible_Hand": "信用點(看不見的手)", - "Abandoned_and_Insulted": "燃素 & 金屬(被廢棄與損害的)", - "Spring_of_Life": "固態淨水 & 虛粒子(生命之泉)", - "The_Land_of_Gold": "基本食材 & 蛋白米(黃金大地)", - "The_Blossom_in_the_Storm": "氣態流體 & 種子(風暴中怒放的花)", - "Legend_of_the_Puppet_Master": "廢棄機巧零件 & 玉兆單元(偃師傳說)", + "Scalpel_and_Screwdriver": "鏽跡齒輪 & 老舊臼齒(手術刀與螺絲起子)", "The_Wages_of_Humanity": "一人嘉禾 & 藥草萃取物(贍養人類)", + "Legend_of_the_Puppet_Master": "廢棄機巧零件 & 玉兆單元(偃師傳說)", + "The_Land_of_Gold": "基本食材 & 蛋白米(黃金大地)", + "Spring_of_Life": "固態淨水 & 虛粒子(生命之泉)", "Fragments_of_Illusory_Dreams": "安逸 & 碎夢(幻夢的殘片)", - "Scalpel_and_Screwdriver": "鏽跡齒輪 & 老舊臼齒(手術刀與螺絲起子)" + "The_Blossom_in_the_Storm": "氣態流體 & 種子(風暴中怒放的花)", + "Abandoned_and_Insulted": "燃素 & 金屬(被廢棄與損害的)" }, "Duration": { "name": "派遣時間", diff --git a/tasks/assignment/keywords/entry.py b/tasks/assignment/keywords/entry.py index cab71c000..49fc7f34b 100644 --- a/tasks/assignment/keywords/entry.py +++ b/tasks/assignment/keywords/entry.py @@ -3,71 +3,8 @@ from .classes import AssignmentEntry # This file was auto-generated, do not modify it manually. To generate: # ``` python -m dev_tools.keyword_extract ``` -Nine_Billion_Names = AssignmentEntry( - id=1, - name='Nine_Billion_Names', - cn='九十亿个名字', - cht='九十億個名字', - en='Nine Billion Names', - jp='九十億の御名', - es='Nueve mil millones de nombres', -) -Destruction_of_the_Destroyer = AssignmentEntry( - id=2, - name='Destruction_of_the_Destroyer', - cn='毁灭者的覆灭', - cht='毀滅者的覆滅', - en='Destruction of the Destroyer', - jp='壊滅者の覆没', - es='La destrucción del destructor', -) -Winter_Soldiers = AssignmentEntry( - id=3, - name='Winter_Soldiers', - cn='寒冬的战士们', - cht='寒冬的戰士們', - en='Winter Soldiers', - jp='寒冬の戦士たち', - es='Los guerreros del invierno', -) -Born_to_Obey = AssignmentEntry( - id=4, - name='Born_to_Obey', - cn='生而服从', - cht='生而服從', - en='Born to Obey', - jp='生まれながらに服従する', - es='Creados para obedecer', -) -Root_Out_the_Turpitude = AssignmentEntry( - id=5, - name='Root_Out_the_Turpitude', - cn='根除恶孽', - cht='根除惡孽', - en='Root Out the Turpitude', - jp='悪孽を根絶やしに', - es='La raíz del mal', -) -Fire_Lord_Inflames_Blades_of_War = AssignmentEntry( - id=6, - name='Fire_Lord_Inflames_Blades_of_War', - cn='火帝动炉销剑戟', - cht='火帝動爐銷劍戟', - en='Fire Lord Inflames Blades of War', - jp='剣戟を焼却する火帝炉', - es='Prendan los fuelles, fundan las armas', -) -A_Startling_Night_Terror = AssignmentEntry( - id=7, - name='A_Startling_Night_Terror', - cn='劫梦惊魂', - cht='劫夢驚魂', - en='A Startling Night Terror', - jp='魂震える悪夢', - es='Pesadilla aterradora', -) Tranquility_of_Vimala_bhumi = AssignmentEntry( - id=8, + id=1, name='Tranquility_of_Vimala_bhumi', cn='离垢清净', cht='離垢清淨', @@ -75,17 +12,71 @@ Tranquility_of_Vimala_bhumi = AssignmentEntry( jp='離垢清浄', es='Limpieza y purificación', ) -Nameless_Land_Nameless_People = AssignmentEntry( - id=9, - name='Nameless_Land_Nameless_People', - cn='无名之地,无名之人', - cht='無名之地,無名之人', - en='Nameless Land, Nameless People', - jp='無名の地、無名の人', - es='Lugar anónimo, personas anónimas', +A_Startling_Night_Terror = AssignmentEntry( + id=2, + name='A_Startling_Night_Terror', + cn='劫梦惊魂', + cht='劫夢驚魂', + en='A Startling Night Terror', + jp='魂震える悪夢', + es='Pesadilla aterradora', +) +Fire_Lord_Inflames_Blades_of_War = AssignmentEntry( + id=3, + name='Fire_Lord_Inflames_Blades_of_War', + cn='火帝动炉销剑戟', + cht='火帝動爐銷劍戟', + en='Fire Lord Inflames Blades of War', + jp='剣戟を焼却する火帝炉', + es='Prendan los fuelles, fundan las armas', +) +Root_Out_the_Turpitude = AssignmentEntry( + id=4, + name='Root_Out_the_Turpitude', + cn='根除恶孽', + cht='根除惡孽', + en='Root Out the Turpitude', + jp='悪孽を根絶やしに', + es='La raíz del mal', +) +Born_to_Obey = AssignmentEntry( + id=5, + name='Born_to_Obey', + cn='生而服从', + cht='生而服從', + en='Born to Obey', + jp='生まれながらに服従する', + es='Creados para obedecer', +) +Winter_Soldiers = AssignmentEntry( + id=6, + name='Winter_Soldiers', + cn='寒冬的战士们', + cht='寒冬的戰士們', + en='Winter Soldiers', + jp='寒冬の戦士たち', + es='Los guerreros del invierno', +) +Destruction_of_the_Destroyer = AssignmentEntry( + id=7, + name='Destruction_of_the_Destroyer', + cn='毁灭者的覆灭', + cht='毀滅者的覆滅', + en='Destruction of the Destroyer', + jp='壊滅者の覆没', + es='La destrucción del destructor', +) +Nine_Billion_Names = AssignmentEntry( + id=8, + name='Nine_Billion_Names', + cn='九十亿个名字', + cht='九十億個名字', + en='Nine Billion Names', + jp='九十億の御名', + es='Nueve mil millones de nombres', ) Akashic_Records = AssignmentEntry( - id=10, + id=9, name='Akashic_Records', cn='阿卡夏记录', cht='阿卡夏紀錄', @@ -93,6 +84,15 @@ Akashic_Records = AssignmentEntry( jp='アーカーシャの記録', es='Los Registros de Akasha', ) +Nameless_Land_Nameless_People = AssignmentEntry( + id=10, + name='Nameless_Land_Nameless_People', + cn='无名之地,无名之人', + cht='無名之地,無名之人', + en='Nameless Land, Nameless People', + jp='無名の地、無名の人', + es='Lugar anónimo, personas anónimas', +) The_Invisible_Hand = AssignmentEntry( id=11, name='The_Invisible_Hand', @@ -102,71 +102,8 @@ The_Invisible_Hand = AssignmentEntry( jp='見えざる手', es='La mano invisible', ) -Abandoned_and_Insulted = AssignmentEntry( - id=12, - name='Abandoned_and_Insulted', - cn='被废弃与损害的', - cht='被廢棄與損害的', - en='Abandoned and Insulted', - jp='捨てられしものと傷つけられしもの', - es='Abandonado e insultado', -) -Spring_of_Life = AssignmentEntry( - id=13, - name='Spring_of_Life', - cn='生命之泉', - cht='生命之泉', - en='Spring of Life', - jp='生命の泉', - es='La fuente de la vida', -) -The_Land_of_Gold = AssignmentEntry( - id=14, - name='The_Land_of_Gold', - cn='黄金大地', - cht='黃金大地', - en='The Land of Gold', - jp='黄金の大地', - es='Tierra de oportunidades', -) -The_Blossom_in_the_Storm = AssignmentEntry( - id=15, - name='The_Blossom_in_the_Storm', - cn='风暴中怒放的花', - cht='風暴中怒放的花', - en='The Blossom in the Storm', - jp='嵐の中で咲き誇る花', - es='Flores en la tormenta', -) -Legend_of_the_Puppet_Master = AssignmentEntry( - id=16, - name='Legend_of_the_Puppet_Master', - cn='偃师传说', - cht='偃師傳說', - en='Legend of the Puppet Master', - jp='傀儡師伝説', - es='La leyenda del titiritero', -) -The_Wages_of_Humanity = AssignmentEntry( - id=17, - name='The_Wages_of_Humanity', - cn='赡养人类', - cht='贍養人類', - en='The Wages of Humanity', - jp='人類扶養', - es='La paga de la humanidad', -) -Fragments_of_Illusory_Dreams = AssignmentEntry( - id=18, - name='Fragments_of_Illusory_Dreams', - cn='幻梦的残片', - cht='幻夢的殘片', - en='Fragments of Illusory Dreams', - jp='幻夢の残片', - es='Fragmentos de sueños ilusorios', -) Scalpel_and_Screwdriver = AssignmentEntry( - id=19, + id=12, name='Scalpel_and_Screwdriver', cn='手术刀与螺丝刀', cht='手術刀與螺絲起子', @@ -174,3 +111,66 @@ Scalpel_and_Screwdriver = AssignmentEntry( jp='メスとスクリュードライバー', es='Bisturí y destornillador', ) +The_Wages_of_Humanity = AssignmentEntry( + id=13, + name='The_Wages_of_Humanity', + cn='赡养人类', + cht='贍養人類', + en='The Wages of Humanity', + jp='人類扶養', + es='La paga de la humanidad', +) +Legend_of_the_Puppet_Master = AssignmentEntry( + id=14, + name='Legend_of_the_Puppet_Master', + cn='偃师传说', + cht='偃師傳說', + en='Legend of the Puppet Master', + jp='傀儡師伝説', + es='La leyenda del titiritero', +) +The_Land_of_Gold = AssignmentEntry( + id=15, + name='The_Land_of_Gold', + cn='黄金大地', + cht='黃金大地', + en='The Land of Gold', + jp='黄金の大地', + es='Tierra de oportunidades', +) +Spring_of_Life = AssignmentEntry( + id=16, + name='Spring_of_Life', + cn='生命之泉', + cht='生命之泉', + en='Spring of Life', + jp='生命の泉', + es='La fuente de la vida', +) +Fragments_of_Illusory_Dreams = AssignmentEntry( + id=17, + name='Fragments_of_Illusory_Dreams', + cn='幻梦的残片', + cht='幻夢的殘片', + en='Fragments of Illusory Dreams', + jp='幻夢の残片', + es='Fragmentos de sueños ilusorios', +) +The_Blossom_in_the_Storm = AssignmentEntry( + id=18, + name='The_Blossom_in_the_Storm', + cn='风暴中怒放的花', + cht='風暴中怒放的花', + en='The Blossom in the Storm', + jp='嵐の中で咲き誇る花', + es='Flores en la tormenta', +) +Abandoned_and_Insulted = AssignmentEntry( + id=19, + name='Abandoned_and_Insulted', + cn='被废弃与损害的', + cht='被廢棄與損害的', + en='Abandoned and Insulted', + jp='捨てられしものと傷つけられしもの', + es='Abandonado e insultado', +) diff --git a/tasks/assignment/keywords/entry_detailed.py b/tasks/assignment/keywords/entry_detailed.py index 418e8f578..f295f67e7 100644 --- a/tasks/assignment/keywords/entry_detailed.py +++ b/tasks/assignment/keywords/entry_detailed.py @@ -3,71 +3,8 @@ from .classes import AssignmentEntryDetailed # This file was auto-generated, do not modify it manually. To generate: # ``` python -m dev_tools.keyword_extract ``` -Nine_Billion_Names = AssignmentEntryDetailed( - id=1, - name='Nine_Billion_Names', - cn='熄灭原核(九十亿个名字)', - cht='熄滅原核(九十億個名字)', - en='Extinguished Core (Nine Billion Names)', - jp='消滅した原核(九十億の御名)', - es='Núcleo apagado (Nueve mil millones de nombres)', -) -Destruction_of_the_Destroyer = AssignmentEntryDetailed( - id=2, - name='Destruction_of_the_Destroyer', - cn='掠夺的本能(毁灭者的覆灭)', - cht='掠奪的本能(毀滅者的覆滅)', - en="Thief's Instinct (Destruction of the Destroyer)", - jp='略奪の本能(壊滅者の覆没)', - es='Instinto del ladrón (La destrucción del destructor)', -) -Winter_Soldiers = AssignmentEntryDetailed( - id=3, - name='Winter_Soldiers', - cn='铁卫扣饰(寒冬的战士们)', - cht='鐵衛扣飾(寒冬的戰士們)', - en='Silvermane Badge (Winter Soldiers)', - jp='シルバーメインの釦(寒冬の戦士たち)', - es='Pin del guardia (Los guerreros del invierno)', -) -Born_to_Obey = AssignmentEntryDetailed( - id=4, - name='Born_to_Obey', - cn='古代零件(生而服从)', - cht='古代零件(生而服從)', - en='Ancient Part (Born to Obey)', - jp='古代パーツ(生まれながらに服従する)', - es='Componente antiguo (Creados para obedecer)', -) -Root_Out_the_Turpitude = AssignmentEntryDetailed( - id=5, - name='Root_Out_the_Turpitude', - cn='永寿幼芽(根除恶孽)', - cht='永壽幼芽(根除惡孽)', - en='Immortal Scionette (Root Out the Turpitude)', - jp='永寿の萌芽(悪孽を根絶やしに)', - es='Brote verde inmortal (La raíz del mal)', -) -Fire_Lord_Inflames_Blades_of_War = AssignmentEntryDetailed( - id=6, - name='Fire_Lord_Inflames_Blades_of_War', - cn='工造机杼(火帝动炉销剑戟)', - cht='工造機杼(火帝動爐銷劍戟)', - en="Artifex's Module (Fire Lord Inflames Blades of War)", - jp='工造機関(剣戟を焼却する火帝炉)', - es='Componente artificial mecánico (Prendan los fuelles, fundan las armas)', -) -A_Startling_Night_Terror = AssignmentEntryDetailed( - id=7, - name='A_Startling_Night_Terror', - cn='蓄梦元件(劫梦惊魂)', - cht='蓄夢元件(劫夢驚魂)', - en='Dream Collection Component (A Startling Night Terror)', - jp='ドリームコレクションパーツ(魂震える悪夢)', - es='Componente del acumulador de sueños (Pesadilla aterradora)', -) Tranquility_of_Vimala_bhumi = AssignmentEntryDetailed( - id=8, + id=1, name='Tranquility_of_Vimala_bhumi', cn='思绪末屑(离垢清净)', cht='思緒末屑(離垢清淨)', @@ -75,17 +12,71 @@ Tranquility_of_Vimala_bhumi = AssignmentEntryDetailed( jp='思考の粉末(離垢清浄)', es='Jirones de pensamientos (Limpieza y purificación)', ) -Nameless_Land_Nameless_People = AssignmentEntryDetailed( - id=9, - name='Nameless_Land_Nameless_People', - cn='角色经验材料(无名之地,无名之人)', - cht='角色經驗素材(無名之地,無名之人)', - en='Character EXP Material (Nameless Land, Nameless People)', - jp='キャラクター経験値素材(無名の地、無名の人)', - es='Material de EXP de personaje (Lugar anónimo, personas anónimas)', +A_Startling_Night_Terror = AssignmentEntryDetailed( + id=2, + name='A_Startling_Night_Terror', + cn='蓄梦元件(劫梦惊魂)', + cht='蓄夢元件(劫夢驚魂)', + en='Dream Collection Component (A Startling Night Terror)', + jp='ドリームコレクションパーツ(魂震える悪夢)', + es='Componente del acumulador de sueños (Pesadilla aterradora)', +) +Fire_Lord_Inflames_Blades_of_War = AssignmentEntryDetailed( + id=3, + name='Fire_Lord_Inflames_Blades_of_War', + cn='工造机杼(火帝动炉销剑戟)', + cht='工造機杼(火帝動爐銷劍戟)', + en="Artifex's Module (Fire Lord Inflames Blades of War)", + jp='工造機関(剣戟を焼却する火帝炉)', + es='Componente artificial mecánico (Prendan los fuelles, fundan las armas)', +) +Root_Out_the_Turpitude = AssignmentEntryDetailed( + id=4, + name='Root_Out_the_Turpitude', + cn='永寿幼芽(根除恶孽)', + cht='永壽幼芽(根除惡孽)', + en='Immortal Scionette (Root Out the Turpitude)', + jp='永寿の萌芽(悪孽を根絶やしに)', + es='Brote verde inmortal (La raíz del mal)', +) +Born_to_Obey = AssignmentEntryDetailed( + id=5, + name='Born_to_Obey', + cn='古代零件(生而服从)', + cht='古代零件(生而服從)', + en='Ancient Part (Born to Obey)', + jp='古代パーツ(生まれながらに服従する)', + es='Componente antiguo (Creados para obedecer)', +) +Winter_Soldiers = AssignmentEntryDetailed( + id=6, + name='Winter_Soldiers', + cn='铁卫扣饰(寒冬的战士们)', + cht='鐵衛扣飾(寒冬的戰士們)', + en='Silvermane Badge (Winter Soldiers)', + jp='シルバーメインの釦(寒冬の戦士たち)', + es='Pin del guardia (Los guerreros del invierno)', +) +Destruction_of_the_Destroyer = AssignmentEntryDetailed( + id=7, + name='Destruction_of_the_Destroyer', + cn='掠夺的本能(毁灭者的覆灭)', + cht='掠奪的本能(毀滅者的覆滅)', + en="Thief's Instinct (Destruction of the Destroyer)", + jp='略奪の本能(壊滅者の覆没)', + es='Instinto del ladrón (La destrucción del destructor)', +) +Nine_Billion_Names = AssignmentEntryDetailed( + id=8, + name='Nine_Billion_Names', + cn='熄灭原核(九十亿个名字)', + cht='熄滅原核(九十億個名字)', + en='Extinguished Core (Nine Billion Names)', + jp='消滅した原核(九十億の御名)', + es='Núcleo apagado (Nueve mil millones de nombres)', ) Akashic_Records = AssignmentEntryDetailed( - id=10, + id=9, name='Akashic_Records', cn='光锥经验材料(阿卡夏记录)', cht='光錐經驗素材(阿卡夏紀錄)', @@ -93,6 +84,15 @@ Akashic_Records = AssignmentEntryDetailed( jp='光円錐経験値素材(アーカーシャの記録)', es='Material de EXP de conos de luz (Los Registros de Akasha)', ) +Nameless_Land_Nameless_People = AssignmentEntryDetailed( + id=10, + name='Nameless_Land_Nameless_People', + cn='角色经验材料(无名之地,无名之人)', + cht='角色經驗素材(無名之地,無名之人)', + en='Character EXP Material (Nameless Land, Nameless People)', + jp='キャラクター経験値素材(無名の地、無名の人)', + es='Material de EXP de personaje (Lugar anónimo, personas anónimas)', +) The_Invisible_Hand = AssignmentEntryDetailed( id=11, name='The_Invisible_Hand', @@ -102,71 +102,8 @@ The_Invisible_Hand = AssignmentEntryDetailed( jp='信用ポイント(見えざる手)', es='Crédito (La mano invisible)', ) -Abandoned_and_Insulted = AssignmentEntryDetailed( - id=12, - name='Abandoned_and_Insulted', - cn='燃素 & 金属(被废弃与损害的)', - cht='燃素 & 金屬(被廢棄與損害的)', - en='Phlogiston & Metal (Abandoned and Insulted)', - jp='燃素 & 金属(捨てられしものと傷つけられしもの)', - es='Flogisto & Metal (Abandonado e insultado)', -) -Spring_of_Life = AssignmentEntryDetailed( - id=13, - name='Spring_of_Life', - cn='固态净水 & 虚粒子(生命之泉)', - cht='固態淨水 & 虛粒子(生命之泉)', - en='Solid Water & Virtual Particle (Spring of Life)', - jp='固形純水 & 仮想粒子(生命の泉)', - es='Agua sólida & Partícula virtual (La fuente de la vida)', -) -The_Land_of_Gold = AssignmentEntryDetailed( - id=14, - name='The_Land_of_Gold', - cn='基本食材 & 蛋白米(黄金大地)', - cht='基本食材 & 蛋白米(黃金大地)', - en='Basic Ingredients & Protein Rice (The Land of Gold)', - jp='基本食材 & タンパク米(黄金の大地)', - es='Ingredientes básicos & Arroz proteico (Tierra de oportunidades)', -) -The_Blossom_in_the_Storm = AssignmentEntryDetailed( - id=15, - name='The_Blossom_in_the_Storm', - cn='气态流体 & 种子(风暴中怒放的花)', - cht='氣態流體 & 種子(風暴中怒放的花)', - en='Gaseous Liquid & Seed (The Blossom in the Storm)', - jp='気態流体 & 種子(嵐の中で咲き誇る花)', - es='Líquido gaseoso & Semilla (Flores en la tormenta)', -) -Legend_of_the_Puppet_Master = AssignmentEntryDetailed( - id=16, - name='Legend_of_the_Puppet_Master', - cn='废弃机巧零件 & 玉兆单元(偃师传说)', - cht='廢棄機巧零件 & 玉兆單元(偃師傳說)', - en='Discarded Ingenium Parts & Jade Abacus Unit (Legend of the Puppet Master)', - jp='廃棄された機巧部品 & 玉兆単元(傀儡師伝説)', - es='Componentes mecánicos abandonados & Unidad de ábaco de jade (La leyenda del titiritero)', -) -The_Wages_of_Humanity = AssignmentEntryDetailed( - id=17, - name='The_Wages_of_Humanity', - cn='一人嘉禾 & 药草提取物(赡养人类)', - cht='一人嘉禾 & 藥草萃取物(贍養人類)', - en='Human-Height Auspicious Crops & Extract of Medicinal Herbs (The Wages of Humanity)', - jp='一人稲 & 薬草抽出物(人類扶養)', - es='Cosecha tan alta como una persona & Extracto de hierbas medicinales (La paga de la humanidad)', -) -Fragments_of_Illusory_Dreams = AssignmentEntryDetailed( - id=18, - name='Fragments_of_Illusory_Dreams', - cn='安逸 & 碎梦(幻梦的残片)', - cht='安逸 & 碎夢(幻夢的殘片)', - en='Tranquility & Broken Dreams (Fragments of Illusory Dreams)', - jp='安逸 & 砕けた夢(幻夢の残片)', - es='Tranquilidad & Sueños rotos (Fragmentos de sueños ilusorios)', -) Scalpel_and_Screwdriver = AssignmentEntryDetailed( - id=19, + id=12, name='Scalpel_and_Screwdriver', cn='锈迹齿轮 & 老旧臼齿(手术刀与螺丝刀)', cht='鏽跡齒輪 & 老舊臼齒(手術刀與螺絲起子)', @@ -174,3 +111,66 @@ Scalpel_and_Screwdriver = AssignmentEntryDetailed( jp='錆びた歯車 & 古びた大臼歯(メスとスクリュードライバー)', es='Engranaje oxidado & Muela vieja (Bisturí y destornillador)', ) +The_Wages_of_Humanity = AssignmentEntryDetailed( + id=13, + name='The_Wages_of_Humanity', + cn='一人嘉禾 & 药草提取物(赡养人类)', + cht='一人嘉禾 & 藥草萃取物(贍養人類)', + en='Human-Height Auspicious Crops & Extract of Medicinal Herbs (The Wages of Humanity)', + jp='一人稲 & 薬草抽出物(人類扶養)', + es='Cosecha tan alta como una persona & Extracto de hierbas medicinales (La paga de la humanidad)', +) +Legend_of_the_Puppet_Master = AssignmentEntryDetailed( + id=14, + name='Legend_of_the_Puppet_Master', + cn='废弃机巧零件 & 玉兆单元(偃师传说)', + cht='廢棄機巧零件 & 玉兆單元(偃師傳說)', + en='Discarded Ingenium Parts & Jade Abacus Unit (Legend of the Puppet Master)', + jp='廃棄された機巧部品 & 玉兆単元(傀儡師伝説)', + es='Componentes mecánicos abandonados & Unidad de ábaco de jade (La leyenda del titiritero)', +) +The_Land_of_Gold = AssignmentEntryDetailed( + id=15, + name='The_Land_of_Gold', + cn='基本食材 & 蛋白米(黄金大地)', + cht='基本食材 & 蛋白米(黃金大地)', + en='Basic Ingredients & Protein Rice (The Land of Gold)', + jp='基本食材 & タンパク米(黄金の大地)', + es='Ingredientes básicos & Arroz proteico (Tierra de oportunidades)', +) +Spring_of_Life = AssignmentEntryDetailed( + id=16, + name='Spring_of_Life', + cn='固态净水 & 虚粒子(生命之泉)', + cht='固態淨水 & 虛粒子(生命之泉)', + en='Solid Water & Virtual Particle (Spring of Life)', + jp='固形純水 & 仮想粒子(生命の泉)', + es='Agua sólida & Partícula virtual (La fuente de la vida)', +) +Fragments_of_Illusory_Dreams = AssignmentEntryDetailed( + id=17, + name='Fragments_of_Illusory_Dreams', + cn='安逸 & 碎梦(幻梦的残片)', + cht='安逸 & 碎夢(幻夢的殘片)', + en='Tranquility & Broken Dreams (Fragments of Illusory Dreams)', + jp='安逸 & 砕けた夢(幻夢の残片)', + es='Tranquilidad & Sueños rotos (Fragmentos de sueños ilusorios)', +) +The_Blossom_in_the_Storm = AssignmentEntryDetailed( + id=18, + name='The_Blossom_in_the_Storm', + cn='气态流体 & 种子(风暴中怒放的花)', + cht='氣態流體 & 種子(風暴中怒放的花)', + en='Gaseous Liquid & Seed (The Blossom in the Storm)', + jp='気態流体 & 種子(嵐の中で咲き誇る花)', + es='Líquido gaseoso & Semilla (Flores en la tormenta)', +) +Abandoned_and_Insulted = AssignmentEntryDetailed( + id=19, + name='Abandoned_and_Insulted', + cn='燃素 & 金属(被废弃与损害的)', + cht='燃素 & 金屬(被廢棄與損害的)', + en='Phlogiston & Metal (Abandoned and Insulted)', + jp='燃素 & 金属(捨てられしものと傷つけられしもの)', + es='Flogisto & Metal (Abandonado e insultado)', +) From eb920b7fecba5888144afc0937f923c6ee3b3d32 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 13 May 2024 12:34:36 +0800 Subject: [PATCH 088/114] Add: [ALAS] Button.match_template_binary() --- module/base/button.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/module/base/button.py b/module/base/button.py index cae5fc18e..153df61df 100644 --- a/module/base/button.py +++ b/module/base/button.py @@ -55,8 +55,13 @@ class Button(Resource): def image(self): return load_image(self.file, self.area) + @cached_property + def image_binary(self): + return rgb2gray(self.image) + def resource_release(self): del_cached_property(self, 'image') + del_cached_property(self, 'image_binary') self.clear_offset() def __str__(self): @@ -113,6 +118,29 @@ class Button(Resource): self._button_offset = np.array(point) + self.search[:2] - self.area[:2] return sim > similarity + def match_template_binary(self, image, similarity=0.85, direct_match=False) -> bool: + """ + Detects assets by template matching. + + To Some buttons, its location may not be static, `_button_offset` will be set. + + Args: + image: Screenshot. + similarity (float): 0-1. + direct_match: True to ignore `self.search` + + Returns: + bool. + """ + if not direct_match: + image = crop(image, self.search, copy=False) + image = rgb2gray(image) + res = cv2.matchTemplate(self.image_binary, image, cv2.TM_CCOEFF_NORMED) + _, sim, _, point = cv2.minMaxLoc(res) + + self._button_offset = np.array(point) + self.search[:2] - self.area[:2] + return sim > similarity + def match_multi_template(self, image, similarity=0.85, direct_match=False): """ Detects assets by template matching, return multiple reults @@ -225,6 +253,13 @@ class ButtonWrapper(Resource): return True return False + def match_template_binary(self, image, similarity=0.85, direct_match=False) -> bool: + for assets in self.buttons: + if assets.match_template_binary(image, similarity=similarity, direct_match=direct_match): + self._matched_button = assets + return True + return False + def match_multi_template(self, image, similarity=0.85, threshold=5, direct_match=False): """ Detects assets by template matching, return multiple results From 3018449c9c8d9cd5899947824b08af2b31acc072 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 13 May 2024 12:36:11 +0800 Subject: [PATCH 089/114] Fix: Match binary template on MAIN_GOTO_CHARACTER and MAP_EXIT --- assets/share/base/page/MAP_EXIT.2.png | Bin 0 -> 6491 bytes tasks/base/assets/assets_base_page.py | 23 ++++-- tasks/base/main_page.py | 52 +------------- tasks/base/ui.py | 99 +++++++++++++++++++++++--- tasks/daily/trail.py | 3 +- tasks/forgotten_hall/ui.py | 3 +- tasks/rogue/entry/entry.py | 3 +- 7 files changed, 112 insertions(+), 71 deletions(-) create mode 100644 assets/share/base/page/MAP_EXIT.2.png diff --git a/assets/share/base/page/MAP_EXIT.2.png b/assets/share/base/page/MAP_EXIT.2.png new file mode 100644 index 0000000000000000000000000000000000000000..6a4dcb099bc3cd4deaadb502939cc8b93a2957fa GIT binary patch literal 6491 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 Q>CYh#?(1pRsM|&T2P}Y=>;M1& literal 0 HcmV?d00001 diff --git a/tasks/base/assets/assets_base_page.py b/tasks/base/assets/assets_base_page.py index 0bb0d9dde..c43a575ae 100644 --- a/tasks/base/assets/assets_base_page.py +++ b/tasks/base/assets/assets_base_page.py @@ -280,13 +280,22 @@ MAP_CHECK = ButtonWrapper( ) MAP_EXIT = ButtonWrapper( name='MAP_EXIT', - share=Button( - file='./assets/share/base/page/MAP_EXIT.png', - area=(27, 46, 44, 74), - search=(7, 26, 64, 94), - color=(142, 144, 148), - button=(27, 46, 44, 74), - ), + share=[ + Button( + file='./assets/share/base/page/MAP_EXIT.png', + area=(27, 46, 44, 74), + search=(7, 26, 64, 94), + color=(142, 144, 148), + button=(27, 46, 44, 74), + ), + Button( + file='./assets/share/base/page/MAP_EXIT.2.png', + area=(27, 46, 44, 74), + search=(7, 26, 64, 94), + color=(160, 187, 206), + button=(27, 46, 44, 74), + ), + ], ) MAP_GOTO_WORLD = ButtonWrapper( name='MAP_GOTO_WORLD', diff --git a/tasks/base/main_page.py b/tasks/base/main_page.py index acd41fdcd..93f514aa7 100644 --- a/tasks/base/main_page.py +++ b/tasks/base/main_page.py @@ -5,11 +5,9 @@ from module.config.server import VALID_LANG from module.exception import RequestHumanTakeover, ScriptError from module.logger import logger from module.ocr.ocr import OcrWhiteLetterOnComplexBackground -from tasks.base.assets.assets_base_main_page import OCR_MAP_NAME, ROGUE_LEAVE_FOR_NOW -from tasks.base.assets.assets_base_page import CLOSE, MAP_EXIT -from tasks.base.page import Page, page_gacha, page_main +from tasks.base.assets.assets_base_main_page import OCR_MAP_NAME +from tasks.base.page import Page, page_main from tasks.base.popup import PopupHandler -from tasks.daily.assets.assets_daily_trial import START_TRIAL from tasks.map.keywords import KEYWORDS_MAP_PLANE, MapPlane @@ -158,49 +156,3 @@ class MainPage(PopupHandler): self.handle_lang_check(page=page_main) return True - - def ui_leave_special(self): - """ - Leave from: - - Rogue domains - - Character trials - - Returns: - bool: If left a special plane - - Pages: - in: Any - out: page_main - """ - if not self.appear(MAP_EXIT): - return False - - logger.info('UI leave special') - skip_first_screenshot = True - clicked = False - while 1: - if skip_first_screenshot: - skip_first_screenshot = False - else: - self.device.screenshot() - - # End - if clicked: - if self.appear(page_main.check_button): - logger.info(f'Leave to {page_main}') - break - - if self.appear_then_click(MAP_EXIT, interval=2): - continue - if self.handle_popup_confirm(): - continue - if self.match_template_color(START_TRIAL, interval=2): - logger.info(f'{START_TRIAL} -> {CLOSE}') - self.device.click(CLOSE) - clicked = True - continue - if self.handle_ui_close(page_gacha.check_button, interval=2): - continue - if self.appear_then_click(ROGUE_LEAVE_FOR_NOW, interval=2): - clicked = True - continue diff --git a/tasks/base/ui.py b/tasks/base/ui.py index 09d18db42..0c159cce3 100644 --- a/tasks/base/ui.py +++ b/tasks/base/ui.py @@ -4,13 +4,14 @@ from module.base.timer import Timer from module.exception import GameNotRunningError, GamePageUnknownError from module.logger import logger from module.ocr.ocr import Ocr -from tasks.base.assets.assets_base_page import MAP_EXIT +from tasks.base.assets.assets_base_main_page import ROGUE_LEAVE_FOR_NOW +from tasks.base.assets.assets_base_page import CLOSE, MAIN_GOTO_CHARACTER, MAP_EXIT from tasks.base.main_page import MainPage -from tasks.base.page import Page, page_main +from tasks.base.page import Page, page_gacha, page_main from tasks.combat.assets.assets_combat_finish import COMBAT_EXIT from tasks.combat.assets.assets_combat_interact import MAP_LOADING from tasks.combat.assets.assets_combat_prepare import COMBAT_PREPARE -from tasks.daily.assets.assets_daily_trial import INFO_CLOSE +from tasks.daily.assets.assets_daily_trial import INFO_CLOSE, START_TRIAL from tasks.login.assets.assets_login import LOGIN_CONFIRM @@ -24,6 +25,8 @@ class UI(MainPage): page (Page): interval: """ + if page == page_main: + return self.is_in_main(interval=interval) return self.appear(page.check_button, interval=interval) def ui_get_current_page(self, skip_first_screenshot=True): @@ -139,7 +142,7 @@ class UI(MainPage): for page in Page.iter_pages(): if page.parent is None or page.check_button is None: continue - if self.appear(page.check_button, interval=5): + if self.ui_page_appear(page, interval=5): logger.info(f'Page switch: {page} -> {page.parent}') self.handle_lang_check(page) if self.ui_page_confirm(page): @@ -297,14 +300,41 @@ class UI(MainPage): if additional(): continue - def is_in_main(self): - if self.appear(page_main.check_button): - if self.image_color_count(page_main.check_button, color=(235, 235, 235), threshold=234, count=400): - return True - if self.appear(MAP_EXIT): + def is_in_main(self, interval=0): + self.device.stuck_record_add(MAIN_GOTO_CHARACTER) + + if interval and not self.interval_is_reached(MAIN_GOTO_CHARACTER, interval=interval): + return False + + appear = False + if MAIN_GOTO_CHARACTER.match_template_binary(self.device.image): + if self.image_color_count(MAIN_GOTO_CHARACTER, color=(235, 235, 235), threshold=234, count=400): + appear = True + if not appear: + if MAP_EXIT.match_template_binary(self.device.image): + if self.image_color_count(MAP_EXIT, color=(235, 235, 235), threshold=221, count=50): + appear = True + + if appear and interval: + self.interval_reset(MAIN_GOTO_CHARACTER, interval=interval) + + return appear + + def is_in_map_exit(self, interval=0): + self.device.stuck_record_add(MAP_EXIT) + + if interval and not self.interval_is_reached(MAP_EXIT, interval=interval): + return False + + appear = False + if MAP_EXIT.match_template_binary(self.device.image): if self.image_color_count(MAP_EXIT, color=(235, 235, 235), threshold=221, count=50): - return True - return False + appear = True + + if appear and interval: + self.interval_reset(MAP_EXIT, interval=interval) + + return appear def ui_goto_main(self): return self.ui_ensure(destination=page_main) @@ -380,3 +410,50 @@ class UI(MainPage): button (Button): """ pass + + def ui_leave_special(self): + """ + Leave from: + - Rogue domains + - Character trials + + Returns: + bool: If left a special plane + + Pages: + in: Any + out: page_main + """ + if not self.is_in_map_exit(): + return False + + logger.info('UI leave special') + skip_first_screenshot = True + clicked = False + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + # End + if clicked: + if self.is_in_main(): + logger.info(f'Leave to {page_main}') + break + + if self.is_in_map_exit(interval=2): + self.device.click(MAP_EXIT) + continue + if self.handle_popup_confirm(): + continue + if self.match_template_color(START_TRIAL, interval=2): + logger.info(f'{START_TRIAL} -> {CLOSE}') + self.device.click(CLOSE) + clicked = True + continue + if self.handle_ui_close(page_gacha.check_button, interval=2): + continue + if self.appear_then_click(ROGUE_LEAVE_FOR_NOW, interval=2): + clicked = True + continue diff --git a/tasks/daily/trail.py b/tasks/daily/trail.py index f911b1491..a07459f8f 100644 --- a/tasks/daily/trail.py +++ b/tasks/daily/trail.py @@ -90,7 +90,8 @@ class CharacterTrial(UI): if self.match_template_color(START_TRIAL): break - if self.appear_then_click(MAP_EXIT): + if self.is_in_map_exit(interval=2): + self.device.click(MAP_EXIT) continue if self.handle_popup_confirm(): continue diff --git a/tasks/forgotten_hall/ui.py b/tasks/forgotten_hall/ui.py index a07c08753..14e9c3d59 100644 --- a/tasks/forgotten_hall/ui.py +++ b/tasks/forgotten_hall/ui.py @@ -210,7 +210,8 @@ class ForgottenHallUI(DungeonUI, ForgottenHallTeam): logger.info("Forgotten hall dungeon exited") break - if self.appear_then_click(MAP_EXIT): + if self.is_in_map_exit(interval=2): + self.device.click(MAP_EXIT) continue if self.handle_popup_confirm(): continue diff --git a/tasks/rogue/entry/entry.py b/tasks/rogue/entry/entry.py index 35b647919..448e33888 100644 --- a/tasks/rogue/entry/entry.py +++ b/tasks/rogue/entry/entry.py @@ -292,7 +292,8 @@ class RogueEntry(RouteBase, RogueRewardHandler, RoguePathHandler, DungeonUI): if self.handle_ui_back(self._is_page_rogue_path): continue # From ui_leave_special() - if self.appear_then_click(MAP_EXIT, interval=2): + if self.is_in_map_exit(interval=2): + self.device.click(MAP_EXIT) continue if self.handle_popup_confirm(): continue From ee3cb4f5ef0816bb5881698c63b1283c47402f97 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 13 May 2024 12:38:55 +0800 Subject: [PATCH 090/114] Fix: Wait current team shown up --- tasks/combat/team.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tasks/combat/team.py b/tasks/combat/team.py index 2282c178c..e2535b3ab 100644 --- a/tasks/combat/team.py +++ b/tasks/combat/team.py @@ -60,12 +60,13 @@ class CombatTeam(UI): logger.warning('Wait current team timeout') break current = self._get_team() - if current == index: - logger.attr('Team', current) - logger.info(f'Already selected to the correct team') - return False - else: - break + if current: + if current == index: + logger.attr('Team', current) + logger.info(f'Already selected to the correct team') + return False + else: + break # Set team retry = Timer(2, count=10) From ba9f2973d29337a780a13f220eca2bda512c4d7e Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 13 May 2024 12:40:44 +0800 Subject: [PATCH 091/114] Fix: Avoid double clicking COMBAT_TEAM_SUPPORT --- tasks/combat/combat.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tasks/combat/combat.py b/tasks/combat/combat.py index 9767615cd..fe8885af1 100644 --- a/tasks/combat/combat.py +++ b/tasks/combat/combat.py @@ -107,18 +107,20 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSuppo raise RequestHumanTakeover # Click - if support_character and self.appear(COMBAT_TEAM_SUPPORT): + if support_character and self.appear(COMBAT_TEAM_SUPPORT, interval=2): if pre_set_team: self.team_set(team) pre_set_team = False continue self.support_set(support_character) + self.interval_reset(COMBAT_TEAM_SUPPORT) support_set = True continue if support_set and self.appear(COMBAT_TEAM_PREPARE, interval=2): self.team_set(team) self.device.click(COMBAT_TEAM_PREPARE) self.interval_reset(COMBAT_TEAM_PREPARE) + self.interval_reset(COMBAT_TEAM_SUPPORT) continue if self.appear(COMBAT_TEAM_PREPARE): self.interval_reset(COMBAT_PREPARE) From bc82e00df9b617647976fe9d33865eeadcc6c17c Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 13 May 2024 12:57:49 +0800 Subject: [PATCH 092/114] Upd: Route copy Jarilo_BackwaterPass_F1_X503Y736 --- route/rogue/Combat/Jarilo_BackwaterPass_F1.py | 37 +++++++++++++++++++ route/rogue/route.json | 11 ++++++ tasks/rogue/route/loader.py | 4 ++ 3 files changed, 52 insertions(+) diff --git a/route/rogue/Combat/Jarilo_BackwaterPass_F1.py b/route/rogue/Combat/Jarilo_BackwaterPass_F1.py index 12db93cf2..d74adad67 100644 --- a/route/rogue/Combat/Jarilo_BackwaterPass_F1.py +++ b/route/rogue/Combat/Jarilo_BackwaterPass_F1.py @@ -116,6 +116,43 @@ class Route(RouteBase): self.clear_enemy(enemy2left.straight_run()) self.clear_enemy(enemy3.straight_run()) + def Jarilo_BackwaterPass_F1_X503Y736(self): + """ + | Waypoint | Position | Direction | Rotation | + | ---------- | ------------------------- | --------- | -------- | + | spawn | Waypoint((507.2, 733.7)), | 6.7 | 4 | + | enemy1 | Waypoint((507.0, 644.0)), | 12.6 | 6 | + | enemy2left | Waypoint((536.0, 630.5)), | 48.1 | 43 | + | enemy3 | Waypoint((557.0, 585.2)), | 114.1 | 6 | + | exit_ | Waypoint((557.0, 585.2)), | 114.1 | 6 | + | exit1 | Waypoint((549.5, 575.4)), | 356.2 | 354 | + | exit2 | Waypoint((565.4, 575.6)), | 4.1 | 359 | + """ + self.map_init(plane=Jarilo_BackwaterPass, floor="F1", position=(503.2, 736.9)) + self.register_domain_exit( + Waypoint((557.0, 585.2)), end_rotation=6, + left_door=Waypoint((549.5, 575.4)), right_door=Waypoint((565.4, 575.6))) + enemy1 = Waypoint((507.0, 644.0)) + enemy2left = Waypoint((536.0, 630.5)) + enemy3 = Waypoint((557.0, 585.2)) + # ===== End of generated waypoints ===== + + self.clear_enemy(enemy1) + self.clear_enemy(enemy2left.straight_run()) + self.clear_enemy(enemy3.straight_run()) + + """ + Notes + Herta_SupplyZone_F2_X397Y239 is the same as Herta_SupplyZone_F2_X397Y233 + but for wrong spawn point detected + """ + # Best 3 predictions: [ + # ('Combat_Jarilo_BackwaterPass_F1_X507Y733', 0.26, (503.2, 736.9)), + # ('Combat_Luofu_ArtisanshipCommission_F1_X41Y640', 0.18, (50.7, 644.4)), + # ('Combat_Luofu_DivinationCommission_F1_X737Y372', 0.174, (717.2, 355.4)), + # ('Combat_Herta_SupplyZone_F2_X45Y369', 0.168, (46.5, 370.0)) + # ] + def Jarilo_BackwaterPass_F1_X555Y643(self): """ | Waypoint | Position | Direction | Rotation | diff --git a/route/rogue/route.json b/route/rogue/route.json index 2987b3112..ffb40f57f 100644 --- a/route/rogue/route.json +++ b/route/rogue/route.json @@ -241,6 +241,17 @@ ], "domain": "Combat" }, + { + "name": "Combat_Jarilo_BackwaterPass_F1_X503Y736", + "route": "route.rogue.Combat.Jarilo_BackwaterPass_F1:Jarilo_BackwaterPass_F1_X503Y736", + "plane": "Jarilo_BackwaterPass", + "floor": "F1", + "position": [ + 503.2, + 736.9 + ], + "domain": "Combat" + }, { "name": "Combat_Jarilo_BackwaterPass_F1_X555Y643", "route": "route.rogue.Combat.Jarilo_BackwaterPass_F1:Jarilo_BackwaterPass_F1_X555Y643", diff --git a/tasks/rogue/route/loader.py b/tasks/rogue/route/loader.py index 7fa9e4f86..ab6e0e4de 100644 --- a/tasks/rogue/route/loader.py +++ b/tasks/rogue/route/loader.py @@ -177,6 +177,10 @@ class RouteLoader(RogueUI, MinimapWrapper, RouteLoader_, CharacterSwitch): return True # Before Combat_Luofu_Cloudford_F1_X281Y873 if route.name in [ + # ('Combat_Jarilo_BackwaterPass_F1_X507Y733', 0.26, (503.2, 736.9)), + # ('Combat_Herta_SupplyZone_F2_X45Y369', 0.168, (46.5, 370.0)) + 'Jarilo_BackwaterPass_F1_X507Y733', + 'Jarilo_BackwaterPass_F1_X555Y643', 'Occurrence_Jarilo_BackwaterPass_F1_X553Y643', 'Combat_Jarilo_GreatMine_F1_X545Y513', 'Combat_Herta_SupplyZone_F2_X45Y369', From 5eabcbe22d7efdf10d5970ec47593f37116c39e6 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 13 May 2024 14:59:48 +0800 Subject: [PATCH 093/114] Fix: Check COMBAT_AGAIN color prevent clicking before loaded --- tasks/combat/combat.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tasks/combat/combat.py b/tasks/combat/combat.py index fe8885af1..9548ab445 100644 --- a/tasks/combat/combat.py +++ b/tasks/combat/combat.py @@ -162,7 +162,8 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSuppo if callable(expected_end) and expected_end(): logger.info(f'Combat execute ended at {expected_end.__name__}') break - if self.appear(COMBAT_AGAIN): + if (self.appear(COMBAT_AGAIN) and + self.image_color_count(COMBAT_AGAIN, color=(227, 227, 228), threshold=221, count=50)): logger.info(f'Combat execute ended at {COMBAT_AGAIN}') break if self.is_in_main(): From 442eaf195b54f904e65f95aa1b3ec0abf93bee24 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 13 May 2024 15:02:56 +0800 Subject: [PATCH 094/114] Fix: Combat should not reenter if stamina exhausted but wave limit set --- tasks/combat/combat.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tasks/combat/combat.py b/tasks/combat/combat.py index 9548ab445..6d9001990 100644 --- a/tasks/combat/combat.py +++ b/tasks/combat/combat.py @@ -218,6 +218,9 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSuppo Returns: bool: True to re-enter combat and run with another wave settings """ + if self.config.stored.TrailblazePower.value < self.combat_wave_cost: + logger.info('Current trailblaze power is not enough for next run') + return False # Wave limit if self.combat_wave_limit: if self.combat_wave_done < self.combat_wave_limit: @@ -230,6 +233,7 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSuppo if self.config.stored.TrailblazePower.value >= self.combat_wave_cost: logger.info('Still having some trailblaze power run with less waves to empty it') return True + return False def combat_finish(self) -> bool: """ From 3566ff03022582eb64db6f5b92525f9d2e2fe1c4 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 13 May 2024 15:07:44 +0800 Subject: [PATCH 095/114] Fix: handle_ascension_dungeon_prepare() is called in weekly --- tasks/combat/combat.py | 1 + tasks/dungeon/weekly.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/tasks/combat/combat.py b/tasks/combat/combat.py index 6d9001990..c19c6c9fc 100644 --- a/tasks/combat/combat.py +++ b/tasks/combat/combat.py @@ -133,6 +133,7 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSuppo trial += 1 continue if self.handle_combat_interact(): + self.map_A_timer.reset() continue if self.handle_ascension_dungeon_prepare(): continue diff --git a/tasks/dungeon/weekly.py b/tasks/dungeon/weekly.py index 846104f47..facf25d01 100644 --- a/tasks/dungeon/weekly.py +++ b/tasks/dungeon/weekly.py @@ -21,6 +21,10 @@ class WeeklyDungeon(Dungeon): dungeon=dungeon, team=team, wave_limit=wave_limit, support_character=support_character, skip_ui_switch=skip_ui_switch) + def handle_ascension_dungeon_prepare(self): + # combat_wave_cost==30 in weekly, but no handle_ascension_dungeon_prepare required + return False + def get_weekly_remain(self) -> int: """ Pages: From 45a6cb668fa5a46376ccf50be4051596800d46a7 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 13 May 2024 15:48:25 +0800 Subject: [PATCH 096/114] Fix: Handle battle pass after combat --- tasks/combat/combat.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tasks/combat/combat.py b/tasks/combat/combat.py index c19c6c9fc..f0c486127 100644 --- a/tasks/combat/combat.py +++ b/tasks/combat/combat.py @@ -181,6 +181,9 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSuppo is_executing = False if self.handle_combat_state(): continue + # Battle pass popup appears just after combat finished and before blessings + if self.handle_battle_pass_notification(): + continue def _combat_can_again(self) -> bool: """ From f7b48efc7a7f0099becfc3b37e10e40d043dc487 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 13 May 2024 15:53:08 +0800 Subject: [PATCH 097/114] Fix: [CN] Remove "|" in ocr results --- module/ocr/keyword.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/ocr/keyword.py b/module/ocr/keyword.py index 82fcdf369..31bb8cdc3 100644 --- a/module/ocr/keyword.py +++ b/module/ocr/keyword.py @@ -7,7 +7,7 @@ import module.config.server as server from module.exception import ScriptError # ord('.') = 65294 -REGEX_PUNCTUATION = re.compile(r'[ ,..\'"“”,。…::;;!!??·・•●〇°*※\-—–-/\\\n\t()\[\]()「」『』【】《》[]]') +REGEX_PUNCTUATION = re.compile(r'[ ,..\'"“”,。…::;;!!??·・•●〇°*※\-—–-/\\|丨\n\t()\[\]()「」『』【】《》[]]') def parse_name(n): From f45c8a4dd6a1e4a83a295a2bbb3bd6d69c89fd82 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 13 May 2024 16:00:04 +0800 Subject: [PATCH 098/114] Fix: Check selected arrow left to character avatar --- .../combat/support/COMBAT_SUPPORT_SELECTED.png | Bin 5334 -> 0 bytes tasks/combat/assets/assets_combat_support.py | 10 ---------- tasks/combat/support.py | 15 ++++++++++----- 3 files changed, 10 insertions(+), 15 deletions(-) delete mode 100644 assets/share/combat/support/COMBAT_SUPPORT_SELECTED.png diff --git a/assets/share/combat/support/COMBAT_SUPPORT_SELECTED.png b/assets/share/combat/support/COMBAT_SUPPORT_SELECTED.png deleted file mode 100644 index 5ea0ab7bca637f1b0662f19fcc334360fa2f470d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5334 zcmeAS@N?(olHy`uVBq!ia0y~yULXGDa2NRXJ+)_H8XdD)H8V8x&|_dA(lx2M73}>fG7bq1|}foFoS^u z#7!KP91VoglmO2T{HJS6&;2~LyZZH7Q1@Zp;j24W*l5jK-Mgw8AoiN&1 zasXz7(H7%qAdGeofN5Yf5Jm%Gw2w3_`bZ11(&o9{;#!idz1 Date: Mon, 13 May 2024 16:33:56 +0800 Subject: [PATCH 099/114] Opt: Re-arrange interval for faster combat support switch --- tasks/combat/combat.py | 10 ++---- tasks/combat/support.py | 71 +++++++++++++++++++++++------------------ 2 files changed, 42 insertions(+), 39 deletions(-) diff --git a/tasks/combat/combat.py b/tasks/combat/combat.py index f0c486127..c460b1f77 100644 --- a/tasks/combat/combat.py +++ b/tasks/combat/combat.py @@ -82,14 +82,11 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSuppo logger.hr('Combat prepare') skip_first_screenshot = True if support_character: - # To set team before support set - pre_set_team = True # Block COMBAT_TEAM_PREPARE before support set support_set = False else: - pre_set_team = False support_set = True - logger.info([support_character, pre_set_team, support_set]) + logger.info([support_character, support_set]) trial = 0 while 1: if skip_first_screenshot: @@ -108,10 +105,7 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSuppo # Click if support_character and self.appear(COMBAT_TEAM_SUPPORT, interval=2): - if pre_set_team: - self.team_set(team) - pre_set_team = False - continue + self.team_set(team) self.support_set(support_character) self.interval_reset(COMBAT_TEAM_SUPPORT) support_set = True diff --git a/tasks/combat/support.py b/tasks/combat/support.py index 937901e6a..237853dd4 100644 --- a/tasks/combat/support.py +++ b/tasks/combat/support.py @@ -130,6 +130,7 @@ class CombatSupport(UI): out: COMBAT_PREPARE """ logger.hr("Combat support") + self.interval_clear(COMBAT_TEAM_SUPPORT) skip_first_screenshot = True selected_support = False while 1: @@ -143,7 +144,7 @@ class CombatSupport(UI): return True # Click - if self.appear(COMBAT_TEAM_SUPPORT, interval=1): + if self.appear(COMBAT_TEAM_SUPPORT, interval=2): self.device.click(COMBAT_TEAM_SUPPORT) self.interval_reset(COMBAT_TEAM_SUPPORT) continue @@ -154,15 +155,30 @@ class CombatSupport(UI): self._select_next_support() self.interval_reset(POPUP_CANCEL) continue - if self.appear(COMBAT_SUPPORT_LIST, interval=1): + if self.appear(COMBAT_SUPPORT_LIST, interval=2): + scroll = AdaptiveScroll(area=COMBAT_SUPPORT_LIST_SCROLL.area, + name=COMBAT_SUPPORT_LIST_SCROLL.name) + if not scroll.appear(main=self): + self.interval_clear(COMBAT_SUPPORT_LIST) + continue if not selected_support and support_character_name != "FirstCharacter": - self._search_support( - support_character_name) # Search support + self._search_support(support_character_name) # Search support selected_support = True self.device.click(COMBAT_SUPPORT_ADD) self.interval_reset(COMBAT_SUPPORT_LIST) continue + def _get_character(self, support_character_name: str) -> SupportCharacter: + if support_character_name.startswith("Trailblazer"): + character = SupportCharacter(f"Stelle{support_character_name[11:]}", self.device.image) + if character: + return character + character = SupportCharacter(f"Caelum{support_character_name[11:]}", self.device.image) + # Should return something + return character + else: + return SupportCharacter(support_character_name, self.device.image) + def _search_support(self, support_character_name: str = "JingYuan"): """ Args: @@ -186,37 +202,30 @@ class CombatSupport(UI): scroll.drag_threshold = backup scroll.set_top(main=self) - logger.info("Searching support") - skip_first_screenshot = False - while 1: - if skip_first_screenshot: - skip_first_screenshot = False - else: - self.device.screenshot() + logger.info("Searching support") + skip_first_screenshot = True + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() - if not support_character_name.startswith("Trailblazer"): - character = SupportCharacter( - support_character_name, self.device.image) + character = self._get_character(support_character_name) + if character: + logger.info("Support found") + if self._select_support(character): + return True else: - character = SupportCharacter(f"Stelle{support_character_name[11:]}", - self.device.image) or SupportCharacter( - f"Caelum{support_character_name[11:]}", self.device.image) - - if character: - logger.info("Support found") - if self._select_support(character): - return True - else: - logger.warning("Support not selected") - return False - - if not scroll.at_bottom(main=self): - scroll.next_page(main=self) - continue - else: - logger.info("Support not found") + logger.warning("Support not selected") return False + if not scroll.at_bottom(main=self): + scroll.next_page(main=self) + continue + else: + logger.info("Support not found") + return False + def _select_support(self, character: SupportCharacter): """ Args: From ab6aabeefad0d3145651b76eb78857f3d13660e1 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 13 May 2024 16:36:29 +0800 Subject: [PATCH 100/114] Opt: Search prioritize characters before scrolling --- tasks/combat/support.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tasks/combat/support.py b/tasks/combat/support.py index 237853dd4..d53548248 100644 --- a/tasks/combat/support.py +++ b/tasks/combat/support.py @@ -192,6 +192,14 @@ class CombatSupport(UI): out: COMBAT_SUPPORT_LIST """ logger.hr("Combat support search") + # Search prioritize characters + character = self._get_character(support_character_name) + if character: + logger.info("Support found in first page") + if self._select_support(character): + return True + + # Search in the following pages scroll = AdaptiveScroll(area=COMBAT_SUPPORT_LIST_SCROLL.area, name=COMBAT_SUPPORT_LIST_SCROLL.name) if scroll.appear(main=self): From af603eadf8a4faf876a4630d8611c8dec5cb3b75 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Mon, 13 May 2024 19:24:17 +0800 Subject: [PATCH 101/114] Fix: Lower OcrCharacterName.merge_thres_y so DanHeng's long name won't mix with others --- tasks/battle_pass/battle_pass.py | 16 ++++++++++++---- tasks/character/switch.py | 2 +- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/tasks/battle_pass/battle_pass.py b/tasks/battle_pass/battle_pass.py index a7e172b41..4386dd136 100644 --- a/tasks/battle_pass/battle_pass.py +++ b/tasks/battle_pass/battle_pass.py @@ -96,6 +96,10 @@ class BattlePassUI(UI): MAX_LEVEL = 70 def _battle_pass_wait_rewards_loaded(self, skip_first_screenshot=True): + """ + Returns: + bool: If load success + """ timeout = Timer(2, count=4).start() while 1: if skip_first_screenshot: @@ -105,12 +109,16 @@ class BattlePassUI(UI): if timeout.reached(): logger.warning('Wait rewards tab loaded timeout') - break + return False if self.appear(REWARDS_LOADED): logger.info('Rewards tab loaded') - break + return True def _battle_pass_wait_missions_loaded(self, skip_first_screenshot=True): + """ + Returns: + bool: If load success + """ timeout = Timer(2, count=4).start() while 1: if skip_first_screenshot: @@ -120,14 +128,14 @@ class BattlePassUI(UI): if timeout.reached(): logger.warning('Wait missions tab loaded timeout') - break + return False # Has scroll and last mission loaded if self.appear(MISSION_PAGE_SCROLL): color = get_color(self.device.image, MISSIONS_LOADED.area) if np.mean(color) > 128: logger.info('Missions tab loaded') - break + return True def battle_pass_goto(self, state: KEYWORDS_BATTLE_PASS_TAB): """ diff --git a/tasks/character/switch.py b/tasks/character/switch.py index 7b36b6806..a30e25796 100644 --- a/tasks/character/switch.py +++ b/tasks/character/switch.py @@ -16,7 +16,7 @@ from tasks.character.keywords import CharacterList, DICT_SORTED_RANGES, KEYWORDS class OcrCharacterName(OcrWhiteLetterOnComplexBackground): merge_thres_x = 20 - merge_thres_y = 20 + merge_thres_y = 10 def after_process(self, result): result = result.replace('蛆', '妲') From acf72b18d5401a0d2aa444581574e52d0e5e2a7e Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Tue, 14 May 2024 01:37:27 +0800 Subject: [PATCH 102/114] =?UTF-8?q?Fix:=20[CN]=20Handle=20OCR=20error=20on?= =?UTF-8?q?=20=E5=8C=BA=E5=9F=9F=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tasks/base/main_page.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/base/main_page.py b/tasks/base/main_page.py index 93f514aa7..439dd5622 100644 --- a/tasks/base/main_page.py +++ b/tasks/base/main_page.py @@ -37,7 +37,7 @@ class OcrPlaneName(OcrWhiteLetterOnComplexBackground): # 区域-战 result = re.sub(r'区域.*战$', '区域战斗', result) # 区域-事 - result = re.sub(r'区域.*事$', '区域事件', result) + result = re.sub(r'区域.*[事件]$', '区域事件', result) # 区域-战 result = re.sub(r'区域.*交$', '区域交易', result) # 区域-事伴, 区域-事祥 From 415fa61df95267840d839c785a41b7844dfebe44 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Tue, 14 May 2024 01:49:31 +0800 Subject: [PATCH 103/114] Fix: Try without preprocess if no doors matched --- module/ocr/ocr.py | 7 +++++-- tasks/rogue/route/exit.py | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/module/ocr/ocr.py b/module/ocr/ocr.py index 82fb32d39..2fa29aa03 100644 --- a/module/ocr/ocr.py +++ b/module/ocr/ocr.py @@ -422,9 +422,12 @@ class Duration(Ocr): class OcrWhiteLetterOnComplexBackground(Ocr): + white_preprocess = True + def pre_process(self, image): - image = extract_white_letters(image, threshold=255) - image = cv2.merge([image, image, image]) + if self.white_preprocess: + image = extract_white_letters(image, threshold=255) + image = cv2.merge([image, image, image]) return image def detect_and_ocr(self, *args, **kwargs): diff --git a/tasks/rogue/route/exit.py b/tasks/rogue/route/exit.py index d3fc611c4..c5d45ee48 100644 --- a/tasks/rogue/route/exit.py +++ b/tasks/rogue/route/exit.py @@ -235,6 +235,10 @@ class RogueExit(CombatInteract): ocr = OcrDomainExit(OCR_DOMAIN_EXIT) results = ocr.matched_ocr(image, keyword_classes=MapPlane) + # Try without preprocess + if not len(results): + ocr.white_preprocess = False + results = ocr.matched_ocr(image, keyword_classes=MapPlane) centers = [area_center(result.area) for result in results] logger.info(f'DomainDoor: {centers}') directions = [self.screen2direction(center) for center in centers] From aeb7926f6d1c440c4d86c3183afe146d204529ed Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Tue, 14 May 2024 01:52:35 +0800 Subject: [PATCH 104/114] Fix: Route never pass through node2 in Jarilo_BackwaterPass_F1_X475Y49 --- route/rogue/Combat/Jarilo_BackwaterPass_F1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/route/rogue/Combat/Jarilo_BackwaterPass_F1.py b/route/rogue/Combat/Jarilo_BackwaterPass_F1.py index d74adad67..a3af0a87d 100644 --- a/route/rogue/Combat/Jarilo_BackwaterPass_F1.py +++ b/route/rogue/Combat/Jarilo_BackwaterPass_F1.py @@ -63,7 +63,7 @@ class Route(RouteBase): ) # 2 self.clear_enemy( - node2, + node2.set_threshold(3), enemy2.straight_run(), ) # 3 From b784c17ac04e2432c934754b4632f5964664f5b1 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Tue, 14 May 2024 01:55:02 +0800 Subject: [PATCH 105/114] Fix: Match Combat_Luofu_DivinationCommission_F1_X97Y457 before Combat_Luofu_Cloudford_F1_X283Y865 --- tasks/rogue/route/loader.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tasks/rogue/route/loader.py b/tasks/rogue/route/loader.py index ab6e0e4de..55b32d27c 100644 --- a/tasks/rogue/route/loader.py +++ b/tasks/rogue/route/loader.py @@ -192,6 +192,10 @@ class RouteLoader(RogueUI, MinimapWrapper, RouteLoader_, CharacterSwitch): # ('Occurrence_Luofu_DivinationCommission_F2_X149Y659', 0.237, (148.9, 658.8)), # ('Occurrence_Luofu_DivinationCommission_F2_X425Y791', 0.11, (425.2, 793.8)) 'Occurrence_Luofu_DivinationCommission_F2_X149Y659', + # ('Combat_Luofu_DivinationCommission_F1_X97Y457', 0.222, (97.8, 456.9)), + # ('Combat_Luofu_ScalegorgeWaterscape_F1_X415Y261', 0.112, (371.8, 289.4)), + # ('Combat_Herta_SupplyZone_F2_X45Y369', 0.104, (11.7, 367.6)) + 'Combat_Luofu_DivinationCommission_F1_X97Y457', ] and similarity > 0.15: return True if route.name in [ From e0707b828610f76183a2cd4fbb849cbacd7da7d9 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Tue, 14 May 2024 01:59:47 +0800 Subject: [PATCH 106/114] Fix: [CN] Handle curio fixed TODO: [EN] Update CURIO_FIXED --- assets/cn/rogue/ui/CURIO_FIXED.png | Bin 0 -> 8231 bytes tasks/rogue/assets/assets_rogue_ui.py | 11 +++++++++++ tasks/rogue/blessing/ui.py | 5 +++++ 3 files changed, 16 insertions(+) create mode 100644 assets/cn/rogue/ui/CURIO_FIXED.png diff --git a/assets/cn/rogue/ui/CURIO_FIXED.png b/assets/cn/rogue/ui/CURIO_FIXED.png new file mode 100644 index 0000000000000000000000000000000000000000..8d6adc00d6174b0022a16479305b081e0a51646b GIT binary patch literal 8231 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 {BLESSING_CONFIRM}') self.device.click(BLESSING_CONFIRM) return True + # Fixed a curio from occurrence + if self.appear(CURIO_FIXED, interval=2): + logger.info(f'{CURIO_FIXED} -> {BLESSING_CONFIRM}') + self.device.click(BLESSING_CONFIRM) + return True return False From e35ab54ebc623966c17afef932c86fd81f51f499 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Tue, 14 May 2024 02:10:46 +0800 Subject: [PATCH 107/114] =?UTF-8?q?Fix:=20[CN]=20Handle=20OCR=20error=20on?= =?UTF-8?q?=20=E5=8C=BA=E5=9F=9F=E7=B2=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tasks/base/main_page.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tasks/base/main_page.py b/tasks/base/main_page.py index 439dd5622..1238ee9ed 100644 --- a/tasks/base/main_page.py +++ b/tasks/base/main_page.py @@ -40,6 +40,8 @@ class OcrPlaneName(OcrWhiteLetterOnComplexBackground): result = re.sub(r'区域.*[事件]$', '区域事件', result) # 区域-战 result = re.sub(r'区域.*交$', '区域交易', result) + # 区域-战 + result = re.sub(r'区域.*[精英]$', '区域精英', result) # 区域-事伴, 区域-事祥 result = re.sub(r'事[伴祥]', '事件', result) # 医域-战斗 From 8375e8729d4fbe20ecc107b2acede8f73bc0bc9e Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Tue, 14 May 2024 02:17:24 +0800 Subject: [PATCH 108/114] Upd: Route copies --- route/rogue/Combat/Luofu_Cloudford_F1.py | 55 +++++++++++++++++++ .../Elite/Jarilo_CorridorofFadingEchoes_F1.py | 30 ++++++++++ .../Elite/Luofu_ArtisanshipCommission_F1.py | 30 ++++++++++ route/rogue/Elite/Luofu_Cloudford_F1.py | 31 +++++++++++ .../rogue/Elite/Luofu_StargazerNavalia_F1.py | 31 +++++++++++ route/rogue/route.json | 55 +++++++++++++++++++ 6 files changed, 232 insertions(+) diff --git a/route/rogue/Combat/Luofu_Cloudford_F1.py b/route/rogue/Combat/Luofu_Cloudford_F1.py index a8c5fde6e..e320e3973 100644 --- a/route/rogue/Combat/Luofu_Cloudford_F1.py +++ b/route/rogue/Combat/Luofu_Cloudford_F1.py @@ -256,3 +256,58 @@ class Route(RouteBase): ) self.clear_item(item4) self.clear_enemy(enemy4) + + def Luofu_Cloudford_F1_X432Y685(self): + """ + | Waypoint | Position | Direction | Rotation | + | -------- | ------------------------- | --------- | -------- | + | spawn | Waypoint((435.4, 669.2)), | 6.7 | 4 | + | item1 | Waypoint((432.2, 628.3)), | 2.7 | 357 | + | enemy1 | Waypoint((428.6, 598.8)), | 8.0 | 177 | + | node2 | Waypoint((421.2, 590.8)), | 44.2 | 285 | + | node3 | Waypoint((366.6, 588.2)), | 274.2 | 274 | + | enemy3 | Waypoint((344.9, 590.4)), | 191.8 | 357 | + | item4 | Waypoint((309.6, 580.2)), | 290.1 | 281 | + | enemy4 | Waypoint((271.3, 585.5)), | 285.0 | 274 | + | exit_ | Waypoint((271.3, 585.5)), | 285.0 | 274 | + | exit1 | Waypoint((267.9, 592.3)), | 275.9 | 274 | + | exit2 | Waypoint((267.8, 580.0)), | 275.8 | 274 | + """ + self.map_init(plane=Luofu_Cloudford, floor="F1", position=(432.8, 685.1)) + self.register_domain_exit( + Waypoint((271.3, 585.5)), end_rotation=274, + left_door=Waypoint((267.9, 592.3)), right_door=Waypoint((267.8, 580.0))) + item1 = Waypoint((432.2, 628.3)) + enemy1 = Waypoint((428.6, 598.8)) + node2 = Waypoint((421.2, 590.8)) + node3 = Waypoint((366.6, 588.2)) + enemy3 = Waypoint((344.9, 590.4)) + item4 = Waypoint((309.6, 580.2)) + enemy4 = Waypoint((271.3, 585.5)) + # ===== End of generated waypoints ===== + + self.clear_item(item1) + self.clear_enemy(enemy1) + # Go through bridges + self.rotation_set(270) + self.minimap.lock_rotation(270) + self.clear_enemy( + node2.set_threshold(3), + node3.set_threshold(3), + enemy3, + ) + self.clear_item(item4) + self.clear_enemy(enemy4) + + """ + Notes + Luofu_Cloudford_F1_X435Y685 is the same as Luofu_Cloudford_F1_X435Y669 + but for wrong spawn point detected + """ + # Best 3 predictions: [ + # ('Combat_Luofu_Cloudford_F1_X433Y617', 0.195, (432.8, 668.4)), + # ('Combat_Herta_SupplyZone_F2_X45Y369', 0.18, (24.2, 372.2)), + # ('Combat_Luofu_Cloudford_F1_X435Y669', 0.18, (432.8, 685.1)) + # ] + # (432.9, 684.9) + # ('Combat_Luofu_Cloudford_F1_X435Y669', 0.172, (432.8, 685.0)) diff --git a/route/rogue/Elite/Jarilo_CorridorofFadingEchoes_F1.py b/route/rogue/Elite/Jarilo_CorridorofFadingEchoes_F1.py index db8fb74cc..9438b33b1 100644 --- a/route/rogue/Elite/Jarilo_CorridorofFadingEchoes_F1.py +++ b/route/rogue/Elite/Jarilo_CorridorofFadingEchoes_F1.py @@ -30,6 +30,36 @@ class Route(RouteBase): but for wrong spawn point detected """ + def Jarilo_CorridorofFadingEchoes_F1_X415Y953(self): + """ + | Waypoint | Position | Direction | Rotation | + | -------- | ------------------------- | --------- | -------- | + | spawn | Waypoint((415.5, 947.9)), | 96.7 | 91 | + | enemy | Waypoint((464.0, 953.0)), | 96.8 | 94 | + | reward | Waypoint((472.7, 958.5)), | 214.6 | 114 | + | exit_ | Waypoint((480.0, 944.0)), | 92.7 | 84 | + """ + self.map_init(plane=Jarilo_CorridorofFadingEchoes, floor="F1", position=(415.4, 953.3)) + enemy = Waypoint((464.0, 953.0)) + reward = Waypoint((472.7, 958.5)) + exit_ = Waypoint((480.0, 944.0)) + + self.clear_elite(enemy) + self.domain_reward(reward) + self.domain_single_exit(exit_) + # ===== End of generated waypoints ===== + + """ + Notes + Jarilo_CorridorofFadingEchoes_F1_X415Y953 is the same as Jarilo_CorridorofFadingEchoes_F1_X415Y947 + but for wrong spawn point detected + """ + # Best 3 predictions: [ + # ('Elite_Jarilo_CorridorofFadingEchoes_F1_X415Y933', 0.169, (415.4, 953.3)), + # ('Elite_Jarilo_CorridorofFadingEchoes_F1_X415Y947', 0.169, (415.4, 953.3)), + # ('Elite_Herta_SupplyZone_F2_X680Y247', 0.162, (738.4, 252.2)) + # ] + def Jarilo_CorridorofFadingEchoes_F1_X415Y947(self): """ | Waypoint | Position | Direction | Rotation | diff --git a/route/rogue/Elite/Luofu_ArtisanshipCommission_F1.py b/route/rogue/Elite/Luofu_ArtisanshipCommission_F1.py index c563530d0..d376ff325 100644 --- a/route/rogue/Elite/Luofu_ArtisanshipCommission_F1.py +++ b/route/rogue/Elite/Luofu_ArtisanshipCommission_F1.py @@ -24,6 +24,36 @@ class Route(RouteBase): self.domain_single_exit(exit_) # ===== End of generated waypoints ===== + def Luofu_ArtisanshipCommission_F1_X391Y493(self): + """ + | Waypoint | Position | Direction | Rotation | + | -------- | ------------------------- | --------- | -------- | + | spawn | Waypoint((385.2, 494.6)), | 94.2 | 91 | + | enemy | Waypoint((444.2, 490.5)), | 94.2 | 91 | + | reward | Waypoint((448.6, 497.2)), | 149.7 | 91 | + | exit_ | Waypoint((458.0, 483.7)), | 94.2 | 91 | + """ + self.map_init(plane=Luofu_ArtisanshipCommission, floor="F1", position=(391.1, 493.2)) + enemy = Waypoint((444.2, 490.5)) + reward = Waypoint((448.6, 497.2)) + exit_ = Waypoint((458.0, 483.7)) + + self.clear_elite(enemy) + self.domain_reward(reward) + self.domain_single_exit(exit_) + # ===== End of generated waypoints ===== + + """ + Notes + Luofu_ArtisanshipCommission_F1_X391Y493 is the same as Luofu_ArtisanshipCommission_F1_X385Y494 + but for wrong spawn point detected + """ + # Best 3 predictions: [ + # ('Elite_Luofu_ArtisanshipCommission_F1_X385Y494', 0.182, (391.1, 493.2)), + # ('Elite_Jarilo_CorridorofFadingEchoes_F1_X415Y933', 0.157, (364.0, 951.0)), + # ('Elite_Jarilo_CorridorofFadingEchoes_F1_X415Y947', 0.157, (364.0, 951.0)) + # ] + def Luofu_ArtisanshipCommission_F1_X504Y493(self): """ | Waypoint | Position | Direction | Rotation | diff --git a/route/rogue/Elite/Luofu_Cloudford_F1.py b/route/rogue/Elite/Luofu_Cloudford_F1.py index 2ade4c4ab..6d9c7d26a 100644 --- a/route/rogue/Elite/Luofu_Cloudford_F1.py +++ b/route/rogue/Elite/Luofu_Cloudford_F1.py @@ -25,3 +25,34 @@ class Route(RouteBase): self.domain_reward(reward) self.domain_single_exit(exit_) # ===== End of generated waypoints ===== + + @locked_rotation(0) + def Luofu_Cloudford_F1_X342Y1003(self): + """ + | Waypoint | Position | Direction | Rotation | + | -------- | -------------------------- | --------- | -------- | + | spawn | Waypoint((337.3, 1003.4)), | 6.7 | 4 | + | enemy | Waypoint((336.2, 962.2)), | 6.7 | 4 | + | reward | Waypoint((342.9, 950.8)), | 44.2 | 31 | + | exit_ | Waypoint((328.8, 942.8)), | 316.1 | 331 | + """ + self.map_init(plane=Luofu_Cloudford, floor="F1", position=(342.3, 1003.4)) + enemy = Waypoint((336.2, 962.2)) + reward = Waypoint((342.9, 950.8)) + exit_ = Waypoint((328.8, 942.8)) + + self.clear_elite(enemy) + self.domain_reward(reward) + self.domain_single_exit(exit_) + # ===== End of generated waypoints ===== + + """ + Notes + Luofu_Cloudford_F1_X342Y1003 is the same as Luofu_Cloudford_F1_X337Y1003 + but for wrong spawn point detected + """ + # Best 3 predictions: [ + # ('Elite_Luofu_Cloudford_F1_X337Y1003', 0.169, (342.3, 1002.7)), + # ('Elite_Luofu_ArtisanshipCommission_F1_X504Y493', 0.106, (519.3, 452.7)), + # ('Elite_Jarilo_CorridorofFadingEchoes_F1_X415Y933', 0.104, (433.8, 982.0)) + # ] diff --git a/route/rogue/Elite/Luofu_StargazerNavalia_F1.py b/route/rogue/Elite/Luofu_StargazerNavalia_F1.py index ca696d3aa..cc1d221f7 100644 --- a/route/rogue/Elite/Luofu_StargazerNavalia_F1.py +++ b/route/rogue/Elite/Luofu_StargazerNavalia_F1.py @@ -25,3 +25,34 @@ class Route(RouteBase): self.domain_reward(reward) self.domain_single_exit(exit_) # ===== End of generated waypoints ===== + + @locked_rotation(90) + def Luofu_StargazerNavalia_F1_X617Y511(self): + """ + | Waypoint | Position | Direction | Rotation | + | -------- | ------------------------- | --------- | -------- | + | spawn | Waypoint((617.5, 511.5)), | 96.7 | 91 | + | enemy | Waypoint((664.6, 512.6)), | 96.8 | 94 | + | reward | Waypoint((677.1, 521.2)), | 212.8 | 108 | + | exit_ | Waypoint((684.6, 505.0)), | 91.3 | 82 | + """ + self.map_init(plane=Luofu_StargazerNavalia, floor="F1", position=(617.5, 511.5)) + enemy = Waypoint((664.6, 512.6)) + reward = Waypoint((677.1, 521.2)) + exit_ = Waypoint((684.6, 505.0)) + + self.clear_elite(enemy) + self.domain_reward(reward) + self.domain_single_exit(exit_) + # ===== End of generated waypoints ===== + + """ + Notes + Herta_SupplyZone_F2_X397Y239 is the same as Herta_SupplyZone_F2_X397Y233 + but for wrong spawn point detected + """ + # Best 3 predictions: [ + # ('Elite_Luofu_StargazerNavalia_F1_X617Y511', 0.338, (621.0, 507.0)), + # ('Elite_Luofu_ArtisanshipCommission_F1_X385Y494', 0.203, (329.2, 492.8)), + # ('Elite_Jarilo_SilvermaneGuardRestrictedZone_F1_X225Y425', 0.181, (224.8, 423.2)) + # ] diff --git a/route/rogue/route.json b/route/rogue/route.json index ffb40f57f..6f78834c5 100644 --- a/route/rogue/route.json +++ b/route/rogue/route.json @@ -912,6 +912,17 @@ ], "domain": "Combat" }, + { + "name": "Combat_Luofu_Cloudford_F1_X432Y685", + "route": "route.rogue.Combat.Luofu_Cloudford_F1:Luofu_Cloudford_F1_X432Y685", + "plane": "Luofu_Cloudford", + "floor": "F1", + "position": [ + 432.8, + 685.1 + ], + "domain": "Combat" + }, { "name": "Combat_Luofu_Cloudford_F1Rogue_X59Y405", "route": "route.rogue.Combat.Luofu_Cloudford_F1Rogue:Luofu_Cloudford_F1Rogue_X59Y405", @@ -1297,6 +1308,17 @@ ], "domain": "Elite" }, + { + "name": "Elite_Jarilo_CorridorofFadingEchoes_F1_X415Y953", + "route": "route.rogue.Elite.Jarilo_CorridorofFadingEchoes_F1:Jarilo_CorridorofFadingEchoes_F1_X415Y953", + "plane": "Jarilo_CorridorofFadingEchoes", + "floor": "F1", + "position": [ + 415.4, + 953.3 + ], + "domain": "Elite" + }, { "name": "Elite_Jarilo_CorridorofFadingEchoes_F1_X415Y947", "route": "route.rogue.Elite.Jarilo_CorridorofFadingEchoes_F1:Jarilo_CorridorofFadingEchoes_F1_X415Y947", @@ -1396,6 +1418,17 @@ ], "domain": "Elite" }, + { + "name": "Elite_Luofu_ArtisanshipCommission_F1_X391Y493", + "route": "route.rogue.Elite.Luofu_ArtisanshipCommission_F1:Luofu_ArtisanshipCommission_F1_X391Y493", + "plane": "Luofu_ArtisanshipCommission", + "floor": "F1", + "position": [ + 391.1, + 493.2 + ], + "domain": "Elite" + }, { "name": "Elite_Luofu_ArtisanshipCommission_F1_X504Y493", "route": "route.rogue.Elite.Luofu_ArtisanshipCommission_F1:Luofu_ArtisanshipCommission_F1_X504Y493", @@ -1418,6 +1451,17 @@ ], "domain": "Elite" }, + { + "name": "Elite_Luofu_Cloudford_F1_X342Y1003", + "route": "route.rogue.Elite.Luofu_Cloudford_F1:Luofu_Cloudford_F1_X342Y1003", + "plane": "Luofu_Cloudford", + "floor": "F1", + "position": [ + 342.3, + 1003.4 + ], + "domain": "Elite" + }, { "name": "Elite_Luofu_DivinationCommission_F2_X338Y345", "route": "route.rogue.Elite.Luofu_DivinationCommission_F2:Luofu_DivinationCommission_F2_X338Y345", @@ -1451,6 +1495,17 @@ ], "domain": "Elite" }, + { + "name": "Elite_Luofu_StargazerNavalia_F1_X617Y511", + "route": "route.rogue.Elite.Luofu_StargazerNavalia_F1:Luofu_StargazerNavalia_F1_X617Y511", + "plane": "Luofu_StargazerNavalia", + "floor": "F1", + "position": [ + 617.5, + 511.5 + ], + "domain": "Elite" + }, { "name": "Occurrence_Herta_StorageZone_F1_X273Y93", "route": "route.rogue.Occurrence.Herta_StorageZone_F1:Herta_StorageZone_F1_X273Y93", From c15e12226c301db3c479ea90a0f9f8e9fdff5566 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Tue, 14 May 2024 02:26:50 +0800 Subject: [PATCH 109/114] Fix: Pass rough corners in Jarilo_CorridorofFadingEchoes_F1_X201Y1071 --- .../Combat/Jarilo_CorridorofFadingEchoes_F1.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/route/rogue/Combat/Jarilo_CorridorofFadingEchoes_F1.py b/route/rogue/Combat/Jarilo_CorridorofFadingEchoes_F1.py index 004217fb9..080668a9e 100644 --- a/route/rogue/Combat/Jarilo_CorridorofFadingEchoes_F1.py +++ b/route/rogue/Combat/Jarilo_CorridorofFadingEchoes_F1.py @@ -31,6 +31,7 @@ class Route(RouteBase): | ----------- | -------------------------- | --------- | -------- | | spawn | Waypoint((201.2, 1071.4)), | 6.7 | 4 | | enemy1right | Waypoint((200.3, 1032.4)), | 342.0 | 343 | + | node1 | Waypoint((194.6, 1023.4)), | 109.3 | 294 | | enemy1left | Waypoint((168.6, 1022.3)), | 279.8 | 89 | | node2 | Waypoint((118.4, 1019.0)), | 282.9 | 285 | | enemy2left | Waypoint((105.2, 1012.0)), | 317.9 | 315 | @@ -46,6 +47,7 @@ class Route(RouteBase): Waypoint((103.4, 919.2)), end_rotation=4, left_door=Waypoint((98.8, 908.9)), right_door=Waypoint((111.4, 909.8))) enemy1right = Waypoint((200.3, 1032.4)) + node1 = Waypoint((194.6, 1023.4)) enemy1left = Waypoint((168.6, 1022.3)) node2 = Waypoint((118.4, 1019.0)) enemy2left = Waypoint((105.2, 1012.0)) @@ -57,20 +59,22 @@ class Route(RouteBase): # 1 self.rotation_set(315) self.clear_enemy( - enemy1right.set_threshold(5), - enemy1left.set_threshold(5), + enemy1right.set_threshold(3), + node1.set_threshold(3), + enemy1left.set_threshold(3), ) # 2 self.clear_enemy( - enemy1left.set_threshold(5), + enemy1left.set_threshold(3), node2.set_threshold(5), enemy2left, enemy2right, ) # 3 + self.rotation_set(0) self.clear_enemy( - node3.set_threshold(5), - enemy3.straight_run(), + node3.set_threshold(3), + enemy3, ) def Jarilo_CorridorofFadingEchoes_F1_X266Y457(self): From caf3af9973a1005ec3bbf406e738f0ed73d7e9f4 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Tue, 14 May 2024 02:35:39 +0800 Subject: [PATCH 110/114] Dev: Save tracking image on each waypoint --- tasks/map/control/control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/map/control/control.py b/tasks/map/control/control.py index 90d752fa3..bf99c8302 100644 --- a/tasks/map/control/control.py +++ b/tasks/map/control/control.py @@ -133,6 +133,7 @@ class MapControl(Combat, AimDetectorMixin): """ logger.hr('Goto', level=2) logger.info(f'Goto {waypoint}') + self.screenshot_tracking_add() self.waypoint = waypoint self.device.stuck_record_clear() self.device.click_record_clear() @@ -353,7 +354,6 @@ class MapControl(Combat, AimDetectorMixin): list[str]: A list of walk result """ logger.hr('Goto', level=1) - self.screenshot_tracking_add() self.map_A_timer.clear() self.map_E_timer.clear() self.map_run_2x_timer.clear() From 5f9ec0ed579379a800ff8daa611c78112f930821 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Tue, 14 May 2024 02:45:12 +0800 Subject: [PATCH 111/114] Fix: Multiple right doors are classified as left and right doors --- tasks/rogue/route/exit.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tasks/rogue/route/exit.py b/tasks/rogue/route/exit.py index c5d45ee48..cf7c21bc8 100644 --- a/tasks/rogue/route/exit.py +++ b/tasks/rogue/route/exit.py @@ -253,8 +253,17 @@ class RogueExit(CombatInteract): else: return None, results[0].matched_keyword else: - results = [r for d, r in sorted(zip(directions, results))] - return results[0].matched_keyword, results[-1].matched_keyword + left = [r for d, r in sorted(zip(directions, results)) if d < 0] + right = [r for d, r in sorted(zip(directions, results)) if d >= 0] + if len(left): + left = left[0].matched_keyword + else: + left = None + if len(right): + right = right[-1].matched_keyword + else: + right = None + return left, right def choose_door(self, left_door: MapPlane | None, right_door: MapPlane | None) -> str | None: """ From fdb61da22308adc4538d33b4813e66b9d4bd7a49 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Tue, 14 May 2024 02:45:58 +0800 Subject: [PATCH 112/114] Fix: Pass through door2 in Herta_SupplyZone_F2_X45Y369 --- route/rogue/Combat/Herta_SupplyZone_F2.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/route/rogue/Combat/Herta_SupplyZone_F2.py b/route/rogue/Combat/Herta_SupplyZone_F2.py index 95a9e606e..46357df1f 100644 --- a/route/rogue/Combat/Herta_SupplyZone_F2.py +++ b/route/rogue/Combat/Herta_SupplyZone_F2.py @@ -15,6 +15,7 @@ class Route(RouteBase): | enemy1 | Waypoint((46.2, 328.2)), | 12.6 | 8 | | item2 | Waypoint((42.4, 299.0)), | 352.8 | 348 | | door2 | Waypoint((46.4, 284.5)), | 4.2 | 1 | + | door2end | Waypoint((47.2, 274.8)), | 11.1 | 4 | | enemy2left | Waypoint((31.2, 248.8)), | 183.8 | 84 | | enemy2right | Waypoint((55.2, 247.2)), | 96.7 | 91 | | item3 | Waypoint((68.5, 226.5)), | 30.2 | 29 | @@ -32,6 +33,7 @@ class Route(RouteBase): enemy1 = Waypoint((46.2, 328.2)) item2 = Waypoint((42.4, 299.0)) door2 = Waypoint((46.4, 284.5)) + door2end = Waypoint((47.2, 274.8)) enemy2left = Waypoint((31.2, 248.8)) enemy2right = Waypoint((55.2, 247.2)) item3 = Waypoint((68.5, 226.5)) @@ -47,6 +49,7 @@ class Route(RouteBase): # self.clear_item(item2) self.clear_enemy( door2.set_threshold(3), + door2end.set_threshold(3), # Go through door enemy2left, enemy2right.straight_run(), @@ -58,6 +61,7 @@ class Route(RouteBase): self.clear_enemy( enemy3.straight_run(), ) + # ('Combat_Herta_SupplyZone_F2_X45Y369', 0.243, (57.2, 351.6)) def Herta_SupplyZone_F2_X397Y233(self): """ From 8885d0e791334784abe05c1c5c43e06dddb96cb2 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Tue, 14 May 2024 02:58:10 +0800 Subject: [PATCH 113/114] Fix: Scroll was never set if only one button is at bottom --- tasks/rogue/event/event.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tasks/rogue/event/event.py b/tasks/rogue/event/event.py index 97daf9ccc..d6aad912a 100644 --- a/tasks/rogue/event/event.py +++ b/tasks/rogue/event/event.py @@ -188,7 +188,11 @@ class RogueEvent(RogueUI): # Only one option, click directly if count == 1: if self.interval_is_reached(CHOOSE_OPTION, interval=2): - self.device.click(self.valid_options[0].prefix_icon) + button = self.valid_options[0].prefix_icon + # Option at bottom + if button.area[1] > 500 and SCROLL_OPTION.appear(main=self): + SCROLL_OPTION.set_bottom(main=self) + self.device.click(button) self.interval_reset(CHOOSE_OPTION, interval=2) return True From da52834bb4f6eff57cc2afbc50531f6757f96e95 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Tue, 14 May 2024 03:06:36 +0800 Subject: [PATCH 114/114] Fix: Special match Jarilo_BackwaterPass_F1_X613Y755 before Occurrence_Luofu_DivinationCommission_F2_X425Y791 --- tasks/rogue/route/loader.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tasks/rogue/route/loader.py b/tasks/rogue/route/loader.py index 55b32d27c..ecf78455c 100644 --- a/tasks/rogue/route/loader.py +++ b/tasks/rogue/route/loader.py @@ -196,6 +196,11 @@ class RouteLoader(RogueUI, MinimapWrapper, RouteLoader_, CharacterSwitch): # ('Combat_Luofu_ScalegorgeWaterscape_F1_X415Y261', 0.112, (371.8, 289.4)), # ('Combat_Herta_SupplyZone_F2_X45Y369', 0.104, (11.7, 367.6)) 'Combat_Luofu_DivinationCommission_F1_X97Y457', + # ('Occurrence_Jarilo_BackwaterPass_F1_X613Y755', 0.206, (611.3, 759.0)), + # ('Occurrence_Jarilo_BackwaterPass_F1_X611Y761', 0.206, (611.3, 759.0)), + # ('Occurrence_Luofu_DivinationCommission_F2_X425Y791', 0.105, (429.7, 791.6)) + 'Occurrence_Jarilo_BackwaterPass_F1_X613Y755', + 'Occurrence_Jarilo_BackwaterPass_F1_X611Y761', ] and similarity > 0.15: return True if route.name in [