diff --git a/assets/share/combat/fuel/EXTRACT_RESERVED_TRAILBLAZE_POWER.png b/assets/share/combat/fuel/EXTRACT_RESERVED_TRAILBLAZE_POWER.png new file mode 100644 index 000000000..35c84833b Binary files /dev/null and b/assets/share/combat/fuel/EXTRACT_RESERVED_TRAILBLAZE_POWER.png differ diff --git a/assets/share/combat/fuel/FUEL.SEARCH.png b/assets/share/combat/fuel/FUEL.SEARCH.png new file mode 100644 index 000000000..74c45cf5f Binary files /dev/null and b/assets/share/combat/fuel/FUEL.SEARCH.png differ diff --git a/assets/share/combat/fuel/FUEL.png b/assets/share/combat/fuel/FUEL.png new file mode 100644 index 000000000..2a7d0466d Binary files /dev/null and b/assets/share/combat/fuel/FUEL.png differ diff --git a/assets/share/combat/fuel/FUEL_ENTRANCE.png b/assets/share/combat/fuel/FUEL_ENTRANCE.png new file mode 100644 index 000000000..fd6211b55 Binary files /dev/null and b/assets/share/combat/fuel/FUEL_ENTRANCE.png differ diff --git a/assets/share/combat/fuel/FUEL_MINUS.png b/assets/share/combat/fuel/FUEL_MINUS.png new file mode 100644 index 000000000..ed2382221 Binary files /dev/null and b/assets/share/combat/fuel/FUEL_MINUS.png differ diff --git a/assets/share/combat/fuel/FUEL_PLUS.png b/assets/share/combat/fuel/FUEL_PLUS.png new file mode 100644 index 000000000..97bbce249 Binary files /dev/null and b/assets/share/combat/fuel/FUEL_PLUS.png differ diff --git a/assets/share/combat/fuel/FUEL_SELECTED.SEARCH.png b/assets/share/combat/fuel/FUEL_SELECTED.SEARCH.png new file mode 100644 index 000000000..74c45cf5f Binary files /dev/null and b/assets/share/combat/fuel/FUEL_SELECTED.SEARCH.png differ diff --git a/assets/share/combat/fuel/FUEL_SELECTED.png b/assets/share/combat/fuel/FUEL_SELECTED.png new file mode 100644 index 000000000..caf5132f4 Binary files /dev/null and b/assets/share/combat/fuel/FUEL_SELECTED.png differ diff --git a/assets/share/combat/fuel/FUEL_SLIDER.png b/assets/share/combat/fuel/FUEL_SLIDER.png new file mode 100644 index 000000000..ba1e610e9 Binary files /dev/null and b/assets/share/combat/fuel/FUEL_SLIDER.png differ diff --git a/assets/share/combat/fuel/OCR_EXTRACT_RESERVED_TRAILBLAZE_POWER_COUNT.png b/assets/share/combat/fuel/OCR_EXTRACT_RESERVED_TRAILBLAZE_POWER_COUNT.png new file mode 100644 index 000000000..dfb042972 Binary files /dev/null and b/assets/share/combat/fuel/OCR_EXTRACT_RESERVED_TRAILBLAZE_POWER_COUNT.png differ diff --git a/assets/share/combat/fuel/OCR_FUEL.png b/assets/share/combat/fuel/OCR_FUEL.png new file mode 100644 index 000000000..e584be709 Binary files /dev/null and b/assets/share/combat/fuel/OCR_FUEL.png differ diff --git a/assets/share/combat/fuel/OCR_FUEL_COUNT.png b/assets/share/combat/fuel/OCR_FUEL_COUNT.png new file mode 100644 index 000000000..4f304588a Binary files /dev/null and b/assets/share/combat/fuel/OCR_FUEL_COUNT.png differ diff --git a/assets/share/combat/fuel/OCR_RESERVED_TRAILBLAZE_POWER.png b/assets/share/combat/fuel/OCR_RESERVED_TRAILBLAZE_POWER.png new file mode 100644 index 000000000..591b0d209 Binary files /dev/null and b/assets/share/combat/fuel/OCR_RESERVED_TRAILBLAZE_POWER.png differ diff --git a/assets/share/combat/fuel/RESERVED_MINUS.png b/assets/share/combat/fuel/RESERVED_MINUS.png new file mode 100644 index 000000000..674a78685 Binary files /dev/null and b/assets/share/combat/fuel/RESERVED_MINUS.png differ diff --git a/assets/share/combat/fuel/RESERVED_PLUS.png b/assets/share/combat/fuel/RESERVED_PLUS.png new file mode 100644 index 000000000..d6fe7bf76 Binary files /dev/null and b/assets/share/combat/fuel/RESERVED_PLUS.png differ diff --git a/assets/share/combat/fuel/RESERVED_SLIDER.png b/assets/share/combat/fuel/RESERVED_SLIDER.png new file mode 100644 index 000000000..8de0e9f36 Binary files /dev/null and b/assets/share/combat/fuel/RESERVED_SLIDER.png differ diff --git a/assets/share/combat/fuel/RESERVED_TRAILBLAZE_POWER_ENTRANCE.png b/assets/share/combat/fuel/RESERVED_TRAILBLAZE_POWER_ENTRANCE.png new file mode 100644 index 000000000..f724a7998 Binary files /dev/null and b/assets/share/combat/fuel/RESERVED_TRAILBLAZE_POWER_ENTRANCE.png differ diff --git a/assets/share/combat/fuel/USING_FUEL.png b/assets/share/combat/fuel/USING_FUEL.png new file mode 100644 index 000000000..b9d962e7a Binary files /dev/null and b/assets/share/combat/fuel/USING_FUEL.png differ diff --git a/config/template.json b/config/template.json index 32ab57133..da1b72da5 100644 --- a/config/template.json +++ b/config/template.json @@ -111,6 +111,11 @@ "DungeonDouble": {}, "EchoOfWar": {}, "SimulatedUniverse": {} + }, + "TrailblazePower": { + "ExtractReservedTrailblazePower": false, + "UseFuel": false, + "FuelReserve": 5 } }, "DailyQuest": { @@ -140,6 +145,7 @@ "BattlePassQuestEchoOfWar": {}, "BattlePassQuestCredits": {}, "BattlePassQuestSynthesizeConsumables": {}, + "BattlePassQuestStagnantShadow": {}, "BattlePassQuestCavernOfCorrosion": {}, "BattlePassQuestTrailblazePower": {} } @@ -252,6 +258,11 @@ "DungeonSupport": { "Use": "when_daily", "Character": "FirstCharacter" + }, + "TrailblazePower": { + "ExtractReservedTrailblazePower": false, + "UseFuel": false, + "FuelReserve": 5 } }, "Daemon": { diff --git a/module/config/argument/args.json b/module/config/argument/args.json index ff7a1a043..cfc1ed4f6 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -738,6 +738,20 @@ "order": 6, "color": "#8fb5fe" } + }, + "TrailblazePower": { + "ExtractReservedTrailblazePower": { + "type": "checkbox", + "value": false + }, + "UseFuel": { + "type": "checkbox", + "value": false + }, + "FuelReserve": { + "type": "input", + "value": 5 + } } }, "DailyQuest": { @@ -858,6 +872,12 @@ "display": "display", "stored": "StoredBattlePassQuestSynthesizeConsumables" }, + "BattlePassQuestStagnantShadow": { + "type": "stored", + "value": {}, + "display": "display", + "stored": "StoredBattlePassQuestStagnantShadow" + }, "BattlePassQuestCavernOfCorrosion": { "type": "stored", "value": {}, @@ -1532,6 +1552,20 @@ "Yukong" ] } + }, + "TrailblazePower": { + "ExtractReservedTrailblazePower": { + "type": "checkbox", + "value": false + }, + "UseFuel": { + "type": "checkbox", + "value": false + }, + "FuelReserve": { + "type": "input", + "value": 5 + } } }, "Daemon": { diff --git a/module/config/argument/argument.yaml b/module/config/argument/argument.yaml index f98b1d85b..518883531 100644 --- a/module/config/argument/argument.yaml +++ b/module/config/argument/argument.yaml @@ -103,6 +103,10 @@ Dungeon: Team: value: 1 option: [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] +TrailblazePower: + ExtractReservedTrailblazePower: false + UseFuel: false + FuelReserve: 5 DungeonSupport: Use: value: when_daily @@ -179,6 +183,9 @@ BattlePassStorage: BattlePassQuestSynthesizeConsumables: stored: StoredBattlePassQuestSynthesizeConsumables display: display + BattlePassQuestStagnantShadow: + stored: StoredBattlePassQuestStagnantShadow + display: display BattlePassQuestCavernOfCorrosion: stored: StoredBattlePassQuestCavernOfCorrosion display: display diff --git a/module/config/argument/stored.json b/module/config/argument/stored.json index 4886e0a0d..857b38430 100644 --- a/module/config/argument/stored.json +++ b/module/config/argument/stored.json @@ -715,7 +715,9 @@ "quest4": "", "quest5": "", "quest6": "", - "total": 6, + "quest7": "", + "quest8": "", + "total": 8, "value": 0 }, "order": 0, @@ -806,6 +808,19 @@ "order": 0, "color": "#777777" }, + "BattlePassQuestStagnantShadow": { + "name": "BattlePassQuestStagnantShadow", + "path": "BattlePass.BattlePassStorage.BattlePassQuestStagnantShadow", + "i18n": "BattlePassStorage.BattlePassQuestStagnantShadow.name", + "stored": "StoredBattlePassQuestStagnantShadow", + "attrs": { + "time": "2020-01-01 00:00:00", + "total": 3, + "value": 0 + }, + "order": 0, + "color": "#777777" + }, "BattlePassQuestCavernOfCorrosion": { "name": "BattlePassQuestCavernOfCorrosion", "path": "BattlePass.BattlePassStorage.BattlePassQuestCavernOfCorrosion", diff --git a/module/config/argument/task.yaml b/module/config/argument/task.yaml index 38d173565..c71f2c3df 100644 --- a/module/config/argument/task.yaml +++ b/module/config/argument/task.yaml @@ -28,6 +28,7 @@ Daily: - Dungeon - DungeonSupport - DungeonStorage + - TrailblazePower DailyQuest: - Scheduler - DailyStorage @@ -64,6 +65,7 @@ Weekly: - Scheduler - Ornament - DungeonSupport + - TrailblazePower # ==================== Tool ==================== diff --git a/module/config/config.py b/module/config/config.py index 61d8cb5d7..fc5a680fe 100644 --- a/module/config/config.py +++ b/module/config/config.py @@ -516,7 +516,17 @@ class AzurLaneConfig(ConfigUpdater, ManualConfig, GeneratedConfig, ConfigWatcher self.stored.DailyActivity.clear() if self.stored.DailyQuest.is_expired(): logger.info('DailyQuest expired') - self.stored.DailyQuest.clear() + q = self.stored.DailyQuest + q.clear() + # Assume fixed quests + q.write_quests([ + 'Complete_1_Daily_Mission', + 'Log_in_to_the_game', + 'Dispatch_1_assignments', + 'Complete_Divergent_Universe_or_Simulated_Universe_1_times', + 'Obtain_victory_in_combat_with_Support_Characters_1_times', + 'Consume_120_Trailblaze_Power', + ]) def update_battle_pass_quests(self): """ diff --git a/module/config/config_generated.py b/module/config/config_generated.py index 8bc01c1ef..f9dd18ef4 100644 --- a/module/config/config_generated.py +++ b/module/config/config_generated.py @@ -46,6 +46,11 @@ class GeneratedConfig: 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, Cavern_of_Corrosion_Path_of_Cavalier Dungeon_Team = 1 # 1, 2, 3, 4, 5, 6, 7, 8, 9 + # Group `TrailblazePower` + TrailblazePower_ExtractReservedTrailblazePower = False + TrailblazePower_UseFuel = False + TrailblazePower_FuelReserve = 5 + # Group `DungeonSupport` DungeonSupport_Use = 'when_daily' # always_use, when_daily, do_not_use DungeonSupport_Character = 'FirstCharacter' # FirstCharacter, Acheron, Argenti, Arlan, Asta, Aventurine, Bailu, BlackSwan, Blade, Boothill, Bronya, Clara, DanHeng, DanHengImbibitorLunae, DrRatio, Firefly, FuXuan, Gallagher, Gepard, Guinaifen, Hanya, Herta, Himeko, Hook, Huohuo, JingYuan, Jingliu, Kafka, Luka, Luocha, Lynx, March7th, Misha, Natasha, Pela, Qingque, Robin, RuanMei, Sampo, Seele, Serval, SilverWolf, Sparkle, Sushang, Tingyun, TopazNumby, TrailblazerDestruction, TrailblazerHarmony, TrailblazerPreservation, Welt, Xueyi, Yanqing, Yukong @@ -130,6 +135,7 @@ class GeneratedConfig: BattlePassStorage_BattlePassQuestEchoOfWar = {} BattlePassStorage_BattlePassQuestCredits = {} BattlePassStorage_BattlePassQuestSynthesizeConsumables = {} + BattlePassStorage_BattlePassQuestStagnantShadow = {} BattlePassStorage_BattlePassQuestCavernOfCorrosion = {} BattlePassStorage_BattlePassQuestTrailblazePower = {} diff --git a/module/config/config_updater.py b/module/config/config_updater.py index 2f10e12cf..0b5d8cf1c 100644 --- a/module/config/config_updater.py +++ b/module/config/config_updater.py @@ -828,6 +828,8 @@ class ConfigUpdater: Yields: str: Arg path that should be hidden """ + if deep_get(data, 'Dungeon.TrailblazePower.UseFuel') == False: + yield 'Dungeon.TrailblazePower.UseFuelUntilRemainCount' if deep_get(data, 'Rogue.RogueBlessing.PresetBlessingFilter') != 'custom': yield 'Rogue.RogueBlessing.CustomBlessingFilter' if deep_get(data, 'Rogue.RogueBlessing.PresetResonanceFilter') != 'custom': diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index 2fe47eee4..2acdccb59 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -345,6 +345,24 @@ "9": "9" } }, + "TrailblazePower": { + "_info": { + "name": "TrailblazePower Settings", + "help": "" + }, + "ExtractReservedTrailblazePower": { + "name": "Extract reserve exploration power", + "help": "" + }, + "UseFuel": { + "name": "Use Fuel", + "help": "" + }, + "FuelReserve": { + "name": "Reserve X Fuel", + "help": "" + } + }, "DungeonSupport": { "_info": { "name": "Support Settings", @@ -740,6 +758,10 @@ "name": "Synthesize Consumables 10 time(s)", "help": "" }, + "BattlePassQuestStagnantShadow": { + "name": "Clear Stagnant Shadow 3 time(s)", + "help": "" + }, "BattlePassQuestCavernOfCorrosion": { "name": "Clear Cavern of Corrosion 8 time(s)", "help": "" diff --git a/module/config/i18n/es-ES.json b/module/config/i18n/es-ES.json index e4057ac0c..833fadeca 100644 --- a/module/config/i18n/es-ES.json +++ b/module/config/i18n/es-ES.json @@ -345,6 +345,24 @@ "9": "9" } }, + "TrailblazePower": { + "_info": { + "name": "Ajustes de Poder Trazacaminos", + "help": "" + }, + "ExtractReservedTrailblazePower": { + "name": "Extraer reserva de poder de trazacaminos", + "help": "" + }, + "UseFuel": { + "name": "Usar combustible", + "help": "" + }, + "FuelReserve": { + "name": "Reserva X Combustible", + "help": "" + } + }, "DungeonSupport": { "_info": { "name": "Ajustes de Apoyo", @@ -740,6 +758,10 @@ "name": "Sintetiza consumibles 10 veces", "help": "" }, + "BattlePassQuestStagnantShadow": { + "name": "Completa Sombra paralizada 3 veces", + "help": "" + }, "BattlePassQuestCavernOfCorrosion": { "name": "Completa Caverna de la corrosión 8 veces", "help": "" diff --git a/module/config/i18n/ja-JP.json b/module/config/i18n/ja-JP.json index 31d977902..75952b0b7 100644 --- a/module/config/i18n/ja-JP.json +++ b/module/config/i18n/ja-JP.json @@ -345,6 +345,24 @@ "9": "9" } }, + "TrailblazePower": { + "_info": { + "name": "TrailblazePower._info.name", + "help": "TrailblazePower._info.help" + }, + "ExtractReservedTrailblazePower": { + "name": "TrailblazePower.ExtractReservedTrailblazePower.name", + "help": "TrailblazePower.ExtractReservedTrailblazePower.help" + }, + "UseFuel": { + "name": "TrailblazePower.UseFuel.name", + "help": "TrailblazePower.UseFuel.help" + }, + "FuelReserve": { + "name": "TrailblazePower.FuelReserve.name", + "help": "TrailblazePower.FuelReserve.help" + } + }, "DungeonSupport": { "_info": { "name": "DungeonSupport._info.name", @@ -740,6 +758,10 @@ "name": "BattlePassStorage.BattlePassQuestSynthesizeConsumables.name", "help": "BattlePassStorage.BattlePassQuestSynthesizeConsumables.help" }, + "BattlePassQuestStagnantShadow": { + "name": "BattlePassStorage.BattlePassQuestStagnantShadow.name", + "help": "BattlePassStorage.BattlePassQuestStagnantShadow.help" + }, "BattlePassQuestCavernOfCorrosion": { "name": "BattlePassStorage.BattlePassQuestCavernOfCorrosion.name", "help": "BattlePassStorage.BattlePassQuestCavernOfCorrosion.help" diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index 3c1799431..b00f69610 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -345,6 +345,24 @@ "9": "9" } }, + "TrailblazePower": { + "_info": { + "name": "开拓力设置", + "help": "" + }, + "ExtractReservedTrailblazePower": { + "name": "取出后备开拓力", + "help": "" + }, + "UseFuel": { + "name": "使用燃料", + "help": "" + }, + "FuelReserve": { + "name": "保留 X 燃料", + "help": "" + } + }, "DungeonSupport": { "_info": { "name": "支援设置", @@ -740,6 +758,10 @@ "name": "累计合成消耗品10次", "help": "" }, + "BattlePassQuestStagnantShadow": { + "name": "完成3次「凝滞虚影」", + "help": "" + }, "BattlePassQuestCavernOfCorrosion": { "name": "完成8次「侵蚀隧洞」", "help": "" diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index 3a2694342..ad1c59e42 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -345,6 +345,24 @@ "9": "9" } }, + "TrailblazePower": { + "_info": { + "name": "開拓力設定", + "help": "" + }, + "ExtractReservedTrailblazePower": { + "name": "取出後備開拓力", + "help": "" + }, + "UseFuel": { + "name": "使用燃料", + "help": "" + }, + "FuelReserve": { + "name": "保留 X 燃料", + "help": "" + } + }, "DungeonSupport": { "_info": { "name": "支援設定", @@ -740,6 +758,10 @@ "name": "累计合成消耗品10次", "help": "" }, + "BattlePassQuestStagnantShadow": { + "name": "完成3次「凝滯虛影」", + "help": "" + }, "BattlePassQuestCavernOfCorrosion": { "name": "完成8次「侵蚀隧洞」", "help": "" diff --git a/module/config/stored/classes.py b/module/config/stored/classes.py index ae0af52e7..8d5073e61 100644 --- a/module/config/stored/classes.py +++ b/module/config/stored/classes.py @@ -234,8 +234,10 @@ class StoredDaily(StoredCounter, StoredExpiredAt0400): quest4 = '' quest5 = '' quest6 = '' + quest7 = '' + quest8 = '' - FIXED_TOTAL = 6 + FIXED_TOTAL = 8 def load_quests(self): """ @@ -245,7 +247,8 @@ class StoredDaily(StoredCounter, StoredExpiredAt0400): # DailyQuest should be lazy loaded from tasks.daily.keywords import DailyQuest quests = [] - for name in [self.quest1, self.quest2, self.quest3, self.quest4, self.quest5, self.quest6]: + for name in [self.quest1, self.quest2, self.quest3, self.quest4, + self.quest5, self.quest6, self.quest7, self.quest8]: if not name: continue try: @@ -288,6 +291,14 @@ class StoredDaily(StoredCounter, StoredExpiredAt0400): self.quest6 = quests[5] except IndexError: self.quest6 = '' + try: + self.quest7 = quests[6] + except IndexError: + self.quest7 = '' + try: + self.quest8 = quests[7] + except IndexError: + self.quest8 = '' def clear(self): with self._config.multi_set(): @@ -297,6 +308,8 @@ class StoredDaily(StoredCounter, StoredExpiredAt0400): self.quest4 = '' self.quest5 = '' self.quest6 = '' + self.quest7 = '' + self.quest8 = '' class StoredDungeonDouble(StoredExpiredAt0400): @@ -411,9 +424,8 @@ class StoredBattlePassQuestSynthesizeConsumables(StoredCounter): FIXED_TOTAL = 10 -# Not exists on client side -# class StoredBattlePassQuestStagnantShadow(StoredCounter): -# FIXED_TOTAL = 8 +class StoredBattlePassQuestStagnantShadow(StoredCounter): + FIXED_TOTAL = 3 class StoredBattlePassQuestCavernOfCorrosion(StoredCounter): diff --git a/module/config/stored/stored_generated.py b/module/config/stored/stored_generated.py index a4a4d4fb2..25489d7b0 100644 --- a/module/config/stored/stored_generated.py +++ b/module/config/stored/stored_generated.py @@ -6,6 +6,7 @@ from module.config.stored.classes import ( StoredBattlePassQuestCavernOfCorrosion, StoredBattlePassQuestCredits, StoredBattlePassQuestEchoOfWar, + StoredBattlePassQuestStagnantShadow, StoredBattlePassQuestSynthesizeConsumables, StoredBattlePassQuestTrailblazePower, StoredBattlePassSimulatedUniverse, @@ -97,6 +98,7 @@ class StoredGenerated: BattlePassQuestEchoOfWar = StoredBattlePassQuestEchoOfWar("BattlePass.BattlePassStorage.BattlePassQuestEchoOfWar") BattlePassQuestCredits = StoredBattlePassQuestCredits("BattlePass.BattlePassStorage.BattlePassQuestCredits") BattlePassQuestSynthesizeConsumables = StoredBattlePassQuestSynthesizeConsumables("BattlePass.BattlePassStorage.BattlePassQuestSynthesizeConsumables") + BattlePassQuestStagnantShadow = StoredBattlePassQuestStagnantShadow("BattlePass.BattlePassStorage.BattlePassQuestStagnantShadow") BattlePassQuestCavernOfCorrosion = StoredBattlePassQuestCavernOfCorrosion("BattlePass.BattlePassStorage.BattlePassQuestCavernOfCorrosion") BattlePassQuestTrailblazePower = StoredBattlePassQuestTrailblazePower("BattlePass.BattlePassStorage.BattlePassQuestTrailblazePower") Assignment = StoredAssignment("Assignment.Assignment.Assignment") diff --git a/tasks/battle_pass/battle_pass.py b/tasks/battle_pass/battle_pass.py index c81f1343a..000c3ce3f 100644 --- a/tasks/battle_pass/battle_pass.py +++ b/tasks/battle_pass/battle_pass.py @@ -354,6 +354,8 @@ class BattlePassUI(UI): self.config.stored.BattlePassQuestCredits, KEYWORDS_BATTLE_PASS_QUEST.Synthesize_Consumables_1_times: self.config.stored.BattlePassQuestSynthesizeConsumables, + KEYWORDS_BATTLE_PASS_QUEST.Clear_Stagnant_Shadow_1_times: + self.config.stored.BattlePassQuestStagnantShadow, KEYWORDS_BATTLE_PASS_QUEST.Clear_Cavern_of_Corrosion_1_times: self.config.stored.BattlePassQuestCavernOfCorrosion, KEYWORDS_BATTLE_PASS_QUEST.Consume_a_total_of_1_Trailblaze_Power_1400_Trailblazer_Power_max: diff --git a/tasks/combat/assets/assets_combat_fuel.py b/tasks/combat/assets/assets_combat_fuel.py new file mode 100644 index 000000000..c34ebee6b --- /dev/null +++ b/tasks/combat/assets/assets_combat_fuel.py @@ -0,0 +1,165 @@ +from module.base.button import Button, ButtonWrapper + +# This file was auto-generated, do not modify it manually. To generate: +# ``` python -m dev_tools.button_extract ``` + +EXTRACT_RESERVED_TRAILBLAZE_POWER = ButtonWrapper( + name='EXTRACT_RESERVED_TRAILBLAZE_POWER', + share=Button( + file='./assets/share/combat/fuel/EXTRACT_RESERVED_TRAILBLAZE_POWER.png', + area=(909, 506, 929, 526), + search=(889, 486, 949, 546), + color=(91, 91, 91), + button=(909, 506, 929, 526), + ), +) +FUEL = ButtonWrapper( + name='FUEL', + share=Button( + file='./assets/share/combat/fuel/FUEL.png', + area=(592, 276, 688, 366), + search=(474, 271, 811, 396), + color=(123, 96, 134), + button=(592, 276, 688, 366), + ), +) +FUEL_ENTRANCE = ButtonWrapper( + name='FUEL_ENTRANCE', + share=Button( + file='./assets/share/combat/fuel/FUEL_ENTRANCE.png', + area=(1035, 26, 1056, 48), + search=(1015, 6, 1076, 68), + color=(188, 180, 226), + button=(1035, 26, 1056, 48), + ), +) +FUEL_MINUS = ButtonWrapper( + name='FUEL_MINUS', + share=Button( + file='./assets/share/combat/fuel/FUEL_MINUS.png', + area=(472, 425, 510, 450), + search=(452, 405, 530, 470), + color=(236, 236, 236), + button=(472, 425, 510, 450), + ), +) +FUEL_PLUS = ButtonWrapper( + name='FUEL_PLUS', + share=Button( + file='./assets/share/combat/fuel/FUEL_PLUS.png', + area=(967, 426, 1005, 449), + search=(947, 406, 1025, 469), + color=(232, 232, 232), + button=(967, 426, 1005, 449), + ), +) +FUEL_SELECTED = ButtonWrapper( + name='FUEL_SELECTED', + share=Button( + file='./assets/share/combat/fuel/FUEL_SELECTED.png', + area=(587, 271, 692, 368), + search=(474, 271, 811, 396), + color=(136, 112, 144), + button=(587, 271, 692, 368), + ), +) +FUEL_SLIDER = ButtonWrapper( + name='FUEL_SLIDER', + share=Button( + file='./assets/share/combat/fuel/FUEL_SLIDER.png', + area=(561, 434, 916, 441), + search=(541, 414, 936, 461), + color=(215, 185, 154), + button=(561, 434, 916, 441), + ), +) +OCR_EXTRACT_RESERVED_TRAILBLAZE_POWER_COUNT = ButtonWrapper( + name='OCR_EXTRACT_RESERVED_TRAILBLAZE_POWER_COUNT', + share=Button( + file='./assets/share/combat/fuel/OCR_EXTRACT_RESERVED_TRAILBLAZE_POWER_COUNT.png', + area=(425, 415, 688, 436), + search=(405, 395, 708, 456), + color=(192, 192, 192), + button=(425, 415, 688, 436), + ), +) +OCR_FUEL = ButtonWrapper( + name='OCR_FUEL', + share=Button( + file='./assets/share/combat/fuel/OCR_FUEL.png', + area=(605, 369, 678, 386), + search=(585, 349, 698, 406), + color=(66, 66, 66), + button=(605, 369, 678, 386), + ), +) +OCR_FUEL_COUNT = ButtonWrapper( + name='OCR_FUEL_COUNT', + share=Button( + file='./assets/share/combat/fuel/OCR_FUEL_COUNT.png', + area=(686, 409, 881, 425), + search=(666, 389, 901, 445), + color=(205, 205, 205), + button=(686, 409, 881, 425), + ), +) +OCR_RESERVED_TRAILBLAZE_POWER = ButtonWrapper( + name='OCR_RESERVED_TRAILBLAZE_POWER', + share=Button( + file='./assets/share/combat/fuel/OCR_RESERVED_TRAILBLAZE_POWER.png', + area=(883, 29, 992, 44), + search=(863, 9, 1012, 64), + color=(51, 65, 65), + button=(883, 29, 992, 44), + ), +) +RESERVED_MINUS = ButtonWrapper( + name='RESERVED_MINUS', + share=Button( + file='./assets/share/combat/fuel/RESERVED_MINUS.png', + area=(248, 474, 281, 498), + search=(228, 454, 301, 518), + color=(238, 238, 238), + button=(248, 474, 281, 498), + ), +) +RESERVED_PLUS = ButtonWrapper( + name='RESERVED_PLUS', + share=Button( + file='./assets/share/combat/fuel/RESERVED_PLUS.png', + area=(938, 475, 974, 498), + search=(918, 455, 994, 518), + color=(232, 232, 232), + button=(938, 475, 974, 498), + ), +) +RESERVED_SLIDER = ButtonWrapper( + name='RESERVED_SLIDER', + share=Button( + file='./assets/share/combat/fuel/RESERVED_SLIDER.png', + area=(334, 483, 873, 489), + search=(314, 463, 893, 509), + color=(212, 173, 130), + button=(334, 483, 873, 489), + ), +) +RESERVED_TRAILBLAZE_POWER_ENTRANCE = ButtonWrapper( + name='RESERVED_TRAILBLAZE_POWER_ENTRANCE', + share=Button( + file='./assets/share/combat/fuel/RESERVED_TRAILBLAZE_POWER_ENTRANCE.png', + area=(895, 26, 916, 48), + search=(875, 6, 936, 68), + color=(154, 213, 214), + button=(895, 26, 916, 48), + ), +) +USING_FUEL = ButtonWrapper( + name='USING_FUEL', + share=Button( + file='./assets/share/combat/fuel/USING_FUEL.png', + area=(263, 265, 363, 365), + search=(243, 245, 383, 385), + color=(161, 116, 129), + button=(263, 265, 363, 365), + ), +) diff --git a/tasks/combat/combat.py b/tasks/combat/combat.py index c5a1bb1e2..538aff780 100644 --- a/tasks/combat/combat.py +++ b/tasks/combat/combat.py @@ -5,6 +5,7 @@ from tasks.combat.assets.assets_combat_finish import COMBAT_AGAIN, COMBAT_EXIT from tasks.combat.assets.assets_combat_interact import DUNGEON_COMBAT_INTERACT from tasks.combat.assets.assets_combat_prepare import COMBAT_PREPARE from tasks.combat.assets.assets_combat_team import COMBAT_TEAM_PREPARE, COMBAT_TEAM_SUPPORT +from tasks.combat.fuel import Fuel from tasks.combat.interact import CombatInteract from tasks.combat.obtain import CombatObtain from tasks.combat.prepare import CombatPrepare @@ -17,7 +18,7 @@ from tasks.map.control.joystick import MapControlJoystick class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSupport, CombatSkill, CombatObtain, - MapControlJoystick): + MapControlJoystick, Fuel): dungeon: DungeonList | None = None is_doing_planner: bool = False @@ -53,8 +54,7 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSuppo # Check limits if self.config.stored.TrailblazePower.value < self.combat_wave_cost: - logger.info('Trailblaze power exhausted, cannot continue combat') - return False + return self._try_get_more_trablaize_power(self.config.stored.TrailblazePower.value, self.combat_wave_cost) if self.combat_waves <= 0: logger.info('Combat wave limited, cannot continue combat') return False @@ -221,8 +221,7 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSuppo logger.info(f'Current has {current}, combat costs {self.combat_wave_cost}, can run again') return True else: - logger.info(f'Current has {current}, combat costs {self.combat_wave_cost}, can not run again') - return False + return self._try_get_more_trablaize_power(current, self.combat_wave_cost * self.combat_waves) elif self.combat_wave_cost <= 0: logger.info(f'Free combat, combat costs {self.combat_wave_cost}, can not run again') return False @@ -231,8 +230,25 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSuppo logger.info(f'Current has {current}, combat costs {self.combat_wave_cost}, can run again') return True else: - logger.info(f'Current has {current}, combat costs {self.combat_wave_cost}, can not run again') - return False + return self._try_get_more_trablaize_power(current, self.combat_wave_cost * self.combat_waves) + + def _try_get_more_trablaize_power(self, current, cost): + if self.config.TrailblazePower_ExtractReservedTrailblazePower: + logger.info('Extract reserved trailblaze power to get more trailblaze power') + if self.extract_reserved_trailblaze_power(current): + self.combat_get_trailblaze_power() + self.get_interval_timer(COMBAT_EXIT).wait() + if self.config.TrailblazePower_UseFuel: + logger.info('Use fuel to get more trailblaze power') + if self.use_fuel(current): + self.combat_get_trailblaze_power() + self.get_interval_timer(COMBAT_AGAIN).wait() + + if current >= cost: + return True + else: + logger.info(f'Current has {current}, combat costs {self.combat_wave_cost}, can not run again') + return False def _combat_should_reenter(self): """ diff --git a/tasks/combat/fuel.py b/tasks/combat/fuel.py new file mode 100644 index 000000000..12a494a70 --- /dev/null +++ b/tasks/combat/fuel.py @@ -0,0 +1,209 @@ +import module.config.server as server +from module.base.timer import Timer +from module.base.utils import area_offset, crop +from module.logger import logger +from module.ocr.ocr import Digit +from tasks.base.assets.assets_base_popup import GET_REWARD, POPUP_CANCEL, POPUP_CONFIRM +from tasks.base.ui import UI +from tasks.combat.assets.assets_combat_finish import COMBAT_AGAIN, COMBAT_EXIT +from tasks.combat.assets.assets_combat_fuel import * +from tasks.combat.assets.assets_combat_prepare import COMBAT_PREPARE +from tasks.item.slider import Slider + + +class Fuel(UI): + fuel_trailblaze_power = 60 + + def _use_fuel_finish(self): + """ + Two possible finish states after using fuel/extract trailblaze power: + 1. COMBAT_PREPARE + 2. COMBAT_AGAIN + """ + if self.appear(COMBAT_AGAIN): + if self.image_color_count(COMBAT_AGAIN, color=(227, 227, 228), threshold=221, count=50): + logger.info(f'Use fuel finished at COMBAT_AGAIN') + return True + if self.appear(COMBAT_PREPARE): + if self.image_color_count(COMBAT_PREPARE.button, color=(230, 230, 230), threshold=240, count=400): + logger.info(f'Use fuel finished at COMBAT_AGAIN') + return True + return False + + def _fuel_confirm(self, skip_first_screenshot=True): + """ + Pages: + in: fuel popup + out: _use_fuel_finish + """ + logger.info('Fuel confirm') + self.interval_clear([POPUP_CONFIRM, POPUP_CANCEL, GET_REWARD]) + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + if self._use_fuel_finish(): + break + if self.handle_popup_confirm(): + continue + if self.handle_reward(): + continue + + self._fuel_wait_leave() + + def _fuel_cancel(self, skip_first_screenshot=True): + """ + Pages: + in: fuel popup + out: _use_fuel_finish + """ + logger.info('Fuel cancel') + self.interval_clear([POPUP_CONFIRM, POPUP_CANCEL, GET_REWARD]) + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + if self._use_fuel_finish(): + break + if self.handle_popup_cancel(): + continue + if self.handle_reward(): + continue + + self._fuel_wait_leave() + + def _fuel_wait_leave(self): + # Blur disappears before popup + # so there's a short period of time that COMBAT_AGAIN is unclickable + # This is equivalent to poor sleep + timer = self.get_interval_timer(COMBAT_AGAIN, interval=5, renew=True) + timer.set_current(4.4) + timer = self.get_interval_timer(COMBAT_EXIT, interval=5, renew=True) + timer.set_current(4.4) + + def extract_reserved_trailblaze_power(self, current, skip_first_screenshot=True): + """ + Extract reserved trailblaze power from previous combat. + + Returns: + bool: If extracted + """ + logger.info('Extract reserved trailblaze power') + reserved = Digit(OCR_RESERVED_TRAILBLAZE_POWER).ocr_single_line(self.device.image) + if reserved <= 0: + logger.info('No reserved trailblaze power') + return False + + self.interval_clear([POPUP_CONFIRM, POPUP_CANCEL, GET_REWARD]) + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + if self.appear(POPUP_CONFIRM): + break + if self.appear_then_click(EXTRACT_RESERVED_TRAILBLAZE_POWER): + continue + if self.appear_then_click(RESERVED_TRAILBLAZE_POWER_ENTRANCE): + continue + + count = min(reserved, self.config.stored.TrailblazePower.FIXED_TOTAL - current) + logger.info(f'Having {reserved} reserved, going to use {count}') + self.set_reserved_trailblaze_power(count, total=reserved) + self._fuel_confirm() + return True + + def set_reserved_trailblaze_power(self, count, total): + slider = Slider(main=self, slider=RESERVED_SLIDER) + slider.set(count, total) + self.ui_ensure_index( + count, letter=Digit(OCR_EXTRACT_RESERVED_TRAILBLAZE_POWER_COUNT, lang=server.lang), + next_button=RESERVED_PLUS, prev_button=RESERVED_MINUS, + skip_first_screenshot=True + ) + + def set_fuel_count(self, count): + slider = Slider(main=self, slider=FUEL_SLIDER) + # Can only use 5 fuel at one time + slider.set(count, 5) + self.ui_ensure_index( + count, letter=Digit(OCR_FUEL_COUNT, lang=server.lang), + next_button=FUEL_PLUS, prev_button=FUEL_MINUS, + skip_first_screenshot=True + ) + + def use_fuel(self, current, skip_first_screenshot=True): + """ + Args: + current: + skip_first_screenshot: + + Returns: + bool: If used + """ + limit = self.config.stored.TrailblazePower.FIXED_TOTAL + use = (limit - current) // self.fuel_trailblaze_power + if use == 0: + logger.info(f"Current trailblaze power is near {limit}, no need to use fuel") + return False + + logger.info("Use Fuel") + + timeout = Timer(1, count=3) + has_fuel = False + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + if self.appear(FUEL_SELECTED): + logger.info('Fuel selected') + break + if self.appear(POPUP_CONFIRM): + timeout.start() + if self.appear(FUEL_SELECTED) or self.appear(FUEL): + has_fuel = True + if not has_fuel and timeout.reached(): + logger.info("No fuel found") + return False + if self.appear_then_click(FUEL): + has_fuel = True + continue + if not self.appear(POPUP_CONFIRM) and self.appear_then_click(FUEL_ENTRANCE): + continue + + offset = FUEL_SELECTED.button_offset + image = crop(self.device.image, area_offset(OCR_FUEL.area, offset), copy=False) + count = Digit(OCR_FUEL).ocr_single_line(image, direct_ocr=True) + + reserve = self.config.TrailblazePower_FuelReserve + available_count = max(count - reserve, 0) + use = min(use, available_count) + logger.info(f'Having {count} fuel, reserve {reserve} fuel, going to use {use} fuel') + if use <= 0: + logger.info("Fuel remain is under the reserve threshold, stop using fuel") + self._fuel_cancel() + + skip_first_screenshot = True + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + if self.appear(USING_FUEL): + break + if self.appear(FUEL) and self.handle_popup_confirm(): + continue + if self.appear(FUEL_SELECTED) and self.handle_popup_confirm(): + continue + + self.set_fuel_count(use) + self._fuel_confirm() + return True diff --git a/tasks/combat/prepare.py b/tasks/combat/prepare.py index 0b47a2c41..98dcf718a 100644 --- a/tasks/combat/prepare.py +++ b/tasks/combat/prepare.py @@ -24,6 +24,8 @@ class TrailblazePowerOcr(DigitCounter): result = re.sub(r'[买米装:()]', '', result) # 61240 -> 6/240 result = re.sub(r'1240$', '/240', result) + # 0*0/24 -> 0/240 + result = re.sub(r'24$', '240', result) return result diff --git a/tasks/daily/daily_quest.py b/tasks/daily/daily_quest.py index 744f8ccc4..a5d7a9d7b 100644 --- a/tasks/daily/daily_quest.py +++ b/tasks/daily/daily_quest.py @@ -350,8 +350,8 @@ class DailyQuestUI(DungeonUI, RouteLoader): else: logger.info('Daily support cannot achieved, dungeon task is scheduled tomorrow') if KEYWORDS_DAILY_QUEST.Consume_120_Trailblaze_Power in quests: - # 12h10min in advance, waiting for stamina - if dungeon < reset - timedelta(hours=12, minutes=10): + # 6h in advance, waiting for stamina + if dungeon < reset - timedelta(hours=6, minutes=0): logger.info('Stamina consume can be achieved in the future') future += 200 else: diff --git a/tasks/dungeon/dungeon.py b/tasks/dungeon/dungeon.py index 6969a4fe4..63c4315cf 100644 --- a/tasks/dungeon/dungeon.py +++ b/tasks/dungeon/dungeon.py @@ -1,5 +1,8 @@ +from datetime import timedelta + from module.base.decorator import set_cached_property from module.base.utils import area_offset +from module.config.stored.classes import now from module.logger import logger from tasks.battle_pass.keywords import KEYWORDS_BATTLE_PASS_QUEST from tasks.combat.combat import Combat @@ -121,6 +124,12 @@ class Dungeon(DungeonStamina, DungeonEvent, Combat): if KEYWORDS_DAILY_QUEST.Clear_Stagnant_Shadow_1_times in self.daily_quests: logger.info('Achieve daily quest Clear_Stagnant_Shadow_1_times') self.achieved_daily_quest = True + if KEYWORDS_BATTLE_PASS_QUEST.Clear_Stagnant_Shadow_1_times in self.weekly_quests: + logger.info('Done weekly quest Clear_Stagnant_Shadow_1_times once') + self.config.stored.BattlePassQuestStagnantShadow.add() + if self.config.stored.BattlePassQuestStagnantShadow.is_full(): + logger.info('Achieved weekly quest Clear_Stagnant_Shadow_1_times') + self.achieved_weekly_quest = True # Cavern_of_Corrosion if dungeon.is_Cavern_of_Corrosion: if KEYWORDS_DAILY_QUEST.Clear_Cavern_of_Corrosion_1_times in self.daily_quests: @@ -222,6 +231,10 @@ class Dungeon(DungeonStamina, DungeonEvent, Combat): or self.config.stored.DungeonDouble.calyx > 0 or self.config.stored.DungeonDouble.relic > 0 or self.config.stored.DungeonDouble.rogue > 0): + update = self.config.stored.DungeonDouble.time + if update <= now() <= update + timedelta(seconds=5): + logger.info('Dungeon double just updated, skip') + return logger.info('Get dungeon double remains') # UI switches switched = self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index) diff --git a/tasks/item/slider.py b/tasks/item/slider.py index af3c47ffe..556eccef6 100644 --- a/tasks/item/slider.py +++ b/tasks/item/slider.py @@ -93,6 +93,16 @@ class Slider: logger.warning('Detected slider too short') # self.main.device.image_save() + def is_slider_arrive(self, detect): + """ + Args: + detect: Area to detect + + Returns: + bool: If slider arrive detect area + """ + return self.main.image_color_count(detect, color=(255, 255, 255), threshold=235, count=50) + def set(self, value: int, total: int, skip_first_screenshot=True): """ Args: @@ -158,7 +168,7 @@ class Slider: if trial > 3: logger.warning('Slider.set failed after 3 trial') return False - if self.main.image_color_count(detect, color=(255, 255, 255), threshold=221, count=50): + if self.is_slider_arrive(detect): logger.info('Slider set done') return True diff --git a/tasks/ornament/ornament.py b/tasks/ornament/ornament.py index 843384022..ec2add752 100644 --- a/tasks/ornament/ornament.py +++ b/tasks/ornament/ornament.py @@ -78,6 +78,7 @@ class Ornament(OrnamentCombat): if self.config.stored.DungeonDouble.calyx or self.config.stored.DungeonDouble.relic: logger.info('During double calyx or relic event, delay Ornament') future = self.config.cross_get('Dungeon.Scheduler.NextRun', default=DEFAULT_TIME) + future = max(now(), future) future = future + timedelta(minutes=1) with self.config.multi_set(): self.config.task_delay(target=future)