Add: Semi-auto, Opsi semi-auto and benchmark

- Add: dependency prettytable
- Fix: Running an Alas module directly
- Del: Remove all assets in daemon module, because it's redundant
This commit is contained in:
LmeSzinc 2021-10-25 18:49:56 +08:00
parent 3249190d61
commit dd7b389797
60 changed files with 481 additions and 70 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

View File

@ -1212,5 +1212,21 @@
"ActionPointPreserve": 500,
"HazardLevel": 5
}
},
"Daemon": {
"Daemon": {
"EnterMap": true
}
},
"OpsiDaemon": {
"OpsiDaemon": {
"RepairShip": true
}
},
"Benchmark": {
"Benchmark": {
"TestScreenshotMethod": true,
"TestClickMethod": true
}
}
}

View File

@ -14,14 +14,15 @@ class ModuleBase:
device: Device
stat: AzurStats
def __init__(self, config, device=None):
def __init__(self, config, device=None, task=None):
"""
Args:
config (AzurLaneConfig, str):
device (Device):
config (AzurLaneConfig, str): Name of the user config under ./config
device (Device): To reuse a device. If None, create a new Device object.
task (str): Bind a task only for dev purpose. Usually to be None for auto task scheduling.
"""
if isinstance(config, str):
self.config = AzurLaneConfig(config)
self.config = AzurLaneConfig(config, task=task)
else:
self.config = config
if device is not None:

View File

@ -5467,5 +5467,33 @@
]
}
}
},
"Daemon": {
"Daemon": {
"EnterMap": {
"type": "checkbox",
"value": true
}
}
},
"OpsiDaemon": {
"OpsiDaemon": {
"RepairShip": {
"type": "checkbox",
"value": true
}
}
},
"Benchmark": {
"Benchmark": {
"TestScreenshotMethod": {
"type": "checkbox",
"value": true
},
"TestClickMethod": {
"type": "checkbox",
"value": true
}
}
}
}

View File

@ -403,3 +403,13 @@ OpsiMeowfficerFarming:
HazardLevel:
value: 5
option: [1, 2, 3, 4, 5, 6]
# ==================== Tools ====================
Daemon:
EnterMap: true
OpsiDaemon:
RepairShip: true
Benchmark:
TestScreenshotMethod: true
TestClickMethod: true

View File

@ -45,6 +45,11 @@
"OpsiDaily",
"OpsiObscure",
"OpsiMeowfficerFarming"
],
"Tool": [
"Daemon",
"OpsiDaemon",
"Benchmark"
]
}
}

View File

@ -191,3 +191,12 @@ OpsiObscure:
OpsiMeowfficerFarming:
- Scheduler
- OpsiMeowfficerFarming
# ==================== Tool ====================
Daemon:
- Daemon
OpsiDaemon:
- OpsiDaemon
Benchmark:
- Benchmark

View File

@ -278,3 +278,13 @@ class GeneratedConfig:
# Group `OpsiMeowfficerFarming`
OpsiMeowfficerFarming_ActionPointPreserve = 500
OpsiMeowfficerFarming_HazardLevel = 5 # 1, 2, 3, 4, 5, 6
# Group `Daemon`
Daemon_EnterMap = True
# Group `OpsiDaemon`
OpsiDaemon_RepairShip = True
# Group `Benchmark`
Benchmark_TestScreenshotMethod = True
Benchmark_TestClickMethod = True

View File

@ -23,6 +23,10 @@
"Opsi": {
"name": "OpSi",
"help": ""
},
"Tool": {
"name": "Tools",
"help": ""
}
},
"Task": {
@ -169,6 +173,18 @@
"OpsiMeowfficerFarming": {
"name": "OpSi Meowfficer Farming",
"help": ""
},
"Daemon": {
"name": "Semi-auto Clicking",
"help": ""
},
"OpsiDaemon": {
"name": "Opsi Semi-auto",
"help": ""
},
"Benchmark": {
"name": "Performance Test",
"help": ""
}
},
"Scheduler": {
@ -1450,6 +1466,40 @@
"6": "6"
}
},
"Daemon": {
"_info": {
"name": "Semi-auto Clicking",
"help": "Useful when attacking a stage haven't adapted by Alas. Can save tons of manual clicks\nAlas will click combat preparation, click combat drop, skip story, auto-retire, close popups. You only need to choose enemies."
},
"EnterMap": {
"name": "Auto Click Stage Preparation",
"help": "Prepare your fleets before using this"
}
},
"OpsiDaemon": {
"_info": {
"name": "Useful when attacking logger zones or Opsi story. Can save tons of manual clicks.\nAlas will click combat preparation, click combat drop, skip story, close popups. You only need to choose enemies and switch zones.",
"help": "OpsiDaemon._info.help"
},
"RepairShip": {
"name": "Repair Ships When Nearing Port",
"help": "After you move fleet to port, Alas will repair your ships in port.\nWhen it's done, move your fleet outside of port in 30s to avoid repairing again"
}
},
"Benchmark": {
"_info": {
"name": "Performance Test",
"help": "Test which screenshot method and click method is the fastest. Speed is vary from emulators and PCs."
},
"TestScreenshotMethod": {
"name": "Test Screenshots Methods",
"help": ""
},
"TestClickMethod": {
"name": "Test Click Methods",
"help": ""
}
},
"Gui": {
"Aside": {
"Install": "Install",

View File

@ -23,6 +23,10 @@
"Opsi": {
"name": "Menu.Opsi.name",
"help": "Menu.Opsi.help"
},
"Tool": {
"name": "Menu.Tool.name",
"help": "Menu.Tool.help"
}
},
"Task": {
@ -169,6 +173,18 @@
"OpsiMeowfficerFarming": {
"name": "Task.OpsiMeowfficerFarming.name",
"help": "Task.OpsiMeowfficerFarming.help"
},
"Daemon": {
"name": "Task.Daemon.name",
"help": "Task.Daemon.help"
},
"OpsiDaemon": {
"name": "Task.OpsiDaemon.name",
"help": "Task.OpsiDaemon.help"
},
"Benchmark": {
"name": "Task.Benchmark.name",
"help": "Task.Benchmark.help"
}
},
"Scheduler": {
@ -1450,6 +1466,40 @@
"6": "6"
}
},
"Daemon": {
"_info": {
"name": "Daemon._info.name",
"help": "Daemon._info.help"
},
"EnterMap": {
"name": "Daemon.EnterMap.name",
"help": "Daemon.EnterMap.help"
}
},
"OpsiDaemon": {
"_info": {
"name": "OpsiDaemon._info.name",
"help": "OpsiDaemon._info.help"
},
"RepairShip": {
"name": "OpsiDaemon.RepairShip.name",
"help": "OpsiDaemon.RepairShip.help"
}
},
"Benchmark": {
"_info": {
"name": "Benchmark._info.name",
"help": "Benchmark._info.help"
},
"TestScreenshotMethod": {
"name": "Benchmark.TestScreenshotMethod.name",
"help": "Benchmark.TestScreenshotMethod.help"
},
"TestClickMethod": {
"name": "Benchmark.TestClickMethod.name",
"help": "Benchmark.TestClickMethod.help"
}
},
"Gui": {
"Aside": {
"Install": "Gui.Aside.Install",

View File

@ -23,6 +23,10 @@
"Opsi": {
"name": "大世界",
"help": ""
},
"Tool": {
"name": "工具",
"help": ""
}
},
"Task": {
@ -169,6 +173,18 @@
"OpsiMeowfficerFarming": {
"name": "短猫相接",
"help": ""
},
"Daemon": {
"name": "半自动点击",
"help": ""
},
"OpsiDaemon": {
"name": "大世界半自动",
"help": ""
},
"Benchmark": {
"name": "性能测试",
"help": ""
}
},
"Scheduler": {
@ -1450,6 +1466,40 @@
"6": "6"
}
},
"Daemon": {
"_info": {
"name": "半自动点击",
"help": "在出击未适配的关卡时有用,能减少大量人工操作\nAlas 将帮助点击战斗准备,点击战斗结算,跳过剧情,自动退役,关闭各种弹窗。你只需要手动选择敌人。"
},
"EnterMap": {
"name": "自动点击进图准备",
"help": "使用之前,需要准备好出击队伍"
}
},
"OpsiDaemon": {
"_info": {
"name": "大世界半自动",
"help": "在出击大世界深渊海域(红宝箱图)或完成大世界主线时有用,能减少大量人工操作\nAlas 将帮助点击战斗准备,点击战斗结算,跳过剧情,关闭各种弹窗。你只需要手动选择敌人和手动切换海域。"
},
"RepairShip": {
"name": "靠近港口时自动修船",
"help": "手动把队伍移动至港口后Alas将帮助修船\n修理完成后需要在30秒内将队伍移出港口否则将再次修船"
}
},
"Benchmark": {
"_info": {
"name": "性能测试",
"help": "测试哪个截图方案和点击方案速度更快,速度因模拟器和电脑配置而异"
},
"TestScreenshotMethod": {
"name": "测试截图方案",
"help": ""
},
"TestClickMethod": {
"name": "测试点击方案",
"help": ""
}
},
"Gui": {
"Aside": {
"Install": "安装",

View File

@ -23,6 +23,10 @@
"Opsi": {
"name": "Menu.Opsi.name",
"help": "Menu.Opsi.help"
},
"Tool": {
"name": "Menu.Tool.name",
"help": "Menu.Tool.help"
}
},
"Task": {
@ -169,6 +173,18 @@
"OpsiMeowfficerFarming": {
"name": "Task.OpsiMeowfficerFarming.name",
"help": "Task.OpsiMeowfficerFarming.help"
},
"Daemon": {
"name": "Task.Daemon.name",
"help": "Task.Daemon.help"
},
"OpsiDaemon": {
"name": "Task.OpsiDaemon.name",
"help": "Task.OpsiDaemon.help"
},
"Benchmark": {
"name": "Task.Benchmark.name",
"help": "Task.Benchmark.help"
}
},
"Scheduler": {
@ -1450,6 +1466,40 @@
"6": "6"
}
},
"Daemon": {
"_info": {
"name": "Daemon._info.name",
"help": "Daemon._info.help"
},
"EnterMap": {
"name": "Daemon.EnterMap.name",
"help": "Daemon.EnterMap.help"
}
},
"OpsiDaemon": {
"_info": {
"name": "OpsiDaemon._info.name",
"help": "OpsiDaemon._info.help"
},
"RepairShip": {
"name": "OpsiDaemon.RepairShip.name",
"help": "OpsiDaemon.RepairShip.help"
}
},
"Benchmark": {
"_info": {
"name": "Benchmark._info.name",
"help": "Benchmark._info.help"
},
"TestScreenshotMethod": {
"name": "Benchmark.TestScreenshotMethod.name",
"help": "Benchmark.TestScreenshotMethod.help"
},
"TestClickMethod": {
"name": "Benchmark.TestClickMethod.name",
"help": "Benchmark.TestClickMethod.help"
}
},
"Gui": {
"Aside": {
"Install": "Gui.Aside.Install",

View File

@ -1,13 +0,0 @@
from module.base.button import Button
from module.base.template import Template
# This file was automatically generated by dev_tools.button_extract.
# Don't modify it manually.
AMBUSH_AVOID = Button(area={'cn': (753, 444, 927, 502), 'en': (753, 443, 927, 503), 'jp': (753, 444, 927, 502), 'tw': (762, 443, 927, 499)}, color={'cn': (87, 129, 189), 'en': (94, 136, 195), 'jp': (87, 129, 189), 'tw': (92, 133, 192)}, button={'cn': (979, 444, 1152, 502), 'en': (978, 443, 1153, 503), 'jp': (979, 444, 1152, 502), 'tw': (979, 444, 1152, 502)}, file={'cn': './assets/cn/daemon/AMBUSH_AVOID.png', 'en': './assets/en/daemon/AMBUSH_AVOID.png', 'jp': './assets/jp/daemon/AMBUSH_AVOID.png', 'tw': './assets/tw/daemon/AMBUSH_AVOID.png'})
AT_SEA = Button(area={'cn': (749, 654, 921, 707), 'en': (748, 652, 922, 702), 'jp': (748, 653, 921, 707), 'tw': (749, 654, 921, 707)}, color={'cn': (213, 124, 124), 'en': (211, 124, 124), 'jp': (210, 122, 122), 'tw': (213, 124, 124)}, button={'cn': (749, 654, 921, 707), 'en': (748, 652, 922, 702), 'jp': (748, 653, 921, 707), 'tw': (749, 654, 921, 707)}, file={'cn': './assets/cn/daemon/AT_SEA.png', 'en': './assets/en/daemon/AT_SEA.png', 'jp': './assets/jp/daemon/AT_SEA.png', 'tw': './assets/tw/daemon/AT_SEA.png'})
FLEET_PREPARATION = Button(area={'cn': (981, 575, 1180, 636), 'en': (978, 572, 1179, 635), 'jp': (1040, 604, 1242, 670), 'tw': (981, 575, 1180, 636)}, color={'cn': (235, 185, 114), 'en': (234, 177, 90), 'jp': (233, 181, 99), 'tw': (235, 185, 114)}, button={'cn': (981, 575, 1180, 636), 'en': (978, 572, 1179, 635), 'jp': (1040, 604, 1242, 670), 'tw': (981, 575, 1180, 636)}, file={'cn': './assets/cn/daemon/FLEET_PREPARATION.png', 'en': './assets/en/daemon/FLEET_PREPARATION.png', 'jp': './assets/jp/daemon/FLEET_PREPARATION.png', 'tw': './assets/tw/daemon/FLEET_PREPARATION.png'})
GET_EMERGENCY_REPAIR = Button(area={'cn': (645, 352, 666, 360), 'en': (645, 352, 666, 360), 'jp': (645, 352, 666, 360), 'tw': (645, 352, 666, 360)}, color={'cn': (255, 255, 255), 'en': (255, 255, 255), 'jp': (255, 255, 255), 'tw': (255, 255, 255)}, button={'cn': (645, 352, 666, 360), 'en': (645, 352, 666, 360), 'jp': (645, 352, 666, 360), 'tw': (645, 352, 666, 360)}, file={'cn': './assets/cn/daemon/GET_EMERGENCY_REPAIR.png', 'en': './assets/en/daemon/GET_EMERGENCY_REPAIR.png', 'jp': './assets/jp/daemon/GET_EMERGENCY_REPAIR.png', 'tw': './assets/tw/daemon/GET_EMERGENCY_REPAIR.png'})
GET_ITEMS = Button(area={'cn': (538, 217, 741, 253), 'en': (550, 215, 739, 246), 'jp': (539, 220, 741, 252), 'tw': (538, 217, 741, 253)}, color={'cn': (160, 192, 248), 'en': (157, 187, 233), 'jp': (146, 184, 249), 'tw': (160, 192, 248)}, button={'cn': (1120, 643, 1146, 666), 'en': (1120, 643, 1146, 666), 'jp': (1120, 643, 1146, 666), 'tw': (1120, 643, 1146, 666)}, file={'cn': './assets/cn/daemon/GET_ITEMS.png', 'en': './assets/en/daemon/GET_ITEMS.png', 'jp': './assets/jp/daemon/GET_ITEMS.png', 'tw': './assets/tw/daemon/GET_ITEMS.png'})
MAP_PREPARATION = Button(area={'cn': (853, 488, 1053, 549), 'en': (852, 489, 1054, 553), 'jp': (850, 485, 1051, 548), 'tw': (853, 488, 1053, 549)}, color={'cn': (235, 186, 114), 'en': (234, 179, 93), 'jp': (232, 181, 101), 'tw': (235, 186, 114)}, button={'cn': (853, 488, 1053, 549), 'en': (852, 489, 1054, 553), 'jp': (850, 485, 1051, 548), 'tw': (853, 488, 1053, 549)}, file={'cn': './assets/cn/daemon/MAP_PREPARATION.png', 'en': './assets/en/daemon/MAP_PREPARATION.png', 'jp': './assets/jp/daemon/MAP_PREPARATION.png', 'tw': './assets/tw/daemon/MAP_PREPARATION.png'})
STRATEGY_OPEN = Button(area={'cn': (1102, 480, 1178, 482), 'en': (1102, 480, 1178, 482), 'jp': (1102, 480, 1178, 482), 'tw': (1102, 480, 1178, 482)}, color={'cn': (255, 223, 74), 'en': (255, 223, 74), 'jp': (255, 223, 74), 'tw': (255, 223, 74)}, button={'cn': (1064, 405, 1093, 483), 'en': (1064, 405, 1093, 483), 'jp': (1064, 405, 1093, 483), 'tw': (1064, 405, 1093, 483)}, file={'cn': './assets/cn/daemon/STRATEGY_OPEN.png', 'en': './assets/en/daemon/STRATEGY_OPEN.png', 'jp': './assets/jp/daemon/STRATEGY_OPEN.png', 'tw': './assets/tw/daemon/STRATEGY_OPEN.png'})

136
module/daemon/benchmark.py Normal file
View File

@ -0,0 +1,136 @@
import time
import numpy as np
from prettytable import PrettyTable
from module.base.utils import float2str as float2str_
from module.base.utils import random_rectangle_point
from module.daemon.daemon_base import DaemonBase
from module.logger import logger
from module.ui.ui import UI, page_campaign
def float2str(n, decimal=3):
if not isinstance(n, (float, int)):
return str(n)
else:
return float2str_(n, decimal=decimal) + 's'
class Benchmark(DaemonBase, UI):
TEST_TOTAL = 15
TEST_BEST = int(TEST_TOTAL * 0.8)
def benchmark_test(self, func, *args, **kwargs):
"""
Args:
func: Function to test.
*args: Passes to func.
**kwargs: Passes to func.
Returns:
float: Time cost on average.
"""
logger.hr(f'Benchmark test', level=2)
logger.info(f'Testing function: {func.__name__}')
record = []
for n in range(1, self.TEST_TOTAL + 1):
start = time.time()
try:
func(*args, **kwargs)
except Exception as e:
logger.exception(e)
logger.warning(f'Benchmark tests failed on func: {func.__name__}')
return 'Failed'
cost = time.time() - start
logger.attr(
f'{str(n).rjust(2, "0")}/{self.TEST_TOTAL}',
f'{float2str(cost)}'
)
record.append(cost)
logger.info('Benchmark tests done')
average = float(np.mean(np.sort(record)[:self.TEST_BEST]))
logger.info(f'Time cost {float2str(average)} ({self.TEST_BEST} best results out of {self.TEST_TOTAL} tests)')
return average
@staticmethod
def evaluate_screenshot(cost):
if not isinstance(cost, (float, int)):
return str(cost)
if cost < 0.25:
return 'Very Fast'
if cost < 0.45:
return 'Fast'
if cost < 0.65:
return 'Medium'
if cost < 0.95:
return 'Slow'
return 'Very Slow'
@staticmethod
def evaluate_click(cost):
if not isinstance(cost, (float, int)):
return str(cost)
if cost < 0.1:
return 'Fast'
if cost < 0.2:
return 'Medium'
return 'Slow'
@staticmethod
def show(test, data, evaluate_func):
"""
+--------------+--------+--------+
| Screenshot | time | Speed |
+--------------+--------+--------+
| ADB | 0.319s | Fast |
| uiautomator2 | 0.476s | Medium |
| aScreenCap | Failed | Failed |
+--------------+--------+--------+
"""
table = PrettyTable()
table.field_names = [test, 'Time', 'Speed']
for row in data:
table.add_row([row[0], f'{float2str(row[1])}', evaluate_func(row[1])])
for row in table.get_string().split('\n'):
logger.info(row)
def run(self):
logger.hr('Benchmark', level=1)
self.ui_ensure(page_campaign)
data = []
if self.config.Benchmark_TestScreenshotMethod:
data.append(['ADB', self.benchmark_test(self.device._screenshot_adb)])
data.append(['uiautomator2', self.benchmark_test(self.device._screenshot_uiautomator2)])
data.append(['aScreenCap', self.benchmark_test(self.device._screenshot_ascreencap)])
screenshot = data
data = []
area = (124, 4, 649, 106) # Somewhere save to click.
if self.config.Benchmark_TestClickMethod:
x, y = random_rectangle_point(area)
data.append(['ADB', self.benchmark_test(self.device._click_adb, x, y)])
x, y = random_rectangle_point(area)
data.append(['uiautomator2', self.benchmark_test(self.device._click_uiautomator2, x, y)])
x, y = random_rectangle_point(area)
data.append(['minitouch', self.benchmark_test(self.device._click_minitouch, x, y)])
click = data
logger.hr('Benchmark Results', level=1)
if screenshot:
self.show(test='Screenshot', data=screenshot, evaluate_func=self.evaluate_screenshot)
if click:
self.show(test='Click', data=click, evaluate_func=self.evaluate_click)
if __name__ == '__main__':
b = Benchmark('alas', task='Benchmark')
b.run()

View File

@ -1,13 +1,12 @@
from module.campaign.campaign_base import CampaignBase
from module.daemon.assets import *
from module.exception import *
from module.daemon.daemon_base import DaemonBase
from module.exception import CampaignEnd
from module.handler.ambush import MAP_AMBUSH_EVADE
from module.map.map_operation import MAP_PREPARATION, FLEET_PREPARATION
class AzurLaneDaemon(CampaignBase):
def daemon(self):
self.device.disable_stuck_detection()
class AzurLaneDaemon(DaemonBase, CampaignBase):
def run(self):
while 1:
self.device.screenshot()
@ -17,26 +16,23 @@ class AzurLaneDaemon(CampaignBase):
# Combat
if self.combat_appear():
# if self.handle_combat_automation_set(auto=True):
# continue
# self.device.click(BATTLE_PREPARATION)
self.combat_preparation()
try:
if self.handle_battle_status(save_get_items=False):
self.combat_status(save_get_items=False, expected_end='no_searching')
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):
if self.appear_then_click(MAP_AMBUSH_EVADE, offset=(20, 20)):
self.device.sleep(1)
continue
if self.appear_then_click(STRATEGY_OPEN):
if self.handle_mystery_items():
continue
# Map preparation
if self.config.ENABLE_SEMI_MAP_PREPARATION:
if self.config.Daemon_EnterMap:
if self.appear_then_click(MAP_PREPARATION, offset=(20, 20), interval=2):
continue
if self.appear_then_click(FLEET_PREPARATION, offset=(20, 20), interval=2):
@ -50,14 +46,25 @@ class AzurLaneDaemon(CampaignBase):
pass
# Urgent commission
if self.handle_urgent_commission(save_get_items=False):
if self.handle_urgent_commission():
continue
# Popups
if self.handle_guild_popup_cancel():
return True
if self.handle_vote_popup():
continue
# Story
if self.config.ENABLE_SEMI_STORY_SKIP:
self.story_skip()
if self.story_skip():
continue
# End
# No end condition, stop it manually.
return True
if __name__ == '__main__':
b = AzurLaneDaemon('alas', task='Daemon')
b.run()

View File

@ -0,0 +1,7 @@
from module.base.base import ModuleBase
class DaemonBase(ModuleBase):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.device.disable_stuck_detection()

View File

@ -1,13 +1,12 @@
from module.exception import *
from module.daemon.daemon_base import DaemonBase
from module.exception import CampaignEnd
from module.logger import logger
from module.os_combat.combat import Combat, ContinuousCombat
from module.os_handler.port import PortHandler, PORT_ENTER
class AzurLaneDaemon(Combat, PortHandler):
def daemon(self):
self.device.disable_stuck_detection()
class AzurLaneDaemon(DaemonBase, Combat, PortHandler):
def run(self):
while 1:
self.device.screenshot()
@ -19,41 +18,32 @@ class AzurLaneDaemon(Combat, PortHandler):
if self.combat_appear():
self.combat_preparation()
try:
if self.handle_battle_status(save_get_items=False):
self.combat_status(save_get_items=False, expected_end='no_searching')
if self.handle_battle_status():
self.combat_status(expected_end='no_searching')
continue
except (CampaignEnd, ContinuousCombat):
continue
# Map operation
# Map preparation
# Retire
pass
# Emotion
pass
# Urgent commission
if self.handle_urgent_commission(save_get_items=False):
# Map events
if self.handle_map_event():
continue
# Story
if self.config.ENABLE_OS_SEMI_STORY_SKIP:
self.story_skip()
self.handle_map_event()
# Port repair
if self.config.OpsiDaemon_RepairShip:
if self.appear(PORT_ENTER, offset=(20, 20), interval=30):
self.port_enter()
self.port_dock_repair()
self.port_quit()
self.interval_reset(PORT_ENTER)
logger.info('Port repair finished, move your fleet out of the port in 30s to avoid repairing again')
logger.info('Port repair finished, '
'please move your fleet out of the port in 30s to avoid repairing again')
# End
# No end condition, stop it manually.
return True
if __name__ == '__main__':
b = AzurLaneDaemon('alas', task='OpsiDaemon')
b.run()

View File

@ -112,6 +112,8 @@ class Device(Screenshot, Control, AppControl):
"""
Disable stuck detection and its handler. Usually uses in semi auto and debugging.
"""
logger.info('Disable stuck detection')
def empty_function(*arg, **kwargs):
return False

View File

@ -77,7 +77,7 @@ class Combat(Combat_, MapEventHandler):
# self.emotion.reduce(fleet_index)
break
def handle_get_items(self, save_get_items=False):
def handle_get_items(self, drop=None):
if self.appear(GET_ITEMS_1, offset=5, interval=self.battle_status_click_interval):
self.device.click(CLICK_SAFE_AREA)
self.interval_reset(BATTLE_STATUS_S)
@ -107,8 +107,8 @@ class Combat(Combat_, MapEventHandler):
return self.handle_os_in_map()
def combat_status(self, save_get_items=False, expected_end=None):
super().combat_status(save_get_items=False, expected_end=self._os_combat_expected_end)
def combat_status(self, drop=None, expected_end=None):
super().combat_status(drop=drop, expected_end=self._os_combat_expected_end)
def combat(self, *args, **kwargs):
"""

View File

@ -18,3 +18,4 @@ anyio==1.3.1
uvicorn[standard]
aiofiles
wrapt==1.13.1
prettytable==2.2.1

View File

@ -28,7 +28,7 @@ h11==0.12.0 # via uvicorn
httptools==0.2.0 # via uvicorn
idna==2.6 # via requests
imageio==2.9.0 # via scikit-image
importlib-metadata==4.8.1 # via click
importlib-metadata==4.8.1 # via click, prettytable
inflection==0.5.1 # via -r requirements-in.txt
jellyfish==0.8.8 # via -r requirements-in.txt
kiwisolver==1.3.2 # via matplotlib
@ -43,6 +43,7 @@ opencv-python==4.5.3.56 # via -r requirements-in.txt
packaging==20.9 # via deprecation, uiautomator2
pillow==8.3.2 # via -r requirements-in.txt, cnocr, gluoncv, imageio, matplotlib, scikit-image, uiautomator2
portalocker==2.3.2 # via gluoncv
prettytable==2.2.1 # via -r requirements-in.txt
progress==1.6 # via uiautomator2
py==1.10.0 # via retry
pyelftools==0.27 # via apkutils2
@ -70,6 +71,7 @@ urllib3==1.22 # via requests
user-agents==2.2.0 # via pywebio
uvicorn[standard]==0.15.0 # via -r requirements-in.txt
watchgod==0.7 # via uvicorn
wcwidth==0.2.5 # via prettytable
websockets==10.0 # via uvicorn
whichcraft==0.6.1 # via adbutils, uiautomator2
wrapt==1.13.1 # via -r requirements-in.txt, deprecated