|
|
|
|
@@ -1,5 +1,3 @@
|
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
|
|
|
|
|
|
import numpy as np
|
|
|
|
|
|
|
|
|
|
from module.exception import MapWalkError
|
|
|
|
|
@@ -8,15 +6,9 @@ from module.logger import logger
|
|
|
|
|
from module.map.map_grids import SelectedGrids
|
|
|
|
|
from module.os.assets import MAP_EXIT
|
|
|
|
|
from module.os.map import OSMap
|
|
|
|
|
from module.os_handler.action_point import ActionPointLimit
|
|
|
|
|
from module.reward.reward import Reward
|
|
|
|
|
from module.ui.ui import page_os
|
|
|
|
|
|
|
|
|
|
RECORD_MISSION_ACCEPT = ('DailyRecord', 'os_mission_accept')
|
|
|
|
|
RECORD_MISSION_FINISH = ('DailyRecord', 'os_mission_finish')
|
|
|
|
|
RECORD_SUPPLY_BUY = ('DailyRecord', 'os_supply_buy')
|
|
|
|
|
RECORD_OBSCURE_FINISH = ('DailyRecord', 'os_obscure_finish')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OperationSiren(Reward, OSMap):
|
|
|
|
|
def os_init(self):
|
|
|
|
|
@@ -27,7 +19,7 @@ class OperationSiren(Reward, OSMap):
|
|
|
|
|
in: IN_MAP or IN_GLOBE or page_os or any page
|
|
|
|
|
out: IN_MAP
|
|
|
|
|
"""
|
|
|
|
|
logger.hr('OS init')
|
|
|
|
|
logger.hr('OS init', level=1)
|
|
|
|
|
|
|
|
|
|
# UI switching
|
|
|
|
|
self.device.screenshot()
|
|
|
|
|
@@ -71,6 +63,7 @@ class OperationSiren(Reward, OSMap):
|
|
|
|
|
refresh (bool): If already at target zone,
|
|
|
|
|
set false to skip zone switching,
|
|
|
|
|
set true to re-enter current zone to refresh.
|
|
|
|
|
stop_if_safe (bool): Return false if zone is SAFE.
|
|
|
|
|
|
|
|
|
|
Pages:
|
|
|
|
|
in: IN_MAP or IN_GLOBE
|
|
|
|
|
@@ -106,6 +99,8 @@ class OperationSiren(Reward, OSMap):
|
|
|
|
|
if hasattr(self, 'zone'):
|
|
|
|
|
del self.zone
|
|
|
|
|
self.get_current_zone()
|
|
|
|
|
# Fleet repairs before starting if needed
|
|
|
|
|
self.handle_fleet_repair(revert=False)
|
|
|
|
|
# self.map_init()
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
@@ -151,28 +146,21 @@ class OperationSiren(Reward, OSMap):
|
|
|
|
|
self.globe_goto(prev)
|
|
|
|
|
|
|
|
|
|
def handle_fleet_repair(self, revert=True):
|
|
|
|
|
if self.config.OS_REPAIR_THRESHOLD > 0:
|
|
|
|
|
if self.config.OpsiGeneral_RepairThreshold > 0:
|
|
|
|
|
self.hp_get()
|
|
|
|
|
check = [round(data, 2) <= self.config.OS_REPAIR_THRESHOLD if use
|
|
|
|
|
else False for data, use in zip(self.hp, self.hp_has_ship)]
|
|
|
|
|
check = [round(data, 2) <= self.config.OpsiGeneral_RepairThreshold if use else False
|
|
|
|
|
for data, use in zip(self.hp, self.hp_has_ship)]
|
|
|
|
|
if any(check):
|
|
|
|
|
logger.info('At least one ship is below threshold '
|
|
|
|
|
f'{str(int(self.config.OS_REPAIR_THRESHOLD * 100))}%, '
|
|
|
|
|
f'{str(int(self.config.OpsiGeneral_RepairThreshold * 100))}%, '
|
|
|
|
|
'retreating to nearest azur port for repairs')
|
|
|
|
|
self.fleet_repair(revert=revert)
|
|
|
|
|
else:
|
|
|
|
|
logger.info('No ship found to be below threshold '
|
|
|
|
|
f'{str(int(self.config.OS_REPAIR_THRESHOLD * 100))}%, '
|
|
|
|
|
f'{str(int(self.config.OpsiGeneral_RepairThreshold * 100))}%, '
|
|
|
|
|
'continue OS exploration')
|
|
|
|
|
self.hp_reset()
|
|
|
|
|
|
|
|
|
|
def handle_reward(self):
|
|
|
|
|
backup = self.config.cover(DO_OS_IN_DAILY=False)
|
|
|
|
|
if super().handle_reward():
|
|
|
|
|
logger.hr('OS re-init')
|
|
|
|
|
self.os_init()
|
|
|
|
|
backup.recover()
|
|
|
|
|
|
|
|
|
|
def os_port_daily(self, mission=True, supply=True):
|
|
|
|
|
"""
|
|
|
|
|
Accept all missions and buy all supplies in all ports.
|
|
|
|
|
@@ -221,7 +209,6 @@ class OperationSiren(Reward, OSMap):
|
|
|
|
|
bool: True if all finished.
|
|
|
|
|
"""
|
|
|
|
|
logger.hr('OS finish daily mission', level=1)
|
|
|
|
|
backup = self.config.cover(OS_ACTION_POINT_BOX_USE=True)
|
|
|
|
|
while 1:
|
|
|
|
|
result = self.os_get_next_mission2()
|
|
|
|
|
if not result:
|
|
|
|
|
@@ -231,170 +218,95 @@ class OperationSiren(Reward, OSMap):
|
|
|
|
|
self.run_auto_search()
|
|
|
|
|
self.handle_fleet_repair(revert=False)
|
|
|
|
|
|
|
|
|
|
backup.recover()
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def os_meowfficer_farming(self, hazard_level=5, daily=False):
|
|
|
|
|
def os_daily(self):
|
|
|
|
|
if self.config.OpsiDaily_DoMission or self.config.OpsiDaily_BuySupply:
|
|
|
|
|
self.os_finish_daily_mission()
|
|
|
|
|
self.config.check_task_switch()
|
|
|
|
|
self.os_port_daily(mission=self.config.OpsiDaily_DoMission, supply=self.config.OpsiDaily_BuySupply)
|
|
|
|
|
|
|
|
|
|
if self.config.OpsiDaily_DoMission:
|
|
|
|
|
self.os_finish_daily_mission()
|
|
|
|
|
|
|
|
|
|
def os_meowfficer_farming(self):
|
|
|
|
|
"""
|
|
|
|
|
Args:
|
|
|
|
|
hazard_level (int): 1 to 6. Recommend 3 or 5 for higher meowfficer searching point per action points ratio.
|
|
|
|
|
daily (bool): If false, loop until AP lower than OS_ACTION_POINT_PRESERVE.
|
|
|
|
|
If True, loop until run out of AP (not including boxes).
|
|
|
|
|
If True and ENABLE_OS_ASH_ATTACK, loop until ash beacon fully collected today,
|
|
|
|
|
then loop until run out of AP (not including boxes).
|
|
|
|
|
Recommend 3 or 5 for higher meowfficer searching point per action points ratio.
|
|
|
|
|
"""
|
|
|
|
|
logger.hr(f'OS meowfficer farming, hazard_level={hazard_level}', level=1)
|
|
|
|
|
logger.hr(f'OS meowfficer farming, hazard_level={self.config.OpsiMeowfficerFarming_HazardLevel}', level=1)
|
|
|
|
|
while 1:
|
|
|
|
|
self.handle_reward()
|
|
|
|
|
if daily:
|
|
|
|
|
if self.config.ENABLE_OS_ASH_ATTACK:
|
|
|
|
|
if self._ash_fully_collected:
|
|
|
|
|
self.config.OS_ACTION_POINT_BOX_USE = False
|
|
|
|
|
else:
|
|
|
|
|
self.config.OS_ACTION_POINT_BOX_USE = False
|
|
|
|
|
if self.config.OpsiGeneral_AshAttack and not self._ash_fully_collected:
|
|
|
|
|
self.config.OS_ACTION_POINT_PRESERVE = 0
|
|
|
|
|
else:
|
|
|
|
|
self.config.OS_ACTION_POINT_PRESERVE = self.config.OpsiMeowfficerFarming_ActionPointPreserve
|
|
|
|
|
|
|
|
|
|
# (1252, 1012) is the coordinate of zone 134 (the center zone) in os_globe_map.png
|
|
|
|
|
zones = self.zone_select(hazard_level=hazard_level) \
|
|
|
|
|
zones = self.zone_select(hazard_level=self.config.OpsiMeowfficerFarming_HazardLevel) \
|
|
|
|
|
.delete(SelectedGrids([self.zone])) \
|
|
|
|
|
.delete(SelectedGrids(self.zones.select(is_port=True))) \
|
|
|
|
|
.sort_by_clock_degree(center=(1252, 1012), start=self.zone.location)
|
|
|
|
|
|
|
|
|
|
logger.hr(f'OS meowfficer farming, zone_id={zones[0].zone_id}', level=1)
|
|
|
|
|
self.globe_goto(zones[0])
|
|
|
|
|
self.run_auto_search()
|
|
|
|
|
self.handle_fleet_repair(revert=False)
|
|
|
|
|
self.config.check_task_switch()
|
|
|
|
|
|
|
|
|
|
def _clear_os_world(self):
|
|
|
|
|
for hazard_level in range(self.config.OS_WORLD_MIN_LEVEL, (self.config.OS_WORLD_MAX_LEVEL + 1)):
|
|
|
|
|
zones = self.zone_select(hazard_level=hazard_level) \
|
|
|
|
|
.delete(SelectedGrids(self.zones.select(is_port=True))) \
|
|
|
|
|
.sort_by_clock_degree(center=(1252, 1012), start=self.zone.location)
|
|
|
|
|
def os_explore(self):
|
|
|
|
|
logger.hr('OS explore', level=1)
|
|
|
|
|
order = [int(f.strip(' \t\r\n')) for f in self.config.OS_EXPLORE_FILTER.split('>')]
|
|
|
|
|
if self.config.OpsiExplore_LastZone in order:
|
|
|
|
|
order = order[order.index(self.config.OpsiExplore_LastZone) + 1:]
|
|
|
|
|
else:
|
|
|
|
|
logger.warning(f'Invalid OpsiExplore_LastZone={self.config.OpsiExplore_LastZone}, re-explore')
|
|
|
|
|
|
|
|
|
|
for zone in zones:
|
|
|
|
|
self.handle_reward()
|
|
|
|
|
if not self.globe_goto(zone, stop_if_safe=True):
|
|
|
|
|
continue
|
|
|
|
|
self.run_auto_search()
|
|
|
|
|
self.handle_fleet_repair(revert=False)
|
|
|
|
|
for zone in order:
|
|
|
|
|
if not self.globe_goto(zone, stop_if_safe=True):
|
|
|
|
|
logger.info(f'Zone cleared: {self.name_to_zone(zone)}')
|
|
|
|
|
self.config.OpsiExplore_LastZone = zone
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
def clear_os_world(self):
|
|
|
|
|
"""
|
|
|
|
|
Returns:
|
|
|
|
|
bool: If executed.
|
|
|
|
|
"""
|
|
|
|
|
# Force to use AP boxes
|
|
|
|
|
backup = self.config.cover(OS_ACTION_POINT_PRESERVE=40, OS_ACTION_POINT_BOX_USE=True)
|
|
|
|
|
|
|
|
|
|
# Fleet repairs before starting if needed
|
|
|
|
|
self.handle_fleet_repair(revert=False)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
self._clear_os_world()
|
|
|
|
|
except ActionPointLimit:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
backup.recover()
|
|
|
|
|
return True
|
|
|
|
|
logger.hr(f'OS explore {zone}', level=1)
|
|
|
|
|
self.os_order_execute(recon_scan=True)
|
|
|
|
|
self.config.task_delay(minute=30)
|
|
|
|
|
self.run_auto_search()
|
|
|
|
|
self.config.OpsiExplore_LastZone = zone
|
|
|
|
|
logger.info(f'Zone cleared: {self.name_to_zone(zone)}')
|
|
|
|
|
self.handle_fleet_repair(revert=False)
|
|
|
|
|
self.config.check_task_switch()
|
|
|
|
|
|
|
|
|
|
def clear_obscure(self):
|
|
|
|
|
"""
|
|
|
|
|
Returns:
|
|
|
|
|
bool: If executed
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
ActionPointLimit:
|
|
|
|
|
"""
|
|
|
|
|
logger.hr('OS clear obscure zone', level=1)
|
|
|
|
|
result = self.os_get_next_obscure(use_logger=self.config.OS_OBSCURE_USE_LOGGER)
|
|
|
|
|
logger.hr('OS clear obscure', level=1)
|
|
|
|
|
if self.config.OpsiObscure_ForceRun:
|
|
|
|
|
logger.info('OS obscure finish is under force run')
|
|
|
|
|
|
|
|
|
|
result = self.os_get_next_obscure(use_logger=self.config.OpsiObscure_UseLogger)
|
|
|
|
|
if not result:
|
|
|
|
|
# No obscure coordinates, delay next run to tomorrow.
|
|
|
|
|
record = self.config.get_server_last_update(since=(0,)) + timedelta(days=1)
|
|
|
|
|
record = datetime.strftime(record, self.config.TIME_FORMAT)
|
|
|
|
|
self.config.config.set(*RECORD_OBSCURE_FINISH, record)
|
|
|
|
|
self.config.save()
|
|
|
|
|
return False
|
|
|
|
|
self.config.task_delay(server_update=True)
|
|
|
|
|
self.config.task_stop()
|
|
|
|
|
|
|
|
|
|
self.get_current_zone()
|
|
|
|
|
self.os_order_execute(recon_scan=True, submarine_call=self.config.OS_OBSCURE_SUBMARINE_CALL)
|
|
|
|
|
self.os_order_execute(recon_scan=True, submarine_call=self.config.OpsiObscure_CallSubmarine)
|
|
|
|
|
|
|
|
|
|
# Delay next run 30min or 60min.
|
|
|
|
|
delta = 60 if self.config.OS_OBSCURE_SUBMARINE_CALL else 30
|
|
|
|
|
record = datetime.now() + timedelta(minutes=delta)
|
|
|
|
|
record = datetime.strftime(record, self.config.TIME_FORMAT)
|
|
|
|
|
self.config.config.set(*RECORD_OBSCURE_FINISH, record)
|
|
|
|
|
self.config.save()
|
|
|
|
|
delta = 60 if self.config.OpsiObscure_CallSubmarine else 30
|
|
|
|
|
if not self.config.OpsiObscure_ForceRun:
|
|
|
|
|
self.config.task_delay(minute=delta)
|
|
|
|
|
|
|
|
|
|
self.run_auto_search()
|
|
|
|
|
self.map_exit()
|
|
|
|
|
self.handle_fleet_repair(revert=False)
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def os_obscure_finish(self):
|
|
|
|
|
if self.config.OS_OBSCURE_FORCE_RUN:
|
|
|
|
|
logger.info('OS obscure finish is under force run')
|
|
|
|
|
|
|
|
|
|
def os_obscure(self):
|
|
|
|
|
while 1:
|
|
|
|
|
try:
|
|
|
|
|
result = self.clear_obscure()
|
|
|
|
|
except ActionPointLimit:
|
|
|
|
|
self.clear_obscure()
|
|
|
|
|
if self.config.OpsiObscure_ForceRun:
|
|
|
|
|
self.config.check_task_switch()
|
|
|
|
|
continue
|
|
|
|
|
else:
|
|
|
|
|
break
|
|
|
|
|
if not result:
|
|
|
|
|
break
|
|
|
|
|
if not self.config.OS_OBSCURE_FORCE_RUN:
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
def _operation_siren(self, daily=False):
|
|
|
|
|
"""
|
|
|
|
|
Raises:
|
|
|
|
|
ActionPointLimit:
|
|
|
|
|
"""
|
|
|
|
|
mission = self.config.ENABLE_OS_MISSION_ACCEPT \
|
|
|
|
|
and not self.config.record_executed_since(option=RECORD_MISSION_ACCEPT, since=(0,))
|
|
|
|
|
supply = self.config.ENABLE_OS_SUPPLY_BUY \
|
|
|
|
|
and not self.config.record_executed_since(option=RECORD_SUPPLY_BUY, since=(0,))
|
|
|
|
|
if mission or supply:
|
|
|
|
|
# Force to clear all missions before accepting.
|
|
|
|
|
# Because players can only hold 7 mission, and unable to accept the same mission twice.
|
|
|
|
|
self.os_finish_daily_mission()
|
|
|
|
|
if self.os_port_daily(mission=mission, supply=supply):
|
|
|
|
|
if mission:
|
|
|
|
|
self.config.record_save(RECORD_MISSION_ACCEPT)
|
|
|
|
|
if supply:
|
|
|
|
|
self.config.record_save(RECORD_SUPPLY_BUY)
|
|
|
|
|
|
|
|
|
|
# Fleet repairs before starting if needed
|
|
|
|
|
self.handle_fleet_repair(revert=False)
|
|
|
|
|
|
|
|
|
|
finish = self.config.ENABLE_OS_MISSION_FINISH \
|
|
|
|
|
and not self.config.record_executed_since(option=RECORD_MISSION_FINISH, since=(0,))
|
|
|
|
|
if finish:
|
|
|
|
|
if self.os_finish_daily_mission():
|
|
|
|
|
self.config.record_save(RECORD_MISSION_FINISH)
|
|
|
|
|
|
|
|
|
|
if self.config.ENABLE_OS_OBSCURE_FINISH:
|
|
|
|
|
if self.config.OS_OBSCURE_FORCE_RUN:
|
|
|
|
|
self.os_obscure_finish()
|
|
|
|
|
|
|
|
|
|
if self.config.ENABLE_OS_MEOWFFICER_FARMING:
|
|
|
|
|
self.os_meowfficer_farming(hazard_level=self.config.OS_MEOWFFICER_FARMING_LEVEL, daily=daily)
|
|
|
|
|
|
|
|
|
|
def operation_siren(self):
|
|
|
|
|
try:
|
|
|
|
|
self._operation_siren(daily=False)
|
|
|
|
|
except ActionPointLimit:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def operation_siren_daily(self):
|
|
|
|
|
"""
|
|
|
|
|
Returns:
|
|
|
|
|
bool: If executed.
|
|
|
|
|
"""
|
|
|
|
|
# Force to use AP boxes
|
|
|
|
|
backup = self.config.cover(OS_ACTION_POINT_PRESERVE=40)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
self._operation_siren(daily=True)
|
|
|
|
|
except ActionPointLimit:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
backup.recover()
|
|
|
|
|
return True
|
|
|
|
|
|