diff --git a/assets/share/combat/interact/DUNGEON_COMBAT_INTERACT_TEXT.png b/assets/share/combat/interact/DUNGEON_COMBAT_INTERACT_TEXT.png new file mode 100644 index 000000000..48aed9ba0 Binary files /dev/null and b/assets/share/combat/interact/DUNGEON_COMBAT_INTERACT_TEXT.png differ diff --git a/tasks/combat/assets/assets_combat_interact.py b/tasks/combat/assets/assets_combat_interact.py index 5b575ed58..790440071 100644 --- a/tasks/combat/assets/assets_combat_interact.py +++ b/tasks/combat/assets/assets_combat_interact.py @@ -13,6 +13,16 @@ DUNGEON_COMBAT_INTERACT = ButtonWrapper( button=(750, 411, 997, 448), ), ) +DUNGEON_COMBAT_INTERACT_TEXT = ButtonWrapper( + name='DUNGEON_COMBAT_INTERACT_TEXT', + share=Button( + file='./assets/share/combat/interact/DUNGEON_COMBAT_INTERACT_TEXT.png', + area=(790, 391, 1055, 456), + search=(770, 371, 1075, 476), + color=(47, 51, 53), + button=(790, 391, 1055, 456), + ), +) MAP_LOADING = ButtonWrapper( name='MAP_LOADING', share=Button( diff --git a/tasks/combat/combat.py b/tasks/combat/combat.py index d26d29bb4..3412b2068 100644 --- a/tasks/combat/combat.py +++ b/tasks/combat/combat.py @@ -65,27 +65,6 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSuppo return False - def combat_enter_from_map(self, skip_first_screenshot=True): - """ - Pages: - in: page_main, DUNGEON_COMBAT_INTERACT - out: COMBAT_PREPARE - """ - logger.info('Combat enter from map') - while 1: - if skip_first_screenshot: - skip_first_screenshot = False - else: - self.device.screenshot() - - if self.appear(COMBAT_PREPARE): - # Confirm page loaded - if self.image_color_count(COMBAT_PREPARE.button, color=(230, 230, 230), threshold=240, count=400): - logger.info(f'At {COMBAT_PREPARE}') - break - if self.handle_combat_interact(): - continue - def combat_prepare(self, team=1, support_character: str = None): """ Args: diff --git a/tasks/combat/interact.py b/tasks/combat/interact.py index 5d60b3c1c..110160077 100644 --- a/tasks/combat/interact.py +++ b/tasks/combat/interact.py @@ -1,6 +1,8 @@ from module.base.utils import color_similar, get_color +from module.logger import logger from tasks.base.ui import UI from tasks.combat.assets.assets_combat_interact import DUNGEON_COMBAT_INTERACT, MAP_LOADING +from tasks.combat.assets.assets_combat_prepare import COMBAT_PREPARE from tasks.map.assets.assets_map_control import A_BUTTON @@ -15,6 +17,27 @@ class CombatInteract(UI): return False + def combat_enter_from_map(self, skip_first_screenshot=True): + """ + Pages: + in: page_main, DUNGEON_COMBAT_INTERACT + out: COMBAT_PREPARE + """ + logger.info('Combat enter from map') + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + if self.appear(COMBAT_PREPARE): + # Confirm page loaded + if self.image_color_count(COMBAT_PREPARE.button, color=(230, 230, 230), threshold=240, count=400): + logger.info(f'At {COMBAT_PREPARE}') + break + if self.handle_combat_interact(): + continue + def is_map_loading(self): if self.appear(MAP_LOADING, similarity=0.75): return True diff --git a/tasks/dungeon/dungeon.py b/tasks/dungeon/dungeon.py index dfde2b113..c57c7ef89 100644 --- a/tasks/dungeon/dungeon.py +++ b/tasks/dungeon/dungeon.py @@ -45,8 +45,12 @@ class Dungeon(DungeonUI, DungeonEvent, Combat): logger.info(f'Dungeon: {dungeon}, team={team}, wave_limit={wave_limit}, support_character={support_character}') if not skip_ui_switch: - self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index) - self.dungeon_goto(dungeon) + interact = self.get_dungeon_interact() + if interact is not None and interact == dungeon: + logger.info('Already nearby dungeon') + else: + self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index) + self.dungeon_goto(dungeon) if dungeon == KEYWORDS_DUNGEON_LIST.Stagnant_Shadow_Blaze: if self.handle_destructible_around_blaze(): diff --git a/tasks/dungeon/ui.py b/tasks/dungeon/ui.py index 9809d3449..587878e79 100644 --- a/tasks/dungeon/ui.py +++ b/tasks/dungeon/ui.py @@ -6,19 +6,23 @@ from module.base.base import ModuleBase from module.base.button import ClickButton from module.base.timer import Timer from module.base.utils import get_color +from module.exception import ScriptError from module.logger import logger from module.ocr.ocr import Ocr, OcrResultButton from module.ocr.utils import split_and_pair_button_attr from module.ui.draggable_list import DraggableList from module.ui.switch import Switch from tasks.base.page import page_guide +from tasks.combat.assets.assets_combat_interact import DUNGEON_COMBAT_INTERACT, DUNGEON_COMBAT_INTERACT_TEXT from tasks.combat.assets.assets_combat_prepare import COMBAT_PREPARE +from tasks.combat.interact import CombatInteract from tasks.dungeon.assets.assets_dungeon_ui import * from tasks.dungeon.keywords import ( DungeonList, DungeonNav, DungeonTab, KEYWORDS_DUNGEON_ENTRANCE, + KEYWORDS_DUNGEON_LIST, KEYWORDS_DUNGEON_NAV, KEYWORDS_DUNGEON_TAB ) @@ -116,7 +120,7 @@ DUNGEON_LIST = DraggableDungeonList( ocr_class=OcrDungeonList, search_button=OCR_DUNGEON_LIST) -class DungeonUI(DungeonState): +class DungeonUI(DungeonState, CombatInteract): def dungeon_tab_goto(self, state: DungeonTab): """ Args: @@ -372,6 +376,53 @@ class DungeonUI(DungeonState): logger.warning(f'Cannot find dungeon entrance of {dungeon}') continue + def get_dungeon_interact(self) -> DungeonList | None: + """ + Pages: + in: page_main + """ + if not self.appear(DUNGEON_COMBAT_INTERACT): + logger.info('No dungeon interact') + return None + + ocr = OcrDungeonList(DUNGEON_COMBAT_INTERACT_TEXT) + result = ocr.detect_and_ocr(self.device.image) + + result = ' '.join([row.ocr_text for row in result]) + + # Calyx (Crimson): Bud of XXX -> Bud of XXX + result = re.sub(r'Calyx\s*\(.*?\):*', '', result) + # Stagnant Shadow: Shap XXX -> Shape of XXX + result = re.sub(r'Stagnant\s*Shadow[:\s]*\w*', 'Shape of', result) + # Cavern of Corrosion: Pa XXX -> Path of XXX + result = re.sub(r'Cavern\s*of\s*Corrosion[:\s]*\w*', 'Path of', result) + # Echo of War: XXX -> XXX + result = re.sub(r'Echo\s*of\s*War:*', '', result) + # Divine See -> Divine Seed + result = re.sub(r'Divine\s*\w*', 'Divine Seed', result) + # Destructio Beginning -> Destruction's Beginning + result = re.sub(r"Destruct[a-zA-Z0-9_']*", "Destruction's", result) + + # Dungeons + try: + dungeon = DungeonList.find(result) + logger.attr('DungeonInteract', dungeon) + return dungeon + except ScriptError: + pass + # Simulated Universe returns Simulated_Universe_World_1 + try: + dungeon = DungeonNav.find(result) + if dungeon == KEYWORDS_DUNGEON_NAV.Simulated_Universe: + dungeon = KEYWORDS_DUNGEON_LIST.Simulated_Universe_World_1 + logger.attr('DungeonInteract', dungeon) + return dungeon + except ScriptError: + pass + # Unknown + logger.attr('DungeonInteract', None) + return None + def dungeon_goto(self, dungeon: DungeonList): """ Returns: