mirror of
https://github.com/LmeSzinc/StarRailCopilot.git
synced 2024-11-25 01:49:41 +00:00
Opt: Faster UI navigation to forgotten hall
This commit is contained in:
parent
3b11204fd9
commit
95c7b6dd53
@ -229,6 +229,7 @@ class DraggableList:
|
|||||||
|
|
||||||
if skip_first_load_rows:
|
if skip_first_load_rows:
|
||||||
skip_first_load_rows = False
|
skip_first_load_rows = False
|
||||||
|
load_rows_interval.reset()
|
||||||
else:
|
else:
|
||||||
if load_rows_interval.reached():
|
if load_rows_interval.reached():
|
||||||
self.load_rows(main=main)
|
self.load_rows(main=main)
|
||||||
@ -240,7 +241,7 @@ class DraggableList:
|
|||||||
|
|
||||||
# End
|
# End
|
||||||
if self.is_row_selected(button, main=main):
|
if self.is_row_selected(button, main=main):
|
||||||
logger.info('Row selected')
|
logger.info(f'Row selected at {row}')
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Click
|
# Click
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from functools import cached_property
|
||||||
from typing import ClassVar
|
from typing import ClassVar
|
||||||
|
|
||||||
|
from module.exception import ScriptError
|
||||||
from module.ocr.keyword import Keyword
|
from module.ocr.keyword import Keyword
|
||||||
|
|
||||||
|
|
||||||
@ -18,46 +20,66 @@ class DungeonTab(Keyword):
|
|||||||
class DungeonList(Keyword):
|
class DungeonList(Keyword):
|
||||||
instances: ClassVar = {}
|
instances: ClassVar = {}
|
||||||
|
|
||||||
@property
|
@cached_property
|
||||||
def is_Calyx_Golden(self):
|
def is_Calyx_Golden(self):
|
||||||
return 'Calyx_Golden' in self.name
|
return 'Calyx_Golden' in self.name
|
||||||
|
|
||||||
@property
|
@cached_property
|
||||||
def is_Calyx_Crimson(self):
|
def is_Calyx_Crimson(self):
|
||||||
return 'Calyx_Crimson' in self.name
|
return 'Calyx_Crimson' in self.name
|
||||||
|
|
||||||
@property
|
@cached_property
|
||||||
def is_Stagnant_Shadow(self):
|
def is_Stagnant_Shadow(self):
|
||||||
return 'Stagnant_Shadow' in self.name
|
return 'Stagnant_Shadow' in self.name
|
||||||
|
|
||||||
@property
|
@cached_property
|
||||||
def is_Cavern_of_Corrosion(self):
|
def is_Cavern_of_Corrosion(self):
|
||||||
return 'Cavern_of_Corrosion' in self.name
|
return 'Cavern_of_Corrosion' in self.name
|
||||||
|
|
||||||
@property
|
@cached_property
|
||||||
def is_Echo_of_War(self):
|
def is_Echo_of_War(self):
|
||||||
return 'Echo_of_War' in self.name
|
return 'Echo_of_War' in self.name
|
||||||
|
|
||||||
@property
|
@cached_property
|
||||||
def is_Simulated_Universe(self):
|
def is_Simulated_Universe(self):
|
||||||
return 'Simulated_Universe' in self.name
|
return 'Simulated_Universe' in self.name
|
||||||
|
|
||||||
@property
|
@cached_property
|
||||||
def is_Forgotten_Hall(self):
|
def is_Forgotten_Hall(self):
|
||||||
return 'Forgotten_Hall' or 'Last_Vestiges' in self.name
|
return 'Forgotten_Hall' or 'Last_Vestiges' in self.name
|
||||||
|
|
||||||
@property
|
@cached_property
|
||||||
def is_Last_Vestiges(self):
|
def is_Last_Vestiges(self):
|
||||||
return 'Last_Vestiges' in self.name
|
return 'Last_Vestiges' in self.name
|
||||||
|
|
||||||
@property
|
@cached_property
|
||||||
def is_daily_dungeon(self):
|
def is_daily_dungeon(self):
|
||||||
return self.is_Calyx_Golden or self.is_Calyx_Crimson or self.is_Stagnant_Shadow or self.is_Cavern_of_Corrosion
|
return self.is_Calyx_Golden or self.is_Calyx_Crimson or self.is_Stagnant_Shadow or self.is_Cavern_of_Corrosion
|
||||||
|
|
||||||
@property
|
@cached_property
|
||||||
def is_weekly_dungeon(self):
|
def is_weekly_dungeon(self):
|
||||||
return self.is_Echo_of_War
|
return self.is_Echo_of_War
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def dungeon_nav(self) -> DungeonNav:
|
||||||
|
import tasks.dungeon.keywords.nav as KEYWORDS_DUNGEON_NAV
|
||||||
|
if self.is_Simulated_Universe:
|
||||||
|
return KEYWORDS_DUNGEON_NAV.Simulated_Universe
|
||||||
|
if self.is_Calyx_Golden:
|
||||||
|
return KEYWORDS_DUNGEON_NAV.Calyx_Golden
|
||||||
|
if self.is_Calyx_Crimson:
|
||||||
|
return KEYWORDS_DUNGEON_NAV.Calyx_Crimson
|
||||||
|
if self.is_Stagnant_Shadow:
|
||||||
|
return KEYWORDS_DUNGEON_NAV.Stagnant_Shadow
|
||||||
|
if self.is_Cavern_of_Corrosion:
|
||||||
|
return KEYWORDS_DUNGEON_NAV.Cavern_of_Corrosion
|
||||||
|
if self.is_Echo_of_War:
|
||||||
|
return KEYWORDS_DUNGEON_NAV.Echo_of_War
|
||||||
|
if self.is_Forgotten_Hall:
|
||||||
|
return KEYWORDS_DUNGEON_NAV.Forgotten_Hall
|
||||||
|
|
||||||
|
raise ScriptError(f'Cannot convert {self} to DungeonNav, please check keyword extractions')
|
||||||
|
|
||||||
|
|
||||||
@dataclass(repr=False)
|
@dataclass(repr=False)
|
||||||
class DungeonEntrance(Keyword):
|
class DungeonEntrance(Keyword):
|
||||||
|
@ -71,12 +71,19 @@ class OcrDungeonList(Ocr):
|
|||||||
result = result.replace('翼', '巽') # 巽风之形
|
result = result.replace('翼', '巽') # 巽风之形
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
class OcrDungeonListLimitEntrance(OcrDungeonList):
|
class OcrDungeonListLimitEntrance(OcrDungeonList):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.button = ClickButton((*self.button.area[:3], self.button.area[3] - 70))
|
self.button = ClickButton((*self.button.area[:3], self.button.area[3] - 70))
|
||||||
|
|
||||||
|
|
||||||
|
class DraggableDungeonNav(DraggableList):
|
||||||
|
# 0.5 is the magic number to reach bottom in 1 swipe
|
||||||
|
# but relax we still have retires when magic doesn't work
|
||||||
|
drag_vector = (0.50, 0.52)
|
||||||
|
|
||||||
|
|
||||||
class DraggableDungeonList(DraggableList):
|
class DraggableDungeonList(DraggableList):
|
||||||
teleports: list[OcrResultButton] = []
|
teleports: list[OcrResultButton] = []
|
||||||
navigates: list[OcrResultButton] = []
|
navigates: list[OcrResultButton] = []
|
||||||
@ -96,7 +103,7 @@ class DraggableDungeonList(DraggableList):
|
|||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
DUNGEON_NAV_LIST = DraggableList(
|
DUNGEON_NAV_LIST = DraggableDungeonNav(
|
||||||
'DungeonNavList', keyword_class=DungeonNav, ocr_class=OcrDungeonNav, search_button=OCR_DUNGEON_NAV)
|
'DungeonNavList', keyword_class=DungeonNav, ocr_class=OcrDungeonNav, search_button=OCR_DUNGEON_NAV)
|
||||||
DUNGEON_LIST = DraggableDungeonList(
|
DUNGEON_LIST = DraggableDungeonList(
|
||||||
'DungeonList', keyword_class=[DungeonList, DungeonEntrance],
|
'DungeonList', keyword_class=[DungeonList, DungeonEntrance],
|
||||||
@ -127,6 +134,13 @@ class DungeonUI(UI):
|
|||||||
self._dungeon_wait_survival_loaded()
|
self._dungeon_wait_survival_loaded()
|
||||||
|
|
||||||
def _dungeon_wait_daily_training_loaded(self, skip_first_screenshot=True):
|
def _dungeon_wait_daily_training_loaded(self, skip_first_screenshot=True):
|
||||||
|
"""
|
||||||
|
Returns:
|
||||||
|
bool: True if wait success, False if wait timeout.
|
||||||
|
|
||||||
|
Pages:
|
||||||
|
in: page_guide, Daily_Training
|
||||||
|
"""
|
||||||
timeout = Timer(2, count=4).start()
|
timeout = Timer(2, count=4).start()
|
||||||
while 1:
|
while 1:
|
||||||
if skip_first_screenshot:
|
if skip_first_screenshot:
|
||||||
@ -136,13 +150,20 @@ class DungeonUI(UI):
|
|||||||
|
|
||||||
if timeout.reached():
|
if timeout.reached():
|
||||||
logger.warning('Wait daily training loaded timeout')
|
logger.warning('Wait daily training loaded timeout')
|
||||||
break
|
return False
|
||||||
color = get_color(self.device.image, DAILY_TRAINING_LOADED.area)
|
color = get_color(self.device.image, DAILY_TRAINING_LOADED.area)
|
||||||
if np.mean(color) < 128:
|
if np.mean(color) < 128:
|
||||||
logger.info('Daily training loaded')
|
logger.info('Daily training loaded')
|
||||||
break
|
return True
|
||||||
|
|
||||||
def _dungeon_wait_survival_loaded(self, skip_first_screenshot=True):
|
def _dungeon_wait_survival_loaded(self, skip_first_screenshot=True):
|
||||||
|
"""
|
||||||
|
Returns:
|
||||||
|
bool: True if wait success, False if wait timeout.
|
||||||
|
|
||||||
|
Pages:
|
||||||
|
in: page_guide, Survival_Index
|
||||||
|
"""
|
||||||
timeout = Timer(2, count=4).start()
|
timeout = Timer(2, count=4).start()
|
||||||
while 1:
|
while 1:
|
||||||
if skip_first_screenshot:
|
if skip_first_screenshot:
|
||||||
@ -152,10 +173,68 @@ class DungeonUI(UI):
|
|||||||
|
|
||||||
if timeout.reached():
|
if timeout.reached():
|
||||||
logger.warning('Wait survival index loaded timeout')
|
logger.warning('Wait survival index loaded timeout')
|
||||||
break
|
return False
|
||||||
if self.appear(SURVIVAL_INDEX_LOADED):
|
if self.appear(SURVIVAL_INDEX_LOADED):
|
||||||
logger.info('Survival index loaded')
|
logger.info('Survival index loaded')
|
||||||
break
|
return True
|
||||||
|
|
||||||
|
def _dungeon_wait_until_forgotten_hall_stabled(self, skip_first_screenshot=True):
|
||||||
|
"""
|
||||||
|
Returns:
|
||||||
|
bool: True if wait success, False if wait timeout.
|
||||||
|
|
||||||
|
Pages:
|
||||||
|
in: page_guide, Survival_Index
|
||||||
|
"""
|
||||||
|
# Wait until Forgotten_Hall stabled
|
||||||
|
timeout = Timer(2, count=4).start()
|
||||||
|
while 1:
|
||||||
|
if skip_first_screenshot:
|
||||||
|
skip_first_screenshot = False
|
||||||
|
else:
|
||||||
|
self.device.screenshot()
|
||||||
|
|
||||||
|
# End
|
||||||
|
if timeout.reached():
|
||||||
|
logger.warning('Wait until Forgotten_Hall stabled timeout')
|
||||||
|
return False
|
||||||
|
|
||||||
|
DUNGEON_NAV_LIST.load_rows(main=self)
|
||||||
|
|
||||||
|
# End
|
||||||
|
button = DUNGEON_NAV_LIST.keyword2button(KEYWORDS_DUNGEON_NAV.Forgotten_Hall)
|
||||||
|
if button:
|
||||||
|
# 513 is the top of the last row of DungeonNav
|
||||||
|
if button.area[1] > 513:
|
||||||
|
logger.info('DungeonNav row Forgotten_Hall stabled')
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _dungeon_nav_select(self, dungeon: DungeonList):
|
||||||
|
"""
|
||||||
|
Equivalent to `DUNGEON_NAV_LIST.select_row(dungeon.dungeon_nav, main=self)`
|
||||||
|
but with tricks to be faster
|
||||||
|
"""
|
||||||
|
# Check the first page
|
||||||
|
DUNGEON_NAV_LIST.load_rows(main=self)
|
||||||
|
if dungeon.dungeon_nav in [
|
||||||
|
KEYWORDS_DUNGEON_NAV.Simulated_Universe,
|
||||||
|
KEYWORDS_DUNGEON_NAV.Calyx_Golden,
|
||||||
|
KEYWORDS_DUNGEON_NAV.Calyx_Crimson,
|
||||||
|
KEYWORDS_DUNGEON_NAV.Stagnant_Shadow,
|
||||||
|
KEYWORDS_DUNGEON_NAV.Cavern_of_Corrosion,
|
||||||
|
]:
|
||||||
|
button = DUNGEON_NAV_LIST.keyword2button(dungeon.dungeon_nav)
|
||||||
|
if button:
|
||||||
|
DUNGEON_NAV_LIST.select_row(dungeon.dungeon_nav, main=self, insight=False)
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Check the second page
|
||||||
|
while 1:
|
||||||
|
DUNGEON_NAV_LIST.drag_page('down', main=self)
|
||||||
|
# No skip_first_screenshot since drag_page is just called
|
||||||
|
if self._dungeon_wait_until_forgotten_hall_stabled(skip_first_screenshot=False):
|
||||||
|
DUNGEON_NAV_LIST.select_row(dungeon.dungeon_nav, main=self, insight=False)
|
||||||
|
return True
|
||||||
|
|
||||||
def _dungeon_insight(self, dungeon: DungeonList):
|
def _dungeon_insight(self, dungeon: DungeonList):
|
||||||
"""
|
"""
|
||||||
@ -259,28 +338,17 @@ class DungeonUI(UI):
|
|||||||
# Reset search button
|
# Reset search button
|
||||||
DUNGEON_LIST.search_button = OCR_DUNGEON_LIST
|
DUNGEON_LIST.search_button = OCR_DUNGEON_LIST
|
||||||
|
|
||||||
if dungeon.is_Calyx_Golden:
|
if dungeon.is_Calyx_Golden \
|
||||||
DUNGEON_NAV_LIST.select_row(KEYWORDS_DUNGEON_NAV.Calyx_Golden, main=self)
|
or dungeon.is_Calyx_Crimson \
|
||||||
self._dungeon_insight(dungeon)
|
or dungeon.is_Stagnant_Shadow \
|
||||||
self._dungeon_enter(dungeon)
|
or dungeon.is_Stagnant_Shadow \
|
||||||
return True
|
or dungeon.is_Cavern_of_Corrosion:
|
||||||
if dungeon.is_Calyx_Crimson:
|
self._dungeon_nav_select(dungeon)
|
||||||
DUNGEON_NAV_LIST.select_row(KEYWORDS_DUNGEON_NAV.Calyx_Crimson, main=self)
|
|
||||||
self._dungeon_insight(dungeon)
|
|
||||||
self._dungeon_enter(dungeon)
|
|
||||||
return True
|
|
||||||
if dungeon.is_Stagnant_Shadow:
|
|
||||||
DUNGEON_NAV_LIST.select_row(KEYWORDS_DUNGEON_NAV.Stagnant_Shadow, main=self)
|
|
||||||
self._dungeon_insight(dungeon)
|
|
||||||
self._dungeon_enter(dungeon)
|
|
||||||
return True
|
|
||||||
if dungeon.is_Cavern_of_Corrosion:
|
|
||||||
DUNGEON_NAV_LIST.select_row(KEYWORDS_DUNGEON_NAV.Cavern_of_Corrosion, main=self)
|
|
||||||
self._dungeon_insight(dungeon)
|
self._dungeon_insight(dungeon)
|
||||||
self._dungeon_enter(dungeon)
|
self._dungeon_enter(dungeon)
|
||||||
return True
|
return True
|
||||||
if dungeon.is_Forgotten_Hall:
|
if dungeon.is_Forgotten_Hall:
|
||||||
DUNGEON_NAV_LIST.select_row(KEYWORDS_DUNGEON_NAV.Forgotten_Hall, main=self)
|
self._dungeon_nav_select(dungeon)
|
||||||
self._dungeon_insight(dungeon)
|
self._dungeon_insight(dungeon)
|
||||||
self._dungeon_enter(dungeon, enter_check_button=FORGOTTEN_HALL_CHECK)
|
self._dungeon_enter(dungeon, enter_check_button=FORGOTTEN_HALL_CHECK)
|
||||||
return True
|
return True
|
||||||
|
Loading…
Reference in New Issue
Block a user