Refactor: Abstract StaminaStatus class to handle various states

This commit is contained in:
LmeSzinc 2024-08-11 02:27:40 +08:00
parent 20ec5d0316
commit 216a1bac2f
38 changed files with 464 additions and 315 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

View File

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,165 +0,0 @@
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 ```
EXTRACT_RESERVED_TRAILBLAZE_POWER = ButtonWrapper(
name='EXTRACT_RESERVED_TRAILBLAZE_POWER',
share=Button(
file='./assets/share/combat/fuel/EXTRACT_RESERVED_TRAILBLAZE_POWER.png',
area=(909, 506, 929, 526),
search=(889, 486, 949, 546),
color=(91, 91, 91),
button=(909, 506, 929, 526),
),
)
FUEL = ButtonWrapper(
name='FUEL',
share=Button(
file='./assets/share/combat/fuel/FUEL.png',
area=(592, 276, 688, 366),
search=(474, 271, 811, 396),
color=(123, 96, 134),
button=(592, 276, 688, 366),
),
)
FUEL_ENTRANCE = ButtonWrapper(
name='FUEL_ENTRANCE',
share=Button(
file='./assets/share/combat/fuel/FUEL_ENTRANCE.png',
area=(1035, 26, 1056, 48),
search=(1015, 6, 1076, 68),
color=(188, 180, 226),
button=(1035, 26, 1056, 48),
),
)
FUEL_MINUS = ButtonWrapper(
name='FUEL_MINUS',
share=Button(
file='./assets/share/combat/fuel/FUEL_MINUS.png',
area=(472, 425, 510, 450),
search=(452, 405, 530, 470),
color=(236, 236, 236),
button=(472, 425, 510, 450),
),
)
FUEL_PLUS = ButtonWrapper(
name='FUEL_PLUS',
share=Button(
file='./assets/share/combat/fuel/FUEL_PLUS.png',
area=(967, 426, 1005, 449),
search=(947, 406, 1025, 469),
color=(232, 232, 232),
button=(967, 426, 1005, 449),
),
)
FUEL_SELECTED = ButtonWrapper(
name='FUEL_SELECTED',
share=Button(
file='./assets/share/combat/fuel/FUEL_SELECTED.png',
area=(587, 271, 692, 368),
search=(474, 271, 811, 396),
color=(136, 112, 144),
button=(587, 271, 692, 368),
),
)
FUEL_SLIDER = ButtonWrapper(
name='FUEL_SLIDER',
share=Button(
file='./assets/share/combat/fuel/FUEL_SLIDER.png',
area=(561, 434, 916, 441),
search=(541, 414, 936, 461),
color=(215, 185, 154),
button=(561, 434, 916, 441),
),
)
OCR_EXTRACT_RESERVED_TRAILBLAZE_POWER_COUNT = ButtonWrapper(
name='OCR_EXTRACT_RESERVED_TRAILBLAZE_POWER_COUNT',
share=Button(
file='./assets/share/combat/fuel/OCR_EXTRACT_RESERVED_TRAILBLAZE_POWER_COUNT.png',
area=(425, 415, 688, 436),
search=(405, 395, 708, 456),
color=(192, 192, 192),
button=(425, 415, 688, 436),
),
)
OCR_FUEL = ButtonWrapper(
name='OCR_FUEL',
share=Button(
file='./assets/share/combat/fuel/OCR_FUEL.png',
area=(605, 369, 678, 386),
search=(585, 349, 698, 406),
color=(66, 66, 66),
button=(605, 369, 678, 386),
),
)
OCR_FUEL_COUNT = ButtonWrapper(
name='OCR_FUEL_COUNT',
share=Button(
file='./assets/share/combat/fuel/OCR_FUEL_COUNT.png',
area=(686, 409, 881, 425),
search=(666, 389, 901, 445),
color=(205, 205, 205),
button=(686, 409, 881, 425),
),
)
OCR_RESERVED_TRAILBLAZE_POWER = ButtonWrapper(
name='OCR_RESERVED_TRAILBLAZE_POWER',
share=Button(
file='./assets/share/combat/fuel/OCR_RESERVED_TRAILBLAZE_POWER.png',
area=(883, 29, 992, 44),
search=(863, 9, 1012, 64),
color=(51, 65, 65),
button=(883, 29, 992, 44),
),
)
RESERVED_MINUS = ButtonWrapper(
name='RESERVED_MINUS',
share=Button(
file='./assets/share/combat/fuel/RESERVED_MINUS.png',
area=(248, 474, 281, 498),
search=(228, 454, 301, 518),
color=(238, 238, 238),
button=(248, 474, 281, 498),
),
)
RESERVED_PLUS = ButtonWrapper(
name='RESERVED_PLUS',
share=Button(
file='./assets/share/combat/fuel/RESERVED_PLUS.png',
area=(938, 475, 974, 498),
search=(918, 455, 994, 518),
color=(232, 232, 232),
button=(938, 475, 974, 498),
),
)
RESERVED_SLIDER = ButtonWrapper(
name='RESERVED_SLIDER',
share=Button(
file='./assets/share/combat/fuel/RESERVED_SLIDER.png',
area=(334, 483, 873, 489),
search=(314, 463, 893, 509),
color=(212, 173, 130),
button=(334, 483, 873, 489),
),
)
RESERVED_TRAILBLAZE_POWER_ENTRANCE = ButtonWrapper(
name='RESERVED_TRAILBLAZE_POWER_ENTRANCE',
share=Button(
file='./assets/share/combat/fuel/RESERVED_TRAILBLAZE_POWER_ENTRANCE.png',
area=(895, 26, 916, 48),
search=(875, 6, 936, 68),
color=(154, 213, 214),
button=(895, 26, 916, 48),
),
)
USING_FUEL = ButtonWrapper(
name='USING_FUEL',
share=Button(
file='./assets/share/combat/fuel/USING_FUEL.png',
area=(263, 265, 363, 365),
search=(243, 245, 383, 385),
color=(161, 116, 129),
button=(263, 265, 363, 365),
),
)

View File

@ -20,16 +20,6 @@ COMBAT_PREPARE = ButtonWrapper(
button=(956, 640, 1225, 676), button=(956, 640, 1225, 676),
), ),
) )
OCR_TRAILBLAZE_POWER = ButtonWrapper(
name='OCR_TRAILBLAZE_POWER',
share=Button(
file='./assets/share/combat/prepare/OCR_TRAILBLAZE_POWER.png',
area=(998, 26, 1130, 48),
search=(978, 6, 1150, 68),
color=(77, 76, 87),
button=(998, 26, 1130, 48),
),
)
OCR_WAVE_COST = ButtonWrapper( OCR_WAVE_COST = ButtonWrapper(
name='OCR_WAVE_COST', name='OCR_WAVE_COST',
share=Button( share=Button(

View File

@ -0,0 +1,85 @@
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 ```
FUEL = ButtonWrapper(
name='FUEL',
share=Button(
file='./assets/share/combat/stamina/fuel/FUEL.png',
area=(592, 276, 688, 366),
search=(474, 271, 811, 396),
color=(123, 96, 134),
button=(592, 276, 688, 366),
),
)
FUEL_MINUS = ButtonWrapper(
name='FUEL_MINUS',
share=Button(
file='./assets/share/combat/stamina/fuel/FUEL_MINUS.png',
area=(472, 425, 510, 450),
search=(452, 405, 530, 470),
color=(236, 236, 236),
button=(472, 425, 510, 450),
),
)
FUEL_PLUS = ButtonWrapper(
name='FUEL_PLUS',
share=Button(
file='./assets/share/combat/stamina/fuel/FUEL_PLUS.png',
area=(967, 426, 1005, 449),
search=(947, 406, 1025, 469),
color=(232, 232, 232),
button=(967, 426, 1005, 449),
),
)
FUEL_SELECTED = ButtonWrapper(
name='FUEL_SELECTED',
share=Button(
file='./assets/share/combat/stamina/fuel/FUEL_SELECTED.png',
area=(587, 271, 692, 368),
search=(474, 271, 811, 396),
color=(136, 112, 144),
button=(587, 271, 692, 368),
),
)
FUEL_SLIDER = ButtonWrapper(
name='FUEL_SLIDER',
share=Button(
file='./assets/share/combat/stamina/fuel/FUEL_SLIDER.png',
area=(561, 434, 916, 441),
search=(541, 414, 936, 461),
color=(215, 185, 154),
button=(561, 434, 916, 441),
),
)
OCR_FUEL = ButtonWrapper(
name='OCR_FUEL',
share=Button(
file='./assets/share/combat/stamina/fuel/OCR_FUEL.png',
area=(605, 369, 678, 386),
search=(585, 349, 698, 406),
color=(66, 66, 66),
button=(605, 369, 678, 386),
),
)
OCR_FUEL_COUNT = ButtonWrapper(
name='OCR_FUEL_COUNT',
share=Button(
file='./assets/share/combat/stamina/fuel/OCR_FUEL_COUNT.png',
area=(686, 409, 881, 425),
search=(666, 389, 901, 445),
color=(205, 205, 205),
button=(686, 409, 881, 425),
),
)
USING_FUEL = ButtonWrapper(
name='USING_FUEL',
share=Button(
file='./assets/share/combat/stamina/fuel/USING_FUEL.png',
area=(263, 265, 363, 365),
search=(243, 245, 383, 385),
color=(161, 116, 129),
button=(263, 265, 363, 365),
),
)

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 ```
EXTRACT_RESERVED_TRAILBLAZE_POWER = ButtonWrapper(
name='EXTRACT_RESERVED_TRAILBLAZE_POWER',
share=Button(
file='./assets/share/combat/stamina/reserved/EXTRACT_RESERVED_TRAILBLAZE_POWER.png',
area=(909, 506, 929, 526),
search=(889, 486, 949, 546),
color=(91, 91, 91),
button=(909, 506, 929, 526),
),
)
OCR_EXTRACT_RESERVED_TRAILBLAZE_POWER_COUNT = ButtonWrapper(
name='OCR_EXTRACT_RESERVED_TRAILBLAZE_POWER_COUNT',
share=Button(
file='./assets/share/combat/stamina/reserved/OCR_EXTRACT_RESERVED_TRAILBLAZE_POWER_COUNT.png',
area=(425, 415, 688, 436),
search=(405, 395, 708, 456),
color=(192, 192, 192),
button=(425, 415, 688, 436),
),
)
RESERVED_MINUS = ButtonWrapper(
name='RESERVED_MINUS',
share=Button(
file='./assets/share/combat/stamina/reserved/RESERVED_MINUS.png',
area=(248, 474, 281, 498),
search=(228, 454, 301, 518),
color=(238, 238, 238),
button=(248, 474, 281, 498),
),
)
RESERVED_PLUS = ButtonWrapper(
name='RESERVED_PLUS',
share=Button(
file='./assets/share/combat/stamina/reserved/RESERVED_PLUS.png',
area=(938, 475, 974, 498),
search=(918, 455, 994, 518),
color=(232, 232, 232),
button=(938, 475, 974, 498),
),
)
RESERVED_SLIDER = ButtonWrapper(
name='RESERVED_SLIDER',
share=Button(
file='./assets/share/combat/stamina/reserved/RESERVED_SLIDER.png',
area=(334, 483, 873, 489),
search=(314, 463, 893, 509),
color=(212, 173, 130),
button=(334, 483, 873, 489),
),
)

View File

@ -0,0 +1,75 @@
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 ```
ICON_SEARCH = ButtonWrapper(
name='ICON_SEARCH',
share=Button(
file='./assets/share/combat/stamina/status/ICON_SEARCH.png',
area=(568, 8, 1265, 66),
search=(548, 0, 1280, 86),
color=(71, 70, 100),
button=(568, 8, 1265, 66),
),
)
IMMERSIFIER_ICON = ButtonWrapper(
name='IMMERSIFIER_ICON',
share=Button(
file='./assets/share/combat/stamina/status/IMMERSIFIER_ICON.png',
area=(1047, 26, 1066, 49),
search=(1027, 6, 1086, 69),
color=(138, 127, 117),
button=(1047, 26, 1066, 49),
),
)
IMMERSIFIER_OCR = ButtonWrapper(
name='IMMERSIFIER_OCR',
share=Button(
file='./assets/share/combat/stamina/status/IMMERSIFIER_OCR.png',
area=(1049, 26, 1151, 48),
search=(1029, 6, 1171, 68),
color=(64, 61, 61),
button=(1049, 26, 1151, 48),
),
)
RESERVED_ICON = ButtonWrapper(
name='RESERVED_ICON',
share=Button(
file='./assets/share/combat/stamina/status/RESERVED_ICON.png',
area=(895, 26, 916, 48),
search=(875, 6, 936, 68),
color=(155, 212, 215),
button=(895, 26, 916, 48),
),
)
RESERVED_OCR = ButtonWrapper(
name='RESERVED_OCR',
share=Button(
file='./assets/share/combat/stamina/status/RESERVED_OCR.png',
area=(895, 26, 999, 48),
search=(875, 6, 1019, 68),
color=(50, 69, 83),
button=(895, 26, 999, 48),
),
)
STAMINA_ICON = ButtonWrapper(
name='STAMINA_ICON',
share=Button(
file='./assets/share/combat/stamina/status/STAMINA_ICON.png',
area=(873, 26, 894, 48),
search=(853, 6, 914, 68),
color=(188, 180, 226),
button=(873, 26, 894, 48),
),
)
STAMINA_OCR = ButtonWrapper(
name='STAMINA_OCR',
share=Button(
file='./assets/share/combat/stamina/status/STAMINA_OCR.png',
area=(873, 26, 1013, 48),
search=(853, 6, 1033, 68),
color=(66, 64, 88),
button=(873, 26, 1013, 48),
),
)

View File

@ -54,7 +54,7 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSuppo
# Check limits # Check limits
if self.config.stored.TrailblazePower.value < self.combat_wave_cost: if self.config.stored.TrailblazePower.value < self.combat_wave_cost:
return self._try_get_more_trablaize_power(self.config.stored.TrailblazePower.value, self.combat_wave_cost) return self._try_get_more_trablaize_power(self.combat_wave_cost)
if self.combat_waves <= 0: if self.combat_waves <= 0:
logger.info('Combat wave limited, cannot continue combat') logger.info('Combat wave limited, cannot continue combat')
return False return False
@ -225,7 +225,7 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSuppo
logger.info(f'Current has {current}, combat costs {self.combat_wave_cost}, can run again') logger.info(f'Current has {current}, combat costs {self.combat_wave_cost}, can run again')
return True return True
else: else:
return self._try_get_more_trablaize_power(current, self.combat_wave_cost * self.combat_waves) return self._try_get_more_trablaize_power(self.combat_wave_cost * self.combat_waves)
elif self.combat_wave_cost <= 0: elif self.combat_wave_cost <= 0:
logger.info(f'Free combat, combat costs {self.combat_wave_cost}, can not run again') logger.info(f'Free combat, combat costs {self.combat_wave_cost}, can not run again')
return False return False
@ -234,20 +234,15 @@ class Combat(CombatInteract, CombatPrepare, CombatState, CombatTeam, CombatSuppo
logger.info(f'Current has {current}, combat costs {self.combat_wave_cost}, can run again') logger.info(f'Current has {current}, combat costs {self.combat_wave_cost}, can run again')
return True return True
else: else:
return self._try_get_more_trablaize_power(current, self.combat_wave_cost * self.combat_waves) return self._try_get_more_trablaize_power(self.combat_wave_cost * self.combat_waves)
def _try_get_more_trablaize_power(self, current, cost):
if self.config.TrailblazePower_ExtractReservedTrailblazePower:
logger.info('Extract reserved trailblaze power to get more trailblaze power')
if self.extract_reserved_trailblaze_power(current):
self.combat_get_trailblaze_power()
self.get_interval_timer(COMBAT_EXIT).wait()
if self.config.TrailblazePower_UseFuel:
logger.info('Use fuel to get more trailblaze power')
if self.use_fuel(current):
self.combat_get_trailblaze_power()
self.get_interval_timer(COMBAT_AGAIN).wait()
def _try_get_more_trablaize_power(self, cost):
self.extract_stamina(
update=False,
use_reserved=self.config.TrailblazePower_ExtractReservedTrailblazePower,
use_fuel=self.config.TrailblazePower_UseFuel
)
current = self.config.stored.TrailblazePower.value
if current >= cost: if current >= cost:
return True return True
else: else:

View File

@ -4,14 +4,16 @@ from module.base.utils import area_offset, crop
from module.logger import logger from module.logger import logger
from module.ocr.ocr import Digit from module.ocr.ocr import Digit
from tasks.base.assets.assets_base_popup import GET_REWARD, POPUP_CANCEL, POPUP_CONFIRM from tasks.base.assets.assets_base_popup import GET_REWARD, POPUP_CANCEL, POPUP_CONFIRM
from tasks.base.ui import UI
from tasks.combat.assets.assets_combat_finish import COMBAT_AGAIN, COMBAT_EXIT from tasks.combat.assets.assets_combat_finish import COMBAT_AGAIN, COMBAT_EXIT
from tasks.combat.assets.assets_combat_fuel import *
from tasks.combat.assets.assets_combat_prepare import COMBAT_PREPARE from tasks.combat.assets.assets_combat_prepare import COMBAT_PREPARE
from tasks.combat.assets.assets_combat_stamina_fuel import *
from tasks.combat.assets.assets_combat_stamina_reserved import *
from tasks.combat.assets.assets_combat_stamina_status import *
from tasks.combat.stamina_status import StaminaStatus
from tasks.item.slider import Slider from tasks.item.slider import Slider
class Fuel(UI): class Fuel(StaminaStatus):
fuel_trailblaze_power = 60 fuel_trailblaze_power = 60
def _use_fuel_finish(self): def _use_fuel_finish(self):
@ -26,7 +28,7 @@ class Fuel(UI):
return True return True
if self.appear(COMBAT_PREPARE): if self.appear(COMBAT_PREPARE):
if self.image_color_count(COMBAT_PREPARE.button, color=(230, 230, 230), threshold=240, count=400): if self.image_color_count(COMBAT_PREPARE.button, color=(230, 230, 230), threshold=240, count=400):
logger.info(f'Use fuel finished at COMBAT_AGAIN') logger.info(f'Use fuel finished at COMBAT_PREPARE')
return True return True
return False return False
@ -87,7 +89,7 @@ class Fuel(UI):
timer = self.get_interval_timer(COMBAT_EXIT, interval=5, renew=True) timer = self.get_interval_timer(COMBAT_EXIT, interval=5, renew=True)
timer.set_current(4.4) timer.set_current(4.4)
def extract_reserved_trailblaze_power(self, current, skip_first_screenshot=True): def extract_reserved_trailblaze_power(self, skip_first_screenshot=True):
""" """
Extract reserved trailblaze power from previous combat. Extract reserved trailblaze power from previous combat.
@ -95,11 +97,8 @@ class Fuel(UI):
bool: If extracted bool: If extracted
""" """
logger.info('Extract reserved trailblaze power') logger.info('Extract reserved trailblaze power')
reserved = Digit(OCR_RESERVED_TRAILBLAZE_POWER).ocr_single_line(self.device.image)
if reserved <= 0:
logger.info('No reserved trailblaze power')
return False
RESERVED_ICON.load_search(ICON_SEARCH.area)
self.interval_clear([POPUP_CONFIRM, POPUP_CANCEL, GET_REWARD]) self.interval_clear([POPUP_CONFIRM, POPUP_CANCEL, GET_REWARD])
while 1: while 1:
if skip_first_screenshot: if skip_first_screenshot:
@ -111,12 +110,14 @@ class Fuel(UI):
break break
if self.appear_then_click(EXTRACT_RESERVED_TRAILBLAZE_POWER): if self.appear_then_click(EXTRACT_RESERVED_TRAILBLAZE_POWER):
continue continue
if self.appear_then_click(RESERVED_TRAILBLAZE_POWER_ENTRANCE): if self.appear_then_click(RESERVED_ICON):
continue continue
count = min(reserved, self.config.stored.TrailblazePower.FIXED_TOTAL - current) # No need, amount will be set by game client
logger.info(f'Having {reserved} reserved, going to use {count}') # count = min(reserved, self.config.stored.TrailblazePower.FIXED_TOTAL - current)
self.set_reserved_trailblaze_power(count, total=reserved) # logger.info(f'Having {reserved} reserved, going to use {count}')
# self.set_reserved_trailblaze_power(count, total=reserved)
self._fuel_confirm() self._fuel_confirm()
return True return True
@ -160,6 +161,7 @@ class Fuel(UI):
logger.info("Use Fuel") logger.info("Use Fuel")
STAMINA_ICON.load_search(ICON_SEARCH.area)
timeout = Timer(1, count=3) timeout = Timer(1, count=3)
has_fuel = False has_fuel = False
while 1: while 1:
@ -182,7 +184,7 @@ class Fuel(UI):
if self.appear_then_click(FUEL): if self.appear_then_click(FUEL):
has_fuel = True has_fuel = True
continue continue
if not self.appear(POPUP_CONFIRM) and self.appear_then_click(FUEL_ENTRANCE): if not self.appear(POPUP_CONFIRM) and self.appear_then_click(STAMINA_ICON):
continue continue
offset = FUEL_SELECTED.button_offset offset = FUEL_SELECTED.button_offset
@ -215,3 +217,39 @@ class Fuel(UI):
self.set_fuel_count(use) self.set_fuel_count(use)
self._fuel_confirm() self._fuel_confirm()
return True return True
def extract_stamina(self, update=True, use_reserved=True, use_fuel=False):
"""
Args:
update:
use_reserved:
use_fuel:
Returns:
bool: If used
"""
if not use_reserved and not use_fuel:
return False
logger.hr('Extract stamina', level=2)
logger.info(f'Extract stamina, reserved={use_reserved}, fuel={use_fuel}')
if update:
self.update_stamina_status()
used = False
if use_reserved:
if self.config.stored.Reserved.value <= 0:
logger.info('No reserved stamina')
else:
self.extract_reserved_trailblaze_power()
used = True
self.update_stamina_status()
self.get_interval_timer(COMBAT_AGAIN).wait()
if use_fuel:
self.use_fuel(current=self.config.stored.TrailblazePower.value)
used = True
self.update_stamina_status()
self.get_interval_timer(COMBAT_AGAIN).wait()
return used

View File

@ -1,35 +1,20 @@
import re
import module.config.server as server import module.config.server as server
from module.base.timer import Timer from module.base.timer import Timer
from module.base.utils import color_similar, get_color from module.base.utils import color_similar, get_color
from module.logger import logger from module.logger import logger
from module.ocr.ocr import Digit, DigitCounter from module.ocr.ocr import Digit
from tasks.base.ui import UI
from tasks.combat.assets.assets_combat_prepare import ( from tasks.combat.assets.assets_combat_prepare import (
OCR_TRAILBLAZE_POWER,
OCR_WAVE_COST, OCR_WAVE_COST,
OCR_WAVE_COUNT, OCR_WAVE_COUNT,
WAVE_MINUS, WAVE_MINUS,
WAVE_PLUS, WAVE_SLIDER WAVE_PLUS,
WAVE_SLIDER
) )
from tasks.combat.stamina_status import StaminaStatus
from tasks.item.slider import Slider from tasks.item.slider import Slider
class TrailblazePowerOcr(DigitCounter): class CombatPrepare(StaminaStatus):
def after_process(self, result):
result = super().after_process(result)
# The trailblaze power icon is recognized as 买
# OCR_TRAILBLAZE_POWER includes the icon because the length varies by value
result = re.sub(r'[买米装:()]', '', result)
# 61240 -> 6/240
result = re.sub(r'1240$', '/240', result)
# 0*0/24 -> 0/240
result = re.sub(r'24$', '240', result)
return result
class CombatPrepare(UI):
# Current combat waves, # Current combat waves,
combat_waves = 1 combat_waves = 1
# Limit combat runs, 0 means no limit. # Limit combat runs, 0 means no limit.
@ -78,20 +63,20 @@ class CombatPrepare(UI):
else: else:
self.device.screenshot() self.device.screenshot()
current, _, total = TrailblazePowerOcr(OCR_TRAILBLAZE_POWER).ocr_single_line(self.device.image) data = self.update_stamina_status(image=self.device.image)
# Empty result if data.stamina is None:
if total == 0:
continue continue
# Confirm if it is > 240, sometimes just OCR errors # Confirm if it is > 240, sometimes just OCR errors
if current > 240 and timeout.reached(): # if current > 240 and timeout.reached():
# break
if expect_reduce and timeout.reached():
break break
if expect_reduce and current >= before: if expect_reduce and data.stamina >= before:
continue continue
if current <= 240: if data.stamina <= 240:
break break
self.config.stored.TrailblazePower.value = current return data.stamina
return current
def combat_get_wave_cost(self, skip_first_screenshot=True): def combat_get_wave_cost(self, skip_first_screenshot=True):
""" """

View File

@ -0,0 +1,161 @@
import re
from pydantic import BaseModel
from module.base.timer import Timer
from module.base.utils import crop
from module.logger import logger
from module.ocr.ocr import Digit, DigitCounter
from tasks.base.ui import UI
from tasks.combat.assets.assets_combat_stamina_status import *
class StaminaOcr(DigitCounter):
def after_process(self, result):
result = super().after_process(result)
# The trailblaze power icon is recognized as 买
# OCR_TRAILBLAZE_POWER includes the icon because the length varies by value
result = re.sub(r'[买米装来:()]', '', result)
# 61240 -> 6/240
result = re.sub(r'1240$', '/240', result)
# 0*0/24 -> 0/240
result = re.sub(r'24$', '240', result)
return result
class ReservedOcr(Digit):
pass
class ImmersifierOcr(DigitCounter):
pass
class DataStaminaStatus(BaseModel):
stamina: int | None
reserved: int | None
immersifier: int | None
class StaminaStatus(UI):
@staticmethod
def get_stamina_status(image) -> DataStaminaStatus:
"""
Update trailblaze power, stored trailblaze power, immersifier
Returns:
int: Stamina, or None if stamina not displayed or error on OCR
int: Reserved stamina
int: Immersifier
"""
for button in [STAMINA_ICON, RESERVED_ICON, IMMERSIFIER_ICON]:
button.load_search(ICON_SEARCH.area)
stamina = None
if STAMINA_ICON.match_template(image):
STAMINA_OCR.load_offset(STAMINA_ICON)
im = crop(image, STAMINA_OCR.button, copy=False)
stamina, _, total = StaminaOcr(STAMINA_OCR).ocr_single_line(im, direct_ocr=True)
if total > 240 or total == 0:
logger.warning(f'Unexpected stamina total: {total}')
stamina = None
reserved = None
if RESERVED_ICON.match_template(image):
RESERVED_OCR.load_offset(RESERVED_ICON)
im = crop(image, RESERVED_OCR.button, copy=False)
reserved = ReservedOcr(RESERVED_OCR).ocr_single_line(im, direct_ocr=True)
if reserved > 2400:
logger.warning(f'Unexpected reserved value: {reserved}')
reserved = None
immersifier = None
if IMMERSIFIER_ICON.match_template(image):
IMMERSIFIER_OCR.load_offset(IMMERSIFIER_ICON)
im = crop(image, IMMERSIFIER_OCR.button, copy=False)
immersifier, _, total = StaminaOcr(IMMERSIFIER_OCR).ocr_single_line(im, direct_ocr=True)
if total != 8:
logger.warning(f'Unexpected immersifier total: {total}')
immersifier = None
return DataStaminaStatus(
stamina=stamina,
reserved=reserved,
immersifier=immersifier,
)
def update_stamina_status(
self,
image=None,
skip_first_screenshot=True,
expect_stamina=False,
expect_reserved=False,
expect_immersifier=False,
) -> DataStaminaStatus:
"""
Update stamina status with retry
Args:
image: Detect given image only, no new screenshot will be taken
and all expect_* are considered False
skip_first_screenshot:
expect_stamina:
True to expect stamina exists, retry detect if it wasn't
expect_reserved:
expect_immersifier:
Pages:
in: page_guild, Survival_Index, Simulated_Universe
or page_rogue, LEVEL_CONFIRM
or rogue, REWARD_CLOSE
"""
timeout = Timer(1, count=2).start()
if image is None:
image = self.device.image
use_cached_image = False
else:
skip_first_screenshot = True
use_cached_image = True
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
self.device.screenshot()
image = self.device.image
# Timeout
if timeout.reached():
logger.warning('dungeon_update_stamina() timeout')
return DataStaminaStatus(
stamina=None,
reserved=None,
immersifier=None,
)
# Ocr
status = self.get_stamina_status(image)
valid = True
if expect_stamina and status.stamina is None:
valid = False
if expect_reserved and status.reserved is None:
valid = False
if expect_immersifier and status.immersifier is None:
valid = False
if status.stamina is None and status.reserved is None and status.immersifier is None:
logger.warning('update_stamina_status: No icon detected')
valid = False
# Write config
with self.config.multi_set():
if status.stamina is not None:
self.config.stored.TrailblazePower.value = status.stamina
if status.reserved is not None:
self.config.stored.Reserved.value = status.reserved
if status.immersifier is not None:
self.config.stored.Immersifier.value = status.reserved
if use_cached_image or valid:
return status
else:
continue

View File

@ -41,16 +41,6 @@ AMOUNT_PLUS = ButtonWrapper(
), ),
], ],
) )
ENTER_IMMERSIFIER = ButtonWrapper(
name='ENTER_IMMERSIFIER',
share=Button(
file='./assets/share/dungeon/stamina/ENTER_IMMERSIFIER.png',
area=(1047, 26, 1066, 49),
search=(1027, 6, 1086, 69),
color=(138, 127, 117),
button=(1049, 26, 1151, 48),
),
)
IMMERSIFIER_CHECK = ButtonWrapper( IMMERSIFIER_CHECK = ButtonWrapper(
name='IMMERSIFIER_CHECK', name='IMMERSIFIER_CHECK',
share=Button( share=Button(

View File

@ -3,6 +3,7 @@ from module.base.timer import Timer
from module.logger import logger 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.combat.assets.assets_combat_stamina_status import ICON_SEARCH, IMMERSIFIER_ICON
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.keywords import KEYWORDS_DUNGEON_TAB
from tasks.dungeon.ui import DungeonUI from tasks.dungeon.ui import DungeonUI
@ -16,6 +17,7 @@ class DungeonStamina(DungeonUI):
out: IMMERSIFIER_CHECK out: IMMERSIFIER_CHECK
""" """
logger.info('Enter immersifier') logger.info('Enter immersifier')
IMMERSIFIER_ICON.load_search(ICON_SEARCH.area)
while 1: while 1:
if skip_first_screenshot: if skip_first_screenshot:
skip_first_screenshot = False skip_first_screenshot = False
@ -24,7 +26,7 @@ class DungeonStamina(DungeonUI):
if self.appear(IMMERSIFIER_CHECK): if self.appear(IMMERSIFIER_CHECK):
break break
if self.appear_then_click(ENTER_IMMERSIFIER, interval=2): if self.appear_then_click(IMMERSIFIER_ICON, interval=2):
continue continue
def _immersifier_exit(self, skip_first_screenshot=True): def _immersifier_exit(self, skip_first_screenshot=True):
@ -140,7 +142,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_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index) self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index)
self.dungeon_update_stamina() self.update_stamina_status()
before = self.config.stored.Immersifier.value before = self.config.stored.Immersifier.value
if self.config.stored.Immersifier.is_full(): if self.config.stored.Immersifier.is_full():
@ -159,7 +161,7 @@ class DungeonStamina(DungeonUI):
self._immersifier_enter() self._immersifier_enter()
self._item_amount_set(amount, ocr_button=OCR_IMMERSIFIER_AMOUNT) self._item_amount_set(amount, ocr_button=OCR_IMMERSIFIER_AMOUNT)
self._item_confirm() self._item_confirm()
self.dungeon_update_stamina() self.update_stamina_status()
diff = self.config.stored.Immersifier.value - before diff = self.config.stored.Immersifier.value - before
logger.info(f'Stored {diff} immersifiers') logger.info(f'Stored {diff} immersifiers')
return diff return diff

View File

@ -1,14 +1,13 @@
from datetime import timedelta from datetime import timedelta
from module.base.base import ModuleBase from module.base.base import ModuleBase
from module.base.timer import Timer
from module.base.utils import crop from module.base.utils import crop
from module.config.stored.classes import now from module.config.stored.classes import now
from module.config.utils import DEFAULT_TIME, get_server_next_monday_update, get_server_next_update from module.config.utils import DEFAULT_TIME, get_server_next_monday_update, get_server_next_update
from module.logger import logger from module.logger import logger
from module.ocr.ocr import DigitCounter from module.ocr.ocr import DigitCounter
from tasks.base.ui import UI from tasks.combat.stamina_status import StaminaStatus
from tasks.dungeon.assets.assets_dungeon_state import OCR_SIMUNI_POINT, OCR_SIMUNI_POINT_OFFSET, OCR_STAMINA from tasks.dungeon.assets.assets_dungeon_state import OCR_SIMUNI_POINT, OCR_SIMUNI_POINT_OFFSET
from tasks.dungeon.keywords import DungeonList from tasks.dungeon.keywords import DungeonList
@ -19,7 +18,7 @@ class OcrSimUniPoint(DigitCounter):
return result return result
class DungeonState(UI): class DungeonState(StaminaStatus):
def dungeon_get_simuni_point(self, image=None) -> int: def dungeon_get_simuni_point(self, image=None) -> int:
""" """
Page: Page:
@ -48,67 +47,6 @@ class DungeonState(UI):
logger.warning(f'Invalid SimulatedUniverse points: {value}/{total}') logger.warning(f'Invalid SimulatedUniverse points: {value}/{total}')
return 0 return 0
def dungeon_update_stamina(self, image=None, skip_first_screenshot=True):
"""
Returns:
bool: If success
Pages:
in: page_guild, Survival_Index, Simulated_Universe
or page_rogue, LEVEL_CONFIRM
or rogue, REWARD_CLOSE
"""
ocr = DigitCounter(OCR_STAMINA)
timeout = Timer(1, count=2).start()
if image is None:
image = self.device.image
use_cached_image = False
else:
skip_first_screenshot = True
use_cached_image = True
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
self.device.screenshot()
image = self.device.image
stamina = (0, 0, 0)
immersifier = (0, 0, 0)
if timeout.reached():
logger.warning('dungeon_update_stamina() timeout')
return False
for row in ocr.detect_and_ocr(image):
if row.ocr_text.isdigit():
continue
if row.ocr_text == '+':
continue
if not ocr.is_format_matched(row.ocr_text):
continue
data = ocr.format_result(row.ocr_text)
if data[2] == self.config.stored.TrailblazePower.FIXED_TOTAL:
stamina = data
if data[2] == self.config.stored.Immersifier.FIXED_TOTAL:
immersifier = data
if stamina[2] > 0 and immersifier[2] > 0:
break
if use_cached_image:
logger.info('dungeon_update_stamina() ended')
return
stamina = stamina[0]
immersifier = immersifier[0]
logger.attr('TrailblazePower', stamina)
logger.attr('Imersifier', immersifier)
with self.config.multi_set():
self.config.stored.TrailblazePower.value = stamina
self.config.stored.Immersifier.value = immersifier
return True
def dungeon_update_simuni(self): def dungeon_update_simuni(self):
""" """
Update rogue weekly points, stamina, immersifier Update rogue weekly points, stamina, immersifier
@ -122,8 +60,8 @@ class DungeonState(UI):
def func(image): def func(image):
logger.info('Update thread start') logger.info('Update thread start')
with self.config.multi_set(): with self.config.multi_set():
self.dungeon_get_simuni_point(image) # self.dungeon_get_simuni_point(image)
self.dungeon_update_stamina(image) self.update_stamina_status(image)
ModuleBase.worker.submit(func, self.device.image) ModuleBase.worker.submit(func, self.device.image)

View File

@ -130,7 +130,7 @@ class OrnamentCombat(Dungeon, RouteLoader, DungeonState):
after = before after = before
for _ in range(3): for _ in range(3):
self.dungeon_update_stamina() self.update_stamina_status()
after = self.get_equivalent_stamina() after = self.get_equivalent_stamina()
if expect_reduce: if expect_reduce:
if before > after: if before > after:

View File

@ -261,7 +261,7 @@ class RogueEntry(RouteBase, RogueRewardHandler, RoguePathHandler, DungeonRogueUI
if not self.image_color_count(LEVEL_CONFIRM, color=(223, 223, 225), threshold=240, count=50): if not self.image_color_count(LEVEL_CONFIRM, color=(223, 223, 225), threshold=240, count=50):
self.interval_clear(LEVEL_CONFIRM) self.interval_clear(LEVEL_CONFIRM)
continue continue
self.dungeon_update_stamina() self.update_stamina_status()
self.check_stop_condition() self.check_stop_condition()
self.device.click(LEVEL_CONFIRM) self.device.click(LEVEL_CONFIRM)
continue continue

View File

@ -54,7 +54,7 @@ class RogueReward(RogueUI, CombatInteract, DungeonState):
confirm.reset() confirm.reset()
continue continue
if self.appear(REWARD_CLOSE, interval=2): if self.appear(REWARD_CLOSE, interval=2):
self.dungeon_update_stamina() self.update_stamina_status()
if not init: if not init:
initial_stamina = self.config.stored.TrailblazePower.value initial_stamina = self.config.stored.TrailblazePower.value
initial_immersifier = self.config.stored.Immersifier.value initial_immersifier = self.config.stored.Immersifier.value