Merge branch 'master' of https://github.com/whoamikyo/AzurLaneAutoScript
@ -65,7 +65,7 @@ enable_coin_reward = yes
|
|||||||
enable_mission_reward = yes
|
enable_mission_reward = yes
|
||||||
enable_commission_reward = yes
|
enable_commission_reward = yes
|
||||||
enable_tactical_reward = yes
|
enable_tactical_reward = yes
|
||||||
commission_time_limit = 23:30
|
commission_time_limit = 0
|
||||||
duration_shorter_than_2 = 11
|
duration_shorter_than_2 = 11
|
||||||
duration_longer_than_6 = -11
|
duration_longer_than_6 = -11
|
||||||
expire_shorter_than_2 = 11
|
expire_shorter_than_2 = 11
|
||||||
|
241
dev_tools/item_stastistics.py
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
# os.chdir('../')
|
||||||
|
import module.config.server as server
|
||||||
|
|
||||||
|
server.server = 'cn' # Don't need to edit, it's used to avoid error.
|
||||||
|
print(os.getcwd())
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from PIL import Image
|
||||||
|
import cv2
|
||||||
|
import time
|
||||||
|
from module.combat.assets import GET_ITEMS_1, GET_ITEMS_2
|
||||||
|
from module.handler.assets import INFO_BAR_1
|
||||||
|
from module.base.button import ButtonGrid, Button
|
||||||
|
from module.base.ocr import Ocr
|
||||||
|
from module.logger import logger
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
Set your folder here
|
||||||
|
Examples: xxx/campaign_7_2
|
||||||
|
"""
|
||||||
|
IMAGE_FOLDER = ''
|
||||||
|
STATUS_ITEMS_INTERVAL = 10
|
||||||
|
TEMPLATE_THRESHOLD = 0.9
|
||||||
|
|
||||||
|
BATTLE_STATUS_FOLDER = f'{IMAGE_FOLDER}/status'
|
||||||
|
GET_ITEMS_FOLDER = f'{IMAGE_FOLDER}/get_items'
|
||||||
|
TEMPLATE_FOLDER = f'{IMAGE_FOLDER}/item_template'
|
||||||
|
for f_ in [TEMPLATE_FOLDER]:
|
||||||
|
if not os.path.exists(f_):
|
||||||
|
os.mkdir(f_)
|
||||||
|
BATTLE_STATUS_TIMESTAMP = np.array([int(f.split('.')[0]) for f in os.listdir(BATTLE_STATUS_FOLDER)])
|
||||||
|
ITEM_GRIDS_1_ODD = ButtonGrid(origin=(336, 298), delta=(128, 0), button_shape=(96, 96), grid_shape=(5, 1))
|
||||||
|
ITEM_GRIDS_1_EVEN = ButtonGrid(origin=(400, 298), delta=(128, 0), button_shape=(96, 96), grid_shape=(4, 1))
|
||||||
|
ITEM_GRIDS_2 = ButtonGrid(origin=(336, 227), delta=(128, 142), button_shape=(96, 96), grid_shape=(5, 2))
|
||||||
|
ENEMY_GENRE_BUTTON = Button(area=(782, 285, 961, 319), color=(), button=(), name='ENEMY_GENRE')
|
||||||
|
|
||||||
|
|
||||||
|
class AmountOcr(Ocr):
|
||||||
|
def ocr(self, image):
|
||||||
|
start_time = time.time()
|
||||||
|
|
||||||
|
image_list = [self.pre_process(i) for i in image]
|
||||||
|
result_list = self.cnocr.ocr_for_single_lines(image_list)
|
||||||
|
result_list = [self.after_process(result) for result in result_list]
|
||||||
|
|
||||||
|
if len(self.buttons) == 1:
|
||||||
|
result_list = result_list[0]
|
||||||
|
logger.attr(name='%s %ss' % (self.name, str(round(time.time() - start_time, 3)).ljust(5, '0')),
|
||||||
|
text=str(result_list))
|
||||||
|
|
||||||
|
return result_list
|
||||||
|
|
||||||
|
def after_process(self, raw):
|
||||||
|
"""
|
||||||
|
Returns:
|
||||||
|
int:
|
||||||
|
"""
|
||||||
|
raw = super().after_process(raw)
|
||||||
|
if not raw:
|
||||||
|
result = 0
|
||||||
|
else:
|
||||||
|
result = int(raw)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
AMOUNT_OCR = AmountOcr([], back=(-200, -200, -200), lang='digit', name='Amount_ocr')
|
||||||
|
ENEMY_GENRE_OCR = Ocr(ENEMY_GENRE_BUTTON, lang='cnocr', use_binary=False, back=(127, 127, 127))
|
||||||
|
|
||||||
|
|
||||||
|
class ImageError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ItemTemplate:
|
||||||
|
def __init__(self, image):
|
||||||
|
self.image = np.array(image)
|
||||||
|
|
||||||
|
def match(self, image):
|
||||||
|
res = cv2.matchTemplate(self.image, np.array(image), cv2.TM_CCOEFF_NORMED)
|
||||||
|
_, similarity, _, _ = cv2.minMaxLoc(res)
|
||||||
|
return similarity > TEMPLATE_THRESHOLD
|
||||||
|
|
||||||
|
def save(self, name):
|
||||||
|
image = Image.fromarray(self.image)
|
||||||
|
image.save(f'{TEMPLATE_FOLDER}/{name}.png')
|
||||||
|
|
||||||
|
|
||||||
|
class ItemTemplateGroup:
|
||||||
|
def __init__(self):
|
||||||
|
self.templates = {}
|
||||||
|
for file in os.listdir(TEMPLATE_FOLDER):
|
||||||
|
name = file[:-4]
|
||||||
|
image = Image.open(f'{TEMPLATE_FOLDER}/{file}').convert('RGB')
|
||||||
|
self.templates[name] = ItemTemplate(image)
|
||||||
|
|
||||||
|
def match(self, item):
|
||||||
|
for name, template in self.templates.items():
|
||||||
|
if template.match(item.image):
|
||||||
|
return name
|
||||||
|
|
||||||
|
template = ItemTemplate(item.get_template())
|
||||||
|
name = [int(n) for n in self.templates.keys() if n.isdigit()]
|
||||||
|
if len(name):
|
||||||
|
name = str(max(name) + 1)
|
||||||
|
else:
|
||||||
|
name = str(len(self.templates.keys()) + 1)
|
||||||
|
|
||||||
|
logger.info(f'New item template: {name}')
|
||||||
|
self.templates[name] = template
|
||||||
|
template.save(name)
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
template_group = ItemTemplateGroup()
|
||||||
|
|
||||||
|
|
||||||
|
class Item:
|
||||||
|
def __init__(self, image):
|
||||||
|
self.image = image
|
||||||
|
self.is_valid = np.mean(np.array(image.convert('L')) > 127) > 0.1
|
||||||
|
self.name = 'Default_item'
|
||||||
|
self.amount = 1
|
||||||
|
if self.is_valid:
|
||||||
|
self.name = template_group.match(self)
|
||||||
|
if not self.name.startswith('_') and '_' in self.name:
|
||||||
|
self.name = '_'.join(self.name.split('_')[:-1])
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'{self.name}_x{self.amount}'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def has_amount(self):
|
||||||
|
return 'T' in self.name or self.name == '物资'
|
||||||
|
|
||||||
|
def get_template(self):
|
||||||
|
# return self.image.crop((5, 5, 90, 68))
|
||||||
|
return self.image.crop((40, 21, 89, 70))
|
||||||
|
|
||||||
|
def get_amount(self):
|
||||||
|
return self.image.crop((60, 75, 91, 88))
|
||||||
|
|
||||||
|
|
||||||
|
class Items:
|
||||||
|
def __init__(self, timestamp):
|
||||||
|
self.timestamp = timestamp
|
||||||
|
self.get_items = Image.open(f'{GET_ITEMS_FOLDER}/{timestamp}.png').convert('RGB')
|
||||||
|
|
||||||
|
# Enemy genre
|
||||||
|
interval = np.abs(BATTLE_STATUS_TIMESTAMP - timestamp)
|
||||||
|
if np.min(interval) > STATUS_ITEMS_INTERVAL * 1000:
|
||||||
|
raise ImageError(f'Timestamp: {timestamp}, battle_status image not found.')
|
||||||
|
self.status_timestamp = BATTLE_STATUS_TIMESTAMP[np.argmin(interval)]
|
||||||
|
self.enemy = 'Default_enemy'
|
||||||
|
|
||||||
|
# get_item image properties
|
||||||
|
if INFO_BAR_1.appear_on(self.get_items):
|
||||||
|
raise ImageError(f'Timestamp: {timestamp}, Info bar')
|
||||||
|
if GET_ITEMS_1.appear_on(self.get_items):
|
||||||
|
self.row = 1
|
||||||
|
self.is_odd = self.get_is_odd(self.get_items)
|
||||||
|
self.grids = ITEM_GRIDS_1_ODD if self.is_odd else ITEM_GRIDS_1_EVEN
|
||||||
|
elif GET_ITEMS_2.appear_on(self.get_items):
|
||||||
|
self.row = 2
|
||||||
|
self.is_odd = True
|
||||||
|
self.grids = ITEM_GRIDS_2
|
||||||
|
else:
|
||||||
|
raise ImageError(f'Timestamp: {timestamp}, Image is not a get_items image.')
|
||||||
|
|
||||||
|
# Crop items
|
||||||
|
self.items = []
|
||||||
|
for button in self.grids.buttons():
|
||||||
|
item = Item(self.get_items.crop(button.area))
|
||||||
|
if item.is_valid:
|
||||||
|
self.items.append(item)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_is_odd(image):
|
||||||
|
image = image.crop((628, 294, 651, 396))
|
||||||
|
return np.mean(np.array(image.convert('L')) > 127) > 0.1
|
||||||
|
|
||||||
|
def predict(self):
|
||||||
|
self.battle_status = Image.open(f'{BATTLE_STATUS_FOLDER}/{self.status_timestamp}.png').convert('RGB')
|
||||||
|
self.enemy = ENEMY_GENRE_OCR.ocr(self.battle_status)
|
||||||
|
enemy = self.enemy
|
||||||
|
# Delete wrong OCR result
|
||||||
|
for letter in '-一个―~(':
|
||||||
|
enemy = enemy.replace(letter, '')
|
||||||
|
self.enemy = enemy
|
||||||
|
|
||||||
|
amount_items = [item for item in self.items if item.has_amount]
|
||||||
|
amount = AMOUNT_OCR.ocr([item.get_amount() for item in amount_items])
|
||||||
|
for a, i in zip(amount, amount_items):
|
||||||
|
i.amount = a
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
return [[self.timestamp, self.status_timestamp, self.enemy, item.name, item.amount] for item in self.items]
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
These code is for testing
|
||||||
|
Set your image name here
|
||||||
|
Examples: 159022xxxxxxx (int)
|
||||||
|
"""
|
||||||
|
# ts = 1590227624035
|
||||||
|
# items = Items(ts)
|
||||||
|
# for item in items.items:
|
||||||
|
# print(item.amount, item.name)
|
||||||
|
|
||||||
|
"""
|
||||||
|
These code is for template extracting
|
||||||
|
"""
|
||||||
|
# from tqdm import tqdm
|
||||||
|
# for ts in tqdm([int(f.split('.')[0]) for f in os.listdir(GET_ITEMS_FOLDER)]):
|
||||||
|
# try:
|
||||||
|
# items = Items(ts)
|
||||||
|
# except Exception:
|
||||||
|
# logger.warning(f'Error image: {ts}')
|
||||||
|
# continue
|
||||||
|
|
||||||
|
"""
|
||||||
|
These code is for final statistic
|
||||||
|
Set your csv file name here
|
||||||
|
Examples: c64.csv
|
||||||
|
"""
|
||||||
|
# csv_file = 'c64.csv'
|
||||||
|
# import csv
|
||||||
|
# from tqdm import tqdm
|
||||||
|
# with open(csv_file, 'a', newline='') as file:
|
||||||
|
# writer = csv.writer(file)
|
||||||
|
# for ts in tqdm([int(f.split('.')[0]) for f in os.listdir(GET_ITEMS_FOLDER)]):
|
||||||
|
# try:
|
||||||
|
# items = Items(ts)
|
||||||
|
# items.predict()
|
||||||
|
# writer.writerows(items.get_data())
|
||||||
|
# except Exception:
|
||||||
|
# logger.warning(f'Error image: {ts}')
|
||||||
|
# continue
|
BIN
dev_tools/item_template/主炮部件T1.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
dev_tools/item_template/主炮部件T2.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
dev_tools/item_template/主炮部件T3.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
dev_tools/item_template/物资.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
dev_tools/item_template/科技箱T1.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
dev_tools/item_template/科技箱T2.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
dev_tools/item_template/科技箱T3.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
dev_tools/item_template/科技箱T4.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
dev_tools/item_template/舰载机部件T1.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
dev_tools/item_template/舰载机部件T2.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
dev_tools/item_template/舰载机部件T3.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
dev_tools/item_template/通用部件T1.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
dev_tools/item_template/通用部件T2.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
dev_tools/item_template/通用部件T3.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
dev_tools/item_template/防空炮部件T1.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
dev_tools/item_template/防空炮部件T2.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
dev_tools/item_template/防空炮部件T3.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
dev_tools/item_template/鱼雷部件T1.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
dev_tools/item_template/鱼雷部件T2.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
dev_tools/item_template/鱼雷部件T3.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
141
doc/item_statistics_en.md
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
# How to do statistics on item drop rate
|
||||||
|
|
||||||
|
This document will show how to use `dev_tools/item_statistics.py`
|
||||||
|
|
||||||
|
## Enable statistics in alas
|
||||||
|
|
||||||
|
In alas GUI,
|
||||||
|
|
||||||
|
- set `enable_drop_screenshot` to `yes`, if enabled, alas will add a 1s sleep before screenshot, to avoid capture the flash. There's a flash light when item shows up.
|
||||||
|
- set `drop_screenshot_folder` to be the folder you want to save. It is recommended to save it in SSD.
|
||||||
|
|
||||||
|
After few hours or few days of running, you will get a folder structure like:
|
||||||
|
|
||||||
|
```
|
||||||
|
<your_folder>
|
||||||
|
campaign_7_2
|
||||||
|
get_items
|
||||||
|
158323xxxxxxx.png
|
||||||
|
158323xxxxxxx.png
|
||||||
|
158323xxxxxxx.png
|
||||||
|
get_mission
|
||||||
|
get_ship
|
||||||
|
mystery
|
||||||
|
status
|
||||||
|
campaign_10_4_HARD
|
||||||
|
get_items
|
||||||
|
get_mission
|
||||||
|
get_ship
|
||||||
|
status
|
||||||
|
d3
|
||||||
|
get_items
|
||||||
|
get_mission
|
||||||
|
get_ship
|
||||||
|
status
|
||||||
|
```
|
||||||
|
|
||||||
|
Screenshot are named after millesecond timestamp.
|
||||||
|
|
||||||
|
## Prepare a new environment
|
||||||
|
|
||||||
|
- Prepare another virtual environment, accoring to `requirements.txt`. But use the GPU version of `mxnet`.
|
||||||
|
|
||||||
|
I am using GTX1080Ti, and I installed `mxnet-cu80==1.4.1`, `CUDA8.0`, `cuDNN`. Google `mxnet gpu install`, and see how to do in details. You may intall other version of CUDA, and mxnet for that CUDA, because you are using another graphic card.
|
||||||
|
|
||||||
|
- Look for the cnocr in your virtual environment. Replace site-packages\cnocr\cn_ocr.py line 89
|
||||||
|
|
||||||
|
```
|
||||||
|
mod = mx.mod.Module(symbol=sym, context=mx.cpu(), data_names=data_names, label_names=None)
|
||||||
|
```
|
||||||
|
|
||||||
|
to be:
|
||||||
|
|
||||||
|
```
|
||||||
|
mod = mx.mod.Module(symbol=sym, context=mx.gpu(), data_names=data_names, label_names=None)
|
||||||
|
```
|
||||||
|
|
||||||
|
Now cnocr will run on GPU.
|
||||||
|
|
||||||
|
You can skip these anyway, and use the same environment as alas, but the OCR will run really slow.
|
||||||
|
|
||||||
|
- Install tqdm, a package to show progressbar.
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install tqdm
|
||||||
|
```
|
||||||
|
|
||||||
|
## Extract item_template
|
||||||
|
|
||||||
|
Copy folder `dev_tools\item_template` to the map folder such as `<your_folder>\campaign_7_2`.
|
||||||
|
|
||||||
|
Change the folder in line 24
|
||||||
|
|
||||||
|
These template are named in chinese, rename them in English.
|
||||||
|
|
||||||
|
>**How to a name template image**
|
||||||
|
>
|
||||||
|
>You should use their full name, such as "138.6mm单装炮Mle1929T3", instead of short name or nickname, such as "DD_gun".
|
||||||
|
>
|
||||||
|
>If you have same item with different image, use names like `torpedo_part.png`, `torpedo_part_2.png`, they will a classified as torpedo_part
|
||||||
|
|
||||||
|
Uncomment the part for item extract in dev_tools/item_statistics.py, and run, you will have some new item templates. Here's an example log:
|
||||||
|
|
||||||
|
```
|
||||||
|
1%| | 107/12668 [00:05<10:24, 20.10it/s]2020-06-03 10:39:42.609 | INFO | New item template: 50
|
||||||
|
1%| | 158/12668 [00:07<10:42, 19.47it/s]2020-06-03 10:39:45.098 | INFO | New item template: 51
|
||||||
|
2%|▏ | 207/12668 [00:10<10:33, 19.66it/s]2020-06-03 10:39:47.772 | INFO | New item template: 52
|
||||||
|
2%|▏ | 215/12668 [00:10<11:20, 18.29it/s]2020-06-03 10:39:48.304 | INFO | New item template: 53
|
||||||
|
100%|██████████| 12668/12668 [10:33<00:00, 19.99it/s]
|
||||||
|
```
|
||||||
|
|
||||||
|
Rename those new templates.
|
||||||
|
|
||||||
|
If you find some items haven't been extracted, try use line 140, instead of 141.
|
||||||
|
|
||||||
|
## Final statistic
|
||||||
|
|
||||||
|
Uncomment the part for final statistic, configure the csv file you wang to save.
|
||||||
|
|
||||||
|
The ocr model may not works fine in EN.
|
||||||
|
|
||||||
|
Here's an example log:
|
||||||
|
|
||||||
|
```
|
||||||
|
2020-06-03 12:23:55.355 | INFO | [ENEMY_GENRE 0.007s] 中型侦查舰队
|
||||||
|
2020-06-03 12:23:55.363 | INFO | [Amount_ocr 0.009s] [1, 1, 22]
|
||||||
|
100%|█████████▉| 14916/14919 [20:32<00:00, 13.20it/s]2020-06-03 12:23:55.442 | INFO | [ENEMY_GENRE 0.007s] 大型航空舰队
|
||||||
|
2020-06-03 12:23:55.455 | INFO | [Amount_ocr 0.013s] [1, 1, 1, 17]
|
||||||
|
2020-06-03 12:23:55.539 | INFO | [ENEMY_GENRE 0.007s] 敌方旗舰
|
||||||
|
2020-06-03 12:23:55.549 | INFO | [Amount_ocr 0.010s] [1, 2, 1, 63]
|
||||||
|
100%|█████████▉| 14918/14919 [20:33<00:00, 12.35it/s]2020-06-03 12:23:55.623 | INFO | [ENEMY_GENRE 0.007s] 精英舰队
|
||||||
|
2020-06-03 12:23:55.633 | INFO | [Amount_ocr 0.010s] [1, 1, 1, 17]
|
||||||
|
100%|██████████| 14919/14919 [20:33<00:00, 12.10it/s]
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you got a csv file, formated to be:
|
||||||
|
|
||||||
|
```
|
||||||
|
<get_item_timestamp>, <battle_status_timestamp>, <enemy_genre>, <item_name>, <item_amount>
|
||||||
|
```
|
||||||
|
|
||||||
|
like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
1590271317900,1590271315841,中型主力舰队,主炮部件T3,1
|
||||||
|
1590271317900,1590271315841,中型主力舰队,物资,23
|
||||||
|
1590271359374,1590271357251,小型侦查舰队,通用部件T1,1
|
||||||
|
1590271359374,1590271357251,小型侦查舰队,鱼雷部件T2,1
|
||||||
|
1590271359374,1590271357251,小型侦查舰队,物资,13
|
||||||
|
1590271415308,1590271413207,敌方旗舰,彗星,1
|
||||||
|
1590271415308,1590271413207,敌方旗舰,通用部件T3,1
|
||||||
|
1590271415308,1590271413207,敌方旗舰,科技箱T1,1
|
||||||
|
1590271415308,1590271413207,敌方旗舰,物资,42
|
||||||
|
1590271415308,1590271413207,敌方旗舰,_比萨研发物资,1
|
||||||
|
1590271415308,1590271413207,敌方旗舰,_鸢尾之印,1
|
||||||
|
```
|
||||||
|
|
||||||
|
You can open it in Excel or load it into database.
|
||||||
|
|
||||||
|
## Improvement
|
||||||
|
|
||||||
|
These code is running on single thread, you can try adding multiprocess to speed up. I didn't do that because it's still acceptable (20it/s without ocr, 12it/s with ocr)
|
@ -91,11 +91,14 @@ class Timer:
|
|||||||
self.limit = limit
|
self.limit = limit
|
||||||
self.count = count
|
self.count = count
|
||||||
self._current = 0
|
self._current = 0
|
||||||
self._reach_count = 0
|
self._reach_count = count
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
if not self.started():
|
if not self.started():
|
||||||
self._current = time.time()
|
self._current = time.time()
|
||||||
|
self._reach_count = 0
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
def started(self):
|
def started(self):
|
||||||
return bool(self._current)
|
return bool(self._current)
|
||||||
|
@ -229,7 +229,7 @@ def main(ini_name=''):
|
|||||||
|
|
||||||
reward_commission = reward_parser.add_argument_group('委托设置', '')
|
reward_commission = reward_parser.add_argument_group('委托设置', '')
|
||||||
reward_commission.add_argument('--启用委托收获', default=default('--启用委托收获'), choices=['是', '否'])
|
reward_commission.add_argument('--启用委托收获', default=default('--启用委托收获'), choices=['是', '否'])
|
||||||
reward_commission.add_argument('--委托时间限制', default=default('--委托时间限制'), help='忽略完成时间超过限制的委托, 格式: 23:30')
|
reward_commission.add_argument('--委托时间限制', default=default('--委托时间限制'), help='忽略完成时间超过限制的委托, 格式: 23:30, 不需要就填0')
|
||||||
|
|
||||||
priority1 = reward_commission.add_argument_group('委托耗时优先级', '')
|
priority1 = reward_commission.add_argument_group('委托耗时优先级', '')
|
||||||
priority1.add_argument('--委托耗时小于2h', default=default('--委托耗时小于2h'), help='')
|
priority1.add_argument('--委托耗时小于2h', default=default('--委托耗时小于2h'), help='')
|
||||||
|
@ -227,7 +227,7 @@ def main(ini_name=''):
|
|||||||
|
|
||||||
reward_commission = reward_parser.add_argument_group('Commission settings', '')
|
reward_commission = reward_parser.add_argument_group('Commission settings', '')
|
||||||
reward_commission.add_argument('--enable_commission_reward', default=default('--enable_commission_reward'), choices=['yes', 'no'])
|
reward_commission.add_argument('--enable_commission_reward', default=default('--enable_commission_reward'), choices=['yes', 'no'])
|
||||||
reward_commission.add_argument('--commission_time_limit', default=default('--commission_time_limit'), help='Ignore orders whose completion time exceeds the limit, Format: 23:30')
|
reward_commission.add_argument('--commission_time_limit', default=default('--commission_time_limit'), help='Ignore orders whose completion time exceeds the limit, Format: 23:30. Fill in 0 if it is not needed')
|
||||||
|
|
||||||
priority1 = reward_commission.add_argument_group('Commission priority by time duration', '')
|
priority1 = reward_commission.add_argument_group('Commission priority by time duration', '')
|
||||||
priority1.add_argument('--duration_shorter_than_2', default=default('--duration_shorter_than_2'), help='')
|
priority1.add_argument('--duration_shorter_than_2', default=default('--duration_shorter_than_2'), help='')
|
||||||
|
@ -462,7 +462,10 @@ class AzurLaneConfig:
|
|||||||
self.REWARD_INTERVAL = int(option['reward_interval'])
|
self.REWARD_INTERVAL = int(option['reward_interval'])
|
||||||
for attr in ['enable_reward', 'enable_oil_reward', 'enable_coin_reward', 'enable_mission_reward', 'enable_commission_reward', 'enable_tactical_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.__setattr__(attr.upper(), to_bool(option[attr]))
|
||||||
self.COMMISSION_TIME_LIMIT = future_time(option['commission_time_limit'])
|
if not option['commission_time_limit'].isdigit():
|
||||||
|
self.COMMISSION_TIME_LIMIT = future_time(option['commission_time_limit'])
|
||||||
|
else:
|
||||||
|
self.COMMISSION_TIME_LIMIT = 0
|
||||||
for attr in self.COMMISSION_PRIORITY.keys():
|
for attr in self.COMMISSION_PRIORITY.keys():
|
||||||
self.COMMISSION_PRIORITY[attr] = int(option[attr])
|
self.COMMISSION_PRIORITY[attr] = int(option[attr])
|
||||||
self.TACTICAL_NIGHT_RANGE = future_time_range(option['tactical_night_range'])
|
self.TACTICAL_NIGHT_RANGE = future_time_range(option['tactical_night_range'])
|
||||||
|
@ -27,11 +27,6 @@ class Equipment(InfoHandler):
|
|||||||
|
|
||||||
self.device.screenshot()
|
self.device.screenshot()
|
||||||
if SWIPE_CHECK.match(self.device.image):
|
if SWIPE_CHECK.match(self.device.image):
|
||||||
if swipe_timer.reached():
|
|
||||||
import time
|
|
||||||
from PIL import Image
|
|
||||||
self.device.image.save(f'{int(time.time() * 1000)}.png')
|
|
||||||
Image.fromarray(SWIPE_CHECK.image).save(f'{int(time.time() * 1000)}.png')
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if self.appear(check_button, offset=(30, 30)) and not SWIPE_CHECK.match(self.device.image):
|
if self.appear(check_button, offset=(30, 30)) and not SWIPE_CHECK.match(self.device.image):
|
||||||
|
@ -32,8 +32,7 @@ class ExerciseCombat(HpDaemon, OpponentChoose, ExerciseEquipment):
|
|||||||
bool: True if wins. False if quit.
|
bool: True if wins. False if quit.
|
||||||
"""
|
"""
|
||||||
logger.info('Combat execute')
|
logger.info('Combat execute')
|
||||||
self.low_hp_confirm_timer = Timer(self.config.LOW_HP_CONFIRM_WAIT, count=2)
|
self.low_hp_confirm_timer = Timer(self.config.LOW_HP_CONFIRM_WAIT, count=2).start()
|
||||||
self.low_hp_confirm_timer.start()
|
|
||||||
show_hp_timer = Timer(5)
|
show_hp_timer = Timer(5)
|
||||||
success = True
|
success = True
|
||||||
end = False
|
end = False
|
||||||
|
@ -110,8 +110,7 @@ class InfoHandler(ModuleBase):
|
|||||||
|
|
||||||
def ensure_no_story(self, skip_first_screenshot=True):
|
def ensure_no_story(self, skip_first_screenshot=True):
|
||||||
logger.info('Ensure no story')
|
logger.info('Ensure no story')
|
||||||
story_timer = Timer(5, count=4)
|
story_timer = Timer(5, count=10).start()
|
||||||
story_timer.start()
|
|
||||||
while 1:
|
while 1:
|
||||||
if skip_first_screenshot:
|
if skip_first_screenshot:
|
||||||
skip_first_screenshot = False
|
skip_first_screenshot = False
|
||||||
|
@ -9,7 +9,7 @@ class LoginHandler(Combat):
|
|||||||
def handle_app_login(self):
|
def handle_app_login(self):
|
||||||
logger.hr('App login')
|
logger.hr('App login')
|
||||||
|
|
||||||
confirm_timer = Timer(1.5, count=4)
|
confirm_timer = Timer(1.5, count=4).start()
|
||||||
while 1:
|
while 1:
|
||||||
self.device.screenshot()
|
self.device.screenshot()
|
||||||
|
|
||||||
|
@ -52,9 +52,8 @@ class Reward(RewardCommission, RewardTacticalClass):
|
|||||||
logger.hr('Reward receive')
|
logger.hr('Reward receive')
|
||||||
|
|
||||||
reward = False
|
reward = False
|
||||||
exit_timer = Timer(1, count=3)
|
exit_timer = Timer(1, count=3).start()
|
||||||
click_timer = Timer(1)
|
click_timer = Timer(1)
|
||||||
exit_timer.start()
|
|
||||||
btn = []
|
btn = []
|
||||||
if self.config.ENABLE_REWARD:
|
if self.config.ENABLE_REWARD:
|
||||||
btn.append(REWARD_3)
|
btn.append(REWARD_3)
|
||||||
|
@ -148,8 +148,6 @@ class BookGroup:
|
|||||||
|
|
||||||
|
|
||||||
class RewardTacticalClass(UI, InfoHandler):
|
class RewardTacticalClass(UI, InfoHandler):
|
||||||
tactical_animation_timer = Timer(2, count=3)
|
|
||||||
|
|
||||||
def _tactical_animation_running(self):
|
def _tactical_animation_running(self):
|
||||||
"""
|
"""
|
||||||
Returns:
|
Returns:
|
||||||
@ -208,6 +206,8 @@ class RewardTacticalClass(UI, InfoHandler):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
logger.hr('Tactical class receive')
|
logger.hr('Tactical class receive')
|
||||||
|
tactical_class_timout = Timer(10, count=10).start()
|
||||||
|
tactical_animation_timer = Timer(2, count=3).start()
|
||||||
while 1:
|
while 1:
|
||||||
if skip_first_screenshot:
|
if skip_first_screenshot:
|
||||||
skip_first_screenshot = False
|
skip_first_screenshot = False
|
||||||
@ -215,11 +215,14 @@ class RewardTacticalClass(UI, InfoHandler):
|
|||||||
self.device.screenshot()
|
self.device.screenshot()
|
||||||
|
|
||||||
if self.appear_then_click(REWARD_2, interval=1):
|
if self.appear_then_click(REWARD_2, interval=1):
|
||||||
|
tactical_class_timout.reset()
|
||||||
continue
|
continue
|
||||||
if self.handle_popup_confirm():
|
if self.handle_popup_confirm():
|
||||||
|
tactical_class_timout.reset()
|
||||||
continue
|
continue
|
||||||
if self.handle_urgent_commission(save_get_items=False):
|
if self.handle_urgent_commission(save_get_items=False):
|
||||||
# Only one button in the middle, when skill reach max level.
|
# Only one button in the middle, when skill reach max level.
|
||||||
|
tactical_class_timout.reset()
|
||||||
continue
|
continue
|
||||||
if self.appear(TACTICAL_CLASS_CANCEL, offset=(30, 30), interval=2) \
|
if self.appear(TACTICAL_CLASS_CANCEL, offset=(30, 30), interval=2) \
|
||||||
and self.appear(TACTICAL_CLASS_START, offset=(30, 30)):
|
and self.appear(TACTICAL_CLASS_START, offset=(30, 30)):
|
||||||
@ -228,17 +231,21 @@ class RewardTacticalClass(UI, InfoHandler):
|
|||||||
self._tactical_books_choose()
|
self._tactical_books_choose()
|
||||||
self.device.click(TACTICAL_CLASS_START)
|
self.device.click(TACTICAL_CLASS_START)
|
||||||
self.interval_reset(TACTICAL_CLASS_CANCEL)
|
self.interval_reset(TACTICAL_CLASS_CANCEL)
|
||||||
|
tactical_class_timout.reset()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# End
|
# End
|
||||||
if self.appear(TACTICAL_CHECK, offset=(20, 20)):
|
if self.appear(TACTICAL_CHECK, offset=(20, 20)):
|
||||||
self.ui_current = page_tactical
|
self.ui_current = page_tactical
|
||||||
if not self._tactical_animation_running():
|
if not self._tactical_animation_running():
|
||||||
if self.tactical_animation_timer.reached():
|
if tactical_animation_timer.reached():
|
||||||
logger.info('Tactical reward end.')
|
logger.info('Tactical reward end.')
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
self.tactical_animation_timer.reset()
|
tactical_animation_timer.reset()
|
||||||
|
if tactical_class_timout.reached():
|
||||||
|
logger.info('Tactical reward timeout.')
|
||||||
|
break
|
||||||
|
|
||||||
self.ui_goto(page_reward, skip_first_screenshot=True)
|
self.ui_goto(page_reward, skip_first_screenshot=True)
|
||||||
return True
|
return True
|
||||||
|