Refactor: Abstract DungeonState class to be shared in rogue
BIN
assets/share/dungeon/state/OCR_SIMUNI_POINT.png
Normal file
After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 16 KiB |
35
tasks/dungeon/assets/assets_dungeon_state.py
Normal file
@ -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),
|
||||
),
|
||||
)
|
@ -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(
|
||||
|
122
tasks/dungeon/state.py
Normal file
@ -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()
|
@ -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')
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|