diff --git a/assets/share/forgotten_hall/DUNGEON_ENTER_CHECKED.png b/assets/share/forgotten_hall/DUNGEON_ENTER_CHECKED.png new file mode 100644 index 000000000..7329d5358 Binary files /dev/null and b/assets/share/forgotten_hall/DUNGEON_ENTER_CHECKED.png differ diff --git a/assets/share/forgotten_hall/EFFECT_NOTIFICATION.png b/assets/share/forgotten_hall/EFFECT_NOTIFICATION.png new file mode 100644 index 000000000..f8e5f9793 Binary files /dev/null and b/assets/share/forgotten_hall/EFFECT_NOTIFICATION.png differ diff --git a/assets/share/forgotten_hall/ENTER_FORGOTTEN_HALL_DUNGEON.png b/assets/share/forgotten_hall/ENTER_FORGOTTEN_HALL_DUNGEON.png new file mode 100644 index 000000000..3a8628bf7 Binary files /dev/null and b/assets/share/forgotten_hall/ENTER_FORGOTTEN_HALL_DUNGEON.png differ diff --git a/assets/share/forgotten_hall/EXIT_CONFIRM.png b/assets/share/forgotten_hall/EXIT_CONFIRM.png new file mode 100644 index 000000000..158392e39 Binary files /dev/null and b/assets/share/forgotten_hall/EXIT_CONFIRM.png differ diff --git a/assets/share/forgotten_hall/EXIT_DUNGEON.png b/assets/share/forgotten_hall/EXIT_DUNGEON.png new file mode 100644 index 000000000..33a55117f Binary files /dev/null and b/assets/share/forgotten_hall/EXIT_DUNGEON.png differ diff --git a/assets/share/forgotten_hall/FIRST_CHARACTER.png b/assets/share/forgotten_hall/FIRST_CHARACTER.png new file mode 100644 index 000000000..0a5cc853c Binary files /dev/null and b/assets/share/forgotten_hall/FIRST_CHARACTER.png differ diff --git a/tasks/daily/daily_quest.py b/tasks/daily/daily_quest.py index d1ab63415..e56aa6467 100644 --- a/tasks/daily/daily_quest.py +++ b/tasks/daily/daily_quest.py @@ -9,6 +9,7 @@ from tasks.daily.camera import CameraUI from tasks.daily.consumable_usage import ConsumableUsageUI from tasks.daily.keywords import DailyQuest, DailyQuestState, KEYWORDS_DAILY_QUEST, KEYWORDS_DAILY_QUEST_STATE from tasks.daily.synthesize import SynthesizeConsumablesUI, SynthesizeMaterialUI +from tasks.daily.use_technique import UseTechniqueUI from tasks.dungeon.keywords import KEYWORDS_DUNGEON_TAB from tasks.dungeon.ui import DungeonUI @@ -186,6 +187,9 @@ class DailyQuestUI(DungeonUI): if KEYWORDS_DAILY_QUEST.Use_Consumables_1_time in quests: if ConsumableUsageUI(self.config, self.device).use_consumable(): done += 1 + if KEYWORDS_DAILY_QUEST.Use_Technique_2_times in quests: + UseTechniqueUI(self.config, self.device).use_technique(2) + done += 1 return done diff --git a/tasks/daily/use_technique.py b/tasks/daily/use_technique.py new file mode 100644 index 000000000..6c08975ba --- /dev/null +++ b/tasks/daily/use_technique.py @@ -0,0 +1,51 @@ +from module.logger import logger +from tasks.base.page import page_main +from tasks.dungeon.keywords import KEYWORDS_DUNGEON_LIST +from tasks.forgotten_hall.keywords import KEYWORDS_FORGOTTEN_HALL_STAGE +from tasks.forgotten_hall.ui import ForgottenHallUI +from tasks.map.control.joystick import MapControlJoystick + + +class UseTechniqueUI(MapControlJoystick, ForgottenHallUI): + + def _use_technique(self, count: int, skip_first_screenshot=True): + remains = self.map_get_technique_points() + if count > remains: + logger.warning(f"Try to use technique {count} times but only have {remains}") + return + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + remains_after = self.map_get_technique_points() + if remains - remains_after >= count: + logger.info(f"{remains - remains_after} techniques used") + break + self.handle_map_E() + + def use_technique(self, count: int, skip_first_screenshot=True): + """ + Args: + skip_first_screenshot: + count: use {count} times + + Examples: + self = UseTechniquesUI('alas') + self.device.screenshot() + self.use_techniques(2) + + Pages: + in: Any + out: page_forgotten_hall, FORGOTTEN_HALL_CHECKED + """ + logger.hr('Use techniques', level=2) + self.ui_ensure(page_main) + self.stage_goto(KEYWORDS_DUNGEON_LIST.The_Last_Vestiges_of_Towering_Citadel, + KEYWORDS_FORGOTTEN_HALL_STAGE.Stage_1) + self._choose_first_character() + self._enter_forgotten_hall_dungeon() + self._use_technique(count, skip_first_screenshot=skip_first_screenshot) + self.exit_dungeon() + self.ui_goto_main() diff --git a/tasks/forgotten_hall/assets/assets_forgotten_hall.py b/tasks/forgotten_hall/assets/assets_forgotten_hall.py index 179e7f3ce..c681fb3d6 100644 --- a/tasks/forgotten_hall/assets/assets_forgotten_hall.py +++ b/tasks/forgotten_hall/assets/assets_forgotten_hall.py @@ -3,6 +3,36 @@ 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 ``` +DUNGEON_ENTER_CHECKED = ButtonWrapper( + name='DUNGEON_ENTER_CHECKED', + share=Button( + file='./assets/share/forgotten_hall/DUNGEON_ENTER_CHECKED.png', + area=(26, 247, 40, 256), + search=(6, 227, 60, 276), + color=(175, 180, 183), + button=(26, 247, 40, 256), + ), +) +EFFECT_NOTIFICATION = ButtonWrapper( + name='EFFECT_NOTIFICATION', + share=Button( + file='./assets/share/forgotten_hall/EFFECT_NOTIFICATION.png', + area=(237, 302, 315, 380), + search=(217, 282, 335, 400), + color=(128, 114, 85), + button=(237, 302, 315, 380), + ), +) +ENTER_FORGOTTEN_HALL_DUNGEON = ButtonWrapper( + name='ENTER_FORGOTTEN_HALL_DUNGEON', + share=Button( + file='./assets/share/forgotten_hall/ENTER_FORGOTTEN_HALL_DUNGEON.png', + area=(1233, 653, 1241, 680), + search=(1213, 633, 1261, 700), + color=(219, 221, 221), + button=(1233, 653, 1241, 680), + ), +) ENTRANCE_CHECKED = ButtonWrapper( name='ENTRANCE_CHECKED', share=Button( @@ -13,6 +43,36 @@ ENTRANCE_CHECKED = ButtonWrapper( button=(62, 662, 80, 680), ), ) +EXIT_CONFIRM = ButtonWrapper( + name='EXIT_CONFIRM', + share=Button( + file='./assets/share/forgotten_hall/EXIT_CONFIRM.png', + area=(776, 458, 800, 482), + search=(756, 438, 820, 502), + color=(94, 88, 76), + button=(776, 458, 800, 482), + ), +) +EXIT_DUNGEON = ButtonWrapper( + name='EXIT_DUNGEON', + share=Button( + file='./assets/share/forgotten_hall/EXIT_DUNGEON.png', + area=(15, 45, 45, 75), + search=(0, 25, 65, 95), + color=(113, 117, 124), + button=(15, 45, 45, 75), + ), +) +FIRST_CHARACTER = ButtonWrapper( + name='FIRST_CHARACTER', + share=Button( + file='./assets/share/forgotten_hall/FIRST_CHARACTER.png', + area=(54, 100, 150, 212), + search=(34, 80, 170, 232), + color=(145, 125, 103), + button=(54, 100, 150, 212), + ), +) LAST_VERTIGES = ButtonWrapper( name='LAST_VERTIGES', share=Button( diff --git a/tasks/forgotten_hall/ui.py b/tasks/forgotten_hall/ui.py index 1f57fe3f1..2aead4762 100644 --- a/tasks/forgotten_hall/ui.py +++ b/tasks/forgotten_hall/ui.py @@ -3,7 +3,8 @@ import numpy as np from ppocronnx.predict_system import BoxedResult from module.base.base import ModuleBase -from module.base.utils import area_offset, color_similarity_2d, crop +from module.base.timer import Timer +from module.base.utils import area_offset, color_similarity_2d, crop, get_color from module.logger.logger import logger from module.ocr.keyword import Keyword from module.ocr.ocr import Ocr, OcrResultButton @@ -14,6 +15,7 @@ from tasks.dungeon.keywords import DungeonList from tasks.dungeon.ui import DungeonUI from tasks.forgotten_hall.assets.assets_forgotten_hall import * from tasks.forgotten_hall.keywords import * +from tasks.map.control.joystick import MapControlJoystick class ForgottenHallStageOcr(Ocr): @@ -116,3 +118,68 @@ class ForgottenHallUI(UI): dungeon_ui = DungeonUI(config=self.config, device=self.device) dungeon_ui.dungeon_goto(forgotten_hall) STAGE_LIST.select_row(stage_keyword, main=self) + + def exit_dungeon(self, skip_first_screenshot=True): + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + if self.appear(FORGOTTEN_HALL_CHECK): + logger.info("Forgotten hall dungeon exited") + break + + if self.appear_then_click(EXIT_DUNGEON): + continue + if self.appear_then_click(EXIT_CONFIRM): + continue + + def _choose_first_character(self, skip_first_screenshot=True): + """ + A temporary method used to choose the first character only + """ + interval = Timer(1) + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + if np.mean(get_color(self.device.image, ENTER_FORGOTTEN_HALL_DUNGEON.area)) > 128: + logger.info("First character is chosen") + break + if interval.reached(): + self.device.click(FIRST_CHARACTER) + interval.reset() + + def _enter_forgotten_hall_dungeon(self, skip_first_screenshot=True): + """ + called after team is set + """ + interval = Timer(1) + joystick = MapControlJoystick(self.config, self.device) + while 1: # enter ui -> popup + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + if self.appear(EFFECT_NOTIFICATION): + break + + if interval.reached() and np.mean(get_color(self.device.image, ENTER_FORGOTTEN_HALL_DUNGEON.area)) > 128: + self.device.click(ENTER_FORGOTTEN_HALL_DUNGEON) + interval.reset() + + skip_first_screenshot = True + while 1: # pop up -> dungeon inside + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + if self.match_template_color(DUNGEON_ENTER_CHECKED): + logger.info("Forgotten hall dungeon entered") + break + joystick.handle_map_run()