mirror of
https://github.com/LmeSzinc/AzurLaneAutoScript.git
synced 2025-01-09 10:17:38 +08:00
Refactor: Guild module
- Simplify guild exchange with Filter - Expose submarine mode
This commit is contained in:
parent
ca5a14a2dd
commit
c8c1cd9789
4
alas.py
4
alas.py
@ -20,6 +20,7 @@ from module.logger import logger, log_file
|
|||||||
from module.research.research import RewardResearch
|
from module.research.research import RewardResearch
|
||||||
from module.reward.reward import Reward
|
from module.reward.reward import Reward
|
||||||
from module.tactical.tactical_class import RewardTacticalClass
|
from module.tactical.tactical_class import RewardTacticalClass
|
||||||
|
from module.guild.guild_reward import RewardGuild
|
||||||
|
|
||||||
|
|
||||||
class AzurLaneAutoScript:
|
class AzurLaneAutoScript:
|
||||||
@ -115,6 +116,9 @@ class AzurLaneAutoScript:
|
|||||||
def reward(self):
|
def reward(self):
|
||||||
Reward(config=self.config, device=self.device).run()
|
Reward(config=self.config, device=self.device).run()
|
||||||
|
|
||||||
|
def guild(self):
|
||||||
|
RewardGuild(config=self.config, device=self.device).run()
|
||||||
|
|
||||||
def main(self):
|
def main(self):
|
||||||
CampaignRun(config=self.config, device=self.device).run(
|
CampaignRun(config=self.config, device=self.device).run(
|
||||||
name=self.config.Campaign_Name,
|
name=self.config.Campaign_Name,
|
||||||
|
@ -252,3 +252,23 @@ Reward:
|
|||||||
CollectOil: true
|
CollectOil: true
|
||||||
CollectCoin: true
|
CollectCoin: true
|
||||||
CollectMission: true
|
CollectMission: true
|
||||||
|
Guild:
|
||||||
|
Scheduler:
|
||||||
|
Enable: false
|
||||||
|
NextRun: 2020-01-01 00:00:00
|
||||||
|
Command: Guild
|
||||||
|
SuccessInterval: 40-60
|
||||||
|
FailureInterval: 40-60
|
||||||
|
ServerUpdate: 00:00, 12:00, 18:00, 21:00
|
||||||
|
Logistics:
|
||||||
|
Enable: true
|
||||||
|
ExchangeFilter: |-
|
||||||
|
PlateTorpedoT1 > PlateAntiAirT1 > PlatePlaneT1 > PlateGunT1 > PlateGeneralT1
|
||||||
|
> PlateTorpedoT2 > PlateAntiAirT2 > PlatePlaneT2 > PlateGunT2 > PlateGeneralT2
|
||||||
|
> PlateTorpedoT3 > PlateAntiAirT3 > PlatePlaneT3 > PlateGunT3 > PlateGeneralT3
|
||||||
|
> OxyCola > Coolant > Merit > Coins > Oil
|
||||||
|
Operation:
|
||||||
|
Enable: true
|
||||||
|
JoinThreshold: 1
|
||||||
|
AttackBoss: true
|
||||||
|
BossFleetRecommend: false
|
||||||
|
@ -196,11 +196,11 @@ class Combat(Level, HPBalancer, Retirement, SubmarineCall, CombatAuto, CombatMan
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def combat_execute(self, auto='combat_auto', call_submarine_at_boss=False, drop=None):
|
def combat_execute(self, auto='combat_auto', submarine='do_not_use', drop=None):
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
auto (str): Combat auto mode.
|
auto (str): ['combat_auto', 'combat_manual', 'stand_still_in_the_middle', 'hide_in_bottom_left']
|
||||||
call_submarine_at_boss (bool):
|
submarine (str): ['do_not_use', 'hunt_only', 'every_combat']
|
||||||
drop (DropImage):
|
drop (DropImage):
|
||||||
"""
|
"""
|
||||||
logger.info('Combat execute')
|
logger.info('Combat execute')
|
||||||
@ -226,11 +226,8 @@ class Combat(Level, HPBalancer, Retirement, SubmarineCall, CombatAuto, CombatMan
|
|||||||
if auto != 'combat_auto' and self.auto_mode_checked and self.is_combat_executing():
|
if auto != 'combat_auto' and self.auto_mode_checked and self.is_combat_executing():
|
||||||
if self.handle_combat_weapon_release():
|
if self.handle_combat_weapon_release():
|
||||||
continue
|
continue
|
||||||
if call_submarine_at_boss:
|
if self.handle_submarine_call(submarine):
|
||||||
pass
|
continue
|
||||||
else:
|
|
||||||
if self.handle_submarine_call():
|
|
||||||
continue
|
|
||||||
if self.handle_popup_confirm('COMBAT_EXECUTE'):
|
if self.handle_popup_confirm('COMBAT_EXECUTE'):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -408,7 +405,7 @@ class Combat(Level, HPBalancer, Retirement, SubmarineCall, CombatAuto, CombatMan
|
|||||||
if expected_end():
|
if expected_end():
|
||||||
break
|
break
|
||||||
|
|
||||||
def combat(self, balance_hp=None, emotion_reduce=None, auto_mode=None, call_submarine_at_boss=None,
|
def combat(self, balance_hp=None, emotion_reduce=None, auto_mode=None, submarine_mode=None,
|
||||||
save_get_items=None, expected_end=None, fleet_index=1):
|
save_get_items=None, expected_end=None, fleet_index=1):
|
||||||
"""
|
"""
|
||||||
Execute a combat.
|
Execute a combat.
|
||||||
@ -418,7 +415,7 @@ class Combat(Level, HPBalancer, Retirement, SubmarineCall, CombatAuto, CombatMan
|
|||||||
balance_hp (bool):
|
balance_hp (bool):
|
||||||
emotion_reduce (bool):
|
emotion_reduce (bool):
|
||||||
auto_mode (str): combat_auto, combat_manual, stand_still_in_the_middle, hide_in_bottom_left
|
auto_mode (str): combat_auto, combat_manual, stand_still_in_the_middle, hide_in_bottom_left
|
||||||
call_submarine_at_boss (bool):
|
submarine_mode (str): do_not_use, hunt_only, every_combat
|
||||||
save_get_items (bool):
|
save_get_items (bool):
|
||||||
expected_end (str, callable):
|
expected_end (str, callable):
|
||||||
fleet_index (int): 1 or 2
|
fleet_index (int): 1 or 2
|
||||||
@ -427,7 +424,9 @@ class Combat(Level, HPBalancer, Retirement, SubmarineCall, CombatAuto, CombatMan
|
|||||||
emotion_reduce = emotion_reduce if emotion_reduce is not None else self.config.Emotion_CalculateEmotion
|
emotion_reduce = emotion_reduce if emotion_reduce is not None else self.config.Emotion_CalculateEmotion
|
||||||
if auto_mode is None:
|
if auto_mode is None:
|
||||||
auto_mode = self.config.Fleet_Fleet1Mode if fleet_index == 1 else self.config.Fleet_Fleet2Mode
|
auto_mode = self.config.Fleet_Fleet1Mode if fleet_index == 1 else self.config.Fleet_Fleet2Mode
|
||||||
call_submarine_at_boss = call_submarine_at_boss if call_submarine_at_boss is not None else False
|
if submarine_mode is None:
|
||||||
|
if self.config.Submarine_Fleet:
|
||||||
|
submarine_mode = self.config.Submarine_Mode
|
||||||
self.battle_status_click_interval = 7 if save_get_items else 0
|
self.battle_status_click_interval = 7 if save_get_items else 0
|
||||||
|
|
||||||
# if not hasattr(self, 'emotion'):
|
# if not hasattr(self, 'emotion'):
|
||||||
@ -441,7 +440,7 @@ class Combat(Level, HPBalancer, Retirement, SubmarineCall, CombatAuto, CombatMan
|
|||||||
self.combat_preparation(
|
self.combat_preparation(
|
||||||
balance_hp=balance_hp, emotion_reduce=emotion_reduce, auto=auto_mode, fleet_index=fleet_index)
|
balance_hp=balance_hp, emotion_reduce=emotion_reduce, auto=auto_mode, fleet_index=fleet_index)
|
||||||
self.combat_execute(
|
self.combat_execute(
|
||||||
auto=auto_mode, call_submarine_at_boss=call_submarine_at_boss, drop=drop)
|
auto=auto_mode, submarine=submarine_mode, drop=drop)
|
||||||
self.combat_status(
|
self.combat_status(
|
||||||
drop=drop, expected_end=expected_end)
|
drop=drop, expected_end=expected_end)
|
||||||
self.handle_map_after_combat_story()
|
self.handle_map_after_combat_story()
|
||||||
|
@ -16,14 +16,14 @@ class SubmarineCall(ModuleBase):
|
|||||||
self.submarine_call_timer.reset()
|
self.submarine_call_timer.reset()
|
||||||
self.submarine_call_flag = False
|
self.submarine_call_flag = False
|
||||||
|
|
||||||
def handle_submarine_call(self):
|
def handle_submarine_call(self, submarine='do_not_use'):
|
||||||
"""
|
"""
|
||||||
Returns:
|
Returns:
|
||||||
bool: If call.
|
str: If call.
|
||||||
"""
|
"""
|
||||||
if self.submarine_call_flag:
|
if self.submarine_call_flag:
|
||||||
return False
|
return False
|
||||||
if not self.config.Submarine_Fleet or self.config.Submarine_Mode in ['do_not_use', 'hunt_only']:
|
if submarine in ['do_not_use', 'hunt_only']:
|
||||||
self.submarine_call_flag = True
|
self.submarine_call_flag = True
|
||||||
return False
|
return False
|
||||||
if self.submarine_call_timer.reached():
|
if self.submarine_call_timer.reached():
|
||||||
|
@ -238,7 +238,7 @@ GemsFarming:
|
|||||||
option: [0, 1, 2]
|
option: [0, 1, 2]
|
||||||
Mode:
|
Mode:
|
||||||
value: do_not_use
|
value: do_not_use
|
||||||
option: ['do_not_use','hunt_only', 'every_combat']
|
option: ['do_not_use', 'hunt_only', 'every_combat']
|
||||||
Emotion:
|
Emotion:
|
||||||
CalculateEmotion: true
|
CalculateEmotion: true
|
||||||
IgnoreLowEmotionWarn: false
|
IgnoreLowEmotionWarn: false
|
||||||
@ -360,3 +360,25 @@ Reward:
|
|||||||
CollectOil: true
|
CollectOil: true
|
||||||
CollectCoin: true
|
CollectCoin: true
|
||||||
CollectMission: true
|
CollectMission: true
|
||||||
|
Guild:
|
||||||
|
_info:
|
||||||
|
Menu: Reward
|
||||||
|
Scheduler:
|
||||||
|
Enable: false
|
||||||
|
NextRun: 2020-01-01 00:00:00
|
||||||
|
Command: Guild
|
||||||
|
SuccessInterval: 40-60
|
||||||
|
FailureInterval: 40-60
|
||||||
|
ServerUpdate: 00:00, 12:00, 18:00, 21:00
|
||||||
|
Logistics:
|
||||||
|
Enable: true
|
||||||
|
ExchangeFilter: |-
|
||||||
|
PlateTorpedoT1 > PlateAntiAirT1 > PlatePlaneT1 > PlateGunT1 > PlateGeneralT1
|
||||||
|
> PlateTorpedoT2 > PlateAntiAirT2 > PlatePlaneT2 > PlateGunT2 > PlateGeneralT2
|
||||||
|
> PlateTorpedoT3 > PlateAntiAirT3 > PlatePlaneT3 > PlateGunT3 > PlateGeneralT3
|
||||||
|
> OxyCola > Coolant > Merit > Coins > Oil
|
||||||
|
Operation:
|
||||||
|
Enable: true
|
||||||
|
JoinThreshold: 1
|
||||||
|
AttackBoss: true
|
||||||
|
BossFleetRecommend: false
|
||||||
|
@ -124,3 +124,11 @@ class GeneratedConfig:
|
|||||||
Reward_CollectOil = True
|
Reward_CollectOil = True
|
||||||
Reward_CollectCoin = True
|
Reward_CollectCoin = True
|
||||||
Reward_CollectMission = True
|
Reward_CollectMission = True
|
||||||
|
|
||||||
|
# Func `Guild`
|
||||||
|
Logistics_Enable = True
|
||||||
|
Logistics_ExchangeFilter = 'PlateTorpedoT1 > PlateAntiAirT1 > PlatePlaneT1 > PlateGunT1 > PlateGeneralT1\n> PlateTorpedoT2 > PlateAntiAirT2 > PlatePlaneT2 > PlateGunT2 > PlateGeneralT2\n> PlateTorpedoT3 > PlateAntiAirT3 > PlatePlaneT3 > PlateGunT3 > PlateGeneralT3\n> OxyCola > Coolant > Merit > Coins > Oil'
|
||||||
|
Operation_Enable = True
|
||||||
|
Operation_JoinThreshold = 1
|
||||||
|
Operation_AttackBoss = True
|
||||||
|
Operation_BossFleetRecommend = False
|
||||||
|
@ -6,8 +6,7 @@ class ManualConfig:
|
|||||||
|
|
||||||
SCHEDULER_PRIORITY = """
|
SCHEDULER_PRIORITY = """
|
||||||
Restart
|
Restart
|
||||||
> Research > Commission > Tactical > Dorm > Reward
|
> Research > Commission > Tactical > Dorm > Guild > Reward
|
||||||
> GuildLogistics > GuildOperation
|
|
||||||
> Meowfficer > Gacha > Shop
|
> Meowfficer > Gacha > Shop
|
||||||
> OpsiObscure
|
> OpsiObscure
|
||||||
> Exercise > Daily > Hard > OpsiAsh
|
> Exercise > Daily > Hard > OpsiAsh
|
||||||
|
@ -1,27 +1,11 @@
|
|||||||
from datetime import datetime, timedelta
|
|
||||||
|
|
||||||
from module.base.decorator import cached_property
|
|
||||||
from module.base.utils import ensure_time
|
|
||||||
from module.guild.assets import GUILD_RED_DOT
|
|
||||||
from module.guild.lobby import GuildLobby
|
from module.guild.lobby import GuildLobby
|
||||||
from module.guild.logistics import GuildLogistics, RECORD_OPTION_LOGISTICS, RECORD_SINCE_LOGISTICS
|
from module.guild.logistics import GuildLogistics
|
||||||
from module.guild.operations import GuildOperations, RECORD_OPTION_DISPATCH, RECORD_SINCE_DISPATCH
|
from module.guild.operations import GuildOperations
|
||||||
from module.logger import logger
|
from module.ui.ui import page_guild, page_main
|
||||||
from module.ui.ui import page_guild
|
|
||||||
|
|
||||||
GUILD_RECORD = ('RewardRecord', 'guild')
|
|
||||||
|
|
||||||
|
|
||||||
class RewardGuild(GuildLobby, GuildLogistics, GuildOperations):
|
class RewardGuild(GuildLobby, GuildLogistics, GuildOperations):
|
||||||
@cached_property
|
def run(self):
|
||||||
def guild_interval(self):
|
|
||||||
return int(ensure_time(self.config.GUILD_INTERVAL, precision=3) * 60)
|
|
||||||
|
|
||||||
def guild_interval_reset(self):
|
|
||||||
""" Call this method after guild run executed """
|
|
||||||
del self.__dict__['guild_interval']
|
|
||||||
|
|
||||||
def handle_guild(self):
|
|
||||||
"""
|
"""
|
||||||
ALAS handler function for guild reward loop
|
ALAS handler function for guild reward loop
|
||||||
|
|
||||||
@ -32,43 +16,28 @@ class RewardGuild(GuildLobby, GuildLogistics, GuildOperations):
|
|||||||
in: page_main
|
in: page_main
|
||||||
out: page_main
|
out: page_main
|
||||||
"""
|
"""
|
||||||
if not self.config.ENABLE_GUILD_LOGISTICS and not self.config.ENABLE_GUILD_OPERATIONS:
|
if not self.config.Logistics_Enable and not self.config.Operation_Enable:
|
||||||
return False
|
self.config.Scheduler_Enable = False
|
||||||
|
self.config.task_stop()
|
||||||
now = datetime.now()
|
|
||||||
guild_record = datetime.strptime(self.config.config.get(*GUILD_RECORD), self.config.TIME_FORMAT)
|
|
||||||
update = guild_record + timedelta(seconds=self.guild_interval)
|
|
||||||
attr = f'{GUILD_RECORD[0]}_{GUILD_RECORD[1]}'
|
|
||||||
logger.attr(f'{attr}', f'Record time: {guild_record}')
|
|
||||||
logger.attr(f'{attr}', f'Next update: {update}')
|
|
||||||
if not now > update:
|
|
||||||
return False
|
|
||||||
if not self.appear(GUILD_RED_DOT, offset=(30, 30)):
|
|
||||||
logger.info('Guild red dot not appears, skip current check')
|
|
||||||
self.config.record_save(option=GUILD_RECORD)
|
|
||||||
return False
|
|
||||||
|
|
||||||
self.ui_ensure(page_guild)
|
self.ui_ensure(page_guild)
|
||||||
|
success = True
|
||||||
|
|
||||||
# Lobby
|
# Lobby
|
||||||
self.guild_lobby()
|
self.guild_lobby()
|
||||||
|
|
||||||
# Logistics
|
# Logistics
|
||||||
if self.config.ENABLE_GUILD_LOGISTICS \
|
if self.config.Logistics_Enable:
|
||||||
and not self.config.record_executed_since(option=RECORD_OPTION_LOGISTICS, since=RECORD_SINCE_LOGISTICS):
|
success &= self.guild_logistics()
|
||||||
# Save record when guild mission has been finished this week
|
|
||||||
# If finished, only need to check guild logostics once a day
|
|
||||||
# If not finished, check it in every guild reward
|
|
||||||
if self.guild_logistics():
|
|
||||||
self.config.record_save(option=RECORD_OPTION_LOGISTICS)
|
|
||||||
|
|
||||||
# Operation
|
# Operation
|
||||||
if self.config.ENABLE_GUILD_OPERATIONS \
|
if self.config.Operation_Enable:
|
||||||
and not self.config.record_executed_since(option=RECORD_OPTION_DISPATCH, since=RECORD_SINCE_DISPATCH):
|
success &= self.guild_operations()
|
||||||
# Check guild operation 4 times a day
|
|
||||||
self.guild_operations()
|
|
||||||
|
|
||||||
self.guild_interval_reset()
|
self.ui_goto(page_main)
|
||||||
self.config.record_save(option=GUILD_RECORD)
|
|
||||||
self.ui_goto_main()
|
# Scheduler
|
||||||
return True
|
if success:
|
||||||
|
self.config.task_delay(server_update=True)
|
||||||
|
else:
|
||||||
|
self.config.task_delay(success=False, server_update=True)
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
from module.base.button import ButtonGrid
|
from module.base.button import ButtonGrid
|
||||||
from module.base.decorator import cached_property, Config
|
from module.base.decorator import cached_property, Config
|
||||||
|
from module.base.filter import Filter
|
||||||
from module.base.timer import Timer
|
from module.base.timer import Timer
|
||||||
from module.base.utils import *
|
from module.base.utils import *
|
||||||
from module.combat.assets import GET_ITEMS_1
|
from module.combat.assets import GET_ITEMS_1
|
||||||
@ -10,34 +13,11 @@ from module.logger import logger
|
|||||||
from module.ocr.ocr import Digit
|
from module.ocr.ocr import Digit
|
||||||
from module.statistics.item import ItemGrid
|
from module.statistics.item import ItemGrid
|
||||||
|
|
||||||
RECORD_OPTION_LOGISTICS = ('RewardRecord', 'logistics')
|
|
||||||
RECORD_SINCE_LOGISTICS = (0,)
|
|
||||||
|
|
||||||
EXCHANGE_GRIDS = ButtonGrid(
|
EXCHANGE_GRIDS = ButtonGrid(
|
||||||
origin=(470, 470), delta=(198.5, 0), button_shape=(83, 83), grid_shape=(3, 1), name='EXCHANGE_GRIDS')
|
origin=(470, 470), delta=(198.5, 0), button_shape=(83, 83), grid_shape=(3, 1), name='EXCHANGE_GRIDS')
|
||||||
EXCHANGE_BUTTONS = ButtonGrid(
|
EXCHANGE_BUTTONS = ButtonGrid(
|
||||||
origin=(440, 609), delta=(198.5, 0), button_shape=(144, 31), grid_shape=(3, 1), name='EXCHANGE_BUTTONS')
|
origin=(440, 609), delta=(198.5, 0), button_shape=(144, 31), grid_shape=(3, 1), name='EXCHANGE_BUTTONS')
|
||||||
|
EXCHANGE_FILTER = Filter(regex=re.compile('^(.*?)$'), attr=('name',))
|
||||||
DEFAULT_ITEM_PRIORITY = [
|
|
||||||
't1',
|
|
||||||
't2',
|
|
||||||
't3',
|
|
||||||
'oxycola',
|
|
||||||
'coolant',
|
|
||||||
'coins',
|
|
||||||
'oil',
|
|
||||||
'merit'
|
|
||||||
]
|
|
||||||
|
|
||||||
DEFAULT_PLATE_PRIORITY = [
|
|
||||||
'torpedo',
|
|
||||||
'antiair',
|
|
||||||
'plane',
|
|
||||||
'gun',
|
|
||||||
'general'
|
|
||||||
]
|
|
||||||
|
|
||||||
GRADES = [s for s in DEFAULT_ITEM_PRIORITY if len(s) == 2]
|
|
||||||
|
|
||||||
|
|
||||||
class ExchangeLimitOcr(Digit):
|
class ExchangeLimitOcr(Digit):
|
||||||
@ -348,179 +328,37 @@ class GuildLogistics(GuildBase):
|
|||||||
|
|
||||||
logger.info(f'supply_checked: {supply_checked}, mission_checked: {mission_checked}, '
|
logger.info(f'supply_checked: {supply_checked}, mission_checked: {mission_checked}, '
|
||||||
f'exchange_checked: {exchange_checked}, mission_finished: {self._guild_logistics_mission_finished}')
|
f'exchange_checked: {exchange_checked}, mission_finished: {self._guild_logistics_mission_finished}')
|
||||||
return all([supply_checked, mission_checked, exchange_checked, self._guild_logistics_mission_finished])
|
# Azur Lane receives new guild missions now
|
||||||
|
# No longer consider `self._guild_logistics_mission_finished` as a check
|
||||||
@staticmethod
|
return all([supply_checked, mission_checked, exchange_checked])
|
||||||
def _guild_exchange_priorities_helper(title, string_priority, default_priority):
|
|
||||||
"""
|
|
||||||
Helper for _guild_exchange_priorities for repeated usage
|
|
||||||
|
|
||||||
Use defaults if configurations are found
|
|
||||||
invalid in any manner
|
|
||||||
|
|
||||||
Pages:
|
|
||||||
in: GUILD_LOGISTICS
|
|
||||||
out: GUILD_LOGISTICS
|
|
||||||
"""
|
|
||||||
# Parse the string to a list, perform any special processing when applicable
|
|
||||||
priority_parsed = [s.strip().lower() for s in string_priority.split('>')]
|
|
||||||
priority_parsed = list(filter(''.__ne__, priority_parsed))
|
|
||||||
priority = priority_parsed.copy()
|
|
||||||
[priority.remove(s) for s in priority_parsed if s not in default_priority]
|
|
||||||
|
|
||||||
# If after all that processing, result is empty list, then use default
|
|
||||||
if len(priority) == 0:
|
|
||||||
priority = default_priority
|
|
||||||
|
|
||||||
# logger.info(f'{title:10}: {priority}')
|
|
||||||
return priority
|
|
||||||
|
|
||||||
def _guild_exchange_priorities(self):
|
|
||||||
"""
|
|
||||||
Set up priorities lists and dictionaries
|
|
||||||
based on configurations
|
|
||||||
|
|
||||||
Pages:
|
|
||||||
in: GUILD_LOGISTICS
|
|
||||||
out: GUILD_LOGISTICS
|
|
||||||
"""
|
|
||||||
# Items
|
|
||||||
item_priority = self._guild_exchange_priorities_helper(
|
|
||||||
'Item', self.config.GUILD_LOGISTICS_ITEM_ORDER_STRING, DEFAULT_ITEM_PRIORITY)
|
|
||||||
|
|
||||||
# T1 Grade Plates
|
|
||||||
t1_priority = self._guild_exchange_priorities_helper(
|
|
||||||
'T1 Plate', self.config.GUILD_LOGISTICS_PLATE_T1_ORDER_STRING, DEFAULT_PLATE_PRIORITY)
|
|
||||||
|
|
||||||
# T2 Grade Plates
|
|
||||||
t2_priority = self._guild_exchange_priorities_helper(
|
|
||||||
'T2 Plate', self.config.GUILD_LOGISTICS_PLATE_T2_ORDER_STRING, DEFAULT_PLATE_PRIORITY)
|
|
||||||
|
|
||||||
# T3 Grade Plates
|
|
||||||
t3_priority = self._guild_exchange_priorities_helper(
|
|
||||||
'T3 Plate', self.config.GUILD_LOGISTICS_PLATE_T3_ORDER_STRING, DEFAULT_PLATE_PRIORITY)
|
|
||||||
|
|
||||||
# Build dictionary
|
|
||||||
grade_to_plate_priorities = dict()
|
|
||||||
grade_to_plate_priorities['t1'] = t1_priority
|
|
||||||
grade_to_plate_priorities['t2'] = t2_priority
|
|
||||||
grade_to_plate_priorities['t3'] = t3_priority
|
|
||||||
|
|
||||||
return item_priority, grade_to_plate_priorities
|
|
||||||
|
|
||||||
def _guild_exchange_scan(self):
|
def _guild_exchange_scan(self):
|
||||||
"""
|
"""
|
||||||
Image scan of available options
|
Image scan of available options.
|
||||||
to be exchanged. Summarizes matching
|
Not exchangeable items are tagged enough=False.
|
||||||
templates and whether red text present
|
|
||||||
in a list of tuples
|
Returns:
|
||||||
|
list[Item]:
|
||||||
|
|
||||||
Pages:
|
Pages:
|
||||||
in: GUILD_LOGISTICS
|
in: GUILD_LOGISTICS
|
||||||
out: GUILD_LOGISTICS
|
out: GUILD_LOGISTICS
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Scan the available exchange items that are selectable
|
# Scan the available exchange items that are selectable
|
||||||
self.exchange_items._load_image(self.device.image)
|
items = self.exchange_items.predict(self.device.image, name=True, amount=False)
|
||||||
name = [self.exchange_items.match_template(item.image) for item in self.exchange_items.items]
|
|
||||||
name = [str(item).lower() for item in name]
|
|
||||||
|
|
||||||
# Loop EXCHANGE_GRIDS to detect for red text in bottom right area
|
# Loop EXCHANGE_GRIDS to detect for red text in bottom right area
|
||||||
# indicating player lacks inventory for that item
|
# indicating player lacks inventory for that item
|
||||||
in_red_list = []
|
for item, button in zip(items, EXCHANGE_GRIDS.buttons):
|
||||||
for button in EXCHANGE_GRIDS.buttons:
|
|
||||||
area = area_offset((35, 64, 83, 83), button.area[0:2])
|
area = area_offset((35, 64, 83, 83), button.area[0:2])
|
||||||
if self.image_color_count(area, color=(255, 93, 90), threshold=221, count=20):
|
if self.image_color_count(area, color=(255, 93, 90), threshold=221, count=20):
|
||||||
in_red_list.append(True)
|
item.enough = False
|
||||||
else:
|
else:
|
||||||
in_red_list.append(False)
|
item.enough = True
|
||||||
|
|
||||||
# Zip contents of both lists into tuples
|
text = [str(item.name) if item.enough else str(item.name) + ' (not enough)' for item in items]
|
||||||
return zip(name, in_red_list)
|
logger.info(f'Exchange items: {", ".join(text)}')
|
||||||
|
return items
|
||||||
@staticmethod
|
|
||||||
def _guild_exchange_check(options, item_priority, grade_to_plate_priorities):
|
|
||||||
"""
|
|
||||||
Sift through all exchangeable options
|
|
||||||
Record details on each to determine
|
|
||||||
selection order
|
|
||||||
|
|
||||||
Pages:
|
|
||||||
in: GUILD_LOGISTICS
|
|
||||||
out: GUILD_LOGISTICS
|
|
||||||
"""
|
|
||||||
# Contains the details of all options
|
|
||||||
choices = dict()
|
|
||||||
|
|
||||||
for i, (option, in_red) in enumerate(options):
|
|
||||||
# Options already sorted sequentially
|
|
||||||
# Button indexes are in sync
|
|
||||||
btn = EXCHANGE_BUTTONS[i, 0]
|
|
||||||
|
|
||||||
# Defaults set absurd values, which tells ALAS to skip option
|
|
||||||
item_weight = len(DEFAULT_ITEM_PRIORITY)
|
|
||||||
plate_weight = len(DEFAULT_PLATE_PRIORITY)
|
|
||||||
can_exchange = False
|
|
||||||
|
|
||||||
# Player lacks inventory of this item
|
|
||||||
# so leave this choice under all defaults
|
|
||||||
# to skip
|
|
||||||
if not in_red:
|
|
||||||
# Plate perhaps, extract last
|
|
||||||
# 2 characters to ensure
|
|
||||||
grade = option[-2:]
|
|
||||||
if grade in GRADES:
|
|
||||||
item_weight = item_priority.index(grade)
|
|
||||||
can_exchange = True
|
|
||||||
|
|
||||||
plate_priority = grade_to_plate_priorities.get(grade)
|
|
||||||
plate_name = option[5:-2]
|
|
||||||
if plate_name in plate_priority:
|
|
||||||
plate_weight = plate_priority.index(plate_name)
|
|
||||||
|
|
||||||
# Did weight update?
|
|
||||||
# If not, then this choice given less priority
|
|
||||||
# also set to absurd cost to avoid using
|
|
||||||
if plate_weight == len(DEFAULT_PLATE_PRIORITY):
|
|
||||||
item_weight = len(DEFAULT_ITEM_PRIORITY)
|
|
||||||
can_exchange = False
|
|
||||||
|
|
||||||
# Else normal item, check normally
|
|
||||||
# Plates are skipped since only grade in priority
|
|
||||||
if option in item_priority:
|
|
||||||
item_weight = item_priority.index(option)
|
|
||||||
can_exchange = True
|
|
||||||
|
|
||||||
choices[f'{i + 1}'] = [item_weight, plate_weight, i + 1, can_exchange, btn]
|
|
||||||
logger.info(f'Choice #{i + 1} - Name: {option:15}, Weight: {item_weight:3}, Exchangeable: {can_exchange}')
|
|
||||||
|
|
||||||
return choices
|
|
||||||
|
|
||||||
def _guild_exchange_select(self, choices):
|
|
||||||
"""
|
|
||||||
Execute exchange action on choices
|
|
||||||
The order of selection based on item weight
|
|
||||||
If none are applicable, return False
|
|
||||||
|
|
||||||
Pages:
|
|
||||||
in: GUILD_LOGISTICS
|
|
||||||
out: GUILD_LOGISTICS
|
|
||||||
"""
|
|
||||||
while len(choices):
|
|
||||||
# Select minimum by order of details
|
|
||||||
# First, item_weight then plate_weight then button_index
|
|
||||||
key = min(choices, key=choices.get)
|
|
||||||
details = choices.get(key)
|
|
||||||
|
|
||||||
# Item is exchangeable and exchange was a success
|
|
||||||
if details[3]:
|
|
||||||
self.device.click(details[4])
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
# Remove this choice since inapplicable, then choose again
|
|
||||||
choices.pop(key)
|
|
||||||
logger.warning('Failed to exchange with any of the 3 available options')
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _guild_exchange(self):
|
def _guild_exchange(self):
|
||||||
"""
|
"""
|
||||||
@ -529,19 +367,28 @@ class GuildLogistics(GuildBase):
|
|||||||
If unable to exchange at all, loop terminates
|
If unable to exchange at all, loop terminates
|
||||||
prematurely
|
prematurely
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: If clicked.
|
||||||
|
|
||||||
Pages:
|
Pages:
|
||||||
in: GUILD_LOGISTICS
|
in: GUILD_LOGISTICS
|
||||||
out: GUILD_LOGISTICS
|
out: GUILD_LOGISTICS
|
||||||
"""
|
"""
|
||||||
item_priority, grade_to_plate_priorities = self._guild_exchange_priorities()
|
|
||||||
if not GUILD_EXCHANGE_LIMIT.ocr(self.device.image) > 0:
|
if not GUILD_EXCHANGE_LIMIT.ocr(self.device.image) > 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
options = self._guild_exchange_scan()
|
items = self._guild_exchange_scan()
|
||||||
choices = self._guild_exchange_check(options, item_priority, grade_to_plate_priorities)
|
EXCHANGE_FILTER.load(self.config.Logistics_ExchangeFilter)
|
||||||
if self._guild_exchange_select(choices):
|
selected = EXCHANGE_FILTER.apply(items, func=lambda item: item.enough)
|
||||||
|
logger.attr('Exchange_sort', ' > '.join([str(item.name) for item in selected]))
|
||||||
|
|
||||||
|
if len(selected):
|
||||||
|
button = EXCHANGE_BUTTONS.buttons[items.index(selected[0])]
|
||||||
|
# Just bored click, will retry in self._guild_logistics_collect
|
||||||
|
self.device.click(button)
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
|
logger.warning('No guild exchange items satisfy current filter, or not having enough resources')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def guild_logistics(self):
|
def guild_logistics(self):
|
||||||
@ -555,15 +402,9 @@ class GuildLogistics(GuildBase):
|
|||||||
in: page_guild
|
in: page_guild
|
||||||
out: page_guild, GUILD_LOGISTICS
|
out: page_guild, GUILD_LOGISTICS
|
||||||
"""
|
"""
|
||||||
# Transition to Logistics
|
self.guild_side_navbar_ensure(bottom=3)
|
||||||
if not self.guild_side_navbar_ensure(bottom=3):
|
|
||||||
logger.info('Logistics sidebar not ensured, try again on next reward loop')
|
|
||||||
return False
|
|
||||||
self._guild_logistics_ensure()
|
self._guild_logistics_ensure()
|
||||||
|
|
||||||
# Run
|
result = self._guild_logistics_collect()
|
||||||
checked = self._guild_logistics_collect()
|
logger.info(f'Guild logistics run success: {result}')
|
||||||
if checked:
|
return result
|
||||||
logger.info('All guild logistics finished today, skip checking them today')
|
|
||||||
|
|
||||||
return checked
|
|
||||||
|
@ -4,17 +4,11 @@ from module.base.utils import *
|
|||||||
from module.guild.assets import *
|
from module.guild.assets import *
|
||||||
from module.guild.base import GuildBase
|
from module.guild.base import GuildBase
|
||||||
from module.logger import logger
|
from module.logger import logger
|
||||||
from module.map_detection.utils import Points
|
|
||||||
from module.ocr.ocr import DigitCounter
|
from module.ocr.ocr import DigitCounter
|
||||||
from module.template.assets import TEMPLATE_OPERATIONS_RED_DOT
|
from module.template.assets import TEMPLATE_OPERATIONS_RED_DOT
|
||||||
|
|
||||||
GUILD_OPERATIONS_PROGRESS = DigitCounter(OCR_GUILD_OPERATIONS_PROGRESS, letter=(255, 247, 247), threshold=64)
|
GUILD_OPERATIONS_PROGRESS = DigitCounter(OCR_GUILD_OPERATIONS_PROGRESS, letter=(255, 247, 247), threshold=64)
|
||||||
|
|
||||||
RECORD_OPTION_DISPATCH = ('RewardRecord', 'operations_dispatch')
|
|
||||||
RECORD_SINCE_DISPATCH = (6, 12, 18, 21,)
|
|
||||||
RECORD_OPTION_BOSS = ('RewardRecord', 'operations_boss')
|
|
||||||
RECORD_SINCE_BOSS = (0,)
|
|
||||||
|
|
||||||
|
|
||||||
class GuildOperations(GuildBase):
|
class GuildOperations(GuildBase):
|
||||||
def _guild_operations_ensure(self, skip_first_screenshot=True):
|
def _guild_operations_ensure(self, skip_first_screenshot=True):
|
||||||
@ -36,7 +30,7 @@ class GuildOperations(GuildBase):
|
|||||||
self.device.click(GUILD_OPERATIONS_CLICK_SAFE_AREA)
|
self.device.click(GUILD_OPERATIONS_CLICK_SAFE_AREA)
|
||||||
else:
|
else:
|
||||||
current, remain, total = GUILD_OPERATIONS_PROGRESS.ocr(self.device.image)
|
current, remain, total = GUILD_OPERATIONS_PROGRESS.ocr(self.device.image)
|
||||||
threshold = total * self.config.GUILD_OPERATIONS_JOIN_THRESHOLD
|
threshold = total * self.config.Operation_JoinThreshold
|
||||||
if current <= threshold:
|
if current <= threshold:
|
||||||
logger.info('Joining Operation, current progress less than '
|
logger.info('Joining Operation, current progress less than '
|
||||||
f'threshold ({threshold:.2f})')
|
f'threshold ({threshold:.2f})')
|
||||||
@ -141,11 +135,9 @@ class GuildOperations(GuildBase):
|
|||||||
if len(entrance_1):
|
if len(entrance_1):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
backup = self.config.cover(DEVICE_CONTROL_METHOD='minitouch')
|
|
||||||
p1, p2 = random_rectangle_vector(
|
p1, p2 = random_rectangle_vector(
|
||||||
(-600, 0), box=detection_area, random_range=(-50, -50, 50, 50), padding=20)
|
(-600, 0), box=detection_area, random_range=(-50, -50, 50, 50), padding=20)
|
||||||
self.device.drag(p1, p2, segments=2, shake=(0, 25), point_random=(0, 0, 0, 0), shake_random=(0, -5, 0, 5))
|
self.device.drag(p1, p2, segments=2, shake=(0, 25), point_random=(0, 0, 0, 0), shake_random=(0, -5, 0, 5))
|
||||||
backup.recover()
|
|
||||||
self.device.sleep(0.3)
|
self.device.sleep(0.3)
|
||||||
|
|
||||||
logger.warning('Failed to find active operation dispatch')
|
logger.warning('Failed to find active operation dispatch')
|
||||||
@ -397,7 +389,7 @@ class GuildOperations(GuildBase):
|
|||||||
return False
|
return False
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if self.config.ENABLE_GUILD_OPERATIONS_BOSS_RECOMMEND:
|
if self.config.Operation_BossFleetRecommend:
|
||||||
if self.info_bar_count() and self.appear_then_click(GUILD_DISPATCH_RECOMMEND_2, interval=3):
|
if self.info_bar_count() and self.appear_then_click(GUILD_DISPATCH_RECOMMEND_2, interval=3):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -428,9 +420,7 @@ class GuildOperations(GuildBase):
|
|||||||
|
|
||||||
if not self._guild_operations_boss_preparation(az):
|
if not self._guild_operations_boss_preparation(az):
|
||||||
return False
|
return False
|
||||||
backup = self.config.cover(SUBMARINE=1, SUBMARINE_MODE='every_combat')
|
az.combat_execute(auto='combat_auto', submarine='every_combat')
|
||||||
az.combat_execute(auto='combat_auto')
|
|
||||||
backup.recover()
|
|
||||||
az.combat_status(expected_end='in_ui')
|
az.combat_status(expected_end='in_ui')
|
||||||
logger.info('Guild Raid Boss has been repelled')
|
logger.info('Guild Raid Boss has been repelled')
|
||||||
return True
|
return True
|
||||||
@ -448,31 +438,28 @@ class GuildOperations(GuildBase):
|
|||||||
return appear
|
return appear
|
||||||
|
|
||||||
def guild_operations(self):
|
def guild_operations(self):
|
||||||
if not self.guild_side_navbar_ensure(bottom=1):
|
self.guild_side_navbar_ensure(bottom=1)
|
||||||
logger.info('Operations sidebar not ensured, try again on next reward loop')
|
|
||||||
return None
|
|
||||||
self._guild_operations_ensure()
|
self._guild_operations_ensure()
|
||||||
# Determine the mode of operations, currently 3 are available
|
# Determine the mode of operations, currently 3 are available
|
||||||
operations_mode = self._guild_operation_get_mode()
|
operations_mode = self._guild_operation_get_mode()
|
||||||
if operations_mode is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Execute actions based on the detected mode
|
# Execute actions based on the detected mode
|
||||||
if operations_mode == 0:
|
if operations_mode == 0:
|
||||||
return
|
result = True
|
||||||
elif operations_mode == 1:
|
elif operations_mode == 1:
|
||||||
self._guild_operations_dispatch()
|
self._guild_operations_dispatch()
|
||||||
self.config.record_save(option=RECORD_OPTION_DISPATCH)
|
result = True
|
||||||
|
elif operations_mode == 2:
|
||||||
|
if self._guild_operations_boss_available():
|
||||||
|
if self.config.Operation_AttackBoss:
|
||||||
|
result = self._guild_operations_boss_combat()
|
||||||
|
else:
|
||||||
|
logger.info('Auto-battle disabled, play manually to complete this Guild Task')
|
||||||
|
result = True
|
||||||
|
else:
|
||||||
|
result = True
|
||||||
else:
|
else:
|
||||||
# Limit check for Guild Raid Boss to once a day
|
result = False
|
||||||
if not self.config.record_executed_since(option=RECORD_OPTION_BOSS, since=RECORD_SINCE_BOSS):
|
|
||||||
skip_record = False
|
|
||||||
if self._guild_operations_boss_available():
|
|
||||||
if self.config.ENABLE_GUILD_OPERATIONS_BOSS_AUTO:
|
|
||||||
if not self._guild_operations_boss_combat():
|
|
||||||
skip_record = True
|
|
||||||
else:
|
|
||||||
logger.info('Auto-battle disabled, play manually to complete this Guild Task')
|
|
||||||
|
|
||||||
if not skip_record:
|
logger.info(f'Guild operation run success: {result}')
|
||||||
self.config.record_save(option=RECORD_OPTION_BOSS)
|
return result
|
||||||
|
Loading…
Reference in New Issue
Block a user