StarRailCopilot/module/config/config.py
LmeSzinc a42aa00864 Add: 适配了活动微层混合D3
- 修改了GUI的活动图入口
- 修正了UI_MASK文件
- 修改舰队识别, boss识别方式为模板匹配
- 修改当前舰队识别的色相阈值
- 增加了对boss右下角小图标的识别
- 修改了错误格子的修正方法, 若当列格子数量不足, 将被剔除
- 修复了退役后不会快速重新进入战斗的问题
2020-03-31 14:23:44 +08:00

406 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_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')
option = config['Setting']
# Serial
self.SERIAL = option['serial']
# 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_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()