1
0
mirror of https://github.com/TheFunny/ArisuAutoSweeper synced 2025-12-16 15:35:12 +00:00
ArisuAutoSweeper/tasks/mission/mission.py
2024-01-15 19:25:15 +08:00

284 lines
11 KiB
Python

import json
import math
from datetime import datetime, timedelta
from enum import Enum
from filelock import FileLock
from module.base.timer import Timer
from module.logger import logger
from tasks.cafe.cafe import Cafe
from tasks.circle.circle import Circle
from tasks.item.data_update import DataUpdate
from tasks.mail.mail import Mail
from tasks.mission.ui import MissionUI, CommissionsUI, SWITCH_QUEST
from tasks.task.task import Task
class MissionStatus(Enum):
AP = 0 # Calculate AP and decide to terminate Mission module or not
NAVIGATE = 1 # Navigate to the stage page for example the commissions page or mission page
SELECT = 2 # Select the stage mode for example hard or normal in mission
ENTER = 3 # Search and for the stage in the stage list and enter
SWEEP = 4 # Sweep the stage
RECHARGE = 5 # Recharge AP from other taks if they are enabled
FINISH = -1 # Inidicate termination of Mission module
class Mission(MissionUI, CommissionsUI):
@property
def stage_ap(self):
match self.current_mode:
case "N":
return 10
case "H":
return 20
case "E":
if self.current_stage >= "09":
return 20
elif self.current_stage <= "04":
return 10
else:
return 15
case "XP" | "CR":
if self.current_stage >= "08":
return 40
else:
return int(self.current_stage, base=10) * 5
@property
def mission_info(self) -> list:
"""
Read the config from MCE/config.json and extract the queue, a list of list.
If queue is empty repopulate from preferred template.
Format of each element in queue: [mode, stage, sweep_num]
e.g. ["N", "1-1", 3]
Mode Acronyms:
"N" : Normal Mission
"H" : Hard Mission
"E" : Event Quest
"CR" : Item Retrieval / Commission where you get credit
"XP" : Base Defense / Commission where you get exp
Returns:
list of list
"""
queue = []
try:
with open("MCE/config.json") as json_file:
config_data = json.load(json_file)
queue = config_data["Queue"]
self.recharge_AP = config_data["RechargeAP"]
self.reset_daily = config_data["ResetDaily"]
self.reset_time = config_data["ResetTime"]
self.last_run = config_data["LastRun"]
self.event = config_data["Event"]
if self.check_reset_daily() or not queue:
preferred_template = config_data["PreferredTemplate"]
queue = config_data["Templates"][preferred_template]
if not self.event:
queue = [x for x in queue if x[0] != "E"]
except:
logger.error("Failed to read configuration file")
finally:
return queue
def check_reset_daily(self):
# Check if it's time to reset the queue
if self.reset_daily:
current_datetime = datetime.now().replace(microsecond=0) # Round to the nearest second
current_date = current_datetime.date()
current_time = current_datetime.time()
last_run_datetime = datetime.strptime(self.last_run, "%Y-%m-%d %H:%M:%S")
reset_time = datetime.strptime(self.reset_time, "%H:%M:%S").time()
# Check if the difference between the current date and last run date is 2 or greater days
if (current_date - last_run_datetime.date()).days >= 2:
# Set self.last_run to yesterday's date with time as reset_time
yesterday_datetime = current_datetime - timedelta(days=1)
yesterday_date = yesterday_datetime.date()
self.last_run = str(datetime.combine(yesterday_date, reset_time))
logger.info("Reset Daily activated")
return True
# Check if the current date is different from the last run date and the current time is greater than or equal to the reset time
elif current_date != last_run_datetime.date() and current_time >= reset_time:
self.last_run = str(datetime.now().replace(microsecond=0))
logger.info("Reset Daily activated")
return True
return False
@property
def valid_task(self) -> list:
task = self.mission_info
if not task:
logger.warning('Mission enabled but no task set')
# self.error_handler()
return task
@property
def current_mode(self):
return self.task[0][0]
@property
def current_stage(self):
return self.task[0][1]
@property
def current_count(self):
if self.current_mode == "H" and self.task[0][2] > 3:
return 3
return self.task[0][2]
@current_count.setter
def current_count(self, value):
self.task[0][2] = value
def select(self) -> bool:
"""
A wrapper method to select the current_mode
by calling the specific method based on its type.
Return
True if selection happens without any problem, False otherwise.
"""
if self.current_mode in ["N", "H"]:
return self.select_mission(self.current_mode, self.current_stage)
elif self.current_mode in ["CR", "XP"]:
return self.select_commission(self.current_mode)
elif self.current_mode == "E":
return self.select_mode(SWITCH_QUEST)
else:
logger.error("Uknown mode")
return False
def get_realistic_count(self) -> int:
"""
Calculate the possible number of sweeps based on the current AP
"""
possible_count = math.floor(self.current_ap / self.stage_ap)
return min(possible_count, self.current_count)
def update_task(self, failure=False):
"""
Update self.task and save the current state of the queue in
MCE/config.json.
"""
try:
if failure or self.current_count == self.realistic_count:
self.previous_mode = self.current_mode
self.task.pop(0)
else:
self.previous_mode = None
self.current_count -= self.realistic_count
with open("MCE/config.json", "r") as json_file:
config_data = json.load(json_file)
with open("MCE/config.json", "w") as json_file:
config_data["Queue"] = self.task
config_data["LastRun"] = self.last_run
json.dump(config_data, json_file, indent=2)
except:
logger.error("Failed to save configuration")
self.task = []
def update_ap(self):
ap = self.config.stored.AP
ap_old = ap.value
ap_new = ap_old - self.stage_ap * self.realistic_count
ap.set(ap_new, ap.total)
logger.info(f'Set AP: {ap_old} -> {ap_new}')
def recharge(self) -> bool:
"""
Check if AP related modules such as cafe, circle, task, mail are enabled and run them if they are.
task_call only works after the current task is finished so is not suitable.
"""
cafe_reward = self.config.cross_get(["Cafe", "Scheduler", "Enable"]) and self.config.cross_get(
["Cafe", "Cafe", "Reward"])
circle = self.config.cross_get(["Circle", "Scheduler", "Enable"])
task = self.config.cross_get(["Task", "Scheduler", "Enable"])
mail = self.config.cross_get(["Mail", "Scheduler", "Enable"])
ap_tasks = [(cafe_reward, Cafe), (circle, Circle), (task, Task), (mail, Mail)]
modules = [x[1] for x in ap_tasks if x[0]]
if not modules:
logger.info("Recharge AP was enabled but no AP related modules were enabled")
return False
for module in modules:
module(config=self.config, device=self.device).run()
return True
def handle_mission(self, status):
match status:
case MissionStatus.AP:
if not self.task:
return MissionStatus.FINISH
self.realistic_count = self.get_realistic_count()
if self.realistic_count == 0 and self.recharge_AP:
self.recharge_AP = False
return MissionStatus.RECHARGE
elif self.realistic_count == 0 and not self.recharge_AP:
return MissionStatus.FINISH
else:
return MissionStatus.NAVIGATE
case MissionStatus.NAVIGATE:
self.navigate(self.previous_mode, self.current_mode)
return MissionStatus.SELECT
case MissionStatus.SELECT:
if self.select():
return MissionStatus.ENTER
self.update_task(failure=True)
return MissionStatus.AP
case MissionStatus.ENTER:
if self.enter_stage(self.current_mode, self.current_stage):
return MissionStatus.SWEEP
self.update_task(failure=True)
return MissionStatus.AP
case MissionStatus.SWEEP:
if self.do_sweep(self.current_mode, self.realistic_count):
self.update_ap()
self.update_task()
else:
self.update_task(failure=True)
return MissionStatus.AP
case MissionStatus.RECHARGE:
if self.recharge():
DataUpdate(config=self.config, device=self.device).run()
return MissionStatus.AP
return MissionStatus.FINISH
case MissionStatus.FINISH:
return status
case _:
logger.warning(f'Invalid status: {status}')
return status
def run(self):
self.lock = FileLock("MCE/config.json.lock")
with self.lock.acquire():
self.previous_mode = None
self.task = self.valid_task
if self.task:
action_timer = Timer(0.5, 1)
status = MissionStatus.AP
"""Update the dashboard to accurately calculate AP"""
DataUpdate(config=self.config, device=self.device).run()
while 1:
self.device.screenshot()
if self.ui_additional():
continue
if action_timer.reached_and_reset():
logger.attr('Status', status)
status = self.handle_mission(status)
if status == MissionStatus.FINISH:
break
# delay mission to 7 hours if there are still stages in the queue
self.config.task_delay(minute=420) if self.task else self.config.task_delay(server_update=True)