Add: Switching dungeon tabs

This commit is contained in:
LmeSzinc 2023-05-23 01:00:20 +08:00
parent baaca91568
commit 02b9c96e7b
22 changed files with 372 additions and 1 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -183,7 +183,7 @@ def iter_assets():
for path, frames in deep_iter(data, depth=3):
print(path, frames)
first = frames[1]
search = DataAssets.area_to_search(first.area)
search = first.search if first.search else DataAssets.area_to_search(first.area)
for frame in frames.values():
frame.search = search

167
module/ui/switch.py Normal file
View File

@ -0,0 +1,167 @@
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 reties.
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 (Button):
click_button (Button):
"""
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:
"""
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)
# Handle additional popups
if self.handle_additional(main=main):
continue
# End
if current == state:
return changed
# 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')
return False
counter += 1
continue
# 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

View File

@ -76,6 +76,11 @@ page_menu = Page(MENU_CHECK)
page_menu.link(CLOSE, destination=page_main)
page_main.link(MAIN_GOTO_MENU, destination=page_menu)
# Character
page_character = Page(CHARACTER_CHECK)
page_character.link(CLOSE, destination=page_main)
page_main.link(MAIN_GOTO_CHARACTER, destination=page_character)
# Team
page_team = Page(TEAM_CHECK)
page_team.link(CLOSE, destination=page_main)

View File

@ -0,0 +1,105 @@
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 ```
DAILY_TRAINING_CHECK = ButtonWrapper(
name='DAILY_TRAINING_CHECK',
share=Button(
file='./assets/share/dungeon/ui/DAILY_TRAINING_CHECK.png',
area=(239, 55, 279, 94),
search=(109, 45, 491, 104),
color=(129, 129, 129),
button=(239, 55, 279, 94),
),
)
DAILY_TRAINING_CLICK = ButtonWrapper(
name='DAILY_TRAINING_CLICK',
share=Button(
file='./assets/share/dungeon/ui/DAILY_TRAINING_CLICK.png',
area=(230, 55, 270, 94),
search=(109, 45, 491, 104),
color=(98, 96, 96),
button=(230, 55, 270, 94),
),
)
DAILY_TRAINING_LOADED = ButtonWrapper(
name='DAILY_TRAINING_LOADED',
share=Button(
file='./assets/share/dungeon/ui/DAILY_TRAINING_LOADED.png',
area=(1139, 520, 1156, 539),
search=(1119, 500, 1176, 559),
color=(63, 56, 50),
button=(1139, 520, 1156, 539),
),
)
OCR_DUNGEON_LIST = ButtonWrapper(
name='OCR_DUNGEON_LIST',
share=Button(
file='./assets/share/dungeon/ui/OCR_DUNGEON_LIST.png',
area=(432, 128, 1169, 625),
search=(412, 108, 1189, 645),
color=(221, 222, 225),
button=(432, 128, 1169, 625),
),
)
OCR_DUNGEON_NAV = ButtonWrapper(
name='OCR_DUNGEON_NAV',
share=Button(
file='./assets/share/dungeon/ui/OCR_DUNGEON_NAV.png',
area=(108, 132, 428, 613),
search=(88, 112, 448, 633),
color=(178, 177, 177),
button=(108, 132, 428, 613),
),
)
OPERATION_BRIEFING_CHECK = ButtonWrapper(
name='OPERATION_BRIEFING_CHECK',
share=Button(
file='./assets/share/dungeon/ui/OPERATION_BRIEFING_CHECK.png',
area=(147, 53, 191, 96),
search=(109, 45, 491, 104),
color=(151, 151, 151),
button=(147, 53, 191, 96),
),
)
OPERATION_BRIEFING_CLICK = ButtonWrapper(
name='OPERATION_BRIEFING_CLICK',
share=Button(
file='./assets/share/dungeon/ui/OPERATION_BRIEFING_CLICK.png',
area=(138, 53, 182, 96),
search=(109, 45, 491, 104),
color=(83, 81, 81),
button=(138, 53, 182, 96),
),
)
SURVIVAL_INDEX_CHECK = ButtonWrapper(
name='SURVIVAL_INDEX_CHECK',
share=Button(
file='./assets/share/dungeon/ui/SURVIVAL_INDEX_CHECK.png',
area=(330, 55, 368, 93),
search=(109, 45, 491, 104),
color=(136, 136, 136),
button=(330, 55, 368, 93),
),
)
SURVIVAL_INDEX_CLICK = ButtonWrapper(
name='SURVIVAL_INDEX_CLICK',
share=Button(
file='./assets/share/dungeon/ui/SURVIVAL_INDEX_CLICK.png',
area=(421, 56, 459, 93),
search=(109, 45, 491, 104),
color=(95, 93, 93),
button=(421, 56, 459, 93),
),
)
SURVIVAL_INDEX_LOADED = ButtonWrapper(
name='SURVIVAL_INDEX_LOADED',
share=Button(
file='./assets/share/dungeon/ui/SURVIVAL_INDEX_LOADED.png',
area=(451, 244, 481, 265),
search=(446, 239, 486, 269),
color=(143, 150, 203),
button=(451, 244, 481, 265),
),
)

94
tasks/dungeon/ui.py Normal file
View File

@ -0,0 +1,94 @@
import numpy as np
from module.base.timer import Timer
from module.base.utils import get_color
from module.logger import logger
from module.ui.switch import Switch
from tasks.base.page import page_guide
from tasks.base.ui import UI
from tasks.dungeon.assets.assets_dungeon_ui import *
from tasks.dungeon.keywords import DungeonTab, KEYWORDS_DUNGEON_TAB
class DungeonTabSwitch(Switch):
def click(self, state, main):
"""
Args:
state (str):
main (ModuleBase):
"""
button = self.get_data(state)['click_button']
_ = main.appear(button) # Search button to load offset
main.device.click(button)
SWITCH_DUNGEON_TAB = DungeonTabSwitch('DungeonTab', is_selector=True)
SWITCH_DUNGEON_TAB.add_state(
KEYWORDS_DUNGEON_TAB.Operation_Briefing,
check_button=OPERATION_BRIEFING_CHECK,
click_button=OPERATION_BRIEFING_CLICK
)
SWITCH_DUNGEON_TAB.add_state(
KEYWORDS_DUNGEON_TAB.Daily_Training,
check_button=DAILY_TRAINING_CHECK,
click_button=DAILY_TRAINING_CLICK
)
SWITCH_DUNGEON_TAB.add_state(
KEYWORDS_DUNGEON_TAB.Survival_Index,
check_button=SURVIVAL_INDEX_CHECK,
click_button=SURVIVAL_INDEX_CLICK
)
class DungeonUI(UI):
def dungeon_tab_goto(self, state: DungeonTab):
"""
Args:
state:
Examples:
self = DungeonUI('alas')
self.device.screenshot()
self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Operation_Briefing)
self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Daily_Training)
self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index)
"""
self.ui_ensure(page_guide)
SWITCH_DUNGEON_TAB.set(state, main=self)
if state == KEYWORDS_DUNGEON_TAB.Daily_Training:
logger.info(f'Tab goto {state}, wait until loaded')
self._dungeon_wait_daily_training_loaded()
elif state == KEYWORDS_DUNGEON_TAB.Survival_Index:
logger.info(f'Tab goto {state}, wait until loaded')
self._dungeon_wait_survival_loaded()
def _dungeon_wait_daily_training_loaded(self, skip_first_screenshot=True):
timeout = Timer(2, count=4).start()
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
self.device.screenshot()
if timeout.reached():
logger.warning('Wait daily training loaded timeout')
break
color = get_color(self.device.image, DAILY_TRAINING_LOADED.area)
if np.mean(color) < 128:
logger.info('Daily training loaded')
break
def _dungeon_wait_survival_loaded(self, skip_first_screenshot=True):
timeout = Timer(2, count=4).start()
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
self.device.screenshot()
if timeout.reached():
logger.warning('Wait survival index loaded timeout')
break
if self.appear(SURVIVAL_INDEX_LOADED):
logger.info('Survival index loaded')
break