StarRailCopilot/tasks/rogue/entry/path.py

153 lines
5.7 KiB
Python

from module.base.decorator import cached_property
from module.base.timer import Timer
from module.exception import ScriptError
from module.logger import logger
from tasks.base.assets.assets_base_page import BACK
from tasks.rogue.assets.assets_rogue_path import *
from tasks.rogue.assets.assets_rogue_ui import ROGUE_LAUNCH
from tasks.rogue.bleesing.ui import RogueUI
from tasks.rogue.exception import RogueTeamNotPrepared
from tasks.rogue.keywords import KEYWORDS_ROGUE_PATH, RoguePath
def area_pad_around(area, pad):
"""
Inner offset an area.
Args:
area: (upper_left_x, upper_left_y, bottom_right_x, bottom_right_y).
pad (tuple):
Returns:
tuple: (upper_left_x, upper_left_y, bottom_right_x, bottom_right_y).
"""
upper_left_x, upper_left_y, bottom_right_x, bottom_right_y = area
upper_left_x_pad, upper_left_y_pad, bottom_right_x_pad, bottom_right_y_pad = pad
return upper_left_x + upper_left_x_pad, \
upper_left_y + upper_left_y_pad, \
bottom_right_x - bottom_right_x_pad, \
bottom_right_y - bottom_right_y_pad
class RoguePathHandler(RogueUI):
@cached_property
def _rogue_path_checks(self) -> dict[RoguePath, ButtonWrapper]:
buttons = {
KEYWORDS_ROGUE_PATH.Preservation: CHECK_PRESERVATION,
KEYWORDS_ROGUE_PATH.Remembrance: CHECK_REMEMBRANCE,
KEYWORDS_ROGUE_PATH.Nihility: CHECK_NIHILITY,
KEYWORDS_ROGUE_PATH.Abundance: CHECK_ABUNDANCE,
KEYWORDS_ROGUE_PATH.The_Hunt: CHECK_THE_HUNT,
KEYWORDS_ROGUE_PATH.Destruction: CHECK_DESTRUCTION,
KEYWORDS_ROGUE_PATH.Elation: CHECK_ELATION,
}
return buttons
@cached_property
def _rogue_path_clicks(self) -> dict[RoguePath, ButtonWrapper]:
buttons = {
KEYWORDS_ROGUE_PATH.Preservation: CLICK_PRESERVATION,
KEYWORDS_ROGUE_PATH.Remembrance: CLICK_REMEMBRANCE,
KEYWORDS_ROGUE_PATH.Nihility: CLICK_NIHILITY,
KEYWORDS_ROGUE_PATH.Abundance: CLICK_ABUNDANCE,
KEYWORDS_ROGUE_PATH.The_Hunt: CLICK_THE_HUNT,
KEYWORDS_ROGUE_PATH.Destruction: CLICK_DESTRUCTION,
KEYWORDS_ROGUE_PATH.Elation: CLICK_ELATION,
}
# Path list is sliding, expand search area
for b in buttons.values():
b.load_search(area_pad_around(b.area, pad=(-100, -5, -100, -5)))
return buttons
def _get_path_click(self, path: RoguePath) -> ButtonWrapper:
try:
return self._rogue_path_clicks[path]
except KeyError:
logger.critical(f'Invalid rogue path: {path}')
raise ScriptError
def _get_selected_path(self, skip_first_screenshot=True) -> RoguePath | None:
timeout = Timer(1, count=5).start()
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
self.device.screenshot()
if timeout.reached():
break
for path, button in self._rogue_path_checks.items():
# Check colors to wait appear animation
if self.match_template_color(button):
logger.attr('SelectPath', path)
return path
logger.warning('Unable to get select path')
return None
def _is_page_rogue_path(self) -> bool:
appear = [self.appear(button) for button in self._rogue_path_clicks.values()]
return all(appear)
def _is_team_prepared(self) -> bool:
"""
Pages:
in: is_page_rogue_launch()
"""
slots = CHARACTER_EMPTY.match_multi_template(self.device.image)
slots = 4 - len(slots)
logger.attr('TeamSlotsPrepared', slots)
return slots > 0
def rogue_path_select(self, path: str | RoguePath, skip_first_screenshot=True):
"""
Raises:
RogueTeamNotPrepared:
Pages:
in: LAUNCH_ROGUE
out: is_page_choose_bonus()
or page_main if previous rogue run had bonus selected but didn't finish any domain
"""
logger.hr('Rogue path select', level=2)
path: RoguePath = RoguePath.find(path)
logger.info(f'Select path: {path}')
entry = self._get_path_click(path)
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
self.device.screenshot()
# End
if self.is_page_choose_bonus():
logger.info('rogue_path_select ended at is_page_choose_bonus')
break
if self.is_in_main():
logger.info('rogue_path_select ended at page_main')
break
if self.appear(ROGUE_LAUNCH, interval=2):
if not self._is_team_prepared():
raise RogueTeamNotPrepared
self.device.click(ROGUE_LAUNCH)
continue
# The average level of your team is lower than the recommended level.
# Continue anyway?
if self.handle_popup_confirm():
continue
# Select path
if self.interval_is_reached(entry, interval=2) and self._is_page_rogue_path():
if self.appear_then_click(entry, interval=2):
self.interval_reset(entry, interval=2)
continue
# Confirm path
if self.appear(CONFIRM_PATH, interval=2):
if self._get_selected_path() == path:
self.device.click(CONFIRM_PATH)
continue
else:
logger.warning('Selected to the wrong path')
self.device.click(BACK)
continue