AzurLaneAutoScript/module/os/map_operation.py

220 lines
7.4 KiB
Python

from module.base.decorator import Config
from module.base.timer import Timer
from module.base.utils import *
from module.exception import ScriptError, MapDetectionError
from module.logger import logger
from module.ocr.ocr import Ocr
from module.os.assets import *
from module.os.globe_zone import Zone
from module.os.map_fleet_selector import OSFleetSelector
from module.os_handler.assets import AUTO_SEARCH_REWARD
from module.os_handler.map_order import MapOrderHandler
from module.os_handler.mission import MissionHandler
from module.os_handler.port import PortHandler
from module.os_handler.storage import StorageHandler
class OSMapOperation(MapOrderHandler, MissionHandler, PortHandler, StorageHandler, OSFleetSelector):
zone: Zone
is_zone_name_hidden = False
def is_meowfficer_searching(self):
"""
Returns:
bool:
Page:
in: IN_MAP
"""
return self.appear(MEOWFFICER_SEARCHING, offset=(10, 10))
def get_meowfficer_searching_percentage(self):
"""
Returns:
float: 0 to 1.
Pages:
in: IN_MAP, is_meowfficer_searching == True
"""
return color_bar_percentage(
self.device.image, area=MEOWFFICER_SEARCHING_PERCENTAGE.area, prev_color=(74, 223, 255))
@Config.when(SERVER='en')
def get_zone_name(self):
# For EN only
from string import whitespace
ocr = Ocr(MAP_NAME, lang='cnocr', letter=(214, 235, 235), threshold=96, name='OCR_OS_MAP_NAME')
name = ocr.ocr(self.device.image)
name = name.translate(dict.fromkeys(map(ord, whitespace)))
name = name.lower()
self.is_zone_name_hidden = 'safe' in name
if '-' in name:
name = name.split('-')[0]
if 'é' in name: # Méditerranée name maps
name = name.replace('é', 'e')
if 'nvcity' in name: # NY City Port read as 'V' rather than 'Y'
name = 'nycity'
return name
@Config.when(SERVER='jp')
def get_zone_name(self):
# For JP only
ocr = Ocr(MAP_NAME, lang='jp', letter=(214, 231, 255), threshold=127, name='OCR_OS_MAP_NAME')
name = ocr.ocr(self.device.image)
self.is_zone_name_hidden = '安全' in name
# Remove punctuations
for char in '':
name = name.replace(char, '')
# Remove '異常海域' and 'セイレーン要塞海域'
if '' in name:
name = name.split('')[0]
if '' in name:
name = name.split('')[0]
# Remove '安全海域' or '秘密海域' at the end of jp ocr.
name = name.rstrip('安全秘密異常要塞海域')
# Kanji '一' and '力' are not used, while Katakana 'ー' and 'カ' are misread as Kanji sometimes.
# Katakana 'ペ' may be misread as Hiragana 'ぺ'.
name = name.replace('', '').replace('', '').replace('', '')
return name
@Config.when(SERVER='tw')
def get_zone_name(self):
# For TW only
ocr = Ocr(MAP_NAME, lang='tw', letter=(198, 215, 239), threshold=127, name='OCR_OS_MAP_NAME')
name = ocr.ocr(self.device.image)
self.is_zone_name_hidden = '安全' in name
# Remove '塞壬要塞海域'
if '' in name:
name = name.split('')[0]
# Remove '安全海域', '隱秘海域', '深淵海域' at the end of tw ocr.
name = name.rstrip('安全隱秘塞壬要塞深淵海域一')
return name
@Config.when(SERVER=None)
def get_zone_name(self):
# For CN only
ocr = Ocr(MAP_NAME, lang='cnocr', letter=(214, 231, 255), threshold=127, name='OCR_OS_MAP_NAME')
name = ocr.ocr(self.device.image)
self.is_zone_name_hidden = '安全' in name
if '-' in name:
name = name.split('-')[0]
else:
name = name.rstrip('安全隐秘塞壬要塞深渊海域-')
return name
def get_current_zone(self):
"""
Returns:
Zone:
Raises:
MapDetectionError: If failed to parse zone name.
ScriptError:
"""
name = self.get_zone_name()
logger.info(f'Map name processed: {name}')
try:
self.zone = self.name_to_zone(name)
except ScriptError as e:
raise MapDetectionError(*e.args)
logger.attr('Zone', self.zone)
return self.zone
def zone_init(self, skip_first_screenshot=True):
"""
Wrap get_current_zone(), set self.zone to the current zone.
This method must be called after entering a new zone.
Handle map events and the animation that zone names appear from the top.
Args:
skip_first_screenshot (bool):
Returns:
Zone: Current zone.
Raises:
MapDetectionError: If failed to parse zone name.
"""
timeout = Timer(1.5, count=5).start()
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
self.device.screenshot()
if timeout.reached():
logger.warning('Zone init timeout')
break
if self.handle_map_event():
timeout.reset()
continue
if self.is_in_map():
try:
return self.get_current_zone()
except MapDetectionError:
continue
else:
timeout.reset()
logger.warning('Unable to get zone name, get current zone from globe map instead')
if hasattr(self, 'get_current_zone_from_globe'):
self.zone = self.get_current_zone_from_globe()
else:
logger.warning('OperationSiren.get_current_zone_from_globe() not exists')
if not self.is_in_map():
logger.warning('Trying to get zone name, but not in OS map')
self.get_current_zone()
logger.attr('Zone', self.zone)
return self.zone
def is_in_special_zone(self):
"""
Returns:
bool: If in an obscure zone, abyssal zone, or stronghold.
"""
return self.appear(MAP_EXIT, offset=(20, 20))
def map_exit(self, skip_first_screenshot=True):
"""
Exit from an obscure zone, abyssal zone, or stronghold.
Args:
skip_first_screenshot:
Pages:
in: is_in_map
out: is_in_map, zone that you came from
"""
logger.hr('Map exit')
confirm_timer = Timer(1, count=2)
changed = False
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
self.device.screenshot()
# End
if changed and self.is_in_map():
if confirm_timer.reached():
break
else:
confirm_timer.reset()
if self.appear_then_click(MAP_EXIT, offset=(20, 20), interval=5):
continue
if self.handle_popup_confirm('MAP_EXIT'):
self.interval_reset(MAP_EXIT)
continue
if self.appear_then_click(AUTO_SEARCH_REWARD, offset=(50, 50)):
# Sometimes appeared
self.device.screenshot_interval_set()
continue
if self.handle_map_event():
self.interval_reset(MAP_EXIT)
changed = True
continue
self.zone_init()