Fix: Enter classic rogue

This commit is contained in:
LmeSzinc 2024-06-22 00:42:40 +08:00
parent f1d75967e7
commit ca1fb97a27
14 changed files with 260 additions and 65 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -554,8 +554,9 @@ class KeywordExtract:
yield hash_ yield hash_
def generate(self): def generate(self):
self.load_keywords(['模拟宇宙', '拟造花萼(金)', '拟造花萼(赤)', '凝滞虚影', '侵蚀隧洞', '历战余响', self.load_keywords(['饰品提取', '差分宇宙', '模拟宇宙',
'忘却之庭', '虚构叙事']) '拟造花萼(金)', '拟造花萼(赤)', '凝滞虚影', '侵蚀隧洞', '历战余响',
'最近更新', '忘却之庭', '虚构叙事', '末日幻影'])
self.write_keywords(keyword_class='DungeonNav', output_file='./tasks/dungeon/keywords/nav.py') self.write_keywords(keyword_class='DungeonNav', output_file='./tasks/dungeon/keywords/nav.py')
self.load_keywords(['行动摘要', '生存索引', '每日实训', '模拟宇宙', '逐光捡金', '战术训练']) self.load_keywords(['行动摘要', '生存索引', '每日实训', '模拟宇宙', '逐光捡金', '战术训练'])
self.write_keywords(keyword_class='DungeonTab', output_file='./tasks/dungeon/keywords/tab.py') self.write_keywords(keyword_class='DungeonTab', output_file='./tasks/dungeon/keywords/tab.py')

View File

@ -163,26 +163,6 @@ SURVIVAL_INDEX_CLICK = ButtonWrapper(
button=(332, 94, 370, 133), button=(332, 94, 370, 133),
), ),
) )
SURVIVAL_INDEX_OE_LOADED = ButtonWrapper(
name='SURVIVAL_INDEX_OE_LOADED',
share=Button(
file='./assets/share/dungeon/ui/SURVIVAL_INDEX_OE_LOADED.png',
area=(473, 207, 498, 232),
search=(453, 187, 518, 252),
color=(185, 135, 74),
button=(473, 207, 498, 232),
),
)
SURVIVAL_INDEX_SU_LOADED = ButtonWrapper(
name='SURVIVAL_INDEX_SU_LOADED',
share=Button(
file='./assets/share/dungeon/ui/SURVIVAL_INDEX_SU_LOADED.png',
area=(451, 286, 476, 302),
search=(449, 263, 489, 356),
color=(142, 150, 197),
button=(451, 286, 476, 302),
),
)
TAB_SEARCH = ButtonWrapper( TAB_SEARCH = ButtonWrapper(
name='TAB_SEARCH', name='TAB_SEARCH',
share=Button( share=Button(

View File

@ -0,0 +1,55 @@
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 ```
DIVERGENT_UNIVERSE_LOADED = ButtonWrapper(
name='DIVERGENT_UNIVERSE_LOADED',
share=Button(
file='./assets/share/dungeon/ui_rogue/DIVERGENT_UNIVERSE_LOADED.png',
area=(893, 608, 911, 628),
search=(873, 588, 931, 648),
color=(103, 102, 102),
button=(893, 608, 911, 628),
),
)
LAST_TELEPORT = ButtonWrapper(
name='LAST_TELEPORT',
share=Button(
file='./assets/share/dungeon/ui_rogue/LAST_TELEPORT.png',
area=(1018, 579, 1038, 599),
search=(998, 559, 1058, 619),
color=(84, 83, 85),
button=(1018, 579, 1038, 599),
),
)
SIMULATED_UNIVERSE_LOADED_CLASSIC = ButtonWrapper(
name='SIMULATED_UNIVERSE_LOADED_CLASSIC',
share=Button(
file='./assets/share/dungeon/ui_rogue/SIMULATED_UNIVERSE_LOADED_CLASSIC.png',
area=(549, 607, 572, 629),
search=(529, 587, 592, 649),
color=(147, 137, 157),
button=(549, 607, 572, 629),
),
)
SURVIVAL_INDEX_OE_LOADED = ButtonWrapper(
name='SURVIVAL_INDEX_OE_LOADED',
share=Button(
file='./assets/share/dungeon/ui_rogue/SURVIVAL_INDEX_OE_LOADED.png',
area=(473, 207, 498, 232),
search=(453, 187, 518, 252),
color=(185, 135, 74),
button=(473, 207, 498, 232),
),
)
SURVIVAL_INDEX_SU_LOADED = ButtonWrapper(
name='SURVIVAL_INDEX_SU_LOADED',
share=Button(
file='./assets/share/dungeon/ui_rogue/SURVIVAL_INDEX_SU_LOADED.png',
area=(451, 286, 476, 302),
search=(449, 263, 489, 356),
color=(142, 150, 197),
button=(451, 286, 476, 302),
),
)

View File

@ -3,8 +3,26 @@ from .classes import DungeonNav
# This file was auto-generated, do not modify it manually. To generate: # This file was auto-generated, do not modify it manually. To generate:
# ``` python -m dev_tools.keyword_extract ``` # ``` python -m dev_tools.keyword_extract ```
Simulated_Universe = DungeonNav( Ornament_Extraction = DungeonNav(
id=1, id=1,
name='Ornament_Extraction',
cn='饰品提取',
cht='飾品提取',
en='Ornament Extraction',
jp='オーナメント抽出',
es='Extracción de ornamentos',
)
Divergent_Universe = DungeonNav(
id=2,
name='Divergent_Universe',
cn='差分宇宙',
cht='差分宇宙',
en='Divergent Universe',
jp='階差宇宙',
es='Universo Diferenciado',
)
Simulated_Universe = DungeonNav(
id=3,
name='Simulated_Universe', name='Simulated_Universe',
cn='模拟宇宙', cn='模拟宇宙',
cht='模擬宇宙', cht='模擬宇宙',
@ -13,7 +31,7 @@ Simulated_Universe = DungeonNav(
es='Universo Simulado', es='Universo Simulado',
) )
Calyx_Golden = DungeonNav( Calyx_Golden = DungeonNav(
id=2, id=4,
name='Calyx_Golden', name='Calyx_Golden',
cn='拟造花萼(金)', cn='拟造花萼(金)',
cht='擬造花萼(金)', cht='擬造花萼(金)',
@ -22,7 +40,7 @@ Calyx_Golden = DungeonNav(
es='Cáliz (oro)', es='Cáliz (oro)',
) )
Calyx_Crimson = DungeonNav( Calyx_Crimson = DungeonNav(
id=3, id=5,
name='Calyx_Crimson', name='Calyx_Crimson',
cn='拟造花萼(赤)', cn='拟造花萼(赤)',
cht='擬造花萼(赤)', cht='擬造花萼(赤)',
@ -31,7 +49,7 @@ Calyx_Crimson = DungeonNav(
es='Cáliz (carmesí)', es='Cáliz (carmesí)',
) )
Stagnant_Shadow = DungeonNav( Stagnant_Shadow = DungeonNav(
id=4, id=6,
name='Stagnant_Shadow', name='Stagnant_Shadow',
cn='凝滞虚影', cn='凝滞虚影',
cht='凝滯虛影', cht='凝滯虛影',
@ -40,7 +58,7 @@ Stagnant_Shadow = DungeonNav(
es='Sombra paralizada', es='Sombra paralizada',
) )
Cavern_of_Corrosion = DungeonNav( Cavern_of_Corrosion = DungeonNav(
id=5, id=7,
name='Cavern_of_Corrosion', name='Cavern_of_Corrosion',
cn='侵蚀隧洞', cn='侵蚀隧洞',
cht='侵蝕隧洞', cht='侵蝕隧洞',
@ -49,7 +67,7 @@ Cavern_of_Corrosion = DungeonNav(
es='Caverna de la corrosión', es='Caverna de la corrosión',
) )
Echo_of_War = DungeonNav( Echo_of_War = DungeonNav(
id=6, id=8,
name='Echo_of_War', name='Echo_of_War',
cn='历战余响', cn='历战余响',
cht='歷戰餘響', cht='歷戰餘響',
@ -57,8 +75,17 @@ Echo_of_War = DungeonNav(
jp='歴戦余韻', jp='歴戦余韻',
es='Ecos de la guerra', es='Ecos de la guerra',
) )
Latest_Update = DungeonNav(
id=9,
name='Latest_Update',
cn='最近更新',
cht='最近更新',
en='Latest Update',
jp='最近更新',
es='Actualización reciente',
)
Forgotten_Hall = DungeonNav( Forgotten_Hall = DungeonNav(
id=7, id=10,
name='Forgotten_Hall', name='Forgotten_Hall',
cn='忘却之庭', cn='忘却之庭',
cht='忘卻之庭', cht='忘卻之庭',
@ -67,7 +94,7 @@ Forgotten_Hall = DungeonNav(
es='Salón olvidado', es='Salón olvidado',
) )
Pure_Fiction = DungeonNav( Pure_Fiction = DungeonNav(
id=8, id=11,
name='Pure_Fiction', name='Pure_Fiction',
cn='虚构叙事', cn='虚构叙事',
cht='虛構敘事', cht='虛構敘事',
@ -75,3 +102,12 @@ Pure_Fiction = DungeonNav(
jp='虚構叙事', jp='虚構叙事',
es='Pura ficción', es='Pura ficción',
) )
Apocalyptic_Shadow = DungeonNav(
id=12,
name='Apocalyptic_Shadow',
cn='末日幻影',
cht='末日幻影',
en='Apocalyptic Shadow',
jp='末日の幻影',
es='Espejismo apocalíptico',
)

View File

@ -4,6 +4,7 @@ from module.logger import logger
from module.ocr.ocr import Digit from module.ocr.ocr import Digit
from tasks.base.page import page_guide from tasks.base.page import page_guide
from tasks.dungeon.assets.assets_dungeon_stamina import * from tasks.dungeon.assets.assets_dungeon_stamina import *
from tasks.dungeon.keywords import KEYWORDS_DUNGEON_TAB
from tasks.dungeon.ui import DungeonUI from tasks.dungeon.ui import DungeonUI
@ -138,7 +139,7 @@ class DungeonStamina(DungeonUI):
""" """
logger.hr('Immersifier store', level=2) logger.hr('Immersifier store', level=2)
logger.info(f'Max store: {max_store}') logger.info(f'Max store: {max_store}')
self.dungeon_goto_rogue() self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index)
self.dungeon_update_stamina() self.dungeon_update_stamina()
before = self.config.stored.Immersifier.value before = self.config.stored.Immersifier.value

View File

@ -17,6 +17,7 @@ from tasks.base.page import page_guide
from tasks.combat.assets.assets_combat_interact import DUNGEON_COMBAT_INTERACT, DUNGEON_COMBAT_INTERACT_TEXT from tasks.combat.assets.assets_combat_interact import DUNGEON_COMBAT_INTERACT, DUNGEON_COMBAT_INTERACT_TEXT
from tasks.combat.assets.assets_combat_prepare import COMBAT_PREPARE from tasks.combat.assets.assets_combat_prepare import COMBAT_PREPARE
from tasks.dungeon.assets.assets_dungeon_ui import * from tasks.dungeon.assets.assets_dungeon_ui import *
from tasks.dungeon.assets.assets_dungeon_ui_rogue import *
from tasks.dungeon.keywords import ( from tasks.dungeon.keywords import (
DungeonList, DungeonList,
DungeonNav, DungeonNav,
@ -337,6 +338,17 @@ class DungeonUI(DungeonState):
logger.info('Treasures lightward loaded (event locked)') logger.info('Treasures lightward loaded (event locked)')
return True return True
def _dungeon_list_button_has_content(self):
# Check if having any content
# List background: 254, guild border: 225
r, g, b = cv2.split(self.image_crop(LIST_LOADED_CHECK, copy=False))
minimum = cv2.min(cv2.min(r, g), b)
minimum = inrange(minimum, lower=0, upper=180)
if minimum.size > 100:
return True
else:
return False
def _dungeon_wait_until_dungeon_list_loaded(self, skip_first_screenshot=True): def _dungeon_wait_until_dungeon_list_loaded(self, skip_first_screenshot=True):
timeout = Timer(1, count=3).start() timeout = Timer(1, count=3).start()
while 1: while 1:
@ -350,14 +362,9 @@ class DungeonUI(DungeonState):
logger.warning('Wait until dungeon list loaded timeout') logger.warning('Wait until dungeon list loaded timeout')
return False return False
# Check if having any content if self._dungeon_list_button_has_content():
# List background: 254, guild border: 225
r, g, b = cv2.split(self.image_crop(LIST_LOADED_CHECK, copy=False))
minimum = cv2.min(cv2.min(r, g), b)
minimum = inrange(minimum, lower=0, upper=180)
if minimum.size > 100:
logger.info('Dungeon list loaded') logger.info('Dungeon list loaded')
break return True
def _dungeon_wait_until_echo_or_war_stabled(self, skip_first_screenshot=True): def _dungeon_wait_until_echo_or_war_stabled(self, skip_first_screenshot=True):
""" """
@ -431,12 +438,13 @@ class DungeonUI(DungeonState):
break break
# Check if it's at the first page. # Check if it's at the first page.
if button := DUNGEON_NAV_LIST.keyword2button(KEYWORDS_DUNGEON_NAV.Simulated_Universe, show_warning=False): if DUNGEON_NAV_LIST.keyword2button(KEYWORDS_DUNGEON_NAV.Simulated_Universe, show_warning=False):
# Going to use a faster method to navigate but can only start from list top # Going to use a faster method to navigate but can only start from list top
logger.info('DUNGEON_NAV_LIST at top') logger.info('DUNGEON_NAV_LIST at top')
# Update points if possible # Update points if possible
if DUNGEON_NAV_LIST.is_row_selected(button, main=self): # 2.3, No longer weekly points after Divergent Universe unlocked
self.dungeon_update_simuni() # if DUNGEON_NAV_LIST.is_row_selected(button, main=self):
# self.dungeon_update_simuni()
# Treasures lightward is always at top # Treasures lightward is always at top
elif DUNGEON_NAV_LIST.keyword2button(KEYWORDS_DUNGEON_NAV.Forgotten_Hall, show_warning=False) \ elif DUNGEON_NAV_LIST.keyword2button(KEYWORDS_DUNGEON_NAV.Forgotten_Hall, show_warning=False) \
or DUNGEON_NAV_LIST.keyword2button(KEYWORDS_DUNGEON_NAV.Pure_Fiction, show_warning=False): or DUNGEON_NAV_LIST.keyword2button(KEYWORDS_DUNGEON_NAV.Pure_Fiction, show_warning=False):
@ -450,6 +458,8 @@ class DungeonUI(DungeonState):
# Check the first page # Check the first page
if nav in [ if nav in [
KEYWORDS_DUNGEON_NAV.Simulated_Universe, KEYWORDS_DUNGEON_NAV.Simulated_Universe,
KEYWORDS_DUNGEON_NAV.Divergent_Universe,
KEYWORDS_DUNGEON_NAV.Ornament_Extraction,
KEYWORDS_DUNGEON_NAV.Calyx_Golden, KEYWORDS_DUNGEON_NAV.Calyx_Golden,
KEYWORDS_DUNGEON_NAV.Calyx_Crimson, KEYWORDS_DUNGEON_NAV.Calyx_Crimson,
KEYWORDS_DUNGEON_NAV.Stagnant_Shadow, KEYWORDS_DUNGEON_NAV.Stagnant_Shadow,
@ -698,26 +708,6 @@ class DungeonUI(DungeonState):
logger.attr('DungeonInteract', None) logger.attr('DungeonInteract', None)
return None return None
def dungeon_goto_rogue(self):
"""
Goto Simulated Universe page but not pressing the TELEPORT button
Pages:
in: Any
out: page_guide, Survival_Index, Simulated_Universe
Examples:
self = DungeonUI('src')
self.device.screenshot()
self.dungeon_goto_rogue()
self._rogue_teleport()
"""
self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index)
if self.appear(SURVIVAL_INDEX_SU_LOADED):
logger.info('Already at nav Simulated_Universe')
else:
self._dungeon_nav_goto(KEYWORDS_DUNGEON_NAV.Simulated_Universe)
def dungeon_goto(self, dungeon: DungeonList): def dungeon_goto(self, dungeon: DungeonList):
""" """
Returns: Returns:

131
tasks/dungeon/ui_rogue.py Normal file
View File

@ -0,0 +1,131 @@
from module.base.timer import Timer
from module.base.utils import random_rectangle_vector
from module.logger import logger
from tasks.base.page import page_guide
from tasks.dungeon.assets.assets_dungeon_ui import *
from tasks.dungeon.assets.assets_dungeon_ui_rogue import *
from tasks.dungeon.keywords import KEYWORDS_DUNGEON_NAV, KEYWORDS_DUNGEON_TAB
from tasks.dungeon.ui import DungeonUI, SWITCH_DUNGEON_TAB
from tasks.forgotten_hall.assets.assets_forgotten_hall_ui import TELEPORT
class DungeonRogueUI(DungeonUI):
def dungeon_goto_rogue(self):
"""
Goto Simulated Universe page but not pressing the TELEPORT button
Pages:
in: Any
out: page_guide, Survival_Index, Simulated_Universe
Examples:
self = DungeonUI('src')
self.device.screenshot()
self.dungeon_goto_rogue()
self._rogue_teleport()
"""
logger.hr('Dungeon tab goto', level=2)
ui_switched = self.ui_ensure(page_guide)
SWITCH_DUNGEON_TAB.wait(main=self)
if (
self.appear(SIMULATED_UNIVERSE_CLICK)
or self.appear(SIMULATED_UNIVERSE_CHECK)
or self.appear(SURVIVAL_INDEX_OE_LOADED)
):
logger.info('Having rogue tab')
state = KEYWORDS_DUNGEON_TAB.Simulated_Universe
# Switch tab
tab_switched = SWITCH_DUNGEON_TAB.set(state, main=self)
if ui_switched or tab_switched:
logger.info(f'Tab goto {state}, wait until loaded')
self._dungeon_wait_until_rogue_loaded()
# Switch nav
self._dungeon_nav_goto(KEYWORDS_DUNGEON_NAV.Simulated_Universe)
# No idea how to wait list loaded
# List is not able to swipe without fully loaded
self.wait_until_stable(LIST_LOADED_CHECK)
# Swipe
self._dungeon_rogue_swipe_down()
else:
logger.info('No rogue tab')
state = KEYWORDS_DUNGEON_TAB.Survival_Index
# Switch tab
tab_switched = SWITCH_DUNGEON_TAB.set(state, main=self)
if ui_switched or tab_switched:
logger.info(f'Tab goto {state}, wait until loaded')
self._dungeon_wait_survival_index_loaded()
# Switch nav
if self.appear(SURVIVAL_INDEX_SU_LOADED):
logger.info('Already at nav Simulated_Universe')
else:
self._dungeon_nav_goto(KEYWORDS_DUNGEON_NAV.Simulated_Universe)
def _dungeon_wait_until_rogue_loaded(self, skip_first_screenshot=True):
"""
Returns:
bool: True if wait success, False if wait timeout.
Pages:
in: page_guide, Simulated_Universe
"""
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 rogue tab loaded timeout')
return False
if self.appear(DIVERGENT_UNIVERSE_LOADED):
logger.info('Rogue tab loaded, DIVERGENT_UNIVERSE_LOADED')
return True
# No LAST_TELEPORT, may hit teleport button of old screenshots from Ornament Extraction
# if self.appear(LAST_TELEPORT):
# logger.info('Rogue tab loaded, LAST_TELEPORT')
# return True
def _dungeon_rogue_swipe_down(self, skip_first_screenshot=True):
"""
Swipe down to find teleport button of classic rogue
Note that this method will change SIMULATED_UNIVERSE_LOADED_CLASSIC.search, original value should have a backup
"""
# Already having classic rogue entry insight
SIMULATED_UNIVERSE_LOADED_CLASSIC.load_search(OCR_DUNGEON_LIST.button)
if self.appear(SIMULATED_UNIVERSE_LOADED_CLASSIC):
buttons = TELEPORT.match_multi_template(self.device.image)
y = SIMULATED_UNIVERSE_LOADED_CLASSIC.button[1]
for button in buttons:
# And having a teleport button below
if button.button[1] > y:
logger.info('Classic rogue teleport already in sight')
return True
logger.info('Dungeon rogue swipe down')
interval = Timer(2, count=4)
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
self.device.screenshot()
# End
if self.appear(SIMULATED_UNIVERSE_LOADED_CLASSIC):
if self.appear(LAST_TELEPORT):
logger.info('Classic rogue teleport at end')
return True
# Swipe
if interval.reached():
p1, p2 = random_rectangle_vector(
(0, -450), box=OCR_DUNGEON_LIST.button, random_range=(-20, -20, 20, 20), padding=5)
self.device.swipe(p1, p2)
interval.reset()
if __name__ == '__main__':
self = DungeonRogueUI('src')
self.device.screenshot()
self.dungeon_goto_rogue()

View File

@ -15,7 +15,7 @@ from tasks.base.page import page_guide, page_item, page_main, page_rogue
from tasks.dungeon.keywords import DungeonList from tasks.dungeon.keywords import DungeonList
from tasks.dungeon.keywords.dungeon import Simulated_Universe_World_1 from tasks.dungeon.keywords.dungeon import Simulated_Universe_World_1
from tasks.dungeon.state import OcrSimUniPoint from tasks.dungeon.state import OcrSimUniPoint
from tasks.dungeon.ui import DungeonUI from tasks.dungeon.ui_rogue import DungeonRogueUI
from tasks.forgotten_hall.assets.assets_forgotten_hall_ui import TELEPORT from tasks.forgotten_hall.assets.assets_forgotten_hall_ui import TELEPORT
from tasks.rogue.assets.assets_rogue_entry import ( from tasks.rogue.assets.assets_rogue_entry import (
LEVEL_CONFIRM, LEVEL_CONFIRM,
@ -103,7 +103,7 @@ class OcrRogueWorld(Ocr):
return 0 return 0
class RogueEntry(RouteBase, RogueRewardHandler, RoguePathHandler, DungeonUI): class RogueEntry(RouteBase, RogueRewardHandler, RoguePathHandler, DungeonRogueUI):
def _rogue_world_wait(self, skip_first_screenshot=True): def _rogue_world_wait(self, skip_first_screenshot=True):
""" """
Wait is_page_rogue_main() fully loaded Wait is_page_rogue_main() fully loaded
@ -308,7 +308,7 @@ class RogueEntry(RouteBase, RogueRewardHandler, RoguePathHandler, DungeonUI):
def _rogue_teleport(self, skip_first_screenshot=True): def _rogue_teleport(self, skip_first_screenshot=True):
""" """
Pages: Pages:
in: page_guide, Survival_Index, Simulated_Universe in: page_guide, Simulated_Universe, Simulated_Universe
out: page_rogue, is_page_rogue_main() out: page_rogue, is_page_rogue_main()
""" """
logger.info('Rogue teleport') logger.info('Rogue teleport')
@ -333,7 +333,8 @@ class RogueEntry(RouteBase, RogueRewardHandler, RoguePathHandler, DungeonUI):
if self.appear(page_guide.check_button, interval=2): if self.appear(page_guide.check_button, interval=2):
buttons = TELEPORT.match_multi_template(self.device.image) buttons = TELEPORT.match_multi_template(self.device.image)
if len(buttons): if len(buttons):
buttons = sorted(buttons, key=lambda x: x.area[1]) # 2.3, classic rogue is always at bottom
buttons = sorted(buttons, key=lambda x: x.area[1], reverse=True)
self.device.click(buttons[0]) self.device.click(buttons[0])
continue continue