mirror of
https://gitee.com/sui-feng-cb/AzurLaneAutoScript1
synced 2026-03-09 18:39:04 +08:00
Add: 15-1 daemon
Add: plane statistics method Upd: config MoveDownTime for 15-1 daemon and battle time in plane statistics Opt: StopCondition RunCount for daemon 15-1 Opt: save screenshot in quit menu Fix: use uiautomator2 long_click and pause retry
This commit is contained in:
parent
400d55d9fb
commit
3ed07d0ba1
4
alas.py
4
alas.py
@ -418,6 +418,10 @@ class AzurLaneAutoScript:
|
||||
GemsFarming(config=self.config, device=self.device).run(
|
||||
name=self.config.Campaign_Name, folder=self.config.Campaign_Event, mode=self.config.Campaign_Mode)
|
||||
|
||||
def daemon_15_1(self):
|
||||
from module.daemon.daemon_15_1 import AzurLaneDaemon
|
||||
AzurLaneDaemon(config=self.config, device=self.device, task="Daemon_15_1").run()
|
||||
|
||||
def daemon(self):
|
||||
from module.daemon.daemon import AzurLaneDaemon
|
||||
AzurLaneDaemon(config=self.config, device=self.device, task="Daemon").run()
|
||||
|
||||
BIN
assets/cn/combat/BATTLE_TIME.png
Normal file
BIN
assets/cn/combat/BATTLE_TIME.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
BIN
assets/cn/combat/MOVE_LEFT.png
Normal file
BIN
assets/cn/combat/MOVE_LEFT.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
@ -2086,6 +2086,18 @@
|
||||
"Storage": {}
|
||||
}
|
||||
},
|
||||
"Daemon_15_1": {
|
||||
"Daemon_15_1": {
|
||||
"AutoCombat": false,
|
||||
"QuitTime": "02:00",
|
||||
"MoveDownTime": 1,
|
||||
"RunCount": 0,
|
||||
"QuitScreenshot": false
|
||||
},
|
||||
"Storage": {
|
||||
"Storage": {}
|
||||
}
|
||||
},
|
||||
"Daemon": {
|
||||
"Daemon": {
|
||||
"EnterMap": true
|
||||
|
||||
143
dev_tools/plane_statistics.py
Normal file
143
dev_tools/plane_statistics.py
Normal file
@ -0,0 +1,143 @@
|
||||
import csv
|
||||
from tqdm import tqdm
|
||||
|
||||
from module.base.button import ButtonGrid
|
||||
from module.base.decorator import cached_property, run_once
|
||||
from module.base.utils import load_image
|
||||
from module.combat.assets import BATTLE_TIME
|
||||
from module.daemon.daemon_15_1 import BattleTime as BattleTime_
|
||||
from module.logger import logger
|
||||
from module.ocr.al_ocr import AlOcr
|
||||
from module.ocr.ocr import Ocr, Digit
|
||||
from module.statistics.utils import *
|
||||
|
||||
|
||||
class BattleTime(BattleTime_):
|
||||
@staticmethod
|
||||
def parse_time(string):
|
||||
return string
|
||||
|
||||
|
||||
class PlaneOcr(Digit):
|
||||
def __init__(self, buttons, lang='azur_lane', letter=(255, 251, 247), threshold=128, alphabet='0123456789IDSBX',
|
||||
name=None):
|
||||
super().__init__(buttons, lang=lang, letter=letter, threshold=threshold, alphabet=alphabet, name=name)
|
||||
|
||||
def after_process(self, result):
|
||||
result = result.replace('X', '') if 'X' in result else '0'
|
||||
result = super().after_process(result)
|
||||
return result
|
||||
|
||||
|
||||
class PlaneStatistics:
|
||||
DROP_FOLDER = './screenshots'
|
||||
CNOCR_CONTEXT = 'cpu'
|
||||
CSV_FILE = 'drop_result.csv'
|
||||
CSV_OVERWRITE = True
|
||||
CSV_ENCODING = 'utf-8'
|
||||
PLANE_ROWS = 7
|
||||
PLANE_GRID = ButtonGrid(origin=(1230, 90), button_shape=(48, 30), grid_shape=(1, 7), delta=(0, 43))
|
||||
|
||||
def __init__(self):
|
||||
AlOcr.CNOCR_CONTEXT = PlaneStatistics.CNOCR_CONTEXT
|
||||
Ocr.SHOW_LOG = False
|
||||
self.PLANE_GRID.grid_shape = (1, self.PLANE_ROWS)
|
||||
self.place_ocr_model = PlaneOcr(self.PLANE_GRID.buttons)
|
||||
self.time_ocr_model = BattleTime(BATTLE_TIME)
|
||||
|
||||
@property
|
||||
def csv_file(self):
|
||||
return os.path.join(PlaneStatistics.DROP_FOLDER, PlaneStatistics.CSV_FILE)
|
||||
|
||||
@staticmethod
|
||||
def drop_folder(campaign):
|
||||
return os.path.join(PlaneStatistics.DROP_FOLDER, campaign)
|
||||
|
||||
@cached_property
|
||||
def csv_overwrite_check(self):
|
||||
"""
|
||||
Remove existing csv file. This method only run once.
|
||||
"""
|
||||
if PlaneStatistics.CSV_OVERWRITE:
|
||||
if os.path.exists(self.csv_file):
|
||||
logger.info(f'Remove existing csv file: {self.csv_file}')
|
||||
os.remove(self.csv_file)
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
@run_once
|
||||
def csv_write_column_name(writer, columns):
|
||||
writer.writerows([columns])
|
||||
|
||||
def parse_plane(self, file):
|
||||
ts = os.path.splitext(os.path.basename(file))[0]
|
||||
campaign = os.path.basename(os.path.abspath(os.path.join(file, '../')))
|
||||
campaign = campaign.replace('campaign_', '')
|
||||
images = unpack(load_image(file))
|
||||
image = images[0]
|
||||
plane = self.place_ocr_model.ocr(image)
|
||||
time = self.time_ocr_model.ocr(image)
|
||||
yield ['\'' + ts, campaign, str(plane), sum(plane), time]
|
||||
|
||||
def extract_plane(self, campaign):
|
||||
"""
|
||||
Extract images from a given folder.
|
||||
|
||||
Args:
|
||||
campaign (str):
|
||||
"""
|
||||
print('')
|
||||
logger.hr(f'extract plane statistics from {campaign}', level=1)
|
||||
_ = self.csv_overwrite_check
|
||||
|
||||
with open(self.csv_file, 'a', newline='', encoding=PlaneStatistics.CSV_ENCODING) as csv_file:
|
||||
writer = csv.writer(csv_file)
|
||||
self.csv_write_column_name(writer, ['截图文件名称', '关卡名称', 'ocr识别结果', '飞机总数', '右上角时间'])
|
||||
for ts, file in tqdm(load_folder(self.drop_folder(campaign)).items()):
|
||||
try:
|
||||
rows = self.parse_plane(file)
|
||||
writer.writerows(rows)
|
||||
except ImageError as e:
|
||||
logger.warning(e)
|
||||
continue
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
logger.warning(f'Error on image {ts}')
|
||||
continue
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Drop screenshot folder. Default to './screenshots'
|
||||
# 截图文件夹名称
|
||||
PlaneStatistics.DROP_FOLDER = './screenshots'
|
||||
# 'cpu' or 'gpu', default to 'cpu'.
|
||||
# Use 'gpu' for faster prediction, but you must have the gpu version of mxnet installed.
|
||||
PlaneStatistics.CNOCR_CONTEXT = 'cpu'
|
||||
# Name of the output csv file.
|
||||
# This will write to {DROP_FOLDER}/{CSV_FILE}.
|
||||
# 结果文件名称
|
||||
PlaneStatistics.CSV_FILE = 'plane_result.csv'
|
||||
# If True, remove existing file before extraction.
|
||||
# 是否覆写csv
|
||||
PlaneStatistics.CSV_OVERWRITE = True
|
||||
# Usually to be 'utf-8'.
|
||||
# For better Chinese export to Excel, use 'gbk'.
|
||||
PlaneStatistics.CSV_ENCODING = 'gbk'
|
||||
# Default to be 7.
|
||||
# This will change the ocr of rows of plane.
|
||||
# 飞机识别行数(最多有几行飞机)
|
||||
PlaneStatistics.PLANE_ROWS = 9
|
||||
# campaign names to export under DROP_FOLDER.
|
||||
# This will load {DROP_FOLDER}/{CAMPAIGN}.
|
||||
# Just a demonstration here, you should modify it to your own.
|
||||
# 关卡名称(截图文件夹内的需要识别图片的文件夹的名称)
|
||||
CAMPAIGNS = ['campaign_15_1']
|
||||
|
||||
stat = PlaneStatistics()
|
||||
|
||||
"""
|
||||
Step 1:
|
||||
Run this code.
|
||||
"""
|
||||
for i in CAMPAIGNS:
|
||||
stat.extract_plane(i)
|
||||
@ -17,6 +17,7 @@ BATTLE_STATUS_B = Button(area={'cn': (625, 297, 712, 317), 'en': (625, 297, 712,
|
||||
BATTLE_STATUS_C = Button(area={'cn': (625, 211, 647, 297), 'en': (625, 211, 647, 297), 'jp': (625, 211, 647, 297), 'tw': (625, 211, 647, 297)}, color={'cn': (199, 208, 198), 'en': (199, 208, 198), 'jp': (199, 208, 198), 'tw': (199, 208, 198)}, button={'cn': (1000, 631, 1055, 689), 'en': (1000, 631, 1055, 689), 'jp': (1000, 631, 1055, 689), 'tw': (1000, 631, 1055, 689)}, file={'cn': './assets/cn/combat/BATTLE_STATUS_C.png', 'en': './assets/en/combat/BATTLE_STATUS_C.png', 'jp': './assets/jp/combat/BATTLE_STATUS_C.png', 'tw': './assets/tw/combat/BATTLE_STATUS_C.png'})
|
||||
BATTLE_STATUS_D = Button(area={'cn': (618, 191, 639, 317), 'en': (618, 191, 639, 317), 'jp': (618, 191, 639, 317), 'tw': (618, 191, 639, 317)}, color={'cn': (199, 208, 199), 'en': (199, 208, 199), 'jp': (199, 208, 199), 'tw': (199, 208, 199)}, button={'cn': (1000, 631, 1055, 689), 'en': (1000, 631, 1055, 689), 'jp': (1000, 631, 1055, 689), 'tw': (1000, 631, 1055, 689)}, file={'cn': './assets/cn/combat/BATTLE_STATUS_D.png', 'en': './assets/en/combat/BATTLE_STATUS_D.png', 'jp': './assets/jp/combat/BATTLE_STATUS_D.png', 'tw': './assets/tw/combat/BATTLE_STATUS_D.png'})
|
||||
BATTLE_STATUS_S = Button(area={'cn': (643, 297, 722, 317), 'en': (643, 297, 722, 317), 'jp': (643, 297, 722, 317), 'tw': (643, 297, 722, 317)}, color={'cn': (233, 242, 127), 'en': (233, 242, 127), 'jp': (233, 242, 127), 'tw': (233, 242, 127)}, button={'cn': (1000, 631, 1055, 689), 'en': (999, 630, 1047, 691), 'jp': (1000, 631, 1055, 689), 'tw': (1000, 631, 1055, 689)}, file={'cn': './assets/cn/combat/BATTLE_STATUS_S.png', 'en': './assets/en/combat/BATTLE_STATUS_S.png', 'jp': './assets/jp/combat/BATTLE_STATUS_S.png', 'tw': './assets/tw/combat/BATTLE_STATUS_S.png'})
|
||||
BATTLE_TIME = Button(area={'cn': (1062, 36, 1119, 59), 'en': (1062, 36, 1119, 59), 'jp': (1062, 36, 1119, 59), 'tw': (1062, 36, 1119, 59)}, color={'cn': (98, 158, 99), 'en': (98, 158, 99), 'jp': (98, 158, 99), 'tw': (98, 158, 99)}, button={'cn': (1062, 36, 1119, 59), 'en': (1062, 36, 1119, 59), 'jp': (1062, 36, 1119, 59), 'tw': (1062, 36, 1119, 59)}, file={'cn': './assets/cn/combat/BATTLE_TIME.png', 'en': './assets/cn/combat/BATTLE_TIME.png', 'jp': './assets/cn/combat/BATTLE_TIME.png', 'tw': './assets/cn/combat/BATTLE_TIME.png'})
|
||||
COMBAT_AUTO = Button(area={'cn': (136, 573, 167, 604), 'en': (136, 573, 167, 604), 'jp': (136, 573, 167, 604), 'tw': (136, 573, 167, 604)}, color={'cn': (229, 242, 255), 'en': (229, 242, 255), 'jp': (229, 242, 255), 'tw': (229, 242, 255)}, button={'cn': (136, 573, 167, 604), 'en': (136, 573, 167, 604), 'jp': (136, 573, 167, 604), 'tw': (136, 573, 167, 604)}, file={'cn': './assets/cn/combat/COMBAT_AUTO.png', 'en': './assets/en/combat/COMBAT_AUTO.png', 'jp': './assets/jp/combat/COMBAT_AUTO.png', 'tw': './assets/tw/combat/COMBAT_AUTO.png'})
|
||||
COMBAT_AUTO_133 = Button(area={'cn': (131, 568, 170, 609), 'en': (131, 568, 170, 609), 'jp': (131, 568, 170, 609), 'tw': (131, 568, 170, 609)}, color={'cn': (234, 244, 255), 'en': (234, 244, 255), 'jp': (234, 244, 255), 'tw': (234, 244, 255)}, button={'cn': (131, 568, 170, 609), 'en': (131, 568, 170, 609), 'jp': (131, 568, 170, 609), 'tw': (131, 568, 170, 609)}, file={'cn': './assets/cn/combat/COMBAT_AUTO_133.png', 'en': './assets/en/combat/COMBAT_AUTO_133.png', 'jp': './assets/jp/combat/COMBAT_AUTO_133.png', 'tw': './assets/tw/combat/COMBAT_AUTO_133.png'})
|
||||
COMBAT_AUTO_150 = Button(area={'cn': (129, 567, 172, 611), 'en': (129, 567, 172, 611), 'jp': (129, 567, 172, 611), 'tw': (129, 567, 172, 611)}, color={'cn': (238, 247, 255), 'en': (238, 247, 255), 'jp': (238, 247, 255), 'tw': (238, 247, 255)}, button={'cn': (129, 567, 172, 611), 'en': (129, 567, 172, 611), 'jp': (129, 567, 172, 611), 'tw': (129, 567, 172, 611)}, file={'cn': './assets/cn/combat/COMBAT_AUTO_150.png', 'en': './assets/en/combat/COMBAT_AUTO_150.png', 'jp': './assets/jp/combat/COMBAT_AUTO_150.png', 'tw': './assets/tw/combat/COMBAT_AUTO_150.png'})
|
||||
@ -38,6 +39,7 @@ GET_SHIP = Button(area={'cn': (1104, 610, 1110, 630), 'en': (1104, 610, 1110, 63
|
||||
LOADING_BAR = Button(area={'cn': (33, 676, 1247, 680), 'en': (33, 676, 1247, 680), 'jp': (33, 676, 1247, 680), 'tw': (33, 676, 1247, 680)}, color={'cn': (172, 205, 232), 'en': (172, 205, 232), 'jp': (172, 205, 232), 'tw': (172, 205, 232)}, button={'cn': (33, 676, 1247, 680), 'en': (33, 676, 1247, 680), 'jp': (33, 676, 1247, 680), 'tw': (33, 676, 1247, 680)}, file={'cn': './assets/cn/combat/LOADING_BAR.png', 'en': './assets/en/combat/LOADING_BAR.png', 'jp': './assets/jp/combat/LOADING_BAR.png', 'tw': './assets/tw/combat/LOADING_BAR.png'})
|
||||
MAIN_FLEET_POWER_ZERO = Button(area={'cn': (131, 151, 232, 206), 'en': (131, 151, 232, 206), 'jp': (131, 151, 232, 206), 'tw': (131, 151, 232, 206)}, color={'cn': (63, 79, 98), 'en': (63, 79, 98), 'jp': (63, 79, 98), 'tw': (63, 79, 98)}, button={'cn': (131, 151, 232, 206), 'en': (131, 151, 232, 206), 'jp': (131, 151, 232, 206), 'tw': (131, 151, 232, 206)}, file={'cn': './assets/cn/combat/MAIN_FLEET_POWER_ZERO.png', 'en': './assets/en/combat/MAIN_FLEET_POWER_ZERO.png', 'jp': './assets/jp/combat/MAIN_FLEET_POWER_ZERO.png', 'tw': './assets/tw/combat/MAIN_FLEET_POWER_ZERO.png'})
|
||||
MOVE_DOWN = Button(area={'cn': (148, 647, 155, 669), 'en': (148, 647, 155, 669), 'jp': (148, 647, 155, 669), 'tw': (148, 647, 155, 669)}, color={'cn': (21, 28, 57), 'en': (21, 28, 57), 'jp': (21, 28, 57), 'tw': (21, 28, 57)}, button={'cn': (148, 647, 155, 669), 'en': (148, 647, 155, 669), 'jp': (148, 647, 155, 669), 'tw': (148, 647, 155, 669)}, file={'cn': './assets/cn/combat/MOVE_DOWN.png', 'en': './assets/en/combat/MOVE_DOWN.png', 'jp': './assets/jp/combat/MOVE_DOWN.png', 'tw': './assets/tw/combat/MOVE_DOWN.png'})
|
||||
MOVE_LEFT = Button(area={'cn': (3, 585, 27, 591), 'en': (3, 585, 27, 591), 'jp': (3, 585, 27, 591), 'tw': (3, 585, 27, 591)}, color={'cn': (35, 108, 156), 'en': (35, 108, 156), 'jp': (35, 108, 156), 'tw': (35, 108, 156)}, button={'cn': (3, 585, 27, 591), 'en': (3, 585, 27, 591), 'jp': (3, 585, 27, 591), 'tw': (3, 585, 27, 591)}, file={'cn': './assets/cn/combat/MOVE_LEFT.png', 'en': './assets/cn/combat/MOVE_LEFT.png', 'jp': './assets/cn/combat/MOVE_LEFT.png', 'tw': './assets/cn/combat/MOVE_LEFT.png'})
|
||||
MOVE_LEFT_DOWN = Button(area={'cn': (67, 668, 112, 707), 'en': (67, 668, 112, 707), 'jp': (67, 668, 112, 707), 'tw': (67, 668, 112, 707)}, color={'cn': (65, 80, 100), 'en': (65, 80, 100), 'jp': (65, 80, 100), 'tw': (65, 80, 100)}, button={'cn': (67, 668, 112, 707), 'en': (67, 668, 112, 707), 'jp': (67, 668, 112, 707), 'tw': (67, 668, 112, 707)}, file={'cn': './assets/cn/combat/MOVE_LEFT_DOWN.png', 'en': './assets/en/combat/MOVE_LEFT_DOWN.png', 'jp': './assets/jp/combat/MOVE_LEFT_DOWN.png', 'tw': './assets/tw/combat/MOVE_LEFT_DOWN.png'})
|
||||
NEW_SHIP = Button(area={'cn': (206, 87, 213, 93), 'en': (206, 87, 213, 93), 'jp': (206, 87, 213, 93), 'tw': (206, 87, 213, 93)}, color={'cn': (235, 171, 60), 'en': (235, 171, 60), 'jp': (235, 171, 60), 'tw': (235, 171, 60)}, button={'cn': (206, 87, 213, 93), 'en': (206, 87, 213, 93), 'jp': (206, 87, 213, 93), 'tw': (206, 87, 213, 93)}, file={'cn': './assets/cn/combat/NEW_SHIP.png', 'en': './assets/en/combat/NEW_SHIP.png', 'jp': './assets/jp/combat/NEW_SHIP.png', 'tw': './assets/tw/combat/NEW_SHIP.png'})
|
||||
OPTS_INFO_D = Button(area={'cn': (601, 151, 704, 178), 'en': (565, 143, 692, 179), 'jp': (512, 154, 605, 176), 'tw': (602, 152, 702, 177)}, color={'cn': (158, 110, 113), 'en': (171, 116, 110), 'jp': (201, 187, 191), 'tw': (164, 130, 137)}, button={'cn': (583, 605, 677, 628), 'en': (590, 587, 627, 647), 'jp': (574, 596, 685, 635), 'tw': (583, 604, 676, 627)}, file={'cn': './assets/cn/combat/OPTS_INFO_D.png', 'en': './assets/en/combat/OPTS_INFO_D.png', 'jp': './assets/jp/combat/OPTS_INFO_D.png', 'tw': './assets/tw/combat/OPTS_INFO_D.png'})
|
||||
|
||||
@ -9982,6 +9982,38 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Daemon_15_1": {
|
||||
"Daemon_15_1": {
|
||||
"AutoCombat": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
},
|
||||
"QuitTime": {
|
||||
"type": "input",
|
||||
"value": "02:00"
|
||||
},
|
||||
"MoveDownTime": {
|
||||
"type": "input",
|
||||
"value": 1
|
||||
},
|
||||
"RunCount": {
|
||||
"type": "input",
|
||||
"value": 0
|
||||
},
|
||||
"QuitScreenshot": {
|
||||
"type": "checkbox",
|
||||
"value": false
|
||||
}
|
||||
},
|
||||
"Storage": {
|
||||
"Storage": {
|
||||
"type": "storage",
|
||||
"value": {},
|
||||
"valuetype": "ignore",
|
||||
"display": "disabled"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Daemon": {
|
||||
"Daemon": {
|
||||
"EnterMap": {
|
||||
|
||||
@ -779,6 +779,12 @@ OpsiHazard1Leveling:
|
||||
|
||||
# ==================== Tools ====================
|
||||
|
||||
Daemon_15_1:
|
||||
AutoCombat: false
|
||||
QuitTime: 02:00
|
||||
MoveDownTime: 1
|
||||
RunCount: 0
|
||||
QuitScreenshot: false
|
||||
Daemon:
|
||||
EnterMap: true
|
||||
OpsiDaemon:
|
||||
|
||||
@ -101,6 +101,7 @@
|
||||
"menu": "collapse",
|
||||
"page": "tool",
|
||||
"tasks": [
|
||||
"Daemon_15_1",
|
||||
"Daemon",
|
||||
"OpsiDaemon",
|
||||
"EventStory",
|
||||
|
||||
@ -338,6 +338,8 @@ Tool:
|
||||
menu: 'collapse'
|
||||
page: 'tool'
|
||||
tasks:
|
||||
Daemon_15_1:
|
||||
- Daemon_15_1
|
||||
Daemon:
|
||||
- Daemon
|
||||
OpsiDaemon:
|
||||
|
||||
@ -460,6 +460,13 @@ class GeneratedConfig:
|
||||
OpsiHazard1Leveling_OperationCoinsPreserve = 100000
|
||||
OpsiHazard1Leveling_DoScanningDevice = False
|
||||
|
||||
# Group `Daemon_15_1`
|
||||
Daemon_15_1_AutoCombat = False
|
||||
Daemon_15_1_QuitTime = '02:00'
|
||||
Daemon_15_1_MoveDownTime = 1
|
||||
Daemon_15_1_RunCount = 0
|
||||
Daemon_15_1_QuitScreenshot = False
|
||||
|
||||
# Group `Daemon`
|
||||
Daemon_EnterMap = True
|
||||
|
||||
|
||||
@ -258,6 +258,10 @@
|
||||
"name": "Cross Month Daily",
|
||||
"help": " ALAS will enter OpSi 10min before OpSi reset, wait until OpSi reset but not exit OpSi. Then do the daily, obscure, abyssal and meowfficer farming to get extra gold plates. When running dailies, settings in task \"OpSiDaily\" are used, the rest function are the same.\n IMPORTANT: Please do not touch the game while ALAS is waiting for OpSi reset."
|
||||
},
|
||||
"Daemon_15_1": {
|
||||
"name": "Task.Daemon_15_1.name",
|
||||
"help": "Task.Daemon_15_1.help"
|
||||
},
|
||||
"Daemon": {
|
||||
"name": "Normal Semi-auto",
|
||||
"help": ""
|
||||
@ -2656,6 +2660,32 @@
|
||||
"help": "Exchange purple coins to operation coins, which may cause a shortage of purple coins"
|
||||
}
|
||||
},
|
||||
"Daemon_15_1": {
|
||||
"_info": {
|
||||
"name": "Daemon_15_1._info.name",
|
||||
"help": "Daemon_15_1._info.help"
|
||||
},
|
||||
"AutoCombat": {
|
||||
"name": "Daemon_15_1.AutoCombat.name",
|
||||
"help": "Daemon_15_1.AutoCombat.help"
|
||||
},
|
||||
"QuitTime": {
|
||||
"name": "Daemon_15_1.QuitTime.name",
|
||||
"help": "Daemon_15_1.QuitTime.help"
|
||||
},
|
||||
"MoveDownTime": {
|
||||
"name": "Daemon_15_1.MoveDownTime.name",
|
||||
"help": "Daemon_15_1.MoveDownTime.help"
|
||||
},
|
||||
"RunCount": {
|
||||
"name": "Daemon_15_1.RunCount.name",
|
||||
"help": "Daemon_15_1.RunCount.help"
|
||||
},
|
||||
"QuitScreenshot": {
|
||||
"name": "Daemon_15_1.QuitScreenshot.name",
|
||||
"help": "Daemon_15_1.QuitScreenshot.help"
|
||||
}
|
||||
},
|
||||
"Daemon": {
|
||||
"_info": {
|
||||
"name": "Semi-auto Clicking",
|
||||
|
||||
@ -258,6 +258,10 @@
|
||||
"name": "Cross Month Daily",
|
||||
"help": " ALAS will enter OpSi 10min before OpSi reset, wait until OpSi reset but not exit OpSi. Then do the daily, obscure, abyssal and meowfficer farming to get extra gold plates. When running dailies, settings in task \"OpSiDaily\" are used, the rest function are the same.\n IMPORTANT: Please do not touch the game while ALAS is waiting for OpSi reset."
|
||||
},
|
||||
"Daemon_15_1": {
|
||||
"name": "Task.Daemon_15_1.name",
|
||||
"help": "Task.Daemon_15_1.help"
|
||||
},
|
||||
"Daemon": {
|
||||
"name": "半自動クリック",
|
||||
"help": ""
|
||||
@ -2656,6 +2660,32 @@
|
||||
"help": "OpsiHazard1Leveling.DoScanningDevice.help"
|
||||
}
|
||||
},
|
||||
"Daemon_15_1": {
|
||||
"_info": {
|
||||
"name": "Daemon_15_1._info.name",
|
||||
"help": "Daemon_15_1._info.help"
|
||||
},
|
||||
"AutoCombat": {
|
||||
"name": "Daemon_15_1.AutoCombat.name",
|
||||
"help": "Daemon_15_1.AutoCombat.help"
|
||||
},
|
||||
"QuitTime": {
|
||||
"name": "Daemon_15_1.QuitTime.name",
|
||||
"help": "Daemon_15_1.QuitTime.help"
|
||||
},
|
||||
"MoveDownTime": {
|
||||
"name": "Daemon_15_1.MoveDownTime.name",
|
||||
"help": "Daemon_15_1.MoveDownTime.help"
|
||||
},
|
||||
"RunCount": {
|
||||
"name": "Daemon_15_1.RunCount.name",
|
||||
"help": "Daemon_15_1.RunCount.help"
|
||||
},
|
||||
"QuitScreenshot": {
|
||||
"name": "Daemon_15_1.QuitScreenshot.name",
|
||||
"help": "Daemon_15_1.QuitScreenshot.help"
|
||||
}
|
||||
},
|
||||
"Daemon": {
|
||||
"_info": {
|
||||
"name": "Daemon._info.name",
|
||||
|
||||
@ -258,6 +258,10 @@
|
||||
"name": "跨月每日",
|
||||
"help": " Alas将在大世界跨月重置之前10分钟进入大世界,等待大世界重置但不退出大世界,然后完成新一天的大世界每日、隐秘海域、深渊海域和短猫相接,以获得额外的金菜。运行大世界每日时,按\"大世界每日\"任务设置运行,其余同理。\n 重要:Alas等待跨月期间,请不要操作游戏。"
|
||||
},
|
||||
"Daemon_15_1": {
|
||||
"name": "15-1测试",
|
||||
"help": ""
|
||||
},
|
||||
"Daemon": {
|
||||
"name": "半自动点击",
|
||||
"help": ""
|
||||
@ -2656,6 +2660,32 @@
|
||||
"help": "消耗紫币换取黄币,可能会导致紫币不足"
|
||||
}
|
||||
},
|
||||
"Daemon_15_1": {
|
||||
"_info": {
|
||||
"name": "15-1支援舰队测试用",
|
||||
"help": "配好队,关闭自律,进图后关闭阵容锁定,手动选择一个需要测试的敌人,然后运行该任务\n注意:只能使用经典战斗ui(老战斗ui)!!!\n使用结束后需要手动关闭此任务"
|
||||
},
|
||||
"AutoCombat": {
|
||||
"name": "使用自律战斗",
|
||||
"help": ""
|
||||
},
|
||||
"QuitTime": {
|
||||
"name": "截图并退出关卡的时间",
|
||||
"help": "如2:00, 02:30等"
|
||||
},
|
||||
"MoveDownTime": {
|
||||
"name": "非自律开局向下拉的时间(秒)",
|
||||
"help": "在非自律战斗下,开局按向下按键的时长(秒)\n根据前排航速的不同,手动修改这个值确保前排在旗舰头像下方,例如:\n1s:埃吉尔 岛风 安克雷奇\n1.5s:埃吉尔 圣女贞德 安克雷奇"
|
||||
},
|
||||
"RunCount": {
|
||||
"name": "测试次数大于 X 后停止",
|
||||
"help": "每运行一次,次数减一,归零后停止任务\n0 表示不限制次数"
|
||||
},
|
||||
"QuitScreenshot": {
|
||||
"name": "保存退出界面截图",
|
||||
"help": "开启后,会额外保存一份退出界面时的截图,以供测试使用\n大部分情况不需要开启这个选项"
|
||||
}
|
||||
},
|
||||
"Daemon": {
|
||||
"_info": {
|
||||
"name": "半自动点击",
|
||||
|
||||
@ -258,6 +258,10 @@
|
||||
"name": "跨月每日",
|
||||
"help": " Alas將在大世界跨月重置之前10分鐘進入大世界,等待大世界重置但不退出大世界,然後完成新一天的大世界每日、隱秘海域、深淵海域和短貓相接,以獲得額外的金菜。運行大世界每日時,按\"大世界每日\"任務設定運行,其餘同理。\n 重要:Alas等待跨月期間,請不要操作遊戲。"
|
||||
},
|
||||
"Daemon_15_1": {
|
||||
"name": "Task.Daemon_15_1.name",
|
||||
"help": "Task.Daemon_15_1.help"
|
||||
},
|
||||
"Daemon": {
|
||||
"name": "半自動點擊",
|
||||
"help": ""
|
||||
@ -2656,6 +2660,32 @@
|
||||
"help": "消耗紫幣換取黃幣,可能會導致紫幣不足"
|
||||
}
|
||||
},
|
||||
"Daemon_15_1": {
|
||||
"_info": {
|
||||
"name": "Daemon_15_1._info.name",
|
||||
"help": "Daemon_15_1._info.help"
|
||||
},
|
||||
"AutoCombat": {
|
||||
"name": "Daemon_15_1.AutoCombat.name",
|
||||
"help": "Daemon_15_1.AutoCombat.help"
|
||||
},
|
||||
"QuitTime": {
|
||||
"name": "Daemon_15_1.QuitTime.name",
|
||||
"help": "Daemon_15_1.QuitTime.help"
|
||||
},
|
||||
"MoveDownTime": {
|
||||
"name": "Daemon_15_1.MoveDownTime.name",
|
||||
"help": "Daemon_15_1.MoveDownTime.help"
|
||||
},
|
||||
"RunCount": {
|
||||
"name": "Daemon_15_1.RunCount.name",
|
||||
"help": "Daemon_15_1.RunCount.help"
|
||||
},
|
||||
"QuitScreenshot": {
|
||||
"name": "Daemon_15_1.QuitScreenshot.name",
|
||||
"help": "Daemon_15_1.QuitScreenshot.help"
|
||||
}
|
||||
},
|
||||
"Daemon": {
|
||||
"_info": {
|
||||
"name": "半自動點擊",
|
||||
|
||||
157
module/daemon/daemon_15_1.py
Normal file
157
module/daemon/daemon_15_1.py
Normal file
@ -0,0 +1,157 @@
|
||||
from datetime import datetime, timedelta
|
||||
import re
|
||||
|
||||
|
||||
from module.base.timer import Timer
|
||||
from module.base.utils import copy_image
|
||||
from module.campaign.campaign_base import CampaignBase
|
||||
from module.combat.assets import BATTLE_TIME, MOVE_DOWN, MOVE_LEFT
|
||||
from module.combat_ui.assets import QUIT
|
||||
from module.daemon.daemon_base import DaemonBase
|
||||
from module.exception import CampaignEnd
|
||||
from module.exercise.assets import QUIT_RECONFIRM
|
||||
from module.handler.ambush import MAP_AMBUSH_EVADE
|
||||
from module.logger import logger
|
||||
from module.map.assets import MAP_OFFENSIVE
|
||||
from module.ocr.ocr import Duration
|
||||
|
||||
|
||||
class BattleTime(Duration):
|
||||
SHOW_LOG = False
|
||||
|
||||
def __init__(self, buttons, lang='azur_lane', letter=(148, 255, 99), threshold=128, alphabet='0123456789:IDSB',
|
||||
name=None):
|
||||
super().__init__(buttons, lang=lang, letter=letter, threshold=threshold, alphabet=alphabet, name=name)
|
||||
|
||||
@staticmethod
|
||||
def parse_time(string):
|
||||
result = re.search(r'(\d{1,2}):?(\d{2})', string)
|
||||
if result:
|
||||
result = [int(s) for s in result.groups()]
|
||||
return timedelta(hours=0, minutes=result[0], seconds=result[1])
|
||||
else:
|
||||
logger.warning(f'Invalid duration: {string}')
|
||||
return timedelta(hours=0, minutes=0, seconds=0)
|
||||
|
||||
|
||||
class AzurLaneDaemon(DaemonBase, CampaignBase):
|
||||
battle_time_ocr_model = BattleTime(BATTLE_TIME)
|
||||
|
||||
@property
|
||||
def battle_time(self):
|
||||
return self.battle_time_ocr_model.ocr(self.device.image).total_seconds()
|
||||
|
||||
@property
|
||||
def quit_time(self):
|
||||
string = self.config.Daemon_15_1_QuitTime
|
||||
string = string.strip().replace(':', ':')
|
||||
t = datetime.strptime(string, "%M:%S")
|
||||
return timedelta(hours=t.hour, minutes=t.minute, seconds=t.second).total_seconds()
|
||||
|
||||
def run(self):
|
||||
move = True
|
||||
is_limit = False
|
||||
end = False
|
||||
self.device.screenshot_interval_set()
|
||||
self.config.override(Emulator_ControlMethod='uiautomator2')
|
||||
while 1:
|
||||
self.device.screenshot()
|
||||
|
||||
# End
|
||||
if is_limit and self.config.Daemon_15_1_RunCount <= 0:
|
||||
logger.hr('Triggered stop condition: Run count')
|
||||
self.config.Daemon_15_1_RunCount = 0
|
||||
end = True
|
||||
is_limit = self.config.Daemon_15_1_RunCount
|
||||
pause = self.is_combat_executing()
|
||||
# running a combat
|
||||
if pause:
|
||||
if not self.config.Daemon_15_1_AutoCombat and move:
|
||||
move = False
|
||||
self.device.long_click(MOVE_DOWN, duration=self.config.Daemon_15_1_MoveDownTime)
|
||||
self.device.long_click(MOVE_LEFT, duration=(3, 4))
|
||||
continue
|
||||
|
||||
# End
|
||||
battle_time = self.battle_time
|
||||
if battle_time and battle_time <= self.quit_time:
|
||||
with self.stat.new(genre='campaign_15_1', method='save') as record:
|
||||
if self.config.Daemon_15_1_RunCount:
|
||||
self.config.Daemon_15_1_RunCount -= 1
|
||||
combat_image = copy_image(self.device.image)
|
||||
|
||||
self.device.screenshot_interval_set()
|
||||
skip_first_screenshot = True
|
||||
pause_interval = Timer(0.5, count=1)
|
||||
while 1:
|
||||
if skip_first_screenshot:
|
||||
skip_first_screenshot = False
|
||||
else:
|
||||
self.device.screenshot()
|
||||
|
||||
if pause_interval.reached():
|
||||
pause = self.is_combat_executing()
|
||||
if pause:
|
||||
self.device.click(pause)
|
||||
pause_interval.reset()
|
||||
continue
|
||||
|
||||
if QUIT.match_luma(self.device.image, offset=(20, 20)):
|
||||
record.add(combat_image)
|
||||
if self.config.Daemon_15_1_QuitScreenshot:
|
||||
record.add(self.device.image)
|
||||
break
|
||||
continue
|
||||
|
||||
# Quit
|
||||
if self.handle_combat_quit():
|
||||
continue
|
||||
if self.appear_then_click(QUIT_RECONFIRM, offset=(20, 20), interval=5):
|
||||
move = True
|
||||
if end:
|
||||
break
|
||||
continue
|
||||
|
||||
# Combat
|
||||
if self.combat_appear():
|
||||
self.combat_preparation(auto='combat_auto' if self.config.Daemon_15_1_AutoCombat else '')
|
||||
try:
|
||||
if self.handle_battle_status():
|
||||
self.combat_status(expected_end='no_searching')
|
||||
continue
|
||||
except CampaignEnd:
|
||||
continue
|
||||
|
||||
# Map operation
|
||||
if self.appear_then_click(MAP_AMBUSH_EVADE, offset=(20, 20)):
|
||||
self.device.sleep(1)
|
||||
continue
|
||||
if self.handle_mystery_items():
|
||||
continue
|
||||
|
||||
# Retire
|
||||
if self.handle_retirement():
|
||||
continue
|
||||
|
||||
# Emotion
|
||||
pass
|
||||
|
||||
# Urgent commission
|
||||
if self.handle_urgent_commission():
|
||||
continue
|
||||
|
||||
# Popups
|
||||
if self.handle_guild_popup_cancel():
|
||||
return True
|
||||
if self.handle_vote_popup():
|
||||
continue
|
||||
|
||||
# Story
|
||||
if self.story_skip():
|
||||
continue
|
||||
|
||||
# Map Offensive
|
||||
if not end and self.appear_then_click(MAP_OFFENSIVE, interval=2):
|
||||
continue
|
||||
|
||||
return True
|
||||
@ -15,6 +15,7 @@ MOD_CONFIG_DICT = {}
|
||||
|
||||
def get_available_func():
|
||||
return (
|
||||
'Daemon_15_1',
|
||||
'Daemon',
|
||||
'OpsiDaemon',
|
||||
'EventStory',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user