mirror of
https://github.com/LmeSzinc/StarRailCopilot.git
synced 2024-11-25 01:49:41 +00:00
Add: Task Ornament
This commit is contained in:
parent
71e01d9bd8
commit
4afe0bf908
@ -10,7 +10,7 @@ class ManualConfig:
|
|||||||
|
|
||||||
SCHEDULER_PRIORITY = """
|
SCHEDULER_PRIORITY = """
|
||||||
Restart
|
Restart
|
||||||
> Weekly > Dungeon > Assignment
|
> Weekly > Ornament > Dungeon > Assignment
|
||||||
> BattlePass > DailyQuest
|
> BattlePass > DailyQuest
|
||||||
> Freebies > DataUpdate
|
> Freebies > DataUpdate
|
||||||
> Rogue
|
> Rogue
|
||||||
|
4
src.py
4
src.py
@ -58,6 +58,10 @@ class StarRailCopilot(AzurLaneAutoScript):
|
|||||||
from tasks.rogue.rogue import Rogue
|
from tasks.rogue.rogue import Rogue
|
||||||
Rogue(config=self.config, device=self.device).run()
|
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):
|
def benchmark(self):
|
||||||
from module.daemon.benchmark import run_benchmark
|
from module.daemon.benchmark import run_benchmark
|
||||||
run_benchmark(config=self.config)
|
run_benchmark(config=self.config)
|
||||||
|
@ -15,6 +15,7 @@ class Dungeon(DungeonStamina, DungeonEvent, Combat):
|
|||||||
achieved_daily_quest = False
|
achieved_daily_quest = False
|
||||||
achieved_weekly_quest = False
|
achieved_weekly_quest = False
|
||||||
running_double = False
|
running_double = False
|
||||||
|
support_once = True
|
||||||
daily_quests = []
|
daily_quests = []
|
||||||
weekly_quests = []
|
weekly_quests = []
|
||||||
|
|
||||||
@ -76,14 +77,16 @@ class Dungeon(DungeonStamina, DungeonEvent, Combat):
|
|||||||
wave_limit = relic
|
wave_limit = relic
|
||||||
if relic == 0:
|
if relic == 0:
|
||||||
return 0
|
return 0
|
||||||
if dungeon.is_Ornament_Extraction and self.running_double and \
|
# No need, already checked in Survival_Index
|
||||||
self.config.stored.DungeonDouble.rogue > 0:
|
# if dungeon.is_Ornament_Extraction and self.running_double and \
|
||||||
rogue = self.get_double_event_remain_at_combat()
|
# self.config.stored.DungeonDouble.rogue > 0:
|
||||||
if rogue is not None and rogue < self.config.stored.DungeonDouble.rogue:
|
# rogue = self.get_double_event_remain_at_combat()
|
||||||
self.config.stored.DungeonDouble.rogue = rogue
|
# if rogue is not None and rogue < self.config.stored.DungeonDouble.rogue:
|
||||||
wave_limit = rogue
|
# self.config.stored.DungeonDouble.rogue = rogue
|
||||||
if rogue == 0:
|
# wave_limit = rogue
|
||||||
return 0
|
# if rogue == 0:
|
||||||
|
# return 0
|
||||||
|
|
||||||
# Combat
|
# Combat
|
||||||
self.dungeon = dungeon
|
self.dungeon = dungeon
|
||||||
count = self.combat(team=team, wave_limit=wave_limit, support_character=support_character)
|
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
|
out: page_main
|
||||||
"""
|
"""
|
||||||
require = self.require_compulsory_support()
|
require = self.require_compulsory_support()
|
||||||
if require:
|
if require and self.support_once:
|
||||||
logger.info('Run once with support')
|
logger.info('Run once with support')
|
||||||
count = self._dungeon_run(dungeon=dungeon, team=team, wave_limit=1,
|
count = self._dungeon_run(dungeon=dungeon, team=team, wave_limit=1,
|
||||||
support_character=self.config.DungeonSupport_Character)
|
support_character=self.config.DungeonSupport_Character)
|
||||||
@ -192,22 +195,22 @@ class Dungeon(DungeonStamina, DungeonEvent, Combat):
|
|||||||
|
|
||||||
return count
|
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:
|
else:
|
||||||
# Normal run
|
# Normal run
|
||||||
return self._dungeon_run(dungeon=dungeon, team=team, wave_limit=wave_limit,
|
return self._dungeon_run(dungeon=dungeon, team=team, wave_limit=wave_limit,
|
||||||
support_character=support_character)
|
support_character=support_character)
|
||||||
|
|
||||||
def run(self):
|
def update_double_event_record(self):
|
||||||
self.config.update_battle_pass_quests()
|
"""
|
||||||
self.config.update_daily_quests()
|
Pages:
|
||||||
self.check_synthesize()
|
in: Any
|
||||||
self.called_daily_support = False
|
out: page_guide, Survival_Index
|
||||||
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()
|
|
||||||
|
|
||||||
# Update double event records
|
# Update double event records
|
||||||
if (self.config.stored.DungeonDouble.is_expired()
|
if (self.config.stored.DungeonDouble.is_expired()
|
||||||
or self.config.stored.DungeonDouble.calyx > 0
|
or self.config.stored.DungeonDouble.calyx > 0
|
||||||
@ -216,7 +219,8 @@ class Dungeon(DungeonStamina, DungeonEvent, Combat):
|
|||||||
logger.info('Get dungeon double remains')
|
logger.info('Get dungeon double remains')
|
||||||
# UI switches
|
# UI switches
|
||||||
switched = self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index)
|
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
|
# Nav must at top, reset nav states
|
||||||
self.ui_goto_main()
|
self.ui_goto_main()
|
||||||
self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index)
|
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.relic = relic
|
||||||
self.config.stored.DungeonDouble.rogue = rogue
|
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
|
# Run double events
|
||||||
planner = self.planner.get_dungeon(double_calyx=True)
|
planner = self.planner.get_dungeon(double_calyx=True)
|
||||||
# Double calyx
|
# Double calyx
|
||||||
@ -274,14 +290,6 @@ class Dungeon(DungeonStamina, DungeonEvent, Combat):
|
|||||||
final = planner
|
final = planner
|
||||||
self.is_doing_planner = True
|
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
|
# Use all stamina
|
||||||
if do_rogue:
|
if do_rogue:
|
||||||
# Use support if prioritize rogue
|
# Use support if prioritize rogue
|
||||||
|
@ -131,7 +131,7 @@ class DungeonState(UI):
|
|||||||
"""
|
"""
|
||||||
Delay tasks that use stamina
|
Delay tasks that use stamina
|
||||||
"""
|
"""
|
||||||
if dungeon.is_Simulated_Universe:
|
if dungeon.is_Simulated_Universe or dungeon.is_Ornament_Extraction:
|
||||||
limit = 80
|
limit = 80
|
||||||
elif dungeon.is_Cavern_of_Corrosion:
|
elif dungeon.is_Cavern_of_Corrosion:
|
||||||
limit = 80
|
limit = 80
|
||||||
@ -182,7 +182,7 @@ class DungeonState(UI):
|
|||||||
logger.info(f'Approaching next monday, delay to {next_monday} instead')
|
logger.info(f'Approaching next monday, delay to {next_monday} instead')
|
||||||
future = next_monday
|
future = next_monday
|
||||||
|
|
||||||
tasks = ['Dungeon', 'Weekly']
|
tasks = ['Dungeon', 'Weekly', 'Ornament']
|
||||||
with self.config.multi_set():
|
with self.config.multi_set():
|
||||||
for task in tasks:
|
for task in tasks:
|
||||||
next_run = self.config.cross_get(keys=f'{task}.Scheduler.NextRun', default=DEFAULT_TIME)
|
next_run = self.config.cross_get(keys=f'{task}.Scheduler.NextRun', default=DEFAULT_TIME)
|
||||||
|
@ -311,6 +311,13 @@ class DungeonUI(DungeonState):
|
|||||||
logger.info('Survival index loaded, SURVIVAL_INDEX_OE_LOADED')
|
logger.info('Survival index loaded, SURVIVAL_INDEX_OE_LOADED')
|
||||||
return True
|
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):
|
def _dungeon_wait_treasures_lightward_loaded(self, skip_first_screenshot=True):
|
||||||
"""
|
"""
|
||||||
Returns:
|
Returns:
|
||||||
|
@ -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.base.assets.assets_base_popup import POPUP_CANCEL
|
||||||
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_support import COMBAT_SUPPORT_LIST
|
from tasks.combat.assets.assets_combat_support import COMBAT_SUPPORT_LIST
|
||||||
from tasks.combat.combat import Combat
|
from tasks.dungeon.dungeon import Dungeon
|
||||||
from tasks.dungeon.event import DungeonEvent
|
from tasks.dungeon.state import DungeonState
|
||||||
from tasks.map.route.loader import RouteLoader
|
from tasks.map.route.loader import RouteLoader
|
||||||
from tasks.map.route.route.daily import OrnamentExtraction__route
|
from tasks.map.route.route.daily import OrnamentExtraction__route
|
||||||
from tasks.ornament.assets.assets_ornament_combat import *
|
from tasks.ornament.assets.assets_ornament_combat import *
|
||||||
from tasks.ornament.assets.assets_ornament_ui 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):
|
def combat_enter_from_map(self, skip_first_screenshot=True):
|
||||||
# Don't enter from map, UI too deep inside
|
# Don't enter from map, UI too deep inside
|
||||||
# Enter from survival index instead
|
# Enter from survival index instead
|
||||||
@ -23,23 +23,6 @@ class OrnamentCombat(DungeonEvent, Combat, RouteLoader):
|
|||||||
# Different position to OCR
|
# Different position to OCR
|
||||||
return super().get_double_event_remain_at_combat(button)
|
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):
|
def oe_leave(self, skip_first_screenshot=True):
|
||||||
self.interval_clear([COMBAT_PREPARE, MAP_EXIT])
|
self.interval_clear([COMBAT_PREPARE, MAP_EXIT])
|
||||||
logger.hr('OE leave')
|
logger.hr('OE leave')
|
||||||
@ -121,6 +104,32 @@ class OrnamentCombat(DungeonEvent, Combat, RouteLoader):
|
|||||||
self.interval_reset(COMBAT_SUPPORT_LIST)
|
self.interval_reset(COMBAT_SUPPORT_LIST)
|
||||||
continue
|
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:
|
def is_team_prepared(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Pages:
|
Pages:
|
||||||
@ -169,7 +178,11 @@ class OrnamentCombat(DungeonEvent, Combat, RouteLoader):
|
|||||||
# End
|
# End
|
||||||
if self.is_in_main():
|
if self.is_in_main():
|
||||||
logger.info('Combat map entered')
|
logger.info('Combat map entered')
|
||||||
|
self.device.screenshot_interval_set()
|
||||||
break
|
break
|
||||||
|
if self.is_combat_executing():
|
||||||
|
self.device.screenshot_interval_set()
|
||||||
|
return True
|
||||||
# Relics full
|
# Relics full
|
||||||
# Clicking between COMBAT_PREPARE and COMBAT_TEAM_PREPARE
|
# Clicking between COMBAT_PREPARE and COMBAT_TEAM_PREPARE
|
||||||
if trial > 5:
|
if trial > 5:
|
||||||
@ -187,6 +200,7 @@ class OrnamentCombat(DungeonEvent, Combat, RouteLoader):
|
|||||||
if support_set and self.appear(COMBAT_PREPARE, interval=5):
|
if support_set and self.appear(COMBAT_PREPARE, interval=5):
|
||||||
# Long loading after COMBAT_PREPARE
|
# Long loading after COMBAT_PREPARE
|
||||||
self.device.click(COMBAT_PREPARE)
|
self.device.click(COMBAT_PREPARE)
|
||||||
|
self.device.screenshot_interval_set('combat')
|
||||||
trial += 1
|
trial += 1
|
||||||
continue
|
continue
|
||||||
if self.handle_popup_confirm():
|
if self.handle_popup_confirm():
|
||||||
|
99
tasks/ornament/ornament.py
Normal file
99
tasks/ornament/ornament.py
Normal 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)
|
Loading…
Reference in New Issue
Block a user