StarRailCopilot/module/ui/switch.py
2024-11-15 16:41:57 +08:00

199 lines
5.5 KiB
Python

from module.base.base import ModuleBase
from module.base.button import Button
from module.base.timer import Timer
from module.exception import ScriptError
from module.logger import logger
class Switch:
"""
A wrapper to handle switches in game, switch among states with retries.
Examples:
# Definitions
submarine_hunt = Switch('Submarine_hunt', offset=120)
submarine_hunt.add_state('on', check_button=SUBMARINE_HUNT_ON)
submarine_hunt.add_state('off', check_button=SUBMARINE_HUNT_OFF)
# Change state to ON
submarine_view.set('on', main=self)
"""
def __init__(self, name='Switch', is_selector=False):
"""
Args:
name (str):
is_selector (bool): True if this is a multi choice, click to choose one of the switches.
For example: | [Daily] | Urgent | -> click -> | Daily | [Urgent] |
False if this is a switch, click the switch itself, and it changed in the same position.
For example: | [ON] | -> click -> | [OFF] |
"""
self.name = name
self.is_choice = is_selector
self.state_list = []
def add_state(self, state, check_button, click_button=None):
"""
Args:
state (str):
check_button (ButtonWrapper):
click_button (ButtonWrapper):
"""
self.state_list.append({
'state': state,
'check_button': check_button,
'click_button': click_button if click_button is not None else check_button,
})
def appear(self, main):
"""
Args:
main (ModuleBase):
Returns:
bool
"""
for data in self.state_list:
if main.appear(data['check_button']):
return True
return False
def get(self, main):
"""
Args:
main (ModuleBase):
Returns:
str: state name or 'unknown'.
"""
for data in self.state_list:
if main.appear(data['check_button']):
return data['state']
return 'unknown'
def click(self, state, main):
"""
Args:
state (str):
main (ModuleBase):
"""
button = self.get_data(state)['click_button']
main.device.click(button)
def get_data(self, state):
"""
Args:
state (str):
Returns:
dict: Dictionary in add_state
Raises:
ScriptError: If state invalid
"""
for row in self.state_list:
if row['state'] == state:
return row
logger.warning(f'Switch {self.name} received an invalid state {state}')
raise ScriptError(f'Switch {self.name} received an invalid state {state}')
def handle_additional(self, main):
"""
Args:
main (ModuleBase):
Returns:
bool: If handled
"""
return False
def set(self, state, main, skip_first_screenshot=True):
"""
Args:
state:
main (ModuleBase):
skip_first_screenshot (bool):
Returns:
bool: If clicked
"""
logger.info(f'{self.name} set to {state}')
self.get_data(state)
counter = 0
changed = False
warning_show_timer = Timer(5, count=10).start()
click_timer = Timer(1, count=3)
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
main.device.screenshot()
# Detect
current = self.get(main=main)
logger.attr(self.name, current)
# End
if current == state:
return changed
# Handle additional popups
if self.handle_additional(main=main):
continue
# Warning
if current == 'unknown':
if warning_show_timer.reached():
logger.warning(f'Unknown {self.name} switch')
warning_show_timer.reset()
if counter >= 1:
logger.warning(f'{self.name} switch {state} asset has evaluated to unknown too many times, '
f'asset should be re-verified')
counter += 1
# Click
if click_timer.reached():
click_state = state if self.is_choice else current
self.click(click_state, main=main)
click_timer.reset()
changed = True
return changed
def wait(self, main, skip_first_screenshot=True):
"""
Wait until any state activated
Args:
main (ModuleBase):
skip_first_screenshot:
Returns:
bool: If success
"""
timeout = Timer(2, count=6).start()
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
main.device.screenshot()
# Detect
current = self.get(main=main)
logger.attr(self.name, current)
# End
if current != 'unknown':
return True
if timeout.reached():
logger.warning(f'{self.name} wait activated timeout')
return False
# Handle additional popups
if self.handle_additional(main=main):
continue