From e1a6b8760f5bef051a5718f716fd664d0e0dc7f6 Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Thu, 2 Nov 2023 03:56:01 +0800 Subject: [PATCH] Refactor: Abstract DungeonState class to be shared in rogue --- .../share/dungeon/state/OCR_SIMUNI_POINT.png | Bin 0 -> 18544 bytes .../OCR_SIMUNI_POINT_OFFSET.SEARCH.png | Bin .../{ui => state}/OCR_SIMUNI_POINT_OFFSET.png | Bin .../state/OCR_STAMINA.png} | Bin assets/share/dungeon/ui/OCR_SIMUNI_POINT.png | Bin 16420 -> 0 bytes tasks/dungeon/assets/assets_dungeon_state.py | 35 +++++ tasks/dungeon/assets/assets_dungeon_ui.py | 20 --- tasks/dungeon/state.py | 122 ++++++++++++++++++ tasks/dungeon/ui.py | 40 +----- tasks/rogue/assets/assets_rogue_reward.py | 10 -- tasks/rogue/entry/entry.py | 4 +- tasks/rogue/event/reward.py | 46 +------ 12 files changed, 168 insertions(+), 109 deletions(-) create mode 100644 assets/share/dungeon/state/OCR_SIMUNI_POINT.png rename assets/share/dungeon/{ui => state}/OCR_SIMUNI_POINT_OFFSET.SEARCH.png (100%) rename assets/share/dungeon/{ui => state}/OCR_SIMUNI_POINT_OFFSET.png (100%) rename assets/share/{rogue/reward/OCR_REMAIN.png => dungeon/state/OCR_STAMINA.png} (100%) delete mode 100644 assets/share/dungeon/ui/OCR_SIMUNI_POINT.png create mode 100644 tasks/dungeon/assets/assets_dungeon_state.py create mode 100644 tasks/dungeon/state.py diff --git a/assets/share/dungeon/state/OCR_SIMUNI_POINT.png b/assets/share/dungeon/state/OCR_SIMUNI_POINT.png new file mode 100644 index 0000000000000000000000000000000000000000..51bf72943a53422e27f0e8a65892184a664516f8 GIT binary patch literal 18544 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 GpZ^Ql11zBc diff --git a/tasks/dungeon/assets/assets_dungeon_state.py b/tasks/dungeon/assets/assets_dungeon_state.py new file mode 100644 index 000000000..f8f1477ea --- /dev/null +++ b/tasks/dungeon/assets/assets_dungeon_state.py @@ -0,0 +1,35 @@ +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 ``` + +OCR_SIMUNI_POINT = ButtonWrapper( + name='OCR_SIMUNI_POINT', + share=Button( + file='./assets/share/dungeon/state/OCR_SIMUNI_POINT.png', + area=(580, 237, 860, 277), + search=(560, 217, 880, 297), + color=(163, 170, 252), + button=(580, 237, 860, 277), + ), +) +OCR_SIMUNI_POINT_OFFSET = ButtonWrapper( + name='OCR_SIMUNI_POINT_OFFSET', + share=Button( + file='./assets/share/dungeon/state/OCR_SIMUNI_POINT_OFFSET.png', + area=(685, 250, 717, 273), + search=(583, 187, 883, 387), + color=(199, 200, 250), + button=(685, 250, 717, 273), + ), +) +OCR_STAMINA = ButtonWrapper( + name='OCR_STAMINA', + share=Button( + file='./assets/share/dungeon/state/OCR_STAMINA.png', + area=(675, 11, 1181, 64), + search=(655, 0, 1201, 84), + color=(75, 89, 125), + button=(675, 11, 1181, 64), + ), +) diff --git a/tasks/dungeon/assets/assets_dungeon_ui.py b/tasks/dungeon/assets/assets_dungeon_ui.py index b296a7243..8d04b5748 100644 --- a/tasks/dungeon/assets/assets_dungeon_ui.py +++ b/tasks/dungeon/assets/assets_dungeon_ui.py @@ -53,26 +53,6 @@ OCR_DUNGEON_NAV = ButtonWrapper( button=(117, 182, 423, 641), ), ) -OCR_SIMUNI_POINT = ButtonWrapper( - name='OCR_SIMUNI_POINT', - share=Button( - file='./assets/share/dungeon/ui/OCR_SIMUNI_POINT.png', - area=(580, 237, 820, 277), - search=(560, 217, 840, 297), - color=(166, 168, 252), - button=(580, 237, 820, 277), - ), -) -OCR_SIMUNI_POINT_OFFSET = ButtonWrapper( - name='OCR_SIMUNI_POINT_OFFSET', - share=Button( - file='./assets/share/dungeon/ui/OCR_SIMUNI_POINT_OFFSET.png', - area=(685, 250, 717, 273), - search=(583, 187, 883, 387), - color=(199, 200, 250), - button=(685, 250, 717, 273), - ), -) OCR_WEEKLY_LIMIT = ButtonWrapper( name='OCR_WEEKLY_LIMIT', share=Button( diff --git a/tasks/dungeon/state.py b/tasks/dungeon/state.py new file mode 100644 index 000000000..f653a1139 --- /dev/null +++ b/tasks/dungeon/state.py @@ -0,0 +1,122 @@ +import threading + +from module.base.timer import Timer +from module.base.utils import crop +from module.logger import logger +from module.ocr.ocr import DigitCounter +from tasks.base.ui import UI +from tasks.dungeon.assets.assets_dungeon_state import OCR_SIMUNI_POINT, OCR_SIMUNI_POINT_OFFSET, OCR_STAMINA + + +class OcrSimUniPoint(DigitCounter): + def after_process(self, result): + result = super().after_process(result) + result = result.replace('O', '0').replace('o', '0') + return result + + +class DungeonState(UI): + def dungeon_get_simuni_point(self, image=None) -> int: + """ + Page: + in: page_guide, Survival_Index, Simulated_Universe + """ + logger.info('Get simulated universe points') + if image is None: + image = self.device.image + + _ = OCR_SIMUNI_POINT_OFFSET.match_template(image) + OCR_SIMUNI_POINT.load_offset(OCR_SIMUNI_POINT_OFFSET) + area = ( + OCR_SIMUNI_POINT.area[0], + OCR_SIMUNI_POINT.button[1], + OCR_SIMUNI_POINT.area[2], + OCR_SIMUNI_POINT.button[3], + ) + + ocr = OcrSimUniPoint(OCR_SIMUNI_POINT) + value, _, total = ocr.ocr_single_line(crop(image, area), direct_ocr=True) + if total and value <= total: + logger.attr('SimulatedUniverse', f'{value}/{total}') + self.config.stored.SimulatedUniverse.set(value, total) + return value + else: + logger.warning(f'Invalid SimulatedUniverse points: {value}/{total}') + return 0 + + def dungeon_update_stamina(self, image=None, skip_first_screenshot=True): + """ + Returns: + bool: If success + + Pages: + in: page_guild, Survival_Index, Simulated_Universe + or page_rogue, LEVEL_CONFIRM + or rogue, REWARD_CLOSE + """ + ocr = DigitCounter(OCR_STAMINA) + timeout = Timer(1, count=2).start() + if image is None: + image = self.device.image + else: + skip_first_screenshot = True + + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + stamina = (0, 0, 0) + immersifier = (0, 0, 0) + + if timeout.reached(): + logger.warning('dungeon_update_stamina() timeout') + return False + + for row in ocr.detect_and_ocr(image): + if row.ocr_text.isdigit(): + continue + if row.ocr_text == '+': + continue + if '/' not in row.ocr_text: + continue + data = ocr.format_result(row.ocr_text) + if data[2] == self.config.stored.TrailblazePower.FIXED_TOTAL: + stamina = data + if data[2] == self.config.stored.Immersifier.FIXED_TOTAL: + immersifier = data + + if stamina[2] > 0 and immersifier[2] > 0: + break + if image is not None: + logger.warning('dungeon_update_stamina() ended') + return + + stamina = stamina[0] + immersifier = immersifier[0] + logger.attr('TrailblazePower', stamina) + logger.attr('Imersifier', immersifier) + with self.config.multi_set(): + self.config.stored.TrailblazePower.value = stamina + self.config.stored.Immersifier.value = immersifier + return True + + def dungeon_update_simuni(self): + """ + Update rogue weekly points, stamina, immersifier + Run in a new thread to be faster as data is not used immediately + + Page: + in: page_guide, Survival_Index, Simulated_Universe + """ + logger.info('dungeon_update_simuni') + + def func(image): + logger.info('Update thread start') + with self.config.multi_set(): + self.dungeon_get_simuni_point(image) + self.dungeon_update_stamina(image) + + thread = threading.Thread(target=func, args=(self.device.image,)) + thread.start() diff --git a/tasks/dungeon/ui.py b/tasks/dungeon/ui.py index b254df6cf..14c656aaf 100644 --- a/tasks/dungeon/ui.py +++ b/tasks/dungeon/ui.py @@ -7,12 +7,11 @@ from module.base.button import ClickButton from module.base.timer import Timer from module.base.utils import get_color from module.logger import logger -from module.ocr.ocr import DigitCounter, Ocr, OcrResultButton +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.base.ui import UI from tasks.combat.assets.assets_combat_prepare import COMBAT_PREPARE from tasks.dungeon.assets.assets_dungeon_ui import * from tasks.dungeon.keywords import ( @@ -24,6 +23,7 @@ from tasks.dungeon.keywords import ( KEYWORDS_DUNGEON_TAB ) from tasks.dungeon.keywords.classes import DungeonEntrance +from tasks.dungeon.state import DungeonState class DungeonTabSwitch(Switch): @@ -78,13 +78,6 @@ class OcrDungeonList(Ocr): return result -class OcrSimUniPoint(DigitCounter): - def after_process(self, result): - result = super().after_process(result) - result = result.replace('O', '0').replace('o', '0') - return result - - class OcrDungeonListLimitEntrance(OcrDungeonList): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -123,7 +116,7 @@ DUNGEON_LIST = DraggableDungeonList( ocr_class=OcrDungeonList, search_button=OCR_DUNGEON_LIST) -class DungeonUI(UI): +class DungeonUI(DungeonState): def dungeon_tab_goto(self, state: DungeonTab): """ Args: @@ -233,31 +226,6 @@ class DungeonUI(UI): logger.info('No Forgotten_Hall in list skip waiting') return False - def dungeon_get_simuni_point(self) -> int: - """ - Page: - in: page_guide, Survival_Index, Simulated_Universe - """ - logger.info('Get simulated universe points') - _ = self.appear(OCR_SIMUNI_POINT_OFFSET) - OCR_SIMUNI_POINT.load_offset(OCR_SIMUNI_POINT_OFFSET) - area = ( - OCR_SIMUNI_POINT.area[0], - OCR_SIMUNI_POINT.button[1], - OCR_SIMUNI_POINT.area[2], - OCR_SIMUNI_POINT.button[3], - ) - - ocr = OcrSimUniPoint(OCR_SIMUNI_POINT) - value, _, total = ocr.ocr_single_line(self.image_crop(area), direct_ocr=True) - if total and value <= total: - logger.attr('SimulatedUniverse', f'{value}/{total}') - self.config.stored.SimulatedUniverse.set(value, total) - return value - else: - logger.warning(f'Invalid SimulatedUniverse points: {value}/{total}') - return 0 - def _dungeon_nav_goto(self, dungeon: DungeonList, skip_first_screenshot=True): """ Equivalent to `DUNGEON_NAV_LIST.select_row(dungeon.dungeon_nav, main=self)` @@ -301,7 +269,7 @@ class DungeonUI(UI): logger.info('DUNGEON_NAV_LIST at top') # Update points if possible if DUNGEON_NAV_LIST.is_row_selected(button, main=self): - self.dungeon_get_simuni_point() + self.dungeon_update_simuni() else: # To start from any list states. logger.info('DUNGEON_NAV_LIST not at top') diff --git a/tasks/rogue/assets/assets_rogue_reward.py b/tasks/rogue/assets/assets_rogue_reward.py index 416550e94..674294929 100644 --- a/tasks/rogue/assets/assets_rogue_reward.py +++ b/tasks/rogue/assets/assets_rogue_reward.py @@ -3,16 +3,6 @@ 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 ``` -OCR_REMAIN = ButtonWrapper( - name='OCR_REMAIN', - share=Button( - file='./assets/share/rogue/reward/OCR_REMAIN.png', - area=(675, 11, 1181, 64), - search=(655, 0, 1201, 84), - color=(75, 89, 125), - button=(675, 11, 1181, 64), - ), -) REWARD_CLOSE = ButtonWrapper( name='REWARD_CLOSE', share=Button( diff --git a/tasks/rogue/entry/entry.py b/tasks/rogue/entry/entry.py index 9094a826c..4eac87b9e 100644 --- a/tasks/rogue/entry/entry.py +++ b/tasks/rogue/entry/entry.py @@ -156,7 +156,9 @@ class RogueEntry(DungeonUI, RogueRewardHandler, RoguePathHandler, RouteBase): self.device.click(WORLD_ENTER) self.interval_reset(REWARD_ENTER, interval=2) continue - if self.appear_then_click(LEVEL_CONFIRM, interval=2): + if self.appear(LEVEL_CONFIRM, interval=2): + self.dungeon_update_stamina() + self.device.click(LEVEL_CONFIRM) continue if self.appear_then_click(REWARD_CLOSE, interval=2): continue diff --git a/tasks/rogue/event/reward.py b/tasks/rogue/event/reward.py index c80ef8998..673db4dbf 100644 --- a/tasks/rogue/event/reward.py +++ b/tasks/rogue/event/reward.py @@ -2,52 +2,14 @@ from datetime import datetime, timedelta from module.base.timer import Timer from module.logger import logger -from module.ocr.ocr import DigitCounter from tasks.base.assets.assets_base_popup import GET_REWARD from tasks.combat.interact import CombatInteract -from tasks.rogue.assets.assets_rogue_reward import OCR_REMAIN, REWARD_CLOSE, USE_IMMERSIFIER, USE_STAMINA +from tasks.dungeon.state import DungeonState +from tasks.rogue.assets.assets_rogue_reward import REWARD_CLOSE, USE_IMMERSIFIER, USE_STAMINA from tasks.rogue.bleesing.ui import RogueUI -class RogueReward(RogueUI, CombatInteract): - def _reward_update_stamina(self, skip_first_screenshot=True): - ocr = DigitCounter(OCR_REMAIN) - timeout = Timer(1, count=2).start() - while 1: - if skip_first_screenshot: - skip_first_screenshot = False - else: - self.device.screenshot() - - stamina = (0, 0, 0) - immersifier = (0, 0, 0) - - if timeout.reached(): - logger.warning('_reward_update_stamina() timeout') - break - - for row in ocr.detect_and_ocr(self.device.image): - if row.ocr_text.isdigit(): - continue - if row.ocr_text == '+': - continue - data = ocr.format_result(row.ocr_text) - if data[2] == self.config.stored.TrailblazePower.FIXED_TOTAL: - stamina = data - if data[2] == self.config.stored.Immersifier.FIXED_TOTAL: - immersifier = data - - if stamina[2] > 0 and immersifier[2] > 0: - break - - stamina = stamina[0] - immersifier = immersifier[0] - logger.attr('TrailblazePower', stamina) - logger.attr('Imersifier', immersifier) - with self.config.multi_set(): - self.config.stored.TrailblazePower.value = stamina - self.config.stored.Immersifier.value = immersifier - +class RogueReward(RogueUI, CombatInteract, DungeonState): def claim_domain_reward( self, use_trailblaze_power=False, @@ -86,7 +48,7 @@ class RogueReward(RogueUI, CombatInteract): confirm.reset() continue if self.appear(REWARD_CLOSE, interval=2): - self._reward_update_stamina() + self.dungeon_update_stamina() if use_immersifier and self.config.stored.Immersifier.value > 0: self.device.click(USE_IMMERSIFIER) self.interval_reset(USE_STAMINA)