mirror of
https://github.com/LmeSzinc/AzurLaneAutoScript.git
synced 2025-04-04 22:53:31 +08:00
After the update of auto search, ALAS needs to clear and reselect fleets every time before entering a map. 45c50bda294fbbdd9342ebd7b393de3ce6879359 It is better to add some check to ensure that fleets are selected correctly, because emulators may drop clicks randomly.
187 lines
6.7 KiB
Python
187 lines
6.7 KiB
Python
import numpy as np
|
|
from PIL import ImageStat
|
|
|
|
from module.base.base import ModuleBase
|
|
from module.base.button import Button
|
|
from module.base.utils import area_offset, area_cross_area
|
|
from module.handler.assets import INFO_BAR_1
|
|
from module.logger import logger
|
|
from module.map.assets import *
|
|
|
|
|
|
class FleetOperator:
|
|
FLEET_BAR_SHAPE_Y = 36
|
|
FLEET_BAR_MARGIN_Y = 6
|
|
FLEET_BAR_ACTIVE_STD = 45 # Active: 67, inactive: 12.
|
|
FLEET_IN_USE_STD = 20 # In use 52, not in use (3, 6).
|
|
FLEET_PREPARE_OPERATION_SLEEP = (0.25, 0.35)
|
|
|
|
def __init__(self, choose, bar, clear, in_use, main):
|
|
"""
|
|
Args:
|
|
choose(Button):
|
|
bar(Button):
|
|
clear(Button):
|
|
"""
|
|
self._choose = choose
|
|
self._bar = bar
|
|
self._clear = clear
|
|
self._in_use = in_use
|
|
self.main = main
|
|
|
|
def __str__(self):
|
|
return str(self._choose)[:-7]
|
|
|
|
def parse_fleet_bar(self, image):
|
|
"""
|
|
Args:
|
|
image(PIL.Image.Image): Image of fleet choosing bar.
|
|
|
|
Returns:
|
|
list: List of int. Chosen fleet range from 1 to 6.
|
|
"""
|
|
result = []
|
|
for index, y in enumerate(range(0, image.size[1], self.FLEET_BAR_SHAPE_Y + self.FLEET_BAR_MARGIN_Y)):
|
|
area = (0, y, image.size[0], y + self.FLEET_BAR_SHAPE_Y)
|
|
stat = ImageStat.Stat(image.crop(area))
|
|
if np.std(stat.mean, ddof=1) > self.FLEET_BAR_ACTIVE_STD:
|
|
result.append(index + 1)
|
|
logger.info('Current selected: %s' % str(result))
|
|
return result
|
|
|
|
def get_button(self, index):
|
|
"""
|
|
Args:
|
|
index(int): Fleet index, 1-6.
|
|
|
|
Returns:
|
|
Button: Button instance.
|
|
"""
|
|
area = area_offset(area=(
|
|
0,
|
|
(self.FLEET_BAR_SHAPE_Y + self.FLEET_BAR_MARGIN_Y) * (index - 1),
|
|
self._bar.area[2] - self._bar.area[0],
|
|
(self.FLEET_BAR_SHAPE_Y + self.FLEET_BAR_MARGIN_Y) * (index - 1) + self.FLEET_BAR_SHAPE_Y
|
|
), offset=(self._bar.area[0:2]))
|
|
return Button(area=(), color=(), button=area, name='%s_INDEX_%s' % (str(self._bar), str(index)))
|
|
|
|
def allow(self):
|
|
return self.main.appear(self._choose)
|
|
|
|
def clear(self):
|
|
while 1:
|
|
if not self.in_use():
|
|
break
|
|
self.main.device.click(self._clear)
|
|
# No need to sleep because the game reacts quickly here, and nothing
|
|
# goes wrong even if the clear button is clicked multiple times.
|
|
# If we sleep, the info bar of auto search will pop up, then we may
|
|
# have to waste a few seconds for it to disappear in in_use().
|
|
self.main.device.screenshot()
|
|
|
|
def open(self):
|
|
while 1:
|
|
if self.bar_opened():
|
|
break
|
|
self.main.device.click(self._choose)
|
|
# The fleet bar won't open or close immediately after the click,
|
|
# so we need to sleep a while and wait for it.
|
|
# Call sleep() in close() and click() below for the same reason.
|
|
self.main.device.sleep(self.FLEET_PREPARE_OPERATION_SLEEP)
|
|
self.main.device.screenshot()
|
|
|
|
def close(self):
|
|
while 1:
|
|
if not self.bar_opened():
|
|
break
|
|
self.main.device.click(self._choose)
|
|
self.main.device.sleep(self.FLEET_PREPARE_OPERATION_SLEEP)
|
|
self.main.device.screenshot()
|
|
|
|
def click(self, index):
|
|
while 1:
|
|
if not self.bar_opened():
|
|
if self.in_use():
|
|
break
|
|
self.open()
|
|
self.main.device.click(self.get_button(index))
|
|
self.main.device.sleep(self.FLEET_PREPARE_OPERATION_SLEEP)
|
|
self.main.device.screenshot()
|
|
|
|
def selected(self):
|
|
data = self.parse_fleet_bar(self.main.device.image.crop(self._bar.area))
|
|
return data
|
|
|
|
def in_use(self):
|
|
# Handle the info bar of auto search info.
|
|
if area_cross_area(self._in_use.area, INFO_BAR_1.area):
|
|
self.main.handle_info_bar()
|
|
image = np.array(self.main.device.image.crop(self._in_use.area).convert('L'))
|
|
return np.std(image.flatten(), ddof=1) > self.FLEET_IN_USE_STD
|
|
|
|
def bar_opened(self):
|
|
# Check the brightness of the rightest column of the bar area.
|
|
luma = np.array(self.main.device.image.crop(self._bar.area).convert('L'))[:, -1]
|
|
return np.sum(luma > 127) / luma.size > 0.5
|
|
|
|
def ensure_to_be(self, index):
|
|
self.open()
|
|
if index in self.selected():
|
|
self.close()
|
|
else:
|
|
self.click(index)
|
|
|
|
|
|
class FleetPreparation(ModuleBase):
|
|
map_fleet_checked = False
|
|
map_is_hard_mode = False
|
|
|
|
def fleet_preparation(self):
|
|
"""Change fleets.
|
|
|
|
Returns:
|
|
bool: True if changed.
|
|
"""
|
|
logger.info(f'Using fleet: {[self.config.FLEET_1, self.config.FLEET_2, self.config.SUBMARINE]}')
|
|
if self.map_fleet_checked:
|
|
return False
|
|
self.map_is_hard_mode = self.appear(FLEET_PREPARATION_HARD_1) or self.appear(FLEET_PREPARATION_HARD_2)
|
|
if self.map_is_hard_mode:
|
|
logger.info('Hard Campaign. No fleet preparation')
|
|
return False
|
|
|
|
fleet_1 = FleetOperator(
|
|
choose=FLEET_1_CHOOSE, bar=FLEET_1_BAR, clear=FLEET_1_CLEAR, in_use=FLEET_1_IN_USE, main=self)
|
|
fleet_2 = FleetOperator(
|
|
choose=FLEET_2_CHOOSE, bar=FLEET_2_BAR, clear=FLEET_2_CLEAR, in_use=FLEET_2_IN_USE, main=self)
|
|
submarine = FleetOperator(
|
|
choose=SUBMARINE_CHOOSE, bar=SUBMARINE_BAR, clear=SUBMARINE_CLEAR, in_use=SUBMARINE_IN_USE, main=self)
|
|
|
|
# Submarine.
|
|
if submarine.allow():
|
|
if self.config.SUBMARINE:
|
|
submarine.ensure_to_be(self.config.SUBMARINE)
|
|
else:
|
|
submarine.clear()
|
|
|
|
# No need, this may clear FLEET_2 by mistake, clear FLEET_2 in map config.
|
|
# if not fleet_2.allow():
|
|
# self.config.FLEET_2 = 0
|
|
|
|
# Not using fleet 2.
|
|
if not self.config.FLEET_2:
|
|
if fleet_2.allow():
|
|
fleet_2.clear()
|
|
fleet_1.ensure_to_be(self.config.FLEET_1)
|
|
self.map_fleet_checked = True
|
|
return True
|
|
|
|
# Using both fleets.
|
|
# Force to set it again.
|
|
# Fleets may reversed, because AL no longer treat the fleet with smaller index as first fleet
|
|
fleet_2.clear()
|
|
fleet_1.ensure_to_be(self.config.FLEET_1)
|
|
fleet_2.ensure_to_be(self.config.FLEET_2)
|
|
self.map_fleet_checked = True
|
|
return True
|