StarRailCopilot/module/config/config.py

419 lines
13 KiB
Python

import codecs
import configparser
import copy
import os
from datetime import datetime, timedelta
import cv2
import numpy as np
from PIL import Image
from module.config.dictionary import *
from module.logger import logger
class AzurLaneConfig:
"""
Basic Config.
"""
CONFIG_FILE = ''
config = configparser.ConfigParser(interpolation=None)
"""
Fleet
"""
ENABLE_FLEET_CONTROL = True
# Fleet 1-6, if empty use 0.
_FLEET_1 = 1
FLEET_2 = 2
FLEET_3 = 3
# Formation 1-3.
_FLEET_1_FORMATION = 2
FLEET_2_FORMATION = 2
FLEET_3_FORMATION = 2
# Fleet 1-2, if empty use 0.
SUBMARINE = 0
FLEET_CHECKED = False
USING_SPARE_FLEET = False
@property
def FLEET_1(self):
return self.FLEET_3 if self.USING_SPARE_FLEET else self._FLEET_1
@FLEET_1.setter
def FLEET_1(self, value):
self._FLEET_1 = value
@property
def FLEET_1_FORMATION(self):
return self.FLEET_3_FORMATION if self.USING_SPARE_FLEET else self._FLEET_1_FORMATION
@FLEET_1_FORMATION.setter
def FLEET_1_FORMATION(self, value):
self._FLEET_1_FORMATION = value
"""
module.assets
"""
ASSETS_FOLDER = './assets'
"""
module.base
"""
COLOR_SIMILAR_THRESHOLD = 10
BUTTON_OFFSET = 30
BUTTON_MATCH_SIMILARITY = 0.85
SLEEP_AFTER_CLICK = 0
WAIT_BEFORE_SAVING_SCREEN_SHOT = 1
"""
module.combat
"""
ENABLE_SAVE_GET_ITEMS = True
ENABLE_HP_BALANCE = False
ENABLE_MAP_FLEET_LOCK = True
SUBMARINE_MODE = ''
SUBMARINE_CALL_AT_BOSS = False
"""
module.campaign
"""
CAMPAIGN_NAME = 'default'
CAMPAIGN_MODE = 'normal'
ENABLE_STOP_CONDITION = True
STOP_IF_OIL_LOWER_THAN = 5000
STOP_IF_COUNT_GREATER_THAN = 0
STOP_IF_TIME_REACH = 0
STOP_IF_TRIGGER_EMOTION_LIMIT = False
STOP_IF_DOCK_FULL = False
"""
module.event
"""
EVENT_NAME = ''
CAMPAIGN_EVENT = ''
EVENT_NAME_AB = ''
"""
module.combat.emotion
"""
ENABLE_EMOTION_REDUCE = True
IGNORE_LOW_EMOTION_WARN = False
EMOTION_LIMIT_TRIGGERED = False
TIME_FORMAT = '%Y-%m-%d_%H:%M:%S'
# 20, 30, 40, 50, 60
FLEET_1_RECOVER_PER_HOUR = 50
FLEET_1_EMOTION_LIMIT = 120
FLEET_2_RECOVER_PER_HOUR = 20
FLEET_2_EMOTION_LIMIT = 50
FLEET_3_RECOVER_PER_HOUR = 20
FLEET_3_EMOTION_LIMIT = 50
"""
module.device
"""
SERIAL = '127.0.0.1:62001'
COMMAND = ''
USE_ADB_SREENSHOT = False
USE_ADB_CONTROL = False
SCREEN_SHOT_SAVE_FOLDER_BASE = './screenshot'
SCREEN_SHOT_SAVE_FOLDER = ''
SCREEN_SHOT_SAVE_INTERVAL = 5 # Seconds between two save. Saves in the interval will be dropped.
"""
module.daily
"""
ENABLE_DAILY_MISSION = True
FLEET_DAILY = 3
FLEET_DAILY_EQUIPMENT = [1, 1, 1, 1, 1, 1]
DAILY_CHOOSE = {
4: 1, # 商船护送
5: 1, # 海域突进
1: 2, # 战术研修, 1航空 2炮击 3雷击
2: 1, # 斩首行动
3: 1, # 破交作战
}
"""
module.hard
"""
ENABLE_HARD_CAMPAIGN = True
HARD_CAMPAIGN = '10-4'
FLEET_HARD = 1
FLEET_HARD_EQUIPMENT = [1, 1, 1, 1, 1, 1]
"""
module.exercise
"""
ENABLE_EXERCISE = True
EXERCISE_CHOOSE_MODE = 'max_exp'
EXERCISE_PRESERVE = 0
LOW_HP_THRESHOLD = 0.40
LOW_HP_CONFIRM_WAIT = 1.0
OPPONENT_CHALLENGE_TRIAL = 1
EXERCISE_FLEET_EQUIPMENT = [1, 1, 1, 1, 1, 1]
"""
error_log
"""
ERROR_LOG_FOLDER = './log/error'
"""
module.map.fleet
"""
MAP_HAS_AMBUSH = False
"""
module.retire
"""
ENABLE_RETIREMENT = True
RETIRE_MODE = '10' # all, 10
RETIRE_N = True
RETIRE_R = False
RETIRE_SR = False
RETIRE_SSR = False
"""
module.map.perspective
"""
# Screen
SCREEN_SIZE = (1280, 720)
DETECTING_AREA = (123, 55, 1280, 720)
SCREEN_CENTER = (SCREEN_SIZE[0] / 2, SCREEN_SIZE[1] / 2)
MID_Y = SCREEN_CENTER[1]
# UI mask
UI_MASK_FILE = './module/map/ui_mask.png'
UI_MASK = np.array(Image.open(UI_MASK_FILE).convert('L'))
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
UI_MASK_STROKE = cv2.erode(UI_MASK, kernel).astype('uint8')
# Parameters for scipy.signal.find_peaks
# https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.find_peaks.html
INTERNAL_LINES_FIND_PEAKS_PARAMETERS = {
'height': (150, 255 - 40),
'width': 2,
'prominence': 10,
'distance': 35,
}
EDGE_LINES_FIND_PEAKS_PARAMETERS = {
'height': (255 - 24, 255),
'prominence': 10,
'distance': 50,
# 'width': (0, 7),
'wlen': 1000
}
# Parameters for cv2.HoughLines
INTERNAL_LINES_HOUGHLINES_THRESHOLD = 75
EDGE_LINES_HOUGHLINES_THRESHOLD = 75
# Parameters for lines pre-cleansing
HORIZONTAL_LINES_THETA_THRESHOLD = 0.005
VERTICAL_LINES_THETA_THRESHOLD = 18
# Parameters for perspective calculating
VANISH_POINT_RANGE = ((540, 740), (-3000, -1000))
DISTANCE_POINT_X_RANGE = ((-3200, -1600),)
# Parameters for line cleansing
ERROR_LINES_TOLERANCE = (-10, 10)
MID_DIFF_RANGE = (129 - 3, 129 + 3)
COINCIDENT_POINT_RANGE = (
(
-abs(ERROR_LINES_TOLERANCE[0]) * MID_DIFF_RANGE[1],
# SCREEN_SIZE[0] + ERROR_LINES_TOLERANCE[1] * MID_DIFF_RANGE[1]
200
),
MID_DIFF_RANGE
)
"""
module.daemon
"""
ENABLE_SEMI_MAP_PREPARATION = True
ENABLE_SEMI_STORY_SKIP = True
"""
C_7_2_mystery_farming
"""
C72_BOSS_FLEET_STEP_ON_A3 = True
"""
C_12_4_leveling
"""
C124_NON_S3_ENTER_TOLERANCE = 1
C124_NON_S3_WITHDRAW_TOLERANCE = 0
C124_AMMO_PICK_UP = 3
def create_folder(self):
self.SCREEN_SHOT_SAVE_FOLDER = self.SCREEN_SHOT_SAVE_FOLDER_BASE + '/' + self.CAMPAIGN_NAME
for folder in [self.SCREEN_SHOT_SAVE_FOLDER_BASE, self.ASSETS_FOLDER, self.SCREEN_SHOT_SAVE_FOLDER, self.ERROR_LOG_FOLDER]:
if not os.path.exists(folder):
os.mkdir(folder)
def merge(self, other):
"""
Args:
other (AzurLaneConfig, Config):
Returns:
AzurLaneConfig
"""
config = copy.copy(self)
for attr in dir(config):
if attr.endswith('__'):
continue
if hasattr(other, attr):
value = other.__getattribute__(attr)
if value is not None:
config.__setattr__(attr, value)
return config
def load_config_file(self, name='main'):
self.CONFIG_FILE = f'./config/{name}.ini'
self.config.read_file(codecs.open(self.CONFIG_FILE, "r", "utf8"))
self.load_from_config(self.config)
def save(self):
self.config.write(codecs.open(self.CONFIG_FILE, "w+", "utf8"))
def load_from_config(self, config):
"""
Args:
config(configparser.ConfigParser):
"""
self.COMMAND = config.get('Command', 'command')
# Emulator
option = config['Emulator']
self.SERIAL = option['serial']
option = config['Setting']
# Stop condition
self.ENABLE_STOP_CONDITION = to_bool(option['enable_stop_condition'])
self.STOP_IF_COUNT_GREATER_THAN = int(option['if_count_greater_than'])
if not option['if_time_reach'].isdigit():
hour, minute = [int(x) for x in option['if_time_reach'].split(':')]
limit = datetime.now().replace(hour=hour, minute=minute, second=0, microsecond=0)
limit = limit + timedelta(1) if limit < datetime.now() else limit
self.STOP_IF_TIME_REACH = limit
else:
self.STOP_IF_TIME_REACH = 0
self.STOP_IF_OIL_LOWER_THAN = int(option['if_oil_lower_than'])
self.STOP_IF_TRIGGER_EMOTION_LIMIT = to_bool(option['if_trigger_emotion_control'])
self.STOP_IF_DOCK_FULL = to_bool(option['if_dock_full'])
# Fleet
self.ENABLE_FLEET_CONTROL = to_bool(option['enable_fleet_control'])
for n in ['1', '2', '3']:
self.__setattr__(f'FLEET_{n}', int(option[f'fleet_index_{n}']))
self.__setattr__(f'FLEET_{n}_FORMATION', int(option[f'fleet_formation_{n}'].split('_')[1]))
self.SUBMARINE = int(option['fleet_index_4']) if to_bool(option['fleet_index_4']) else 0
self.SUBMARINE_MODE = option['submarine_mode']
self.SUBMARINE_CALL_AT_BOSS = option['submarine_mode'] == 'when_boss_combat_boss_appear'
# Emotion
self.ENABLE_EMOTION_REDUCE = to_bool(option['enable_emotion_reduce'])
self.IGNORE_LOW_EMOTION_WARN = to_bool(option['ignore_low_emotion_warn'])
for n in ['1', '2', '3']:
recover = dic_emotion_recover[option[f'emotion_recover_{n}']]
recover += 10 if to_bool(option[f'hole_fleet_married_{n}']) else 0
self.__setattr__(f'FLEET_{n}_RECOVER_PER_HOUR', recover)
self.__setattr__(f'FLEET_{n}_EMOTION_LIMIT', dic_emotion_limit[option[f'emotion_control_{n}']])
# HP balance, save get items -> combat
self.ENABLE_HP_BALANCE = to_bool(option['enable_hp_balance'])
self.ENABLE_SAVE_GET_ITEMS = to_bool(option['enable_drop_screenshot'])
self.SCREEN_SHOT_SAVE_FOLDER_BASE = option['drop_screenshot_folder']
# Retirement
self.ENABLE_RETIREMENT = to_bool(option['enable_retirement'])
self.RETIRE_MODE = option['retire_mode'].split('_')[1]
for r in ['n', 'r', 'sr', 'ssr']:
self.__setattr__(f'RETIRE_{r.upper()}', to_bool(option[f'retire_{r}']))
option = config['Main']
self.CAMPAIGN_NAME = option['main_stage']
self.CAMPAIGN_NAME = 'campaign_' + self.CAMPAIGN_NAME.replace('-', '_')
option = config['Daily']
for n in ['daily_mission', 'hard_campaign', 'exercise']:
self.__setattr__(f'ENABLE_{n.upper()}', option[f'enable_{n}'])
# Daily mission
self.ENABLE_DAILY_MISSION = to_bool(option['enable_daily_mission'])
for n in [1, 2, 4, 5]:
self.DAILY_CHOOSE[n] = dic_daily[option[f'daily_mission_{n}']]
self.FLEET_DAILY = int(option['daily_fleet'])
self.FLEET_DAILY_EQUIPMENT = equip(option['daily_equipment'])
# Hard
self.ENABLE_HARD_CAMPAIGN = to_bool(option['enable_hard_campaign'])
self.HARD_CAMPAIGN = option['hard_campaign']
self.FLEET_HARD = int(option['hard_fleet'])
self.FLEET_HARD_EQUIPMENT = equip(option['hard_equipment'])
# Exercise
self.ENABLE_EXERCISE = to_bool(option['enable_exercise'])
self.EXERCISE_CHOOSE_MODE = option['exercise_choose_mode']
self.EXERCISE_PRESERVE = int(option['exercise_preserve'])
self.OPPONENT_CHALLENGE_TRIAL = int(option['exercise_try'])
self.LOW_HP_THRESHOLD = float(option['exercise_hp_threshold'])
self.LOW_HP_CONFIRM_WAIT = float(option['exercise_low_hp_confirm'])
self.EXERCISE_FLEET_EQUIPMENT = equip(option['exercise_equipment'])
# Event
option = config['Event']
self.EVENT_NAME = option['event_name']
if 'sp' in ''.join(os.listdir(f'./campaign/{self.EVENT_NAME}')):
self.CAMPAIGN_EVENT = option['sp_stage']
else:
self.CAMPAIGN_EVENT = option['event_stage']
# Event_daily_ab
option = config['Event_daily_ab']
self.EVENT_NAME_AB = option['event_name_ab']
# Semi_auto
option = config['Semi_auto']
self.ENABLE_SEMI_MAP_PREPARATION = to_bool(option['enable_semi_map_preparation'])
self.ENABLE_SEMI_STORY_SKIP = to_bool(option['enable_semi_story_skip'])
# C_7_2_mystery_farming
option = config['C72_mystery_farming']
self.C72_BOSS_FLEET_STEP_ON_A3 = to_bool(option['boss_fleet_step_on_a3'])
if self.COMMAND.lower() == 'c72_mystery_farming' and not self.C72_BOSS_FLEET_STEP_ON_A3:
self.FLEET_2 = 0
# C_12_4_leveling
option = config['C124_leveling']
self.C124_NON_S3_ENTER_TOLERANCE = int(option['non_s3_enemy_enter_tolerance'])
self.C124_NON_S3_WITHDRAW_TOLERANCE = int(option['non_s3_enemy_withdraw_tolerance'])
self.C124_AMMO_PICK_UP = int(option['ammo_pick_up_124'])
def record_executed_since(self, option, since):
"""
Args:
option (tuple(str)): (Section, Option), such as ('DailyRecord', 'exercise').
since (tuple(int)): Update hour in Azurlane, such as (0, 12, 18,).
Returns:
bool: If got a record after last game update.
"""
record = datetime.strptime(self.config.get(*option), self.TIME_FORMAT)
since = np.array(since)
hour = since[since <= datetime.now().hour][-1]
update = datetime.now().replace(hour=hour, minute=0, second=0, microsecond=0)
logger.attr(f'{option[0]}_{option[1]}', f'Record time: {record}')
logger.attr(f'{option[0]}_{option[1]}', f'Last update: {update}')
return record > update
def record_save(self, option):
record = datetime.strftime(datetime.now(), self.TIME_FORMAT)
self.config.set(option[0], option[1], record)
self.save()
def __init__(self, conf='main'):
"""
Args:
conf (str): Config to load.
"""
self.load_config_file(conf)
self.create_folder()
# cfg = AzurLaneConfig()