Merge pull request #539 from LmeSzinc/dev

Bug fix
This commit is contained in:
LmeSzinc 2024-06-22 01:04:44 +08:00 committed by GitHub
commit 9ec27664b2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 323 additions and 82 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_
def generate(self):
self.load_keywords(['模拟宇宙', '拟造花萼(金)', '拟造花萼(赤)', '凝滞虚影', '侵蚀隧洞', '历战余响',
'忘却之庭', '虚构叙事'])
self.load_keywords(['饰品提取', '差分宇宙', '模拟宇宙',
'拟造花萼(金)', '拟造花萼(赤)', '凝滞虚影', '侵蚀隧洞', '历战余响',
'最近更新', '忘却之庭', '虚构叙事', '末日幻影'])
self.write_keywords(keyword_class='DungeonNav', output_file='./tasks/dungeon/keywords/nav.py')
self.load_keywords(['行动摘要', '生存索引', '每日实训', '模拟宇宙', '逐光捡金', '战术训练'])
self.write_keywords(keyword_class='DungeonTab', output_file='./tasks/dungeon/keywords/tab.py')

View File

@ -137,14 +137,14 @@ class Switch:
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
# Handle additional popups
if self.handle_additional(main=main):
continue
# Warning
if current == 'unknown':
if warning_show_timer.reached():
@ -165,3 +165,36 @@ class Switch:
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

View File

@ -163,26 +163,6 @@ SURVIVAL_INDEX_CLICK = ButtonWrapper(
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(
name='TAB_SEARCH',
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:
# ``` python -m dev_tools.keyword_extract ```
Simulated_Universe = DungeonNav(
Ornament_Extraction = DungeonNav(
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',
cn='模拟宇宙',
cht='模擬宇宙',
@ -13,7 +31,7 @@ Simulated_Universe = DungeonNav(
es='Universo Simulado',
)
Calyx_Golden = DungeonNav(
id=2,
id=4,
name='Calyx_Golden',
cn='拟造花萼(金)',
cht='擬造花萼(金)',
@ -22,7 +40,7 @@ Calyx_Golden = DungeonNav(
es='Cáliz (oro)',
)
Calyx_Crimson = DungeonNav(
id=3,
id=5,
name='Calyx_Crimson',
cn='拟造花萼(赤)',
cht='擬造花萼(赤)',
@ -31,7 +49,7 @@ Calyx_Crimson = DungeonNav(
es='Cáliz (carmesí)',
)
Stagnant_Shadow = DungeonNav(
id=4,
id=6,
name='Stagnant_Shadow',
cn='凝滞虚影',
cht='凝滯虛影',
@ -40,7 +58,7 @@ Stagnant_Shadow = DungeonNav(
es='Sombra paralizada',
)
Cavern_of_Corrosion = DungeonNav(
id=5,
id=7,
name='Cavern_of_Corrosion',
cn='侵蚀隧洞',
cht='侵蝕隧洞',
@ -49,7 +67,7 @@ Cavern_of_Corrosion = DungeonNav(
es='Caverna de la corrosión',
)
Echo_of_War = DungeonNav(
id=6,
id=8,
name='Echo_of_War',
cn='历战余响',
cht='歷戰餘響',
@ -57,8 +75,17 @@ Echo_of_War = DungeonNav(
jp='歴戦余韻',
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(
id=7,
id=10,
name='Forgotten_Hall',
cn='忘却之庭',
cht='忘卻之庭',
@ -67,7 +94,7 @@ Forgotten_Hall = DungeonNav(
es='Salón olvidado',
)
Pure_Fiction = DungeonNav(
id=8,
id=11,
name='Pure_Fiction',
cn='虚构叙事',
cht='虛構敘事',
@ -75,3 +102,12 @@ Pure_Fiction = DungeonNav(
jp='虚構叙事',
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 tasks.base.page import page_guide
from tasks.dungeon.assets.assets_dungeon_stamina import *
from tasks.dungeon.keywords import KEYWORDS_DUNGEON_TAB
from tasks.dungeon.ui import DungeonUI
@ -138,7 +139,7 @@ class DungeonStamina(DungeonUI):
"""
logger.hr('Immersifier store', level=2)
logger.info(f'Max store: {max_store}')
self.dungeon_goto_rogue()
self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index)
self.dungeon_update_stamina()
before = self.config.stored.Immersifier.value

View File

@ -139,6 +139,8 @@ class DungeonState(UI):
limit = 30
else:
limit = 60
if self.config.is_cloud_game:
limit = 120
# Double event is not yet finished, do it today as possible
update = get_server_next_update(self.config.Scheduler_ServerUpdate)

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_prepare import COMBAT_PREPARE
from tasks.dungeon.assets.assets_dungeon_ui import *
from tasks.dungeon.assets.assets_dungeon_ui_rogue import *
from tasks.dungeon.keywords import (
DungeonList,
DungeonNav,
@ -337,6 +338,17 @@ class DungeonUI(DungeonState):
logger.info('Treasures lightward loaded (event locked)')
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):
timeout = Timer(1, count=3).start()
while 1:
@ -350,14 +362,9 @@ class DungeonUI(DungeonState):
logger.warning('Wait until dungeon list loaded timeout')
return False
# 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:
if self._dungeon_list_button_has_content():
logger.info('Dungeon list loaded')
break
return True
def _dungeon_wait_until_echo_or_war_stabled(self, skip_first_screenshot=True):
"""
@ -431,12 +438,14 @@ class DungeonUI(DungeonState):
break
# 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) \
or DUNGEON_NAV_LIST.keyword2button(KEYWORDS_DUNGEON_NAV.Ornament_Extraction, show_warning=False):
# Going to use a faster method to navigate but can only start from list top
logger.info('DUNGEON_NAV_LIST at top')
# Update points if possible
if DUNGEON_NAV_LIST.is_row_selected(button, main=self):
self.dungeon_update_simuni()
# 2.3, No longer weekly points after Divergent Universe unlocked
# if DUNGEON_NAV_LIST.is_row_selected(button, main=self):
# self.dungeon_update_simuni()
# Treasures lightward is always at top
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):
@ -450,6 +459,8 @@ class DungeonUI(DungeonState):
# Check the first page
if nav in [
KEYWORDS_DUNGEON_NAV.Simulated_Universe,
KEYWORDS_DUNGEON_NAV.Divergent_Universe,
KEYWORDS_DUNGEON_NAV.Ornament_Extraction,
KEYWORDS_DUNGEON_NAV.Calyx_Golden,
KEYWORDS_DUNGEON_NAV.Calyx_Crimson,
KEYWORDS_DUNGEON_NAV.Stagnant_Shadow,
@ -698,26 +709,6 @@ class DungeonUI(DungeonState):
logger.attr('DungeonInteract', 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):
"""
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

@ -4,10 +4,9 @@ from scipy import signal
from module.base.base import ModuleBase
from module.base.button import ButtonWrapper
from module.base.decorator import cached_property, del_cached_property
from module.base.decorator import cached_property
from module.base.timer import Timer
from module.base.utils import Lines, area_center, area_offset, color_similarity_2d
from module.exception import ScriptError
from module.logger import logger
@ -71,6 +70,8 @@ class InventoryManager:
ERROR_LINES_TOLERANCE = (-10, 10)
COINCIDENT_POINT_ENCOURAGE_DISTANCE = 1.
MAXIMUM_ITEMS = 30
def __init__(self, main: ModuleBase, inventory: ButtonWrapper):
"""
max_count: expected max count of this inventory page
@ -248,7 +249,10 @@ class InventoryManager:
else:
self.selected = selected[0]
logger.info(f'Inventory: {len(self.items)} items, selected {self.selected}')
count = len(self.items)
logger.info(f'Inventory: {count} items, selected {self.selected}')
if count > self.MAXIMUM_ITEMS:
logger.warning('Too many inventory items detected')
def get_row_first(self, row=1, first=0) -> InventoryItem | None:
"""
@ -292,12 +296,9 @@ class InventoryManager:
def select(self, item, skip_first_screenshot=True):
logger.info(f'Inventory select {item}')
if isinstance(item, InventoryItem):
pass
loca = item.loca
else:
try:
item = self.items[item]
except KeyError:
raise ScriptError(f'Inventory select {item} but is not in current items')
loca = item
interval = Timer(2, count=6)
while 1:
@ -306,8 +307,16 @@ class InventoryManager:
else:
self.main.device.screenshot()
self.update()
if len(self.items) > self.MAXIMUM_ITEMS:
continue
try:
item = self.items[loca]
except KeyError:
logger.warning(f'Item {loca} is not in inventory, cannot select')
continue
# End
del_cached_property(item, 'is_selected')
if item.is_selected:
logger.info('Inventory item selected')
break
@ -315,8 +324,7 @@ class InventoryManager:
if interval.reached():
self.main.device.click(item)
interval.reset()
self.update()
continue
def wait_selected(self, skip_first_screenshot=True):
"""
@ -334,8 +342,10 @@ class InventoryManager:
self.main.device.screenshot()
self.update()
if self.selected is not None:
return True
if timeout.reached():
logger.warning('Wait inventory selected timeout')
return False
if len(self.items) > self.MAXIMUM_ITEMS:
continue
if self.selected is not None:
return True

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.dungeon import Simulated_Universe_World_1
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.rogue.assets.assets_rogue_entry import (
LEVEL_CONFIRM,
@ -103,7 +103,7 @@ class OcrRogueWorld(Ocr):
return 0
class RogueEntry(RouteBase, RogueRewardHandler, RoguePathHandler, DungeonUI):
class RogueEntry(RouteBase, RogueRewardHandler, RoguePathHandler, DungeonRogueUI):
def _rogue_world_wait(self, skip_first_screenshot=True):
"""
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):
"""
Pages:
in: page_guide, Survival_Index, Simulated_Universe
in: page_guide, Simulated_Universe, Simulated_Universe
out: page_rogue, is_page_rogue_main()
"""
logger.info('Rogue teleport')
@ -333,7 +333,8 @@ class RogueEntry(RouteBase, RogueRewardHandler, RoguePathHandler, DungeonUI):
if self.appear(page_guide.check_button, interval=2):
buttons = TELEPORT.match_multi_template(self.device.image)
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])
continue