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/ui/CHECK_BLESSING.png b/assets/share/rogue/ui/CHECK_BLESSING.png new file mode 100644 index 000000000..71adc7077 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/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/config/template.json b/config/template.json index 020892a60..48ddb2e39 100644 --- a/config/template.json +++ b/config/template.json @@ -90,7 +90,9 @@ "PresetResonanceFilter": "preset-1", "CustomResonanceFilter": "回响构音:均晶转变 > 回响构音:零维强化\n> 回响构音:第二次初恋 > 回响构音:体验的富翁\n> 回响构音:局外人 > 回响构音:怀疑的四重根\n> 回响构音:诸法无我 > 回响构音:诸行无常\n> 回响构音:射不主皮 > 回响构音:柘弓危矢\n> 回响构音:激变变星 > 回响构音:极端氦闪\n> 回响构音:末日狂欢 > 回响构音:树苗长高舞", "PresetBlessingFilter": "preset-1", - "CustomBlessingFilter": "巡猎-3 > 《冠军晚餐·猫的摇篮》 > 丰饶众生,一法界心 > 毁灭-3 \n> 火堆外的夜 > 巡猎-2 > 毁灭-2 > 巡猎 > reset > random" + "CustomBlessingFilter": "巡猎-3 > 《冠军晚餐·猫的摇篮》 > 丰饶众生,一法界心 > 毁灭-3 \n> 火堆外的夜 > 巡猎-2 > 毁灭-2 > 巡猎 > reset > random", + "PresetCurioFilter": "preset-1", + "CustomCurioFilter": "博士之袍 > 福灵胶 > 巡猎火漆 > 分裂金币 > 信仰债券 > random" } } } \ No newline at end of file diff --git a/module/base/filter.py b/module/base/filter.py index ed0918f28..9d029384a 100644 --- a/module/base/filter.py +++ b/module/base/filter.py @@ -125,13 +125,11 @@ class MultiLangFilter(Filter): continue obj_value = obj.__getattribute__(attr) - if isinstance(obj_value, str): - if obj_value.lower() != str(value): + if isinstance(obj_value, (str, int)): + if str(obj_value).lower() != str(value): return False if isinstance(obj_value, list): - for candidate in obj_value: - if str(candidate) == str(value): - return True - return False + 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 00f1bf30c..652cbb424 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -492,6 +492,18 @@ "CustomBlessingFilter": { "type": "textarea", "value": "巡猎-3 > 《冠军晚餐·猫的摇篮》 > 丰饶众生,一法界心 > 毁灭-3 \n> 火堆外的夜 > 巡猎-2 > 毁灭-2 > 巡猎 > reset > random" + }, + "PresetCurioFilter": { + "type": "select", + "value": "preset-1", + "option": [ + "preset-1", + "custom" + ] + }, + "CustomCurioFilter": { + "type": "textarea", + "value": "博士之袍 > 福灵胶 > 巡猎火漆 > 分裂金币 > 信仰债券 > random" } } } diff --git a/module/config/argument/argument.yaml b/module/config/argument/argument.yaml index cacdb6523..c368d5538 100644 --- a/module/config/argument/argument.yaml +++ b/module/config/argument/argument.yaml @@ -130,3 +130,8 @@ Rogue: CustomBlessingFilter: |- 巡猎-3 > 《冠军晚餐·猫的摇篮》 > 丰饶众生,一法界心 > 毁灭-3 > 火堆外的夜 > 巡猎-2 > 毁灭-2 > 巡猎 > reset > random + PresetCurioFilter: + value: preset-1 + option: [ preset-1, custom ] + CustomCurioFilter: |- + 博士之袍 > 福灵胶 > 分裂金币 > 信仰债券 > 换境桂冠 > 俱乐部券 > 碎星芳饵 > random diff --git a/module/config/config_generated.py b/module/config/config_generated.py index 895668b75..b49fc8248 100644 --- a/module/config/config_generated.py +++ b/module/config/config_generated.py @@ -58,3 +58,5 @@ class GeneratedConfig: Rogue_CustomResonanceFilter = '回响构音:均晶转变 > 回响构音:零维强化\n> 回响构音:第二次初恋 > 回响构音:体验的富翁\n> 回响构音:局外人 > 回响构音:怀疑的四重根\n> 回响构音:诸法无我 > 回响构音:诸行无常\n> 回响构音:射不主皮 > 回响构音:柘弓危矢\n> 回响构音:激变变星 > 回响构音:极端氦闪\n> 回响构音:末日狂欢 > 回响构音:树苗长高舞' Rogue_PresetBlessingFilter = 'preset-1' # preset-1, custom Rogue_CustomBlessingFilter = '巡猎-3 > 《冠军晚餐·猫的摇篮》 > 丰饶众生,一法界心 > 毁灭-3 \n> 火堆外的夜 > 巡猎-2 > 毁灭-2 > 巡猎 > reset > random' + Rogue_PresetCurioFilter = 'preset-1' # preset-1, custom + Rogue_CustomCurioFilter = '博士之袍 > 福灵胶 > 巡猎火漆 > 分裂金币 > 信仰债券 > random' diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index 0b6541eba..ebafa2ae1 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -402,6 +402,16 @@ "CustomBlessingFilter": { "name": "Rogue.CustomBlessingFilter.name", "help": "Rogue.CustomBlessingFilter.help" + }, + "PresetCurioFilter": { + "name": "Rogue.PresetCurioFilter.name", + "help": "Rogue.PresetCurioFilter.help", + "preset-1": "preset-1", + "custom": "custom" + }, + "CustomCurioFilter": { + "name": "Rogue.CustomCurioFilter.name", + "help": "Rogue.CustomCurioFilter.help" } }, "Gui": { diff --git a/module/config/i18n/ja-JP.json b/module/config/i18n/ja-JP.json index 12c674571..9ae8f98a1 100644 --- a/module/config/i18n/ja-JP.json +++ b/module/config/i18n/ja-JP.json @@ -402,6 +402,16 @@ "CustomBlessingFilter": { "name": "Rogue.CustomBlessingFilter.name", "help": "Rogue.CustomBlessingFilter.help" + }, + "PresetCurioFilter": { + "name": "Rogue.PresetCurioFilter.name", + "help": "Rogue.PresetCurioFilter.help", + "preset-1": "preset-1", + "custom": "custom" + }, + "CustomCurioFilter": { + "name": "Rogue.CustomCurioFilter.name", + "help": "Rogue.CustomCurioFilter.help" } }, "Gui": { diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index 6fc2f823a..b5b2752f3 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -402,6 +402,16 @@ "CustomBlessingFilter": { "name": "自定义祝福过滤器", "help": "" + }, + "PresetCurioFilter": { + "name": "奇物过滤器", + "help": "", + "preset-1": "预设 1", + "custom": "自定义" + }, + "CustomCurioFilter": { + "name": "自定义祝福过滤器", + "help": "" } }, "Gui": { diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index 32fb043e3..bce6ded9c 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -402,6 +402,16 @@ "CustomBlessingFilter": { "name": "Rogue.CustomBlessingFilter.name", "help": "Rogue.CustomBlessingFilter.help" + }, + "PresetCurioFilter": { + "name": "Rogue.PresetCurioFilter.name", + "help": "Rogue.PresetCurioFilter.help", + "preset-1": "preset-1", + "custom": "custom" + }, + "CustomCurioFilter": { + "name": "Rogue.CustomCurioFilter.name", + "help": "Rogue.CustomCurioFilter.help" } }, "Gui": { diff --git a/tasks/rogue/assets/assets_rogue_blessing.py b/tasks/rogue/assets/assets_rogue_blessing.py index b37ad8522..ab91cda7a 100644 --- a/tasks/rogue/assets/assets_rogue_blessing.py +++ b/tasks/rogue/assets/assets_rogue_blessing.py @@ -43,6 +43,16 @@ BOTTOM_WHITE_BAR = ButtonWrapper( 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( diff --git a/tasks/rogue/assets/assets_rogue_ui.py b/tasks/rogue/assets/assets_rogue_ui.py index 6c80f1fa9..1973de44d 100644 --- a/tasks/rogue/assets/assets_rogue_ui.py +++ b/tasks/rogue/assets/assets_rogue_ui.py @@ -3,6 +3,16 @@ 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 ``` +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=(50, 48, 43), + button=(946, 17, 986, 57), + ), +) CONFIRM = ButtonWrapper( name='CONFIRM', share=Button( @@ -13,6 +23,26 @@ CONFIRM = ButtonWrapper( button=(960, 629, 1233, 677), ), ) +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), + ), +) +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_BUFF = ButtonWrapper( name='PAGE_CHOOSE_BUFF', share=Button( diff --git a/tasks/rogue/blessing.py b/tasks/rogue/blessing.py index 45bcd0392..0a5286b7d 100644 --- a/tasks/rogue/blessing.py +++ b/tasks/rogue/blessing.py @@ -6,12 +6,12 @@ from module.base.filter import MultiLangFilter from module.base.timer import Timer from module.base.utils import area_offset, get_color from module.logger import logger -from module.ocr.keyword import Keyword -from module.ocr.ocr import Ocr, OcrResultButton, DigitCounter +from module.ocr.ocr import Ocr, OcrResultButton, DigitCounter, Digit from tasks.rogue.assets.assets_rogue_blessing import * from tasks.rogue.assets.assets_rogue_ui import CONFIRM from tasks.rogue.keywords import * from tasks.rogue.preset import * +from tasks.rogue.selector import RogueSelector from tasks.rogue.ui import RogueUI from tasks.rogue.utils import get_regex_from_keyword_name, parse_name @@ -54,46 +54,61 @@ class RogueBuffOcr(Ocr): "柘弓危失": "柘弓危矢", "飞虹凿齿": "飞虹诛凿齿", "天培步危": "天棓步危", - "云[摘销]": "云镝", + "云[摘销]?逐步离": "云镝逐步离", "制桑": "制穹桑", "乌号基": "乌号綦", "追摩物": "追孽物", "特月": "狩月", - "彤弓素增": "彤弓素矰", + "彤弓素增?": "彤弓素矰", "白决射御": "白矢决射御", "苦表": "苦衷", "[沦沧]肌髓": "沦浃肌髓", "进发": "迸发", "永缩体": "永坍缩体", "完美体验:绒默": "完美体验:缄默", - r"\w*灾$": "禳灾", + "灭回归不等式": "湮灭回归不等式", + r".*灾$": "禳灾", + "虚安供品": "虚妄供品", + "原初的苦$": "原初的苦衷", + "厌离邪苦": "厌离邪秽苦", + r".*繁.*": "葳蕤繁祉,延彼遐龄" } - for pattern, replace in replace_pattern_dict.items(): - result = re.sub(pattern, replace, result) + for pat, replace in replace_pattern_dict.items(): + result = re.sub(pat, replace, result) return result -class RogueBlessingUI(RogueUI): +class RogueBlessingSelector(RogueUI, 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.image_color_count(BOTTOM_WHITE_BAR.area, color=(255, 255, 255), count=5000): + return 0 color = get_color(self.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 buffs_recognition(self): - self.wait_until_blessing_loaded() + def recognition(self): + self.ocr_results = [] + self._wait_until_blessing_loaded() ocr = RogueBuffOcr(OCR_ROGUE_BUFF) results = ocr.matched_ocr(self.device.image, [RogueBlessing, RogueResonance]) 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.") - self.blessings = results + self.ocr_results = results return results - def ui_select_blessing(self, blessing: OcrResultButton | None, skip_first_screenshot=True, enforce=False): + 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 @@ -104,7 +119,7 @@ class RogueBlessingUI(RogueUI): """ There is a white border if a blessing is selected. """ - top_border = area_offset(blessing.area, (0, -180)) + top_border = area_offset(target.area, (0, -180)) return self.image_color_count(top_border, (255, 255, 255)) def is_select_blessing_complete(): @@ -117,7 +132,9 @@ class RogueBlessingUI(RogueUI): or (self.is_page_choose_blessing() and not is_blessing_selected())) interval = Timer(1) - if not blessing: + enforce = False + + if not target: enforce = True # start -> selected @@ -131,13 +148,13 @@ class RogueBlessingUI(RogueUI): if enforce: logger.info("Buff selected (enforce)") else: - logger.info(f"Buff {blessing} selected") + logger.info(f"Buff {target} selected") break if interval.reached(): if enforce: self.device.click(BLESSING_ENFORCE) else: - self.device.click(blessing) + self.device.click(target) interval.reset() skip_first_screenshot = True @@ -154,11 +171,11 @@ class RogueBlessingUI(RogueUI): self.device.click(CONFIRM) interval.reset() - def get_reset_count(self): + def _get_reset_count(self): current, _, _ = DigitCounter(OCR_RESET_COUNT).ocr_single_line(self.device.image) return current - def wait_until_blessing_loaded(self, timer=Timer(0.3, count=1), timeout=Timer(5, count=10)): + 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() @@ -182,8 +199,14 @@ class RogueBlessingUI(RogueUI): if not self.is_page_choose_blessing(): return False - reset_count = self.get_reset_count() + 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.device.image) + if reset_cost > self.cosmic_fragment: + logger.info("Does not have enough cosmic fragment") return False interval = Timer(1) @@ -193,7 +216,7 @@ class RogueBlessingUI(RogueUI): else: self.device.screenshot() - new_count = self.get_reset_count() + new_count = self._get_reset_count() if reset_count - new_count == 1: logger.info("Reset once") @@ -203,19 +226,9 @@ class RogueBlessingUI(RogueUI): interval.reset() return True - -class RogueBlessingSelector(RogueBlessingUI): - """ - Usage: - self = RogueBlessingSelector('alas') - self.device.screenshot() - self.buff_recognition() - self.select_blessing(self.) - """ - - def load_filter(self) -> MultiLangFilter: + def load_filter(self): filter_ = None - keyword = self.blessings[0].matched_keyword + keyword = self.ocr_results[0].matched_keyword if isinstance(keyword, RogueBlessing): filter_ = BLESSING_FILTER if self.config.Rogue_PresetBlessingFilter == 'preset-1': @@ -223,61 +236,25 @@ class RogueBlessingSelector(RogueBlessingUI): if self.config.Rogue_PresetBlessingFilter == 'custom': filter_.load(parse_name(self.config.Rogue_CustomBlessingFilter)) if isinstance(keyword, RogueResonance): - if len(self.blessings) == 1: - return filter_ filter_ = RESONANCE_FILTER if self.config.Rogue_PresetResonanceFilter == 'preset-1': RESONANCE_FILTER.load(parse_name(RESONANCE_PRESET_1)) if self.config.Rogue_PresetResonanceFilter == 'custom': RESONANCE_FILTER.load(parse_name(self.config.Rogue_CustomResonanceFilter)) - return filter_ + self.filter_ = filter_ - def apply_filter(self): - def match_ocr_result(matched_keyword: Keyword): - for blessing in self.blessings: - if blessing.matched_keyword == matched_keyword: - return blessing - return None + 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 not self.blessings: - return [] - - filter_ = self.load_filter() - if not filter_: - return self.blessings - blessing_keywords = [blessing.matched_keyword for blessing in self.blessings] - priority = filter_.apply(blessing_keywords) - priority = [option if isinstance(option, str) else match_ocr_result(option) for option in priority] - return priority - - def select_blessing(self, priority: list): - if not self.blessings: - logger.info('No blessing recognized, randomly choose one') - self.ui_select_blessing(None, enforce=True) - return - - if not len(priority): - logger.info('No blessing project satisfies current filter, randomly choose one') - choose = np.random.choice(self.blessings) - self.ui_select_blessing(choose) - return - - for option in priority: - # preset - if isinstance(option, str): - if option.lower() == 'reset': - if self.reset_blessing_list(): - self.wait_until_blessing_loaded() - self.buffs_recognition() - self.select_blessing(self.apply_filter()) - return - else: - continue - if option.lower() == 'random': - choose = np.random.choice(self.blessings) - self.ui_select_blessing(choose) - return - - if isinstance(option, OcrResultButton): - self.ui_select_blessing(option) - return + if isinstance(option, OcrResultButton): + self.ui_select(option) + return True + return False diff --git a/tasks/rogue/curio.py b/tasks/rogue/curio.py index 15b9f10d3..92afe5b77 100644 --- a/tasks/rogue/curio.py +++ b/tasks/rogue/curio.py @@ -2,6 +2,7 @@ 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 @@ -9,7 +10,18 @@ from module.ocr.ocr import Ocr, OcrResultButton from tasks.rogue.assets.assets_rogue_curio import * from tasks.rogue.assets.assets_rogue_ui import CONFIRM from tasks.rogue.keywords import RogueCurio +from tasks.rogue.preset import CURIO_PRESET_1 +from tasks.rogue.selector import RogueSelector from tasks.rogue.ui import RogueUI +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',) +FILTER_REGEX = re.compile(pattern) +CURIO_FILTER = MultiLangFilter(FILTER_REGEX, CURIO_FILTER_ATTR, CURIO_FILTER_PRESET) class RogueCurioOcr(Ocr): @@ -17,27 +29,28 @@ class RogueCurioOcr(Ocr): 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 RogueCurioUI(RogueUI): - def curio_recognition(self): +class RogueCurioSelector(RogueUI, RogueSelector): + def recognition(self): + self.ocr_results = [] ocr = RogueCurioOcr(OCR_ROGUE_CURIO) results = ocr.matched_ocr(self.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_curios = results + self.ocr_results = results return results - def ui_select_curio(self, curio: OcrResultButton | None, skip_first_screenshot=True, enforce=True): + def ui_select(self, target: OcrResultButton | None, skip_first_screenshot=True): def is_curio_selected(): - return np.mean(get_color(self.device.image, tuple(curio.area))) > 70 + return np.mean(get_color(self.device.image, tuple(target.area))) > 70 # shiny background def is_select_curio_complete(): """ @@ -45,6 +58,9 @@ class RogueCurioUI(RogueUI): """ return self.is_in_main() + enforce = False + if not target: + enforce = True interval = Timer(1) # start -> selected while 1: @@ -57,13 +73,13 @@ class RogueCurioUI(RogueUI): if enforce: logger.info("Curio selected (enforce)") else: - logger.info(f"Curio {curio} selected") + logger.info(f"Curio {target} selected") break if interval.reached(): if enforce: self.device.click(CURIO_ENFORCE) else: - self.device.click(curio) + self.device.click(target) interval.reset() skip_first_screenshot = True @@ -79,3 +95,21 @@ class RogueCurioUI(RogueUI): if interval.reached(): self.device.click(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 isinstance(option, OcrResultButton): + self.ui_select(option) + return True + return False + + def load_filter(self): + filter_ = CURIO_FILTER + if self.config.Rogue_PresetCurioFilter == 'preset-1': + filter_.load(parse_name(CURIO_PRESET_1)) + if self.config.Rogue_PresetCurioFilter == 'custom': + filter_.load(parse_name(self.config.Rogue_CustomCurioFilter)) + self.filter_ = filter_ diff --git a/tasks/rogue/keywords/classes.py b/tasks/rogue/keywords/classes.py index c9baedf11..171a7cf8e 100644 --- a/tasks/rogue/keywords/classes.py +++ b/tasks/rogue/keywords/classes.py @@ -47,3 +47,8 @@ class RogueResonance(Keyword): @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")] diff --git a/tasks/rogue/preset.py b/tasks/rogue/preset.py index 2c14534a3..119c982bb 100644 --- a/tasks/rogue/preset.py +++ b/tasks/rogue/preset.py @@ -7,3 +7,4 @@ RESONANCE_PRESET_1 = ("回响构音:均晶转变 > 回响构音:零维强化 "> 回响构音:射不主皮 > 回响构音:柘弓危矢" "> 回响构音:激变变星 > 回响构音:极端氦闪" "> 回响构音:末日狂欢 > 回响构音:树苗长高舞") +CURIO_PRESET_1 = "博士之袍 > 福灵胶 > 分裂金币 > 信仰债券 > 换境桂冠 > 俱乐部券 > 碎星芳饵 > random" diff --git a/tasks/rogue/selector.py b/tasks/rogue/selector.py new file mode 100644 index 000000000..ee2103255 --- /dev/null +++ b/tasks/rogue/selector.py @@ -0,0 +1,65 @@ +import numpy as np + +from module.base.filter import MultiLangFilter +from module.logger import logger +from module.ocr.keyword import Keyword +from module.ocr.ocr import OcrResultButton + + +class RogueSelector: + """ + An Interface used in blessing, curio, and other ui selection in rogue + """ + ocr_results: list[OcrResultButton] + filter_: MultiLangFilter + preset_methods: dict[str, callable] + + 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 index 49fddc00f..b2eb6d5c0 100644 --- a/tasks/rogue/ui.py +++ b/tasks/rogue/ui.py @@ -1,3 +1,4 @@ +from module.ocr.ocr import Digit from tasks.base.ui import UI from tasks.rogue.assets.assets_rogue_ui import * from tasks.rogue.keywords import * @@ -5,13 +6,17 @@ from tasks.rogue.keywords import * class RogueUI(UI): path: RoguePath - ocr_blessings: list - ocr_curios: list - claimed_blessings: list - claimed_curios: list + cosmic_fragment_cache: int + + @property + def cosmic_fragment(self): + if self.appear(COSMIC_FRAGMENT): + self.cosmic_fragment_cache = Digit(OCR_COSMIC_FRAGMENT).ocr_single_line(self.device.image) + return self.cosmic_fragment_cache def is_page_choose_blessing(self): - return self.image_color_count(PAGE_CHOOSE_BUFF, (245, 245, 245), count=200) + 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)