diff --git a/assets/share/base/page/BATTLE_PASS_CHECK.png b/assets/share/base/page/BATTLE_PASS_CHECK.png new file mode 100644 index 000000000..db1558b89 Binary files /dev/null and b/assets/share/base/page/BATTLE_PASS_CHECK.png differ diff --git a/assets/share/base/page/CHARACTER_CHECK.png b/assets/share/base/page/CHARACTER_CHECK.png new file mode 100644 index 000000000..4fb4dcd67 Binary files /dev/null and b/assets/share/base/page/CHARACTER_CHECK.png differ diff --git a/assets/share/base/page/CLOSE.png b/assets/share/base/page/CLOSE.png new file mode 100644 index 000000000..3d746bfdd Binary files /dev/null and b/assets/share/base/page/CLOSE.png differ diff --git a/assets/share/base/page/EVENT_CHECK.png b/assets/share/base/page/EVENT_CHECK.png new file mode 100644 index 000000000..050cd75aa Binary files /dev/null and b/assets/share/base/page/EVENT_CHECK.png differ diff --git a/assets/share/base/page/GACHA_CHECK.png b/assets/share/base/page/GACHA_CHECK.png new file mode 100644 index 000000000..52df0b92d Binary files /dev/null and b/assets/share/base/page/GACHA_CHECK.png differ diff --git a/assets/share/base/page/GUIDE_CHECK.png b/assets/share/base/page/GUIDE_CHECK.png new file mode 100644 index 000000000..297de5dc4 Binary files /dev/null and b/assets/share/base/page/GUIDE_CHECK.png differ diff --git a/assets/share/base/page/GUIDE_CLOSE.png b/assets/share/base/page/GUIDE_CLOSE.png new file mode 100644 index 000000000..447128b52 Binary files /dev/null and b/assets/share/base/page/GUIDE_CLOSE.png differ diff --git a/assets/share/base/page/ITEM_CHECK.png b/assets/share/base/page/ITEM_CHECK.png new file mode 100644 index 000000000..5210098a5 Binary files /dev/null and b/assets/share/base/page/ITEM_CHECK.png differ diff --git a/assets/share/base/page/MAIN_GOTO_BATTLE_PASS.png b/assets/share/base/page/MAIN_GOTO_BATTLE_PASS.png new file mode 100644 index 000000000..a90639ed2 Binary files /dev/null and b/assets/share/base/page/MAIN_GOTO_BATTLE_PASS.png differ diff --git a/assets/share/base/page/MAIN_GOTO_CHARACTER.png b/assets/share/base/page/MAIN_GOTO_CHARACTER.png new file mode 100644 index 000000000..db5298cbd Binary files /dev/null and b/assets/share/base/page/MAIN_GOTO_CHARACTER.png differ diff --git a/assets/share/base/page/MAIN_GOTO_EVENT.png b/assets/share/base/page/MAIN_GOTO_EVENT.png new file mode 100644 index 000000000..c3a74ac75 Binary files /dev/null and b/assets/share/base/page/MAIN_GOTO_EVENT.png differ diff --git a/assets/share/base/page/MAIN_GOTO_GACHA.png b/assets/share/base/page/MAIN_GOTO_GACHA.png new file mode 100644 index 000000000..c56530198 Binary files /dev/null and b/assets/share/base/page/MAIN_GOTO_GACHA.png differ diff --git a/assets/share/base/page/MAIN_GOTO_GUIDE.png b/assets/share/base/page/MAIN_GOTO_GUIDE.png new file mode 100644 index 000000000..e2e8342ad Binary files /dev/null and b/assets/share/base/page/MAIN_GOTO_GUIDE.png differ diff --git a/assets/share/base/page/MAIN_GOTO_ITEM.png b/assets/share/base/page/MAIN_GOTO_ITEM.png new file mode 100644 index 000000000..7d57ec423 Binary files /dev/null and b/assets/share/base/page/MAIN_GOTO_ITEM.png differ diff --git a/assets/share/base/page/MAIN_GOTO_MENU.png b/assets/share/base/page/MAIN_GOTO_MENU.png new file mode 100644 index 000000000..3927eb94b Binary files /dev/null and b/assets/share/base/page/MAIN_GOTO_MENU.png differ diff --git a/assets/share/base/page/MAIN_GOTO_MESSAGE.png b/assets/share/base/page/MAIN_GOTO_MESSAGE.png new file mode 100644 index 000000000..6dd66fa7a Binary files /dev/null and b/assets/share/base/page/MAIN_GOTO_MESSAGE.png differ diff --git a/assets/share/base/page/MAIN_GOTO_MISSION.png b/assets/share/base/page/MAIN_GOTO_MISSION.png new file mode 100644 index 000000000..d2bfcec76 Binary files /dev/null and b/assets/share/base/page/MAIN_GOTO_MISSION.png differ diff --git a/assets/share/base/page/MAIN_GOTO_TEAM.png b/assets/share/base/page/MAIN_GOTO_TEAM.png new file mode 100644 index 000000000..7ebf8acba Binary files /dev/null and b/assets/share/base/page/MAIN_GOTO_TEAM.png differ diff --git a/assets/share/base/page/MAIN_GOTO_TUTORIAL.png b/assets/share/base/page/MAIN_GOTO_TUTORIAL.png new file mode 100644 index 000000000..001743f2e Binary files /dev/null and b/assets/share/base/page/MAIN_GOTO_TUTORIAL.png differ diff --git a/assets/share/base/page/MENU_CHECK.png b/assets/share/base/page/MENU_CHECK.png new file mode 100644 index 000000000..eda73bbf8 Binary files /dev/null and b/assets/share/base/page/MENU_CHECK.png differ diff --git a/assets/share/base/page/MESSAGE_CLOSE.png b/assets/share/base/page/MESSAGE_CLOSE.png new file mode 100644 index 000000000..84a60b267 Binary files /dev/null and b/assets/share/base/page/MESSAGE_CLOSE.png differ diff --git a/assets/share/base/page/MISSION_CHECK.png b/assets/share/base/page/MISSION_CHECK.png new file mode 100644 index 000000000..baf65ef8a Binary files /dev/null and b/assets/share/base/page/MISSION_CHECK.png differ diff --git a/assets/share/base/page/TEAM_CHECK.png b/assets/share/base/page/TEAM_CHECK.png new file mode 100644 index 000000000..e4ce29269 Binary files /dev/null and b/assets/share/base/page/TEAM_CHECK.png differ diff --git a/assets/share/base/page/TUTORIAL_CHECK.png b/assets/share/base/page/TUTORIAL_CHECK.png new file mode 100644 index 000000000..bde1e043b Binary files /dev/null and b/assets/share/base/page/TUTORIAL_CHECK.png differ diff --git a/assets/share/base/popup/BATTLE_PASS_NOTIFICATION.BUTTON.png b/assets/share/base/popup/BATTLE_PASS_NOTIFICATION.BUTTON.png new file mode 100644 index 000000000..e686a7b8e Binary files /dev/null and b/assets/share/base/popup/BATTLE_PASS_NOTIFICATION.BUTTON.png differ diff --git a/assets/share/base/popup/BATTLE_PASS_NOTIFICATION.png b/assets/share/base/popup/BATTLE_PASS_NOTIFICATION.png new file mode 100644 index 000000000..2bf9e0002 Binary files /dev/null and b/assets/share/base/popup/BATTLE_PASS_NOTIFICATION.png differ diff --git a/assets/share/base/popup/GET_REWARD.BUTTON.png b/assets/share/base/popup/GET_REWARD.BUTTON.png new file mode 100644 index 000000000..d50e38b37 Binary files /dev/null and b/assets/share/base/popup/GET_REWARD.BUTTON.png differ diff --git a/assets/share/base/popup/GET_REWARD.png b/assets/share/base/popup/GET_REWARD.png new file mode 100644 index 000000000..16e3e514b Binary files /dev/null and b/assets/share/base/popup/GET_REWARD.png differ diff --git a/assets/share/login/LOGIN_CONFIRM.BUTTON.png b/assets/share/login/LOGIN_CONFIRM.BUTTON.png new file mode 100644 index 000000000..e2d6b561c Binary files /dev/null and b/assets/share/login/LOGIN_CONFIRM.BUTTON.png differ diff --git a/assets/share/login/LOGIN_CONFIRM.png b/assets/share/login/LOGIN_CONFIRM.png new file mode 100644 index 000000000..416e49cc7 Binary files /dev/null and b/assets/share/login/LOGIN_CONFIRM.png differ diff --git a/module/base/base.py b/module/base/base.py index 93c3cff86..27fd0653d 100644 --- a/module/base/base.py +++ b/module/base/base.py @@ -44,7 +44,7 @@ class ModuleBase: self.interval_timer = {} - def match_template(self, button, interval=5, similarity=0.85): + def match_template(self, button, interval=0, similarity=0.85): """ Args: button (ButtonWrapper): @@ -74,7 +74,7 @@ class ModuleBase: return appear - def match_color(self, button, interval=5, threshold=10): + def match_color(self, button, interval=0, threshold=10): """ Args: button (ButtonWrapper): @@ -96,7 +96,7 @@ class ModuleBase: return appear - def match_template_color(self, button, interval=5, similarity=0.85, threshold=30): + def match_template_color(self, button, interval=0, similarity=0.85, threshold=30): """ Args: button (ButtonWrapper): diff --git a/tasks/base/assets/assets_base_page.py b/tasks/base/assets/assets_base_page.py new file mode 100644 index 000000000..70e8d1952 --- /dev/null +++ b/tasks/base/assets/assets_base_page.py @@ -0,0 +1,245 @@ +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 ``` + +BATTLE_PASS_CHECK = ButtonWrapper( + name='BATTLE_PASS_CHECK', + share=Button( + file='./assets/share/base/page/BATTLE_PASS_CHECK.png', + area=(42, 22, 72, 55), + search=(22, 2, 92, 75), + color=(159, 142, 108), + button=(42, 22, 72, 55), + ), +) +CHARACTER_CHECK = ButtonWrapper( + name='CHARACTER_CHECK', + share=Button( + file='./assets/share/base/page/CHARACTER_CHECK.png', + area=(41, 18, 73, 47), + search=(21, 0, 93, 67), + color=(172, 153, 119), + button=(41, 18, 73, 47), + ), +) +CLOSE = ButtonWrapper( + name='CLOSE', + share=Button( + file='./assets/share/base/page/CLOSE.png', + area=(1222, 25, 1252, 55), + search=(1202, 5, 1272, 75), + color=(53, 54, 54), + button=(1222, 25, 1252, 55), + ), +) +EVENT_CHECK = ButtonWrapper( + name='EVENT_CHECK', + share=Button( + file='./assets/share/base/page/EVENT_CHECK.png', + area=(39, 19, 75, 56), + search=(19, 0, 95, 76), + color=(133, 125, 103), + button=(39, 19, 75, 56), + ), +) +GACHA_CHECK = ButtonWrapper( + name='GACHA_CHECK', + share=Button( + file='./assets/share/base/page/GACHA_CHECK.png', + area=(40, 20, 74, 54), + search=(20, 0, 94, 74), + color=(157, 139, 112), + button=(40, 20, 74, 54), + ), +) +GUIDE_CHECK = ButtonWrapper( + name='GUIDE_CHECK', + share=Button( + file='./assets/share/base/page/GUIDE_CHECK.png', + area=(44, 216, 63, 248), + search=(24, 196, 83, 268), + color=(196, 198, 200), + button=(44, 216, 63, 248), + ), +) +GUIDE_CLOSE = ButtonWrapper( + name='GUIDE_CLOSE', + share=Button( + file='./assets/share/base/page/GUIDE_CLOSE.png', + area=(1153, 56, 1183, 87), + search=(1133, 36, 1203, 107), + color=(79, 79, 79), + button=(1153, 56, 1183, 87), + ), +) +ITEM_CHECK = ButtonWrapper( + name='ITEM_CHECK', + share=Button( + file='./assets/share/base/page/ITEM_CHECK.png', + area=(43, 23, 72, 54), + search=(23, 3, 92, 74), + color=(188, 169, 129), + button=(43, 23, 72, 54), + ), +) +MAIN_GOTO_BATTLE_PASS = ButtonWrapper( + name='MAIN_GOTO_BATTLE_PASS', + share=Button( + file='./assets/share/base/page/MAIN_GOTO_BATTLE_PASS.png', + area=(860, 36, 889, 56), + search=(840, 16, 909, 76), + color=(165, 164, 162), + button=(860, 36, 889, 56), + ), +) +MAIN_GOTO_CHARACTER = ButtonWrapper( + name='MAIN_GOTO_CHARACTER', + share=Button( + file='./assets/share/base/page/MAIN_GOTO_CHARACTER.png', + area=(1204, 25, 1234, 51), + search=(1184, 5, 1254, 71), + color=(184, 185, 187), + button=(1204, 25, 1234, 51), + ), +) +MAIN_GOTO_EVENT = ButtonWrapper( + name='MAIN_GOTO_EVENT', + share=Button( + file='./assets/share/base/page/MAIN_GOTO_EVENT.png', + area=(786, 33, 814, 56), + search=(766, 13, 834, 76), + color=(185, 184, 183), + button=(786, 33, 814, 56), + ), +) +MAIN_GOTO_GACHA = ButtonWrapper( + name='MAIN_GOTO_GACHA', + share=Button( + file='./assets/share/base/page/MAIN_GOTO_GACHA.png', + area=(929, 38, 957, 59), + search=(909, 18, 977, 79), + color=(161, 162, 163), + button=(929, 38, 957, 59), + ), +) +MAIN_GOTO_GUIDE = ButtonWrapper( + name='MAIN_GOTO_GUIDE', + share=Button( + file='./assets/share/base/page/MAIN_GOTO_GUIDE.png', + area=(997, 34, 1027, 59), + search=(977, 14, 1047, 79), + color=(170, 171, 173), + button=(997, 34, 1027, 59), + ), +) +MAIN_GOTO_ITEM = ButtonWrapper( + name='MAIN_GOTO_ITEM', + share=Button( + file='./assets/share/base/page/MAIN_GOTO_ITEM.png', + area=(1064, 35, 1098, 59), + search=(1044, 15, 1118, 79), + color=(179, 180, 182), + button=(1064, 35, 1098, 59), + ), +) +MAIN_GOTO_MENU = ButtonWrapper( + name='MAIN_GOTO_MENU', + share=Button( + file='./assets/share/base/page/MAIN_GOTO_MENU.png', + area=(22, 60, 51, 81), + search=(2, 40, 71, 101), + color=(176, 177, 179), + button=(22, 60, 51, 81), + ), +) +MAIN_GOTO_MESSAGE = ButtonWrapper( + name='MAIN_GOTO_MESSAGE', + share=Button( + file='./assets/share/base/page/MAIN_GOTO_MESSAGE.png', + area=(184, 182, 203, 212), + search=(164, 162, 223, 232), + color=(230, 230, 230), + button=(184, 182, 203, 212), + ), +) +MAIN_GOTO_MISSION = ButtonWrapper( + name='MAIN_GOTO_MISSION', + share=Button( + file='./assets/share/base/page/MAIN_GOTO_MISSION.png', + area=(21, 199, 51, 221), + search=(1, 179, 71, 241), + color=(168, 169, 172), + button=(21, 199, 51, 221), + ), +) +MAIN_GOTO_TEAM = ButtonWrapper( + name='MAIN_GOTO_TEAM', + share=Button( + file='./assets/share/base/page/MAIN_GOTO_TEAM.png', + area=(1135, 41, 1166, 58), + search=(1115, 21, 1186, 78), + color=(160, 161, 164), + button=(1135, 41, 1166, 58), + ), +) +MAIN_GOTO_TUTORIAL = ButtonWrapper( + name='MAIN_GOTO_TUTORIAL', + share=Button( + file='./assets/share/base/page/MAIN_GOTO_TUTORIAL.png', + area=(195, 58, 207, 82), + search=(175, 38, 227, 102), + color=(127, 131, 139), + button=(195, 58, 207, 82), + ), +) +MENU_CHECK = ButtonWrapper( + name='MENU_CHECK', + share=Button( + file='./assets/share/base/page/MENU_CHECK.png', + area=(1222, 638, 1252, 669), + search=(1202, 618, 1272, 689), + color=(57, 50, 39), + button=(1222, 638, 1252, 669), + ), +) +MESSAGE_CLOSE = ButtonWrapper( + name='MESSAGE_CLOSE', + share=Button( + file='./assets/share/base/page/MESSAGE_CLOSE.png', + area=(863, 95, 895, 127), + search=(843, 75, 915, 147), + color=(175, 174, 175), + button=(863, 95, 895, 127), + ), +) +MISSION_CHECK = ButtonWrapper( + name='MISSION_CHECK', + share=Button( + file='./assets/share/base/page/MISSION_CHECK.png', + area=(44, 33, 70, 55), + search=(24, 13, 90, 75), + color=(194, 177, 139), + button=(44, 33, 70, 55), + ), +) +TEAM_CHECK = ButtonWrapper( + name='TEAM_CHECK', + share=Button( + file='./assets/share/base/page/TEAM_CHECK.png', + area=(41, 34, 73, 54), + search=(21, 14, 93, 74), + color=(138, 123, 101), + button=(41, 34, 73, 54), + ), +) +TUTORIAL_CHECK = ButtonWrapper( + name='TUTORIAL_CHECK', + share=Button( + file='./assets/share/base/page/TUTORIAL_CHECK.png', + area=(44, 30, 70, 56), + search=(24, 10, 90, 76), + color=(141, 126, 99), + button=(44, 30, 70, 56), + ), +) diff --git a/tasks/base/assets/assets_base_popup.py b/tasks/base/assets/assets_base_popup.py new file mode 100644 index 000000000..bca5fabf1 --- /dev/null +++ b/tasks/base/assets/assets_base_popup.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 ``` + +BATTLE_PASS_NOTIFICATION = ButtonWrapper( + name='BATTLE_PASS_NOTIFICATION', + share=Button( + file='./assets/share/base/popup/BATTLE_PASS_NOTIFICATION.png', + area=(960, 601, 984, 625), + search=(940, 581, 1004, 645), + color=(137, 127, 109), + button=(895, 595, 1180, 630), + ), +) +GET_REWARD = ButtonWrapper( + name='GET_REWARD', + share=Button( + file='./assets/share/base/popup/GET_REWARD.png', + area=(625, 118, 655, 147), + search=(605, 98, 675, 167), + color=(167, 151, 116), + button=(741, 495, 1071, 644), + ), +) diff --git a/tasks/base/page.py b/tasks/base/page.py new file mode 100644 index 000000000..f680e77dc --- /dev/null +++ b/tasks/base/page.py @@ -0,0 +1,122 @@ +import traceback + +from tasks.base.assets.assets_base_page import * + + +class Page: + # Key: str, page name like "page_main" + # Value: Page, page instance + all_pages = {} + + @classmethod + def clear_connection(cls): + for page in cls.all_pages.values(): + page.parent = None + + @classmethod + def init_connection(cls, destination): + """ + Initialize an A* path finding among pages. + + Args: + destination (Page): + """ + cls.clear_connection() + + visited = [destination] + visited = set(visited) + while 1: + new = visited.copy() + for page in visited: + for link in cls.iter_pages(): + if link in visited: + continue + if page in link.links: + link.parent = page + new.add(link) + if len(new) == len(visited): + break + visited = new + + @classmethod + def iter_pages(cls): + return cls.all_pages.values() + + @classmethod + def iter_check_buttons(cls): + for page in cls.all_pages.values(): + yield page.check_button + + def __init__(self, check_button): + self.check_button = check_button + self.links = {} + (filename, line_number, function_name, text) = traceback.extract_stack()[-2] + self.name = text[:text.find('=')].strip() + self.parent = None + Page.all_pages[self.name] = self + + def __eq__(self, other): + return self.name == other.name + + def __hash__(self): + return hash(self.name) + + def __str__(self): + return self.name + + def link(self, button, destination): + self.links[destination] = button + + +# Main page +page_main = Page(MAIN_GOTO_CHARACTER) + +# Menu, entered from phone +page_menu = Page(MENU_CHECK) +page_menu.link(CLOSE, destination=page_main) +page_main.link(MAIN_GOTO_MENU, destination=page_menu) + +# Team +page_team = Page(TEAM_CHECK) +page_team.link(CLOSE, destination=page_main) +page_main.link(MAIN_GOTO_TEAM, destination=page_team) + +# Item, storage +page_item = Page(ITEM_CHECK) +page_item.link(CLOSE, destination=page_main) +page_main.link(MAIN_GOTO_ITEM, destination=page_item) + +# Guide, which includes beginners' guide, daily missions and dungeons +page_guide = Page(GUIDE_CHECK) +page_guide.link(GUIDE_CLOSE, destination=page_main) +page_main.link(MAIN_GOTO_GUIDE, destination=page_guide) + +# Gacha +page_gacha = Page(GACHA_CHECK) +page_gacha.link(CLOSE, destination=page_main) +page_main.link(MAIN_GOTO_GACHA, destination=page_gacha) + +# Battle Pass +page_battle_pass = Page(BATTLE_PASS_CHECK) +page_battle_pass.link(CLOSE, destination=page_main) +page_main.link(MAIN_GOTO_BATTLE_PASS, destination=page_battle_pass) + +# Event +page_event = Page(EVENT_CHECK) +page_event.link(CLOSE, destination=page_main) +page_main.link(MAIN_GOTO_EVENT, destination=page_event) + +# Tutorial +page_tutorial = Page(TUTORIAL_CHECK) +page_tutorial.link(CLOSE, destination=page_main) +page_main.link(MAIN_GOTO_TUTORIAL, destination=page_tutorial) + +# Mission +page_mission = Page(MISSION_CHECK) +page_mission.link(CLOSE, destination=page_main) +page_main.link(MAIN_GOTO_MISSION, destination=page_mission) + +# Message +page_message = Page(MESSAGE_CLOSE) +page_message.link(MESSAGE_CLOSE, destination=page_main) +page_main.link(MAIN_GOTO_MESSAGE, destination=page_message) diff --git a/tasks/base/popup.py b/tasks/base/popup.py new file mode 100644 index 000000000..0f701c380 --- /dev/null +++ b/tasks/base/popup.py @@ -0,0 +1,32 @@ +from module.base.base import ModuleBase +from tasks.base.assets.assets_base_popup import * + + +class PopupHandler(ModuleBase): + def handle_reward(self, interval=5) -> bool: + """ + Args: + interval: + + Returns: + If handled. + """ + if self.appear_then_click(GET_REWARD, interval=interval): + return True + + return False + + def handle_battle_pass_notification(self, interval=5) -> bool: + """ + Popup notification that you enter battle pass the first time. + + Args: + interval: + + Returns: + If handled. + """ + if self.appear_then_click(BATTLE_PASS_NOTIFICATION, interval=interval): + return True + + return False diff --git a/tasks/base/ui.py b/tasks/base/ui.py new file mode 100644 index 000000000..777785d28 --- /dev/null +++ b/tasks/base/ui.py @@ -0,0 +1,175 @@ +from module.base.decorator import run_once +from module.base.timer import Timer +from module.exception import GameNotRunningError, GamePageUnknownError +from module.logger import logger +from tasks.base.page import Page, page_main +from tasks.base.popup import PopupHandler + + +class UI(PopupHandler): + ui_current: Page + + def ui_page_appear(self, page): + """ + Args: + page (Page): + """ + return self.appear(page.check_button) + + def ui_get_current_page(self, skip_first_screenshot=True): + """ + Args: + skip_first_screenshot: + + Returns: + Page: + + Raises: + GameNotRunningError: + GamePageUnknownError: + """ + logger.info("UI get current page") + + @run_once + def app_check(): + if not self.device.app_is_running(): + raise GameNotRunningError("Game not running") + + @run_once + def minicap_check(): + if self.config.Emulator_ControlMethod == "uiautomator2": + self.device.uninstall_minicap() + + @run_once + def rotation_check(): + self.device.get_orientation() + + timeout = Timer(10, count=20).start() + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + if not hasattr(self.device, "image") or self.device.image is None: + self.device.screenshot() + else: + self.device.screenshot() + + # End + if timeout.reached(): + break + + # Known pages + for page in Page.iter_pages(): + if page.check_button is None: + continue + if self.ui_page_appear(page=page): + logger.attr("UI", page.name) + self.ui_current = page + return page + + # Unknown page but able to handle + logger.info("Unknown ui page") + if self.ui_additional(): + timeout.reset() + continue + + app_check() + minicap_check() + rotation_check() + + # Unknown page, need manual switching + logger.warning("Unknown ui page") + logger.attr("EMULATOR__SCREENSHOT_METHOD", self.config.Emulator_ScreenshotMethod) + logger.attr("EMULATOR__CONTROL_METHOD", self.config.Emulator_ControlMethod) + logger.attr("SERVER", self.config.SERVER) + logger.warning("Starting from current page is not supported") + logger.warning(f"Supported page: {[str(page) for page in Page.iter_pages()]}") + logger.warning('Supported page: Any page with a "HOME" button on the upper-right') + logger.critical("Please switch to a supported page before starting SRC") + raise GamePageUnknownError + + def ui_goto(self, destination, skip_first_screenshot=True): + """ + Args: + destination (Page): + skip_first_screenshot: + """ + # Create connection + Page.init_connection(destination) + self.interval_clear(list(Page.iter_check_buttons())) + + logger.hr(f"UI goto {destination}") + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + # Destination page + if self.ui_page_appear(destination): + logger.info(f'Page arrive: {destination}') + break + + # Other pages + clicked = False + for page in Page.iter_pages(): + if page.parent is None or page.check_button is None: + continue + if self.appear(page.check_button, interval=5): + logger.info(f'Page switch: {page} -> {page.parent}') + button = page.links[page.parent] + self.device.click(button) + self.ui_button_interval_reset(button) + clicked = True + break + if clicked: + continue + + # Additional + if self.ui_additional(): + continue + + # Reset connection + Page.clear_connection() + + def ui_ensure(self, destination, skip_first_screenshot=True): + """ + Args: + destination (Page): + skip_first_screenshot: + + Returns: + bool: If UI switched. + """ + logger.hr("UI ensure") + self.ui_get_current_page(skip_first_screenshot=skip_first_screenshot) + if self.ui_current == destination: + logger.info("Already at %s" % destination) + return False + else: + logger.info("Goto %s" % destination) + self.ui_goto(destination, skip_first_screenshot=True) + return True + + def ui_goto_main(self): + return self.ui_ensure(destination=page_main) + + def ui_additional(self) -> bool: + """ + Handle all possible popups during UI switching. + + Returns: + If handled any popup. + """ + if self.handle_reward(): + return True + if self.handle_battle_pass_notification(): + return True + + def ui_button_interval_reset(self, button): + """ + Reset interval of some button to avoid mistaken clicks + + Args: + button (Button): + """ + pass diff --git a/tasks/login/assets/assets_login.py b/tasks/login/assets/assets_login.py new file mode 100644 index 000000000..72a8ae694 --- /dev/null +++ b/tasks/login/assets/assets_login.py @@ -0,0 +1,15 @@ +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 ``` + +LOGIN_CONFIRM = ButtonWrapper( + name='LOGIN_CONFIRM', + share=Button( + file='./assets/share/login/LOGIN_CONFIRM.png', + area=(1188, 44, 1220, 74), + search=(1168, 24, 1240, 94), + color=(140, 124, 144), + button=(683, 327, 1143, 620), + ), +) diff --git a/tasks/login/login.py b/tasks/login/login.py new file mode 100644 index 000000000..98210d1d8 --- /dev/null +++ b/tasks/login/login.py @@ -0,0 +1,65 @@ +from module.base.timer import Timer +from module.logger import logger +from tasks.base.page import page_main +from tasks.base.ui import UI +from tasks.login.assets.assets_login import LOGIN_CONFIRM + + +class Login(UI): + def _handle_app_login(self): + """ + Pages: + in: Any page + out: page_main + + Raises: + GameStuckError: + GameTooManyClickError: + """ + logger.hr('App login') + + orientation_timer = Timer(5) + login_success = False + + while 1: + # Watch device rotation + if not login_success and orientation_timer.reached(): + # Screen may rotate after starting an app + self.device.get_orientation() + orientation_timer.reset() + + self.device.screenshot() + + # End + if self.ui_page_appear(page_main): + logger.info('Login to main confirm') + break + + # Login + if self.appear_then_click(LOGIN_CONFIRM): + continue + + return True + + def handle_app_login(self): + self.device.screenshot_interval_set(1.0) + try: + self._handle_app_login() + finally: + self.device.screenshot_interval_set() + + def app_stop(self): + logger.hr('App stop') + self.device.app_stop() + + def app_start(self): + logger.hr('App start') + self.device.app_start() + self.handle_app_login() + + def app_restart(self): + logger.hr('App restart') + self.device.app_stop() + self.device.app_start() + self.handle_app_login() + self.config.task_delay(server_update=True)