StarRailCopilot/tasks/battle_pass/battle_pass.py

338 lines
12 KiB
Python
Raw Normal View History

2023-07-09 10:09:30 +00:00
import datetime
import re
2023-07-09 10:09:30 +00:00
import numpy as np
from module.base.timer import Timer
from module.base.utils import get_color
2023-07-09 10:09:30 +00:00
from module.config.utils import get_server_next_update
from module.logger.logger import logger
from module.ocr.ocr import Digit, Duration, Ocr
from module.ocr.utils import split_and_pair_buttons
from module.ui.scroll import Scroll
from module.ui.switch import Switch
2023-07-19 05:36:48 +00:00
from tasks.base.assets.assets_base_page import BATTLE_PASS_CHECK, MAIN_GOTO_BATTLE_PASS
from tasks.base.page import page_battle_pass, page_main
from tasks.base.ui import UI
from tasks.battle_pass.assets.assets_battle_pass import *
from tasks.battle_pass.keywords import *
SWITCH_BATTLE_PASS_TAB = Switch('BattlePassTab', is_selector=True)
SWITCH_BATTLE_PASS_TAB.add_state(
KEYWORD_BATTLE_PASS_TAB.Rewards,
check_button=REWARDS_CHECK,
click_button=REWARDS_CLICK
)
SWITCH_BATTLE_PASS_TAB.add_state(
KEYWORD_BATTLE_PASS_TAB.Missions,
check_button=MISSIONS_CHECK,
click_button=MISSIONS_CLICK
)
class BattlePassMissionTab(Switch):
def get(self, main):
"""
Args:
main (ModuleBase):
Returns:
str: state name or 'unknown'.
"""
for data in self.state_list:
if main.image_color_count(data['check_button'], color=(250, 233, 153)):
return data['state']
return 'unknown'
SWITCH_BATTLE_PASS_MISSION_TAB = BattlePassMissionTab('BattlePassMissionTab', is_selector=True)
SWITCH_BATTLE_PASS_MISSION_TAB.add_state(
KEYWORD_BATTLE_PASS_MISSION_TAB.Today_Missions,
check_button=TODAY_MISSION_CLICK,
click_button=TODAY_MISSION_CLICK
)
SWITCH_BATTLE_PASS_MISSION_TAB.add_state(
KEYWORD_BATTLE_PASS_MISSION_TAB.This_Week_Missions,
check_button=WEEK_MISSION_CLICK,
click_button=WEEK_MISSION_CLICK
)
SWITCH_BATTLE_PASS_MISSION_TAB.add_state(
KEYWORD_BATTLE_PASS_MISSION_TAB.This_Period_Missions,
check_button=PERIOD_MISSION_CLICK,
click_button=PERIOD_MISSION_CLICK
)
class BattlePassQuestOcr(Ocr):
def after_process(self, result):
result = super().after_process(result)
2023-09-08 14:23:57 +00:00
if self.lang == 'cn':
result = re.sub("[jJ]", "", result)
return result
class BattlePassUI(UI):
2023-11-17 04:37:16 +00:00
MAX_LEVEL = 70
def _battle_pass_wait_rewards_loaded(self, skip_first_screenshot=True):
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 rewards tab loaded timeout')
break
if self.appear(REWARDS_LOADED):
logger.info('Rewards tab loaded')
break
def _battle_pass_wait_missions_loaded(self, skip_first_screenshot=True, has_scroll=True):
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 missions tab loaded timeout')
break
if has_scroll:
if self.appear(MISSION_PAGE_SCROLL):
logger.info('Rewards tab loaded')
break
else:
color = get_color(self.device.image, MISSIONS_LOADED.area)
if np.mean(color) > 128:
logger.info('Missions tab loaded')
break
def battle_pass_goto(self, state: KEYWORD_BATTLE_PASS_TAB):
"""
Args:
state:
Examples:
self = BattlePassUI('alas')
self.device.screenshot()
self.battle_pass_goto(KEYWORDS_DUNGEON_TAB.Missions)
self.battle_pass_goto(KEYWORDS_DUNGEON_TAB.Rewards)
"""
self.ui_ensure(page_battle_pass)
if SWITCH_BATTLE_PASS_TAB.set(state, main=self):
logger.info(f'Tab goto {state}, wait until loaded')
if state == KEYWORD_BATTLE_PASS_TAB.Missions:
self._battle_pass_wait_missions_loaded(has_scroll=False)
if state == KEYWORD_BATTLE_PASS_TAB.Rewards:
self._battle_pass_wait_rewards_loaded()
def battle_pass_mission_tab_goto(self, state: KEYWORD_BATTLE_PASS_MISSION_TAB):
self.battle_pass_goto(KEYWORD_BATTLE_PASS_TAB.Missions)
if SWITCH_BATTLE_PASS_MISSION_TAB.set(state, main=self):
logger.info(f'Tab goto {state}, wait until loaded')
if state == KEYWORD_BATTLE_PASS_MISSION_TAB.Today_Missions:
self._battle_pass_wait_missions_loaded(has_scroll=False)
if state == KEYWORD_BATTLE_PASS_MISSION_TAB.This_Week_Missions:
self._battle_pass_wait_missions_loaded()
if state == KEYWORD_BATTLE_PASS_MISSION_TAB.This_Period_Missions:
self._battle_pass_wait_missions_loaded()
def handle_choose_gifts(self, interval=5):
"""
Popup when you have purchase Nameless Glory
Args:
interval:
Returns:
If handled
"""
if self.appear_then_click(CLOSE_CHOOSE_GIFT, interval=interval):
return True
return False
def _claim_exp(self, skip_first_screenshot=True):
logger.hr('Claim EXP', level=1)
self.battle_pass_goto(KEYWORD_BATTLE_PASS_TAB.Missions)
claimed = False
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
self.device.screenshot()
if not self.appear(EXP_CLAIM_ALL):
logger.info('No more EXP to claim')
break
if self.appear_then_click(EXP_CLAIM_ALL):
claimed = True
logger.info("All EXP claimed")
continue
logger.attr('EXP claimed', claimed)
return claimed
def _claim_rewards(self, skip_first_screenshot=True):
logger.hr('Claim rewards', level=1)
self.battle_pass_goto(KEYWORD_BATTLE_PASS_TAB.Rewards)
timeout = Timer(5, count=15).start()
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
self.device.screenshot()
if self.reward_appear():
logger.info('Get reward')
break
if self.appear(CLOSE_CHOOSE_GIFT):
logger.info('Got reward but have gift to choose')
break
if timeout.reached():
logger.warning('Claim reward timeout, no rewards to claim')
break
if self.appear_then_click(REWARDS_CLAIM_ALL, interval=2):
timeout.reset()
continue
logger.info('Close reward popup')
skip_first_screenshot = True
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
self.device.screenshot()
if self.appear(BATTLE_PASS_CHECK):
logger.info("Claiming rewards complete")
break
if self.handle_choose_gifts():
logger.info("You have unclaimed gift to choose")
continue
if self.handle_reward():
continue
2023-07-09 10:09:30 +00:00
def _get_battle_pass_level(self) -> int:
digit = Digit(OCR_LEVEL)
return digit.ocr_single_line(self.device.image)
2023-07-09 10:09:30 +00:00
def _get_battle_pass_end(self) -> datetime.datetime:
remain = Duration(OCR_REMAINING_TIME).ocr_single_line(self.device.image)
future = get_server_next_update(self.config.Scheduler_ServerUpdate)
future += datetime.timedelta(days=remain.days)
return future
def claim_battle_pass_rewards(self):
"""
2023-07-09 10:09:30 +00:00
Returns:
int: Current battle pass level
Examples:
self = BattlePassUI('alas')
self.device.screenshot()
self.claim_battle_pass_rewards()
"""
2023-09-12 20:26:36 +00:00
logger.hr('Quest recognise', level=1)
with self.config.multi_set():
previous_level = self._get_battle_pass_level()
self.config.stored.BattlePassLevel.set(previous_level)
quests = self.battle_pass_quests_recognition(
KEYWORD_BATTLE_PASS_MISSION_TAB.Today_Missions, has_scroll=False)
self.config.stored.BattlePassTodayQuest.write_quests(quests)
if previous_level == self.MAX_LEVEL:
return previous_level
claimed_exp = self._claim_exp()
current_level = self._get_battle_pass_level()
self.config.stored.BattlePassLevel.set(current_level)
if claimed_exp and current_level > previous_level:
logger.info("Upgraded, go to claim rewards")
self._claim_rewards()
return current_level
def ocr_single_page(self):
"""
Returns incomplete quests only
"""
ocr = BattlePassQuestOcr(OCR_BATTLE_PASS_QUEST)
results = ocr.matched_ocr(self.device.image, [BattlePassQuest, BattlePassQuestState])
def completed_state(state):
return state != KEYWORD_BATTLE_PASS_QUEST_STATE.Navigate
return [incomplete_quest for incomplete_quest, _ in
split_and_pair_buttons(results, split_func=completed_state, relative_area=(0, 0, 800, 100))]
def battle_pass_quests_recognition(
self,
page: KEYWORD_BATTLE_PASS_MISSION_TAB,
has_scroll=True,
) -> list[BattlePassQuest]:
"""
Args:
page: One of the followings:
KEYWORD_BATTLE_PASS_MISSION_TAB.Today_Missions
KEYWORD_BATTLE_PASS_MISSION_TAB.This_Week_Missions
KEYWORD_BATTLE_PASS_MISSION_TAB.This_Period_Missions
has_scroll: need to scroll to recognize all quests
Returns:
"""
logger.info(f"Recognizing battle pass quests at {page}")
self.battle_pass_mission_tab_goto(page)
if not has_scroll:
results = self.ocr_single_page()
results = [result.matched_keyword for result in results]
else:
scroll = Scroll(MISSION_PAGE_SCROLL, color=(198, 198, 198))
scroll.set_top(main=self)
results = self.ocr_single_page()
while not scroll.at_bottom(main=self):
scroll.next_page(main=self)
results += [result for result in self.ocr_single_page() if result not in results]
results = [result.matched_keyword for result in results]
return results
def has_battle_pass_entrance(self, skip_first_screenshot=True):
"""
Pages:
in: page_main
"""
timeout = Timer(0.5, count=2).start()
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
self.device.screenshot()
if timeout.reached():
break
if self.appear(MAIN_GOTO_BATTLE_PASS, similarity=0.75):
return True
else:
continue
logger.info('No battle pass entrance, probably a gap between two periods')
return False
def run(self):
self.ui_ensure(page_main)
if not self.has_battle_pass_entrance():
self.config.stored.BattlePassTodayQuest.set(0)
self.config.task_delay(server_update=True)
self.config.task_stop()
self.ui_goto(page_battle_pass)
current_level = self.claim_battle_pass_rewards()
if current_level == self.MAX_LEVEL:
2023-07-09 10:09:30 +00:00
self.config.task_delay(target=self._get_battle_pass_end())
else:
self.config.task_delay(server_update=True)