diff --git a/assets/reward/COMMISSION_ADVICE.png b/assets/reward/COMMISSION_ADVICE.png new file mode 100644 index 000000000..8f8c8c404 Binary files /dev/null and b/assets/reward/COMMISSION_ADVICE.png differ diff --git a/assets/reward/COMMISSION_DAILY.png b/assets/reward/COMMISSION_DAILY.png new file mode 100644 index 000000000..cbb66d16c Binary files /dev/null and b/assets/reward/COMMISSION_DAILY.png differ diff --git a/assets/reward/COMMISSION_HAS_PENDING.png b/assets/reward/COMMISSION_HAS_PENDING.png new file mode 100644 index 000000000..6132f09e0 Binary files /dev/null and b/assets/reward/COMMISSION_HAS_PENDING.png differ diff --git a/assets/reward/COMMISSION_NOTICE_AT_CAMPAIGN.png b/assets/reward/COMMISSION_NOTICE_AT_CAMPAIGN.png new file mode 100644 index 000000000..8b97a85de Binary files /dev/null and b/assets/reward/COMMISSION_NOTICE_AT_CAMPAIGN.png differ diff --git a/assets/reward/COMMISSION_OIL_CONFIRM.png b/assets/reward/COMMISSION_OIL_CONFIRM.png new file mode 100644 index 000000000..2334f0bcd Binary files /dev/null and b/assets/reward/COMMISSION_OIL_CONFIRM.png differ diff --git a/assets/reward/COMMISSION_START.png b/assets/reward/COMMISSION_START.png new file mode 100644 index 000000000..1cf507b59 Binary files /dev/null and b/assets/reward/COMMISSION_START.png differ diff --git a/assets/reward/COMMISSION_STOP_SCROLLING.png b/assets/reward/COMMISSION_STOP_SCROLLING.png new file mode 100644 index 000000000..8be7ef16b Binary files /dev/null and b/assets/reward/COMMISSION_STOP_SCROLLING.png differ diff --git a/assets/reward/COMMISSION_URGENT.png b/assets/reward/COMMISSION_URGENT.png new file mode 100644 index 000000000..0fb8a0a24 Binary files /dev/null and b/assets/reward/COMMISSION_URGENT.png differ diff --git a/assets/ui/COMMISSION_CHECK.png b/assets/ui/COMMISSION_CHECK.png new file mode 100644 index 000000000..5e52c15d7 Binary files /dev/null and b/assets/ui/COMMISSION_CHECK.png differ diff --git a/assets/ui/REWARD_GOTO_COMMISSION.png b/assets/ui/REWARD_GOTO_COMMISSION.png new file mode 100644 index 000000000..5dd5ab633 Binary files /dev/null and b/assets/ui/REWARD_GOTO_COMMISSION.png differ diff --git a/config/main_template.conf b/config/main_template.conf index 5a823e808..814e1042d 100644 --- a/config/main_template.conf +++ b/config/main_template.conf @@ -46,6 +46,26 @@ reward_interval = 20 enable_oil_reward = yes enable_coin_reward = yes enable_mission_reward = yes +enable_commission_reward = yes +commission_time_limit = 23:30 +duration_shorter_than_2 = 11 +duration_longer_than_6 = -11 +expire_shorter_than_2 = 11 +expire_longer_than_6 = -11 +daily_comm = 100 +major_comm = 0 +extra_drill = 20 +extra_part = 60 +extra_cube = 80 +extra_oil = 90 +extra_book = 70 +urgent_drill = 45 +urgent_part = 95 +urgent_cube = 165 +urgent_book = 145 +urgent_box = 195 +urgent_gem = 205 +urgent_ship = 155 [Emulator] command = emulator diff --git a/main.pyw b/main.pyw index f113c8b27..c61ad6660 100644 --- a/main.pyw +++ b/main.pyw @@ -154,6 +154,36 @@ def main(): reward_mission = reward_parser.add_argument_group('任务奖励', '') reward_mission.add_argument('--启用任务收获', default=default('--启用任务收获'), choices=['是', '否']) + reward_commission = reward_parser.add_argument_group('委托设置', '') + reward_commission.add_argument('--启用委托收获', default=default('--启用委托收获'), choices=['是', '否']) + reward_commission.add_argument('--委托时间限制', default=default('--委托时间限制'), help='忽略完成时间超过限制的委托, 格式: 23:30') + + priority1 = reward_commission.add_argument_group('委托耗时优先级', '') + priority1.add_argument('--委托耗时小于2h', default=default('--委托耗时小于2h'), help='') + priority1.add_argument('--委托耗时超过6h', default=default('--委托耗时超过6h'), help='') + priority1.add_argument('--委托过期小于2h', default=default('--委托过期小于2h'), help='') + priority1.add_argument('--委托过期大于6h', default=default('--委托过期大于6h'), help='') + + priority2 = reward_commission.add_argument_group('日常委托优先级', '') + priority2.add_argument('--日常委托', default=default('--日常委托'), help='日常资源开发, 高阶战术研发') + priority2.add_argument('--主要委托', default=default('--主要委托'), help='1200油/1000油委托') + + priority3 = reward_commission.add_argument_group('额外委托优先级', '') + priority3.add_argument('--钻头类额外委托', default=default('--钻头类额外委托'), help='短距离航行训练, 近海防卫巡逻') + priority3.add_argument('--部件类额外委托', default=default('--部件类额外委托'), help='矿脉护卫委托, 林木护卫委托') + priority3.add_argument('--魔方类额外委托', default=default('--魔方类额外委托'), help='舰队高阶演习, 舰队护卫演习') + priority3.add_argument('--石油类额外委托', default=default('--石油类额外委托'), help='小型油田开发, 大型油田开发') + priority3.add_argument('--教材类额外委托', default=default('--教材类额外委托'), help='小型商船护卫, 大型商船护卫') + + priority4 = reward_commission.add_argument_group('紧急委托优先级', '') + priority4.add_argument('--钻头类紧急委托', default=default('--钻头类紧急委托'), help='保卫运输部队, 歼灭敌精锐部队') + priority4.add_argument('--部件类紧急委托', default=default('--部件类紧急委托'), help='支援维拉维拉岛, 支援恐班纳') + priority4.add_argument('--魔方类紧急委托', default=default('--魔方类紧急委托'), help='解救商船, 敌袭') + priority4.add_argument('--教材类紧急委托', default=default('--教材类紧急委托'), help='支援土豪尔岛, 支援萌岛') + priority4.add_argument('--装备类紧急委托', default=default('--装备类紧急委托'), help='BIW装备运输, NYB装备研发') + priority4.add_argument('--钻石类紧急委托', default=default('--钻石类紧急委托'), help='BIW要员护卫, NYB巡视护卫') + priority4.add_argument('--观舰类紧急委托', default=default('--观舰类紧急委托'), help='小型观舰仪式, 同盟观舰仪式') + # ==========设备设置========== emulator_parser = subs.add_parser('设备设置') emulator = emulator_parser.add_argument_group('模拟器', '') diff --git a/module/base/ocr.py b/module/base/ocr.py index 1687d92c2..358b35bbd 100644 --- a/module/base/ocr.py +++ b/module/base/ocr.py @@ -16,6 +16,8 @@ OCR_MODELS = { # Font: Impact # Charset: 0123456789ABCDEFSP-:/ 'stage': CnOcr(root='./cnocr_models/stage', model_epoch=56), + + 'cnocr': CnOcr(root='./cnocr_models/cnocr', model_epoch=20) } image_shape = (280, 32) width_range = (0.6, 1.4) @@ -26,7 +28,7 @@ y_range = (-2, 2) class Ocr: def __init__(self, buttons, lang, letter=(255, 255, 255), back=(0, 0, 0), mid_process_height=70, threshold=127, - additional_preprocess=None, length=None, white_list=None, name='OCR'): + additional_preprocess=None, use_binary=True, length=None, white_list=None, name='OCR'): """ Args: lang (str): OCR model. in ['digit', 'cnocr']. @@ -34,6 +36,7 @@ class Ocr: back (tuple(int)): Background RGB. mid_process_height (int): 70 additional_preprocess (callable): + use_binary (bool): length (int, tuple(int)): Expected length. white_list (str): Expected str. buttons (Button, List[Button]): Button or list of Button instance. @@ -45,6 +48,7 @@ class Ocr: self.mid_process_height = mid_process_height self.threshold = threshold self.additional_preprocess = additional_preprocess + self.use_binary=use_binary self.length = (length, length) if isinstance(length, int) else length self.white_list = white_list self.buttons = buttons if isinstance(buttons, list) else [buttons] @@ -80,7 +84,8 @@ class Ocr: image = self.additional_preprocess(image) # Binarization. - _, image = cv2.threshold(image, self.threshold, 255, cv2.THRESH_BINARY) + if self.use_binary: + _, image = cv2.threshold(image, self.threshold, 255, cv2.THRESH_BINARY) # Resize to input size. size = (int(image.shape[1] / image.shape[0] * image_shape[1]), image_shape[1]) diff --git a/module/base/timer.py b/module/base/timer.py index 454cf4634..2ba3b242b 100644 --- a/module/base/timer.py +++ b/module/base/timer.py @@ -1,4 +1,5 @@ import time +from datetime import datetime, timedelta from functools import wraps from module.logger import logger @@ -16,6 +17,20 @@ def timer(function): return function_timer +def future_time(string): + """ + Args: + string (str): Such as 14:59. + + Returns: + 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) + future = future + timedelta(days=1) if future < datetime.now() else future + return future + + class Timer: def __init__(self, limit): self.limit = limit diff --git a/module/config/config.py b/module/config/config.py index 5aa950ce8..1c3fcd6b4 100644 --- a/module/config/config.py +++ b/module/config/config.py @@ -2,12 +2,13 @@ import codecs import configparser import copy import os -from datetime import datetime, timedelta -import cv2 +from datetime import datetime +import cv2 import numpy as np from PIL import Image +from module.base.timer import future_time from module.config.dictionary import * from module.logger import logger @@ -240,6 +241,28 @@ class AzurLaneConfig: ENABLE_OIL_REWARD = True ENABLE_COIN_REWARD = True ENABLE_MISSION_REWARD = True + ENABLE_COMMISSION_REWARD = True + COMMISSION_PRIORITY = { + 'major_comm': 0, + 'daily_comm': 100, + 'extra_drill': 20, + 'extra_part': 60, + 'extra_cube': 80, + 'extra_oil': 90, + 'extra_book': 70, + 'urgent_drill': 45, + 'urgent_part': 95, + 'urgent_book': 145, + 'urgent_box': 195, + 'urgent_cube': 165, + 'urgent_gem': 205, + 'urgent_ship': 155, + 'expire_shorter_than_2': 11, + 'expire_longer_than_6': -11, + 'duration_shorter_than_2': 11, + 'duration_longer_than_6': -11, + } + COMMISSION_TIME_LIMIT = 0 """ C_7_2_mystery_farming @@ -302,10 +325,7 @@ class AzurLaneConfig: 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 + self.STOP_IF_TIME_REACH = future_time(option['if_time_reach']) else: self.STOP_IF_TIME_REACH = 0 self.STOP_IF_OIL_LOWER_THAN = int(option['if_oil_lower_than']) @@ -340,8 +360,11 @@ 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']: + for attr in ['enable_reward', 'enable_oil_reward', 'enable_coin_reward', 'enable_mission_reward', 'enable_commission_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]) option = config['Main'] self.CAMPAIGN_NAME = option['main_stage'] diff --git a/module/config/dictionary.py b/module/config/dictionary.py index a6d38be70..2384a2701 100644 --- a/module/config/dictionary.py +++ b/module/config/dictionary.py @@ -76,6 +76,26 @@ dic_chi_to_eng = { '启用石油收获': 'enable_oil_reward', '启用物资收获': 'enable_coin_reward', '启用任务收获': 'enable_mission_reward', + '启用委托收获': 'enable_commission_reward', + '委托时间限制': 'commission_time_limit', + '委托耗时小于2h': 'duration_shorter_than_2', + '委托耗时超过6h': 'duration_longer_than_6', + '委托过期小于2h': 'expire_shorter_than_2', + '委托过期大于6h': 'expire_longer_than_6', + '日常委托': 'daily_comm', + '主要委托': 'major_comm', + '钻头类额外委托': 'extra_drill', + '部件类额外委托': 'extra_part', + '魔方类额外委托': 'extra_cube', + '石油类额外委托': 'extra_oil', + '教材类额外委托': 'extra_book', + '钻头类紧急委托': 'urgent_drill', + '部件类紧急委托': 'urgent_part', + '魔方类紧急委托': 'urgent_cube', + '教材类紧急委托': 'urgent_book', + '装备类紧急委托': 'urgent_box', + '钻石类紧急委托': 'urgent_gem', + '观舰类紧急委托': 'urgent_ship', '设备': 'serial', '打每日': 'enable_daily_mission', '打困难': 'enable_hard_campaign', diff --git a/module/reward/assets.py b/module/reward/assets.py index d50479cef..ca05b2e7a 100644 --- a/module/reward/assets.py +++ b/module/reward/assets.py @@ -5,6 +5,14 @@ from module.base.template import Template # Don't modified it manually. COIN = Button(area=(403, 64, 436, 88), color=(226, 173, 72), button=(403, 64, 436, 88), file='./assets/reward/COIN.png') +COMMISSION_ADVICE = Button(area=(871, 322, 999, 383), color=(230, 177, 116), button=(871, 322, 999, 383), file='./assets/reward/COMMISSION_ADVICE.png') +COMMISSION_DAILY = Button(area=(35, 132, 67, 186), color=(208, 172, 118), button=(35, 132, 67, 186), file='./assets/reward/COMMISSION_DAILY.png') +COMMISSION_HAS_PENDING = Button(area=(357, 300, 359, 330), color=(86, 201, 173), button=(357, 300, 359, 330), file='./assets/reward/COMMISSION_HAS_PENDING.png') +COMMISSION_NOTICE_AT_CAMPAIGN = Button(area=(1054, 647, 1061, 654), color=(197, 89, 64), button=(1054, 647, 1061, 654), file='./assets/reward/COMMISSION_NOTICE_AT_CAMPAIGN.png') +COMMISSION_OIL_CONFIRM = Button(area=(704, 493, 876, 550), color=(96, 145, 204), button=(704, 493, 876, 550), file='./assets/reward/COMMISSION_OIL_CONFIRM.png') +COMMISSION_START = Button(area=(1028, 322, 1156, 383), color=(229, 175, 113), button=(1028, 322, 1156, 383), file='./assets/reward/COMMISSION_START.png') +COMMISSION_STOP_SCROLLING = Button(area=(115, 236, 179, 487), color=(50, 55, 74), button=(115, 236, 179, 487), file='./assets/reward/COMMISSION_STOP_SCROLLING.png') +COMMISSION_URGENT = Button(area=(35, 231, 68, 281), color=(215, 188, 124), button=(35, 231, 68, 281), file='./assets/reward/COMMISSION_URGENT.png') EXP_INFO_S_REWARD = Button(area=(498, 140, 557, 154), color=(233, 241, 127), button=(498, 140, 557, 154), file='./assets/reward/EXP_INFO_S_REWARD.png') MISSION_MULTI = Button(area=(1041, 8, 1101, 39), color=(226, 192, 142), button=(1041, 8, 1101, 39), file='./assets/reward/MISSION_MULTI.png') MISSION_NOTICE = Button(area=(940, 670, 945, 681), color=(183, 83, 66), button=(940, 670, 945, 681), file='./assets/reward/MISSION_NOTICE.png') diff --git a/module/reward/commission.py b/module/reward/commission.py new file mode 100644 index 000000000..91ca35529 --- /dev/null +++ b/module/reward/commission.py @@ -0,0 +1,465 @@ +import re +from datetime import datetime, timedelta + +import cv2 +import numpy as np +from scipy import signal + +from module.base.ocr import Ocr +from module.base.timer import Timer +from module.base.utils import area_offset, get_color +from module.handler.info_bar import InfoBarHandler +from module.logger import logger +from module.reward.assets import * +from module.ui.page import page_reward, page_commission +from module.ui.ui import UI + +dictionary = { + 'major_comm': ['自主训练', '对抗演习', '科研任务', '工具整备', '战术课程', '货物运输'], + 'daily_comm': ['日常资源开发', '高阶战术研发'], + 'extra_drill': ['航行训练', '防卫巡逻', '海域浮标检查作业'], + 'extra_part': ['委托'], + 'extra_cube': ['演习'], + 'extra_oil': ['油田'], + 'extra_book': ['商船护卫'], + 'urgent_drill': ['运输部队', '侦查部队', '主力部队', '精锐部队'], + 'urgent_part': ['维拉', '伊', '多伦瓦', '恐班纳'], + 'urgent_book': ['土豪尔', '姆波罗', '马拉基', '卡波罗', '马内', '玛丽', '萌', '特林'], + 'urgent_box': ['装备', '物资'], + 'urgent_cube': ['解救', '敌袭'], + 'urgent_gem': ['要员', '度假', '巡视'], + 'urgent_ship': ['观舰'] +} + + +class Commission: + def __init__(self, image, y): + self.y = y + self.stack_y = y + self.area = (188, y - 119, 1199, y) + self.image = image + self.valid = True + + # Name + area = area_offset((211, 26, 415, 49), self.area[0:2]) + button = Button(area=area, color=(), button=area, name='COMMISSION') + ocr = Ocr(button, lang='cnocr', back=(74, 97, 148), use_binary=False) + self.button = button + self.name = ocr.ocr(image) + self.genre = self.parse_name(self.name) + + # Duration time + area = area_offset((290, 74, 390, 92), self.area[0:2]) + button = Button(area=area, color=(), button=area, name='DURATION') + ocr = Ocr(button, lang='stage', back=(57, 85, 132)) + self.duration = self.parse_time(ocr.ocr(image)) + + # Expire time + area = area_offset((-49, 68, -45, 84), self.area[0:2]) + button = Button(area=area, color=(189, 65, 66), button=area, name='IS_URGENT') + if button.appear_on(image): + area = area_offset((-49, 73, 45, 91), self.area[0:2]) + button = Button(area=area, color=(), button=area, name='EXPIRE') + ocr = Ocr(button, lang='stage', back=(189, 65, 66)) + self.expire = self.parse_time(ocr.ocr(image)) + else: + self.expire = None + + # Status + area = area_offset((179, 71, 187, 93), self.area[0:2]) + dic = { + 0: 'finished', + 1: 'running', + 2: 'pending' + } + self.status = dic[int(np.argmax(get_color(image, area)))] + + def __str__(self): + if self.valid: + if self.expire: + return f'{self.name} (Genre: {self.genre}, Status: {self.status}, Duration: {self.duration}, Expire: {self.expire})' + else: + return f'{self.name} (Genre: {self.genre}, Status: {self.status}, Duration: {self.duration})' + else: + return f'{self.name} (Invalid)' + + def __eq__(self, other): + """ + Args: + other (Commission): + + Returns: + bool: + """ + threshold = timedelta(seconds=30) + if not self.valid or not other.valid: + return False + if self.genre != other.genre or self.status != other.status: + return False + if (other.duration < self.duration - threshold) or (other.duration > self.duration + threshold): + return False + if (not self.expire and other.expire) or (self.expire and not other.expire): + return False + if self.expire and other.expire: + if (other.expire < self.expire - threshold) or (other.expire > self.expire + threshold): + return False + + return True + + def parse_time(self, string): + """ + Args: + string (str): Such as 01:00:00, 05:47:10, 17:50:51. + + Returns: + timedelta: datetime.timedelta instance. + """ + string = string.replace('D', '0') # Poor OCR + result = re.search('(\d+):(\d+):(\d+)', string) + if not result: + logger.warning(f'Invalid time string: {string}') + self.valid = False + return None + else: + result = [int(s) for s in result.groups()] + return timedelta(hours=result[0], minutes=result[1], seconds=result[2]) + + def parse_name(self, string): + """ + Args: + string (str): Commission name, such as 'NYB要员护卫'. + + Returns: + str: Commission genre, such as 'urgent_gem'. + """ + for key, value in dictionary.items(): + for keyword in value: + if keyword in string: + return key + + logger.warning(f'Name with unknown genre: {string}') + self.valid = False + return '' + + +class CommissionGroup: + show = (188, 67, 1199, 692) + height = 210 # About 1.5 commission height + lower = int((show[3] - show[1]) / 2 - height / 2) + template_area = (620, lower, 1154, lower + height) + + def __init__(self): + self.template = None + self.swipe = 0 + self.commission = [] + + def __contains__(self, item): + for commission in self.commission: + if commission == item: + return True + + return False + + def __iter__(self): + return iter(self.commission) + + def __bool__(self): + return len(self.commission) > 0 + + @property + def count(self): + return len(self.commission) + + def merge(self, image): + """Load commissions from image. + If you want to load commissions from multiple image, + make sure that the next one and previous one have something same. + Which means, you merge a image, then swipe a little bit, then merge another image. + + Args: + image (PIl.Image.Image): + """ + # Find swipe distance + if self.template is None: + self.template = np.array(image.crop(self.template_area)) + self.swipe = 0 + res = cv2.matchTemplate(self.template, np.array(image), cv2.TM_CCOEFF_NORMED) + _, similarity, _, position = cv2.minMaxLoc(res) + if similarity < 0.85: + logger.warning(f'Low similarity when finding swipe. Similarity: {similarity}, Position: {position}') + self.swipe -= position[1] - self.template_area[1] + self.template = np.array(image.crop(self.template_area)) + + # Find commission position + color_height = np.mean(image.crop((597, 0, 619, 720)).convert('L'), axis=1) + parameters = {'height': 200} + peaks, _ = signal.find_peaks(color_height, **parameters) + peaks = [y for y in peaks if y > 67 + 117] + + # Add commission to list + for y in peaks: + stack_y = y + self.swipe + diff = np.array([c.stack_y - stack_y for c in self.commission]) + if np.any(np.abs(diff) < 3): + continue + commission = Commission(image, y=y) + commission.stack_y = stack_y + logger.info(f'Add commission: {commission}') + self.commission.append(commission) + + +def commission_choose(daily, urgent, priority, time_limit=None): + """ + Args: + daily (CommissionGroup): + urgent (CommissionGroup): + priority (dict): + time_limit (datetime): + + Returns: + CommissionGroup, CommissionGroup: Chosen daily commission, Chosen urgent commission + """ + # Count Commission + commission = daily.commission + urgent.commission + running_count = np.sum([1 for c in commission if c.status == 'running']) + logger.attr('Running', running_count) + if running_count >= 4: + return [], [] + + # Calculate priority + commission = [c for c in commission if c.valid and c.status == 'pending'] + comm_priority = [] + for comm in commission: + pri = priority[comm.genre] + if comm.duration <= timedelta(hours=2): + pri += priority['duration_shorter_than_2'] + if comm.duration >= timedelta(hours=6): + pri += priority['duration_longer_than_6'] + if comm.expire: + if comm.expire <= timedelta(hours=2): + pri += priority['expire_shorter_than_2'] + if comm.expire >= timedelta(hours=6): + pri += priority['expire_longer_than_6'] + comm_priority.append(pri) + + # Sort + commission = list(np.array(commission)[np.argsort(comm_priority)])[::-1] + if time_limit: + commission = [comm for comm in commission if datetime.now() + comm.duration <= time_limit] + commission = commission[:4 - running_count] + daily_choose, urgent_choose = CommissionGroup(), CommissionGroup() + for comm in commission: + if comm in daily: + daily_choose.commission.append(comm) + if comm in urgent: + urgent_choose.commission.append(comm) + + if daily_choose: + logger.info('Choose daily commission') + for comm in daily_choose: + logger.info(comm) + if urgent_choose: + logger.info('Choose urgent commission') + for comm in urgent_choose: + logger.info(comm) + + return daily_choose, urgent_choose + + +class RewardCommission(UI, InfoBarHandler): + daily: CommissionGroup + urgent: CommissionGroup + daily_choose: CommissionGroup + urgent_choose: CommissionGroup + + def _commission_ensure_mode(self, mode): + if self.appear(COMMISSION_DAILY): + current = 'daily' + elif self.appear(COMMISSION_URGENT): + current = 'urgent' + else: + logger.warning('Unknown Commission mode') + return False + if current == mode: + return False + + if mode == 'daily': + self.device.click(COMMISSION_DAILY) + if mode == 'urgent': + self.device.click(COMMISSION_URGENT) + + self.device.sleep(0.3) + self.device.screenshot() + return True + + def _commission_mode_reset(self): + if self.appear(COMMISSION_DAILY): + current, another = COMMISSION_DAILY, COMMISSION_URGENT + elif self.appear(COMMISSION_URGENT): + current, another = COMMISSION_URGENT, COMMISSION_DAILY + else: + logger.warning('Unknown Commission mode') + return False + + self.device.click(another) + self.device.screenshot() + self.device.click(current) + self.device.sleep(0.3) + self.device.screenshot() + + return True + + def _commission_swipe(self, distance=180): + # Distance of two commission is 146px + self.device.swipe((0, -distance), box=(620, 67, 1154, 692), random_range=(-20, -5, 20, 5)) + self.device.click(COMMISSION_STOP_SCROLLING) + self.device.sleep(0.3) + self.device.screenshot() + + def _commission_scan_list(self): + commission = CommissionGroup() + commission.merge(self.device.image) + if commission.count <= 3: + return commission + + prev = commission.count + for _ in range(15): + self._commission_swipe() + commission.merge(self.device.image) + if commission.count - prev <= 0: + break + prev = commission.count + + return commission + + def _commission_scan_all(self): + logger.hr('Scan daily') + self._commission_ensure_mode('daily') + daily = self._commission_scan_list() + + logger.hr('Scan urgent') + self._commission_ensure_mode('urgent') + urgent = self._commission_scan_list() + + logger.hr('Showing commission') + logger.info('Daily commission') + for comm in daily: + logger.info(comm) + if urgent.count: + logger.info('Urgent commission') + for comm in urgent: + logger.info(comm) + + self.daily = daily + self.urgent = urgent + self.daily_choose, self.urgent_choose = commission_choose( + self.daily, + self.urgent, + priority=self.config.COMMISSION_PRIORITY, + time_limit=self.config.COMMISSION_TIME_LIMIT) + return daily, urgent + + def _commission_start_click(self, comm): + """ + Args: + comm (Commission): + """ + logger.info(f'Start commission {comm}') + comm_timer = Timer(3) + while 1: + if comm_timer.reached(): + self.device.click(comm.button) + comm_timer.reset() + + if self.appear_then_click(COMMISSION_OIL_CONFIRM, offset=True, interval=3): + pass + if self.appear_then_click(COMMISSION_START, interval=3): + pass + if self.appear_then_click(COMMISSION_ADVICE, interval=3): + pass + + if self.handle_info_bar(): + break + + self.device.screenshot() + + return True + + def _commission_find_and_start(self, comm): + """ + Args: + comm (Commission): + """ + logger.hr(f'Finding commission') + logger.info(f'Finding commission {comm}') + + commission = CommissionGroup() + prev = 0 + for _ in range(15): + commission.merge(self.device.image) + + if commission.count - prev <= 0: + return True + prev = commission.count + + if comm in commission: + # Update commission position. + # Because once you start a commission, the commission list changes. + for new_comm in commission: + if comm == new_comm: + comm = new_comm + self._commission_start_click(comm) + self._commission_mode_reset() + return True + + self._commission_swipe() + + logger.warning(f'Commission not found: {comm}') + return False + + def commission_start(self): + """ + Method to scan and run commissions. + Make sure current page is page_commission before calls. + """ + logger.hr('Commission scan', level=2) + self._commission_scan_all() + + logger.hr('Commission run', level=2) + if self.daily_choose: + for comm in self.daily_choose: + self._commission_ensure_mode('daily') + self._commission_find_and_start(comm) + if self.urgent_choose: + for comm in self.urgent_choose: + self._commission_ensure_mode('urgent') + self._commission_find_and_start(comm) + if not self.daily_choose and not self.urgent_choose: + logger.info('No commission chose') + + def handle_commission_start(self): + """Make sure current page is page_reward before calls. + + Returns: + bool: If runs a commission. + """ + if not self.config.ENABLE_COMMISSION_REWARD: + return False + self.device.screenshot() + if not self.appear(COMMISSION_HAS_PENDING): + logger.info('No commission pending') + return False + + self.ui_goto(page_commission) + + self.commission_start() + + self.ui_goto(page_reward, skip_first_screenshot=True) + + def commission_notice_show_at_campaign(self): + """Make sure current page is page_campaign before calls. + + Returns: + bool: If any commission finished. + """ + return self.appear(COMMISSION_NOTICE_AT_CAMPAIGN) diff --git a/module/reward/reward.py b/module/reward/reward.py index 73f5bc709..91922238b 100644 --- a/module/reward/reward.py +++ b/module/reward/reward.py @@ -5,10 +5,10 @@ from module.combat.assets import * from module.logger import logger from module.reward.assets import * from module.ui.page import * -from module.ui.ui import UI +from module.reward.commission import RewardCommission -class Reward(UI): +class Reward(RewardCommission): def reward(self): logger.hr('Reward start') self.ui_goto_main() @@ -17,6 +17,8 @@ class Reward(UI): self.ui_goto(page_reward, skip_first_screenshot=True) self._reward_receive() + self.handle_info_bar() + self.handle_commission_start() self.ui_click( click_button=page_reward.links[page_main], @@ -47,6 +49,16 @@ class Reward(UI): exit_timer = Timer(1) click_timer = Timer(1) exit_timer.start() + btn = [] + if self.config.ENABLE_REWARD: + btn.append(REWARD_3) + if self.config.ENABLE_COMMISSION_REWARD: + btn.append(REWARD_1) + if self.config.ENABLE_OIL_REWARD: + btn.append(OIL) + if self.config.ENABLE_COIN_REWARD: + btn.append(COIN) + while 1: self.device.screenshot() @@ -57,7 +69,7 @@ class Reward(UI): reward = True continue - for button in [REWARD_1, REWARD_3, OIL, COIN]: + for button in btn: if not click_timer.reached(): continue if self.appear_then_click(button, interval=1): diff --git a/module/ui/assets.py b/module/ui/assets.py index 72f059c3d..66dbb0152 100644 --- a/module/ui/assets.py +++ b/module/ui/assets.py @@ -8,6 +8,7 @@ BACK_ARROW = Button(area=(36, 53, 82, 55), color=(251, 251, 255), button=(33, 31 CAMPAIGN_GOTO_DAILY = Button(area=(804, 648, 892, 703), color=(189, 145, 78), button=(804, 648, 892, 703), file='./assets/ui/CAMPAIGN_GOTO_DAILY.png') CAMPAIGN_GOTO_EVENT = Button(area=(804, 648, 892, 703), color=(189, 145, 78), button=(1178, 171, 1230, 223), file='./assets/ui/CAMPAIGN_GOTO_EVENT.png') CAMPAIGN_GOTO_EXERCISE = Button(area=(1166, 648, 1248, 703), color=(177, 136, 69), button=(1166, 648, 1248, 703), file='./assets/ui/CAMPAIGN_GOTO_EXERCISE.png') +COMMISSION_CHECK = Button(area=(122, 16, 175, 39), color=(157, 173, 210), button=(122, 16, 175, 39), file='./assets/ui/COMMISSION_CHECK.png') DAILY_CHECK = Button(area=(23, 656, 67, 698), color=(84, 139, 210), button=(23, 656, 67, 698), file='./assets/ui/DAILY_CHECK.png') EVENT_CHECK = Button(area=(123, 63, 206, 109), color=(88, 104, 138), button=(123, 63, 206, 109), file='./assets/ui/EVENT_CHECK.png') EXERCISE_CHECK = Button(area=(1065, 340, 1204, 382), color=(129, 166, 220), button=(1065, 340, 1204, 382), file='./assets/ui/EXERCISE_CHECK.png') @@ -20,5 +21,6 @@ MAIN_GOTO_REWARD = Button(area=(11, 139, 30, 189), color=(69, 81, 115), button=( MISSION_CHECK = Button(area=(120, 15, 173, 40), color=(141, 156, 194), button=(120, 15, 173, 40), file='./assets/ui/MISSION_CHECK.png') OCR_OIL_CV = Button(area=(634, 27, 714, 48), color=(93, 95, 109), button=(634, 27, 714, 48), file='./assets/ui/OCR_OIL_CV.png') 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') SP_CHECK = Button(area=(123, 63, 206, 109), color=(95, 110, 145), button=(123, 63, 206, 109), file='./assets/ui/SP_CHECK.png') diff --git a/module/ui/page.py b/module/ui/page.py index b0bfb0e9b..a9259e7b7 100644 --- a/module/ui/page.py +++ b/module/ui/page.py @@ -70,3 +70,10 @@ page_main.link(button=MAIN_GOTO_REWARD, destination=page_reward) page_mission = Page(MISSION_CHECK) page_mission.link(button=GOTO_MAIN, destination=page_main) page_main.link(button=MAIN_GOTO_MISSION, destination=page_mission) + +# Commission +# Please don't goto commission from campaign. +page_commission = Page(COMMISSION_CHECK) +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)