diff --git a/assets/share/rogue/blessing/BLESSING_ENFORCE.png b/assets/share/rogue/blessing/BLESSING_ENFORCE.png new file mode 100644 index 000000000..6d2ff2d6a Binary files /dev/null and b/assets/share/rogue/blessing/BLESSING_ENFORCE.png differ diff --git a/assets/share/rogue/blessing/BLESSING_RESET.png b/assets/share/rogue/blessing/BLESSING_RESET.png new file mode 100644 index 000000000..fafb39edf Binary files /dev/null and b/assets/share/rogue/blessing/BLESSING_RESET.png differ diff --git a/assets/share/rogue/blessing/BLESSING_STABLE_FLAG.png b/assets/share/rogue/blessing/BLESSING_STABLE_FLAG.png new file mode 100644 index 000000000..8f4d6ca80 Binary files /dev/null and b/assets/share/rogue/blessing/BLESSING_STABLE_FLAG.png differ diff --git a/assets/share/rogue/blessing/BOTTOM_WHITE_BAR.png b/assets/share/rogue/blessing/BOTTOM_WHITE_BAR.png new file mode 100644 index 000000000..74bf6c46f Binary files /dev/null and b/assets/share/rogue/blessing/BOTTOM_WHITE_BAR.png differ diff --git a/assets/share/rogue/blessing/OCR_RESET_COST.png b/assets/share/rogue/blessing/OCR_RESET_COST.png new file mode 100644 index 000000000..4ce490747 Binary files /dev/null and b/assets/share/rogue/blessing/OCR_RESET_COST.png differ diff --git a/assets/share/rogue/blessing/OCR_RESET_COUNT.png b/assets/share/rogue/blessing/OCR_RESET_COUNT.png new file mode 100644 index 000000000..4a7884e81 Binary files /dev/null and b/assets/share/rogue/blessing/OCR_RESET_COUNT.png differ diff --git a/assets/share/rogue/blessing/OCR_ROGUE_BUFF.png b/assets/share/rogue/blessing/OCR_ROGUE_BUFF.png new file mode 100644 index 000000000..cde3737c9 Binary files /dev/null and b/assets/share/rogue/blessing/OCR_ROGUE_BUFF.png differ diff --git a/assets/share/rogue/bonus/BONUS_BOTTOM_WHITE_BAR.png b/assets/share/rogue/bonus/BONUS_BOTTOM_WHITE_BAR.png new file mode 100644 index 000000000..0f0894ff0 Binary files /dev/null and b/assets/share/rogue/bonus/BONUS_BOTTOM_WHITE_BAR.png differ diff --git a/assets/share/rogue/bonus/BONUS_CONFIRM.png b/assets/share/rogue/bonus/BONUS_CONFIRM.png new file mode 100644 index 000000000..23152aedb Binary files /dev/null and b/assets/share/rogue/bonus/BONUS_CONFIRM.png differ diff --git a/assets/share/rogue/curio/CURIO_ENFORCE.png b/assets/share/rogue/curio/CURIO_ENFORCE.png new file mode 100644 index 000000000..f392b0894 Binary files /dev/null and b/assets/share/rogue/curio/CURIO_ENFORCE.png differ diff --git a/assets/share/rogue/curio/OCR_ROGUE_CURIO.png b/assets/share/rogue/curio/OCR_ROGUE_CURIO.png new file mode 100644 index 000000000..31620aa52 Binary files /dev/null and b/assets/share/rogue/curio/OCR_ROGUE_CURIO.png differ diff --git a/assets/share/rogue/ui/BLESSING_CONFIRM.png b/assets/share/rogue/ui/BLESSING_CONFIRM.png new file mode 100644 index 000000000..b54b5345c Binary files /dev/null and b/assets/share/rogue/ui/BLESSING_CONFIRM.png differ diff --git a/assets/share/rogue/ui/CHECK_BLESSING.png b/assets/share/rogue/ui/CHECK_BLESSING.png new file mode 100644 index 000000000..049e4d68e Binary files /dev/null and b/assets/share/rogue/ui/CHECK_BLESSING.png differ diff --git a/assets/share/rogue/ui/COSMIC_FRAGMENT.png b/assets/share/rogue/ui/COSMIC_FRAGMENT.png new file mode 100644 index 000000000..d1ea59075 Binary files /dev/null and b/assets/share/rogue/ui/COSMIC_FRAGMENT.png differ diff --git a/assets/share/rogue/ui/FLAG_UNRECORD.png b/assets/share/rogue/ui/FLAG_UNRECORD.png new file mode 100644 index 000000000..40e34f265 Binary files /dev/null and b/assets/share/rogue/ui/FLAG_UNRECORD.png differ diff --git a/assets/share/rogue/ui/OBTAIN_ITEM_POPUP.png b/assets/share/rogue/ui/OBTAIN_ITEM_POPUP.png new file mode 100644 index 000000000..39aa27a23 Binary files /dev/null and b/assets/share/rogue/ui/OBTAIN_ITEM_POPUP.png differ diff --git a/assets/share/rogue/ui/OCR_COSMIC_FRAGMENT.png b/assets/share/rogue/ui/OCR_COSMIC_FRAGMENT.png new file mode 100644 index 000000000..067bb3a73 Binary files /dev/null and b/assets/share/rogue/ui/OCR_COSMIC_FRAGMENT.png differ diff --git a/assets/share/rogue/ui/PAGE_CHOOSE_BONUS.png b/assets/share/rogue/ui/PAGE_CHOOSE_BONUS.png new file mode 100644 index 000000000..637df54d1 Binary files /dev/null and b/assets/share/rogue/ui/PAGE_CHOOSE_BONUS.png differ diff --git a/assets/share/rogue/ui/PAGE_CHOOSE_BUFF.png b/assets/share/rogue/ui/PAGE_CHOOSE_BUFF.png new file mode 100644 index 000000000..44b01a880 Binary files /dev/null and b/assets/share/rogue/ui/PAGE_CHOOSE_BUFF.png differ diff --git a/assets/share/rogue/ui/PAGE_CHOOSE_CURIO.png b/assets/share/rogue/ui/PAGE_CHOOSE_CURIO.png new file mode 100644 index 000000000..1af80558f Binary files /dev/null and b/assets/share/rogue/ui/PAGE_CHOOSE_CURIO.png differ diff --git a/assets/share/rogue/ui/PAGE_EVENT.png b/assets/share/rogue/ui/PAGE_EVENT.png new file mode 100644 index 000000000..c9c6abbff Binary files /dev/null and b/assets/share/rogue/ui/PAGE_EVENT.png differ diff --git a/config/template.json b/config/template.json index 172acfe23..10fc8f8b8 100644 --- a/config/template.json +++ b/config/template.json @@ -127,5 +127,26 @@ "Duration": 20, "Assignment": {} } + }, + "Rogue": { + "Scheduler": { + "Enable": false, + "NextRun": "2020-01-01 00:00:00", + "Command": "Rogue", + "ServerUpdate": "04:00" + }, + "Rogue": { + "Path": "The Hunt", + "Bonus": "Blessing Cosmos", + "PresetResonanceFilter": "preset-1", + "CustomResonanceFilter": "回响构音:均晶转变 > 回响构音:零维强化\n> 回响构音:第二次初恋 > 回响构音:体验的富翁\n> 回响构音:局外人 > 回响构音:怀疑的四重根\n> 回响构音:诸法无我 > 回响构音:诸行无常\n> 回响构音:射不主皮 > 回响构音:柘弓危矢\n> 回响构音:激变变星 > 回响构音:极端氦闪\n> 回响构音:末日狂欢 > 回响构音:树苗长高舞", + "ResonanceSelectionStrategy": "follow-presets", + "PresetBlessingFilter": "preset-1", + "CustomBlessingFilter": "巡猎-3 > 《冠军晚餐·猫的摇篮》 > 丰饶众生,一法界心 > 毁灭-3 \n> 火堆外的夜 > 巡猎-2 > 毁灭-2 > 巡猎 > reset > random", + "BlessingSelectionStrategy": "follow-presets", + "PresetCurioFilter": "preset-1", + "CustomCurioFilter": "博士之袍 > 福灵胶 > 分裂金币 > 信仰债券 > 换境桂冠 > 俱乐部券 > 碎星芳饵 > random", + "CurioSelectionStrategy": "follow-presets" + } } } \ No newline at end of file diff --git a/dev_tools/keyword_extract.py b/dev_tools/keyword_extract.py index 82787c90b..874b175b2 100644 --- a/dev_tools/keyword_extract.py +++ b/dev_tools/keyword_extract.py @@ -13,8 +13,8 @@ UI_LANGUAGES = ['cn', 'cht', 'en', 'jp'] def text_to_variable(text): text = re.sub("'s |s' ", '_', text) - text = re.sub('[ \-—:\'/•]+', '_', text) - text = re.sub(r'[(),#"?]|', '', text) + text = re.sub('[ \-—:\'/•.]+', '_', text) + text = re.sub(r'[(),#"?!&]|', '', text) # text = re.sub(r'[#_]?\d+(_times?)?', '', text) return text @@ -30,6 +30,12 @@ def dungeon_name(name: str) -> str: return name +def blessing_name(name: str) -> str: + name = text_to_variable(name) + name = re.sub(r'^\d', lambda match: f"_{match.group(0)}", name) + return name + + nickname_count = 0 @@ -147,7 +153,8 @@ class KeywordExtract: keyword_class, output_file: str = '', text_convert=text_to_variable, - generator: CodeGenerator = None + generator: CodeGenerator = None, + extra_attrs: dict[str, dict] = None ): """ Args: @@ -155,6 +162,7 @@ class KeywordExtract: output_file: text_convert: generator: Reuse an existing code generator + extra_attrs: Extra attributes write in keywords """ if generator is None: gen = CodeGenerator() @@ -166,6 +174,12 @@ class KeywordExtract: gen = generator last_id = getattr(gen, 'last_id', 0) + if extra_attrs: + keyword_num = len(self.keywords_id) + for attr_key, attr_value in extra_attrs.items(): + if len(attr_value) != keyword_num: + print(f"Extra attribute {attr_key} does not match the size of keywords") + return for index, keyword in enumerate(self.keywords_id): _, name = self.find_keyword(keyword, lang='en') name = text_convert(replace_templates(name)) @@ -174,6 +188,9 @@ class KeywordExtract: gen.ObjectAttr(key='name', value=name) for lang in UI_LANGUAGES: gen.ObjectAttr(key=lang, value=replace_templates(self.find_keyword(keyword, lang=lang)[1])) + if extra_attrs: + for attr_key, attr_value in extra_attrs.items(): + gen.ObjectAttr(key=attr_key, value=attr_value[keyword]) gen.last_id = index + last_id + 1 if output_file: @@ -279,6 +296,67 @@ class KeywordExtract: self.load_quests(quests) self.write_keywords(keyword_class='BattlePassQuest', output_file='./tasks/battle_pass/keywords/quest.py') + def generate_rogue_buff(self): + # paths + aeons = read_file(os.path.join(TextMap.DATA_FOLDER, 'ExcelOutput', 'RogueAeon.json')) + aeons_hash = [deep_get(aeon, '1.RogueAeonPathName2.Hash') for aeon in aeons.values()] + self.keywords_id = aeons_hash + self.write_keywords(keyword_class='RoguePath', output_file='./tasks/rogue/keywords/path.py') + + # blessings + blessings_info = read_file(os.path.join(TextMap.DATA_FOLDER, 'ExcelOutput', 'RogueBuff.json')) + blessings_name_map = read_file(os.path.join(TextMap.DATA_FOLDER, 'ExcelOutput', 'RogueMazeBuff.json')) + blessings_id = [deep_get(blessing, '1.MazeBuffID') for blessing in blessings_info.values() + if not deep_get(blessing, '1.AeonID')][1:] + resonances_id = [deep_get(blessing, '1.MazeBuffID') for blessing in blessings_info.values() + if deep_get(blessing, '1.AeonID')] + + def get_blessing_infos(id_list, with_enhancement: bool): + blessings_hash = [deep_get(blessings_name_map, f"{blessing_id}.1.BuffName.Hash") + for blessing_id in id_list] + blessings_path_id = {blessing_hash: int(deep_get(blessings_info, f'{blessing_id}.1.RogueBuffType')) - 119 + # 119 is the magic number make type match with path in keyword above + for blessing_hash, blessing_id in zip(blessings_hash, id_list)} + blessings_rarity = {blessing_hash: deep_get(blessings_info, f'{blessing_id}.1.RogueBuffRarity') + for blessing_hash, blessing_id in zip(blessings_hash, id_list)} + enhancement = {blessing_hash: "" for blessing_hash in blessings_hash} + if with_enhancement: + return blessings_hash, {'path_id': blessings_path_id, 'rarity': blessings_rarity, + 'enhancement': enhancement} + else: + return blessings_hash, {'path_id': blessings_path_id, 'rarity': blessings_rarity} + + hash_list, extra_attrs = get_blessing_infos(blessings_id, with_enhancement=True) + self.keywords_id = hash_list + self.write_keywords(keyword_class='RogueBlessing', output_file='./tasks/rogue/keywords/blessing.py', + text_convert=blessing_name, extra_attrs=extra_attrs) + + hash_list, extra_attrs = get_blessing_infos(resonances_id, with_enhancement=False) + self.keywords_id = hash_list + self.write_keywords(keyword_class='RogueResonance', output_file='./tasks/rogue/keywords/resonance.py', + text_convert=blessing_name, extra_attrs=extra_attrs) + + def iter_without_duplication(self, file: dict, keys): + visited = set() + for data in file.values(): + hash_ = deep_get(data, keys=keys) + _, name = self.find_keyword(hash_, lang='cn') + if name in visited: + continue + visited.add(name) + yield hash_ + + def iter_rogue_miracles(self): + miracles = read_file(os.path.join(TextMap.DATA_FOLDER, 'ExcelOutput', 'RogueMiracle.json')) + visited = set() + for data in miracles.values(): + hash_ = deep_get(data, keys='MiracleName.Hash') + _, name = self.find_keyword(hash_, lang='cn') + if name in visited: + continue + visited.add(name) + yield hash_ + def generate(self): self.load_keywords(['模拟宇宙', '拟造花萼(金)', '拟造花萼(赤)', '凝滞虚影', '侵蚀隧洞', '历战余响', '忘却之庭']) self.write_keywords(keyword_class='DungeonNav', output_file='./tasks/dungeon/keywords/nav.py') @@ -308,6 +386,15 @@ class KeywordExtract: self.load_keywords(['养成材料', '光锥', '遗器', '其他材料', '消耗品', '任务', '贵重物']) self.write_keywords(keyword_class='ItemTab', text_convert=lambda name: name.replace(' ', ''), output_file='./tasks/item/keywords/tab.py') + self.generate_rogue_buff() + self.load_keywords(['已强化']) + self.write_keywords(keyword_class='RogueEnhancement', output_file='./tasks/rogue/keywords/enhancement.py') + self.load_keywords(list(self.iter_without_duplication( + read_file(os.path.join(TextMap.DATA_FOLDER, 'ExcelOutput', 'RogueMiracle.json')), 'MiracleName.Hash'))) + self.write_keywords(keyword_class='RogueCurio', output_file='./tasks/rogue/keywords/curio.py') + self.load_keywords(list(self.iter_without_duplication( + read_file(os.path.join(TextMap.DATA_FOLDER, 'ExcelOutput', 'RogueBonus.json')), 'BonusTitle.Hash'))) + self.write_keywords(keyword_class='RogueBonus', output_file='./tasks/rogue/keywords/bonus.py') if __name__ == '__main__': diff --git a/module/base/filter.py b/module/base/filter.py index baac89085..9d029384a 100644 --- a/module/base/filter.py +++ b/module/base/filter.py @@ -101,3 +101,35 @@ class Filter: # Invalid filter will be ignored. # Return strange things and make it impossible to match return ['1nVa1d'] + [None] * (len(self.attr) - 1) + + +class MultiLangFilter(Filter): + """ + To support multi-language, there might be different correct matches of same object. + """ + + def apply_filter_to_obj(self, obj, filter): + """ + Args: + obj (object): In this case, attributes of object are array (instead of plain string). + Any match of element in it will return True + filter (list[str]): + + Returns: + bool: If an object satisfy a filter. + """ + for attr, value in zip(self.attr, filter): + if not value: + continue + if not hasattr(obj, attr): + continue + + obj_value = obj.__getattribute__(attr) + if isinstance(obj_value, (str, int)): + if str(obj_value).lower() != str(value): + return False + if isinstance(obj_value, list): + if value not in obj_value: + return False + + return True diff --git a/module/config/argument/args.json b/module/config/argument/args.json index c745ba0d3..6eb3adbaf 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -982,5 +982,115 @@ "color": "#79dbc4" } } + }, + "Rogue": { + "Scheduler": { + "Enable": { + "type": "checkbox", + "value": false + }, + "NextRun": { + "type": "datetime", + "value": "2020-01-01 00:00:00", + "validate": "datetime" + }, + "Command": { + "type": "input", + "value": "Rogue", + "display": "hide" + }, + "ServerUpdate": { + "type": "input", + "value": "04:00", + "display": "hide" + } + }, + "Rogue": { + "Path": { + "type": "select", + "value": "The Hunt", + "option": [ + "Preservation", + "Remembrance", + "Nihility", + "Abundance", + "Hunt", + "Destruction", + "Elation" + ] + }, + "Bonus": { + "type": "select", + "value": "Blessing Cosmos", + "option": [ + "Blessing Cosmos", + "Miracle Cosmos", + "Fragmented Cosmos" + ] + }, + "PresetResonanceFilter": { + "type": "select", + "value": "preset-1", + "option": [ + "preset-1", + "custom" + ] + }, + "CustomResonanceFilter": { + "type": "textarea", + "value": "回响构音:均晶转变 > 回响构音:零维强化\n> 回响构音:第二次初恋 > 回响构音:体验的富翁\n> 回响构音:局外人 > 回响构音:怀疑的四重根\n> 回响构音:诸法无我 > 回响构音:诸行无常\n> 回响构音:射不主皮 > 回响构音:柘弓危矢\n> 回响构音:激变变星 > 回响构音:极端氦闪\n> 回响构音:末日狂欢 > 回响构音:树苗长高舞" + }, + "ResonanceSelectionStrategy": { + "type": "select", + "value": "follow-presets", + "option": [ + "follow-presets", + "unrecorded-first", + "before-random" + ] + }, + "PresetBlessingFilter": { + "type": "select", + "value": "preset-1", + "option": [ + "preset-1", + "custom" + ] + }, + "CustomBlessingFilter": { + "type": "textarea", + "value": "巡猎-3 > 《冠军晚餐·猫的摇篮》 > 丰饶众生,一法界心 > 毁灭-3 \n> 火堆外的夜 > 巡猎-2 > 毁灭-2 > 巡猎 > reset > random" + }, + "BlessingSelectionStrategy": { + "type": "select", + "value": "follow-presets", + "option": [ + "follow-presets", + "unrecorded-first", + "before-random" + ] + }, + "PresetCurioFilter": { + "type": "select", + "value": "preset-1", + "option": [ + "preset-1", + "custom" + ] + }, + "CustomCurioFilter": { + "type": "textarea", + "value": "博士之袍 > 福灵胶 > 分裂金币 > 信仰债券 > 换境桂冠 > 俱乐部券 > 碎星芳饵 > random" + }, + "CurioSelectionStrategy": { + "type": "select", + "value": "follow-presets", + "option": [ + "follow-presets", + "unrecorded-first", + "before-random" + ] + } + } } } \ No newline at end of file diff --git a/module/config/argument/argument.yaml b/module/config/argument/argument.yaml index 79395b9d1..3488c78d2 100644 --- a/module/config/argument/argument.yaml +++ b/module/config/argument/argument.yaml @@ -164,3 +164,45 @@ Assignment: stored: StoredAssignment order: 3 color: "#79dbc4" + +# ==================== Rogue ==================== + +Rogue: + Path: + value: The Hunt + option: [ Preservation, Remembrance, Nihility, Abundance, Hunt, Destruction, Elation ] + Bonus: + value: Blessing Cosmos + option: [ Blessing Cosmos, Miracle Cosmos, Fragmented Cosmos ] + PresetResonanceFilter: + value: preset-1 + option: [ preset-1, custom ] + CustomResonanceFilter: |- + 回响构音:均晶转变 > 回响构音:零维强化 + > 回响构音:第二次初恋 > 回响构音:体验的富翁 + > 回响构音:局外人 > 回响构音:怀疑的四重根 + > 回响构音:诸法无我 > 回响构音:诸行无常 + > 回响构音:射不主皮 > 回响构音:柘弓危矢 + > 回响构音:激变变星 > 回响构音:极端氦闪 + > 回响构音:末日狂欢 > 回响构音:树苗长高舞 + ResonanceSelectionStrategy: + value: follow-presets + option: [ follow-presets, unrecorded-first, before-random ] + PresetBlessingFilter: + value: preset-1 + option: [ preset-1, custom ] + CustomBlessingFilter: |- + 巡猎-3 > 《冠军晚餐·猫的摇篮》 > 丰饶众生,一法界心 > 毁灭-3 + > 火堆外的夜 > 巡猎-2 > 毁灭-2 > 巡猎 > reset > random + BlessingSelectionStrategy: + value: follow-presets + option: [ follow-presets, unrecorded-first, before-random ] + PresetCurioFilter: + value: preset-1 + option: [ preset-1, custom ] + CustomCurioFilter: |- + 博士之袍 > 福灵胶 > 分裂金币 > 信仰债券 > 换境桂冠 > 俱乐部券 > 碎星芳饵 > random + CurioSelectionStrategy: + value: follow-presets + option: [ follow-presets, unrecorded-first, before-random ] + diff --git a/module/config/argument/menu.json b/module/config/argument/menu.json index e164e6947..cd12b2b03 100644 --- a/module/config/argument/menu.json +++ b/module/config/argument/menu.json @@ -16,5 +16,12 @@ "BattlePass", "Assignment" ] + }, + "Rogue": { + "menu": "list", + "page": "setting", + "tasks": [ + "Rogue" + ] } } \ No newline at end of file diff --git a/module/config/argument/task.yaml b/module/config/argument/task.yaml index f9e9a069e..82d0b7ff5 100644 --- a/module/config/argument/task.yaml +++ b/module/config/argument/task.yaml @@ -38,3 +38,13 @@ Daily: Assignment: - Scheduler - Assignment + +# ==================== Rogue ==================== + +Rogue: + menu: 'list' + page: 'setting' + tasks: + Rogue: + - Scheduler + - Rogue diff --git a/module/config/config_generated.py b/module/config/config_generated.py index 4ef5f57d3..2d8976d87 100644 --- a/module/config/config_generated.py +++ b/module/config/config_generated.py @@ -101,3 +101,16 @@ class GeneratedConfig: Assignment_Name_4 = 'Nine_Billion_Names' # Nine_Billion_Names, Destruction_of_the_Destroyer, Winter_Soldiers, Born_to_Obey, Root_Out_the_Turpitude, Fire_Lord_Inflames_Blades_of_War, Nameless_Land_Nameless_People, Akashic_Records, The_Invisible_Hand, Abandoned_and_Insulted, Spring_of_Life, The_Land_of_Gold, The_Blossom_in_the_Storm, Legend_of_the_Puppet_Master, The_Wages_of_Humanity Assignment_Duration = 20 # 4, 8, 12, 20 Assignment_Assignment = {} + + # Group `Rogue` + Rogue_Path = 'The Hunt' # Preservation, Remembrance, Nihility, Abundance, Hunt, Destruction, Elation + Rogue_Bonus = 'Blessing Cosmos' # Blessing Cosmos, Miracle Cosmos, Fragmented Cosmos + Rogue_PresetResonanceFilter = 'preset-1' # preset-1, custom + Rogue_CustomResonanceFilter = '回响构音:均晶转变 > 回响构音:零维强化\n> 回响构音:第二次初恋 > 回响构音:体验的富翁\n> 回响构音:局外人 > 回响构音:怀疑的四重根\n> 回响构音:诸法无我 > 回响构音:诸行无常\n> 回响构音:射不主皮 > 回响构音:柘弓危矢\n> 回响构音:激变变星 > 回响构音:极端氦闪\n> 回响构音:末日狂欢 > 回响构音:树苗长高舞' + Rogue_ResonanceSelectionStrategy = 'follow-presets' # follow-presets, unrecorded-first, before-random + Rogue_PresetBlessingFilter = 'preset-1' # preset-1, custom + Rogue_CustomBlessingFilter = '巡猎-3 > 《冠军晚餐·猫的摇篮》 > 丰饶众生,一法界心 > 毁灭-3 \n> 火堆外的夜 > 巡猎-2 > 毁灭-2 > 巡猎 > reset > random' + Rogue_BlessingSelectionStrategy = 'follow-presets' # follow-presets, unrecorded-first, before-random + Rogue_PresetCurioFilter = 'preset-1' # preset-1, custom + Rogue_CustomCurioFilter = '博士之袍 > 福灵胶 > 分裂金币 > 信仰债券 > 换境桂冠 > 俱乐部券 > 碎星芳饵 > random' + Rogue_CurioSelectionStrategy = 'follow-presets' # follow-presets, unrecorded-first, before-random diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index d0d15b34c..c3aff3479 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -7,6 +7,10 @@ "Daily": { "name": "Daily", "help": "" + }, + "Rogue": { + "name": "Simulated Universe", + "help": "" } }, "Task": { @@ -33,6 +37,10 @@ "Assignment": { "name": "Assignment", "help": "" + }, + "Rogue": { + "name": "Simulated Universe", + "help": "" } }, "Scheduler": { @@ -678,6 +686,81 @@ "help": "" } }, + "Rogue": { + "_info": { + "name": "Simulated Universe", + "help": "" + }, + "Path": { + "name": "Fate", + "help": "", + "Preservation": "Preservation", + "Remembrance": "Remembrance", + "Nihility": "Nihility", + "Abundance": "Abundance", + "Hunt": "The Hunt", + "Destruction": "Destruction", + "Elation": "Elation" + }, + "Bonus": { + "name": "Blessings of Trailblaze", + "help": "", + "Blessing Cosmos": "Blessing Cosmos (Choose a blessing)", + "Miracle Cosmos": "Miracle Cosmos (Choose a curio)", + "Fragmented Cosmos": "Fragmented Cosmos (Claim some Cosmic Fragments)" + }, + "PresetResonanceFilter": { + "name": "Preset Resonance Filter", + "help": "", + "preset-1": "preset-1", + "custom": "custom" + }, + "CustomResonanceFilter": { + "name": "Custom Resonance Filter", + "help": "" + }, + "ResonanceSelectionStrategy": { + "name": "Resonance Selection Strategy", + "help": "", + "follow-presets": "Follow presets", + "unrecorded-first": "Choose index locked resonance first", + "before-random": "Follow presets, but choose index locked one first when filter goes to randomly choose" + }, + "PresetBlessingFilter": { + "name": "Preset Blessing Filter", + "help": "", + "preset-1": "preset-1", + "custom": "custom" + }, + "CustomBlessingFilter": { + "name": "Custom Blessing Filter", + "help": "" + }, + "BlessingSelectionStrategy": { + "name": "Blessing jSelection Strategy", + "help": "", + "follow-presets": "Follow presets", + "unrecorded-first": "Choose index locked blessing first", + "before-random": "Follow presets, but choose index locked one first when filter goes to randomly choose" + }, + "PresetCurioFilter": { + "name": "Preset Curio Filter", + "help": "", + "preset-1": "preset-1", + "custom": "custom" + }, + "CustomCurioFilter": { + "name": "Custom Curio Filter", + "help": "" + }, + "CurioSelectionStrategy": { + "name": "Curio Selection Strategy", + "help": "", + "follow-presets": "Follow presets", + "unrecorded-first": "Choose index locked curio first", + "before-random": "Follow presets, but choose index locked one first when filter goes to randomly choose" + } + }, "Gui": { "Aside": { "Install": "Install", @@ -780,4 +863,4 @@ "Clear": "Clear" } } -} \ No newline at end of file +} diff --git a/module/config/i18n/ja-JP.json b/module/config/i18n/ja-JP.json index aabad204a..5521a916b 100644 --- a/module/config/i18n/ja-JP.json +++ b/module/config/i18n/ja-JP.json @@ -7,6 +7,10 @@ "Daily": { "name": "Menu.Daily.name", "help": "Menu.Daily.help" + }, + "Rogue": { + "name": "Menu.Rogue.name", + "help": "Menu.Rogue.help" } }, "Task": { @@ -33,6 +37,10 @@ "Assignment": { "name": "依頼設定", "help": "" + }, + "Rogue": { + "name": "Task.Rogue.name", + "help": "Task.Rogue.help" } }, "Scheduler": { @@ -678,6 +686,81 @@ "help": "Assignment.Assignment.help" } }, + "Rogue": { + "_info": { + "name": "Rogue._info.name", + "help": "Rogue._info.help" + }, + "Path": { + "name": "Rogue.Path.name", + "help": "Rogue.Path.help", + "Preservation": "Preservation", + "Remembrance": "Remembrance", + "Nihility": "Nihility", + "Abundance": "Abundance", + "Hunt": "Hunt", + "Destruction": "Destruction", + "Elation": "Elation" + }, + "Bonus": { + "name": "Rogue.Bonus.name", + "help": "Rogue.Bonus.help", + "Blessing Cosmos": "Blessing Cosmos", + "Miracle Cosmos": "Miracle Cosmos", + "Fragmented Cosmos": "Fragmented Cosmos" + }, + "PresetResonanceFilter": { + "name": "Rogue.PresetResonanceFilter.name", + "help": "Rogue.PresetResonanceFilter.help", + "preset-1": "preset-1", + "custom": "custom" + }, + "CustomResonanceFilter": { + "name": "Rogue.CustomResonanceFilter.name", + "help": "Rogue.CustomResonanceFilter.help" + }, + "ResonanceSelectionStrategy": { + "name": "Rogue.ResonanceSelectionStrategy.name", + "help": "Rogue.ResonanceSelectionStrategy.help", + "follow-presets": "follow-presets", + "unrecorded-first": "unrecorded-first", + "before-random": "before-random" + }, + "PresetBlessingFilter": { + "name": "Rogue.PresetBlessingFilter.name", + "help": "Rogue.PresetBlessingFilter.help", + "preset-1": "preset-1", + "custom": "custom" + }, + "CustomBlessingFilter": { + "name": "Rogue.CustomBlessingFilter.name", + "help": "Rogue.CustomBlessingFilter.help" + }, + "BlessingSelectionStrategy": { + "name": "Rogue.BlessingSelectionStrategy.name", + "help": "Rogue.BlessingSelectionStrategy.help", + "follow-presets": "follow-presets", + "unrecorded-first": "unrecorded-first", + "before-random": "before-random" + }, + "PresetCurioFilter": { + "name": "Rogue.PresetCurioFilter.name", + "help": "Rogue.PresetCurioFilter.help", + "preset-1": "preset-1", + "custom": "custom" + }, + "CustomCurioFilter": { + "name": "Rogue.CustomCurioFilter.name", + "help": "Rogue.CustomCurioFilter.help" + }, + "CurioSelectionStrategy": { + "name": "Rogue.CurioSelectionStrategy.name", + "help": "Rogue.CurioSelectionStrategy.help", + "follow-presets": "follow-presets", + "unrecorded-first": "unrecorded-first", + "before-random": "before-random" + } + }, "Gui": { "Aside": { "Install": "インストール", diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index 1e69c3f04..8faac42c2 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -7,6 +7,10 @@ "Daily": { "name": "每日", "help": "" + }, + "Rogue": { + "name": "模拟宇宙", + "help": "" } }, "Task": { @@ -33,6 +37,10 @@ "Assignment": { "name": "委托", "help": "" + }, + "Rogue": { + "name": "模拟宇宙", + "help": "" } }, "Scheduler": { @@ -678,6 +686,81 @@ "help": "" } }, + "Rogue": { + "_info": { + "name": "模拟宇宙", + "help": "" + }, + "Path": { + "name": "命途", + "help": "", + "Preservation": "存护", + "Remembrance": "记忆", + "Nihility": "虚无", + "Abundance": "丰饶", + "Hunt": "巡猎", + "Destruction": "毁灭", + "Elation": "欢愉" + }, + "Bonus": { + "name": "开拓祝福", + "help": "开局三选一", + "Blessing Cosmos": "祝福宇宙(获得一个祝福)", + "Miracle Cosmos": "神奇宇宙(获得一个奇物)", + "Fragmented Cosmos": "破碎宇宙(获得宇宙碎片)" + }, + "PresetResonanceFilter": { + "name": "回响构音过滤器", + "help": "", + "preset-1": "预设 1", + "custom": "自定义" + }, + "CustomResonanceFilter": { + "name": "自定义回响构音过滤器", + "help": "" + }, + "ResonanceSelectionStrategy": { + "name": "回响构音选择策略", + "help": "", + "follow-presets": "遵循预设", + "unrecorded-first": "优先选择图鉴未解锁的回响构音", + "before-random": "遵循预设,但选择\"图鉴未解锁\"的优先级高于\"随机选择\"" + }, + "PresetBlessingFilter": { + "name": "祝福过滤器", + "help": "", + "preset-1": "预设 1", + "custom": "自定义" + }, + "CustomBlessingFilter": { + "name": "自定义祝福过滤器", + "help": "" + }, + "BlessingSelectionStrategy": { + "name": "祝福选择策略", + "help": "", + "follow-presets": "遵循预设", + "unrecorded-first": "优先选择图鉴未解锁的祝福", + "before-random": "遵循预设,但选择\"图鉴未解锁\"的优先级高于\"随机选择\"" + }, + "PresetCurioFilter": { + "name": "奇物过滤器", + "help": "", + "preset-1": "预设 1", + "custom": "自定义" + }, + "CustomCurioFilter": { + "name": "自定义祝福过滤器", + "help": "" + }, + "CurioSelectionStrategy": { + "name": "奇物选择策略", + "help": "", + "follow-presets": "遵循预设", + "unrecorded-first": "优先选择图鉴未解锁的奇物", + "before-random": "遵循预设,但选择\"图鉴未解锁\"的优先级高于\"随机选择\"" + } + }, "Gui": { "Aside": { "Install": "安装", @@ -780,4 +863,4 @@ "Clear": "清除" } } -} \ No newline at end of file +} diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index 7f5a63e58..433b992bb 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -7,6 +7,10 @@ "Daily": { "name": "每日", "help": "" + }, + "Rogue": { + "name": "模擬宇宙", + "help": "" } }, "Task": { @@ -33,6 +37,10 @@ "Assignment": { "name": "委託", "help": "" + }, + "Rogue": { + "name": "模擬宇宙", + "help": "" } }, "Scheduler": { @@ -678,6 +686,81 @@ "help": "" } }, + "Rogue": { + "_info": { + "name": "模擬宇宙", + "help": "" + }, + "Path": { + "name": "命途", + "help": "", + "Preservation": "存護", + "Remembrance": "記憶", + "Nihility": "虛無", + "Abundance": "丰饶", + "Hunt": "巡獵", + "Destruction": "毀滅", + "Elation": "歡愉" + }, + "Bonus": { + "name": "開拓祝福", + "help": "开局三选一", + "Blessing Cosmos": "祝福宇宙(獲得一個祝福)", + "Miracle Cosmos": "神奇宇宙(獲得一個奇物)", + "Fragmented Cosmos": "破碎宇宙(獲得宇宙碎片)" + }, + "PresetResonanceFilter": { + "name": "迴響構音过滤器", + "help": "", + "preset-1": "預設 1", + "custom": "自訂" + }, + "CustomResonanceFilter": { + "name": "自訂迴響構音过滤器", + "help": "" + }, + "ResonanceSelectionStrategy": { + "name": "迴響構音選擇策略", + "help": "", + "follow-presets": "遵循預設", + "unrecorded-first": "優先選擇圖鑑未解鎖的迴響構音", + "before-random": "遵循預設,但選擇\"圖鑑未解鎖\"的優先級高於\"隨機選擇\"" + }, + "PresetBlessingFilter": { + "name": "祝福过滤器", + "help": "", + "preset-1": "預設 1", + "custom": "自訂" + }, + "CustomBlessingFilter": { + "name": "自訂祝福过滤器", + "help": "" + }, + "BlessingSelectionStrategy": { + "name": "祝福選擇策略", + "help": "", + "follow-presets": "遵循預設", + "unrecorded-first": "優先選擇圖鑑未解鎖的祝福", + "before-random": "遵循預設,但選擇\"圖鑑未解鎖\"的優先級高於\"隨機選擇\"" + }, + "PresetCurioFilter": { + "name": "奇物过滤器", + "help": "", + "preset-1": "preset-1", + "custom": "custom" + }, + "CustomCurioFilter": { + "name": "自訂奇物过滤器", + "help": "" + }, + "CurioSelectionStrategy": { + "name": "奇物選擇策略", + "help": "", + "follow-presets": "遵循預設", + "unrecorded-first": "優先選擇圖鑑未解鎖的奇物", + "before-random": "遵循預設,但選擇\"圖鑑未解鎖\"的優先級高於\"隨機選擇\"" + } + }, "Gui": { "Aside": { "Install": "安裝", @@ -780,4 +863,4 @@ "Clear": "清除" } } -} \ No newline at end of file +} diff --git a/module/ocr/keyword.py b/module/ocr/keyword.py index b6f606783..7d78a4793 100644 --- a/module/ocr/keyword.py +++ b/module/ocr/keyword.py @@ -3,10 +3,10 @@ from dataclasses import dataclass from functools import cached_property from typing import ClassVar -from module.exception import ScriptError import module.config.server as server +from module.exception import ScriptError -REGEX_PUNCTUATION = re.compile(r'[ ,.\'"“”,。!!??·•\-—/\\\n\t()\[\]()「」『』【】]') +REGEX_PUNCTUATION = re.compile(r'[ ,.\'"“”,。::!!??·•\-—/\\\n\t()\[\]()「」『』【】《》]') def parse_name(n): @@ -26,6 +26,7 @@ class Keyword: """ Instance attributes and methods """ + @cached_property def ch(self) -> str: return self.cn diff --git a/tasks/rogue/assets/assets_rogue_blessing.py b/tasks/rogue/assets/assets_rogue_blessing.py new file mode 100644 index 000000000..0c12deb9e --- /dev/null +++ b/tasks/rogue/assets/assets_rogue_blessing.py @@ -0,0 +1,75 @@ +from module.base.button import Button, ButtonWrapper + +# This file was auto-generated, do not modify it manually. To generate: +# ``` python -m dev_tools.button_extract ``` + +BLESSING_ENFORCE = ButtonWrapper( + name='BLESSING_ENFORCE', + share=Button( + file='./assets/share/rogue/blessing/BLESSING_ENFORCE.png', + area=(500, 141, 608, 556), + search=(480, 121, 628, 576), + color=(123, 100, 78), + button=(500, 141, 608, 556), + ), +) +BLESSING_RESET = ButtonWrapper( + name='BLESSING_RESET', + share=Button( + file='./assets/share/rogue/blessing/BLESSING_RESET.png', + area=(652, 629, 889, 677), + search=(632, 609, 909, 697), + color=(204, 203, 204), + button=(652, 629, 889, 677), + ), +) +BLESSING_STABLE_FLAG = ButtonWrapper( + name='BLESSING_STABLE_FLAG', + share=Button( + file='./assets/share/rogue/blessing/BLESSING_STABLE_FLAG.png', + area=(846, 520, 896, 542), + search=(826, 500, 916, 562), + color=(250, 250, 250), + button=(846, 520, 896, 542), + ), +) +BOTTOM_WHITE_BAR = ButtonWrapper( + name='BOTTOM_WHITE_BAR', + share=Button( + file='./assets/share/rogue/blessing/BOTTOM_WHITE_BAR.png', + area=(166, 520, 1114, 542), + search=(146, 500, 1134, 562), + color=(210, 211, 211), + button=(166, 520, 1114, 542), + ), +) +OCR_RESET_COST = ButtonWrapper( + name='OCR_RESET_COST', + share=Button( + file='./assets/share/rogue/blessing/OCR_RESET_COST.png', + area=(689, 642, 715, 665), + search=(669, 622, 735, 685), + color=(39, 23, 25), + button=(689, 642, 715, 665), + ), +) +OCR_RESET_COUNT = ButtonWrapper( + name='OCR_RESET_COUNT', + share=Button( + file='./assets/share/rogue/blessing/OCR_RESET_COUNT.png', + area=(655, 593, 873, 623), + search=(635, 573, 893, 643), + color=(23, 24, 26), + button=(655, 593, 873, 623), + ), +) +OCR_ROGUE_BUFF = ButtonWrapper( + name='OCR_ROGUE_BUFF', + share=Button( + file='./assets/share/rogue/blessing/OCR_ROGUE_BUFF.png', + area=(155, 140, 1123, 348), + search=(135, 120, 1143, 368), + color=(80, 87, 106), + button=(155, 140, 1123, 348), + ), +) diff --git a/tasks/rogue/assets/assets_rogue_bonus.py b/tasks/rogue/assets/assets_rogue_bonus.py new file mode 100644 index 000000000..ab2f1f15e --- /dev/null +++ b/tasks/rogue/assets/assets_rogue_bonus.py @@ -0,0 +1,25 @@ +from module.base.button import Button, ButtonWrapper + +# This file was auto-generated, do not modify it manually. To generate: +# ``` python -m dev_tools.button_extract ``` + +BONUS_BOTTOM_WHITE_BAR = ButtonWrapper( + name='BONUS_BOTTOM_WHITE_BAR', + share=Button( + file='./assets/share/rogue/bonus/BONUS_BOTTOM_WHITE_BAR.png', + area=(731, 568, 774, 590), + search=(711, 548, 794, 610), + color=(250, 250, 250), + button=(731, 568, 774, 590), + ), +) +BONUS_CONFIRM = ButtonWrapper( + name='BONUS_CONFIRM', + share=Button( + file='./assets/share/rogue/bonus/BONUS_CONFIRM.png', + area=(504, 629, 620, 677), + search=(484, 609, 640, 697), + color=(222, 224, 224), + button=(504, 629, 620, 677), + ), +) diff --git a/tasks/rogue/assets/assets_rogue_curio.py b/tasks/rogue/assets/assets_rogue_curio.py new file mode 100644 index 000000000..115214bd4 --- /dev/null +++ b/tasks/rogue/assets/assets_rogue_curio.py @@ -0,0 +1,25 @@ +from module.base.button import Button, ButtonWrapper + +# This file was auto-generated, do not modify it manually. To generate: +# ``` python -m dev_tools.button_extract ``` + +CURIO_ENFORCE = ButtonWrapper( + name='CURIO_ENFORCE', + share=Button( + file='./assets/share/rogue/curio/CURIO_ENFORCE.png', + area=(465, 114, 815, 545), + search=(445, 94, 835, 565), + color=(80, 66, 61), + button=(465, 114, 815, 545), + ), +) +OCR_ROGUE_CURIO = ButtonWrapper( + name='OCR_ROGUE_CURIO', + share=Button( + file='./assets/share/rogue/curio/OCR_ROGUE_CURIO.png', + area=(87, 128, 1203, 187), + search=(67, 108, 1223, 207), + color=(18, 19, 20), + button=(87, 128, 1203, 187), + ), +) diff --git a/tasks/rogue/assets/assets_rogue_ui.py b/tasks/rogue/assets/assets_rogue_ui.py new file mode 100644 index 000000000..fcef9e8bb --- /dev/null +++ b/tasks/rogue/assets/assets_rogue_ui.py @@ -0,0 +1,105 @@ +from module.base.button import Button, ButtonWrapper + +# This file was auto-generated, do not modify it manually. To generate: +# ``` python -m dev_tools.button_extract ``` + +BLESSING_CONFIRM = ButtonWrapper( + name='BLESSING_CONFIRM', + share=Button( + file='./assets/share/rogue/ui/BLESSING_CONFIRM.png', + area=(960, 629, 1233, 677), + search=(940, 609, 1253, 697), + color=(217, 218, 218), + button=(960, 629, 1233, 677), + ), +) +CHECK_BLESSING = ButtonWrapper( + name='CHECK_BLESSING', + share=Button( + file='./assets/share/rogue/ui/CHECK_BLESSING.png', + area=(946, 17, 986, 57), + search=(926, 0, 1006, 77), + color=(61, 58, 52), + button=(946, 17, 986, 57), + ), +) +COSMIC_FRAGMENT = ButtonWrapper( + name='COSMIC_FRAGMENT', + share=Button( + file='./assets/share/rogue/ui/COSMIC_FRAGMENT.png', + area=(1146, 19, 1181, 55), + search=(1126, 0, 1201, 75), + color=(86, 68, 62), + button=(1146, 19, 1181, 55), + ), +) +FLAG_UNRECORD = ButtonWrapper( + name='FLAG_UNRECORD', + share=Button( + file='./assets/share/rogue/ui/FLAG_UNRECORD.png', + area=(746, 149, 778, 181), + search=(726, 129, 798, 201), + color=(133, 130, 161), + button=(746, 149, 778, 181), + ), +) +OBTAIN_ITEM_POPUP = ButtonWrapper( + name='OBTAIN_ITEM_POPUP', + share=Button( + file='./assets/share/rogue/ui/OBTAIN_ITEM_POPUP.png', + area=(180, 573, 432, 720), + search=(160, 553, 452, 720), + color=(131, 109, 73), + button=(180, 573, 432, 720), + ), +) +OCR_COSMIC_FRAGMENT = ButtonWrapper( + name='OCR_COSMIC_FRAGMENT', + share=Button( + file='./assets/share/rogue/ui/OCR_COSMIC_FRAGMENT.png', + area=(1183, 27, 1256, 46), + search=(1163, 7, 1276, 66), + color=(30, 32, 32), + button=(1183, 27, 1256, 46), + ), +) +PAGE_CHOOSE_BONUS = ButtonWrapper( + name='PAGE_CHOOSE_BONUS', + share=Button( + file='./assets/share/rogue/ui/PAGE_CHOOSE_BONUS.png', + area=(503, 180, 555, 230), + search=(483, 160, 575, 250), + color=(33, 29, 54), + button=(503, 180, 555, 230), + ), +) +PAGE_CHOOSE_BUFF = ButtonWrapper( + name='PAGE_CHOOSE_BUFF', + share=Button( + file='./assets/share/rogue/ui/PAGE_CHOOSE_BUFF.png', + area=(1020, 78, 1160, 96), + search=(1000, 58, 1180, 116), + color=(38, 39, 41), + button=(1020, 78, 1160, 96), + ), +) +PAGE_CHOOSE_CURIO = ButtonWrapper( + name='PAGE_CHOOSE_CURIO', + share=Button( + file='./assets/share/rogue/ui/PAGE_CHOOSE_CURIO.png', + area=(988, 17, 1028, 57), + search=(968, 0, 1048, 77), + color=(40, 39, 34), + button=(988, 17, 1028, 57), + ), +) +PAGE_EVENT = ButtonWrapper( + name='PAGE_EVENT', + share=Button( + file='./assets/share/rogue/ui/PAGE_EVENT.png', + area=(983, 485, 1007, 509), + search=(963, 465, 1027, 529), + color=(51, 48, 42), + button=(983, 485, 1007, 509), + ), +) diff --git a/tasks/rogue/blessing.py b/tasks/rogue/blessing.py new file mode 100644 index 000000000..5ee1f13cb --- /dev/null +++ b/tasks/rogue/blessing.py @@ -0,0 +1,322 @@ +import re + +import numpy as np + +from module.base.filter import MultiLangFilter +from module.base.timer import Timer +from module.base.utils import get_color +from module.logger import logger +from module.ocr.ocr import Ocr, OcrResultButton, DigitCounter, Digit +from module.ocr.utils import split_and_pair_buttons +from tasks.rogue.assets.assets_rogue_blessing import * +from tasks.rogue.assets.assets_rogue_ui import BLESSING_CONFIRM +from tasks.rogue.keywords import * +from tasks.rogue.preset import * +from tasks.rogue.selector import RogueSelector +from tasks.rogue.utils import get_regex_from_keyword_name, parse_name, is_card_selected + +# normal blessing filter +# path name +pattern = "" +BLESSING_FILTER_ATTR = tuple() +PATH_ATTR_NAME = 'path_name' +path_regex = get_regex_from_keyword_name(RoguePath, PATH_ATTR_NAME) +pattern += path_regex +# remove 'the' in path 'the hunt' +pattern = pattern.lower().replace('the', '') +BLESSING_FILTER_ATTR += (PATH_ATTR_NAME,) + +# rarity +pattern += "([123])?-?" +BLESSING_FILTER_ATTR += ("rarity",) + +# blessing name +BLESSING_ATTR_NAME = 'blessing_name' +blessing_regex = get_regex_from_keyword_name(RogueBlessing, BLESSING_ATTR_NAME) +pattern += blessing_regex +BLESSING_FILTER_ATTR += (BLESSING_ATTR_NAME,) + +# enhanced +ENHANCEMENT_ATTR_NAME = "enhancement" +enhancement_regex = get_regex_from_keyword_name(RogueEnhancement, "enhancement_keyword") +pattern += enhancement_regex +BLESSING_FILTER_ATTR += (ENHANCEMENT_ATTR_NAME,) + +FILETER_REGEX = re.compile(pattern) +BLESSING_FILTER_PRESET = ("reset", "random", "unrecorded") +BLESSING_FILTER = MultiLangFilter(FILETER_REGEX, BLESSING_FILTER_ATTR, BLESSING_FILTER_PRESET) + +# resonance filter +RESONANCE_ATTR_NAME = 'resonance_name' +pattern = get_regex_from_keyword_name(RogueResonance, RESONANCE_ATTR_NAME) + +FILETER_REGEX = re.compile(pattern) +RESONANCE_FILTER_PRESET = ("random", "unrecorded") +RESONANCE_FILTER = MultiLangFilter(FILETER_REGEX, (RESONANCE_ATTR_NAME,), RESONANCE_FILTER_PRESET) + + +class RogueBuffOcr(Ocr): + merge_thres_x = 40 + merge_thres_y = 20 + + def after_process(self, result): + result = super().after_process(result) + if self.lang == 'ch': + replace_pattern_dict = { + "蓬失": "蓬矢", + "柘弓危失": "柘弓危矢", + "飞虹珠?凿?齿": "飞虹诛凿齿", + "天培步危": "天棓步危", + "云[摘销锅]?逐步离": "云镝逐步离", + "制桑": "制穹桑", + "乌号基": "乌号綦", + "追摩物": "追孽物", + "特月": "狩月", + "彤弓素增?": "彤弓素矰", + "白决射御": "白矢决射御", + "苦表": "苦衷", + "[沦沧]肌髓": "沦浃肌髓", + "进发": "迸发", + "永缩体": "永坍缩体", + "完美体验:绒默": "完美体验:缄默", + "灭回归不等式": "湮灭回归不等式", + r".*灾$": "禳灾", + "虚安供品": "虚妄供品", + "原初的苦$": "原初的苦衷", + "厌离邪苦": "厌离邪秽苦", + r".*繁.*": "葳蕤繁祉,延彼遐龄", + } + for pat, replace in replace_pattern_dict.items(): + result = re.sub(pat, replace, result) + return result + + +class RogueBlessingSelector(RogueSelector): + """ + Usage: + self = RogueBlessingSelector('alas') + self.device.screenshot() + self.recognize_and_select() + """ + + def get_blessing_count(self) -> int: + """ + Returns: The number of blessing + """ + if not self.main.image_color_count(BOTTOM_WHITE_BAR.area, color=(255, 255, 255), count=5000): + return 0 + color = get_color(self.main.device.image, BOTTOM_WHITE_BAR.area) + mean = np.mean(color) + return int(mean // 60) # the magic number that maps blessing num with mean_color + + def recognition(self): + def not_enhancement_keyword(keyword): + return keyword != KEYWORDS_ROGUE_ENHANCEMENT.Already_Enhanced + + self.ocr_results = [] + self._wait_until_blessing_loaded() + ocr = RogueBuffOcr(OCR_ROGUE_BUFF) + results = ocr.matched_ocr(self.main.device.image, + [RogueBlessing, RogueResonance, RogueEnhancement]) + + enhanced_blessing = [result for result, _ in + split_and_pair_buttons(results, split_func=not_enhancement_keyword, + relative_area=(-300, -720, 0, 0))] + results = [result for result in results if not_enhancement_keyword(result)] + blessing_count = self.get_blessing_count() + if blessing_count != len(results): + logger.warning(f"The OCR result does not match the blessing count. " + f"Expect {blessing_count}, but recognized {len(results)} only.") + for result in results: + if result in enhanced_blessing: + result.matched_keyword.enhancement = KEYWORDS_ROGUE_ENHANCEMENT.Already_Enhanced.enhancement_keyword + self.ocr_results = results + return results + + def ui_select(self, target: OcrResultButton | None, skip_first_screenshot=True): + """ + Select buff once. Multiple calls needed if there's more than one time to choose + It might occur that all listed blessings are not recognized + So this method provides a hard code way to choose one, which fit in case when blessing num is 1-3 + """ + + def is_select_blessing_complete(): + """ + Case 1: back to main page + Case 2: choose curio + Case 3: another choose blessings, but no blessing is selected when the new selection page loaded + Case 4: event ui + """ + if self.main.is_in_main(): + logger.info("Main page checked") + return True + if self.main.is_page_choose_curio(): + logger.info("Choose curio page checked") + return True + if self.main.is_page_choose_blessing() and not is_card_selected(self.main, target, BLESSING_CONFIRM): + logger.info("A new choose blessing page checked") + return True + if self.main.is_page_event(): + logger.info("Event page checked") + return True + return False + + interval = Timer(1) + enforce = False + + if not target: + enforce = True + + # start -> selected + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.main.device.screenshot() + + if is_card_selected(self.main, target, confirm_button=BLESSING_CONFIRM): + if enforce: + logger.info("Buff selected (enforce)") + else: + logger.info(f"Buff {target} selected") + break + if interval.reached(): + if enforce: + self.main.device.click(BLESSING_ENFORCE) + else: + self.main.device.click(target) + interval.reset() + + skip_first_screenshot = True + # selected -> confirm + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.main.device.screenshot() + + if is_select_blessing_complete(): + logger.info("Select blessing complete") + break + if interval.reached(): + self.main.device.click(BLESSING_CONFIRM) + interval.reset() + + def _get_reset_count(self): + current, _, _ = DigitCounter(OCR_RESET_COUNT).ocr_single_line(self.main.device.image) + return current + + def _wait_until_blessing_loaded(self, timer=Timer(0.3, count=1), timeout=Timer(5, count=10)): + timer.reset() + timeout.reset() + previous_count = self.get_blessing_count() + while 1: + self.main.device.screenshot() + blessing_count = self.get_blessing_count() + + if timeout.reached(): + logger.warning('Wait blessing page loaded timeout') + break + + if previous_count and previous_count == blessing_count: + if timer.reached(): + logger.info('Blessing page stabled') + break + else: + previous_count = blessing_count + timer.reset() + + def reset_blessing_list(self, skip_first_screenshot=True): + if not self.main.is_page_choose_blessing(): + return False + + reset_count = self._get_reset_count() + if not reset_count: + logger.info("Does not have enough reset count") + return False + + reset_cost = Digit(OCR_RESET_COST).ocr_single_line(self.main.device.image) + if reset_cost > self.main.cosmic_fragment: + logger.info("Does not have enough cosmic fragment") + return False + + interval = Timer(1) + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.main.device.screenshot() + + new_count = self._get_reset_count() + + if reset_count - new_count == 1: + logger.info("Reset once") + break + if interval.reached(): + self.main.device.click(BLESSING_RESET) + interval.reset() + return True + + def load_filter(self): + keyword = self.ocr_results[0].matched_keyword + if not isinstance(keyword, (RogueBlessing, RogueResonance)): + return + filter_configs = { + RogueBlessing: { + "filter_": BLESSING_FILTER, + "preset_config": self.main.config.Rogue_PresetBlessingFilter, + "strategy_config": self.main.config.Rogue_BlessingSelectionStrategy, + "preset_values": { + 'preset-1': BLESSING_PRESET_1, + 'custom': self.main.config.Rogue_CustomBlessingFilter + }, + }, + RogueResonance: { + "filter_": RESONANCE_FILTER, + "preset_config": self.main.config.Rogue_PresetResonanceFilter, + "strategy_config": self.main.config.Rogue_ResonanceSelectionStrategy, + "preset_values": { + 'preset-1': RESONANCE_PRESET_1, + 'custom': self.main.config.Rogue_PresetResonanceFilter, + }, + } + } + # preset + config = filter_configs[type(keyword)] + filter_ = config['filter_'] + preset_config = config['preset_config'] + preset_values = config['preset_values'] + string = preset_values[preset_config] + string = parse_name(string) + + # strategy + strategy_config = config['strategy_config'] + if strategy_config == 'unrecorded-first': + string = "unrecorded > " + string + if strategy_config == 'before-random': + string = string.replace('random', 'unrecorded > random') + + filter_.load(string) + self.filter_ = filter_ + + def try_select(self, option: OcrResultButton | str): + if isinstance(option, str): + if option.lower() == 'reset': + if self.reset_blessing_list(): + self.recognize_and_select() + return True + if option.lower() == 'random': + choose = np.random.choice(self.ocr_results) + self.ui_select(choose) + return True + if option.lower() == 'unrecorded': + for result in self.ocr_results: + if self.main.is_unrecorded(result, (0, -720, 300, 0)): + self.ui_select(result) + return True + return False + + if isinstance(option, OcrResultButton): + self.ui_select(option) + return True + return False diff --git a/tasks/rogue/bonus.py b/tasks/rogue/bonus.py new file mode 100644 index 000000000..166eb2d50 --- /dev/null +++ b/tasks/rogue/bonus.py @@ -0,0 +1,90 @@ +import numpy as np + +from module.base.timer import Timer +from module.logger import logger +from module.ocr.ocr import OcrResultButton +from tasks.rogue.assets.assets_rogue_blessing import OCR_ROGUE_BUFF +from tasks.rogue.assets.assets_rogue_bonus import BONUS_BOTTOM_WHITE_BAR, BONUS_CONFIRM +from tasks.rogue.keywords import RogueBonus +from tasks.rogue.selector import RogueSelector +from tasks.rogue.ui import RogueBonusOcr +from tasks.rogue.utils import is_card_selected + + +class RogueBonusSelector(RogueSelector): + def _wait_bonus_page_loaded(self, timer=Timer(0.3, count=1), timeout=Timer(5, count=10)): + timer.reset() + timeout.reset() + while 1: + self.main.device.screenshot() + + if timeout.reached(): + logger.warning('Wait bonus page loaded timeout') + break + + if self.main.appear(BONUS_BOTTOM_WHITE_BAR): + if timer.reached(): + logger.info('Bonus page stabled') + break + else: + timer.reset() + + def recognition(self): + self._wait_bonus_page_loaded() + ocr = RogueBonusOcr(OCR_ROGUE_BUFF) + results = ocr.matched_ocr(self.main.device.image, [RogueBonus]) + expected_count = 3 + if expected_count != len(results): + logger.warning(f"The OCR result does not match the bonus count. " + f"Expect {expected_count}, but recognized {len(results)} only.") + self.ocr_results = results + return results + + def ui_select(self, target: OcrResultButton | None, skip_first_screenshot=True): + interval = Timer(1) + # start -> select + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.main.device.screenshot() + + if is_card_selected(self.main, target, confirm_button=BONUS_CONFIRM): + break + if interval.reached(): + self.main.device.click(target) + interval.reset() + + skip_first_screenshot = True + # select -> confirm + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.main.device.screenshot() + + if self.main.is_in_main(): + logger.info("Main Page Checked") + break + if self.main.is_page_choose_curio(): + logger.info("Choose curio page checked") + break + if self.main.is_page_choose_blessing(): + logger.info("Choose blessing page checked") + break + if interval.reached(): + self.main.device.click(BONUS_CONFIRM) + interval.reset() + + def recognize_and_select(self): + self.recognition() + if not self.ocr_results: + self.ui_select(None) + options = {result.matched_keyword.en: result for result in self.ocr_results} + if self.main.config.Rogue_Bonus not in options.keys(): + logger.warning(f"Can not find option: {self.main.config.Rogue_Bonus}, randomly choose one") + target = np.random.choice(options) + else: + target = options[self.main.config.Rogue_Bonus] + logger.info(f"Choose bonus: {target}") + self.ui_select(target) diff --git a/tasks/rogue/curio.py b/tasks/rogue/curio.py new file mode 100644 index 000000000..78069594a --- /dev/null +++ b/tasks/rogue/curio.py @@ -0,0 +1,142 @@ +import re + +import numpy as np + +from module.base.filter import MultiLangFilter +from module.base.timer import Timer +from module.base.utils import get_color +from module.logger import logger +from module.ocr.ocr import Ocr, OcrResultButton +from tasks.rogue.assets.assets_rogue_curio import * +from tasks.rogue.assets.assets_rogue_ui import BLESSING_CONFIRM +from tasks.rogue.keywords import RogueCurio +from tasks.rogue.preset import CURIO_PRESET_1 +from tasks.rogue.selector import RogueSelector +from tasks.rogue.utils import get_regex_from_keyword_name, parse_name + +CURIO_FILTER_ATTR = tuple() +CURIO_ATTR_NAME = 'curio_name' +pattern = get_regex_from_keyword_name(RogueCurio, CURIO_ATTR_NAME) +CURIO_FILTER_ATTR += (CURIO_ATTR_NAME,) +CURIO_FILTER_PRESET = ('random', 'unrecorded') +FILTER_REGEX = re.compile(pattern) +CURIO_FILTER = MultiLangFilter(FILTER_REGEX, CURIO_FILTER_ATTR, CURIO_FILTER_PRESET) + + +class RogueCurioOcr(Ocr): + merge_thres_y = 40 + + def after_process(self, result): + result = super().after_process(result) + if self.lang == 'ch': + replace_pattern_dict = { + "般": "骰", + "漂灭": "湮灭", + } + for pattern, replace in replace_pattern_dict.items(): + result = re.sub(pattern, replace, result) + return result + + +class RogueCurioSelector(RogueSelector): + def recognition(self): + self.ocr_results = [] + ocr = RogueCurioOcr(OCR_ROGUE_CURIO) + results = ocr.matched_ocr(self.main.device.image, RogueCurio) + expect_num = 3 + if len(results) != expect_num: + logger.warning(f"The OCR result does not match the curio count. " + f"Expect {expect_num}, but recognized {len(results)} only.") + self.ocr_results = results + return results + + def ui_select(self, target: OcrResultButton | None, skip_first_screenshot=True): + def is_curio_selected(): + return np.mean(get_color(self.main.device.image, tuple(target.area))) > 60 # shiny background + + def is_select_curio_complete(): + """ + Case 1: back to main page + Case 2: event page + """ + if self.main.is_in_main(): + logger.info("Main page checked") + return True + if self.main.is_page_event(): + logger.info("Event page checked") + return True + return False + + enforce = False + if not target: + enforce = True + interval = Timer(1) + # start -> selected + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.main.device.screenshot() + + if is_curio_selected(): + if enforce: + logger.info("Curio selected (enforce)") + else: + logger.info(f"Curio {target} selected") + break + if interval.reached(): + if enforce: + self.main.device.click(CURIO_ENFORCE) + else: + self.main.device.click(target) + interval.reset() + + skip_first_screenshot = True + # selected -> confirm + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.main.device.screenshot() + + if is_select_curio_complete(): + break + if interval.reached(): + self.main.device.click(BLESSING_CONFIRM) + interval.reset() + + def try_select(self, option: OcrResultButton | str): + if option == 'random': + target = np.random.choice(self.ocr_results) + self.ui_select(target) + return True + if option == 'unrecorded': + for result in self.ocr_results: + if self.main.is_unrecorded(result, (0, -720, 300, 0)): + self.ui_select(result) + return True + return False + + if isinstance(option, OcrResultButton): + self.ui_select(option) + return True + return False + + def load_filter(self): + filter_ = CURIO_FILTER + string = "" + match self.main.config.Rogue_PresetCurioFilter: + case 'preset-1': + string = CURIO_PRESET_1 + case 'custom': + string = self.main.config.Rogue_CustomCurioFilter + string = parse_name(string) + + match self.main.config.Rogue_CurioSelectionStrategy: + case 'unrecorded-first': + string = 'unrecorded > ' + string + case 'before-random': + string = string.replace('random', 'unrecorded > random') + + filter_.load(string) + self.filter_ = filter_ diff --git a/tasks/rogue/keywords/__init__.py b/tasks/rogue/keywords/__init__.py new file mode 100644 index 000000000..908342010 --- /dev/null +++ b/tasks/rogue/keywords/__init__.py @@ -0,0 +1,9 @@ +import tasks.rogue.keywords.blessing as KEYWORDS_ROGUE_BLESSING +import tasks.rogue.keywords.bonus as KEYWORDS_ROGUE_BONUS +import tasks.rogue.keywords.curio as KEYWORDS_ROGUE_CURIO +import tasks.rogue.keywords.enhancement as KEYWORDS_ROGUE_ENHANCEMENT +import tasks.rogue.keywords.path as KEYWORDS_ROGUE_PATH +import tasks.rogue.keywords.resonance as KEYWORDS_ROGUE_RESONANCE + +from tasks.rogue.keywords.classes import (RogueBlessing, RogueBonus, RogueEnhancement, + RoguePath, RogueResonance, RogueCurio) diff --git a/tasks/rogue/keywords/blessing.py b/tasks/rogue/keywords/blessing.py new file mode 100644 index 000000000..5b4db7879 --- /dev/null +++ b/tasks/rogue/keywords/blessing.py @@ -0,0 +1,1391 @@ +from .classes import RogueBlessing + +# This file was auto-generated, do not modify it manually. To generate: +# ``` python -m dev_tools.keyword_extract ``` + +Divine_Construct_Resonance_Transfer = RogueBlessing( + id=1, + name='Divine_Construct_Resonance_Transfer', + cn='神性构筑•谐振传递', + cht='神性構築•諧振傳遞', + en='Divine Construct: Resonance Transfer', + jp='神性構築・共鳴伝達', + path_id=1, + rarity=3, + enhancement='', +) +Divine_Construct_Metastatic_Field = RogueBlessing( + id=2, + name='Divine_Construct_Metastatic_Field', + cn='神性构筑•超静定场', + cht='神性構築•超靜定場', + en='Divine Construct: Metastatic Field', + jp='神性構築・不静定構造', + path_id=1, + rarity=3, + enhancement='', +) +Divine_Construct_Macrosegregation = RogueBlessing( + id=3, + name='Divine_Construct_Macrosegregation', + cn='神性构筑•宏观偏析', + cht='神性構築•宏觀偏析', + en='Divine Construct: Macrosegregation', + jp='神性構築・マクロ偏析', + path_id=1, + rarity=3, + enhancement='', +) +Interstellar_Construct_Shear_Structure = RogueBlessing( + id=4, + name='Interstellar_Construct_Shear_Structure', + cn='星间构筑•切变结构', + cht='星間構築•切變結構', + en='Interstellar Construct: Shear Structure', + jp='星間構築・剪断構造', + path_id=1, + rarity=2, + enhancement='', +) +Interstellar_Construct_Burst_Lattice = RogueBlessing( + id=5, + name='Interstellar_Construct_Burst_Lattice', + cn='星间构筑•迸裂晶格', + cht='星間構築•迸裂晶格', + en='Interstellar Construct: Burst Lattice', + jp='星間構築・格子欠陥', + path_id=1, + rarity=2, + enhancement='', +) +Interstellar_Construct_Solid_Solution = RogueBlessing( + id=6, + name='Interstellar_Construct_Solid_Solution', + cn='星间构筑•固溶强化', + cht='星間構築•固溶強化', + en='Interstellar Construct: Solid Solution', + jp='星間構築・固溶強化', + path_id=1, + rarity=2, + enhancement='', +) +Interstellar_Construct_Safe_Load = RogueBlessing( + id=7, + name='Interstellar_Construct_Safe_Load', + cn='星间构筑•安全载荷', + cht='星間構築•安全載荷', + en='Interstellar Construct: Safe Load', + jp='星間構築・安全荷重', + path_id=1, + rarity=2, + enhancement='', +) +Interstellar_Construct_Sanctuary = RogueBlessing( + id=8, + name='Interstellar_Construct_Sanctuary', + cn='星间构筑•回馈庇护', + cht='星間構築•回饋庇護', + en='Interstellar Construct: Sanctuary', + jp='星間構築・反作用庇護', + path_id=1, + rarity=2, + enhancement='', +) +Interstellar_Construct_Quadrangular_Pyramid = RogueBlessing( + id=9, + name='Interstellar_Construct_Quadrangular_Pyramid', + cn='星间构筑•四棱锥体', + cht='星間構築•四稜錐體', + en='Interstellar Construct: Quadrangular Pyramid', + jp='星間構築・四角錐', + path_id=1, + rarity=2, + enhancement='', +) +Interstellar_Construct_Hypoeutectoid = RogueBlessing( + id=10, + name='Interstellar_Construct_Hypoeutectoid', + cn='星间构筑•亚共晶体', + cht='星間構築•亞共晶體', + en='Interstellar Construct: Hypoeutectoid', + jp='星間構築・亜共晶', + path_id=1, + rarity=2, + enhancement='', +) +Construct_Assemble = RogueBlessing( + id=11, + name='Construct_Assemble', + cn='构筑•聚塑', + cht='構築•聚塑', + en='Construct: Assemble', + jp='構築・集塑', + path_id=1, + rarity=1, + enhancement='', +) +Construct_Sentinel = RogueBlessing( + id=12, + name='Construct_Sentinel', + cn='构筑•哨戒', + cht='構築•哨戒', + en='Construct: Sentinel', + jp='構築・哨戒', + path_id=1, + rarity=1, + enhancement='', +) +Construct_Patch = RogueBlessing( + id=13, + name='Construct_Patch', + cn='构筑•弥合', + cht='構築•彌合', + en='Construct: Patch', + jp='構築・溶着', + path_id=1, + rarity=1, + enhancement='', +) +Construct_Compensation = RogueBlessing( + id=14, + name='Construct_Compensation', + cn='构筑•补偿', + cht='構築•補償', + en='Construct: Compensation', + jp='構築・補填', + path_id=1, + rarity=1, + enhancement='', +) +Construct_Firmness = RogueBlessing( + id=15, + name='Construct_Firmness', + cn='构筑•坚定', + cht='構築•堅定', + en='Construct: Firmness', + jp='構築・確固', + path_id=1, + rarity=1, + enhancement='', +) +Construct_Rotation = RogueBlessing( + id=16, + name='Construct_Rotation', + cn='构筑•回转', + cht='構築•迴轉', + en='Construct: Rotation', + jp='構築・回転', + path_id=1, + rarity=1, + enhancement='', +) +Construct_Burst = RogueBlessing( + id=17, + name='Construct_Burst', + cn='构筑•迸发', + cht='構築•迸發', + en='Construct: Burst', + jp='構築・勃発', + path_id=1, + rarity=1, + enhancement='', +) +Construct_Concentration = RogueBlessing( + id=18, + name='Construct_Concentration', + cn='构筑•专注', + cht='構築•專注', + en='Construct: Concentration', + jp='構築・専念', + path_id=1, + rarity=1, + enhancement='', +) +Perfect_Experience_Fuli = RogueBlessing( + id=19, + name='Perfect_Experience_Fuli', + cn='完美体验:浮黎', + cht='完美體驗:浮黎', + en='Perfect Experience: Fuli', + jp='完璧体験:浮黎', + path_id=2, + rarity=3, + enhancement='', +) +Perfect_Experience_Innocence = RogueBlessing( + id=20, + name='Perfect_Experience_Innocence', + cn='完美体验:纯真', + cht='完美體驗:純真', + en='Perfect Experience: Innocence', + jp='完璧体験:純真', + path_id=2, + rarity=3, + enhancement='', +) +Perfect_Experience_Reticence = RogueBlessing( + id=21, + name='Perfect_Experience_Reticence', + cn='完美体验:缄默', + cht='完美體驗:緘默', + en='Perfect Experience: Reticence', + jp='完璧体験:沈黙', + path_id=2, + rarity=3, + enhancement='', +) +Ultimate_Experience_Melancholia = RogueBlessing( + id=22, + name='Ultimate_Experience_Melancholia', + cn='极端体验:怅然若失', + cht='極端體驗:悵然若失', + en='Ultimate Experience: Melancholia', + jp='極端体験:茫然自失', + path_id=2, + rarity=2, + enhancement='', +) +Ultimate_Experience_Dizziness = RogueBlessing( + id=23, + name='Ultimate_Experience_Dizziness', + cn='极端体验:头晕目眩', + cht='極端體驗:頭暈目眩', + en='Ultimate Experience: Dizziness', + jp='極端体験:眩暈', + path_id=2, + rarity=2, + enhancement='', +) +Ultimate_Experience_Insensitivity = RogueBlessing( + id=24, + name='Ultimate_Experience_Insensitivity', + cn='极端体验:麻木不仁', + cht='極端體驗:麻木不仁', + en='Ultimate Experience: Insensitivity', + jp='極端体験:無感覚', + path_id=2, + rarity=2, + enhancement='', +) +Ultimate_Experience_Sentimentality = RogueBlessing( + id=25, + name='Ultimate_Experience_Sentimentality', + cn='极端体验:多愁善感', + cht='極端體驗:多愁善感', + en='Ultimate Experience: Sentimentality', + jp='極端体験:多感', + path_id=2, + rarity=2, + enhancement='', +) +Ultimate_Experience_Indelibility = RogueBlessing( + id=26, + name='Ultimate_Experience_Indelibility', + cn='极端体验:沦浃肌髓', + cht='極端體驗:淪浹肌髓', + en='Ultimate Experience: Indelibility', + jp='極端体験:感銘', + path_id=2, + rarity=2, + enhancement='', +) +Ultimate_Experience_Shudder = RogueBlessing( + id=27, + name='Ultimate_Experience_Shudder', + cn='极端体验:不寒而栗', + cht='極端體驗:不寒而慄', + en='Ultimate Experience: Shudder', + jp='極端体験:戦慄', + path_id=2, + rarity=2, + enhancement='', +) +Ultimate_Experience_Maverick = RogueBlessing( + id=28, + name='Ultimate_Experience_Maverick', + cn='极端体验:特立独行', + cht='極端體驗:特立獨行', + en='Ultimate Experience: Maverick', + jp='極端体験:異端児', + path_id=2, + rarity=2, + enhancement='', +) +Experience_Unspeakable_Shame = RogueBlessing( + id=29, + name='Experience_Unspeakable_Shame', + cn='体验:难言的羞耻', + cht='體驗:難言的羞恥', + en='Experience: Unspeakable Shame', + jp='体験:言えない恥', + path_id=2, + rarity=1, + enhancement='', +) +Experience_The_Torment_of_Alienation = RogueBlessing( + id=30, + name='Experience_The_Torment_of_Alienation', + cn='体验:疏离的煎熬', + cht='體驗:疏離的煎熬', + en='Experience: The Torment of Alienation', + jp='体験:疎遠の苦しみ', + path_id=2, + rarity=1, + enhancement='', +) +Experience_Lost_Memory = RogueBlessing( + id=31, + name='Experience_Lost_Memory', + cn='体验:丢失的记忆', + cht='體驗:遺失的記憶', + en='Experience: Lost Memory', + jp='体験:失われた記憶', + path_id=2, + rarity=1, + enhancement='', +) +Experience_Stone_Cold_Hatred = RogueBlessing( + id=32, + name='Experience_Stone_Cold_Hatred', + cn='体验:决绝的痛恨', + cht='體驗:決絕的痛恨', + en='Experience: Stone Cold Hatred', + jp='体験:凄烈な憎悪', + path_id=2, + rarity=1, + enhancement='', +) +Experience_Pain__Suffering = RogueBlessing( + id=33, + name='Experience_Pain__Suffering', + cn='体验:病痛的折磨', + cht='體驗:病痛的折磨', + en='Experience: Pain & Suffering', + jp='体験:病の苛み', + path_id=2, + rarity=1, + enhancement='', +) +Experience_Primordial_Hardship = RogueBlessing( + id=34, + name='Experience_Primordial_Hardship', + cn='体验:原初的苦衷', + cht='體驗:原初的苦衷', + en='Experience: Primordial Hardship', + jp='体験:原初の苦衷', + path_id=2, + rarity=1, + enhancement='', +) +Experience_Thrill_of_Escalation = RogueBlessing( + id=35, + name='Experience_Thrill_of_Escalation', + cn='体验:攀升的刺激', + cht='體驗:攀升的刺激', + en='Experience: Thrill of Escalation', + jp='体験:上昇の刺激', + path_id=2, + rarity=1, + enhancement='', +) +Experience_Responsive_Excitement = RogueBlessing( + id=36, + name='Experience_Responsive_Excitement', + cn='体验:回应的兴奋', + cht='體驗:回應的興奮', + en='Experience: Responsive Excitement', + jp='体験:反響の興奮', + path_id=2, + rarity=1, + enhancement='', +) +Funeral_of_Sensory_Pursuivant = RogueBlessing( + id=37, + name='Funeral_of_Sensory_Pursuivant', + cn='感官追奉者的葬礼', + cht='感官追奉者的葬禮', + en='Funeral of Sensory Pursuivant', + jp='感覚追奉者の葬式', + path_id=3, + rarity=3, + enhancement='', +) +The_Man_in_the_Cover = RogueBlessing( + id=38, + name='The_Man_in_the_Cover', + cn='被装在套子里的人', + cht='被裝在套子裡的人', + en='The Man in the Cover', + jp='箱に入った人', + path_id=3, + rarity=3, + enhancement='', +) +Why_Hasn_t_Everything_Already_Disappeared = RogueBlessing( + id=39, + name='Why_Hasn_t_Everything_Already_Disappeared', + cn='为何一切尚未消失', + cht='為何一切尚未消失', + en="Why Hasn't Everything Already Disappeared?", + jp='なぜすべては消えぬか', + path_id=3, + rarity=3, + enhancement='', +) +Beginning_and_End = RogueBlessing( + id=40, + name='Beginning_and_End', + cn='开端与终结', + cht='開端與終結', + en='Beginning and End', + jp='発端と結末', + path_id=3, + rarity=2, + enhancement='', +) +Café_Self_Deceit = RogueBlessing( + id=41, + name='Café_Self_Deceit', + cn='自欺咖啡馆', + cht='自欺咖啡館', + en='Café Self-Deceit', + jp='自己欺瞞カフェ', + path_id=3, + rarity=2, + enhancement='', +) +Call_of_the_Wilderness = RogueBlessing( + id=42, + name='Call_of_the_Wilderness', + cn='旷野的呼告', + cht='曠野的呼告', + en='Call of the Wilderness', + jp='広野の呼び声', + path_id=3, + rarity=2, + enhancement='', +) +Night_Beyond_Pyre = RogueBlessing( + id=43, + name='Night_Beyond_Pyre', + cn='火堆外的夜', + cht='火堆外的夜', + en='Night Beyond Pyre', + jp='焚火の外の夜', + path_id=3, + rarity=2, + enhancement='', +) +Hell_is_Other_People = RogueBlessing( + id=44, + name='Hell_is_Other_People', + cn='他人即地狱', + cht='他人即地獄', + en='Hell is Other People', + jp='他人は地獄', + path_id=3, + rarity=2, + enhancement='', +) +Twilight_of_Existence = RogueBlessing( + id=45, + name='Twilight_of_Existence', + cn='存在的黄昏', + cht='存在的黃昏', + en='Twilight of Existence', + jp='存在の黄昏', + path_id=3, + rarity=2, + enhancement='', +) +All_Things_are_Possible = RogueBlessing( + id=46, + name='All_Things_are_Possible', + cn='无根据颂歌', + cht='無根據頌歌', + en='All Things are Possible', + jp='根拠なき賛歌', + path_id=3, + rarity=2, + enhancement='', +) +Ignosticism = RogueBlessing( + id=47, + name='Ignosticism', + cn='漠视主义', + cht='漠視主義', + en='Ignosticism', + jp='漠視主義', + path_id=3, + rarity=1, + enhancement='', +) +Questioning_of_Purpose = RogueBlessing( + id=48, + name='Questioning_of_Purpose', + cn='意义质询', + cht='意義質詢', + en='Questioning of Purpose', + jp='意義への詰問', + path_id=3, + rarity=1, + enhancement='', +) +Blind_Vision = RogueBlessing( + id=49, + name='Blind_Vision', + cn='盲目视界', + cht='盲目視界', + en='Blind Vision', + jp='盲目の視界', + path_id=3, + rarity=1, + enhancement='', +) +Tragic_Lecture = RogueBlessing( + id=50, + name='Tragic_Lecture', + cn='悲剧讲座', + cht='悲劇講座', + en='Tragic Lecture', + jp='悲劇講座', + path_id=3, + rarity=1, + enhancement='', +) +Sensory_Labyrinth = RogueBlessing( + id=51, + name='Sensory_Labyrinth', + cn='知觉迷墙', + cht='知覺迷牆', + en='Sensory Labyrinth', + jp='知覚の壁', + path_id=3, + rarity=1, + enhancement='', +) +Emotional_Decluttering = RogueBlessing( + id=52, + name='Emotional_Decluttering', + cn='情绪舍离', + cht='情緒捨離', + en='Emotional Decluttering', + jp='情緒捨離', + path_id=3, + rarity=1, + enhancement='', +) +Offerings_of_Deception = RogueBlessing( + id=53, + name='Offerings_of_Deception', + cn='虚妄供品', + cht='虛妄供品', + en='Offerings of Deception', + jp='虚妄の供物', + path_id=3, + rarity=1, + enhancement='', +) +Before_Sunrise = RogueBlessing( + id=54, + name='Before_Sunrise', + cn='日出之前', + cht='日出之前', + en='Before Sunrise', + jp='日の出前', + path_id=3, + rarity=1, + enhancement='', +) +Prosperity_Longevity = RogueBlessing( + id=55, + name='Prosperity_Longevity', + cn='葳蕤繁祉,延彼遐龄', + cht='葳蕤繁祉,延彼遐齡', + en='Prosperity, Longevity', + jp='彼の遐齢を延さん', + path_id=4, + rarity=3, + enhancement='', +) +Mudra_of_Blessing = RogueBlessing( + id=56, + name='Mudra_of_Blessing', + cn='若罪若福,施诸愿印', + cht='若罪若福,施諸願印', + en='Mudra of Blessing', + jp='若の罪福、皆に施願を', + path_id=4, + rarity=3, + enhancement='', +) +Being_of_Abundance_Becoming_One_Mind = RogueBlessing( + id=57, + name='Being_of_Abundance_Becoming_One_Mind', + cn='丰饶众生,一法界心', + cht='豐饒眾生,一法界心', + en='Being of Abundance, Becoming One Mind', + jp='衆生に豊穣を', + path_id=4, + rarity=3, + enhancement='', +) +Good_Deeds_Come_After_Old_Sins = RogueBlessing( + id=58, + name='Good_Deeds_Come_After_Old_Sins', + cn='灭罪累生善', + cht='滅罪累生善', + en='Good Deeds Come After Old Sins', + jp='滅罪生善', + path_id=4, + rarity=2, + enhancement='', +) +Mortals_of_the_Buddha_Field = RogueBlessing( + id=59, + name='Mortals_of_the_Buddha_Field', + cn='天人不动众', + cht='天人不動眾', + en='Mortals of the Buddha-Field', + jp='天人不動衆', + path_id=4, + rarity=2, + enhancement='', +) +Salvation_From_Damnation = RogueBlessing( + id=60, + name='Salvation_From_Damnation', + cn='慧海度慈航', + cht='慧海度慈航', + en='Salvation From Damnation', + jp='慧海を渡る慈航', + path_id=4, + rarity=2, + enhancement='', +) +Precious_Moon_Like_Candlelight = RogueBlessing( + id=61, + name='Precious_Moon_Like_Candlelight', + cn='宝光烛日月', + cht='寶光燭日月', + en='Precious Moon-Like Candlelight', + jp='日月を燭らす宝光', + path_id=4, + rarity=2, + enhancement='', +) +Aversion_to_Suffering = RogueBlessing( + id=62, + name='Aversion_to_Suffering', + cn='厌离邪秽苦', + cht='厭離邪穢苦', + en='Aversion to Suffering', + jp='邪穢の苦を厭離す', + path_id=4, + rarity=2, + enhancement='', +) +Clear_Lucite_Body = RogueBlessing( + id=63, + name='Clear_Lucite_Body', + cn='明澈琉璃身', + cht='明澈琉璃身', + en='Clear Lucite Body', + jp='明澄琉璃の身', + path_id=4, + rarity=2, + enhancement='', +) +Prajna_Boat = RogueBlessing( + id=64, + name='Prajna_Boat', + cn='大愿般若船', + cht='大願般若船', + en='Prajna Boat', + jp='大愿、般若の船', + path_id=4, + rarity=2, + enhancement='', +) +Rain_of_Truth = RogueBlessing( + id=65, + name='Rain_of_Truth', + cn='法雨', + cht='法雨', + en='Rain of Truth', + jp='法雨', + path_id=4, + rarity=1, + enhancement='', +) +Sweet_Dew = RogueBlessing( + id=66, + name='Sweet_Dew', + cn='甘露', + cht='甘露', + en='Sweet Dew', + jp='甘露', + path_id=4, + rarity=1, + enhancement='', +) +Extended_Life = RogueBlessing( + id=67, + name='Extended_Life', + cn='延寿', + cht='延壽', + en='Extended Life', + jp='延寿', + path_id=4, + rarity=1, + enhancement='', +) +Seal = RogueBlessing( + id=68, + name='Seal', + cn='愿印', + cht='願印', + en='Seal', + jp='願印', + path_id=4, + rarity=1, + enhancement='', +) +Dispel_Disaster = RogueBlessing( + id=69, + name='Dispel_Disaster', + cn='禳灾', + cht='禳災', + en='Dispel Disaster', + jp='厄払い', + path_id=4, + rarity=1, + enhancement='', +) +Rebirth = RogueBlessing( + id=70, + name='Rebirth', + cn='回生', + cht='回生', + en='Rebirth', + jp='回生', + path_id=4, + rarity=1, + enhancement='', +) +Victorious_Force = RogueBlessing( + id=71, + name='Victorious_Force', + cn='胜军', + cht='勝軍', + en='Victorious Force', + jp='勝軍', + path_id=4, + rarity=1, + enhancement='', +) +Blessing = RogueBlessing( + id=72, + name='Blessing', + cn='加持', + cht='加持', + en='Blessing', + jp='加持', + path_id=4, + rarity=1, + enhancement='', +) +Imperial_Reign = RogueBlessing( + id=73, + name='Imperial_Reign', + cn='帝星君临制穹桑', + cht='帝星君臨制穹桑', + en='Imperial Reign', + jp='帝星臨めば穹桑を制す', + path_id=5, + rarity=3, + enhancement='', +) +Imperishable_Victory = RogueBlessing( + id=74, + name='Imperishable_Victory', + cn='帝车超光所向捷', + cht='帝車超光所向捷', + en='Imperishable Victory', + jp='光越す制勝の帝車', + path_id=5, + rarity=3, + enhancement='', +) +Celestial_Annihilation = RogueBlessing( + id=75, + name='Celestial_Annihilation', + cn='帝弓断空彻太清', + cht='帝弓斷空徹太清', + en='Celestial Annihilation', + jp='太清を徹す断空の帝弓', + path_id=5, + rarity=3, + enhancement='', +) +Battle_Against_the_Old_Foe = RogueBlessing( + id=76, + name='Battle_Against_the_Old_Foe', + cn='天舟缴夙敌', + cht='天舟繳夙敵', + en='Battle Against the Old Foe', + jp='夙敵繳める天舟', + path_id=5, + rarity=2, + enhancement='', +) +Archery_Duel = RogueBlessing( + id=77, + name='Archery_Duel', + cn='白矢决射御', + cht='白矢決射御', + en='Archery Duel', + jp='射御を決する白矢', + path_id=5, + rarity=2, + enhancement='', +) +Adept_Bow = RogueBlessing( + id=78, + name='Adept_Bow', + cn='序师执迟彝', + cht='序師執遲彝', + en="Adept's Bow", + jp='遅彝弓を執る序師', + path_id=5, + rarity=2, + enhancement='', +) +Flowing_Mist = RogueBlessing( + id=79, + name='Flowing_Mist', + cn='流岚追孽物', + cht='流嵐追孽物', + en='Flowing Mist', + jp='忌み物を追う流嵐', + path_id=5, + rarity=2, + enhancement='', +) +Auspicious_Star = RogueBlessing( + id=80, + name='Auspicious_Star', + cn='景星助狩月', + cht='景星助狩月', + en='Auspicious Star', + jp='狩月を助ける景星', + path_id=5, + rarity=2, + enhancement='', +) +Ejecting_the_Borisin = RogueBlessing( + id=81, + name='Ejecting_the_Borisin', + cn='云镝逐步离', + cht='雲鏑逐步離', + en='Ejecting the Borisin', + jp='歩離を駆逐せし雲鏑', + path_id=5, + rarity=2, + enhancement='', +) +Monster_Expelling_Rainbow = RogueBlessing( + id=82, + name='Monster_Expelling_Rainbow', + cn='飞虹诛凿齿', + cht='飛虹誅鑿齒', + en='Monster-Expelling Rainbow', + jp='鑿齒を誅つ飛虹', + path_id=5, + rarity=2, + enhancement='', +) +Vermeil_Bow_and_White_Arrow = RogueBlessing( + id=83, + name='Vermeil_Bow_and_White_Arrow', + cn='彤弓素矰', + cht='彤弓素矰', + en='Vermeil Bow and White Arrow', + jp='緋弓素矢', + path_id=5, + rarity=1, + enhancement='', +) +Skirting_Life_and_Death = RogueBlessing( + id=84, + name='Skirting_Life_and_Death', + cn='背生击死', + cht='背生擊死', + en='Skirting Life and Death', + jp='背生撃死', + path_id=5, + rarity=1, + enhancement='', +) +Shrewd_Arrangement = RogueBlessing( + id=85, + name='Shrewd_Arrangement', + cn='背孤击虚', + cht='背孤擊虛', + en='Shrewd Arrangement', + jp='背孤撃虚', + path_id=5, + rarity=1, + enhancement='', +) +Thundering_Chariot = RogueBlessing( + id=86, + name='Thundering_Chariot', + cn='雷车动地', + cht='雷車動地', + en='Thundering Chariot', + jp='雷車動地', + path_id=5, + rarity=1, + enhancement='', +) +Constellation_Surge = RogueBlessing( + id=87, + name='Constellation_Surge', + cn='电射牛斗', + cht='電射牛斗', + en='Constellation Surge', + jp='牛斗射る紫電', + path_id=5, + rarity=1, + enhancement='', +) +Catastrophic_Constellation = RogueBlessing( + id=88, + name='Catastrophic_Constellation', + cn='天棓步危', + cht='天棓步危', + en='Catastrophic Constellation', + jp='危宮へ歩む天棓', + path_id=5, + rarity=1, + enhancement='', +) +Vaulting_Ambition = RogueBlessing( + id=89, + name='Vaulting_Ambition', + cn='桑弧蓬矢', + cht='桑弧蓬矢', + en='Vaulting Ambition', + jp='桑弧蓬矢', + path_id=5, + rarity=1, + enhancement='', +) +Blessed_Bow_and_Arrow = RogueBlessing( + id=90, + name='Blessed_Bow_and_Arrow', + cn='乌号綦箭', + cht='烏號綦箭', + en='Blessed Bow and Arrow', + jp='烏号綦箭', + path_id=5, + rarity=1, + enhancement='', +) +Non_Inverse_Antimatter_Equation = RogueBlessing( + id=91, + name='Non_Inverse_Antimatter_Equation', + cn='反物质非逆方程', + cht='反物質非逆方程式', + en='Non-Inverse Antimatter Equation', + jp='反物質非可逆方程式', + path_id=6, + rarity=3, + enhancement='', +) +Universal_Heat_Death_Characteristic = RogueBlessing( + id=92, + name='Universal_Heat_Death_Characteristic', + cn='寰宇热寂特征数', + cht='寰宇熱寂特徵數', + en='Universal Heat Death Characteristic', + jp='熱的死の固有値', + path_id=6, + rarity=3, + enhancement='', +) +Regression_Inequality_of_Annihilation = RogueBlessing( + id=93, + name='Regression_Inequality_of_Annihilation', + cn='湮灭回归不等式', + cht='湮滅回歸不等式', + en='Regression Inequality of Annihilation', + jp='対消滅回帰不等式', + path_id=6, + rarity=3, + enhancement='', +) +Incremental_Doomsday = RogueBlessing( + id=94, + name='Incremental_Doomsday', + cn='递增性末日', + cht='遞增性末日', + en='Incremental Doomsday', + jp='逓増的終末', + path_id=6, + rarity=2, + enhancement='', +) +Catastrophic_Resonance = RogueBlessing( + id=95, + name='Catastrophic_Resonance', + cn='灾难性共振', + cht='災難性共振', + en='Catastrophic Resonance', + jp='災難的共振', + path_id=6, + rarity=2, + enhancement='', +) +Indicative_Depth_of_Field = RogueBlessing( + id=96, + name='Indicative_Depth_of_Field', + cn='预兆性景深', + cht='預兆性景深', + en='Indicative Depth of Field', + jp='予兆的被写界深度', + path_id=6, + rarity=2, + enhancement='', +) +Devastating_Accretion = RogueBlessing( + id=97, + name='Devastating_Accretion', + cn='毁灭性吸积', + cht='毀滅性吸積', + en='Devastating Accretion', + jp='壊滅的降着', + path_id=6, + rarity=2, + enhancement='', +) +Destructive_Flare = RogueBlessing( + id=98, + name='Destructive_Flare', + cn='破坏性耀发', + cht='破壞性耀發', + en='Destructive Flare', + jp='破壊的フレア', + path_id=6, + rarity=2, + enhancement='', +) +Disciplinary_Flicker = RogueBlessing( + id=99, + name='Disciplinary_Flicker', + cn='戒律性闪变', + cht='戒律性閃變', + en='Disciplinary Flicker', + jp='戒律的フラッシュ', + path_id=6, + rarity=2, + enhancement='', +) +Hazardous_Lucent_Residue = RogueBlessing( + id=100, + name='Hazardous_Lucent_Residue', + cn='危害性余光', + cht='危害性餘光', + en='Hazardous Lucent Residue', + jp='危害的余光', + path_id=6, + rarity=2, + enhancement='', +) +Primordial_Black_Hole = RogueBlessing( + id=101, + name='Primordial_Black_Hole', + cn='原生黑洞', + cht='原生黑洞', + en='Primordial Black Hole', + jp='原始ブラックホール', + path_id=6, + rarity=1, + enhancement='', +) +Reflection = RogueBlessing( + id=102, + name='Reflection', + cn='回光效应', + cht='回光效應', + en='Reflection', + jp='光壊変', + path_id=6, + rarity=1, + enhancement='', +) +Orbital_Redshift = RogueBlessing( + id=103, + name='Orbital_Redshift', + cn='轨道红移', + cht='軌道紅移', + en='Orbital Redshift', + jp='軌道赤方偏移', + path_id=6, + rarity=1, + enhancement='', +) +Instability_Strip = RogueBlessing( + id=104, + name='Instability_Strip', + cn='不稳定带', + cht='不穩定帶', + en='Instability Strip', + jp='不安定帯', + path_id=6, + rarity=1, + enhancement='', +) +Metric_Reservation = RogueBlessing( + id=105, + name='Metric_Reservation', + cn='储备度规', + cht='儲備度規', + en='Metric Reservation', + jp='備蓄計量', + path_id=6, + rarity=1, + enhancement='', +) +Sentinel_Satellite = RogueBlessing( + id=106, + name='Sentinel_Satellite', + cn='哨戒卫星', + cht='哨戒衛星', + en='Sentinel Satellite', + jp='哨戒衛星', + path_id=6, + rarity=1, + enhancement='', +) +Polarization_Receptor = RogueBlessing( + id=107, + name='Polarization_Receptor', + cn='偏振受体', + cht='偏振受體', + en='Polarization Receptor', + jp='偏光受容体', + path_id=6, + rarity=1, + enhancement='', +) +Eternally_Collapsing_Object = RogueBlessing( + id=108, + name='Eternally_Collapsing_Object', + cn='永坍缩体', + cht='永坍縮體', + en='Eternally Collapsing Object', + jp='永久崩壊天体', + path_id=6, + rarity=1, + enhancement='', +) +Auto_Harmonica_Whitest_Night = RogueBlessing( + id=109, + name='Auto_Harmonica_Whitest_Night', + cn='《自动口琴•茫茫白夜》', + cht='《自動口琴•茫茫白夜》', + en='Auto-Harmonica: Whitest Night', + jp='『自動ハーモニカ・茫々たる白夜』', + path_id=7, + rarity=3, + enhancement='', +) +Slaughterhouse_No_4_Rest_in_Peace = RogueBlessing( + id=110, + name='Slaughterhouse_No_4_Rest_in_Peace', + cn='《四号屠场•众生安眠》', + cht='《四號屠場•眾生安眠》', + en='Slaughterhouse No. 4: Rest in Peace', + jp='『四番屠畜場・皆眠りて』', + path_id=7, + rarity=3, + enhancement='', +) +Champion_Dinner_Cat_Cradle = RogueBlessing( + id=111, + name='Champion_Dinner_Cat_Cradle', + cn='《冠军晚餐•猫的摇篮》', + cht='《冠軍晚餐•貓的搖籃》', + en="Champion's Dinner: Cat's Cradle", + jp='『チャンピオンのディナー・猫のゆりかご』', + path_id=7, + rarity=3, + enhancement='', +) +Portrait_of_A_Man_On_Fire = RogueBlessing( + id=112, + name='Portrait_of_A_Man_On_Fire', + cn='《燃烧男子的肖像》', + cht='《燃燒男子的肖像》', + en='Portrait of A Man On Fire', + jp='『燃ゆる男の肖像』', + path_id=7, + rarity=2, + enhancement='', +) +Just_Keep_on_Crying = RogueBlessing( + id=113, + name='Just_Keep_on_Crying', + cn='《流吧,你的眼泪》', + cht='《流吧,你的眼淚》', + en='Just Keep on Crying!', + jp='『流れよ汝が涙』', + path_id=7, + rarity=2, + enhancement='', +) +The_Hourglass_Kindergarten = RogueBlessing( + id=114, + name='The_Hourglass_Kindergarten', + cn='《砂时镜下的幼园》', + cht='《砂時鏡下的幼園》', + en='The Hourglass Kindergarten', + jp='『砂時計の幼稚園』', + path_id=7, + rarity=2, + enhancement='', +) +The_Painted_Albatross = RogueBlessing( + id=115, + name='The_Painted_Albatross', + cn='《被涂污的信天翁》', + cht='《被塗汙的信天翁》', + en='The Painted Albatross', + jp='『汚されたアホウドリ』', + path_id=7, + rarity=2, + enhancement='', +) +_12_Monkeys_and_Angry_Men = RogueBlessing( + id=116, + name='_12_Monkeys_and_Angry_Men', + cn='《十二猴子与怒汉》', + cht='《十二猴子與怒漢》', + en='12 Monkeys and Angry Men', + jp='『十二のサルと怒れる男』', + path_id=7, + rarity=2, + enhancement='', +) +Aiden_Gravitational_Rainbow = RogueBlessing( + id=117, + name='Aiden_Gravitational_Rainbow', + cn='《利尔他引力之虹》', + cht='《利爾他引力之虹》', + en='Aiden Gravitational Rainbow', + jp='『リルタ重力の虹』', + path_id=7, + rarity=2, + enhancement='', +) +Twenty_First_Military_Rule = RogueBlessing( + id=118, + name='Twenty_First_Military_Rule', + cn='《第二十一条军规》', + cht='《第二十一條軍規》', + en='Twenty-First Military Rule', + jp='『キャッチ=21』', + path_id=7, + rarity=2, + enhancement='', +) +Exemplary_Conduct = RogueBlessing( + id=119, + name='Exemplary_Conduct', + cn='《操行满分》', + cht='《操行滿分》', + en='Exemplary Conduct', + jp='『素行満点』', + path_id=7, + rarity=1, + enhancement='', +) +Mostly_Harmful = RogueBlessing( + id=120, + name='Mostly_Harmful', + cn='《基本有害》', + cht='《基本有害》', + en='Mostly Harmful', + jp='『ほとんど有害』', + path_id=7, + rarity=1, + enhancement='', +) +Suspiria = RogueBlessing( + id=121, + name='Suspiria', + cn='《阴风阵阵》', + cht='《陰風陣陣》', + en='Suspiria', + jp='『サスペンス』', + path_id=7, + rarity=1, + enhancement='', +) +Pale_Fire = RogueBlessing( + id=122, + name='Pale_Fire', + cn='《灰暗的火》', + cht='《灰暗的火》', + en='Pale Fire', + jp='『仄暗い炎』', + path_id=7, + rarity=1, + enhancement='', +) +Back_to_the_Lighthouse = RogueBlessing( + id=123, + name='Back_to_the_Lighthouse', + cn='《回灯塔去》', + cht='《回燈塔去》', + en='Back to the Lighthouse', + jp='『灯台へ戻ろう』', + path_id=7, + rarity=1, + enhancement='', +) +Doctor_of_Love = RogueBlessing( + id=124, + name='Doctor_of_Love', + cn='《奇爱医生》', + cht='《奇愛醫生》', + en='Doctor of Love', + jp='『医者の異常な愛情』', + path_id=7, + rarity=1, + enhancement='', +) +Platinum_Age = RogueBlessing( + id=125, + name='Platinum_Age', + cn='《铂金时代》', + cht='《鉑金時代》', + en='Platinum Age', + jp='『白金時代』', + path_id=7, + rarity=1, + enhancement='', +) +Clockwork_Apple = RogueBlessing( + id=126, + name='Clockwork_Apple', + cn='《发条苹果》', + cht='《發條蘋果》', + en='Clockwork Apple', + jp='『時計仕掛けのリンゴ』', + path_id=7, + rarity=1, + enhancement='', +) diff --git a/tasks/rogue/keywords/bonus.py b/tasks/rogue/keywords/bonus.py new file mode 100644 index 000000000..03740015d --- /dev/null +++ b/tasks/rogue/keywords/bonus.py @@ -0,0 +1,29 @@ +from .classes import RogueBonus + +# This file was auto-generated, do not modify it manually. To generate: +# ``` python -m dev_tools.keyword_extract ``` + +Fragmented_Cosmos = RogueBonus( + id=1, + name='Fragmented_Cosmos', + cn='破碎宇宙', + cht='破碎宇宙', + en='Fragmented Cosmos', + jp='破裂した宇宙', +) +Blessing_Cosmos = RogueBonus( + id=2, + name='Blessing_Cosmos', + cn='祝福宇宙', + cht='祝福宇宙', + en='Blessing Cosmos', + jp='祝福された宇宙', +) +Miracle_Cosmos = RogueBonus( + id=3, + name='Miracle_Cosmos', + cn='神奇宇宙', + cht='神奇宇宙', + en='Miracle Cosmos', + jp='奇跡的な宇宙', +) diff --git a/tasks/rogue/keywords/classes.py b/tasks/rogue/keywords/classes.py new file mode 100644 index 000000000..4a740792e --- /dev/null +++ b/tasks/rogue/keywords/classes.py @@ -0,0 +1,70 @@ +from dataclasses import dataclass +from typing import ClassVar + +from dev_tools.keyword_extract import UI_LANGUAGES +from module.ocr.keyword import Keyword + + +@dataclass(repr=False) +class RogueBlessing(Keyword): + instances: ClassVar = {} + path_id: int + rarity: int + enhancement: str + + @property + def path_name(self): + path = RoguePath.instances[self.path_id] + return path.path_name + + @property + def blessing_name(self): + return [self.__getattribute__(f"{server}_parsed") + for server in UI_LANGUAGES if hasattr(self, f"{server}_parsed")] + + +@dataclass(repr=False) +class RoguePath(Keyword): + instances: ClassVar = {} + + @property + def path_name(self): + return [self.__getattribute__(f"{server}_parsed").replace("the", '') + for server in UI_LANGUAGES if hasattr(self, f"{server}_parsed")] + + +@dataclass(repr=False) +class RogueResonance(Keyword): + instances: ClassVar = {} + path_id: int + rarity: int + + @property + def resonance_name(self): + return [self.__getattribute__(f"{server}_parsed") + for server in UI_LANGUAGES if hasattr(self, f"{server}_parsed")] + + +@dataclass(repr=False) +class RogueCurio(Keyword): + instances: ClassVar = {} + + @property + def curio_name(self): + return [self.__getattribute__(f"{server}_parsed") + for server in UI_LANGUAGES if hasattr(self, f"{server}_parsed")] + + +@dataclass(repr=False) +class RogueBonus(Keyword): + instances: ClassVar = {} + + +@dataclass(repr=False) +class RogueEnhancement(Keyword): + instances: ClassVar = {} + + @property + def enhancement_keyword(self): + return [self.__getattribute__(f"{server}_parsed") + for server in UI_LANGUAGES if hasattr(self, f"{server}_parsed")] diff --git a/tasks/rogue/keywords/curio.py b/tasks/rogue/keywords/curio.py new file mode 100644 index 000000000..d68ffb82f --- /dev/null +++ b/tasks/rogue/keywords/curio.py @@ -0,0 +1,381 @@ +from .classes import RogueCurio + +# This file was auto-generated, do not modify it manually. To generate: +# ``` python -m dev_tools.keyword_extract ``` + +Dimension_Reduction_Dice = RogueCurio( + id=1, + name='Dimension_Reduction_Dice', + cn='降维骰子', + cht='降維骰子', + en='Dimension Reduction Dice', + jp='次元削減ダイス', +) +Chaos_Trametes = RogueCurio( + id=2, + name='Chaos_Trametes', + cn='混沌云芝', + cht='混沌雲芝', + en='Chaos Trametes', + jp='混沌の雲芝', +) +Warping_Compound_Eye = RogueCurio( + id=3, + name='Warping_Compound_Eye', + cn='跃迁复眼', + cht='躍遷複眼', + en='Warping Compound Eye', + jp='跳躍複眼', +) +Fruit_of_the_Alien_Tree = RogueCurio( + id=4, + name='Fruit_of_the_Alien_Tree', + cn='异木果实', + cht='異木果實', + en='Fruit of the Alien Tree', + jp='異木の果実', +) +Casket_of_Inaccuracy = RogueCurio( + id=5, + name='Casket_of_Inaccuracy', + cn='测不准匣', + cht='測不準匣', + en='Casket of Inaccuracy', + jp='不確定の匣', +) +Ambergris_Cheese = RogueCurio( + id=6, + name='Ambergris_Cheese', + cn='香涎干酪', + cht='香涎乾酪', + en='Ambergris Cheese', + jp='香涎チーズ', +) +Fortune_Glue = RogueCurio( + id=7, + name='Fortune_Glue', + cn='福灵胶', + cht='福靈膠', + en='Fortune Glue', + jp='幸福クリーム', +) +The_Parchment_That_Always_Eats = RogueCurio( + id=8, + name='The_Parchment_That_Always_Eats', + cn='永不停嘴的羊皮卷', + cht='永不停嘴的羊皮卷', + en='The Parchment That Always Eats', + jp='おしゃべり羊皮紙', +) +Broken_Cuckoo_Clock = RogueCurio( + id=9, + name='Broken_Cuckoo_Clock', + cn='破碎咕咕钟', + cht='破碎咕咕鐘', + en='Broken Cuckoo Clock', + jp='壊れた鳩時計', +) +Mechanical_Cuckoo_Clock = RogueCurio( + id=10, + name='Mechanical_Cuckoo_Clock', + cn='机械咕咕钟', + cht='機械咕咕鐘', + en='Mechanical Cuckoo Clock', + jp='機械式鳩時計', +) +The_Doctor_Robe = RogueCurio( + id=11, + name='The_Doctor_Robe', + cn='博士之袍', + cht='博士之袍', + en="The Doctor's Robe", + jp='博士のローブ', +) +Society_Ticket = RogueCurio( + id=12, + name='Society_Ticket', + cn='俱乐部券', + cht='俱樂部券', + en='Society Ticket', + jp='クラブチケット', +) +Faith_Bond = RogueCurio( + id=13, + name='Faith_Bond', + cn='信仰债券', + cht='信仰債券', + en='Faith Bond', + jp='信仰債券', +) +Robe_of_The_Beauty = RogueCurio( + id=14, + name='Robe_of_The_Beauty', + cn='纯美之袍', + cht='純美之袍', + en='Robe of The Beauty', + jp='純美のローブ', +) +Gold_Coin_of_Discord = RogueCurio( + id=15, + name='Gold_Coin_of_Discord', + cn='分裂金币', + cht='分裂金幣', + en='Gold Coin of Discord', + jp='分裂金貨', +) +Useless_Typewriter = RogueCurio( + id=16, + name='Useless_Typewriter', + cn='无效文字打印机', + cht='無效文字印表機', + en='Useless Typewriter', + jp='無効文字タイプライター', +) +Void_Wick_Trimmer = RogueCurio( + id=17, + name='Void_Wick_Trimmer', + cn='空无烛剪', + cht='空無燭剪', + en='Void Wick Trimmer', + jp='空無の芯切り', +) +Omniscient_Capsule = RogueCurio( + id=18, + name='Omniscient_Capsule', + cn='万识囊', + cht='萬識囊', + en='Omniscient Capsule', + jp='全知袋', +) +Record_from_Beyond_the_Sky = RogueCurio( + id=19, + name='Record_from_Beyond_the_Sky', + cn='天外重声大碟', + cht='天外合唱專輯', + en='Record from Beyond the Sky', + jp='天外聖歌隊のレコード', +) +Entropic_Die = RogueCurio( + id=20, + name='Entropic_Die', + cn='万象无常骰', + cht='萬象無常骰', + en='Entropic Die', + jp='万象無常のサイコロ', +) +Shining_Trapezohedron_Die = RogueCurio( + id=21, + name='Shining_Trapezohedron_Die', + cn='闪耀的偏方三八面骰', + cht='閃耀的偏方三八面骰', + en='Shining Trapezohedron Die', + jp='輝くトラペゾヘドロンサイコロ', +) +Sealing_Wax_of_Preservation = RogueCurio( + id=22, + name='Sealing_Wax_of_Preservation', + cn='存护火漆', + cht='存護火漆', + en='Sealing Wax of Preservation', + jp='存護の封蝋', +) +Sealing_Wax_of_Elation = RogueCurio( + id=23, + name='Sealing_Wax_of_Elation', + cn='欢愉火漆', + cht='歡愉火漆', + en='Sealing Wax of Elation', + jp='愉悦の封蝋', +) +Sealing_Wax_of_The_Hunt = RogueCurio( + id=24, + name='Sealing_Wax_of_The_Hunt', + cn='巡猎火漆', + cht='巡獵火漆', + en='Sealing Wax of The Hunt', + jp='巡狩の封蝋', +) +Sealing_Wax_of_Destruction = RogueCurio( + id=25, + name='Sealing_Wax_of_Destruction', + cn='毁灭火漆', + cht='毀滅火漆', + en='Sealing Wax of Destruction', + jp='壊滅の封蝋', +) +Sealing_Wax_of_Remembrance = RogueCurio( + id=26, + name='Sealing_Wax_of_Remembrance', + cn='记忆火漆', + cht='記憶火漆', + en='Sealing Wax of Remembrance', + jp='記憶の封蝋', +) +Sealing_Wax_of_Nihility = RogueCurio( + id=27, + name='Sealing_Wax_of_Nihility', + cn='虚无火漆', + cht='虛無火漆', + en='Sealing Wax of Nihility', + jp='虚無の封蝋', +) +Sealing_Wax_of_Abundance = RogueCurio( + id=28, + name='Sealing_Wax_of_Abundance', + cn='丰饶火漆', + cht='豐饒火漆', + en='Sealing Wax of Abundance', + jp='豊穣の封蝋', +) +Corrupted_Code = RogueCurio( + id=29, + name='Corrupted_Code', + cn='乱七八糟的代码', + cht='亂七八糟的程式碼', + en='Corrupted Code', + jp='ぐちゃぐちゃなコード', +) +Odd_Code = RogueCurio( + id=30, + name='Odd_Code', + cn='有点蹊跷的代码', + cht='有點蹊蹺的程式碼', + en='Odd Code', + jp='少し怪しげなコード', +) +Normal_Code = RogueCurio( + id=31, + name='Normal_Code', + cn='中规中矩的代码', + cht='中規中矩的程式碼', + en='Normal Code', + jp='杓子定規なコード', +) +Elegant_Code = RogueCurio( + id=32, + name='Elegant_Code', + cn='精确优雅的代码', + cht='精確優雅的程式碼', + en='Elegant Code', + jp='正確で完璧なコード', +) +Mysterious_Code = RogueCurio( + id=33, + name='Mysterious_Code', + cn='没有注释的代码', + cht='沒有註解的程式碼', + en='Mysterious Code', + jp='注釈がないコード', +) +Infinitely_Recursive_Code = RogueCurio( + id=34, + name='Infinitely_Recursive_Code', + cn='无限递归的代码', + cht='無限遞迴的程式碼', + en='Infinitely Recursive Code', + jp='無限再帰するコード', +) +Shattered_Star_Bait = RogueCurio( + id=35, + name='Shattered_Star_Bait', + cn='碎星芳饵', + cht='碎星芳餌', + en='Shattered Star Bait', + jp='砕けた星の釣り餌', +) +Obliteration_Wick_Trimmer = RogueCurio( + id=36, + name='Obliteration_Wick_Trimmer', + cn='湮灭烛剪', + cht='湮滅燭剪', + en='Obliteration Wick Trimmer', + jp='湮滅の芯切り', +) +Insect_Web = RogueCurio( + id=37, + name='Insect_Web', + cn='虫网', + cht='蟲網', + en='Insect Web', + jp='虫網', +) +Angel_type_I_O_U_Dispenser = RogueCurio( + id=38, + name='Angel_type_I_O_U_Dispenser', + cn='天使型谢债发行机', + cht='天使型謝債發行機', + en='Angel-type I.O.U. Dispenser', + jp='天使型謝債発行機', +) +Laurel_Crown_of_Planar_Shifts = RogueCurio( + id=39, + name='Laurel_Crown_of_Planar_Shifts', + cn='换境桂冠', + cht='換境桂冠', + en='Laurel Crown of Planar Shifts', + jp='換境桂冠', +) +Space_Time_Prism = RogueCurio( + id=40, + name='Space_Time_Prism', + cn='时空棱镜', + cht='時空稜鏡', + en='Space-Time Prism', + jp='時空のプリズム', +) +Galactic_Big_Lotto = RogueCurio( + id=41, + name='Galactic_Big_Lotto', + cn='银河大乐透', + cht='銀河大樂透', + en='Galactic Big Lotto', + jp='銀河ビッグロッタリー', +) +Divination_Cuckoo_Clock = RogueCurio( + id=42, + name='Divination_Cuckoo_Clock', + cn='卜筮咕咕钟', + cht='卜筮咕咕鐘', + en='Divination Cuckoo Clock', + jp='占い鳩時計', +) +Black_Forest_Cuckoo_Clock = RogueCurio( + id=43, + name='Black_Forest_Cuckoo_Clock', + cn='黑森林咕咕钟', + cht='黑森林咕咕鐘', + en='Black Forest Cuckoo Clock', + jp='黒森鳩時計', +) +Perpetual_Motion_Cuckoo_Clock = RogueCurio( + id=44, + name='Perpetual_Motion_Cuckoo_Clock', + cn='永动咕咕钟', + cht='永動咕咕鐘', + en='Perpetual Motion Cuckoo Clock', + jp='永久鳩時計', +) +Punklorde_Mentality = RogueCurio( + id=45, + name='Punklorde_Mentality', + cn='朋克洛德精神', + cht='龐克洛德精神', + en='Punklorde Mentality', + jp='パンクロードの精神', +) +Beacon_Coloring_Paste = RogueCurio( + id=46, + name='Beacon_Coloring_Paste', + cn='信标着色剂', + cht='信標著色劑', + en='Beacon Coloring Paste', + jp='ビーコン着色剤', +) +IPC_Cuckoo_Clock = RogueCurio( + id=47, + name='IPC_Cuckoo_Clock', + cn='公司咕咕钟', + cht='公司咕咕鐘', + en='IPC Cuckoo Clock', + jp='カンパニー鳩時計', +) diff --git a/tasks/rogue/keywords/enhancement.py b/tasks/rogue/keywords/enhancement.py new file mode 100644 index 000000000..7012fb94e --- /dev/null +++ b/tasks/rogue/keywords/enhancement.py @@ -0,0 +1,13 @@ +from .classes import RogueEnhancement + +# This file was auto-generated, do not modify it manually. To generate: +# ``` python -m dev_tools.keyword_extract ``` + +Already_Enhanced = RogueEnhancement( + id=1, + name='Already_Enhanced', + cn='已强化', + cht='已強化', + en='Already Enhanced', + jp='強化済み', +) diff --git a/tasks/rogue/keywords/path.py b/tasks/rogue/keywords/path.py new file mode 100644 index 000000000..c9553a693 --- /dev/null +++ b/tasks/rogue/keywords/path.py @@ -0,0 +1,61 @@ +from .classes import RoguePath + +# This file was auto-generated, do not modify it manually. To generate: +# ``` python -m dev_tools.keyword_extract ``` + +Preservation = RoguePath( + id=1, + name='Preservation', + cn='存护', + cht='存護', + en='Preservation', + jp='存護', +) +Remembrance = RoguePath( + id=2, + name='Remembrance', + cn='记忆', + cht='記憶', + en='Remembrance', + jp='記憶', +) +Nihility = RoguePath( + id=3, + name='Nihility', + cn='虚无', + cht='虛無', + en='Nihility', + jp='虚無', +) +Abundance = RoguePath( + id=4, + name='Abundance', + cn='丰饶', + cht='豐饒', + en='Abundance', + jp='豊穣', +) +The_Hunt = RoguePath( + id=5, + name='The_Hunt', + cn='巡猎', + cht='巡獵', + en='The Hunt', + jp='巡狩', +) +Destruction = RoguePath( + id=6, + name='Destruction', + cn='毁灭', + cht='毀滅', + en='Destruction', + jp='壊滅', +) +Elation = RoguePath( + id=7, + name='Elation', + cn='欢愉', + cht='歡愉', + en='Elation', + jp='愉悦', +) diff --git a/tasks/rogue/keywords/resonance.py b/tasks/rogue/keywords/resonance.py new file mode 100644 index 000000000..d99f9e667 --- /dev/null +++ b/tasks/rogue/keywords/resonance.py @@ -0,0 +1,285 @@ +from .classes import RogueResonance + +# This file was auto-generated, do not modify it manually. To generate: +# ``` python -m dev_tools.keyword_extract ``` + +Path_Resonance_Preservation = RogueResonance( + id=1, + name='Path_Resonance_Preservation', + cn='命途回响:「存护」', + cht='命途迴響:「存護」', + en='Path Resonance: Preservation', + jp='運命の反響:「存護」', + path_id=1, + rarity=3, +) +Resonance_Formation_Zero_Dimensional_Reinforcement = RogueResonance( + id=2, + name='Resonance_Formation_Zero_Dimensional_Reinforcement', + cn='回响构音:零维强化', + cht='迴響構音:零維強化', + en='Resonance Formation: Zero-Dimensional Reinforcement', + jp='反響構音:析出硬化', + path_id=1, + rarity=3, +) +Resonance_Formation_Eutectic_Reaction = RogueResonance( + id=3, + name='Resonance_Formation_Eutectic_Reaction', + cn='回响构音:共晶反应', + cht='迴響構音:共晶反應', + en='Resonance Formation: Eutectic Reaction', + jp='反響構音:共晶反応', + path_id=1, + rarity=3, +) +Resonance_Formation_Isomorphous_Reaction = RogueResonance( + id=4, + name='Resonance_Formation_Isomorphous_Reaction', + cn='回响构音:均晶转变', + cht='迴響構音:均晶轉變', + en='Resonance Formation: Isomorphous Reaction', + jp='反響構音:全率固溶体', + path_id=1, + rarity=3, +) +Path_Resonance_Remembrance = RogueResonance( + id=5, + name='Path_Resonance_Remembrance', + cn='命途回响:「记忆」', + cht='命途迴響:「記憶」', + en='Path Resonance: Remembrance', + jp='運命の反響:「記憶」', + path_id=2, + rarity=3, +) +Resonance_Formation_Total_Recall = RogueResonance( + id=6, + name='Resonance_Formation_Total_Recall', + cn='回响构音:全面回忆', + cht='迴響構音:全面回憶', + en='Resonance Formation: Total Recall', + jp='反響構音:全面追憶', + path_id=2, + rarity=3, +) +Resonance_Formation_Rich_Experience = RogueResonance( + id=7, + name='Resonance_Formation_Rich_Experience', + cn='回响构音:体验的富翁', + cht='迴響構音:體驗的富翁', + en='Resonance Formation: Rich Experience', + jp='反響構音:体験の富豪', + path_id=2, + rarity=3, +) +Resonance_Formation_First_Love_Once_More = RogueResonance( + id=8, + name='Resonance_Formation_First_Love_Once_More', + cn='回响构音:第二次初恋', + cht='迴響構音:第二次初戀', + en='Resonance Formation: First Love Once More', + jp='反響構音:二度目の初恋', + path_id=2, + rarity=3, +) +Path_Resonance_Nihility = RogueResonance( + id=9, + name='Path_Resonance_Nihility', + cn='命途回响:「虚无」', + cht='命途迴響:「虛無」', + en='Path Resonance: Nihility', + jp='運命の反響:「虚無」', + path_id=3, + rarity=3, +) +Resonance_Formation_The_Doubtful_Fourfold_Root = RogueResonance( + id=10, + name='Resonance_Formation_The_Doubtful_Fourfold_Root', + cn='回响构音:怀疑的四重根', + cht='迴響構音:懷疑的四重根', + en='Resonance Formation: The Doubtful Fourfold Root', + jp='反響構音:疑いの四つの根', + path_id=3, + rarity=3, +) +Resonance_Formation_Suffering_and_Sunshine = RogueResonance( + id=11, + name='Resonance_Formation_Suffering_and_Sunshine', + cn='回响构音:苦难与阳光', + cht='迴響構音:苦難與陽光', + en='Resonance Formation: Suffering and Sunshine', + jp='反響構音:苦難と陽光', + path_id=3, + rarity=3, +) +Resonance_Formation_Outsider = RogueResonance( + id=12, + name='Resonance_Formation_Outsider', + cn='回响构音:局外人', + cht='迴響構音:局外人', + en='Resonance Formation: Outsider', + jp='反響構音:異邦人', + path_id=3, + rarity=3, +) +Path_Resonance_Abundance = RogueResonance( + id=13, + name='Path_Resonance_Abundance', + cn='命途回响:「丰饶」', + cht='命途迴響:「豐饒」', + en='Path Resonance: Abundance', + jp='運命の反響:「豊穣」', + path_id=4, + rarity=3, +) +Resonance_Formation_Terminal_Nirvana = RogueResonance( + id=14, + name='Resonance_Formation_Terminal_Nirvana', + cn='回响构音:无余涅槃', + cht='迴響構音:無餘涅槃', + en='Resonance Formation: Terminal Nirvana', + jp='反響構音:無余涅槃', + path_id=4, + rarity=3, +) +Resonance_Formation_Anicca = RogueResonance( + id=15, + name='Resonance_Formation_Anicca', + cn='回响构音:诸行无常', + cht='迴響構音:諸行無常', + en='Resonance Formation: Anicca', + jp='反響構音:諸行無常', + path_id=4, + rarity=3, +) +Resonance_Formation_Anatta = RogueResonance( + id=16, + name='Resonance_Formation_Anatta', + cn='回响构音:诸法无我', + cht='迴響構音:諸法無我', + en='Resonance Formation: Anatta', + jp='反響構音:諸法無我', + path_id=4, + rarity=3, +) +Path_Resonance_The_Hunt = RogueResonance( + id=17, + name='Path_Resonance_The_Hunt', + cn='命途回响:「巡猎」', + cht='命途迴響:「巡獵」', + en='Path Resonance: The Hunt', + jp='運命の反響:「巡狩」', + path_id=5, + rarity=3, +) +Resonance_Formation_Star_Hunter = RogueResonance( + id=18, + name='Resonance_Formation_Star_Hunter', + cn='回响构音:狩星巡日', + cht='迴響構音:狩星巡日', + en='Resonance Formation: Star Hunter', + jp='反響構音:星を狩りて日を巡る', + path_id=5, + rarity=3, +) +Resonance_Formation_Bow_and_Arrow = RogueResonance( + id=19, + name='Resonance_Formation_Bow_and_Arrow', + cn='回响构音:柘弓危矢', + cht='迴響構音:柘弓危矢', + en='Resonance Formation: Bow and Arrow', + jp='反響構音:柘弓に疾矢', + path_id=5, + rarity=3, +) +Resonance_Formation_Perfect_Aim = RogueResonance( + id=20, + name='Resonance_Formation_Perfect_Aim', + cn='回响构音:射不主皮', + cht='迴響構音:射不主皮', + en='Resonance Formation: Perfect Aim', + jp='反響構音:射は皮を主とせず', + path_id=5, + rarity=3, +) +Path_Resonance_Destruction = RogueResonance( + id=21, + name='Path_Resonance_Destruction', + cn='命途回响:「毁灭」', + cht='命途迴響:「毀滅」', + en='Path Resonance: Destruction', + jp='運命の反響:「壊滅」', + path_id=6, + rarity=3, +) +Resonance_Formation_Cataclysmic_Variable = RogueResonance( + id=22, + name='Resonance_Formation_Cataclysmic_Variable', + cn='回响构音:激变变星', + cht='迴響構音:激變變星', + en='Resonance Formation: Cataclysmic Variable', + jp='反響構音:激変星', + path_id=6, + rarity=3, +) +Resonance_Formation_Extreme_Helium_Flash = RogueResonance( + id=23, + name='Resonance_Formation_Extreme_Helium_Flash', + cn='回响构音:极端氦闪', + cht='迴響構音:極端氦閃', + en='Resonance Formation: Extreme Helium Flash', + jp='反響構音:ヘリウムフラッシュ', + path_id=6, + rarity=3, +) +Resonance_Formation_Event_Horizon = RogueResonance( + id=24, + name='Resonance_Formation_Event_Horizon', + cn='回响构音:事件视界', + cht='迴響構音:事件視界', + en='Resonance Formation: Event Horizon', + jp='反響構音:事象の地平線', + path_id=6, + rarity=3, +) +Path_Resonance_Elation = RogueResonance( + id=25, + name='Path_Resonance_Elation', + cn='命途回响:「欢愉」', + cht='命途迴響:「歡愉」', + en='Path Resonance: Elation', + jp='運命の反響:「愉悦」', + path_id=7, + rarity=3, +) +Resonance_Formation_Doomsday_Carnival = RogueResonance( + id=26, + name='Resonance_Formation_Doomsday_Carnival', + cn='回响构音:末日狂欢', + cht='迴響構音:末日狂歡', + en='Resonance Formation: Doomsday Carnival', + jp='反響構音:終末の狂宴', + path_id=7, + rarity=3, +) +Resonance_Formation_Dance_of_Growth = RogueResonance( + id=27, + name='Resonance_Formation_Dance_of_Growth', + cn='回响构音:树苗长高舞', + cht='迴響構音:樹苗長高舞', + en='Resonance Formation: Dance of Growth', + jp='反響構音:苗木が育つ踊り', + path_id=7, + rarity=3, +) +Resonance_Formation_Instant_Win = RogueResonance( + id=28, + name='Resonance_Formation_Instant_Win', + cn='回响构音:开盖有奖', + cht='迴響構音:開蓋有獎', + en='Resonance Formation: Instant Win', + jp='反響構音:もう1本', + path_id=7, + rarity=3, +) diff --git a/tasks/rogue/preset.py b/tasks/rogue/preset.py new file mode 100644 index 000000000..119c982bb --- /dev/null +++ b/tasks/rogue/preset.py @@ -0,0 +1,10 @@ +BLESSING_PRESET_1 = ("巡猎-3 > 《冠军晚餐•猫的摇篮》 > 丰饶众生,一法界心 > 毁灭-3 " + "> 火堆外的夜 > 巡猎-2 > 戒律性闪变 > 巡猎 > 存护-2 > reset > random") +RESONANCE_PRESET_1 = ("回响构音:均晶转变 > 回响构音:零维强化" + "> 回响构音:第二次初恋 > 回响构音:体验的富翁" + "> 回响构音:局外人 > 回响构音:怀疑的四重根" + "> 回响构音:诸法无我 > 回响构音:诸行无常" + "> 回响构音:射不主皮 > 回响构音:柘弓危矢" + "> 回响构音:激变变星 > 回响构音:极端氦闪" + "> 回响构音:末日狂欢 > 回响构音:树苗长高舞") +CURIO_PRESET_1 = "博士之袍 > 福灵胶 > 分裂金币 > 信仰债券 > 换境桂冠 > 俱乐部券 > 碎星芳饵 > random" diff --git a/tasks/rogue/selector.py b/tasks/rogue/selector.py index e69de29bb..a64b84936 100644 --- a/tasks/rogue/selector.py +++ b/tasks/rogue/selector.py @@ -0,0 +1,67 @@ +import numpy as np + +from module.logger import logger +from module.ocr.keyword import Keyword +from module.ocr.ocr import OcrResultButton +from tasks.rogue.ui import RogueUI + + +class RogueSelector: + """ + An Interface used in blessing, curio, and other ui selection in rogue + """ + + def __init__(self, main: RogueUI): + self.main = main + self.filter_ = None + self.ocr_results = [] + + def recognition(self): + ... + + def ui_select(self, target: OcrResultButton | None, skip_first_screenshot=True): + ... + + def try_select(self, option: OcrResultButton | str): + ... + + def load_filter(self): + ... + + def perform_selection(self, priority): + if not self.ocr_results: + logger.warning('No blessing recognized, randomly choose one') + self.ui_select(None) + return False + + if not len(priority): + logger.info('No blessing project satisfies current filter, randomly choose one') + choose = np.random.choice(self.ocr_results) + self.ui_select(choose) + return False + + for option in priority: + logger.info(f"Try to choose option: {option}") + if self.try_select(option): + return True + else: + logger.info(f"Can not choose option: {option}") + + def recognize_and_select(self): + def match_ocr_result(matched_keyword: Keyword): + for result in self.ocr_results: + if result.matched_keyword == matched_keyword: + return result + return None + + self.recognition() + self.load_filter() + if self.filter_: + keywords = [result.matched_keyword for result in self.ocr_results] + priority = self.filter_.apply(keywords) + priority = [option if isinstance(option, str) else match_ocr_result(option) for option in priority] + else: + logger.warning("No filter loaded, use random instead") + priority = ['random'] + logger.info(f"Priority: {priority}") + self.perform_selection(priority) diff --git a/tasks/rogue/ui.py b/tasks/rogue/ui.py new file mode 100644 index 000000000..1ce790d4c --- /dev/null +++ b/tasks/rogue/ui.py @@ -0,0 +1,58 @@ +import re + +from module.base.utils import area_offset +from module.ocr.ocr import Digit, Ocr, OcrResultButton +from tasks.base.ui import UI +from tasks.rogue.assets.assets_rogue_ui import * +from tasks.rogue.keywords import * + + +class RogueBonusOcr(Ocr): + def after_process(self, result): + result = super().after_process(result) + if self.lang == 'ch': + replace_pattern_dict = { + "[宇宝][宙审]": "宇宙", + } + for pat, replace in replace_pattern_dict.items(): + result = re.sub(pat, replace, result) + return result + + +class RogueUI(UI): + path: RoguePath + + @property + def cosmic_fragment(self): + """ + Return valid result only when template appear + """ + if self.appear(COSMIC_FRAGMENT): + return Digit(OCR_COSMIC_FRAGMENT).ocr_single_line(self.device.image) + return 0 + + def is_page_choose_blessing(self): + return (self.image_color_count(PAGE_CHOOSE_BUFF, (245, 245, 245), count=200) + and self.appear(CHECK_BLESSING)) + + def is_page_choose_curio(self): + return self.appear(PAGE_CHOOSE_CURIO) + + def is_page_choose_bonus(self): + return self.appear(PAGE_CHOOSE_BONUS) + + def is_page_event(self): + return self.appear(PAGE_EVENT) + + def handle_obtain_item_popup(self, interval=5) -> bool: + """After selecting some curio (e.g. Sealing_Wax_of_*), there will be a popup after back to main page""" + if self.appear_then_click(OBTAIN_ITEM_POPUP, interval=interval): + return True + return False + + def is_unrecorded(self, target: OcrResultButton, relative_area): + """ + To check a rogue keyword is not record in game index by finding template + """ + FLAG_UNRECORD.matched_button.search = area_offset(relative_area, target.area[:2]) + return self.appear(FLAG_UNRECORD) diff --git a/tasks/rogue/utils.py b/tasks/rogue/utils.py new file mode 100644 index 000000000..c8cf5ff9f --- /dev/null +++ b/tasks/rogue/utils.py @@ -0,0 +1,33 @@ +import re + +from module.base.base import ModuleBase +from module.base.utils import area_offset +from module.ocr.ocr import OcrResultButton + +REGEX_PUNCTUATION = re.compile(r'[ ,.\'"“”,。::!!??·•—/()()「」『』【】《》]') + + +def parse_name(n): + n = REGEX_PUNCTUATION.sub('', str(n)).lower() + return n + + +def get_regex_from_keyword_name(keyword, attr_name): + string = "" + for instance in keyword.instances.values(): + if hasattr(instance, attr_name): + for name in instance.__getattribute__(attr_name): + string += f"{name}|" + # some pattern contain each other, make sure each pattern end with "-" or the end of string + return f"(?:({string[:-1]})(?:-|$))?" + + +def is_card_selected(main: ModuleBase, target: OcrResultButton, confirm_button): + """ + There is a white border if a blessing is selected. + For the enforce case, just check the confirm button turning to white + """ + if not target: + return main.image_color_count(confirm_button, (230, 230, 230)) + top_border = area_offset(target.area, (0, -180)) + return main.image_color_count(top_border, (255, 255, 255))