Add: 增加战术学院收获

- 修复委托收获时可能卡住的问题
This commit is contained in:
LmeSzinc 2020-05-05 17:27:19 +08:00
parent fc7e16835f
commit 1c2db1ec26
14 changed files with 340 additions and 24 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -60,6 +60,7 @@ enable_oil_reward = yes
enable_coin_reward = yes
enable_mission_reward = yes
enable_commission_reward = yes
enable_tactical_reward = yes
commission_time_limit = 23:30
duration_shorter_than_2 = 11
duration_longer_than_6 = -11
@ -79,6 +80,11 @@ urgent_book = 95
urgent_box = 195
urgent_gem = 205
urgent_ship = 155
tactical_night_range = 23:30-06:30
tactical_book_tier = 2
tactical_exp_first = yes
tactical_book_tier_night = 3
tactical_exp_first_night = no
[Emulator]
command = emulator

View File

@ -23,7 +23,7 @@ def future_time(string):
string (str): Such as 14:59.
Returns:
datetime: Time with given hour, minute, second in the future.
datetime.datetime: Time with given hour, minute, second in the future.
"""
hour, minute = [int(x) for x in string.split(':')]
future = datetime.now().replace(hour=hour, minute=minute, second=0, microsecond=0)
@ -31,6 +31,31 @@ def future_time(string):
return future
def future_time_range(string):
"""
Args:
string (str): Such as 23:30-06:30.
Returns:
tuple(datetime.datetime): (time start, time end).
"""
start, end = [future_time(s) for s in string.split('-')]
if start > end:
start = start - timedelta(days=1)
return start, end
def time_range_active(time_range):
"""
Args:
time_range(tuple(datetime.datetime)): (time start, time end).
Returns:
bool:
"""
return time_range[0] < datetime.now() < time_range[1]
class Timer:
def __init__(self, limit, count=0):
"""

View File

@ -250,6 +250,14 @@ def main(ini_name=''):
priority4.add_argument('--钻石类紧急委托', default=default('--钻石类紧急委托'), help='BIW要员护卫, NYB巡视护卫')
priority4.add_argument('--观舰类紧急委托', default=default('--观舰类紧急委托'), help='小型观舰仪式, 同盟观舰仪式')
reward_tactical = reward_parser.add_argument_group('战术学院', '只支持续技能书, 不支持学新技能')
reward_tactical.add_argument('--启用战术学院收获', default=default('--启用战术学院收获'), choices=['', ''])
reward_tactical.add_argument('--战术学院夜间时段', default=default('--战术学院夜间时段'), help='格式 23:30-06:30')
reward_tactical.add_argument('--技能书稀有度', default=default('--技能书稀有度'), choices=['3', '2', '1'], help='最多使用T几的技能书\nT3是金书, T2是紫书, T1是蓝书')
reward_tactical.add_argument('--技能书优先使用同类型', default=default('--技能书优先使用同类型'), choices=['', ''], help='选是, 优先使用有150%加成的\n选否, 优先使用同稀有度的技能书')
reward_tactical.add_argument('--技能书夜间稀有度', default=default('--技能书夜间稀有度'), choices=['3', '2', '1'])
reward_tactical.add_argument('--技能书夜间优先使用同类型', default=default('--技能书夜间优先使用同类型'), choices=['', ''])
# ==========设备设置==========
emulator_parser = subs.add_parser('设备设置')
emulator = emulator_parser.add_argument_group('模拟器', '需要运行一次来保存选项, 会检查游戏是否启动')

View File

@ -2,13 +2,12 @@ import codecs
import configparser
import copy
import os
from datetime import datetime
import cv2
import numpy as np
from PIL import Image
from module.base.timer import future_time
from module.base.timer import *
from module.config.dictionary import *
from module.logger import logger
@ -282,6 +281,8 @@ class AzurLaneConfig:
ENABLE_COIN_REWARD = True
ENABLE_MISSION_REWARD = True
ENABLE_COMMISSION_REWARD = True
ENABLE_TACTICAL_REWARD = True
COMMISSION_PRIORITY = {
'major_comm': 0,
'daily_comm': 100,
@ -304,6 +305,12 @@ class AzurLaneConfig:
}
COMMISSION_TIME_LIMIT = 0
TACTICAL_BOOK_TIER = 2
TACTICAL_EXP_FIRST = True
TACTICAL_BOOK_TIER_NIGHT = 3
TACTICAL_EXP_FIRST_NIGHT = False
TACTICAL_NIGHT_RANGE = future_time_range('23:30-06:30') # (Night start, night end), datetime.datetime instance.
"""
C_7_2_mystery_farming
"""
@ -425,11 +432,16 @@ class AzurLaneConfig:
# Reward
option = config['Reward']
self.REWARD_INTERVAL = int(option['reward_interval'])
for attr in ['enable_reward', 'enable_oil_reward', 'enable_coin_reward', 'enable_mission_reward', 'enable_commission_reward']:
for attr in ['enable_reward', 'enable_oil_reward', 'enable_coin_reward', 'enable_mission_reward', 'enable_commission_reward', 'enable_tactical_reward']:
self.__setattr__(attr.upper(), to_bool(option[attr]))
self.COMMISSION_TIME_LIMIT = future_time(option['commission_time_limit'])
for attr in self.COMMISSION_PRIORITY.keys():
self.COMMISSION_PRIORITY[attr] = int(option[attr])
self.TACTICAL_NIGHT_RANGE = future_time_range(option['tactical_night_range'])
self.TACTICAL_BOOK_TIER = int(option['tactical_book_tier'])
self.TACTICAL_EXP_FIRST = to_bool(option['tactical_exp_first'])
self.TACTICAL_BOOK_TIER_NIGHT = int(option['tactical_book_tier_night'])
self.TACTICAL_EXP_FIRST_NIGHT = to_bool(option['tactical_exp_first_night'])
option = config['Main']
self.CAMPAIGN_NAME = option['main_stage']
@ -443,12 +455,12 @@ class AzurLaneConfig:
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'])
self.FLEET_DAILY_EQUIPMENT = to_list(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'])
self.FLEET_HARD_EQUIPMENT = to_list(option['hard_equipment'])
# Exercise
self.ENABLE_EXERCISE = to_bool(option['enable_exercise'])
self.EXERCISE_CHOOSE_MODE = option['exercise_choose_mode']
@ -456,7 +468,7 @@ class AzurLaneConfig:
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'])
self.EXERCISE_FLEET_EQUIPMENT = to_list(option['exercise_equipment'])
# Event
option = config['Event']

View File

@ -91,6 +91,7 @@ dic_chi_to_eng = {
'启用物资收获': 'enable_coin_reward',
'启用任务收获': 'enable_mission_reward',
'启用委托收获': 'enable_commission_reward',
'启用战术学院收获': 'enable_tactical_reward',
'委托时间限制': 'commission_time_limit',
'委托耗时小于2h': 'duration_shorter_than_2',
'委托耗时超过6h': 'duration_longer_than_6',
@ -110,6 +111,11 @@ dic_chi_to_eng = {
'装备类紧急委托': 'urgent_box',
'钻石类紧急委托': 'urgent_gem',
'观舰类紧急委托': 'urgent_ship',
'战术学院夜间时段': 'tactical_night_range',
'技能书稀有度': 'tactical_book_tier',
'技能书优先使用同类型': 'tactical_exp_first',
'技能书夜间稀有度': 'tactical_book_tier_night',
'技能书夜间优先使用同类型': 'tactical_exp_first_night',
'设备': 'serial',
'包名': 'package_name',
'出错时保存log和截图': 'enable_error_log_and_screenshot_save',
@ -198,10 +204,8 @@ def to_bool(string):
return dic_bool.get(string, string)
def equip(string):
def to_list(string):
if string.isdigit():
return None
out = [int(letter.strip()) for letter in string.split(',')]
assert len(out) == 6
return out

View File

@ -21,3 +21,4 @@ REWARD_1 = Button(area=(383, 285, 503, 297), color=(238, 168, 81), button=(383,
REWARD_2 = Button(area=(383, 404, 503, 444), color=(233, 165, 67), button=(383, 404, 503, 444), file='./assets/reward/REWARD_2.png')
REWARD_3 = Button(area=(383, 546, 503, 586), color=(234, 163, 69), button=(383, 546, 503, 586), file='./assets/reward/REWARD_3.png')
REWARD_SAVE_CLICK = Button(area=(415, 184, 496, 214), color=(152, 150, 168), button=(415, 184, 496, 214), file='./assets/reward/REWARD_SAVE_CLICK.png')
TACTICAL_CLASS_START = Button(area=(1024, 590, 1197, 648), color=(96, 139, 194), button=(1024, 590, 1197, 648), file='./assets/reward/TACTICAL_CLASS_START.png')

View File

@ -443,7 +443,7 @@ class RewardCommission(UI, InfoBarHandler, PopupHandler):
logger.info('No commission chose')
def handle_commission_start(self):
"""Make sure current page is page_reward before calls.
"""Remember to make sure current page is page_reward before calls.
Returns:
bool: If runs a commission.
@ -460,6 +460,7 @@ class RewardCommission(UI, InfoBarHandler, PopupHandler):
self.commission_start()
self.ui_goto(page_reward, skip_first_screenshot=True)
return True
def commission_notice_show_at_campaign(self):
"""

View File

@ -6,9 +6,10 @@ from module.logger import logger
from module.reward.assets import *
from module.ui.page import *
from module.reward.commission import RewardCommission
from module.reward.tactical_class import RewardTacticalClass
class Reward(RewardCommission):
class Reward(RewardCommission, RewardTacticalClass):
def reward(self):
if not self.config.ENABLE_REWARD:
return False
@ -21,6 +22,7 @@ class Reward(RewardCommission):
self._reward_receive()
self.handle_info_bar()
self.handle_commission_start()
self.handle_tactical_class()
self.ui_click(
click_button=page_reward.links[page_main],
@ -47,7 +49,7 @@ class Reward(RewardCommission):
Returns:
bool: If rewarded.
"""
logger.hr('Oil Reward')
logger.hr('Reward receive')
reward = False
exit_timer = Timer(1, count=3)
@ -75,11 +77,12 @@ class Reward(RewardCommission):
reward = True
continue
for button in btn:
if not click_timer.reached():
continue
if self.appear_then_click(button, interval=1):
btn.remove(button)
if click_timer.reached() and (
(self.config.ENABLE_OIL_REWARD and self.appear_then_click(OIL, interval=60))
or (self.config.ENABLE_COIN_REWARD and self.appear_then_click(COIN, interval=60))
or (self.config.ENABLE_COMMISSION_REWARD and self.appear_then_click(REWARD_1, interval=1))
or (self.config.ENABLE_REWARD and self.appear_then_click(REWARD_3, interval=1))
):
exit_timer.reset()
click_timer.reset()
reward = True

View File

@ -0,0 +1,247 @@
import numpy as np
from scipy import signal
from module.base.button import Button, ButtonGrid
from module.base.timer import Timer, time_range_active
from module.base.utils import area_offset, get_color, color_similar, color_similarity_2d
from module.handler.popup import PopupHandler
from module.logger import logger
from module.reward.assets import TACTICAL_CLASS_START, REWARD_2
from module.ui.assets import TACTICAL_CHECK
from module.ui.ui import UI, page_tactical, page_reward
GENRE_NAME_DICT = {
1: 'Offensive', # red
2: 'Defensive', # blue
3: 'Support', # yellow
}
BOOKS_GRID = ButtonGrid(origin=(239, 288), delta=(140, 120), button_shape=(98, 98), grid_shape=(6, 2))
class Book:
color_genre = {
1: (214, 69, 74), # Offensive, red
2: (115, 178, 255), # Defensive, blue
3: (247, 190, 99), # Support, yellow
}
color_tier = {
1: (104, 181, 238), # T1, blue
2: (151, 129, 203), # T2, purple
3: (235, 208, 120), # T3, gold
}
def __init__(self, image, button):
"""
Args:
image (PIL.Image.Image):
button (Button):
"""
image = image.crop(button.area)
self.button = button
self.genre = 0
color = get_color(image, (65, 35, 72, 42))
for key, value in self.color_genre.items():
if color_similar(color1=color, color2=value, threshold=30):
self.genre = key
self.tier = 0
color = get_color(image, (83, 61, 92, 70))
for key, value in self.color_tier.items():
if color_similar(color1=color, color2=value, threshold=30):
self.tier = key
color = color_similarity_2d(image.crop((15, 0, 97, 13)), color=(148, 251, 99))
self.exp = bool(np.sum(color > 221) > 50)
self.valid = bool(self.genre and self.tier)
def __str__(self):
# Example: Defensive_T3_Exp
text = f'{GENRE_NAME_DICT.get(self.genre, "Unknown")}_T{self.tier}'
if self.exp:
text += '_Exp'
return text
class BookGroup:
def __init__(self, books):
"""
Args:
books (list[Book]):
"""
self.books = books
def __iter__(self):
return iter(self.books)
def __len__(self):
return len(self.books)
def __bool__(self):
return len(self.books) > 0
def __getitem__(self, item):
return self.books[item]
def __str__(self):
# return str([str(grid) for grid in self])
return '[' + ', '.join([str(grid) for grid in self]) + ']'
def select(self, **kwargs):
"""
Args:
**kwargs: Attributes of Grid.
Returns:
SelectedGrids:
"""
result = []
for grid in self.books:
flag = True
for k, v in kwargs.items():
if grid.__getattribute__(k) != v:
flag = False
if flag:
result.append(grid)
return BookGroup(result)
def add(self, books):
"""
Args:
books(BookGroup):
Returns:
BookGroup:
"""
return BookGroup(self.books + books.books)
def choose(self, tier, exp=True):
"""
Args:
tier (int): Max tier to choose.
exp (bool): True to choose books with exp bonus, False to choose other books in the same tier.
Returns:
Book:
"""
while 1:
books = self.select(tier=tier)
tier -= 1
books_with_exp = books.select(exp=True)
if exp and not books_with_exp:
continue
if books_with_exp:
books = books_with_exp
if books:
logger.attr('Book_choose', books[0])
return books[0]
# End
if tier <= 0:
break
logger.warning('No book choose, return first book.')
return self[0]
class RewardTacticalClass(UI, PopupHandler):
tactical_animation_timer = Timer(2, count=3)
def _tactical_animation_running(self):
"""
Returns:
bool: If showing skill points increasing animation.
"""
color_height = np.mean(self.device.image.crop((922, 0, 1036, 720)).convert('L'), axis=1)
parameters = {'height': 200}
peaks, _ = signal.find_peaks(color_height, **parameters)
peaks = [y for y in peaks if y > 67 + 243]
if not len(peaks):
logger.warning('No student card found.')
for y in peaks:
student_area = (447, y - 243, 1244, y)
area = area_offset((677, 172, 761, 183), student_area[0:2])
# Normal: 160, In skill-increasing animation: 109
if np.mean(get_color(self.device.image, area)) < 135:
return True
return False
def _tactical_books_choose(self):
"""
Choose tactical book according to config.
"""
books = BookGroup([Book(self.device.image, button) for button in BOOKS_GRID.buttons()]).select(valid=True)
logger.attr('Book_count', len(books))
for index in range(1, 4):
logger.info(f'Book_T{index}: {books.select(tier=index)}')
if not books:
logger.warning('No book found.')
if not time_range_active(self.config.TACTICAL_NIGHT_RANGE):
tier = self.config.TACTICAL_BOOK_TIER
exp = self.config.TACTICAL_EXP_FIRST
else:
tier = self.config.TACTICAL_BOOK_TIER_NIGHT
exp = self.config.TACTICAL_EXP_FIRST_NIGHT
book = books.choose(tier=tier, exp=exp)
self.device.click(book.button)
self.device.sleep((0.3, 0.5))
def _tactical_class_receive(self, skip_first_screenshot=True):
"""Remember to make sure current page is page_reward before calls.
Args:
skip_first_screenshot (bool):
Returns:
bool: If rewarded.
"""
if not self.appear(REWARD_2):
logger.info('No tactical class reward.')
return False
logger.hr('Tactical class receive')
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
self.device.screenshot()
if self.appear_then_click(REWARD_2, interval=1):
continue
if self.handle_popup_confirm():
continue
if self.appear(TACTICAL_CLASS_START, offset=(30, 30), interval=1):
self._tactical_books_choose()
self.device.click(TACTICAL_CLASS_START)
continue
# End
if self.appear(TACTICAL_CHECK, offset=(20, 20)):
self.ui_current = page_tactical
if not self._tactical_animation_running():
if self.tactical_animation_timer.reached():
logger.info('Tactical reward end.')
break
else:
self.tactical_animation_timer.reset()
self.ui_goto(page_reward, skip_first_screenshot=True)
return True
def handle_tactical_class(self):
"""
Returns:
bool: If rewarded.
"""
if not self.config.ENABLE_TACTICAL_REWARD:
return False
self._tactical_class_receive()
return True

View File

@ -24,4 +24,6 @@ MISSION_CHECK = Button(area=(120, 15, 173, 40), color=(141, 156, 194), button=(1
REWARD_CHECK = Button(area=(302, 119, 371, 195), color=(146, 118, 120), button=(302, 119, 371, 195), file='./assets/ui/REWARD_CHECK.png')
REWARD_GOTO_COMMISSION = Button(area=(383, 262, 503, 302), color=(91, 136, 199), button=(383, 262, 503, 302), file='./assets/ui/REWARD_GOTO_COMMISSION.png')
REWARD_GOTO_MAIN = Button(area=(1037, 611, 1107, 656), color=(134, 122, 127), button=(1037, 611, 1107, 656), file='./assets/ui/REWARD_GOTO_MAIN.png')
REWARD_GOTO_TACTICAL = Button(area=(383, 404, 503, 444), color=(89, 140, 198), button=(383, 404, 503, 444), file='./assets/ui/REWARD_GOTO_TACTICAL.png')
SP_CHECK = Button(area=(123, 63, 206, 109), color=(95, 110, 145), button=(123, 63, 206, 109), file='./assets/ui/SP_CHECK.png')
TACTICAL_CHECK = Button(area=(122, 14, 231, 38), color=(145, 161, 200), button=(122, 14, 231, 38), file='./assets/ui/TACTICAL_CHECK.png')

View File

@ -78,6 +78,13 @@ page_commission.link(button=GOTO_MAIN, destination=page_main)
page_commission.link(button=BACK_ARROW, destination=page_reward)
page_reward.link(button=REWARD_GOTO_COMMISSION, destination=page_commission)
# Tactical class
# Please don't goto tactical class from academy.
page_tactical = Page(TACTICAL_CHECK)
page_tactical.link(button=GOTO_MAIN, destination=page_main)
page_tactical.link(button=BACK_ARROW, destination=page_reward)
page_reward.link(button=REWARD_GOTO_TACTICAL, destination=page_tactical)
# Event list
page_event_list = Page(EVENT_LIST_CHECK)
page_event_list.link(button=GOTO_MAIN, destination=page_main)