Add: Task Ornament

This commit is contained in:
LmeSzinc 2024-07-02 02:41:12 +08:00
parent 71e01d9bd8
commit 4afe0bf908
7 changed files with 184 additions and 52 deletions

View File

@ -10,7 +10,7 @@ class ManualConfig:
SCHEDULER_PRIORITY = """
Restart
> Weekly > Dungeon > Assignment
> Weekly > Ornament > Dungeon > Assignment
> BattlePass > DailyQuest
> Freebies > DataUpdate
> Rogue

4
src.py
View File

@ -58,6 +58,10 @@ class StarRailCopilot(AzurLaneAutoScript):
from tasks.rogue.rogue import Rogue
Rogue(config=self.config, device=self.device).run()
def ornament(self):
from tasks.ornament.ornament import Ornament
Ornament(config=self.config, device=self.device).run()
def benchmark(self):
from module.daemon.benchmark import run_benchmark
run_benchmark(config=self.config)

View File

@ -15,6 +15,7 @@ class Dungeon(DungeonStamina, DungeonEvent, Combat):
achieved_daily_quest = False
achieved_weekly_quest = False
running_double = False
support_once = True
daily_quests = []
weekly_quests = []
@ -76,14 +77,16 @@ class Dungeon(DungeonStamina, DungeonEvent, Combat):
wave_limit = relic
if relic == 0:
return 0
if dungeon.is_Ornament_Extraction and self.running_double and \
self.config.stored.DungeonDouble.rogue > 0:
rogue = self.get_double_event_remain_at_combat()
if rogue is not None and rogue < self.config.stored.DungeonDouble.rogue:
self.config.stored.DungeonDouble.rogue = rogue
wave_limit = rogue
if rogue == 0:
return 0
# No need, already checked in Survival_Index
# if dungeon.is_Ornament_Extraction and self.running_double and \
# self.config.stored.DungeonDouble.rogue > 0:
# rogue = self.get_double_event_remain_at_combat()
# if rogue is not None and rogue < self.config.stored.DungeonDouble.rogue:
# self.config.stored.DungeonDouble.rogue = rogue
# wave_limit = rogue
# if rogue == 0:
# return 0
# Combat
self.dungeon = dungeon
count = self.combat(team=team, wave_limit=wave_limit, support_character=support_character)
@ -177,7 +180,7 @@ class Dungeon(DungeonStamina, DungeonEvent, Combat):
out: page_main
"""
require = self.require_compulsory_support()
if require:
if require and self.support_once:
logger.info('Run once with support')
count = self._dungeon_run(dungeon=dungeon, team=team, wave_limit=1,
support_character=self.config.DungeonSupport_Character)
@ -192,22 +195,22 @@ class Dungeon(DungeonStamina, DungeonEvent, Combat):
return count
elif require and not self.support_once:
# Run with support all the way
return self._dungeon_run(dungeon=dungeon, team=team, wave_limit=1,
support_character=self.config.DungeonSupport_Character)
else:
# Normal run
return self._dungeon_run(dungeon=dungeon, team=team, wave_limit=wave_limit,
support_character=support_character)
def run(self):
self.config.update_battle_pass_quests()
self.config.update_daily_quests()
self.check_synthesize()
self.called_daily_support = False
self.achieved_daily_quest = False
self.achieved_weekly_quest = False
self.running_double = False
self.daily_quests = self.config.stored.DailyQuest.load_quests()
self.weekly_quests = self.config.stored.BattlePassWeeklyQuest.load_quests()
def update_double_event_record(self):
"""
Pages:
in: Any
out: page_guide, Survival_Index
"""
# Update double event records
if (self.config.stored.DungeonDouble.is_expired()
or self.config.stored.DungeonDouble.calyx > 0
@ -216,7 +219,8 @@ class Dungeon(DungeonStamina, DungeonEvent, Combat):
logger.info('Get dungeon double remains')
# UI switches
switched = self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index)
if not switched:
if not switched and not self._dungeon_survival_index_top_appear():
logger.info('Reset nav states')
# Nav must at top, reset nav states
self.ui_goto_main()
self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index)
@ -237,6 +241,18 @@ class Dungeon(DungeonStamina, DungeonEvent, Combat):
self.config.stored.DungeonDouble.relic = relic
self.config.stored.DungeonDouble.rogue = rogue
def run(self):
self.config.update_battle_pass_quests()
self.config.update_daily_quests()
self.check_synthesize()
self.called_daily_support = False
self.achieved_daily_quest = False
self.achieved_weekly_quest = False
self.running_double = False
self.daily_quests = self.config.stored.DailyQuest.load_quests()
self.weekly_quests = self.config.stored.BattlePassWeeklyQuest.load_quests()
self.update_double_event_record()
# Run double events
planner = self.planner.get_dungeon(double_calyx=True)
# Double calyx
@ -274,14 +290,6 @@ class Dungeon(DungeonStamina, DungeonEvent, Combat):
final = planner
self.is_doing_planner = True
# Check daily
if self.achieved_daily_quest:
self.config.task_call('DailyQuest')
self.config.task_stop()
if self.achieved_weekly_quest:
self.config.task_call('BattlePass')
self.config.task_stop()
# Use all stamina
if do_rogue:
# Use support if prioritize rogue

View File

@ -131,7 +131,7 @@ class DungeonState(UI):
"""
Delay tasks that use stamina
"""
if dungeon.is_Simulated_Universe:
if dungeon.is_Simulated_Universe or dungeon.is_Ornament_Extraction:
limit = 80
elif dungeon.is_Cavern_of_Corrosion:
limit = 80
@ -182,7 +182,7 @@ class DungeonState(UI):
logger.info(f'Approaching next monday, delay to {next_monday} instead')
future = next_monday
tasks = ['Dungeon', 'Weekly']
tasks = ['Dungeon', 'Weekly', 'Ornament']
with self.config.multi_set():
for task in tasks:
next_run = self.config.cross_get(keys=f'{task}.Scheduler.NextRun', default=DEFAULT_TIME)

View File

@ -311,6 +311,13 @@ class DungeonUI(DungeonState):
logger.info('Survival index loaded, SURVIVAL_INDEX_OE_LOADED')
return True
def _dungeon_survival_index_top_appear(self):
if self.appear(SURVIVAL_INDEX_SU_LOADED):
return True
if self.appear(SURVIVAL_INDEX_OE_LOADED):
return True
return False
def _dungeon_wait_treasures_lightward_loaded(self, skip_first_screenshot=True):
"""
Returns:

View File

@ -5,15 +5,15 @@ from tasks.base.assets.assets_base_page import MAP_EXIT
from tasks.base.assets.assets_base_popup import POPUP_CANCEL
from tasks.combat.assets.assets_combat_prepare import COMBAT_PREPARE
from tasks.combat.assets.assets_combat_support import COMBAT_SUPPORT_LIST
from tasks.combat.combat import Combat
from tasks.dungeon.event import DungeonEvent
from tasks.dungeon.dungeon import Dungeon
from tasks.dungeon.state import DungeonState
from tasks.map.route.loader import RouteLoader
from tasks.map.route.route.daily import OrnamentExtraction__route
from tasks.ornament.assets.assets_ornament_combat import *
from tasks.ornament.assets.assets_ornament_ui import *
class OrnamentCombat(DungeonEvent, Combat, RouteLoader):
class OrnamentCombat(Dungeon, RouteLoader, DungeonState):
def combat_enter_from_map(self, skip_first_screenshot=True):
# Don't enter from map, UI too deep inside
# Enter from survival index instead
@ -23,23 +23,6 @@ class OrnamentCombat(DungeonEvent, Combat, RouteLoader):
# Different position to OCR
return super().get_double_event_remain_at_combat(button)
def _dungeon_wait_until_dungeon_list_loaded(self, skip_first_screenshot=True):
# Check save file before entering
result = super()._dungeon_wait_until_dungeon_list_loaded(skip_first_screenshot)
if self.image_color_count(
DIVERGENT_UNIVERSE_SAVE_UNAVAILABLE,
color=(195, 89, 79), threshold=221, count=1000,
):
logger.error(
'Divergent Universe save unavailable, '
'please clear Divergent Universe once before running Ornament Extraction'
)
self.config.task_delay(server_update=True)
self.config.task_stop()
return result
def oe_leave(self, skip_first_screenshot=True):
self.interval_clear([COMBAT_PREPARE, MAP_EXIT])
logger.hr('OE leave')
@ -121,6 +104,32 @@ class OrnamentCombat(DungeonEvent, Combat, RouteLoader):
self.interval_reset(COMBAT_SUPPORT_LIST)
continue
def combat_get_trailblaze_power(self, expect_reduce=False, skip_first_screenshot=True) -> int:
"""
Args:
expect_reduce: Current value is supposed to be lower than the previous.
skip_first_screenshot:
Returns:
int: Equivalent stamina
Pages:
in: COMBAT_PREPARE or COMBAT_REPEAT
"""
before = self.config.stored.TrailblazePower.value + self.config.stored.Immersifier.value * 40
after = before
for _ in range(3):
self.dungeon_update_stamina()
after = self.config.stored.TrailblazePower.value + self.config.stored.Immersifier.value * 40
if expect_reduce:
if before > after:
break
else:
break
return after
def is_team_prepared(self) -> bool:
"""
Pages:
@ -169,7 +178,11 @@ class OrnamentCombat(DungeonEvent, Combat, RouteLoader):
# End
if self.is_in_main():
logger.info('Combat map entered')
self.device.screenshot_interval_set()
break
if self.is_combat_executing():
self.device.screenshot_interval_set()
return True
# Relics full
# Clicking between COMBAT_PREPARE and COMBAT_TEAM_PREPARE
if trial > 5:
@ -187,6 +200,7 @@ class OrnamentCombat(DungeonEvent, Combat, RouteLoader):
if support_set and self.appear(COMBAT_PREPARE, interval=5):
# Long loading after COMBAT_PREPARE
self.device.click(COMBAT_PREPARE)
self.device.screenshot_interval_set('combat')
trial += 1
continue
if self.handle_popup_confirm():

View File

@ -0,0 +1,99 @@
from datetime import timedelta
from module.config.stored.classes import now
from module.config.utils import DEFAULT_TIME
from module.exception import ScriptError
from module.logger import logger
from tasks.dungeon.assets.assets_dungeon_ui_rogue import DIVERGENT_UNIVERSE_SAVE_UNAVAILABLE
from tasks.dungeon.keywords import DungeonList
from tasks.ornament.combat import OrnamentCombat
class Ornament(OrnamentCombat):
# Reuse support, cause exit and re-enter is so time-wasting in Divergent Universe
support_once = False
def _dungeon_wait_until_dungeon_list_loaded(self, skip_first_screenshot=True):
result = super()._dungeon_wait_until_dungeon_list_loaded(skip_first_screenshot)
# Check save file before entering
if self.image_color_count(
DIVERGENT_UNIVERSE_SAVE_UNAVAILABLE,
color=(195, 89, 79), threshold=221, count=1000,
):
logger.error(
'Divergent Universe save unavailable, '
'please clear Divergent Universe once before running Ornament Extraction'
)
self.config.task_delay(server_update=True)
self.config.task_stop()
# Always update double rogue
record = self.config.stored.DungeonDouble.time
if now() - record < timedelta(seconds=5):
# Updated just now
pass
else:
if self.has_double_relic_event():
rogue = self.get_double_rogue_remain()
self.config.stored.DungeonDouble.rogue = rogue
else:
logger.info('No double rogue event')
# Check stamina
logger.info('Check stamina')
stamina = self.combat_get_trailblaze_power()
if stamina < self.combat_wave_cost:
logger.info('Current trailblaze power is not enough for a run')
self.delay_dungeon_task(self.dungeon)
self.config.task_stop()
if not self.config.Ornament_UseStamina and self.config.stored.DungeonDouble.rogue <= 0:
if self.config.stored.Immersifier.value <= 0:
logger.info('Current immersifier is not enough for a run')
self.delay_dungeon_task(self.dungeon)
self.config.task_stop()
return result
def run(self):
self.config.update_battle_pass_quests()
self.config.update_daily_quests()
# self.check_synthesize()
self.called_daily_support = False
self.achieved_daily_quest = False
self.achieved_weekly_quest = False
self.running_double = False
self.daily_quests = self.config.stored.DailyQuest.load_quests()
self.weekly_quests = self.config.stored.BattlePassWeeklyQuest.load_quests()
self.update_double_event_record()
# During double event, do it first
if self.config.stored.DungeonDouble.calyx or self.config.stored.DungeonDouble.relic:
logger.info('During double calyx or relic event, delay Ornament')
future = self.config.cross_get('Dungeon.Scheduler.NextRun', default=DEFAULT_TIME)
future = future + timedelta(minutes=1)
with self.config.multi_set():
self.config.task_delay(target=future)
self.config.task_stop()
# Run
dungeon = DungeonList.find(self.config.Ornament_Dungeon)
self.combat_wave_cost = 40
self.dungeon = dungeon
if self.config.Ornament_UseStamina:
# No limit
self.dungeon_run(dungeon, wave_limit=0)
# Stamina should have exhausted in dungeon_run
raise ScriptError('Ornament finished but stamina was not exhausted')
elif self.config.stored.DungeonDouble.rogue > 0:
# Limited in double events
self.running_double = True
self.dungeon_run(dungeon, wave_limit=self.config.stored.DungeonDouble.rogue)
self.running_double = False
self.config.task_delay(server_update=True)
else:
# Use immersifier only, wave limited in _dungeon_wait_until_dungeon_list_loaded
self.dungeon_run(dungeon, wave_limit=0)
self.config.task_delay(server_update=True)