Add: OpSi abyssal and handle battle status C/D in auto search

This commit is contained in:
LmeSzinc 2021-12-11 00:27:04 +08:00
parent f848fd068d
commit dfd334736f
9 changed files with 158 additions and 5 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@ -21,6 +21,7 @@ SELECT_DANGEROUS = Button(area={'cn': (89, 318, 167, 336), 'en': (82, 254, 173,
SELECT_OBSCURE = Button(area={'cn': (89, 251, 167, 269), 'en': (98, 252, 158, 268), 'jp': (89, 251, 167, 269), 'tw': (88, 250, 167, 270)}, color={'cn': (109, 111, 114), 'en': (96, 97, 101), 'jp': (110, 112, 115), 'tw': (102, 104, 107)}, button={'cn': (89, 251, 167, 269), 'en': (98, 252, 158, 268), 'jp': (89, 251, 167, 269), 'tw': (88, 250, 167, 270)}, file={'cn': './assets/cn/os/SELECT_OBSCURE.png', 'en': './assets/en/os/SELECT_OBSCURE.png', 'jp': './assets/jp/os/SELECT_OBSCURE.png', 'tw': './assets/tw/os/SELECT_OBSCURE.png'})
SELECT_SAFE = Button(area={'cn': (89, 251, 167, 269), 'en': (92, 253, 164, 268), 'jp': (89, 251, 167, 269), 'tw': (88, 250, 167, 270)}, color={'cn': (89, 94, 94), 'en': (103, 109, 108), 'jp': (118, 121, 121), 'tw': (103, 109, 107)}, button={'cn': (89, 251, 167, 269), 'en': (92, 253, 164, 268), 'jp': (89, 251, 167, 269), 'tw': (88, 250, 167, 270)}, file={'cn': './assets/cn/os/SELECT_SAFE.png', 'en': './assets/en/os/SELECT_SAFE.png', 'jp': './assets/jp/os/SELECT_SAFE.png', 'tw': './assets/tw/os/SELECT_SAFE.png'})
SELECT_STRONGHOLD = Button(area={'cn': (89, 251, 166, 269), 'en': (81, 252, 175, 272), 'jp': (89, 251, 167, 269), 'tw': (88, 250, 167, 270)}, color={'cn': (113, 114, 118), 'en': (81, 83, 87), 'jp': (115, 116, 119), 'tw': (103, 105, 108)}, button={'cn': (89, 251, 166, 269), 'en': (81, 252, 175, 272), 'jp': (89, 251, 167, 269), 'tw': (88, 250, 167, 270)}, file={'cn': './assets/cn/os/SELECT_STRONGHOLD.png', 'en': './assets/en/os/SELECT_STRONGHOLD.png', 'jp': './assets/jp/os/SELECT_STRONGHOLD.png', 'tw': './assets/tw/os/SELECT_STRONGHOLD.png'})
STRONGHOLD_PERCENTAGE = Button(area={'cn': (293, 125, 340, 143), 'en': (293, 125, 340, 143), 'jp': (293, 125, 340, 143), 'tw': (293, 125, 340, 143)}, color={'cn': (132, 135, 146), 'en': (132, 135, 146), 'jp': (132, 135, 146), 'tw': (132, 135, 146)}, button={'cn': (293, 125, 340, 143), 'en': (293, 125, 340, 143), 'jp': (293, 125, 340, 143), 'tw': (293, 125, 340, 143)}, file={'cn': './assets/cn/os/STRONGHOLD_PERCENTAGE.png', 'en': './assets/en/os/STRONGHOLD_PERCENTAGE.png', 'jp': './assets/jp/os/STRONGHOLD_PERCENTAGE.png', 'tw': './assets/tw/os/STRONGHOLD_PERCENTAGE.png'})
TEMPLATE_EMPTY_HP = Template(file={'cn': './assets/cn/os/TEMPLATE_EMPTY_HP.png', 'en': './assets/en/os/TEMPLATE_EMPTY_HP.png', 'jp': './assets/jp/os/TEMPLATE_EMPTY_HP.png', 'tw': './assets/tw/os/TEMPLATE_EMPTY_HP.png'})
ZONE_ABYSSAL = Button(area={'cn': (85, 302, 171, 322), 'en': (86, 302, 168, 319), 'jp': (84, 301, 171, 322), 'tw': (84, 301, 172, 322)}, color={'cn': (171, 143, 147), 'en': (176, 136, 138), 'jp': (160, 123, 127), 'tw': (168, 134, 139)}, button={'cn': (85, 302, 171, 322), 'en': (86, 302, 168, 319), 'jp': (84, 301, 171, 322), 'tw': (84, 301, 172, 322)}, file={'cn': './assets/cn/os/ZONE_ABYSSAL.png', 'en': './assets/en/os/ZONE_ABYSSAL.png', 'jp': './assets/jp/os/ZONE_ABYSSAL.png', 'tw': './assets/tw/os/ZONE_ABYSSAL.png'})
ZONE_DANGEROUS = Button(area={'cn': (87, 310, 171, 322), 'en': (71, 310, 184, 319), 'jp': (84, 311, 172, 322), 'tw': (84, 310, 172, 322)}, color={'cn': (153, 177, 197), 'en': (181, 191, 204), 'jp': (139, 167, 189), 'tw': (153, 176, 195)}, button={'cn': (87, 310, 171, 322), 'en': (71, 310, 184, 319), 'jp': (84, 311, 172, 322), 'tw': (84, 310, 172, 322)}, file={'cn': './assets/cn/os/ZONE_DANGEROUS.png', 'en': './assets/en/os/ZONE_DANGEROUS.png', 'jp': './assets/jp/os/ZONE_DANGEROUS.png', 'tw': './assets/tw/os/ZONE_DANGEROUS.png'})

View File

@ -10,7 +10,8 @@ from module.map.fleet import Fleet
from module.map.map_grids import SelectedGrids
from module.map.utils import location_ensure
from module.map_detection.utils import *
from module.os.assets import TEMPLATE_EMPTY_HP
from module.ocr.ocr import Ocr
from module.os.assets import *
from module.os.camera import OSCamera
from module.os.map_base import OSCampaignMap
from module.os_ash.ash import OSAsh
@ -38,6 +39,17 @@ class BossFleet:
__repr__ = __str__
class PercentageOcr(Ocr):
def __init__(self, *args, **kwargs):
kwargs['lang'] = 'azur_lane'
super().__init__(*args, **kwargs)
def pre_process(self, image):
image = super().pre_process(image)
image = np.pad(image, ((2, 2), (0, 0)), mode='constant', constant_values=255)
return image
class OSFleet(OSCamera, Combat, Fleet, OSAsh):
def _goto(self, location, expected=''):
super()._goto(location, expected)
@ -143,6 +155,8 @@ class OSFleet(OSCamera, Combat, Fleet, OSAsh):
def hp_retreat_triggered(self):
return False
need_repair = [False, False, False, False, False, False]
def hp_get(self):
"""
Calculate current HP, also detects the wrench (Ship died, need to repair)
@ -150,6 +164,7 @@ class OSFleet(OSCamera, Combat, Fleet, OSAsh):
super().hp_get()
ship_icon = self._hp_grid().crop((0, -67, 67, 0))
need_repair = [TEMPLATE_EMPTY_HP.match(self.image_area(button)) for button in ship_icon.buttons]
self.need_repair = need_repair
logger.attr('Repair icon', need_repair)
if any(need_repair):
@ -533,3 +548,22 @@ class OSFleet(OSCamera, Combat, Fleet, OSAsh):
self.question_goto(has_fleet_step=True)
result = self.boss_clear(has_fleet_step=True)
return result
def get_stronghold_percentage(self):
"""
Get the clear status in siren stronghold.
Returns:
str: Usually in ['100', '80', '60', '40', '20', '0']
"""
ocr = PercentageOcr(STRONGHOLD_PERCENTAGE, letter=(255, 255, 255), threshold=128, name='STRONGHOLD_PERCENTAGE')
result = ocr.ocr(self.device.image)
result = result.rstrip('7Kk')
for starter in ['100', '80', '60', '40', '20', '0']:
if result.startswith(starter):
result = starter
logger.attr('STRONGHOLD_PERCENTAGE', result)
return result
logger.warning(f'Unexpected STRONGHOLD_PERCENTAGE: {result}')
return result

View File

@ -178,7 +178,10 @@ class OSMap(OSFleet, Map, GlobeCamera):
if self.combat_appear():
self._auto_search_battle_count += 1
logger.attr('battle_count', self._auto_search_battle_count)
self.auto_search_combat()
result = self.auto_search_combat()
if not result:
logger.warning('Fleet died, stop auto search')
break
if self.handle_map_event():
# Auto search can not handle siren searching device.
continue

View File

@ -3,6 +3,7 @@ import numpy as np
from module.exception import MapWalkError, ScriptError, RequestHumanTakeover
from module.logger import logger
from module.map.map_grids import SelectedGrids
from module.os.fleet import BossFleet
from module.os.map import OSMap
from module.reward.reward import Reward
from module.ui.ui import page_os
@ -447,7 +448,10 @@ class OperationSiren(Reward, OSMap):
self.config.task_delay(server_update=True)
self.config.task_stop()
raise NotImplementedError
self.globe_enter(zone)
self.zone_init()
self.os_order_execute(recon_scan=True, submarine_call=False)
self.run_stronghold()
self.fleet_repair(revert=False)
@ -456,6 +460,74 @@ class OperationSiren(Reward, OSMap):
self.clear_stronghold()
self.config.check_task_switch()
def run_stronghold_one_fleet(self, fleet):
"""
Args
fleet (BossFleet):
Returns:
bool: If all cleared.
"""
self.config.override(
OpsiGeneral_BuyAkashiShop=False,
OpsiGeneral_RepairThreshold=0
)
# Try 3 times, because fleet may stuck in fog.
for _ in range(3):
# Attack
self.fleet_set(fleet.fleet_index)
self.run_auto_search()
self.hp_reset()
self.hp_get()
# End
if self.get_stronghold_percentage() == '0':
logger.info('BOSS clear')
return True
elif any(self.need_repair):
logger.info('Auto search stopped, because fleet died')
# Re-enter to reset fleet position
prev = self.zone
self.globe_goto(self.zone_nearest_azur_port(self.zone))
self.globe_goto(prev, types='STRONGHOLD')
return False
else:
logger.info('Auto search stopped, because fleet stuck')
# Re-enter to reset fleet position
prev = self.zone
self.globe_goto(self.zone_nearest_azur_port(self.zone))
self.globe_goto(prev, types='STRONGHOLD')
continue
def run_stronghold(self):
"""
All fleets take turns in attacking siren stronghold.
Returns:
bool: If success to clear.
Pages:
in: Siren logger (abyssal), boss appeared.
out: If success, dangerous or safe zone.
If failed, still in abyssal.
"""
logger.hr(f'Stronghold clear', level=1)
fleets = self.parse_fleet_filter()
for fleet in fleets:
logger.hr(f'Turn: {fleet}', level=2)
if not isinstance(fleet, BossFleet):
self.os_order_execute(recon_scan=False, submarine_call=True)
continue
result = self.run_stronghold_one_fleet(fleet)
if result:
return True
else:
continue
logger.critical('Unable to clear boss, fleets exhausted')
return False
if __name__ == '__main__':
self = OperationSiren('alas', task='OpsiObscure')
@ -464,4 +536,4 @@ if __name__ == '__main__':
self.config = self.config.merge(OSConfig())
self.device.screenshot()
self.os_init()
self.clear_abyssal()
self.clear_stronghold()

View File

@ -152,8 +152,43 @@ class Combat(Combat_, MapEventHandler):
logger.info('Continuous combat detected')
continue
def handle_auto_search_battle_status(self, drop=None):
if self.appear(BATTLE_STATUS_C, interval=self.battle_status_click_interval):
logger.warning('Battle Status C')
# raise GameStuckError('Battle status C')
if drop:
drop.handle_add(self)
else:
self.device.sleep((0.25, 0.5))
self.device.click(BATTLE_STATUS_C)
return True
if self.appear(BATTLE_STATUS_D, interval=self.battle_status_click_interval):
logger.warning('Battle Status D')
# raise GameStuckError('Battle Status D')
if drop:
drop.handle_add(self)
else:
self.device.sleep((0.25, 0.5))
self.device.click(BATTLE_STATUS_D)
return True
return False
def handle_auto_search_exp_info(self):
if self.appear_then_click(EXP_INFO_C):
self.device.sleep((0.25, 0.5))
return True
if self.appear_then_click(EXP_INFO_D):
self.device.sleep((0.25, 0.5))
return True
return False
def auto_search_combat(self):
"""
Returns:
bool: True if enemy cleared, False if fleet died.
Pages:
in: is_combat_loading()
out: combat status
@ -180,17 +215,24 @@ class Combat(Combat_, MapEventHandler):
if self.config.Submarine_Fleet:
submarine_mode = self.config.Submarine_Mode
success = True
while 1:
self.device.screenshot()
if self.handle_submarine_call(submarine_mode):
continue
if self.handle_os_auto_search_map_option():
if success and self.handle_os_auto_search_map_option():
continue
# End
if self.is_combat_executing():
continue
if self.handle_auto_search_battle_status():
success = False
continue
if self.handle_auto_search_exp_info():
success = False
continue
if self.handle_map_event():
continue
if self.is_in_map():
@ -198,3 +240,4 @@ class Combat(Combat_, MapEventHandler):
break
logger.info('Combat end.')
return success