diff --git a/config/template.json b/config/template.json index 1bae146ce..19bf454fe 100644 --- a/config/template.json +++ b/config/template.json @@ -43,9 +43,20 @@ "Name": "Calyx_Golden_Treasures", "NameAtDoubleCalyx": "Calyx_Golden_Treasures", "NameAtDoubleRelic": "Cavern_of_Corrosion_Path_of_Providence", - "Team": 1, - "Support": "when_daily", - "SupportCharacter": "FirstCharacter" + "Team": 1 + }, + "DungeonDaily": { + "CalyxGolden": "Calyx_Golden_Treasures", + "CalyxCrimson": "Calyx_Crimson_Erudition", + "StagnantShadow": "do_not_archive", + "CavernOfCorrosion": "Cavern_of_Corrosion_Path_of_Providence" + }, + "DungeonSupport": { + "Use": "when_daily", + "Character": "FirstCharacter" + }, + "DungeonStorage": { + "DungeonDouble": {} } }, "DailyQuest": { @@ -54,6 +65,37 @@ "NextRun": "2020-01-01 00:00:00", "Command": "DailyQuest", "ServerUpdate": "04:00" + }, + "AchievableQuest": { + "Complete_1_Daily_Mission": "not_supported", + "Clear_Calyx_Golden_1_times": "not_set", + "Complete_Calyx_Crimson_1_time": "not_set", + "Clear_Stagnant_Shadow_1_times": "not_set", + "Clear_Cavern_of_Corrosion_1_times": "not_set", + "In_a_single_battle_inflict_3_Weakness_Break_of_different_Types": "not_supported", + "Inflict_Weakness_Break_5_times": "not_supported", + "Defeat_a_total_of_20_enemies": "not_supported", + "Enter_combat_by_attacking_enemy_Weakness_and_win_3_times": "not_supported", + "Use_Technique_2_times": "achievable", + "Go_on_assignment_1_time": "not_set", + "Take_1_photo": "achievable", + "Destroy_3_destructible_objects": "not_supported", + "Complete_Forgotten_Hall_1_time": "not_supported", + "Complete_Echo_of_War_1_times": "not_supported", + "Complete_1_stage_in_Simulated_Universe_Any_world": "not_supported", + "Obtain_victory_in_combat_with_support_characters_1_time": "not_set", + "Use_an_Ultimate_to_deal_the_final_blow_1_time": "not_supported", + "Level_up_any_character_1_time": "not_supported", + "Level_up_any_Light_Cone_1_time": "not_supported", + "Level_up_any_Relic_1_time": "not_supported", + "Salvage_any_Relic": "achievable", + "Synthesize_Consumable_1_time": "achievable", + "Synthesize_material_1_time": "achievable", + "Use_Consumables_1_time": "achievable" + }, + "DailyStorage": { + "DailyActivity": {}, + "DailyQuest": {} } }, "BattlePass": { diff --git a/module/config/argument/args.json b/module/config/argument/args.json index b3b93a7e9..b1e0f49eb 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -243,17 +243,76 @@ 5, 6 ] + } + }, + "DungeonDaily": { + "CalyxGolden": { + "type": "select", + "value": "Calyx_Golden_Treasures", + "option": [ + "do_not_achieve", + "Calyx_Golden_Memories", + "Calyx_Golden_Aether", + "Calyx_Golden_Treasures" + ] }, - "Support": { + "CalyxCrimson": { + "type": "select", + "value": "Calyx_Crimson_Erudition", + "option": [ + "do_not_achieve", + "Calyx_Crimson_Destruction", + "Calyx_Crimson_Preservation", + "Calyx_Crimson_Hunt", + "Calyx_Crimson_Abundance", + "Calyx_Crimson_Erudition", + "Calyx_Crimson_Harmony", + "Calyx_Crimson_Nihility" + ] + }, + "StagnantShadow": { + "type": "select", + "value": "do_not_archive", + "option": [ + "do_not_achieve", + "Stagnant_Shadow_Quanta", + "Stagnant_Shadow_Gust", + "Stagnant_Shadow_Fulmination", + "Stagnant_Shadow_Blaze", + "Stagnant_Shadow_Spike", + "Stagnant_Shadow_Rime", + "Stagnant_Shadow_Mirage", + "Stagnant_Shadow_Icicle", + "Stagnant_Shadow_Doom", + "Stagnant_Shadow_Celestial" + ] + }, + "CavernOfCorrosion": { + "type": "select", + "value": "Cavern_of_Corrosion_Path_of_Providence", + "option": [ + "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" + ] + } + }, + "DungeonSupport": { + "Use": { "type": "select", "value": "when_daily", "option": [ - "do_not_use", "always_use", - "when_daily" + "when_daily", + "do_not_use" ] }, - "SupportCharacter": { + "Character": { "type": "select", "value": "FirstCharacter", "option": [ @@ -290,6 +349,14 @@ "Yukong" ] } + }, + "DungeonStorage": { + "DungeonDouble": { + "type": "stored", + "value": {}, + "display": "hide", + "stored": "StoredDungeonDouble" + } } }, "DailyQuest": { @@ -313,6 +380,397 @@ "value": "04:00", "display": "hide" } + }, + "AchievableQuest": { + "Complete_1_Daily_Mission": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "Clear_Calyx_Golden_1_times": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "Complete_Calyx_Crimson_1_time": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "Clear_Stagnant_Shadow_1_times": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "Clear_Cavern_of_Corrosion_1_times": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "In_a_single_battle_inflict_3_Weakness_Break_of_different_Types": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "Inflict_Weakness_Break_5_times": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "Defeat_a_total_of_20_enemies": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "Enter_combat_by_attacking_enemy_Weakness_and_win_3_times": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "Use_Technique_2_times": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "Go_on_assignment_1_time": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "Take_1_photo": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "Destroy_3_destructible_objects": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "Complete_Forgotten_Hall_1_time": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "Complete_Echo_of_War_1_times": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "Complete_1_stage_in_Simulated_Universe_Any_world": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "Obtain_victory_in_combat_with_support_characters_1_time": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "Use_an_Ultimate_to_deal_the_final_blow_1_time": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "Level_up_any_character_1_time": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "Level_up_any_Light_Cone_1_time": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "Level_up_any_Relic_1_time": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "Salvage_any_Relic": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "Synthesize_Consumable_1_time": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "Synthesize_material_1_time": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + }, + "Use_Consumables_1_time": { + "type": "state", + "value": "achievable", + "option": [ + "achievable", + "not_set", + "not_supported" + ], + "option_bold": [ + "achievable" + ], + "option_light": [ + "not_supported" + ] + } + }, + "DailyStorage": { + "DailyActivity": { + "type": "stored", + "value": {}, + "display": "hide", + "stored": "StoredDailyActivity" + }, + "DailyQuest": { + "type": "stored", + "value": {}, + "display": "hide", + "stored": "StoredDaily" + } } }, "BattlePass": { diff --git a/module/config/argument/argument.yaml b/module/config/argument/argument.yaml index fde21773c..ff472fd20 100644 --- a/module/config/argument/argument.yaml +++ b/module/config/argument/argument.yaml @@ -71,27 +71,56 @@ Optimization: Dungeon: Name: - # Options will be injected in config updater + # Dungeon names will be injected in config updater value: Calyx_Golden_Treasures - option: [Calyx_Golden_Treasures, ] + option: [ ] NameAtDoubleCalyx: - # Options will be injected in config updater value: Calyx_Golden_Treasures option: [ do_not_participate, ] NameAtDoubleRelic: - # Options will be injected in config updater value: Cavern_of_Corrosion_Path_of_Providence option: [ do_not_participate, ] Team: value: 1 option: [ 1, 2, 3, 4, 5, 6 ] - Support: +DungeonDaily: + # Dungeon names will be injected in config updater + CalyxGolden: + value: Calyx_Golden_Treasures + option: [ do_not_achieve, ] + CalyxCrimson: + value: Calyx_Crimson_Erudition + option: [ do_not_achieve, ] + StagnantShadow: + value: do_not_archive + option: [ do_not_achieve, ] + CavernOfCorrosion: + value: Cavern_of_Corrosion_Path_of_Providence + option: [ do_not_achieve, ] +DungeonSupport: + Use: value: when_daily - option: [do_not_use, always_use, when_daily] - SupportCharacter: + option: [ always_use, when_daily, do_not_use ] + Character: # Options will be injected in config updater value: FirstCharacter - option: [FirstCharacter, ] + option: [ FirstCharacter, ] +DungeonStorage: + DungeonDouble: + stored: StoredDungeonDouble + +AchievableQuest: + # Quests will be injected in config updater +# Complete_1_Daily_Mission: +# type: state +# value: achievable +# option: [ achievable, not_set, not_supported ] +# option_bold: [ achievable, ] +DailyStorage: + DailyActivity: + stored: StoredDailyActivity + DailyQuest: + stored: StoredDaily Assignment: Duration: diff --git a/module/config/argument/task.yaml b/module/config/argument/task.yaml index 6edcdb991..c6cb6e65a 100644 --- a/module/config/argument/task.yaml +++ b/module/config/argument/task.yaml @@ -25,8 +25,13 @@ Daily: Dungeon: - Scheduler - Dungeon + - DungeonDaily + - DungeonSupport + - DungeonStorage DailyQuest: - Scheduler + - AchievableQuest + - DailyStorage BattlePass: - Scheduler Assignment: diff --git a/module/config/config.py b/module/config/config.py index aaeea7101..4da400e6f 100644 --- a/module/config/config.py +++ b/module/config/config.py @@ -3,11 +3,14 @@ import datetime import operator import threading +from module.base.decorator import cached_property, del_cached_property from module.base.filter import Filter from module.base.utils import SelectedGrids from module.config.config_generated import GeneratedConfig from module.config.config_manual import ManualConfig from module.config.config_updater import ConfigUpdater +from module.config.stored.stored_generated import StoredGenerated +from module.config.stored.classes import iter_attribute from module.config.utils import * from module.config.watcher import ConfigWatcher from module.exception import RequestHumanTakeover, ScriptError @@ -168,6 +171,15 @@ class AzurLaneConfig(ConfigUpdater, ManualConfig, GeneratedConfig, ConfigWatcher self.data, keys="Alas.Optimization.CloseGameDuringWait", default=False ) + @cached_property + def stored(self) -> StoredGenerated: + stored = StoredGenerated() + # Bind config + for _, value in iter_attribute(stored): + value._bind(self) + del_cached_property(value, '_stored') + return stored + def get_next_task(self): """ Calculate tasks, set pending_task and waiting_task @@ -241,6 +253,7 @@ class AzurLaneConfig(ConfigUpdater, ManualConfig, GeneratedConfig, ConfigWatcher ) # Don't use self.modified = {}, that will create a new object. self.modified.clear() + del_cached_property(self, 'stored') self.write_file(self.config_name, data=self.data) def update(self): diff --git a/module/config/config_generated.py b/module/config/config_generated.py index 1a169de24..2f9b32380 100644 --- a/module/config/config_generated.py +++ b/module/config/config_generated.py @@ -43,8 +43,50 @@ class GeneratedConfig: Dungeon_NameAtDoubleCalyx = 'Calyx_Golden_Treasures' # do_not_participate, Calyx_Golden_Memories, Calyx_Golden_Aether, Calyx_Golden_Treasures, Calyx_Crimson_Destruction, Calyx_Crimson_Preservation, Calyx_Crimson_Hunt, Calyx_Crimson_Abundance, Calyx_Crimson_Erudition, Calyx_Crimson_Harmony, Calyx_Crimson_Nihility Dungeon_NameAtDoubleRelic = 'Cavern_of_Corrosion_Path_of_Providence' # do_not_participate, 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 Dungeon_Team = 1 # 1, 2, 3, 4, 5, 6 - Dungeon_Support = 'when_daily' # do_not_use, always_use, when_daily - Dungeon_SupportCharacter = 'FirstCharacter' # FirstCharacter, Arlan, Asta, Bailu, Blade, Bronya, Clara, DanHeng, Gepard, Herta, Himeko, Hook, JingYuan, Kafka, Luka, Luocha, March7th, Natasha, Pela, Qingque, Sampo, Seele, Serval, SilverWolf, Sushang, Tingyun, TrailblazerDestruction, TrailblazerPreservation, Welt, Yanqing, Yukong + + # Group `DungeonDaily` + DungeonDaily_CalyxGolden = 'Calyx_Golden_Treasures' # do_not_achieve, Calyx_Golden_Memories, Calyx_Golden_Aether, Calyx_Golden_Treasures + DungeonDaily_CalyxCrimson = 'Calyx_Crimson_Erudition' # do_not_achieve, Calyx_Crimson_Destruction, Calyx_Crimson_Preservation, Calyx_Crimson_Hunt, Calyx_Crimson_Abundance, Calyx_Crimson_Erudition, Calyx_Crimson_Harmony, Calyx_Crimson_Nihility + DungeonDaily_StagnantShadow = 'do_not_archive' # do_not_achieve, Stagnant_Shadow_Quanta, Stagnant_Shadow_Gust, Stagnant_Shadow_Fulmination, Stagnant_Shadow_Blaze, Stagnant_Shadow_Spike, Stagnant_Shadow_Rime, Stagnant_Shadow_Mirage, Stagnant_Shadow_Icicle, Stagnant_Shadow_Doom, Stagnant_Shadow_Celestial + 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 + + # Group `DungeonSupport` + DungeonSupport_Use = 'when_daily' # always_use, when_daily, do_not_use + DungeonSupport_Character = 'FirstCharacter' # FirstCharacter, Arlan, Asta, Bailu, Blade, Bronya, Clara, DanHeng, Gepard, Herta, Himeko, Hook, JingYuan, Kafka, Luka, Luocha, March7th, Natasha, Pela, Qingque, Sampo, Seele, Serval, SilverWolf, Sushang, Tingyun, TrailblazerDestruction, TrailblazerPreservation, Welt, Yanqing, Yukong + + # Group `DungeonStorage` + DungeonStorage_DungeonDouble = {} + + # Group `AchievableQuest` + AchievableQuest_Complete_1_Daily_Mission = 'achievable' # achievable, not_set, not_supported + AchievableQuest_Clear_Calyx_Golden_1_times = 'achievable' # achievable, not_set, not_supported + AchievableQuest_Complete_Calyx_Crimson_1_time = 'achievable' # achievable, not_set, not_supported + AchievableQuest_Clear_Stagnant_Shadow_1_times = 'achievable' # achievable, not_set, not_supported + AchievableQuest_Clear_Cavern_of_Corrosion_1_times = 'achievable' # achievable, not_set, not_supported + AchievableQuest_In_a_single_battle_inflict_3_Weakness_Break_of_different_Types = 'achievable' # achievable, not_set, not_supported + AchievableQuest_Inflict_Weakness_Break_5_times = 'achievable' # achievable, not_set, not_supported + AchievableQuest_Defeat_a_total_of_20_enemies = 'achievable' # achievable, not_set, not_supported + AchievableQuest_Enter_combat_by_attacking_enemy_Weakness_and_win_3_times = 'achievable' # achievable, not_set, not_supported + AchievableQuest_Use_Technique_2_times = 'achievable' # achievable, not_set, not_supported + AchievableQuest_Go_on_assignment_1_time = 'achievable' # achievable, not_set, not_supported + AchievableQuest_Take_1_photo = 'achievable' # achievable, not_set, not_supported + AchievableQuest_Destroy_3_destructible_objects = 'achievable' # achievable, not_set, not_supported + AchievableQuest_Complete_Forgotten_Hall_1_time = 'achievable' # achievable, not_set, not_supported + AchievableQuest_Complete_Echo_of_War_1_times = 'achievable' # achievable, not_set, not_supported + AchievableQuest_Complete_1_stage_in_Simulated_Universe_Any_world = 'achievable' # achievable, not_set, not_supported + AchievableQuest_Obtain_victory_in_combat_with_support_characters_1_time = 'achievable' # achievable, not_set, not_supported + AchievableQuest_Use_an_Ultimate_to_deal_the_final_blow_1_time = 'achievable' # achievable, not_set, not_supported + AchievableQuest_Level_up_any_character_1_time = 'achievable' # achievable, not_set, not_supported + AchievableQuest_Level_up_any_Light_Cone_1_time = 'achievable' # achievable, not_set, not_supported + AchievableQuest_Level_up_any_Relic_1_time = 'achievable' # achievable, not_set, not_supported + AchievableQuest_Salvage_any_Relic = 'achievable' # achievable, not_set, not_supported + AchievableQuest_Synthesize_Consumable_1_time = 'achievable' # achievable, not_set, not_supported + AchievableQuest_Synthesize_material_1_time = 'achievable' # achievable, not_set, not_supported + AchievableQuest_Use_Consumables_1_time = 'achievable' # achievable, not_set, not_supported + + # Group `DailyStorage` + DailyStorage_DailyActivity = {} + DailyStorage_DailyQuest = {} # Group `Assignment` Assignment_Duration = 20 # 4, 8, 12, 20 diff --git a/module/config/config_manual.py b/module/config/config_manual.py index 659b42392..1f4893bbc 100644 --- a/module/config/config_manual.py +++ b/module/config/config_manual.py @@ -8,7 +8,7 @@ class ManualConfig: SCHEDULER_PRIORITY = """ Restart - > Dungeon > Assignment > DailyQuest > BattlePass + > DailyQuest > Dungeon > Assignment > BattlePass """ """ diff --git a/module/config/config_updater.py b/module/config/config_updater.py index b1f6d8a55..87e97ef55 100644 --- a/module/config/config_updater.py +++ b/module/config/config_updater.py @@ -4,7 +4,7 @@ from cached_property import cached_property from deploy.Windows.utils import DEPLOY_TEMPLATE, poor_yaml_read, poor_yaml_write from module.base.timer import timer -from module.config.server import to_package, VALID_PACKAGE, VALID_CHANNEL_PACKAGE +from module.config.server import VALID_CHANNEL_PACKAGE, VALID_PACKAGE, to_package from module.config.utils import * CONFIG_IMPORT = ''' @@ -32,6 +32,11 @@ def gui_lang_to_ingame_lang(lang: str) -> str: return DICT_GUI_TO_INGAME.get(lang, 'en') +def get_generator(): + from module.base.code_generator import CodeGenerator + return CodeGenerator() + + class ConfigGenerator: @cached_property def argument(self): @@ -47,6 +52,55 @@ class ConfigGenerator: """ data = {} raw = read_file(filepath_argument('argument')) + + def option_add(keys, options): + options = deep_get(raw, keys=keys, default=[]) + options + deep_set(raw, keys=keys, value=options) + + # Insert dungeons + from tasks.dungeon.keywords import DungeonList + option_add( + keys='Dungeon.Name.option', + options=[dungeon.name for dungeon in DungeonList.instances.values() if dungeon.is_daily_dungeon]) + # Double events + option_add( + keys='Dungeon.NameAtDoubleCalyx.option', + options=[dungeon.name for dungeon in DungeonList.instances.values() if dungeon.is_Calyx]) + option_add( + keys='Dungeon.NameAtDoubleRelic.option', + options=[dungeon.name for dungeon in DungeonList.instances.values() if dungeon.is_Cavern_of_Corrosion]) + # Dungeon daily + option_add( + keys='DungeonDaily.CalyxGolden.option', + options=[dungeon.name for dungeon in DungeonList.instances.values() if dungeon.is_Calyx_Golden]) + option_add( + keys='DungeonDaily.CalyxCrimson.option', + options=[dungeon.name for dungeon in DungeonList.instances.values() if dungeon.is_Calyx_Crimson]) + option_add( + keys='DungeonDaily.StagnantShadow.option', + options=[dungeon.name for dungeon in DungeonList.instances.values() if dungeon.is_Stagnant_Shadow]) + option_add( + keys='DungeonDaily.CavernOfCorrosion.option', + options=[dungeon.name for dungeon in DungeonList.instances.values() if dungeon.is_Cavern_of_Corrosion]) + # Insert characters + from tasks.character.keywords import CharacterList + unsupported_characters = ["DanHengImbibitorLunae"] + characters = [character.name for character in CharacterList.instances.values() + if character.name not in unsupported_characters] + option_add(keys='DungeonSupport.Character.option', options=characters) + # Insert daily quests + from tasks.daily.keywords import DailyQuest + for quest in DailyQuest.instances.values(): + quest: DailyQuest + deep_set(raw, keys=['AchievableQuest', quest.name], value={ + 'type': 'state', + 'value': 'achievable', + 'option': ['achievable', 'not_set', 'not_supported'], + 'option_bold': ['achievable'], + 'option_light': ['not_supported'], + }) + + # Load for path, value in deep_iter(raw, depth=2): arg = { 'type': 'input', @@ -56,6 +110,9 @@ class ConfigGenerator: if not isinstance(value, dict): value = {'value': value} arg['type'] = data_to_type(value, arg=path[1]) + if arg['type'] == 'stored': + value['value'] = {} + arg['display'] = 'hide' # Hide `stored` by default if isinstance(value['value'], datetime): arg['type'] = 'datetime' arg['validate'] = 'datetime' @@ -63,14 +120,6 @@ class ConfigGenerator: arg.update(value) deep_set(data, keys=path, value=arg) - # Define storage group - # arg = { - # 'type': 'storage', - # 'value': {}, - # 'valuetype': 'ignore', - # 'display': 'disabled', - # } - # deep_set(data, keys=['Storage', 'Storage'], value=arg) return data @cached_property @@ -216,6 +265,28 @@ class ConfigGenerator: for text in lines: f.write(text + '\n') + @timer + def generate_stored(self): + import module.config.stored.classes as classes + gen = get_generator() + gen.add('from module.config.stored.classes import (') + with gen.tab(): + for cls in sorted([name for name in dir(classes) if name.startswith('Stored')]): + gen.add(cls + ',') + gen.add(')') + gen.Empty() + gen.Empty() + gen.Empty() + gen.CommentAutoGenerage('module/config/config_updater.py') + + with gen.Class('StoredGenerated'): + for path, data in deep_iter(self.args, depth=3): + cls = data.get('stored') + if cls: + gen.add(f'{path[-1]} = {cls}("{".".join(path)}")') + + gen.write('module/config/stored/stored_generated.py') + @timer def generate_i18n(self, lang): """ @@ -282,27 +353,44 @@ class ConfigGenerator: if dungeon.name in dailies: value = dungeon.__getattribute__(ingame_lang) deep_set(new, keys=['Dungeon', 'Name', dungeon.name], value=value) - # Copy dungeon i18n to double events - for dungeon in deep_get(new, keys='Dungeon.NameAtDoubleCalyx').values(): - if '_' in dungeon: - value = deep_get(new, keys=['Dungeon', 'Name', dungeon]) - if value: - deep_set(new, keys=['Dungeon', 'NameAtDoubleCalyx', dungeon], value=value) - for dungeon in deep_get(new, keys='Dungeon.NameAtDoubleRelic').values(): - if '_' in dungeon: - value = deep_get(new, keys=['Dungeon', 'Name', dungeon]) - if value: - deep_set(new, keys=['Dungeon', 'NameAtDoubleRelic', dungeon], value=value) + # Copy dungeon i18n to double events + def update_dungeon_names(keys): + for dungeon in deep_get(new, keys=keys).values(): + if '_' in dungeon: + value = deep_get(new, keys=['Dungeon', 'Name', dungeon]) + if value: + deep_set(new, keys=f'{keys}.{dungeon}', value=value) + + update_dungeon_names('Dungeon.NameAtDoubleCalyx') + update_dungeon_names('Dungeon.NameAtDoubleRelic') + update_dungeon_names('DungeonDaily.CalyxGolden') + update_dungeon_names('DungeonDaily.CalyxCrimson') + update_dungeon_names('DungeonDaily.StagnantShadow') + update_dungeon_names('DungeonDaily.CavernOfCorrosion') + + # Character names from tasks.character.keywords import CharacterList ingame_lang = gui_lang_to_ingame_lang(lang) - characters = deep_get(self.argument, keys='Dungeon.SupportCharacter.option') + characters = deep_get(self.argument, keys='DungeonSupport.Character.option') for character in CharacterList.instances.values(): if character.name in characters: value = character.__getattribute__(ingame_lang) if "Trailblazer" in value: continue - deep_set(new, keys=['Dungeon', 'SupportCharacter', character.name], value=value) + deep_set(new, keys=['DungeonSupport', 'Character', character.name], value=value) + + # Daily quests + from tasks.daily.keywords import DailyQuest + for quest in DailyQuest.instances.values(): + value = quest.__getattribute__(ingame_lang) + deep_set(new, keys=['AchievableQuest', quest.name, 'name'], value=value) + # deep_set(new, keys=['DailyQuest', quest.name, 'help'], value='') + copy_from = 'Complete_1_Daily_Mission' + if quest.name != copy_from: + for option in deep_get(self.args, keys=['DailyQuest', 'AchievableQuest', copy_from, 'option']): + value = deep_get(new, keys=['AchievableQuest', copy_from, option]) + deep_set(new, keys=['AchievableQuest', quest.name, option], value=value) # GUI i18n for path, _ in deep_iter(self.gui, depth=2): @@ -371,30 +459,6 @@ class ConfigGenerator: # update('template-docker', docker) # update('template-docker-cn', docker, cn) - def insert_dungeon(self): - from tasks.dungeon.keywords import DungeonList - dungeons = [dungeon.name for dungeon in DungeonList.instances.values() if dungeon.is_daily_dungeon] - deep_set(self.argument, keys='Dungeon.Name.option', value=dungeons) - deep_set(self.args, keys='Dungeon.Dungeon.Name.option', value=dungeons) - - from tasks.character.keywords import CharacterList - unsupported_characters = ["DanHengImbibitorLunae"] - characters = ['FirstCharacter'] + [character.name for character in CharacterList.instances.values() if - character.name not in unsupported_characters] - deep_set(self.argument, keys='Dungeon.SupportCharacter.option', value=characters) - deep_set(self.args, keys='Dungeon.Dungeon.SupportCharacter.option', value=characters) - - # Double events - dungeons = deep_get(self.argument, keys='Dungeon.NameAtDoubleCalyx.option') - dungeons += [dungeon.name for dungeon in DungeonList.instances.values() - if dungeon.is_Calyx_Golden or dungeon.is_Calyx_Crimson] - deep_set(self.argument, keys='Dungeon.NameAtDoubleCalyx.option', value=dungeons) - deep_set(self.args, keys='Dungeon.Dungeon.NameAtDoubleCalyx.option', value=dungeons) - dungeons = deep_get(self.argument, keys='Dungeon.NameAtDoubleRelic.option') - dungeons += [dungeon.name for dungeon in DungeonList.instances.values() if dungeon.is_Cavern_of_Corrosion] - deep_set(self.argument, keys='Dungeon.NameAtDoubleRelic.option', value=dungeons) - deep_set(self.args, keys='Dungeon.Dungeon.NameAtDoubleRelic.option', value=dungeons) - def insert_assignment(self): from tasks.assignment.keywords import AssignmentEntry assignments = [entry.name for entry in AssignmentEntry.instances.values()] @@ -414,13 +478,13 @@ class ConfigGenerator: _ = self.args _ = self.menu # _ = self.event - self.insert_dungeon() self.insert_assignment() self.insert_package() # self.insert_server() write_file(filepath_args(), self.args) write_file(filepath_args('menu'), self.menu) self.generate_code() + self.generate_stored() for lang in LANGUAGES: self.generate_i18n(lang) self.generate_deploy_template() @@ -429,6 +493,8 @@ class ConfigGenerator: class ConfigUpdater: # source, target, (optional)convert_func redirection = [ + ('Dungeon.Dungeon.Support', 'Dungeon.DungeonSupport.Use'), + ('Dungeon.Dungeon.SupportCharacter', 'Dungeon.DungeonSupport.Character'), ] @cached_property @@ -449,7 +515,9 @@ class ConfigUpdater: def deep_load(keys): data = deep_get(self.args, keys=keys, default={}) value = deep_get(old, keys=keys, default=data['value']) - if is_template or value is None or value == '' or data['type'] == 'lock' or data.get('display') == 'hide': + typ = data['type'] + display = data.get('display') + if is_template or value is None or value == '' or typ == 'lock' or (display == 'hide' and typ != 'stored'): value = data['value'] value = parse_value(value, data=data) deep_set(new, keys=keys, value=value) @@ -459,6 +527,7 @@ class ConfigUpdater: if not is_template: new = self.config_redirect(old, new) + new = self.update_state(new) return new @@ -511,6 +580,53 @@ class ConfigUpdater: return new + @staticmethod + def update_state(data): + def set_daily(quest, value): + if value is True: + value = 'achievable' + if value is False: + value = 'not_set' + deep_set(data, keys=['DailyQuest', 'AchievableQuest', quest], value=value) + + set_daily('Complete_1_Daily_Mission', 'not_supported') + # Dungeon + dungeon = deep_get(data, keys='Dungeon.Scheduler.Enable') + set_daily('Clear_Calyx_Golden_1_times', + dungeon and deep_get(data, 'Dungeon.DungeonDaily.CalyxGolden') != 'do_not_achieve') + set_daily('Complete_Calyx_Crimson_1_time', + dungeon and deep_get(data, 'Dungeon.DungeonDaily.CalyxCrimson') != 'do_not_achieve') + set_daily('Clear_Stagnant_Shadow_1_times', + dungeon and deep_get(data, 'Dungeon.DungeonDaily.StagnantShadow') != 'do_not_achieve') + set_daily('Clear_Cavern_of_Corrosion_1_times', + dungeon and deep_get(data, 'Dungeon.DungeonDaily.CavernOfCorrosion') != 'do_not_achieve') + # Combat requirements + set_daily('In_a_single_battle_inflict_3_Weakness_Break_of_different_Types', 'not_supported') + set_daily('Inflict_Weakness_Break_5_times', 'not_supported') + set_daily('Defeat_a_total_of_20_enemies', 'not_supported') + set_daily('Enter_combat_by_attacking_enemy_Weakness_and_win_3_times', 'not_supported') + set_daily('Use_Technique_2_times', 'achievable') + # Other game systems + set_daily('Go_on_assignment_1_time', deep_get(data, 'Assignment.Scheduler.Enable')) + set_daily('Take_1_photo', 'achievable') + set_daily('Destroy_3_destructible_objects', 'not_supported') + set_daily('Complete_Forgotten_Hall_1_time', 'not_supported') + set_daily('Complete_Echo_of_War_1_times', 'not_supported') + set_daily('Complete_1_stage_in_Simulated_Universe_Any_world', 'not_supported') + set_daily('Obtain_victory_in_combat_with_support_characters_1_time', + dungeon and deep_get(data, 'Dungeon.DungeonSupport.Use') in ['when_daily', 'always_use']) + set_daily('Use_an_Ultimate_to_deal_the_final_blow_1_time', 'not_supported') + # Build + set_daily('Level_up_any_character_1_time', 'not_supported') + set_daily('Level_up_any_Light_Cone_1_time', 'not_supported') + set_daily('Level_up_any_Relic_1_time', 'not_supported') + # Items + set_daily('Salvage_any_Relic', 'achievable') + set_daily('Synthesize_Consumable_1_time', 'achievable') + set_daily('Synthesize_material_1_time', 'achievable') + set_daily('Use_Consumables_1_time', 'achievable') + return data + def read_file(self, config_name, is_template=False): """ Read and update config file. diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index e8245cff5..bceddc3cf 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -247,18 +247,77 @@ "4": "4", "5": "5", "6": "6" + } + }, + "DungeonDaily": { + "_info": { + "name": "Daily Quest Settings", + "help": "Clear required dungeon once to achieve daily quests" }, - "Support": { - "name": "Enable buddy support", - "help": "Whether to enable buddy support", - "do_not_use": "do_not_use", - "always_use": "always_use", - "when_daily": "when_daily" + "CalyxGolden": { + "name": "Clear Calyx Golden 1 times", + "help": "", + "do_not_achieve": "Don't Do This Quest", + "Calyx_Golden_Memories": "Material: Character EXP (Bud of Memories)", + "Calyx_Golden_Aether": "Material: Light Cone EXP (Bud of Aether)", + "Calyx_Golden_Treasures": "Material: Credit (Bud of Treasures)" }, - "SupportCharacter": { - "name": "Dungeon.SupportCharacter.name", - "help": "Dungeon.SupportCharacter.help", - "FirstCharacter": "FirstCharacter", + "CalyxCrimson": { + "name": "Clear Calyx Crimson 1 times", + "help": "", + "do_not_achieve": "Don't Do This Quest", + "Calyx_Crimson_Destruction": "Trace: Destruction (Bud of Destruction)", + "Calyx_Crimson_Preservation": "Trace: Preservation (Bud of Preservation)", + "Calyx_Crimson_Hunt": "Trace: Hunt (Bud of Hunt)", + "Calyx_Crimson_Abundance": "Trace: Abundance (Bud of Abundance)", + "Calyx_Crimson_Erudition": "Trace: Erudition (Bud of Erudition)", + "Calyx_Crimson_Harmony": "Trace: Harmony (Bud of Harmony)", + "Calyx_Crimson_Nihility": "Trace: Nihility (Bud of Nihility)" + }, + "StagnantShadow": { + "name": "Clear Stagnant Shadow 1 times", + "help": "", + "do_not_achieve": "Don't Do This Quest", + "Stagnant_Shadow_Quanta": "Ascension: Quantum (Shape of Quanta)", + "Stagnant_Shadow_Gust": "Ascension: Wind (Shape of Gust)", + "Stagnant_Shadow_Fulmination": "Ascension: Lighting (Shape of Fulmination)", + "Stagnant_Shadow_Blaze": "Ascension: Fire (Shape of Blaze)", + "Stagnant_Shadow_Spike": "Ascension: Physical (Shape of Spike)", + "Stagnant_Shadow_Rime": "Ascension: Ice (Shape of Rime)", + "Stagnant_Shadow_Mirage": "Ascension: Imaginary (Shape of Mirage)", + "Stagnant_Shadow_Icicle": "Ascension: Ice (Shape of Icicle)", + "Stagnant_Shadow_Doom": "Ascension: Lighting (Shape of Doom)", + "Stagnant_Shadow_Celestial": "Ascension: Wind (Shape of Celestial)" + }, + "CavernOfCorrosion": { + "name": "Clear Cavern of Corrosion 1 times", + "help": "", + "do_not_achieve": "Don't Do This Quest", + "Cavern_of_Corrosion_Path_of_Gelid_Wind": "Relics: Ice Set & Wind Set (Path of Gelid Wind)", + "Cavern_of_Corrosion_Path_of_Jabbing_Punch": "Relics: Physical Set & Break Effect Set (Path of Jabbing Punch)", + "Cavern_of_Corrosion_Path_of_Drifting": "Relics: Healing Set & Musketeer Set (Path of Drifting)", + "Cavern_of_Corrosion_Path_of_Providence": "Relics: Guard Set & Quantum Set (Path of Providence)", + "Cavern_of_Corrosion_Path_of_Holy_Hymn": "Relics: DEF Set & Lighting Set (Path of Holy Hymn)", + "Cavern_of_Corrosion_Path_of_Conflagration": "Relics: Fire Set & Imaginary Set (Path of Conflagration)", + "Cavern_of_Corrosion_Path_of_Elixir_Seekers": "Relics: HP Set & SPD Set (Path of Elixir Seekers)" + } + }, + "DungeonSupport": { + "_info": { + "name": "Support Settings", + "help": "" + }, + "Use": { + "name": "Use Friend Support", + "help": "", + "always_use": "Always Use", + "when_daily": "Use Only When Required by Dailies", + "do_not_use": "Don't Use" + }, + "Character": { + "name": "Support Character", + "help": "Select a friend support character, if not found, select the default (first) role", + "FirstCharacter": "First Character", "Arlan": "Arlan", "Asta": "Asta", "Bailu": "Bailu", @@ -284,13 +343,218 @@ "SilverWolf": "Silver Wolf", "Sushang": "Sushang", "Tingyun": "Tingyun", - "TrailblazerDestruction": "TrailblazerDestruction", - "TrailblazerPreservation": "TrailblazerPreservation", + "TrailblazerDestruction": "Trailblazer Destruction", + "TrailblazerPreservation": "Trailblazer Preservation", "Welt": "Welt", "Yanqing": "Yanqing", "Yukong": "Yukong" } }, + "DungeonStorage": { + "_info": { + "name": "DungeonStorage._info.name", + "help": "DungeonStorage._info.help" + }, + "DungeonDouble": { + "name": "DungeonStorage.DungeonDouble.name", + "help": "DungeonStorage.DungeonDouble.help" + } + }, + "AchievableQuest": { + "_info": { + "name": "Achievable Quests", + "help": "When the task status is \"Not Set\", you need to configure the SRC as required to achieve the quest\nNote: Please keep more tasks in \"Achievable\" status, otherwise SRC may not be able to grind 500 activity" + }, + "Complete_1_Daily_Mission": { + "name": "Complete 1 Daily Mission", + "help": "", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "Clear_Calyx_Golden_1_times": { + "name": "Clear Calyx (Golden) 1 time(s)", + "help": "Need to configure and enable the \"Dungeon\" task", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "Complete_Calyx_Crimson_1_time": { + "name": "Complete Calyx (Crimson) 1 time", + "help": "Need to configure and enable the \"Dungeon\" task", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "Clear_Stagnant_Shadow_1_times": { + "name": "Clear Stagnant Shadow 1 time(s)", + "help": "Need to configure and enable the \"Dungeon\" task", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "Clear_Cavern_of_Corrosion_1_times": { + "name": "Clear Cavern of Corrosion 1 time(s)", + "help": "Need to configure and enable the \"Dungeon\" task", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "In_a_single_battle_inflict_3_Weakness_Break_of_different_Types": { + "name": "In a single battle, inflict 3 Weakness Break of different Types", + "help": "", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "Inflict_Weakness_Break_5_times": { + "name": "Inflict Weakness Break 5 times", + "help": "", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "Defeat_a_total_of_20_enemies": { + "name": "Defeat a total of 20 enemies", + "help": "", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "Enter_combat_by_attacking_enemy_Weakness_and_win_3_times": { + "name": "Enter combat by attacking enemy's Weakness and win 3 times", + "help": "", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "Use_Technique_2_times": { + "name": "Use Technique 2 times", + "help": "Achievable by default, will go to the abyssal 1 and use technique twice", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "Go_on_assignment_1_time": { + "name": "Go on assignment 1 time", + "help": "Need to configure and enable the \"Assignment\" task", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "Take_1_photo": { + "name": "Take 1 photo", + "help": "Achievable by default", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "Destroy_3_destructible_objects": { + "name": "Destroy 3 destructible objects", + "help": "", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "Complete_Forgotten_Hall_1_time": { + "name": "Complete Forgotten Hall 1 time", + "help": "", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "Complete_Echo_of_War_1_times": { + "name": "Complete Echo of War 1 time(s)", + "help": "", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "Complete_1_stage_in_Simulated_Universe_Any_world": { + "name": "Complete 1 stage in Simulated Universe (Any world)", + "help": "", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "Obtain_victory_in_combat_with_support_characters_1_time": { + "name": "Obtain victory in combat with support characters 1 time", + "help": "Need to configure and enable the \"Dungeon\" task, configure support settings also", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "Use_an_Ultimate_to_deal_the_final_blow_1_time": { + "name": "Use an Ultimate to deal the final blow 1 time", + "help": "", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "Level_up_any_character_1_time": { + "name": "Level up any character 1 time", + "help": "", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "Level_up_any_Light_Cone_1_time": { + "name": "Level up any Light Cone 1 time", + "help": "", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "Level_up_any_Relic_1_time": { + "name": "Level up any Relic 1 time", + "help": "", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "Salvage_any_Relic": { + "name": "Salvage any Relic", + "help": "Achievable by default, will salvage the first one in reverse order of rarity", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "Synthesize_Consumable_1_time": { + "name": "Synthesize Consumable 1 time", + "help": "Achievable by default, will synthesize low-rarity snacks", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "Synthesize_material_1_time": { + "name": "Synthesize material 1 time", + "help": "Achievable by default, will synthesize low-rarity material", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + }, + "Use_Consumables_1_time": { + "name": "Use Consumables 1 time", + "help": "Achievable by default, will use gear. If there is no material, synthesized before use", + "achievable": "Achievable", + "not_set": "Not Set", + "not_supported": "Not Supported Yet" + } + }, + "DailyStorage": { + "_info": { + "name": "DailyStorage._info.name", + "help": "DailyStorage._info.help" + }, + "DailyActivity": { + "name": "DailyStorage.DailyActivity.name", + "help": "DailyStorage.DailyActivity.help" + }, + "DailyQuest": { + "name": "DailyStorage.DailyQuest.name", + "help": "DailyStorage.DailyQuest.help" + } + }, "Assignment": { "_info": { "name": "Assignment Settings", diff --git a/module/config/i18n/ja-JP.json b/module/config/i18n/ja-JP.json index 0bc53d6ca..ecfd1396a 100644 --- a/module/config/i18n/ja-JP.json +++ b/module/config/i18n/ja-JP.json @@ -247,17 +247,76 @@ "4": "4", "5": "5", "6": "6" + } + }, + "DungeonDaily": { + "_info": { + "name": "DungeonDaily._info.name", + "help": "DungeonDaily._info.help" }, - "Support": { - "name": "Dungeon.Support.name", - "help": "Dungeon.Support.help", - "do_not_use": "do_not_use", + "CalyxGolden": { + "name": "DungeonDaily.CalyxGolden.name", + "help": "DungeonDaily.CalyxGolden.help", + "do_not_achieve": "do_not_achieve", + "Calyx_Golden_Memories": "疑似花萼(金)・回憶の蕾", + "Calyx_Golden_Aether": "疑似花萼(金)・エーテルの蕾", + "Calyx_Golden_Treasures": "疑似花萼(金)・秘蔵の蕾" + }, + "CalyxCrimson": { + "name": "DungeonDaily.CalyxCrimson.name", + "help": "DungeonDaily.CalyxCrimson.help", + "do_not_achieve": "do_not_achieve", + "Calyx_Crimson_Destruction": "疑似花萼(赤)・壊滅の蕾", + "Calyx_Crimson_Preservation": "疑似花萼(赤)・存護の蕾", + "Calyx_Crimson_Hunt": "疑似花萼(赤)・巡狩の蕾", + "Calyx_Crimson_Abundance": "疑似花萼(赤)・豊穣の蕾", + "Calyx_Crimson_Erudition": "疑似花萼(赤)・知恵の蕾", + "Calyx_Crimson_Harmony": "疑似花萼(赤)・調和の蕾", + "Calyx_Crimson_Nihility": "疑似花萼(赤)・虚無の蕾" + }, + "StagnantShadow": { + "name": "DungeonDaily.StagnantShadow.name", + "help": "DungeonDaily.StagnantShadow.help", + "do_not_achieve": "do_not_achieve", + "Stagnant_Shadow_Quanta": "凝結虚影・虚海の形", + "Stagnant_Shadow_Gust": "凝結虚影・薫風の形", + "Stagnant_Shadow_Fulmination": "凝結虚影・鳴雷の形", + "Stagnant_Shadow_Blaze": "凝結虚影・炎華の形", + "Stagnant_Shadow_Spike": "凝結虚影・切先の形", + "Stagnant_Shadow_Rime": "凝結虚影・霜晶の形", + "Stagnant_Shadow_Mirage": "凝結虚影・幻光の形", + "Stagnant_Shadow_Icicle": "凝結虚影・氷柱の形", + "Stagnant_Shadow_Doom": "凝結虚影・震厄の形", + "Stagnant_Shadow_Celestial": "凝結虚影・天人の形" + }, + "CavernOfCorrosion": { + "name": "DungeonDaily.CavernOfCorrosion.name", + "help": "DungeonDaily.CavernOfCorrosion.help", + "do_not_achieve": "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": "侵蝕トンネル・薬使の路" + } + }, + "DungeonSupport": { + "_info": { + "name": "DungeonSupport._info.name", + "help": "DungeonSupport._info.help" + }, + "Use": { + "name": "DungeonSupport.Use.name", + "help": "DungeonSupport.Use.help", "always_use": "always_use", - "when_daily": "when_daily" + "when_daily": "when_daily", + "do_not_use": "do_not_use" }, - "SupportCharacter": { - "name": "Dungeon.SupportCharacter.name", - "help": "Dungeon.SupportCharacter.help", + "Character": { + "name": "DungeonSupport.Character.name", + "help": "DungeonSupport.Character.help", "FirstCharacter": "FirstCharacter", "Arlan": "アーラン", "Asta": "アスター", @@ -291,6 +350,211 @@ "Yukong": "御空" } }, + "DungeonStorage": { + "_info": { + "name": "DungeonStorage._info.name", + "help": "DungeonStorage._info.help" + }, + "DungeonDouble": { + "name": "DungeonStorage.DungeonDouble.name", + "help": "DungeonStorage.DungeonDouble.help" + } + }, + "AchievableQuest": { + "_info": { + "name": "AchievableQuest._info.name", + "help": "AchievableQuest._info.help" + }, + "Complete_1_Daily_Mission": { + "name": "デイリークエストを1回クリアする", + "help": "AchievableQuest.Complete_1_Daily_Mission.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "Clear_Calyx_Golden_1_times": { + "name": "「疑似花萼(金)」を1回クリアする", + "help": "AchievableQuest.Clear_Calyx_Golden_1_times.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "Complete_Calyx_Crimson_1_time": { + "name": "「疑似花萼(赤)」を1回クリアする", + "help": "AchievableQuest.Complete_Calyx_Crimson_1_time.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "Clear_Stagnant_Shadow_1_times": { + "name": "「凝結虚影」を1回クリアする", + "help": "AchievableQuest.Clear_Stagnant_Shadow_1_times.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "Clear_Cavern_of_Corrosion_1_times": { + "name": "「侵蝕トンネル」を1回クリアする", + "help": "AchievableQuest.Clear_Cavern_of_Corrosion_1_times.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "In_a_single_battle_inflict_3_Weakness_Break_of_different_Types": { + "name": "一度の戦闘で、異なる3種の属性の弱点撃破を発動する", + "help": "AchievableQuest.In_a_single_battle_inflict_3_Weakness_Break_of_different_Types.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "Inflict_Weakness_Break_5_times": { + "name": "累計で弱点撃破効果を5回発動する", + "help": "AchievableQuest.Inflict_Weakness_Break_5_times.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "Defeat_a_total_of_20_enemies": { + "name": "敵を累計で20体倒す", + "help": "AchievableQuest.Defeat_a_total_of_20_enemies.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "Enter_combat_by_attacking_enemy_Weakness_and_win_3_times": { + "name": "弱点を攻撃して戦闘に入り、3回勝利する", + "help": "AchievableQuest.Enter_combat_by_attacking_enemy_Weakness_and_win_3_times.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "Use_Technique_2_times": { + "name": "秘技を累計2回発動する", + "help": "AchievableQuest.Use_Technique_2_times.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "Go_on_assignment_1_time": { + "name": "依頼に1回派遣する", + "help": "AchievableQuest.Go_on_assignment_1_time.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "Take_1_photo": { + "name": "1回撮影する", + "help": "AchievableQuest.Take_1_photo.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "Destroy_3_destructible_objects": { + "name": "破壊できるオブジェクトを累計で3つ破壊する", + "help": "AchievableQuest.Destroy_3_destructible_objects.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "Complete_Forgotten_Hall_1_time": { + "name": "「忘却の庭」を1回クリアする", + "help": "AchievableQuest.Complete_Forgotten_Hall_1_time.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "Complete_Echo_of_War_1_times": { + "name": "「歴戦余韻」を1回クリアする", + "help": "AchievableQuest.Complete_Echo_of_War_1_times.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "Complete_1_stage_in_Simulated_Universe_Any_world": { + "name": "「模擬宇宙」のエリアを1つクリアする(任意の世界)", + "help": "AchievableQuest.Complete_1_stage_in_Simulated_Universe_Any_world.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "Obtain_victory_in_combat_with_support_characters_1_time": { + "name": "サポートキャラを使い、戦闘に1回勝利する", + "help": "AchievableQuest.Obtain_victory_in_combat_with_support_characters_1_time.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "Use_an_Ultimate_to_deal_the_final_blow_1_time": { + "name": "必殺技で最後の一撃を1回与える", + "help": "AchievableQuest.Use_an_Ultimate_to_deal_the_final_blow_1_time.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "Level_up_any_character_1_time": { + "name": "任意のキャラを1回レベルアップする", + "help": "AchievableQuest.Level_up_any_character_1_time.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "Level_up_any_Light_Cone_1_time": { + "name": "任意の光円錐を1回レベルアップする", + "help": "AchievableQuest.Level_up_any_Light_Cone_1_time.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "Level_up_any_Relic_1_time": { + "name": "任意の遺物を1回レベルアップする", + "help": "AchievableQuest.Level_up_any_Relic_1_time.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "Salvage_any_Relic": { + "name": "任意の遺物1つを分解する", + "help": "AchievableQuest.Salvage_any_Relic.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "Synthesize_Consumable_1_time": { + "name": "消耗品を1回合成する", + "help": "AchievableQuest.Synthesize_Consumable_1_time.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "Synthesize_material_1_time": { + "name": "素材を1回合成する", + "help": "AchievableQuest.Synthesize_material_1_time.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + }, + "Use_Consumables_1_time": { + "name": "消耗品を1個使う", + "help": "AchievableQuest.Use_Consumables_1_time.help", + "achievable": "achievable", + "not_set": "not_set", + "not_supported": "not_supported" + } + }, + "DailyStorage": { + "_info": { + "name": "DailyStorage._info.name", + "help": "DailyStorage._info.help" + }, + "DailyActivity": { + "name": "DailyStorage.DailyActivity.name", + "help": "DailyStorage.DailyActivity.help" + }, + "DailyQuest": { + "name": "DailyStorage.DailyQuest.name", + "help": "DailyStorage.DailyQuest.help" + } + }, "Assignment": { "_info": { "name": "依頼設定", diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index 2dbef8373..0c1d2d24f 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -31,7 +31,7 @@ "help": "" }, "Assignment": { - "name": "委托设置", + "name": "委托", "help": "" } }, @@ -247,15 +247,74 @@ "4": "4", "5": "5", "6": "6" + } + }, + "DungeonDaily": { + "_info": { + "name": "每日任务设置", + "help": "打一次特定的本,以满足每日任务的要求" }, - "Support": { - "name": "启用好友支援", - "help": "是否启用好友支援", - "do_not_use": "否", - "always_use": "是", - "when_daily": "仅当每日任务需要时" + "CalyxGolden": { + "name": "完成1次拟造花萼(金)", + "help": "", + "do_not_achieve": "不完成这个任务", + "Calyx_Golden_Memories": "材料:角色经验(回忆之蕾•拟造花萼金)", + "Calyx_Golden_Aether": "材料:武器经验(以太之蕾•拟造花萼金)", + "Calyx_Golden_Treasures": "材料:信用点(藏珍之蕾•拟造花萼金)" }, - "SupportCharacter": { + "CalyxCrimson": { + "name": "完成1次拟造花萼(赤)", + "help": "", + "do_not_achieve": "不完成这个任务", + "Calyx_Crimson_Destruction": "行迹材料:毁灭(毁灭之蕾•拟造花萼赤)", + "Calyx_Crimson_Preservation": "行迹材料:存护(存护之蕾•拟造花萼赤)", + "Calyx_Crimson_Hunt": "行迹材料:巡猎(存护之蕾•拟造花萼赤)", + "Calyx_Crimson_Abundance": "行迹材料:丰饶(丰饶之蕾•拟造花萼赤)", + "Calyx_Crimson_Erudition": "行迹材料:智识(智识之蕾•拟造花萼赤)", + "Calyx_Crimson_Harmony": "行迹材料:同谐(同谐之蕾•拟造花萼赤)", + "Calyx_Crimson_Nihility": "行迹材料:虚无(虚无之蕾•拟造花萼赤)" + }, + "StagnantShadow": { + "name": "完成1次凝滞虚影", + "help": "", + "do_not_achieve": "不完成这个任务", + "Stagnant_Shadow_Quanta": "角色晋阶材料:量子(空海之形•凝滞虚影)", + "Stagnant_Shadow_Gust": "角色晋阶材料:风(巽风之形•凝滞虚影)", + "Stagnant_Shadow_Fulmination": "角色晋阶材料:雷(鸣雷之形•凝滞虚影)", + "Stagnant_Shadow_Blaze": "角色晋阶材料:火(炎华之形•凝滞虚影)", + "Stagnant_Shadow_Spike": "角色晋阶材料:物理(锋芒之形•凝滞虚影)", + "Stagnant_Shadow_Rime": "角色晋阶材料:冰(霜晶之形•凝滞虚影)", + "Stagnant_Shadow_Mirage": "角色晋阶材料:虚数(幻光之形•凝滞虚影)", + "Stagnant_Shadow_Icicle": "角色晋阶材料:冰(冰棱之形•凝滞虚影)", + "Stagnant_Shadow_Doom": "角色晋阶材料:雷(震厄之形•凝滞虚影)", + "Stagnant_Shadow_Celestial": "角色晋阶材料:风(天人之形•凝滞虚影)" + }, + "CavernOfCorrosion": { + "name": "完成1次侵蚀隧洞", + "help": "", + "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": "遗器:生命套+速度套(药使之径•侵蚀隧洞)" + } + }, + "DungeonSupport": { + "_info": { + "name": "支援设置", + "help": "" + }, + "Use": { + "name": "使用好友支援", + "help": "", + "always_use": "总是使用", + "when_daily": "仅当每日任务需要时使用", + "do_not_use": "不使用" + }, + "Character": { "name": "好友支援角色", "help": "选择好友支援角色,未找到则选择默认(第一个)角色", "FirstCharacter": "支援列表第一个角色", @@ -291,6 +350,211 @@ "Yukong": "驭空" } }, + "DungeonStorage": { + "_info": { + "name": "DungeonStorage._info.name", + "help": "DungeonStorage._info.help" + }, + "DungeonDouble": { + "name": "DungeonStorage.DungeonDouble.name", + "help": "DungeonStorage.DungeonDouble.help" + } + }, + "AchievableQuest": { + "_info": { + "name": "可完成的任务", + "help": "任务状态为 \"未设置\" 时需要按照要求设置SRC,才能启用任务\n注意:请让更多的任务处于 \"可完成\" 状态,否则SRC可能无法完成500点的活跃度要求" + }, + "Complete_1_Daily_Mission": { + "name": "完成1个日常任务", + "help": "", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "Clear_Calyx_Golden_1_times": { + "name": "完成1次「拟造花萼(金)」", + "help": "需要设置并启用\"每日副本\"任务", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "Complete_Calyx_Crimson_1_time": { + "name": "完成1次「拟造花萼(赤)」", + "help": "需要设置并启用\"每日副本\"任务", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "Clear_Stagnant_Shadow_1_times": { + "name": "完成1次「凝滞虚影」", + "help": "需要设置并启用\"每日副本\"任务", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "Clear_Cavern_of_Corrosion_1_times": { + "name": "完成1次「侵蚀隧洞」", + "help": "需要设置并启用\"每日副本\"任务", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "In_a_single_battle_inflict_3_Weakness_Break_of_different_Types": { + "name": "单场战斗中,触发3种不同属性的弱点击破", + "help": "", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "Inflict_Weakness_Break_5_times": { + "name": "累计触发弱点击破效果5次", + "help": "", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "Defeat_a_total_of_20_enemies": { + "name": "累计消灭20个敌人", + "help": "", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "Enter_combat_by_attacking_enemy_Weakness_and_win_3_times": { + "name": "利用弱点进入战斗并获胜3次", + "help": "", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "Use_Technique_2_times": { + "name": "累计施放2次秘技", + "help": "默认可完成,将前往深渊一施放2次秘技", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "Go_on_assignment_1_time": { + "name": "派遣1次委托", + "help": "需要设置并启用\"委托\"任务", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "Take_1_photo": { + "name": "拍照1次", + "help": "默认可完成", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "Destroy_3_destructible_objects": { + "name": "累计击碎3个可破坏物", + "help": "", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "Complete_Forgotten_Hall_1_time": { + "name": "完成1次「忘却之庭」", + "help": "", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "Complete_Echo_of_War_1_times": { + "name": "完成1次「历战余响」", + "help": "", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "Complete_1_stage_in_Simulated_Universe_Any_world": { + "name": "通关「模拟宇宙」(任意世界)的1个区域", + "help": "", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "Obtain_victory_in_combat_with_support_characters_1_time": { + "name": "使用支援角色并获得战斗胜利1次", + "help": "需要设置并启用\"每日副本\",且设置好友支援", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "Use_an_Ultimate_to_deal_the_final_blow_1_time": { + "name": "施放终结技造成制胜一击1次", + "help": "", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "Level_up_any_character_1_time": { + "name": "将任意角色等级提升1次", + "help": "", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "Level_up_any_Light_Cone_1_time": { + "name": "将任意光锥等级提升1次", + "help": "", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "Level_up_any_Relic_1_time": { + "name": "将任意遗器等级提升1次", + "help": "", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "Salvage_any_Relic": { + "name": "分解任意1件遗器", + "help": "默认可完成,将分解遗器稀有度倒序的第一个", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "Synthesize_Consumable_1_time": { + "name": "合成1次消耗品", + "help": "默认可完成,将合成最低级零食", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "Synthesize_material_1_time": { + "name": "合成1次材料", + "help": "默认可完成,将合成最低级材料", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + }, + "Use_Consumables_1_time": { + "name": "使用1件消耗品", + "help": "默认可完成,将使用护具,无材料时先合成再使用", + "achievable": "可完成", + "not_set": "未设置", + "not_supported": "暂未支持" + } + }, + "DailyStorage": { + "_info": { + "name": "DailyStorage._info.name", + "help": "DailyStorage._info.help" + }, + "DailyActivity": { + "name": "DailyStorage.DailyActivity.name", + "help": "DailyStorage.DailyActivity.help" + }, + "DailyQuest": { + "name": "DailyStorage.DailyQuest.name", + "help": "DailyStorage.DailyQuest.help" + } + }, "Assignment": { "_info": { "name": "委托设置", diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index 96150ee04..6bcdffb0c 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -31,7 +31,7 @@ "help": "" }, "Assignment": { - "name": "委託設置", + "name": "委託", "help": "" } }, @@ -247,18 +247,77 @@ "4": "4", "5": "5", "6": "6" + } + }, + "DungeonDaily": { + "_info": { + "name": "每日任務設定", + "help": "打一次特定的本,以滿足每日任務的要求" }, - "Support": { - "name": "Dungeon.Support.name", - "help": "Dungeon.Support.help", - "do_not_use": "do_not_use", - "always_use": "always_use", - "when_daily": "when_daily" + "CalyxGolden": { + "name": "完成1次擬造花萼(金)", + "help": "", + "do_not_achieve": "不完成這個任務", + "Calyx_Golden_Memories": "材料:角色經驗(回憶之蕾•擬造花萼金)", + "Calyx_Golden_Aether": "材料:武器經驗(乙太之蕾•擬造花萼金)", + "Calyx_Golden_Treasures": "材料:信用點(藏珍之蕾•擬造花萼金)" }, - "SupportCharacter": { - "name": "Dungeon.SupportCharacter.name", - "help": "Dungeon.SupportCharacter.help", - "FirstCharacter": "FirstCharacter", + "CalyxCrimson": { + "name": "完成1次擬造花萼(赤)", + "help": "", + "do_not_achieve": "不完成這個任務", + "Calyx_Crimson_Destruction": "行跡材料:毀滅(毀滅之蕾•擬造花萼赤)", + "Calyx_Crimson_Preservation": "行跡材料:存護(存護之蕾•擬造花萼赤)", + "Calyx_Crimson_Hunt": "行跡材料:巡獵(存護之蕾•擬造花萼赤)", + "Calyx_Crimson_Abundance": "行跡材料:豐饒(豐饒之蕾•擬造花萼赤)", + "Calyx_Crimson_Erudition": "行跡材料:智識(智識之蕾•擬造花萼赤)", + "Calyx_Crimson_Harmony": "行跡材料:同諧(同諧之蕾•擬造花萼赤)", + "Calyx_Crimson_Nihility": "行跡材料:虛無(虛無之蕾•擬造花萼赤)" + }, + "StagnantShadow": { + "name": "完成1次凝滯虛影", + "help": "", + "do_not_achieve": "不完成這個任務", + "Stagnant_Shadow_Quanta": "角色晉階材料:量子(空海之形•凝滯虛影)", + "Stagnant_Shadow_Gust": "角色晉階材料:風(巽風之形•凝滯虛影)", + "Stagnant_Shadow_Fulmination": "角色晉階材料:雷(鳴雷之形•凝滯虛影)", + "Stagnant_Shadow_Blaze": "角色晉階材料:火(炎華之形•凝滯虛影)", + "Stagnant_Shadow_Spike": "角色晉階材料:物理(鋒芒之形•凝滯虛影)", + "Stagnant_Shadow_Rime": "角色晉階材料:冰(霜晶之形•凝滯虛影)", + "Stagnant_Shadow_Mirage": "角色晉階材料:虛數(幻光之形•凝滯虛影)", + "Stagnant_Shadow_Icicle": "角色晉階材料:冰(冰稜之形•凝滯虛影)", + "Stagnant_Shadow_Doom": "角色晉階材料:雷(震厄之形•凝滯虛影)", + "Stagnant_Shadow_Celestial": "角色晉階材料:風(天人之形•凝滯虛影)" + }, + "CavernOfCorrosion": { + "name": "完成1次侵蝕隧洞", + "help": "", + "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": "遺器:生命套+速度套(藥使之徑•侵蝕隧洞)" + } + }, + "DungeonSupport": { + "_info": { + "name": "支援設定", + "help": "" + }, + "Use": { + "name": "使用好友支援", + "help": "", + "always_use": "總是使用", + "when_daily": "僅當每日任務需要時使用", + "do_not_use": "不使用" + }, + "Character": { + "name": "好友支援角色", + "help": "選擇好友支援角色,未找到則選擇默認(第一個)角色", + "FirstCharacter": "支援列表第一個角色", "Arlan": "阿蘭", "Asta": "艾絲妲", "Bailu": "白露", @@ -284,16 +343,221 @@ "SilverWolf": "銀狼", "Sushang": "素裳", "Tingyun": "停雲", - "TrailblazerDestruction": "TrailblazerDestruction", - "TrailblazerPreservation": "TrailblazerPreservation", + "TrailblazerDestruction": "開拓者•毀滅", + "TrailblazerPreservation": "開拓者•存護", "Welt": "瓦爾特", "Yanqing": "彥卿", "Yukong": "馭空" } }, + "DungeonStorage": { + "_info": { + "name": "DungeonStorage._info.name", + "help": "DungeonStorage._info.help" + }, + "DungeonDouble": { + "name": "DungeonStorage.DungeonDouble.name", + "help": "DungeonStorage.DungeonDouble.help" + } + }, + "AchievableQuest": { + "_info": { + "name": "可完成的任務", + "help": "任務狀態為 \"未設定\" 時需要按照要求設定SRC,才能啟用任務\n注意:請讓更多的任務處於 \"可完成\" 狀態,否則SRC可能無法完成500點的活躍度要求" + }, + "Complete_1_Daily_Mission": { + "name": "完成1個每日任務", + "help": "", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "Clear_Calyx_Golden_1_times": { + "name": "完成1次「擬造花萼(金)」", + "help": "需要設定並啟用\"每日副本\"任務", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "Complete_Calyx_Crimson_1_time": { + "name": "完成1次「擬造花萼(赤)」", + "help": "需要設定並啟用\"每日副本\"任務", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "Clear_Stagnant_Shadow_1_times": { + "name": "完成1次「凝滯虛影」", + "help": "需要設定並啟用\"每日副本\"任務", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "Clear_Cavern_of_Corrosion_1_times": { + "name": "完成1次「侵蝕隧洞」", + "help": "需要設定並啟用\"每日副本\"任務", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "In_a_single_battle_inflict_3_Weakness_Break_of_different_Types": { + "name": "單場戰鬥中,觸發3種不同屬性的弱點擊破", + "help": "", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "Inflict_Weakness_Break_5_times": { + "name": "累積觸發弱點擊破效果5次", + "help": "", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "Defeat_a_total_of_20_enemies": { + "name": "累積消滅20個敵人", + "help": "", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "Enter_combat_by_attacking_enemy_Weakness_and_win_3_times": { + "name": "利用弱點進入戰鬥並獲勝3次", + "help": "", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "Use_Technique_2_times": { + "name": "累積施放2次秘技", + "help": "默認可完成,將前往深淵一施放2次秘技", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "Go_on_assignment_1_time": { + "name": "派遣1次委託", + "help": "需要設定並啟用\"委託\"任務", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "Take_1_photo": { + "name": "拍照1次", + "help": "默认可完成", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "Destroy_3_destructible_objects": { + "name": "累積擊碎3個可破壞物", + "help": "", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "Complete_Forgotten_Hall_1_time": { + "name": "完成1次「忘卻之庭」", + "help": "", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "Complete_Echo_of_War_1_times": { + "name": "完成1次「歷戰餘響」", + "help": "", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "Complete_1_stage_in_Simulated_Universe_Any_world": { + "name": "完成「模擬宇宙」任意世界的1個區域", + "help": "", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "Obtain_victory_in_combat_with_support_characters_1_time": { + "name": "使用支援角色並獲得戰鬥勝利1次", + "help": "需要設定並啟用\"每日副本\",且設並好友支援", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "Use_an_Ultimate_to_deal_the_final_blow_1_time": { + "name": "施放終結技造成制勝一擊1次", + "help": "", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "Level_up_any_character_1_time": { + "name": "將任意角色等級提升1次", + "help": "", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "Level_up_any_Light_Cone_1_time": { + "name": "將任意光錐等級提升1次", + "help": "", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "Level_up_any_Relic_1_time": { + "name": "將任意遺器等級提升1次", + "help": "", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "Salvage_any_Relic": { + "name": "分解任意1件遺器", + "help": "默認可完成,將分解遺器稀有度倒序的第一個", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "Synthesize_Consumable_1_time": { + "name": "合成1次消耗品", + "help": "默認可完成,將合成最低級零食", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "Synthesize_material_1_time": { + "name": "合成1次素材", + "help": "默認可完成,將合成最低級素材", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + }, + "Use_Consumables_1_time": { + "name": "使用1件消耗品", + "help": "默認可完成,將使用護具,無材料時先合成再使用", + "achievable": "可完成", + "not_set": "未設定", + "not_supported": "暫未支援" + } + }, + "DailyStorage": { + "_info": { + "name": "DailyStorage._info.name", + "help": "DailyStorage._info.help" + }, + "DailyActivity": { + "name": "DailyStorage.DailyActivity.name", + "help": "DailyStorage.DailyActivity.help" + }, + "DailyQuest": { + "name": "DailyStorage.DailyQuest.name", + "help": "DailyStorage.DailyQuest.help" + } + }, "Assignment": { "_info": { - "name": "委託設置", + "name": "委託設定", "help": "領取獎勵並派遣,優先處理指定委託\n若處理指定委託之後未達到上限,則按經驗材料 → 角色專屬素材 → 合成材料的順序來派遣委託" }, "Duration": { diff --git a/module/config/stored/classes.py b/module/config/stored/classes.py new file mode 100644 index 000000000..2127baa22 --- /dev/null +++ b/module/config/stored/classes.py @@ -0,0 +1,242 @@ +import time +from datetime import datetime +from functools import cached_property as functools_cached_property + +from module.base.decorator import cached_property +from module.config.utils import DEFAULT_TIME, deep_get, get_server_last_update +from module.exception import ScriptError + + +def now(): + return datetime.now().replace(microsecond=0) + + +def iter_attribute(cls): + """ + Args: + cls: Class or object + + Yields: + str, obj: Attribute name, attribute value + """ + for attr in dir(cls): + if attr.startswith('_'): + continue + value = getattr(cls, attr) + if type(value).__name__ in ['function', 'property']: + continue + yield attr, value + + +class StoredBase: + time = DEFAULT_TIME + + def __init__(self, key): + self._key = key + self._config = None + + @cached_property + def _name(self): + return self._key.split('.')[-1] + + def _bind(self, config): + """ + Args: + config (AzurLaneConfig): + """ + self._config = config + + @functools_cached_property + def _stored(self): + assert self._config is not None, 'StoredBase._bind() must be called before getting stored data' + from module.logger import logger + + out = {} + stored = deep_get(self._config.data, keys=self._key, default={}) + for attr, default in self._attrs.items(): + value = stored.get(attr, default) + if attr == 'time': + if not isinstance(value, datetime): + try: + value = datetime.fromisoformat(value) + except ValueError: + logger.warning(f'{self._name} has invalid attr: {attr}={value}, use default={default}') + value = default + else: + if not isinstance(value, type(default)): + logger.warning(f'{self._name} has invalid attr: {attr}={value}, use default={default}') + value = default + + out[attr] = value + return out + + @cached_property + def _attrs(self) -> dict: + """ + All attributes defined + """ + attrs = { + # time is the first one + 'time': DEFAULT_TIME + } + for attr, value in iter_attribute(self.__class__): + attrs[attr] = value + return attrs + + def __setattr__(self, key, value): + if key in self._attrs: + stored = self._stored + stored['time'] = now() + stored[key] = value + self._config.modified[self._key] = stored + if self._config.auto_update: + self._config.update() + else: + super().__setattr__(key, value) + + def __getattribute__(self, item): + if not item.startswith('_') and item in self._attrs: + return self._stored[item] + else: + return super().__getattribute__(item) + + def is_expired(self) -> bool: + return False + + def show(self): + """ + Log self + """ + from module.logger import logger + logger.attr(self._name, self._stored) + + def dashboard(self) -> str: + """ + Return a string to show on GUI + """ + return 'None' + + def readable_time(self): + diff = self.time.timestamp() - time.time() + if diff < -1: + return '', 'TimeError' + elif diff < 60: + # < 1 min + return '', 'JustNow' + elif diff < 3600: + return str(int(diff // 60)), 'MinutesAgo' + elif diff < 86400: + return str(int(diff // 86400)), 'HoursAgo' + elif diff < 129600: + return str(int(diff // 129600)), 'DaysAgo' + else: + # > 15 days + return '', 'LongTimeAgo' + + +class StoredExpiredAt0400(StoredBase): + def is_expired(self): + from module.logger import logger + self.show() + expired = self.time < get_server_last_update('04:00') + logger.attr(f'{self._name} expired', expired) + return expired + + +class StoredInt(StoredBase): + value = 0 + + +class StoredCounter(StoredBase): + current = 0 + total = 0 + + def set(self, current, total): + with self._config.multi_set(): + self.current = current + self.total = total + + def to_counter(self) -> str: + return f'{self.current}/{self.total}' + + def is_full(self) -> bool: + return self.current >= self.total + + def get_remain(self) -> int: + return self.total - self.current + + +class StoredDailyActivity(StoredCounter, StoredExpiredAt0400): + def set(self, current): + return super().set(current=current, total=500) + + @property + def _stored(self): + stored = super()._stored + stored['total'] = 500 + return stored + + +class StoredDaily(StoredExpiredAt0400): + quest1 = '' + quest2 = '' + quest3 = '' + quest4 = '' + quest5 = '' + quest6 = '' + + def load_quests(self): + """ + Returns: + list[DailyQuest]: Note that must check if quests are expired + """ + # 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]: + if not name: + continue + try: + quest = DailyQuest.find(name) + quests.append(quest) + except ScriptError: + pass + return quests + + def write_quests(self, quests): + """ + Args: + quests (list[DailyQuest, str]): + """ + from tasks.daily.keywords import DailyQuest + quests = [q.name if isinstance(q, DailyQuest) else q for q in quests] + with self._config.multi_set(): + try: + self.quest1 = quests[0] + except IndexError: + self.quest1 = '' + try: + self.quest2 = quests[1] + except IndexError: + self.quest2 = '' + try: + self.quest3 = quests[2] + except IndexError: + self.quest3 = '' + try: + self.quest4 = quests[3] + except IndexError: + self.quest4 = '' + try: + self.quest5 = quests[4] + except IndexError: + self.quest5 = '' + try: + self.quest6 = quests[5] + except IndexError: + self.quest6 = '' + + +class StoredDungeonDouble(StoredExpiredAt0400): + calyx = 0 + relic = 0 diff --git a/module/config/stored/stored_generated.py b/module/config/stored/stored_generated.py new file mode 100644 index 000000000..cea0b78e8 --- /dev/null +++ b/module/config/stored/stored_generated.py @@ -0,0 +1,18 @@ +from module.config.stored.classes import ( + StoredBase, + StoredCounter, + StoredDaily, + StoredDailyActivity, + StoredDungeonDouble, + StoredExpiredAt0400, + StoredInt, +) + + +# This file was auto-generated, do not modify it manually. To generate: +# ``` python -m module/config/config_updater.py ``` + +class StoredGenerated: + DungeonDouble = StoredDungeonDouble("Dungeon.DungeonStorage.DungeonDouble") + DailyActivity = StoredDailyActivity("DailyQuest.DailyStorage.DailyActivity") + DailyQuest = StoredDaily("DailyQuest.DailyStorage.DailyQuest") diff --git a/module/config/utils.py b/module/config/utils.py index 6fbca7479..4efb890d7 100644 --- a/module/config/utils.py +++ b/module/config/utils.py @@ -332,8 +332,9 @@ def data_to_type(data, **kwargs): """ | Condition | Type | | ------------------------------------ | -------- | - | Value is bool | checkbox | - | Arg has options | select | + | `value` is bool | checkbox | + | Arg has `options` | select | + | Arg has `stored` | select | | `Filter` is in name (in data['arg']) | textarea | | Rest of the args | input | @@ -345,10 +346,12 @@ def data_to_type(data, **kwargs): str: """ kwargs.update(data) - if isinstance(kwargs['value'], bool): + if isinstance(kwargs.get('value'), bool): return 'checkbox' elif 'option' in kwargs and kwargs['option']: return 'select' + elif 'stored' in kwargs and kwargs['stored']: + return 'stored' elif 'Filter' in kwargs['arg']: return 'textarea' else: diff --git a/tasks/combat/combat.py b/tasks/combat/combat.py index 44115f075..dd5511abe 100644 --- a/tasks/combat/combat.py +++ b/tasks/combat/combat.py @@ -3,13 +3,12 @@ from module.logger import logger from tasks.base.assets.assets_base_page import CLOSE from tasks.combat.assets.assets_combat_finish import COMBAT_AGAIN, COMBAT_EXIT from tasks.combat.assets.assets_combat_prepare import COMBAT_PREPARE -from tasks.combat.assets.assets_combat_team import COMBAT_TEAM_PREPARE, COMBAT_TEAM_SUPPORT, COMBAT_TEAM_DISMISSSUPPORT -from tasks.combat.assets.assets_combat_support import COMBAT_SUPPORT_ADD, COMBAT_SUPPORT_LIST +from tasks.combat.assets.assets_combat_team import COMBAT_TEAM_PREPARE, COMBAT_TEAM_SUPPORT from tasks.combat.interact import CombatInteract from tasks.combat.prepare import CombatPrepare from tasks.combat.state import CombatState -from tasks.combat.team import CombatTeam from tasks.combat.support import CombatSupport +from tasks.combat.team import CombatTeam from tasks.map.control.joystick import MapControlJoystick @@ -69,9 +68,8 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSuppo """ Args: team: 1 to 6. - skip_first_screenshot: support_character: Support character name - + Returns: bool: True if success to enter combat False if trialblaze power is not enough @@ -272,21 +270,23 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSuppo self.device.click(COMBAT_EXIT) continue - def combat(self, team: int = 1, wave_limit: int = 0, skip_first_screenshot=True, support_character: str = None): + def is_stamina_exhausted(self) -> bool: + flag = self.state.TrailblazePower < self.combat_wave_cost + logger.attr('StaminaExhausted', flag) + return flag + + def combat(self, team: int = 1, wave_limit: int = 0, support_character: str = None, skip_first_screenshot=True): """ Combat until trailblaze power runs out. Args: team: 1 to 6. wave_limit: Limit combat runs, 0 means no limit. - skip_first_screenshot: - use_support: "do_not_use", "always_use", "when_daily" - is_daily: True if is a daily task support_character: Support character name + skip_first_screenshot: Returns: - bool: True if trailblaze power exhausted - False if reached wave_limit but still have trailblaze power + int: Run count Pages: in: COMBAT_PREPARE @@ -298,6 +298,7 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSuppo self.combat_wave_limit = wave_limit self.combat_wave_done = 0 + run_count = 0 while 1: logger.hr('Combat', level=2) logger.info(f'Combat, team={team}, wave={self.combat_wave_done}/{self.combat_wave_limit}') @@ -312,7 +313,9 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSuppo finish = self.combat_finish() if self._combat_should_reenter(): continue + run_count += 1 if finish: break - return self.state.TrailblazePower < self.combat_wave_cost + logger.attr('CombatRunCount', run_count) + return run_count diff --git a/tasks/combat/state.py b/tasks/combat/state.py index 25cb85d9d..b396f8c74 100644 --- a/tasks/combat/state.py +++ b/tasks/combat/state.py @@ -94,6 +94,7 @@ class CombatState(UI): self._combat_auto_checked = True else: if self._combat_click_interval.reached(): + self.device.image_save() self.device.click(COMBAT_AUTO) self._combat_click_interval.reset() return True diff --git a/tasks/daily/daily_quest.py b/tasks/daily/daily_quest.py index 4146ee9ba..ef1309690 100644 --- a/tasks/daily/daily_quest.py +++ b/tasks/daily/daily_quest.py @@ -99,6 +99,7 @@ class DailyQuestUI(DungeonUI): results = [result.matched_keyword for result in results] logger.info("Daily quests recognition complete") logger.info(f"Daily quests: {results}") + self.config.stored.DailyQuest.write_quests(results) return results def _get_quest_reward(self, skip_first_screenshot=True): @@ -128,7 +129,7 @@ class DailyQuestUI(DungeonUI): def _get_active_point_reward(self, skip_first_screenshot=True): def get_active(): - for button in [ + for b in [ ACTIVE_POINTS_1_UNLOCK, ACTIVE_POINTS_2_UNLOCK, ACTIVE_POINTS_3_UNLOCK, @@ -136,8 +137,8 @@ class DailyQuestUI(DungeonUI): ACTIVE_POINTS_5_UNLOCK ]: # Black gift icon - if self.image_color_count(button, color=(61, 53, 53), threshold=221, count=100): - return button + if self.image_color_count(b, color=(61, 53, 53), threshold=221, count=100): + return b return None interval = Timer(2) @@ -156,6 +157,26 @@ class DailyQuestUI(DungeonUI): self.device.click(active) interval.reset() + # Write stored + point = 0 + for progress, button in zip( + [100, 200, 300, 400, 500], + [ + ACTIVE_POINTS_1_CHECKED, + ACTIVE_POINTS_2_CHECKED, + ACTIVE_POINTS_3_CHECKED, + ACTIVE_POINTS_4_CHECKED, + ACTIVE_POINTS_5_CHECKED + ] + ): + if self.appear(button): + point = progress + logger.attr('Daily activity', point) + with self.config.multi_set(): + self.config.stored.DailyActivity.set(point) + if point == 500: + self.config.stored.DailyQuest.write_quests([]) + def get_daily_rewards(self): """ Returns: diff --git a/tasks/dungeon/dungeon.py b/tasks/dungeon/dungeon.py index dabb256e6..4553e90d8 100644 --- a/tasks/dungeon/dungeon.py +++ b/tasks/dungeon/dungeon.py @@ -1,58 +1,219 @@ from module.base.utils import area_offset from module.logger import logger from tasks.combat.combat import Combat +from tasks.daily.keywords import KEYWORDS_DAILY_QUEST from tasks.dungeon.event import DungeonEvent from tasks.dungeon.keywords import DungeonList, KEYWORDS_DUNGEON_LIST, KEYWORDS_DUNGEON_TAB from tasks.dungeon.ui import DungeonUI class Dungeon(DungeonUI, DungeonEvent, Combat): - def run(self, dungeon: DungeonList = None, team: int = None, use_support: str = None, is_daily: bool = False, - support_character: str = None): - if dungeon is None: - dungeon = DungeonList.find(self.config.Dungeon_Name) + called_daily_support = False + achieved_daily_quest = False + daily_quests = [] + + def _dungeon_run(self, dungeon: DungeonList, team: int = None, wave_limit: int = 0, support_character: str = None, + skip_ui_switch: bool = False): + """ + Args: + dungeon: + team: 1 to 6. + wave_limit: Limit combat runs, 0 means no limit. + support_character: Support character name + skip_ui_switch: True if already at dungeon aside + + Returns: + int: Run count + + Pages: + in: Any + out: page_main + """ if team is None: team = self.config.Dungeon_Team - if use_support is None: - use_support = self.config.Dungeon_Support - if support_character is None: - support_character = self.config.Dungeon_SupportCharacter if use_support == "always_use" or use_support == "when_daily" and is_daily else None + if support_character is None and self.config.DungeonSupport_Use == 'always_use': + support_character = self.config.DungeonSupport_Character - # UI switches - switched = self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index) - if not switched: - # Nav must at top, reset nav states - self.ui_goto_main() + logger.hr('Dungeon run', level=1) + logger.info(f'Dungeon: {dungeon}, team={team}, wave_limit={wave_limit}, support_character={support_character}') + + if not skip_ui_switch: self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index) + self.dungeon_goto(dungeon) - # Check double events - if self.config.Dungeon_NameAtDoubleCalyx != 'do_not_participate' and self.has_double_calyx_event(): - calyx = DungeonList.find(self.config.Dungeon_NameAtDoubleCalyx) - self._dungeon_nav_goto(calyx) - if remain := self.get_double_event_remain(): - self.dungeon_goto(calyx) - if self.combat(team, wave_limit=remain, support_character=support_character): - self.delay_dungeon_task(calyx) - self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index) - if self.config.Dungeon_NameAtDoubleRelic != 'do_not_participate' and self.has_double_relic_event(): - calyx = DungeonList.find(self.config.Dungeon_NameAtDoubleRelic) - self._dungeon_nav_goto(calyx) - if remain := self.get_double_event_remain(): - self.dungeon_goto(calyx) - if self.combat(team, wave_limit=remain, support_character=support_character): - self.delay_dungeon_task(calyx) - self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index) + if dungeon == KEYWORDS_DUNGEON_LIST.Stagnant_Shadow_Blaze: + if self.handle_destructible_around_blaze(): + self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index) + self.dungeon_goto(dungeon) # Combat - self.dungeon_goto(dungeon) + count = self.combat(team=team, wave_limit=wave_limit, support_character=support_character) - if dungeon == KEYWORDS_DUNGEON_LIST.Stagnant_Shadow_Blaze: - if self.handle_destructible_around_blaze(): + # Update quest states + if dungeon.is_Calyx_Golden \ + and KEYWORDS_DAILY_QUEST.Clear_Calyx_Golden_1_times in self.daily_quests: + logger.info('Achieved daily quest Clear_Calyx_Golden_1_times') + self.achieved_daily_quest = True + if dungeon.is_Calyx_Crimson \ + and KEYWORDS_DAILY_QUEST.Complete_Calyx_Crimson_1_time in self.daily_quests: + logger.info('Achieve daily quest Complete_Calyx_Crimson_1_time') + self.achieved_daily_quest = True + if dungeon.is_Stagnant_Shadow \ + and 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 dungeon.is_Cavern_of_Corrosion \ + and KEYWORDS_DAILY_QUEST.Clear_Cavern_of_Corrosion_1_times in self.daily_quests: + logger.info('Achieve daily quest Clear_Cavern_of_Corrosion_1_times') + self.achieved_daily_quest = True + if support_character is not None: + self.called_daily_support = True + if KEYWORDS_DAILY_QUEST.Obtain_victory_in_combat_with_support_characters_1_time: + logger.info('Achieve daily quest Obtain_victory_in_combat_with_support_characters_1_time') + self.achieved_daily_quest = True + + # Check stamina, this may stop current task + if self.is_stamina_exhausted(): + self.delay_dungeon_task(dungeon) + return count + + def dungeon_run( + self, dungeon: DungeonList, team: int = None, wave_limit: int = 0, support_character: str = None): + """ + Run dungeon, and handle daily support + + Args: + dungeon: + team: 1 to 6. + wave_limit: Limit combat runs, 0 means no limit. + support_character: Support character name + + Returns: + int: Run count + + Pages: + in: Any + out: page_main + """ + require = self.require_compulsory_support() + if require: + logger.info('Run once with support') + count = self._dungeon_run(dungeon=dungeon, team=team, wave_limit=1, + support_character=self.config.DungeonSupport_Character) + + logger.info('Run the rest waves without compulsory support') + if wave_limit >= 2 or wave_limit == 0: + # Already at page_name with DUNGEON_COMBAT_INTERACT + if wave_limit >= 2: + wave_limit -= 1 + count += self._dungeon_run(dungeon=dungeon, team=team, wave_limit=wave_limit, + support_character=support_character, skip_ui_switch=True) + + return count + + else: + # Normal run + return self._dungeon_run(dungeon=dungeon, team=team, wave_limit=wave_limit, + support_character=support_character) + + def run(self): + # Update daily quests + if self.config.stored.DailyActivity.is_expired(): + logger.info('Daily activity expired, call task to update') + self.config.task_call('DailyQuest') + self.config.task_stop() + if self.config.stored.DailyQuest.is_expired(): + logger.info('Daily quests expired, call task to update') + self.config.task_call('DailyQuest') + self.config.task_stop() + + self.called_daily_support = False + self.achieved_daily_quest = False + self.daily_quests = self.config.stored.DailyQuest.load_quests() + + # Update double event records + if self.config.stored.DungeonDouble.is_expired(): + logger.info('Get dungeon double remains') + # UI switches + switched = self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index) + if not switched: + # Nav must at top, reset nav states + self.ui_goto_main() self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index) - self.dungeon_goto(dungeon) + # Check remains + calyx = 0 + relic = 0 + if self.has_double_calyx_event(): + self._dungeon_nav_goto(KEYWORDS_DUNGEON_LIST.Calyx_Golden_Treasures) + calyx = self.get_double_event_remain() + if self.has_double_relic_event(): + self._dungeon_nav_goto(KEYWORDS_DUNGEON_LIST.Cavern_of_Corrosion_Path_of_Gelid_Wind) + relic = self.get_double_event_remain() + with self.config.multi_set(): + self.config.stored.DungeonDouble.calyx = calyx + self.config.stored.DungeonDouble.relic = relic - self.combat(team=team, support_character=support_character) - self.delay_dungeon_task(dungeon) + # Run double events + ran_calyx_golden = False + ran_calyx_crimson = False + ran_cavern_of_corrosion = False + # Double calyx + if self.config.Dungeon_NameAtDoubleCalyx != 'do_not_participate' \ + and self.config.stored.DungeonDouble.calyx > 0: + logger.info('Run double calyx') + dungeon = DungeonList.find(self.config.Dungeon_NameAtDoubleCalyx) + if self.dungeon_run(dungeon=dungeon, wave_limit=self.config.stored.DungeonDouble.calyx): + if dungeon.is_Calyx_Golden: + ran_calyx_golden = True + if dungeon.is_Calyx_Crimson: + ran_calyx_crimson = True + # Double relic + if self.config.Dungeon_NameAtDoubleRelic != 'do_not_participate' \ + and self.config.stored.DungeonDouble.relic > 0: + logger.info('Run double relic') + dungeon = DungeonList.find(self.config.Dungeon_NameAtDoubleRelic) + if self.dungeon_run(dungeon=dungeon, wave_limit=self.config.stored.DungeonDouble.relic): + ran_cavern_of_corrosion = True + + # Dungeon to clear all trailblaze power + final = DungeonList.find(self.config.Dungeon_Name) + + # Run dungeon that required by daily quests + # Calyx_Golden + if KEYWORDS_DAILY_QUEST.Clear_Calyx_Golden_1_times in self.daily_quests \ + and self.config.DungeonDaily_CalyxGolden != 'do_not_achieve' \ + and not final.is_Calyx_Golden \ + and not ran_calyx_golden: + logger.info('Run Calyx_Golden once') + dungeon = DungeonList.find(self.config.DungeonDaily_CalyxGolden) + self.dungeon_run(dungeon=dungeon, wave_limit=1) + # Calyx_Crimson + if KEYWORDS_DAILY_QUEST.Complete_Calyx_Crimson_1_time in self.daily_quests \ + and self.config.DungeonDaily_CalyxCrimson != 'do_not_achieve' \ + and not final.is_Calyx_Crimson \ + and not ran_calyx_crimson: + logger.info('Run Calyx_Crimson once') + dungeon = DungeonList.find(self.config.DungeonDaily_CalyxCrimson) + self.dungeon_run(dungeon=dungeon, wave_limit=1) + # Stagnant_Shadow + if KEYWORDS_DAILY_QUEST.Clear_Stagnant_Shadow_1_times in self.daily_quests \ + and self.config.DungeonDaily_StagnantShadow != 'do_not_achieve' \ + and not final.is_Stagnant_Shadow: + logger.info('Run Stagnant_Shadow once') + dungeon = DungeonList.find(self.config.DungeonDaily_StagnantShadow) + self.dungeon_run(dungeon=dungeon, wave_limit=1) + # Cavern_of_Corrosion + if KEYWORDS_DAILY_QUEST.Clear_Cavern_of_Corrosion_1_times in self.daily_quests \ + and self.config.DungeonDaily_CavernOfCorrosion != 'do_not_achieve' \ + and not final.is_Cavern_of_Corrosion \ + and not ran_cavern_of_corrosion: + logger.info('Run Cavern_of_Corrosion once') + dungeon = DungeonList.find(self.config.DungeonDaily_CavernOfCorrosion) + self.dungeon_run(dungeon=dungeon, wave_limit=1) + + # Combat + self.dungeon_run(final) + self.delay_dungeon_task(final) def delay_dungeon_task(self, dungeon): if dungeon.is_Cavern_of_Corrosion: @@ -62,7 +223,11 @@ class Dungeon(DungeonUI, DungeonEvent, Combat): # Recover 1 trailbaze power each 6 minutes cover = max(limit - self.state.TrailblazePower, 0) * 6 logger.info(f'Currently has {self.state.TrailblazePower} need {cover} minutes to reach {limit}') - self.config.task_delay(minute=cover) + logger.attr('achieved_daily_quest', self.achieved_daily_quest) + with self.config.multi_set(): + if self.achieved_daily_quest: + self.config.task_call('DailyQuest') + self.config.task_delay(minute=cover) self.config.task_stop() def handle_destructible_around_blaze(self): @@ -104,3 +269,23 @@ class Dungeon(DungeonUI, DungeonEvent, Combat): break return handled + + def require_compulsory_support(self) -> bool: + require = False + + if not self.config.stored.DailyActivity.is_full(): + if KEYWORDS_DAILY_QUEST.Obtain_victory_in_combat_with_support_characters_1_time \ + in self.daily_quests: + require = True + + logger.attr('called_daily_support', self.called_daily_support) + if self.called_daily_support: + require = False + + # Not required, cause any dungeon run will achieve the quest + logger.attr('DungeonSupport_Use', self.config.DungeonSupport_Use) + if self.config.DungeonSupport_Use == 'always_use': + require = False + + logger.attr('Require compulsory support', require) + return require diff --git a/tasks/dungeon/keywords/classes.py b/tasks/dungeon/keywords/classes.py index 5c49a79d9..39e482dbb 100644 --- a/tasks/dungeon/keywords/classes.py +++ b/tasks/dungeon/keywords/classes.py @@ -28,6 +28,10 @@ class DungeonList(Keyword): def is_Calyx_Crimson(self): return 'Calyx_Crimson' in self.name + @cached_property + def is_Calyx(self): + return self.is_Calyx_Golden or self.is_Calyx_Crimson + @cached_property def is_Stagnant_Shadow(self): return 'Stagnant_Shadow' in self.name