1
0
mirror of https://github.com/TheFunny/ArisuAutoSweeper synced 2026-06-29 21:35:02 +00:00

48 Commits

Author SHA1 Message Date
YoursFunny 447b32e487 feat(stage): add sweep support 2023-11-16 14:13:03 +08:00
YoursFunny 12ede66b04 fix: update en for assets 2023-11-16 14:07:04 +08:00
YoursFunny 95942dd64d refactor: move stage list to its own folder 2023-11-16 14:07:03 +08:00
YoursFunny 51b14b57e6 refactor: set indexes as property 2023-11-16 14:07:02 +08:00
YoursFunny 7c76fc90b5 lang: adjust text of second floor 2023-11-16 14:06:44 +08:00
YoursFunny 227610fdf3 feat(cafe): detect server before attempt on second floor 2023-11-16 14:06:42 +08:00
YoursFunny a8fea1bac3 refactor(cafe): rename template button 2023-11-16 14:06:13 +08:00
YoursFunny 4f3908bc9d fix: adapt for BA 2023-11-16 14:06:12 +08:00
YoursFunny 7b2c3bb165 feat: add en and Global server options 2023-11-13 16:06:00 +08:00
YoursFunny 2f4d64e5d0 fix: adapt AAS 2023-11-13 15:19:48 +08:00
YoursFunny 9e5fd34b79 fix(cafe): update assets 2023-11-12 17:49:49 +08:00
YoursFunny 5487338dc6 fix(cafe): remove crop as not needed 2023-11-12 17:49:49 +08:00
YoursFunny e5a914e80f fix(cafe): crop template from button image 2023-11-12 17:49:49 +08:00
YoursFunny f21a97e08d fix(cafe): apply mask 2023-11-12 17:49:49 +08:00
YoursFunny b4c47e2de2 feat(stage): add stage list recognition 2023-11-12 17:49:49 +08:00
YoursFunny 34208763aa refactor(cafe): adjust crop template button 2023-11-12 17:49:49 +08:00
YoursFunny c3354cf4f5 lang: adjust en gui text 2023-11-12 17:41:46 +08:00
YoursFunny 81abba0270 doc: fix typo 2023-11-12 16:18:16 +08:00
YoursFunny acea188931 doc: add more relative projects 2023-11-12 01:31:04 +08:00
YoursFunny b0621b48e6 fix(webui): use utf-8 encoding for icon reading 2023-11-12 00:10:43 +08:00
YoursFunny 1b83b7077f doc: add en gui pic 2023-11-10 21:47:14 +08:00
YoursFunny f1e3cd9810 feat(login): add update download confirm 2023-11-10 21:17:20 +08:00
YoursFunny f91fc2c55d feat(popup): add ap exceed and item expire handler 2023-11-10 21:17:20 +08:00
YoursFunny d82e206463 fix(tc): stick at certain status 2023-11-10 21:17:18 +08:00
YoursFunny db813c9efb fix: adapt AAS webui 2023-11-07 19:46:24 +08:00
YoursFunny 9d3b5ceff8 feat: add English webui 2023-11-07 19:43:41 +08:00
YoursFunny b83db6bc35 fix(popup): adjust daily reward recognition 2023-11-07 19:14:42 +08:00
YoursFunny 2e4a1f144b fix(popup): support another type of network reconnection 2023-11-07 19:14:01 +08:00
YoursFunny f7444e29dc doc: add gui pic 2023-11-07 14:58:55 +08:00
YoursFunny 1113094cf5 doc: adjust readme icon style 2023-11-07 14:41:05 +08:00
YoursFunny 60e6710181 doc: add readme 2023-11-07 14:22:47 +08:00
YoursFunny 9708ec05d7 fix(cafe): use correct boarder method 2023-11-06 21:24:13 +08:00
YoursFunny 77f0ded95f feat(cafe): handle unexpected popups 2023-11-05 23:19:51 +08:00
YoursFunny 937a7c63e8 fix(cafe): use smaller default pinch area 2023-11-05 23:09:46 +08:00
YoursFunny 61fa6ad28b perf(popup): improve reward handle stability 2023-11-04 16:09:29 +08:00
YoursFunny 0551207714 perf(popup): optimize popup handle method 2023-11-04 16:08:44 +08:00
YoursFunny 826a6b2348 perf(cafe): improve stability 2023-11-04 16:06:49 +08:00
YoursFunny a05c4e8f2f fix(cafe): adjust handle sequence 2023-11-04 16:05:57 +08:00
YoursFunny a20411071c perf: improve cafe reward stability 2023-11-04 16:03:40 +08:00
YoursFunny d53e280bfc perf: use better cafe interact method 2023-11-03 22:27:54 +08:00
YoursFunny 0b2487487b fix: adjust status logging sequence 2023-11-03 21:53:49 +08:00
YoursFunny bea794386c feat: add support for second cafe 2023-11-03 21:50:08 +08:00
YoursFunny 57c0ed8531 fix: improve cafe performance and stability 2023-11-03 21:47:08 +08:00
YoursFunny 75e6417620 feat: add function for button click with interval 2023-11-03 21:42:22 +08:00
YoursFunny 96b5f1db85 fix: Adjust cafe student recognition 2023-11-03 18:35:04 +08:00
YoursFunny 51305f7c0b feat: Switch to self-hosted git repo mirror for cn users 2023-11-03 17:08:04 +08:00
YoursFunny 22647a5e36 fix: Swipe causing too much click 2023-11-02 22:56:05 +08:00
YoursFunny 080c4c130c fix: Back button causing too much click 2023-11-02 22:54:40 +08:00
73 changed files with 1254 additions and 595 deletions
+40
View File
@@ -0,0 +1,40 @@
<img width="150" height="150" align="left" style="float: left; margin: 0 10px 0 0;" alt="AAS icon" src="docs/resources/aas_icon.svg"/>
# ArisuAutoSweeper
**Blue Archive Automation Script**
**| English | [简体中文](README.md) |**
![gui_en.png](docs/resources/gui_en.png)
## Features
The script is still under active development. The following features have been implemented:
- [x] **Cafe** Claim rewards / Interact / Second floor
- [x] **Club** Claim AP
- [x] **Mailbox** Claim rewards
- [x] **Tactical Challenge** Claim rewards / Auto battle
_Currently only supports JP server._
## Relative projects
- [AzurLaneAutoScript](https://github.com/LmeSzinc/AzurLaneAutoScript): Azur Lane auto script
- [StarRailCopilot](https://github.com/LmeSzinc/StarRailCopilot): A bot for Honkai: Star Rail, based on the next
generation of ALAS framework
Some Blue Archive auto scripts:
- [BAAuto](https://github.com/RedDeadDepresso/BAAuto): Blue Archive Automation Script
- [BlueArchiveAutoScript](https://github.com/pur1fying/blue_archive_auto_script): BAAS, used to implement Blue Archive
automation
- [MBA](https://github.com/MaaAssistantArknights/MBA): BA assistant based on the new architecture of MAA
## Acknowledgements
Thanks to [6bir](https://github.com/6bir) for the icon design.
Thanks to [Alas](https://github.com/LmeSzinc/AzurLaneAutoScript) and [SRC](https://github.com/LmeSzinc/StarRailCopilot)
for the development framework.
+38
View File
@@ -0,0 +1,38 @@
<img width="150" height="150" align="left" style="float: left; margin: 0 10px 0 0;" alt="AAS icon" src="docs/resources/aas_icon.svg"/>
# ArisuAutoSweeper
**蔚蓝档案自动化脚本**
**| [English](README.en.md) | 简体中文 |**
![gui_cn.png](docs/resources/gui_cn.png)
## 功能
当前脚本还在活跃开发中,已经实现的功能有:
- [x] **咖啡厅** 领取奖励 / 互动 / 第二咖啡厅
- [x] **公会** 领取体力
- [x] **邮箱** 领取奖励
- [x] **战术对抗赛** 领取奖励 / 自动战斗
_目前仅支持日服。_
## 相关项目
- [AzurLaneAutoScript](https://github.com/LmeSzinc/AzurLaneAutoScript): 碧蓝航线自动化脚本
- [StarRailCopilot](https://github.com/LmeSzinc/StarRailCopilot): 崩坏:星穹铁道脚本,基于下一代Alas框架
一些蔚蓝档案脚本:
- [BAAuto](https://github.com/RedDeadDepresso/BAAuto): 蔚蓝档案自动脚本
- [BlueArchiveAutoScript](https://github.com/pur1fying/blue_archive_auto_script): BAAS,用于实现蔚蓝档案自动化
- [MBA](https://github.com/MaaAssistantArknights/MBA): 基于 MAA 全新架构的 BA 小助手
## 鸣谢
感谢 [6bir](https://github.com/6bir) 为本项目设计的图标。
感谢 [Alas](https://github.com/LmeSzinc/AzurLaneAutoScript) 以及 [SRC](https://github.com/LmeSzinc/StarRailCopilot)
提供的开发框架。
Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

+2 -2
View File
@@ -1,8 +1,8 @@
Deploy: Deploy:
Git: Git:
# URL of AzurLaneAutoScript repository # URL of AzurLaneAutoScript repository
# [CN user] Use 'cn' to get update from git-over-cdn service # [CN user] Use 'cn' to get update from self-hosted git service
# [Other] Use 'global' to get update from https://github.com/TheFunny/ArisuAutoSrcipt # [Other] Use 'global' to get update from https://github.com/TheFunny/ArisuAutoSweeper
Repository: cn Repository: cn
# Branch of Alas # Branch of Alas
# [Developer] Use 'dev', 'app', etc, to try new features # [Developer] Use 'dev', 'app', etc, to try new features
+2 -2
View File
@@ -1,8 +1,8 @@
Deploy: Deploy:
Git: Git:
# URL of AzurLaneAutoScript repository # URL of AzurLaneAutoScript repository
# [CN user] Use 'cn' to get update from git-over-cdn service # [CN user] Use 'cn' to get update from self-hosted git service
# [Other] Use 'global' to get update from https://github.com/TheFunny/ArisuAutoSrcipt # [Other] Use 'global' to get update from https://github.com/TheFunny/ArisuAutoSweeper
Repository: global Repository: global
# Branch of Alas # Branch of Alas
# [Developer] Use 'dev', 'app', etc, to try new features # [Developer] Use 'dev', 'app', etc, to try new features
+2 -1
View File
@@ -43,7 +43,8 @@
"Cafe": { "Cafe": {
"Reward": true, "Reward": true,
"Touch": true, "Touch": true,
"AutoAdjust": true "AutoAdjust": true,
"SecondCafe": true
} }
}, },
"Mail": { "Mail": {
+3 -5
View File
@@ -65,9 +65,6 @@ class ConfigModel:
AppAsarUpdate: bool = True AppAsarUpdate: bool = True
NoSandbox: bool = True NoSandbox: bool = True
# Dynamic
GitOverCdn: bool = False
class DeployConfig(ConfigModel): class DeployConfig(ConfigModel):
def __init__(self, file=DEPLOY_CONFIG): def __init__(self, file=DEPLOY_CONFIG):
@@ -82,8 +79,9 @@ class DeployConfig(ConfigModel):
# Bypass webui.config.DeployConfig.__setattr__() # Bypass webui.config.DeployConfig.__setattr__()
# Don't write these into deploy.yaml # Don't write these into deploy.yaml
super().__setattr__('GitOverCdn', self.Repository in ['cn']) if self.Repository == 'cn':
if self.Repository in ['global', 'cn']: super().__setattr__('Repository', 'https://git.yoursfunny.top/YoursFunny/ArisuAutoSweeper.git')
if self.Repository == 'global':
super().__setattr__('Repository', 'https://github.com/TheFunny/ArisuAutoSweeper') super().__setattr__('Repository', 'https://github.com/TheFunny/ArisuAutoSweeper')
self.write() self.write()
-36
View File
@@ -4,7 +4,6 @@ import os
from deploy.Windows.config import DeployConfig from deploy.Windows.config import DeployConfig
from deploy.Windows.logger import Progress, logger from deploy.Windows.logger import Progress, logger
from deploy.Windows.utils import cached_property from deploy.Windows.utils import cached_property
# from deploy.git_over_cdn.client import GitOverCdnClient
class GitConfigParser(configparser.ConfigParser): class GitConfigParser(configparser.ConfigParser):
@@ -17,25 +16,6 @@ class GitConfigParser(configparser.ConfigParser):
return False return False
# class GitOverCdnClientWindows(GitOverCdnClient):
# def update(self, *args, **kwargs):
# Progress.GitInit()
# _ = super().update(*args, **kwargs)
# Progress.GitShowVersion()
# return _
#
# @cached_property
# def latest_commit(self) -> str:
# _ = super().latest_commit
# Progress.GitLatestCommit()
# return _
#
# def download_pack(self):
# _ = super().download_pack()
# Progress.GitDownloadPack()
# return _
class GitManager(DeployConfig): class GitManager(DeployConfig):
@staticmethod @staticmethod
def remove(file): def remove(file):
@@ -128,18 +108,6 @@ class GitManager(DeployConfig):
self.execute(f'"{self.git}" --no-pager log --no-merges -1') self.execute(f'"{self.git}" --no-pager log --no-merges -1')
Progress.GitShowVersion() Progress.GitShowVersion()
# @property
# def goc_client(self):
# client = GitOverCdnClient(
# url='https://vip.123pan.cn/1815343254/pack/LmeSzinc_StarRailCopilot_master',
# folder=self.root_filepath,
# source='origin',
# branch='master',
# git=self.git,
# )
# client.logger = logger
# return client
def git_install(self): def git_install(self):
logger.hr('Update Alas', 0) logger.hr('Update Alas', 0)
@@ -148,10 +116,6 @@ class GitManager(DeployConfig):
Progress.GitShowVersion() Progress.GitShowVersion()
return return
# if self.GitOverCdn:
# if self.goc_client.update(keep_changes=self.KeepLocalChanges):
# return
self.git_repository_init( self.git_repository_init(
repo=self.Repository, repo=self.Repository,
source='origin', source='origin',
+2 -2
View File
@@ -1,8 +1,8 @@
Deploy: Deploy:
Git: Git:
# URL of AzurLaneAutoScript repository # URL of AzurLaneAutoScript repository
# [CN user] Use 'cn' to get update from git-over-cdn service # [CN user] Use 'cn' to get update from self-hosted git service
# [Other] Use 'global' to get update from https://github.com/TheFunny/ArisuAutoSrcipt # [Other] Use 'global' to get update from https://github.com/TheFunny/ArisuAutoSweeper
Repository: 'global' Repository: 'global'
# Branch of Alas # Branch of Alas
# [Developer] Use 'dev', 'app', etc, to try new features # [Developer] Use 'dev', 'app', etc, to try new features
-266
View File
@@ -1,266 +0,0 @@
import io
import json
import os
import re
import shutil
import subprocess
import zipfile
from typing import Callable, Generic, TypeVar
import requests
from requests.adapters import HTTPAdapter
T = TypeVar("T")
TEMPLATE_FILE = './config/template.yaml'
class cached_property(Generic[T]):
"""
cached-property from https://github.com/pydanny/cached-property
Add typing support
A property that is only computed once per instance and then replaces itself
with an ordinary attribute. Deleting the attribute resets the property.
Source: https://github.com/bottlepy/bottle/commit/fa7733e075da0d790d809aa3d2f53071897e6f76
"""
def __init__(self, func: Callable[..., T]):
self.func = func
def __get__(self, obj, cls) -> T:
if obj is None:
return self
value = obj.__dict__[self.func.__name__] = self.func(obj)
return value
class PrintLogger:
info = print
warning = print
error = print
@staticmethod
def attr(name, text):
print(f'[{name}] {text}')
class GitOverCdnClient:
logger = PrintLogger()
def __init__(self, url, folder, source='origin', branch='master', git='git'):
"""
Args:
url: http://127.0.0.1:22251/pack/LmeSzinc_AzurLaneAutoScript_master/
folder: D:/AzurLaneAutoScript
"""
self.url = url.strip('/')
self.folder = folder.replace('\\', '/')
self.source = source
self.branch = branch
self.git = git
def filepath(self, path):
path = os.path.join(self.folder, '.git', path)
return os.path.abspath(path).replace('\\', '/')
def urlpath(self, path):
return f'{self.url}{path}'
@cached_property
def current_commit(self) -> str:
for file in [
f'./refs/remotes/{self.source}/{self.branch}',
f'./refs/heads/{self.branch}',
'ORIG_HEAD',
]:
file = self.filepath(file)
try:
with open(file, 'r', encoding='utf-8') as f:
commit = f.read()
res = re.search(r'([0-9a-f]{40})', commit)
if res:
commit = res.group(1)
self.logger.attr('CurrentCommit', commit)
return commit
except FileNotFoundError as e:
self.logger.error(f'Failed to get local commit: {e}')
except Exception as e:
self.logger.error(f'Failed to get local commit: {e}')
return ''
@property
def session(self):
session = requests.Session()
session.trust_env = False
session.mount('http://', HTTPAdapter(max_retries=3))
session.mount('https://', HTTPAdapter(max_retries=3))
return session
@cached_property
def latest_commit(self) -> str:
try:
url = self.urlpath('/latest.json')
self.logger.info(f'Fetch url: {url}')
resp = self.session.get(url, timeout=3)
except Exception as e:
self.logger.error(f'Failed to get remote commit: {e}')
return ''
if resp.status_code == 200:
try:
info = json.loads(resp.text)
commit = info['commit']
self.logger.attr('LatestCommit', commit)
return commit
except json.JSONDecodeError:
self.logger.error(f'Failed to get remote commit, response is not a json: {resp.text}')
return ''
except KeyError:
self.logger.error(f'Failed to get remote commit, key "commit" is not found: {resp.text}')
return ''
else:
self.logger.error(f'Failed to get remote commit, status={resp.status_code}, text={resp.text}')
return ''
def download_pack(self):
try:
url = self.urlpath(f'/{self.latest_commit}/{self.current_commit}.zip')
self.logger.info(f'Fetch url: {url}')
resp = self.session.get(url, timeout=20)
except Exception as e:
self.logger.error(f'Failed to download pack: {e}')
return False
if resp.status_code == 200:
try:
zipped = zipfile.ZipFile(io.BytesIO(resp.content))
for file in [f'pack-{self.latest_commit}.pack', f'pack-{self.latest_commit}.idx']:
self.logger.info(f'Unzip {file}')
member = zipped.getinfo(file)
tmp = self.filepath(f'./objects/pack/{file}.tmp')
out = self.filepath(f'./objects/pack/{file}')
with zipped.open(member) as source, open(tmp, "wb") as target:
shutil.copyfileobj(source, target)
os.replace(tmp, out)
return True
except zipfile.BadZipFile as e:
# File is not a zip file
self.logger.error(e)
return False
except KeyError as e:
# There is no item named 'xxx.idx' in the archive
self.logger.error(e)
return False
except Exception as e:
self.logger.error(e)
return False
elif resp.status_code == 404:
self.logger.error(f'Failed to download pack, status={resp.status_code}, no such pack files provided')
return False
else:
self.logger.error(f'Failed to download pack, status={resp.status_code}, text={resp.text}')
return False
def update_refs(self):
file = self.filepath(f'./refs/remotes/{self.source}/{self.branch}')
text = f'{self.latest_commit}\n'
self.logger.info(f'Update refs: {file}')
os.makedirs(os.path.dirname(file), exist_ok=True)
try:
with open(file, 'w', encoding='utf-8', newline='') as f:
f.write(text)
return True
except FileNotFoundError as e:
self.logger.error(f'Failed to get local commit: {e}')
except Exception as e:
self.logger.error(f'Failed to get local commit: {e}')
return False
def git_command(self, *args, timeout=300):
"""
Execute ADB commands in a subprocess,
usually to be used when pulling or pushing large files.
Args:
timeout (int):
Returns:
str:
"""
os.chdir(self.folder)
cmd = list(map(str, args))
cmd = [self.git] + cmd
self.logger.info(f'Execute: {cmd}')
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=False)
try:
stdout, stderr = process.communicate(timeout=timeout)
except subprocess.TimeoutExpired:
process.kill()
stdout, stderr = process.communicate()
self.logger.warning(f'TimeoutExpired when calling {cmd}, stdout={stdout}, stderr={stderr}')
return stdout.decode()
def git_reset(self, keep_changes=False):
"""
git reset --hard <commit>
"""
if keep_changes:
self.git_command('stash')
self.git_command('reset', '--hard', f'{self.source}/{self.branch}')
self.git_command('stash', 'pop')
else:
self.git_command('reset', '--hard', f'{self.source}/{self.branch}')
def get_status(self):
"""
Returns:
str: 'uptodate' if repo is up-to-date
'behind' if repos is not up-to-date
'failed' if failed
"""
_ = self.current_commit
_ = self.latest_commit
if not self.current_commit:
self.logger.error('Failed to get current commit')
return 'failed'
if not self.latest_commit:
self.logger.error('Failed to get latest commit')
return 'failed'
if self.current_commit == self.latest_commit:
self.logger.info('Already up to date')
return 'uptodate'
self.logger.info('Current repo is behind remote')
return 'behind'
def update(self, keep_changes=False):
"""
Args:
keep_changes:
Returns:
bool: If repo is up-to-date
"""
_ = self.current_commit
_ = self.latest_commit
if not self.current_commit:
self.logger.error('Failed to get current commit')
return False
if not self.latest_commit:
self.logger.error('Failed to get latest commit')
return False
if self.current_commit == self.latest_commit:
self.logger.info('Already up to date')
self.git_reset(keep_changes=keep_changes)
return True
if not self.download_pack():
return False
if not self.update_refs():
return False
self.git_reset(keep_changes=keep_changes)
self.logger.info('Update success')
return True
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

+8
View File
@@ -128,6 +128,14 @@ class ModuleBase:
self.device.click(button) self.device.click(button)
return appear return appear
def click_with_interval(self, button, interval=5):
if interval and not self.interval_is_reached(button, interval=interval):
return False
self.device.click(button)
if interval:
self.interval_reset(button, interval=interval)
return True
def wait_until_stable(self, button, timer=Timer(0.3, count=1), timeout=Timer(5, count=10)): def wait_until_stable(self, button, timer=Timer(0.3, count=1), timeout=Timer(5, count=10)):
""" """
A terrible method, don't rely too much on it. A terrible method, don't rely too much on it.
+8 -2
View File
@@ -11,7 +11,8 @@
"value": "auto", "value": "auto",
"option": [ "option": [
"auto", "auto",
"JP-Official" "JP-Official",
"OVERSEA-Global"
] ]
}, },
"GameLanguage": { "GameLanguage": {
@@ -19,7 +20,8 @@
"value": "auto", "value": "auto",
"option": [ "option": [
"auto", "auto",
"jp" "jp",
"en"
] ]
}, },
"ScreenshotMethod": { "ScreenshotMethod": {
@@ -194,6 +196,10 @@
"AutoAdjust": { "AutoAdjust": {
"type": "checkbox", "type": "checkbox",
"value": true "value": true
},
"SecondCafe": {
"type": "checkbox",
"value": true
} }
} }
}, },
+2 -1
View File
@@ -23,7 +23,7 @@ Emulator:
option: [ auto, ] option: [ auto, ]
GameLanguage: GameLanguage:
value: auto value: auto
option: [ auto, jp ] option: [ auto, jp, en ]
ScreenshotMethod: ScreenshotMethod:
value: auto value: auto
option: [ auto, ADB, ADB_nc, uiautomator2, aScreenCap, aScreenCap_nc, DroidCast, DroidCast_raw, scrcpy ] option: [ auto, ADB, ADB_nc, uiautomator2, aScreenCap, aScreenCap_nc, DroidCast, DroidCast_raw, scrcpy ]
@@ -79,6 +79,7 @@ Cafe:
Reward: true Reward: true
Touch: true Touch: true
AutoAdjust: true AutoAdjust: true
SecondCafe: true
TacticalChallenge: TacticalChallenge:
PlayerSelect: PlayerSelect:
+3 -2
View File
@@ -17,8 +17,8 @@ class GeneratedConfig:
# Group `Emulator` # Group `Emulator`
Emulator_Serial = 'auto' Emulator_Serial = 'auto'
Emulator_PackageName = 'auto' # auto, JP-Official Emulator_PackageName = 'auto' # auto, JP-Official, OVERSEA-Global
Emulator_GameLanguage = 'auto' # auto, jp Emulator_GameLanguage = 'auto' # auto, jp, en
Emulator_ScreenshotMethod = 'auto' # auto, ADB, ADB_nc, uiautomator2, aScreenCap, aScreenCap_nc, DroidCast, DroidCast_raw, scrcpy Emulator_ScreenshotMethod = 'auto' # auto, ADB, ADB_nc, uiautomator2, aScreenCap, aScreenCap_nc, DroidCast, DroidCast_raw, scrcpy
Emulator_ControlMethod = 'MaaTouch' # minitouch, MaaTouch Emulator_ControlMethod = 'MaaTouch' # minitouch, MaaTouch
Emulator_AdbRestart = False Emulator_AdbRestart = False
@@ -43,6 +43,7 @@ class GeneratedConfig:
Cafe_Reward = True Cafe_Reward = True
Cafe_Touch = True Cafe_Touch = True
Cafe_AutoAdjust = True Cafe_AutoAdjust = True
Cafe_SecondCafe = True
# Group `TacticalChallenge` # Group `TacticalChallenge`
TacticalChallenge_PlayerSelect = 0 # 0, 1, 2, 3 TacticalChallenge_PlayerSelect = 0 # 0, 1, 2, 3
+197 -191
View File
@@ -1,93 +1,95 @@
{ {
"Menu": { "Menu": {
"Alas": { "Alas": {
"name": "Menu.Alas.name", "name": "AAS",
"help": "Menu.Alas.help" "help": ""
}, },
"Daily": { "Daily": {
"name": "Menu.Daily.name", "name": "Daily",
"help": "Menu.Daily.help" "help": ""
} }
}, },
"Task": { "Task": {
"Alas": { "Alas": {
"name": "Task.Alas.name", "name": "AAS Settings",
"help": "Task.Alas.help" "help": ""
}, },
"Restart": { "Restart": {
"name": "Task.Restart.name", "name": "Error Handling",
"help": "Task.Restart.help" "help": ""
}, },
"Cafe": { "Cafe": {
"name": "Task.Cafe.name", "name": "Cafe",
"help": "Task.Cafe.help" "help": ""
}, },
"Mail": { "Mail": {
"name": "Task.Mail.name", "name": "Mailbox",
"help": "Task.Mail.help" "help": ""
}, },
"Circle": { "Circle": {
"name": "Task.Circle.name", "name": "Club",
"help": "Task.Circle.help" "help": ""
}, },
"TacticalChallenge": { "TacticalChallenge": {
"name": "Task.TacticalChallenge.name", "name": "Tactical Challenge",
"help": "Task.TacticalChallenge.help" "help": ""
}, },
"DataUpdate": { "DataUpdate": {
"name": "Task.DataUpdate.name", "name": "Dashboard Upd",
"help": "Task.DataUpdate.help" "help": ""
} }
}, },
"Scheduler": { "Scheduler": {
"_info": { "_info": {
"name": "Scheduler._info.name", "name": "Scheduler",
"help": "Scheduler._info.help" "help": ""
}, },
"Enable": { "Enable": {
"name": "Scheduler.Enable.name", "name": "Enable Task",
"help": "Scheduler.Enable.help", "help": "Join this task to scheduler.",
"True": "True", "True": "Enabled",
"False": "False" "False": "False"
}, },
"NextRun": { "NextRun": {
"name": "Scheduler.NextRun.name", "name": "Next Run",
"help": "Scheduler.NextRun.help" "help": "Updated automatically after completing the task to set next scheduled run, typically not manually modified\nHowever you can force immediate scheduling if you clear this text field"
}, },
"Command": { "Command": {
"name": "Scheduler.Command.name", "name": "Command",
"help": "Scheduler.Command.help" "help": ""
}, },
"ServerUpdate": { "ServerUpdate": {
"name": "Scheduler.ServerUpdate.name", "name": "Server Update",
"help": "Scheduler.ServerUpdate.help" "help": "Series of server refresh time(s) as to when this task will next run, this is automatically converted to respective time zone, generally do not need to modify"
} }
}, },
"Emulator": { "Emulator": {
"_info": { "_info": {
"name": "Emulator._info.name", "name": "Emulator Settings",
"help": "Emulator._info.help" "help": ""
}, },
"Serial": { "Serial": {
"name": "Emulator.Serial.name", "name": "Serial",
"help": "Emulator.Serial.help" "help": "Common emulator Serial can be queried in the list below\nUse \"auto\" to auto-detect emulators, but if multiple emulators are running or use emulators that do not support auto-detect, \"auto\" cannot be used and serial must be filled in manually\nDefault serial for select emulators:\n- BlueStacks 127.0.0.1:5555\n- BlueStacks4 Hyper-V use \"bluestacks4-hyperv\", \"bluestacks4-hyperv-2\" for multi instance, and so on\n- BlueStacks5 Hyper-V use \"bluestacks5-hyperv\", \"bluestacks5-hyperv-1\" for multi instance, and so on\n- NoxPlayer 127.0.0.1:62001\n- NoxPlayer64bit 127.0.0.1:59865\n- MuMuPlayer/MuMuPlayer X 127.0.0.1:7555\n- MuMuPlayer12 127.0.0.1:16384\n- MemuPlayer 127.0.0.1:21503\n- LDPlayer emulator-5554 or 127.0.0.1:5555\n- WSA use \"wsa-0\" to make the game run in the background, which needs to be controlled or closed by third-party software\nIf there are multiple emulator instances running, the default is reserved for one of them and the others will use different serials to avoid conflicts\nOpen console.bat and run `adb devices` to find them or follow the emulator's official tutorial"
}, },
"PackageName": { "PackageName": {
"name": "Emulator.PackageName.name", "name": "Game Server",
"help": "Emulator.PackageName.help", "help": "Can't distinguish different regions of oversea servers, please select the server manually.",
"auto": "auto", "auto": "Auto-detect",
"JP-Official": "JP-Official" "JP-Official": "[JP]-Official",
"OVERSEA-Global": "[OVERSEA]-Global"
}, },
"GameLanguage": { "GameLanguage": {
"name": "Emulator.GameLanguage.name", "name": "In-game Text Language",
"help": "Emulator.GameLanguage.help", "help": "",
"auto": "auto", "auto": "Auto-detect",
"jp": "jp" "jp": "Japanese",
"en": "English"
}, },
"ScreenshotMethod": { "ScreenshotMethod": {
"name": "Emulator.ScreenshotMethod.name", "name": "Screenshot Method",
"help": "Emulator.ScreenshotMethod.help", "help": "When using auto-select, a benchmark will be performed and automatically changed to the fastest screenshot method.\nGeneral speed: DroidCast_raw >> aScreenCap_nc > ADB_nc >>> aScreenCap > uiautomator2 ~= ADB.\nRun Tools - Performance Test to find the fastest method.",
"auto": "auto", "auto": "Auto-select the fastest",
"ADB": "ADB", "ADB": "ADB",
"ADB_nc": "ADB_nc", "ADB_nc": "ADB_nc",
"uiautomator2": "uiautomator2", "uiautomator2": "uiautomator2",
@@ -98,123 +100,127 @@
"scrcpy": "scrcpy" "scrcpy": "scrcpy"
}, },
"ControlMethod": { "ControlMethod": {
"name": "Emulator.ControlMethod.name", "name": "Control Method",
"help": "Emulator.ControlMethod.help", "help": "Speed: MaaTouch = minitouch >>> uiautomator2 ~= ADB\nMaaTouch is recommended",
"minitouch": "minitouch", "minitouch": "minitouch",
"MaaTouch": "MaaTouch" "MaaTouch": "MaaTouch"
}, },
"AdbRestart": { "AdbRestart": {
"name": "Emulator.AdbRestart.name", "name": "Try to restart adb when no device found",
"help": "Emulator.AdbRestart.help" "help": ""
} }
}, },
"EmulatorInfo": { "EmulatorInfo": {
"_info": { "_info": {
"name": "EmulatorInfo._info.name", "name": "Emulator Settings",
"help": "EmulatorInfo._info.help" "help": "The following values are auto-filled according to \"Serial\", if you dont understand, please don't modify them"
}, },
"Emulator": { "Emulator": {
"name": "EmulatorInfo.Emulator.name", "name": "Emulator Type",
"help": "EmulatorInfo.Emulator.help", "help": "",
"auto": "auto", "auto": "Auto-detect",
"NoxPlayer": "NoxPlayer", "NoxPlayer": "Nox Player",
"NoxPlayer64": "NoxPlayer64", "NoxPlayer64": "Nox Player 64bit",
"BlueStacks4": "BlueStacks4", "BlueStacks4": "BlueStacks 4",
"BlueStacks5": "BlueStacks5", "BlueStacks5": "BlueStacks 5",
"BlueStacks4HyperV": "BlueStacks4HyperV", "BlueStacks4HyperV": "BlueStacks 4 Hyper-V",
"BlueStacks5HyperV": "BlueStacks5HyperV", "BlueStacks5HyperV": "BlueStacks 5 Hyper-V",
"LDPlayer3": "LDPlayer3", "LDPlayer3": "LD Player 3",
"LDPlayer4": "LDPlayer4", "LDPlayer4": "LD Player 4",
"LDPlayer9": "LDPlayer9", "LDPlayer9": "LD Player 9",
"MuMuPlayer": "MuMuPlayer", "MuMuPlayer": "MuMu Player",
"MuMuPlayerX": "MuMuPlayerX", "MuMuPlayerX": "MuMu Player X",
"MuMuPlayer12": "MuMuPlayer12", "MuMuPlayer12": "MuMu Player 12",
"MEmuPlayer": "MEmuPlayer" "MEmuPlayer": "MEmu Player"
}, },
"name": { "name": {
"name": "EmulatorInfo.name.name", "name": "Emulator Instance Name",
"help": "EmulatorInfo.name.help" "help": ""
}, },
"path": { "path": {
"name": "EmulatorInfo.path.name", "name": "Emulator Installation Path",
"help": "EmulatorInfo.path.help" "help": ""
} }
}, },
"Error": { "Error": {
"_info": { "_info": {
"name": "Error._info.name", "name": "Debug Settings",
"help": "Error._info.help" "help": ""
}, },
"Restart": { "Restart": {
"name": "Error.Restart.name", "name": "Restart Game on Error",
"help": "Error.Restart.help", "help": "",
"game": "game", "game": "Restart game",
"game_emulator": "game_emulator" "game_emulator": "Restart emulator and game"
}, },
"SaveError": { "SaveError": {
"name": "Error.SaveError.name", "name": "Record Exception",
"help": "Error.SaveError.help" "help": "Records exception and log into directory for review or sharing"
}, },
"ScreenshotLength": { "ScreenshotLength": {
"name": "Error.ScreenshotLength.name", "name": "Record Screenshot(s)",
"help": "Error.ScreenshotLength.help" "help": "Number of screenshots saved when exception occurs"
}, },
"OnePushConfig": { "OnePushConfig": {
"name": "Error.OnePushConfig.name", "name": "Error notify config",
"help": "Error.OnePushConfig.help" "help": "When AAS cannot handle exception, send a message through Onepush. Configuration document: \nhttps://github.com/LmeSzinc/AzurLaneAutoScript/wiki/Onepush-configuration-%5BEN%5D"
} }
}, },
"Optimization": { "Optimization": {
"_info": { "_info": {
"name": "Optimization._info.name", "name": "Optimization Settings",
"help": "Optimization._info.help" "help": ""
}, },
"ScreenshotInterval": { "ScreenshotInterval": {
"name": "Optimization.ScreenshotInterval.name", "name": "Take Screenshots Every X Second(s)",
"help": "Optimization.ScreenshotInterval.help" "help": "Minimum interval between 2 screenshots, limited in 0.1 ~ 0.3, can help reduce CPU on high-end PCs"
}, },
"CombatScreenshotInterval": { "CombatScreenshotInterval": {
"name": "Optimization.CombatScreenshotInterval.name", "name": "Take Screenshots Every X Second(s) In Combat",
"help": "Optimization.CombatScreenshotInterval.help" "help": "Minimum interval between 2 screenshots, limited in 0.1 ~ 1.0, can help reduce CPU during battle"
}, },
"WhenTaskQueueEmpty": { "WhenTaskQueueEmpty": {
"name": "Optimization.WhenTaskQueueEmpty.name", "name": "When Task Queue is Empty",
"help": "Optimization.WhenTaskQueueEmpty.help", "help": "Close AL when there are no pending tasks, can help reduce CPU",
"stay_there": "stay_there", "stay_there": "Stay There",
"goto_main": "goto_main", "goto_main": "Goto Main Page",
"close_game": "close_game" "close_game": "Close Game"
} }
}, },
"Cafe": { "Cafe": {
"_info": { "_info": {
"name": "Cafe._info.name", "name": "Cafe Settings",
"help": "Cafe._info.help" "help": ""
}, },
"Reward": { "Reward": {
"name": "Cafe.Reward.name", "name": "Claim Reward",
"help": "Cafe.Reward.help" "help": ""
}, },
"Touch": { "Touch": {
"name": "Cafe.Touch.name", "name": "Student Interaction",
"help": "Cafe.Touch.help" "help": "Auto detect intractable student and tap"
}, },
"AutoAdjust": { "AutoAdjust": {
"name": "Cafe.AutoAdjust.name", "name": "Interface Auto Adjustment",
"help": "Cafe.AutoAdjust.help" "help": "Auto adjust cafe interface for better student interaction"
},
"SecondCafe": {
"name": "Second Floor",
"help": "JP server only\nEnable auto switch to second floor and perform interaction"
} }
}, },
"TacticalChallenge": { "TacticalChallenge": {
"_info": { "_info": {
"name": "TacticalChallenge._info.name", "name": "Tactical Challenge Settings",
"help": "TacticalChallenge._info.help" "help": ""
}, },
"PlayerSelect": { "PlayerSelect": {
"name": "TacticalChallenge.PlayerSelect.name", "name": "Select Player",
"help": "TacticalChallenge.PlayerSelect.help", "help": "",
"0": "0", "0": "Random",
"1": "1", "1": "First",
"2": "2", "2": "Second",
"3": "3" "3": "Third"
} }
}, },
"ItemStorage": { "ItemStorage": {
@@ -223,118 +229,118 @@
"help": "ItemStorage._info.help" "help": "ItemStorage._info.help"
}, },
"AP": { "AP": {
"name": "ItemStorage.AP.name", "name": "AP",
"help": "ItemStorage.AP.help" "help": ""
}, },
"Credit": { "Credit": {
"name": "ItemStorage.Credit.name", "name": "Credit",
"help": "ItemStorage.Credit.help" "help": ""
}, },
"Pyroxene": { "Pyroxene": {
"name": "ItemStorage.Pyroxene.name", "name": "Pyroxene",
"help": "ItemStorage.Pyroxene.help" "help": ""
} }
}, },
"Gui": { "Gui": {
"Aside": { "Aside": {
"Install": "Gui.Aside.Install", "Install": "Install",
"Home": "Gui.Aside.Home", "Home": "Home",
"Develop": "Gui.Aside.Develop", "Develop": "Develop",
"Performance": "Gui.Aside.Performance", "Performance": "Perf.",
"Setting": "Gui.Aside.Setting", "Setting": "Settings",
"AddAlas": "Gui.Aside.AddAlas" "AddAlas": "Add"
}, },
"Button": { "Button": {
"Start": "Gui.Button.Start", "Start": "Start",
"Stop": "Gui.Button.Stop", "Stop": "Stop",
"ScrollON": "Gui.Button.ScrollON", "ScrollON": "Auto Scroll ON",
"ScrollOFF": "Gui.Button.ScrollOFF", "ScrollOFF": "Auto Scroll OFF",
"ClearLog": "Gui.Button.ClearLog", "ClearLog": "Clear Log",
"Setting": "Gui.Button.Setting", "Setting": "Setting",
"CheckUpdate": "Gui.Button.CheckUpdate", "CheckUpdate": "Check update",
"ClickToUpdate": "Gui.Button.ClickToUpdate", "ClickToUpdate": "Click to update",
"RetryUpdate": "Gui.Button.RetryUpdate", "RetryUpdate": "Retry update",
"CancelUpdate": "Gui.Button.CancelUpdate" "CancelUpdate": "Cancel update"
}, },
"Toast": { "Toast": {
"DisableTranslateMode": "Gui.Toast.DisableTranslateMode", "DisableTranslateMode": "Click here to disable translate mode",
"ConfigSaved": "Gui.Toast.ConfigSaved", "ConfigSaved": "Config saved",
"AlasIsRunning": "Gui.Toast.AlasIsRunning", "AlasIsRunning": "Scheduler is already running",
"ClickToUpdate": "Gui.Toast.ClickToUpdate" "ClickToUpdate": "New update available, click here to update"
}, },
"Status": { "Status": {
"Running": "Gui.Status.Running", "Running": "Running",
"Inactive": "Gui.Status.Inactive", "Inactive": "Inactive",
"Warning": "Gui.Status.Warning", "Warning": "Warning",
"Updating": "Gui.Status.Updating" "Updating": "Waiting Update"
}, },
"MenuAlas": { "MenuAlas": {
"Overview": "Gui.MenuAlas.Overview", "Overview": "Overview",
"Log": "Gui.MenuAlas.Log" "Log": "Logs"
}, },
"MenuDevelop": { "MenuDevelop": {
"HomePage": "Gui.MenuDevelop.HomePage", "HomePage": "Home",
"Translate": "Gui.MenuDevelop.Translate", "Translate": "Translate",
"Update": "Gui.MenuDevelop.Update", "Update": "Updater",
"Remote": "Gui.MenuDevelop.Remote", "Remote": "Remote access",
"Utils": "Gui.MenuDevelop.Utils" "Utils": "Utils"
}, },
"Overview": { "Overview": {
"Scheduler": "Gui.Overview.Scheduler", "Scheduler": "Scheduler",
"Log": "Gui.Overview.Log", "Log": "Log",
"Running": "Gui.Overview.Running", "Running": "Running",
"Pending": "Gui.Overview.Pending", "Pending": "Pending",
"Waiting": "Gui.Overview.Waiting", "Waiting": "Waiting",
"NoTask": "Gui.Overview.NoTask" "NoTask": "No Task"
}, },
"Dashboard": { "Dashboard": {
"NoData": "Gui.Dashboard.NoData", "NoData": "no data",
"TimeError": "Gui.Dashboard.TimeError", "TimeError": "time error",
"JustNow": "Gui.Dashboard.JustNow", "JustNow": "just now",
"MinutesAgo": "Gui.Dashboard.MinutesAgo", "MinutesAgo": "{time}min ago",
"HoursAgo": "Gui.Dashboard.HoursAgo", "HoursAgo": "{time}h ago",
"DaysAgo": "Gui.Dashboard.DaysAgo", "DaysAgo": "{time}d ago",
"LongTimeAgo": "Gui.Dashboard.LongTimeAgo" "LongTimeAgo": "long time ago"
}, },
"AddAlas": { "AddAlas": {
"PopupTitle": "Gui.AddAlas.PopupTitle", "PopupTitle": "Add new config",
"NewName": "Gui.AddAlas.NewName", "NewName": "New name",
"CopyFrom": "Gui.AddAlas.CopyFrom", "CopyFrom": "Copy from existing config",
"Confirm": "Gui.AddAlas.Confirm", "Confirm": "Add",
"FileExist": "Gui.AddAlas.FileExist", "FileExist": "A config with the same name exists, please choose another one",
"InvalidChar": "Gui.AddAlas.InvalidChar", "InvalidChar": "Config name cannot contain any of the following characters: .\\/:*?\"<>|",
"InvalidPrefixTemplate": "Gui.AddAlas.InvalidPrefixTemplate" "InvalidPrefixTemplate": "Config name cannot start with 'template'"
}, },
"Update": { "Update": {
"UpToDate": "Gui.Update.UpToDate", "UpToDate": "Latest version",
"HaveUpdate": "Gui.Update.HaveUpdate", "HaveUpdate": "A new version is available",
"UpdateStart": "Gui.Update.UpdateStart", "UpdateStart": "Start update",
"UpdateWait": "Gui.Update.UpdateWait", "UpdateWait": "Waiting for all AAS complete current task",
"UpdateRun": "Gui.Update.UpdateRun", "UpdateRun": "Updating",
"UpdateSuccess": "Gui.Update.UpdateSuccess", "UpdateSuccess": "Update succeeded, restarting",
"UpdateFailed": "Gui.Update.UpdateFailed", "UpdateFailed": "Update failed. Logs can be found in ./log/*_gui.txt",
"UpdateChecking": "Gui.Update.UpdateChecking", "UpdateChecking": "Checking for updates",
"UpdateCancel": "Gui.Update.UpdateCancel", "UpdateCancel": "Update canceled, restarting AAS",
"UpdateFinish": "Gui.Update.UpdateFinish", "UpdateFinish": "Update succeeded, please restart manually",
"Local": "Gui.Update.Local", "Local": "Local",
"Upstream": "Gui.Update.Upstream", "Upstream": "Upstream",
"Author": "Gui.Update.Author", "Author": "Author",
"Time": "Gui.Update.Time", "Time": "Commit time",
"Message": "Gui.Update.Message", "Message": "Commit message",
"DisabledWarn": "Gui.Update.DisabledWarn", "DisabledWarn": "Updater module is disabled. You need to manually restart AAS to update",
"DetailedHistory": "Gui.Update.DetailedHistory" "DetailedHistory": "Detailed Commit History"
}, },
"Remote": { "Remote": {
"Running": "Gui.Remote.Running", "Running": "Remote access on",
"NotRunning": "Gui.Remote.NotRunning", "NotRunning": "Not running, server disconnected or offline",
"NotEnable": "Gui.Remote.NotEnable", "NotEnable": "Disabled, set webui password in deploy.yaml and enable remote access",
"EntryPoint": "Gui.Remote.EntryPoint", "EntryPoint": "Entry point:",
"ConfigureHint": "Gui.Remote.ConfigureHint", "ConfigureHint": "Configuration tutorial:",
"SSHNotInstall": "Gui.Remote.SSHNotInstall" "SSHNotInstall": "No SSH command in your system. Please refer to the tutorial to download or install one"
}, },
"Text": { "Text": {
"InvalidFeedBack": "Gui.Text.InvalidFeedBack", "InvalidFeedBack": "Invalid format. Example: {0}",
"Clear": "Gui.Text.Clear" "Clear": "Clear"
} }
} }
} }
+11 -5
View File
@@ -76,13 +76,15 @@
"name": "游戏服务器", "name": "游戏服务器",
"help": "无法区分国际服的不同地区,请手动选择服务器", "help": "无法区分国际服的不同地区,请手动选择服务器",
"auto": "自动检测", "auto": "自动检测",
"JP-Official": "[日服]-官服" "JP-Official": "[日服]-官服",
"OVERSEA-Global": "[国际服]-全球"
}, },
"GameLanguage": { "GameLanguage": {
"name": "游戏内文本语言", "name": "游戏内文本语言",
"help": "", "help": "",
"auto": "自动检测", "auto": "自动检测",
"jp": "日语" "jp": "日语",
"en": "英语"
}, },
"ScreenshotMethod": { "ScreenshotMethod": {
"name": "模拟器截图方案", "name": "模拟器截图方案",
@@ -201,6 +203,10 @@
"AutoAdjust": { "AutoAdjust": {
"name": "自动调整界面", "name": "自动调整界面",
"help": "在进行学生互动点击前对咖啡馆界面进行缩放和位置调整,以增加互动成功率" "help": "在进行学生互动点击前对咖啡馆界面进行缩放和位置调整,以增加互动成功率"
},
"SecondCafe": {
"name": "第二咖啡厅",
"help": "仅支持日服\n自动切换第二咖啡厅进行互动点击"
} }
}, },
"TacticalChallenge": { "TacticalChallenge": {
@@ -309,19 +315,19 @@
"UpToDate": "已是最新版本", "UpToDate": "已是最新版本",
"HaveUpdate": "有新版本可用", "HaveUpdate": "有新版本可用",
"UpdateStart": "开始更新", "UpdateStart": "开始更新",
"UpdateWait": "等待所有 Alas 完成当前任务", "UpdateWait": "等待所有 AAS 完成当前任务",
"UpdateRun": "更新中", "UpdateRun": "更新中",
"UpdateSuccess": "更新成功,正在重启", "UpdateSuccess": "更新成功,正在重启",
"UpdateFailed": "更新失败,可在./log/*_gui.txt中找到错误日志", "UpdateFailed": "更新失败,可在./log/*_gui.txt中找到错误日志",
"UpdateChecking": "检查更新中", "UpdateChecking": "检查更新中",
"UpdateCancel": "取消更新,重启 Alas 中", "UpdateCancel": "取消更新,重启 AAS 中",
"UpdateFinish": "更新成功,请手动重启", "UpdateFinish": "更新成功,请手动重启",
"Local": "本地", "Local": "本地",
"Upstream": "上游仓库", "Upstream": "上游仓库",
"Author": "作者", "Author": "作者",
"Time": "提交时间", "Time": "提交时间",
"Message": "提交信息", "Message": "提交信息",
"DisabledWarn": "更新模块未启用,你需要手动重启 Alas 进行更新", "DisabledWarn": "更新模块未启用,你需要手动重启 AAS 进行更新",
"DetailedHistory": "详细提交历史" "DetailedHistory": "详细提交历史"
}, },
"Remote": { "Remote": {
+3 -2
View File
@@ -5,9 +5,10 @@ Use 'import module.config.server as server' to import, don't use 'from xxx impor
lang = 'jp' # Setting default to cn, will avoid errors when using dev_tools lang = 'jp' # Setting default to cn, will avoid errors when using dev_tools
server = 'JP-Official' server = 'JP-Official'
VALID_LANG = ['jp'] VALID_LANG = ['jp', 'en']
VALID_SERVER = { VALID_SERVER = {
'JP-Official': 'com.YostarJP.BlueArchive' 'JP-Official': 'com.YostarJP.BlueArchive',
'OVERSEA-Global': 'com.nexon.bluearchive',
} }
VALID_PACKAGE = set(list(VALID_SERVER.values())) VALID_PACKAGE = set(list(VALID_SERVER.values()))
+1
View File
@@ -13,6 +13,7 @@ from module.config.atomicwrites import atomic_write
LANGUAGES = ['zh-CN', 'en-US'] LANGUAGES = ['zh-CN', 'en-US']
SERVER_TO_TIMEZONE = { SERVER_TO_TIMEZONE = {
'JP-Official': timedelta(hours=9), 'JP-Official': timedelta(hours=9),
'OVERSEA-Global': timedelta(hours=0),
} }
DEFAULT_TIME = datetime(2020, 1, 1, 0, 0) DEFAULT_TIME = datetime(2020, 1, 1, 0, 0)
+3 -3
View File
@@ -863,8 +863,8 @@ class Connection(ConnectionAttr):
# Auto package detection # Auto package detection
if len(packages) == 0: if len(packages) == 0:
logger.critical(f'No Star Rail package found, ' logger.critical(f'No Blue Archive package found, '
f'please confirm Star Rail has been installed on device "{self.serial}"') f'please confirm Blue Archive has been installed on device "{self.serial}"')
raise RequestHumanTakeover raise RequestHumanTakeover
if len(packages) == 1: if len(packages) == 1:
logger.info('Auto package detection found only one package, using it') logger.info('Auto package detection found only one package, using it')
@@ -877,6 +877,6 @@ class Connection(ConnectionAttr):
# set_server(self.package) # set_server(self.package)
else: else:
logger.critical( logger.critical(
f'Multiple Star Rail packages found, auto package detection cannot decide which to choose, ' f'Multiple Blue Archive packages found, auto package detection cannot decide which to choose, '
'please copy one of the available devices listed above to Alas.Emulator.PackageName') 'please copy one of the available devices listed above to Alas.Emulator.PackageName')
raise RequestHumanTakeover raise RequestHumanTakeover
+1 -1
View File
@@ -172,7 +172,7 @@ class Control(Hermit, Minitouch, Scrcpy, MaaTouch):
self.click(ClickButton(button=area_offset(point_random, p2), name=name)) self.click(ClickButton(button=area_offset(point_random, p2), name=name))
# just used in cafe # just used in cafe
def pinch(self, box=(33, 130, 1247, 569), name='PINCH'): def pinch(self, box=(35, 130, 1250, 560), name='PINCH'):
self.handle_control_check(name) self.handle_control_check(name)
middle_point = (box[0] + box[2]) // 2, (box[1] + box[3]) // 2 middle_point = (box[0] + box[2]) // 2, (box[1] + box[3]) // 2
width = box[2] - middle_point[0] width = box[2] - middle_point[0]
+1 -1
View File
@@ -1110,7 +1110,7 @@ class AlasGUI(Frame):
""" """
AAS is a free open source software, if you paid for AAS from any channel, please refund. AAS is a free open source software, if you paid for AAS from any channel, please refund.
AAS 是一款免费开源软件,如果你在任何渠道付费购买了AAS,请退款。 AAS 是一款免费开源软件,如果你在任何渠道付费购买了AAS,请退款。
Project repository 项目地址:`https://github.com/TheFunny/ArisuAutoSrcipt` Project repository 项目地址:`https://github.com/TheFunny/ArisuAutoSweeper`
""" """
).style("text-align: center") ).style("text-align: center")
+1 -1
View File
@@ -376,7 +376,7 @@ def add_css(filepath):
def _read(path): def _read(path):
with open(path, "r") as f: with open(path, "r", encoding="utf-8") as f:
return f.read() return f.read()
+38
View File
@@ -12,6 +12,7 @@ ACCOUNT_INFO_CHECK = ButtonWrapper(
color=(193, 201, 210), color=(193, 201, 210),
button=(108, 11, 235, 36), button=(108, 11, 235, 36),
), ),
en=None,
) )
BACK = ButtonWrapper( BACK = ButtonWrapper(
name='BACK', name='BACK',
@@ -22,6 +23,7 @@ BACK = ButtonWrapper(
color=(93, 118, 164), color=(93, 118, 164),
button=(34, 19, 81, 56), button=(34, 19, 81, 56),
), ),
en=None,
) )
BOUNTY_CHECK = ButtonWrapper( BOUNTY_CHECK = ButtonWrapper(
name='BOUNTY_CHECK', name='BOUNTY_CHECK',
@@ -32,6 +34,7 @@ BOUNTY_CHECK = ButtonWrapper(
color=(150, 164, 177), color=(150, 164, 177),
button=(107, 10, 158, 37), button=(107, 10, 158, 37),
), ),
en=None,
) )
CAFE_CHECK = ButtonWrapper( CAFE_CHECK = ButtonWrapper(
name='CAFE_CHECK', name='CAFE_CHECK',
@@ -42,6 +45,7 @@ CAFE_CHECK = ButtonWrapper(
color=(188, 197, 205), color=(188, 197, 205),
button=(264, 11, 337, 35), button=(264, 11, 337, 35),
), ),
en=None,
) )
CIRCLE_CHECK = ButtonWrapper( CIRCLE_CHECK = ButtonWrapper(
name='CIRCLE_CHECK', name='CIRCLE_CHECK',
@@ -52,6 +56,7 @@ CIRCLE_CHECK = ButtonWrapper(
color=(195, 203, 211), color=(195, 203, 211),
button=(107, 11, 211, 36), button=(107, 11, 211, 36),
), ),
en=None,
) )
COMMISSIONS_CHECK = ButtonWrapper( COMMISSIONS_CHECK = ButtonWrapper(
name='COMMISSIONS_CHECK', name='COMMISSIONS_CHECK',
@@ -62,6 +67,7 @@ COMMISSIONS_CHECK = ButtonWrapper(
color=(146, 160, 174), color=(146, 160, 174),
button=(107, 10, 158, 37), button=(107, 10, 158, 37),
), ),
en=None,
) )
CRAFTING_CHECK = ButtonWrapper( CRAFTING_CHECK = ButtonWrapper(
name='CRAFTING_CHECK', name='CRAFTING_CHECK',
@@ -72,6 +78,7 @@ CRAFTING_CHECK = ButtonWrapper(
color=(200, 208, 215), color=(200, 208, 215),
button=(108, 10, 214, 37), button=(108, 10, 214, 37),
), ),
en=None,
) )
GACHA_CHECK = ButtonWrapper( GACHA_CHECK = ButtonWrapper(
name='GACHA_CHECK', name='GACHA_CHECK',
@@ -82,6 +89,7 @@ GACHA_CHECK = ButtonWrapper(
color=(135, 148, 164), color=(135, 148, 164),
button=(159, 10, 210, 37), button=(159, 10, 210, 37),
), ),
en=None,
) )
HOME = ButtonWrapper( HOME = ButtonWrapper(
name='HOME', name='HOME',
@@ -92,6 +100,7 @@ HOME = ButtonWrapper(
color=(168, 182, 205), color=(168, 182, 205),
button=(1218, 8, 1253, 41), button=(1218, 8, 1253, 41),
), ),
en=None,
) )
LOADING_CHECK = ButtonWrapper( LOADING_CHECK = ButtonWrapper(
name='LOADING_CHECK', name='LOADING_CHECK',
@@ -102,6 +111,7 @@ LOADING_CHECK = ButtonWrapper(
color=(173, 196, 219), color=(173, 196, 219),
button=(1084, 659, 1120, 674), button=(1084, 659, 1120, 674),
), ),
en=None,
) )
MAIL_CHECK = ButtonWrapper( MAIL_CHECK = ButtonWrapper(
name='MAIL_CHECK', name='MAIL_CHECK',
@@ -112,6 +122,7 @@ MAIL_CHECK = ButtonWrapper(
color=(205, 212, 219), color=(205, 212, 219),
button=(108, 9, 186, 37), button=(108, 9, 186, 37),
), ),
en=None,
) )
MAIN_GO_TO_CAFE = ButtonWrapper( MAIN_GO_TO_CAFE = ButtonWrapper(
name='MAIN_GO_TO_CAFE', name='MAIN_GO_TO_CAFE',
@@ -122,6 +133,7 @@ MAIN_GO_TO_CAFE = ButtonWrapper(
color=(156, 209, 233), color=(156, 209, 233),
button=(81, 638, 102, 668), button=(81, 638, 102, 668),
), ),
en=None,
) )
MAIN_GO_TO_CIRCLE = ButtonWrapper( MAIN_GO_TO_CIRCLE = ButtonWrapper(
name='MAIN_GO_TO_CIRCLE', name='MAIN_GO_TO_CIRCLE',
@@ -132,6 +144,7 @@ MAIN_GO_TO_CIRCLE = ButtonWrapper(
color=(131, 204, 234), color=(131, 204, 234),
button=(540, 631, 583, 660), button=(540, 631, 583, 660),
), ),
en=None,
) )
MAIN_GO_TO_CRAFTING = ButtonWrapper( MAIN_GO_TO_CRAFTING = ButtonWrapper(
name='MAIN_GO_TO_CRAFTING', name='MAIN_GO_TO_CRAFTING',
@@ -142,6 +155,7 @@ MAIN_GO_TO_CRAFTING = ButtonWrapper(
color=(192, 229, 241), color=(192, 229, 241),
button=(665, 622, 693, 664), button=(665, 622, 693, 664),
), ),
en=None,
) )
MAIN_GO_TO_GACHA = ButtonWrapper( MAIN_GO_TO_GACHA = ButtonWrapper(
name='MAIN_GO_TO_GACHA', name='MAIN_GO_TO_GACHA',
@@ -152,6 +166,7 @@ MAIN_GO_TO_GACHA = ButtonWrapper(
color=(157, 219, 241), color=(157, 219, 241),
button=(900, 623, 924, 670), button=(900, 623, 924, 670),
), ),
en=None,
) )
MAIN_GO_TO_MAIL = ButtonWrapper( MAIN_GO_TO_MAIL = ButtonWrapper(
name='MAIN_GO_TO_MAIL', name='MAIN_GO_TO_MAIL',
@@ -162,6 +177,7 @@ MAIN_GO_TO_MAIL = ButtonWrapper(
color=(94, 121, 166), color=(94, 121, 166),
button=(1130, 29, 1156, 49), button=(1130, 29, 1156, 49),
), ),
en=None,
) )
MAIN_GO_TO_MOMOTALK = ButtonWrapper( MAIN_GO_TO_MOMOTALK = ButtonWrapper(
name='MAIN_GO_TO_MOMOTALK', name='MAIN_GO_TO_MOMOTALK',
@@ -172,6 +188,7 @@ MAIN_GO_TO_MOMOTALK = ButtonWrapper(
color=(255, 219, 227), color=(255, 219, 227),
button=(154, 134, 177, 158), button=(154, 134, 177, 158),
), ),
en=None,
) )
MAIN_GO_TO_PURCHASE = ButtonWrapper( MAIN_GO_TO_PURCHASE = ButtonWrapper(
name='MAIN_GO_TO_PURCHASE', name='MAIN_GO_TO_PURCHASE',
@@ -182,6 +199,7 @@ MAIN_GO_TO_PURCHASE = ButtonWrapper(
color=(172, 214, 239), color=(172, 214, 239),
button=(148, 204, 183, 253), button=(148, 204, 183, 253),
), ),
en=None,
) )
MAIN_GO_TO_SCHEDULE = ButtonWrapper( MAIN_GO_TO_SCHEDULE = ButtonWrapper(
name='MAIN_GO_TO_SCHEDULE', name='MAIN_GO_TO_SCHEDULE',
@@ -192,6 +210,7 @@ MAIN_GO_TO_SCHEDULE = ButtonWrapper(
color=(149, 194, 222), color=(149, 194, 222),
button=(194, 638, 216, 672), button=(194, 638, 216, 672),
), ),
en=None,
) )
MAIN_GO_TO_SHOP = ButtonWrapper( MAIN_GO_TO_SHOP = ButtonWrapper(
name='MAIN_GO_TO_SHOP', name='MAIN_GO_TO_SHOP',
@@ -202,6 +221,7 @@ MAIN_GO_TO_SHOP = ButtonWrapper(
color=(146, 208, 235), color=(146, 208, 235),
button=(773, 630, 816, 667), button=(773, 630, 816, 667),
), ),
en=None,
) )
MAIN_GO_TO_TASK = ButtonWrapper( MAIN_GO_TO_TASK = ButtonWrapper(
name='MAIN_GO_TO_TASK', name='MAIN_GO_TO_TASK',
@@ -212,6 +232,7 @@ MAIN_GO_TO_TASK = ButtonWrapper(
color=(226, 207, 203), color=(226, 207, 203),
button=(52, 220, 78, 248), button=(52, 220, 78, 248),
), ),
en=None,
) )
MAIN_GO_TO_WORK = ButtonWrapper( MAIN_GO_TO_WORK = ButtonWrapper(
name='MAIN_GO_TO_WORK', name='MAIN_GO_TO_WORK',
@@ -222,6 +243,7 @@ MAIN_GO_TO_WORK = ButtonWrapper(
color=(135, 149, 169), color=(135, 149, 169),
button=(1167, 605, 1241, 632), button=(1167, 605, 1241, 632),
), ),
en=None,
) )
MISSION_CHECK = ButtonWrapper( MISSION_CHECK = ButtonWrapper(
name='MISSION_CHECK', name='MISSION_CHECK',
@@ -232,6 +254,7 @@ MISSION_CHECK = ButtonWrapper(
color=(188, 197, 206), color=(188, 197, 206),
button=(108, 12, 183, 36), button=(108, 12, 183, 36),
), ),
en=None,
) )
MOMOTALK_CHECK = ButtonWrapper( MOMOTALK_CHECK = ButtonWrapper(
name='MOMOTALK_CHECK', name='MOMOTALK_CHECK',
@@ -242,6 +265,7 @@ MOMOTALK_CHECK = ButtonWrapper(
color=(253, 211, 219), color=(253, 211, 219),
button=(144, 107, 169, 130), button=(144, 107, 169, 130),
), ),
en=None,
) )
MOMOTALK_GO_TO_MAIN = ButtonWrapper( MOMOTALK_GO_TO_MAIN = ButtonWrapper(
name='MOMOTALK_GO_TO_MAIN', name='MOMOTALK_GO_TO_MAIN',
@@ -252,6 +276,7 @@ MOMOTALK_GO_TO_MAIN = ButtonWrapper(
color=(252, 182, 194), color=(252, 182, 194),
button=(1108, 105, 1134, 131), button=(1108, 105, 1134, 131),
), ),
en=None,
) )
SCHEDULE_CHECK = ButtonWrapper( SCHEDULE_CHECK = ButtonWrapper(
name='SCHEDULE_CHECK', name='SCHEDULE_CHECK',
@@ -262,6 +287,7 @@ SCHEDULE_CHECK = ButtonWrapper(
color=(188, 197, 206), color=(188, 197, 206),
button=(108, 12, 159, 36), button=(108, 12, 159, 36),
), ),
en=None,
) )
SCHOOL_EXCHANGE_CHECK = ButtonWrapper( SCHOOL_EXCHANGE_CHECK = ButtonWrapper(
name='SCHOOL_EXCHANGE_CHECK', name='SCHOOL_EXCHANGE_CHECK',
@@ -272,6 +298,7 @@ SCHOOL_EXCHANGE_CHECK = ButtonWrapper(
color=(134, 149, 164), color=(134, 149, 164),
button=(107, 11, 158, 36), button=(107, 11, 158, 36),
), ),
en=None,
) )
SHOP_CHECK = ButtonWrapper( SHOP_CHECK = ButtonWrapper(
name='SHOP_CHECK', name='SHOP_CHECK',
@@ -282,6 +309,7 @@ SHOP_CHECK = ButtonWrapper(
color=(200, 208, 215), color=(200, 208, 215),
button=(108, 10, 212, 36), button=(108, 10, 212, 36),
), ),
en=None,
) )
STORY_CHECK = ButtonWrapper( STORY_CHECK = ButtonWrapper(
name='STORY_CHECK', name='STORY_CHECK',
@@ -292,6 +320,7 @@ STORY_CHECK = ButtonWrapper(
color=(194, 203, 211), color=(194, 203, 211),
button=(108, 11, 157, 36), button=(108, 11, 157, 36),
), ),
en=None,
) )
TACTICAL_CHALLENGE_CHECK = ButtonWrapper( TACTICAL_CHALLENGE_CHECK = ButtonWrapper(
name='TACTICAL_CHALLENGE_CHECK', name='TACTICAL_CHALLENGE_CHECK',
@@ -302,6 +331,7 @@ TACTICAL_CHALLENGE_CHECK = ButtonWrapper(
color=(129, 145, 161), color=(129, 145, 161),
button=(107, 11, 133, 37), button=(107, 11, 133, 37),
), ),
en=None,
) )
TASK_CHECK = ButtonWrapper( TASK_CHECK = ButtonWrapper(
name='TASK_CHECK', name='TASK_CHECK',
@@ -312,6 +342,7 @@ TASK_CHECK = ButtonWrapper(
color=(189, 198, 207), color=(189, 198, 207),
button=(109, 12, 155, 36), button=(109, 12, 155, 36),
), ),
en=None,
) )
WORK_CHECK = ButtonWrapper( WORK_CHECK = ButtonWrapper(
name='WORK_CHECK', name='WORK_CHECK',
@@ -322,6 +353,7 @@ WORK_CHECK = ButtonWrapper(
color=(164, 175, 187), color=(164, 175, 187),
button=(107, 10, 185, 38), button=(107, 10, 185, 38),
), ),
en=None,
) )
WORK_GO_TO_BOUNTY = ButtonWrapper( WORK_GO_TO_BOUNTY = ButtonWrapper(
name='WORK_GO_TO_BOUNTY', name='WORK_GO_TO_BOUNTY',
@@ -332,6 +364,7 @@ WORK_GO_TO_BOUNTY = ButtonWrapper(
color=(165, 181, 208), color=(165, 181, 208),
button=(669, 412, 765, 436), button=(669, 412, 765, 436),
), ),
en=None,
) )
WORK_GO_TO_COMMISSIONS = ButtonWrapper( WORK_GO_TO_COMMISSIONS = ButtonWrapper(
name='WORK_GO_TO_COMMISSIONS', name='WORK_GO_TO_COMMISSIONS',
@@ -342,6 +375,7 @@ WORK_GO_TO_COMMISSIONS = ButtonWrapper(
color=(165, 179, 204), color=(165, 179, 204),
button=(655, 494, 751, 518), button=(655, 494, 751, 518),
), ),
en=None,
) )
WORK_GO_TO_MISSION = ButtonWrapper( WORK_GO_TO_MISSION = ButtonWrapper(
name='WORK_GO_TO_MISSION', name='WORK_GO_TO_MISSION',
@@ -352,6 +386,7 @@ WORK_GO_TO_MISSION = ButtonWrapper(
color=(165, 178, 204), color=(165, 178, 204),
button=(720, 160, 803, 199), button=(720, 160, 803, 199),
), ),
en=None,
) )
WORK_GO_TO_SCHOOL_EXCHANGE = ButtonWrapper( WORK_GO_TO_SCHOOL_EXCHANGE = ButtonWrapper(
name='WORK_GO_TO_SCHOOL_EXCHANGE', name='WORK_GO_TO_SCHOOL_EXCHANGE',
@@ -362,6 +397,7 @@ WORK_GO_TO_SCHOOL_EXCHANGE = ButtonWrapper(
color=(165, 179, 204), color=(165, 179, 204),
button=(641, 575, 758, 599), button=(641, 575, 758, 599),
), ),
en=None,
) )
WORK_GO_TO_STORY = ButtonWrapper( WORK_GO_TO_STORY = ButtonWrapper(
name='WORK_GO_TO_STORY', name='WORK_GO_TO_STORY',
@@ -372,6 +408,7 @@ WORK_GO_TO_STORY = ButtonWrapper(
color=(191, 201, 219), color=(191, 201, 219),
button=(995, 163, 1032, 197), button=(995, 163, 1032, 197),
), ),
en=None,
) )
WORK_GO_TO_TACTICAL_CHALLENGE = ButtonWrapper( WORK_GO_TO_TACTICAL_CHALLENGE = ButtonWrapper(
name='WORK_GO_TO_TACTICAL_CHALLENGE', name='WORK_GO_TO_TACTICAL_CHALLENGE',
@@ -382,4 +419,5 @@ WORK_GO_TO_TACTICAL_CHALLENGE = ButtonWrapper(
color=(159, 174, 200), color=(159, 174, 200),
button=(1012, 535, 1151, 562), button=(1012, 535, 1151, 562),
), ),
en=None,
) )
+43 -3
View File
@@ -12,6 +12,18 @@ AFFECTION_LEVEL_UP = ButtonWrapper(
color=(208, 223, 243), color=(208, 223, 243),
button=(882, 244, 1176, 476), button=(882, 244, 1176, 476),
), ),
en=None,
)
AP_EXCEED = ButtonWrapper(
name='AP_EXCEED',
jp=Button(
file='./assets/jp/base/popup/AP_EXCEED.png',
area=(610, 124, 669, 153),
search=(590, 104, 689, 173),
color=(139, 153, 168),
button=(535, 494, 746, 565),
),
en=None,
) )
DAILY_NEWS = ButtonWrapper( DAILY_NEWS = ButtonWrapper(
name='DAILY_NEWS', name='DAILY_NEWS',
@@ -22,16 +34,18 @@ DAILY_NEWS = ButtonWrapper(
color=(150, 204, 253), color=(150, 204, 253),
button=(1128, 89, 1156, 117), button=(1128, 89, 1156, 117),
), ),
en=None,
) )
DAILY_REWARD = ButtonWrapper( DAILY_REWARD = ButtonWrapper(
name='DAILY_REWARD', name='DAILY_REWARD',
jp=Button( jp=Button(
file='./assets/jp/base/popup/DAILY_REWARD.png', file='./assets/jp/base/popup/DAILY_REWARD.png',
area=(854, 117, 1008, 165), area=(416, 165, 434, 216),
search=(834, 97, 1028, 185), search=(396, 145, 454, 236),
color=(178, 167, 112), color=(203, 227, 237),
button=(920, 632, 1140, 712), button=(920, 632, 1140, 712),
), ),
en=None,
) )
GET_NEW_STUDENT = ButtonWrapper( GET_NEW_STUDENT = ButtonWrapper(
name='GET_NEW_STUDENT', name='GET_NEW_STUDENT',
@@ -42,6 +56,7 @@ GET_NEW_STUDENT = ButtonWrapper(
color=(125, 132, 92), color=(125, 132, 92),
button=(934, 643, 1263, 714), button=(934, 643, 1263, 714),
), ),
en=None,
) )
GET_REWARD = ButtonWrapper( GET_REWARD = ButtonWrapper(
name='GET_REWARD', name='GET_REWARD',
@@ -52,6 +67,7 @@ GET_REWARD = ButtonWrapper(
color=(230, 222, 93), color=(230, 222, 93),
button=(675, 623, 870, 695), button=(675, 623, 870, 695),
), ),
en=None,
) )
GET_REWARD_SKIP = ButtonWrapper( GET_REWARD_SKIP = ButtonWrapper(
name='GET_REWARD_SKIP', name='GET_REWARD_SKIP',
@@ -62,6 +78,18 @@ GET_REWARD_SKIP = ButtonWrapper(
color=(197, 200, 205), color=(197, 200, 205),
button=(1137, 34, 1243, 65), button=(1137, 34, 1243, 65),
), ),
en=None,
)
ITEM_EXPIRED = ButtonWrapper(
name='ITEM_EXPIRED',
jp=Button(
file='./assets/jp/base/popup/ITEM_EXPIRED.png',
area=(612, 147, 668, 175),
search=(592, 127, 688, 195),
color=(131, 145, 162),
button=(537, 487, 742, 553),
),
en=None,
) )
NETWORK_RECONNECT = ButtonWrapper( NETWORK_RECONNECT = ButtonWrapper(
name='NETWORK_RECONNECT', name='NETWORK_RECONNECT',
@@ -72,4 +100,16 @@ NETWORK_RECONNECT = ButtonWrapper(
color=(78, 138, 169), color=(78, 138, 169),
button=(663, 467, 870, 537), button=(663, 467, 870, 537),
), ),
en=None,
)
NETWORK_RECONNECT_OK = ButtonWrapper(
name='NETWORK_RECONNECT_OK',
jp=Button(
file='./assets/jp/base/popup/NETWORK_RECONNECT_OK.png',
area=(744, 487, 791, 515),
search=(724, 467, 811, 535),
color=(91, 165, 196),
button=(665, 468, 870, 536),
),
en=None,
) )
+32 -22
View File
@@ -5,7 +5,7 @@ from tasks.base.assets.assets_base_page import LOADING_CHECK
class PopupHandler(ModuleBase): class PopupHandler(ModuleBase):
def handle_loading(self, interval=5) -> bool: def handle_loading(self) -> bool:
""" """
Args: Args:
interval: interval:
@@ -13,7 +13,7 @@ class PopupHandler(ModuleBase):
Returns: Returns:
If handled. If handled.
""" """
if self.appear(LOADING_CHECK, interval=interval): if self.appear(LOADING_CHECK):
timer = Timer(0.5).start() timer = Timer(0.5).start()
while 1: while 1:
if timer.reached_and_reset(): if timer.reached_and_reset():
@@ -27,7 +27,7 @@ class PopupHandler(ModuleBase):
return False return False
def handle_reward(self, interval=5) -> bool: def handle_reward(self) -> bool:
""" """
Args: Args:
interval: interval:
@@ -35,15 +35,13 @@ class PopupHandler(ModuleBase):
Returns: Returns:
If handled. If handled.
""" """
if self.appear_then_click(GET_REWARD, interval=interval): if self.appear(GET_REWARD) or self.match_color(GET_REWARD, threshold=30):
timer = Timer(0.2).start()
while 1: while 1:
if timer.reached_and_reset(): self.device.screenshot()
self.device.screenshot() if self.appear(GET_REWARD) or self.match_color(GET_REWARD, threshold=30):
if self.appear(GET_REWARD): self.click_with_interval(GET_REWARD, interval=0.5)
self.device.click(GET_REWARD) else:
else: break
break
return True return True
return False return False
@@ -52,13 +50,13 @@ class PopupHandler(ModuleBase):
if self.appear_then_click(GET_REWARD_SKIP, interval=interval): if self.appear_then_click(GET_REWARD_SKIP, interval=interval):
return True return True
def handle_daily_news(self, interval=5) -> bool: def handle_daily_news(self, interval=2) -> bool:
if self.appear_then_click(DAILY_NEWS, interval=interval): if self.appear_then_click(DAILY_NEWS, interval=interval):
return True return True
return False return False
def handle_daily_reward(self, interval=5) -> bool: def handle_daily_reward(self, interval=2) -> bool:
if self.appear_then_click(DAILY_REWARD, interval=interval): if self.appear_then_click(DAILY_REWARD, interval=interval):
return True return True
@@ -67,19 +65,19 @@ class PopupHandler(ModuleBase):
def handle_network_reconnect(self, interval=5) -> bool: def handle_network_reconnect(self, interval=5) -> bool:
if self.appear_then_click(NETWORK_RECONNECT, interval=interval): if self.appear_then_click(NETWORK_RECONNECT, interval=interval):
return True return True
if self.appear_then_click(NETWORK_RECONNECT_OK, interval=interval):
return True
return False return False
def handle_affection_level_up(self, interval=5) -> bool: def handle_affection_level_up(self) -> bool:
if self.appear_then_click(AFFECTION_LEVEL_UP, interval=interval): if self.appear_then_click(AFFECTION_LEVEL_UP):
timer = Timer(0.2).start()
while 1: while 1:
if timer.reached_and_reset(): self.device.screenshot()
self.device.screenshot() if self.appear(AFFECTION_LEVEL_UP):
if self.appear(AFFECTION_LEVEL_UP): self.click_with_interval(AFFECTION_LEVEL_UP, interval=1)
self.device.click(AFFECTION_LEVEL_UP) else:
else: break
break
return True return True
return False return False
@@ -89,3 +87,15 @@ class PopupHandler(ModuleBase):
return True return True
return False return False
def handle_ap_exceed(self, interval=5) -> bool:
if self.appear_then_click(AP_EXCEED, interval=interval):
return True
return False
def handle_item_expired(self, interval=5) -> bool:
if self.appear_then_click(ITEM_EXPIRED, interval=interval):
return True
return False
+8 -2
View File
@@ -49,6 +49,7 @@ class UI(MainPage):
self.device.get_orientation() self.device.get_orientation()
timeout = Timer(10, count=20).start() timeout = Timer(10, count=20).start()
back_timer = Timer(0.5, count=2)
while 1: while 1:
if skip_first_screenshot: if skip_first_screenshot:
skip_first_screenshot = False skip_first_screenshot = False
@@ -76,8 +77,9 @@ class UI(MainPage):
logger.info("Additional ui page handled") logger.info("Additional ui page handled")
timeout.reset() timeout.reset()
continue continue
logger.info("May be in standby main page") if back_timer.reached_and_reset():
self.device.click(BACK) logger.info("Unknown page, try to back")
self.device.click(BACK)
app_check() app_check()
minicap_check() minicap_check()
@@ -341,6 +343,10 @@ class UI(MainPage):
return True return True
if self.handle_new_student(): if self.handle_new_student():
return True return True
if self.handle_ap_exceed():
return True
if self.handle_item_expired():
return True
return False return False
+86 -12
View File
@@ -12,6 +12,51 @@ BOX_CAFE = ButtonWrapper(
color=(175, 181, 186), color=(175, 181, 186),
button=(33, 130, 1247, 569), button=(33, 130, 1247, 569),
), ),
en=None,
)
CAFE_FIRST = ButtonWrapper(
name='CAFE_FIRST',
jp=Button(
file='./assets/jp/cafe/CAFE_FIRST.png',
area=(82, 152, 136, 175),
search=(62, 132, 156, 195),
color=(111, 127, 147),
button=(82, 152, 136, 175),
),
en=None,
)
CAFE_SECOND = ButtonWrapper(
name='CAFE_SECOND',
jp=Button(
file='./assets/jp/cafe/CAFE_SECOND.png',
area=(219, 152, 279, 175),
search=(199, 132, 299, 195),
color=(110, 126, 146),
button=(219, 152, 279, 175),
),
en=None,
)
CHANGE_CAFE_NOT_SELECTED = ButtonWrapper(
name='CHANGE_CAFE_NOT_SELECTED',
jp=Button(
file='./assets/jp/cafe/CHANGE_CAFE_NOT_SELECTED.png',
area=(84, 89, 178, 109),
search=(64, 69, 198, 129),
color=(185, 193, 203),
button=(84, 89, 178, 109),
),
en=None,
)
CHANGE_CAFE_SELECTED = ButtonWrapper(
name='CHANGE_CAFE_SELECTED',
jp=Button(
file='./assets/jp/cafe/CHANGE_CAFE_SELECTED.png',
area=(40, 87, 191, 112),
search=(20, 67, 211, 132),
color=(82, 105, 130),
button=(40, 87, 191, 112),
),
en=None,
) )
CHECK_REWARD = ButtonWrapper( CHECK_REWARD = ButtonWrapper(
name='CHECK_REWARD', name='CHECK_REWARD',
@@ -22,16 +67,18 @@ CHECK_REWARD = ButtonWrapper(
color=(82, 105, 129), color=(82, 105, 129),
button=(1086, 607, 1225, 685), button=(1086, 607, 1225, 685),
), ),
en=None,
) )
CLICKABLE_TEMPLATE = ButtonWrapper( CLICKABLE_TEMPLATE = ButtonWrapper(
name='CLICKABLE_TEMPLATE', name='CLICKABLE_TEMPLATE',
jp=Button( jp=Button(
file='./assets/jp/cafe/CLICKABLE_TEMPLATE.png', file='./assets/jp/cafe/CLICKABLE_TEMPLATE.png',
area=(0, 0, 42, 56), area=(619, 332, 660, 387),
search=(0, 0, 62, 76), search=(599, 312, 680, 407),
color=(77, 63, 1), color=(79, 65, 1),
button=(0, 0, 42, 56), button=(619, 332, 660, 387),
), ),
en=None,
) )
GET_REWARD = ButtonWrapper( GET_REWARD = ButtonWrapper(
name='GET_REWARD', name='GET_REWARD',
@@ -42,26 +89,51 @@ GET_REWARD = ButtonWrapper(
color=(159, 133, 48), color=(159, 133, 48),
button=(546, 494, 735, 563), button=(546, 494, 735, 563),
), ),
en=None,
) )
GET_REWARD_CLOSE = ButtonWrapper( GET_REWARD_CLOSE = ButtonWrapper(
name='GET_REWARD_CLOSE', name='GET_REWARD_CLOSE',
jp=Button( jp=Button(
file='./assets/jp/cafe/GET_REWARD_CLOSE.png', file='./assets/jp/cafe/GET_REWARD_CLOSE.png',
area=(891, 144, 917, 170), area=(970, 134, 996, 160),
search=(871, 124, 937, 190), search=(950, 114, 1016, 180),
color=(173, 179, 189), color=(172, 179, 188),
button=(891, 144, 917, 170), button=(970, 134, 996, 160),
), ),
en=None,
) )
GOT_REWARD = ButtonWrapper( GOT_REWARD = ButtonWrapper(
name='GOT_REWARD', name='GOT_REWARD',
jp=Button( jp=Button(
file='./assets/jp/cafe/GOT_REWARD.png', file='./assets/jp/cafe/GOT_REWARD.png',
area=(609, 507, 672, 535), area=(561, 501, 718, 556),
search=(589, 487, 692, 555), search=(541, 481, 738, 576),
color=(174, 175, 174), color=(209, 210, 209),
button=(609, 507, 672, 535), button=(561, 501, 718, 556),
), ),
en=None,
)
INVENTORY = ButtonWrapper(
name='INVENTORY',
jp=Button(
file='./assets/jp/cafe/INVENTORY.png',
area=(553, 94, 725, 122),
search=(533, 74, 745, 142),
color=(198, 205, 213),
button=(1130, 94, 1156, 120),
),
en=None,
)
MOMOTALK_CLOSE = ButtonWrapper(
name='MOMOTALK_CLOSE',
jp=Button(
file='./assets/jp/cafe/MOMOTALK_CLOSE.png',
area=(824, 82, 850, 108),
search=(804, 62, 870, 128),
color=(252, 182, 194),
button=(824, 82, 850, 108),
),
en=None,
) )
OCR_CAFE = ButtonWrapper( OCR_CAFE = ButtonWrapper(
name='OCR_CAFE', name='OCR_CAFE',
@@ -72,6 +144,7 @@ OCR_CAFE = ButtonWrapper(
color=(87, 107, 129), color=(87, 107, 129),
button=(1103, 642, 1202, 672), button=(1103, 642, 1202, 672),
), ),
en=None,
) )
STUDENT_LIST = ButtonWrapper( STUDENT_LIST = ButtonWrapper(
name='STUDENT_LIST', name='STUDENT_LIST',
@@ -82,4 +155,5 @@ STUDENT_LIST = ButtonWrapper(
color=(145, 157, 172), color=(145, 157, 172),
button=(545, 423, 738, 495), button=(545, 423, 738, 495),
), ),
en=None,
) )
+88 -25
View File
@@ -5,13 +5,24 @@ from enum import Enum
from module.logger import logger from module.logger import logger
from module.base.timer import Timer from module.base.timer import Timer
from module.base.button import ClickButton from module.base.button import ClickButton
from module.base.decorator import Config
from module.base.utils.utils import area_offset from module.base.utils.utils import area_offset
from module.ocr.ocr import Digit from module.ocr.ocr import Digit
from module.ui.switch import Switch
from tasks.base.page import page_cafe from tasks.base.page import page_cafe
from tasks.base.ui import UI from tasks.base.ui import UI
from tasks.cafe.assets.assets_cafe import * from tasks.cafe.assets.assets_cafe import *
SWITCH_CAFE = Switch('Cafe_switch')
SWITCH_CAFE.add_state('off', CHANGE_CAFE_NOT_SELECTED)
SWITCH_CAFE.add_state('on', CHANGE_CAFE_SELECTED)
SWITCH_CAFE_SELECT = Switch('Cafe_switch_select')
SWITCH_CAFE_SELECT.add_state('1', CAFE_FIRST)
SWITCH_CAFE_SELECT.add_state('2', CAFE_SECOND)
class CafeStatus(Enum): class CafeStatus(Enum):
STUDENT_LIST = 0 STUDENT_LIST = 0
OCR = 1 OCR = 1
@@ -23,6 +34,8 @@ class CafeStatus(Enum):
class Cafe(UI): class Cafe(UI):
template = CLICKABLE_TEMPLATE
@staticmethod @staticmethod
def merge_points(points, threshold=3): def merge_points(points, threshold=3):
if len(points) <= 1: if len(points) <= 1:
@@ -44,34 +57,35 @@ class Cafe(UI):
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
# set color range # set color range
lower_hsv = np.array([17, 200, 220]) lower_hsv = np.array([18, 200, 220])
upper_hsv = np.array([28, 255, 255]) upper_hsv = np.array([30, 255, 255])
# get mask # get mask
mask = cv2.inRange(hsv, lower_hsv, upper_hsv) mask = cv2.inRange(hsv, lower_hsv, upper_hsv)
# generate result # generate result
return cv2.bitwise_and(image, image, mask=mask) return cv2.bitwise_and(image, image, mask=mask)
def _match_clickable_points(self, image, threshold=0.9): def _match_clickable_points(self, image, threshold=0.8):
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
template = self.btn.matched_button.image template = cv2.cvtColor(self.template.matched_button.image, cv2.COLOR_BGR2GRAY)
template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED) res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)
loc = np.where(res >= threshold) loc = np.where(res >= threshold)
return [point for point in zip(*loc[::-1])] return [point for point in zip(*loc[::-1])]
def _get_clickable_buttons(self, threshold=0.9, offset=(0, 0)): def _get_clickable_buttons(self, threshold=0.8, offset=(0, 0)):
image = cv2.copyMakeBorder(self.device.image, 20, 20, 10, 60, cv2.BORDER_CONSTANT, value=(0, 0, 0)) image = self.device.image
h, w = image.shape[:2]
image = cv2.rectangle(image, (0, 10), (w - 25, h - 10), (0, 0, 0), 50)
image = self._extract_clickable_from_image(image) image = self._extract_clickable_from_image(image)
points = self._match_clickable_points(image, threshold) points = self._match_clickable_points(image, threshold)
points = self.merge_points(points) points = self.merge_points(points)
if not points: if not points:
return [] return []
area = area_offset((0, 0, self.btn.width, self.btn.height), offset) area = area_offset((0, 0, self.template.width, self.template.height), offset)
return [ return [
ClickButton( ClickButton(
button=area_offset(area, offset=point), button=area_offset(area, offset=point),
name=self.btn.name name=self.template.name
) )
for point in points for point in points
] ]
@@ -98,6 +112,8 @@ class Cafe(UI):
case 'right': case 'right':
self.device.swipe_vector(vector_right, box=BOX_CAFE.area, random_range=random_r, padding=5) self.device.swipe_vector(vector_right, box=BOX_CAFE.area, random_range=random_r, padding=5)
self.device.swipe_vector(vector_right, box=BOX_CAFE.area, random_range=random_r, padding=5) self.device.swipe_vector(vector_right, box=BOX_CAFE.area, random_range=random_r, padding=5)
# solve too much swipe causing restart
self.device.click_record_clear()
def _get_reward_num(self): def _get_reward_num(self):
ocr = Digit(OCR_CAFE) ocr = Digit(OCR_CAFE)
@@ -108,6 +124,13 @@ class Cafe(UI):
logger.attr('Reward', num) logger.attr('Reward', num)
return num return num
def _cafe_additional(self) -> bool:
if self.appear_then_click(INVENTORY):
return True
if self.appear_then_click(MOMOTALK_CLOSE):
return True
return False
def _handle_cafe(self, status): def _handle_cafe(self, status):
match status: match status:
case CafeStatus.STUDENT_LIST: case CafeStatus.STUDENT_LIST:
@@ -121,24 +144,26 @@ class Cafe(UI):
if self.appear_then_click(CHECK_REWARD): if self.appear_then_click(CHECK_REWARD):
return CafeStatus.REWARD return CafeStatus.REWARD
case CafeStatus.REWARD: case CafeStatus.REWARD:
if not self.appear(GET_REWARD_CLOSE):
self.click_with_interval(CHECK_REWARD)
return status
if self.match_color(GOT_REWARD): if self.match_color(GOT_REWARD):
self.device.click(GET_REWARD_CLOSE) self.device.click(GET_REWARD_CLOSE)
return CafeStatus.GOT return CafeStatus.GOT
if not self.appear(GET_REWARD):
return CafeStatus.OCR
if self.match_color(GET_REWARD): if self.match_color(GET_REWARD):
self.device.click(GET_REWARD) self.click_with_interval(GET_REWARD)
case CafeStatus.GOT: case CafeStatus.GOT:
logger.info('Cafe reward have been got') logger.info('Cafe reward have been got')
self.appear_then_click(GET_REWARD_CLOSE) self.appear_then_click(GET_REWARD_CLOSE)
return CafeStatus.CLICK if not self.appear(GET_REWARD_CLOSE):
return CafeStatus.CLICK
case CafeStatus.CLICK: case CafeStatus.CLICK:
buttons = self._get_clickable_buttons(offset=(45, 10)) buttons = self._get_clickable_buttons(offset=(45, 10))
self.click = len(buttons) self.click = len(buttons)
logger.attr('Clickable', self.click) logger.attr('Clickable', self.click)
if not buttons: if not buttons:
return CafeStatus.CHECK return CafeStatus.CHECK
self.device.click(buttons[0]) self.click_with_interval(buttons[0], interval=1)
case CafeStatus.CHECK: case CafeStatus.CHECK:
buttons = self._get_clickable_buttons() buttons = self._get_clickable_buttons()
if not self.is_adjust_on: if not self.is_adjust_on:
@@ -166,8 +191,17 @@ class Cafe(UI):
logger.warning(f'Invalid status: {status}') logger.warning(f'Invalid status: {status}')
return status return status
@Config.when(Emulator_GameLanguage='jp')
def is_second_cafe_on(self):
return self.config.Cafe_SecondCafe
@Config.when(Emulator_GameLanguage=None)
def is_second_cafe_on(self):
return False
is_second_cafe_on = property(is_second_cafe_on)
def run(self): def run(self):
self.btn = CLICKABLE_TEMPLATE
self.click = 0 self.click = 0
self.check = 0 self.check = 0
@@ -179,16 +213,19 @@ class Cafe(UI):
status = CafeStatus.STUDENT_LIST status = CafeStatus.STUDENT_LIST
loading_timer = Timer(2).start() loading_timer = Timer(2).start()
action_timer = Timer(1.5, count=1) # cant be too fast action_timer = Timer(1, count=1) # cant be too fast
check_timer = Timer(1, count=1)
is_list = False is_list = False
is_reset = False is_reset = False
is_second = False
is_enable = is_reward_on or is_touch_on is_enable = is_reward_on or is_touch_on
while is_enable: while 1:
if not is_enable:
break
self.device.screenshot() self.device.screenshot()
if self.ui_additional(): if self.ui_additional() or self._cafe_additional():
continue continue
if not loading_timer.reached(): if not loading_timer.reached():
@@ -196,7 +233,7 @@ class Cafe(UI):
if not is_list and status == CafeStatus.STUDENT_LIST and self.appear(STUDENT_LIST): if not is_list and status == CafeStatus.STUDENT_LIST and self.appear(STUDENT_LIST):
is_list = True is_list = True
loading_timer = Timer(5).start() loading_timer = Timer(3).start()
continue continue
if not is_reward_on and status == CafeStatus.OCR: if not is_reward_on and status == CafeStatus.OCR:
@@ -214,14 +251,40 @@ class Cafe(UI):
is_reset = True is_reset = True
continue continue
if status == CafeStatus.CHECK and not check_timer.reached_and_reset(): if self.is_second_cafe_on and not is_second and status == CafeStatus.FINISHED:
continue if not SWITCH_CAFE.appear(main=self):
logger.warning('Cafe switch not found')
continue
if SWITCH_CAFE.get(main=self) == 'off':
SWITCH_CAFE.set('on', main=self)
logger.info('Switching to second cafe')
if not SWITCH_CAFE_SELECT.appear(main=self):
logger.info('Cafe switch select not found')
continue
match (SWITCH_CAFE_SELECT.get(main=self)):
case '1':
if self.click_with_interval(CAFE_SECOND):
continue
case '2':
logger.info('Cafe second arrived')
SWITCH_CAFE.set('off', main=self)
status = CafeStatus.STUDENT_LIST
is_list = False
is_second = True
self.check = 0
if action_timer.reached_and_reset(): if action_timer.reached_and_reset():
status = self._handle_cafe(status)
logger.attr('Status', status) logger.attr('Status', status)
status = self._handle_cafe(status)
if status is CafeStatus.FINISHED: if not self.is_second_cafe_on:
break if status is CafeStatus.FINISHED:
logger.info('Second cafe is not supported or disabled')
logger.info('Cafe finished')
break
else:
if is_second and status is CafeStatus.FINISHED:
logger.info('Cafe finished')
break
self.config.task_delay(server_update=True, minute=180) self.config.task_delay(server_update=True, minute=180)
+1
View File
@@ -12,4 +12,5 @@ GET_REWARD_AP = ButtonWrapper(
color=(198, 206, 213), color=(198, 206, 213),
button=(543, 457, 735, 524), button=(543, 457, 735, 524),
), ),
en=None,
) )
+1 -1
View File
@@ -47,8 +47,8 @@ class Circle(UI):
status = CircleStatus.GOT status = CircleStatus.GOT
if action_timer.reached_and_reset(): if action_timer.reached_and_reset():
status = self._handle_circle(status)
logger.attr('Status', status) logger.attr('Status', status)
status = self._handle_circle(status)
if status is CircleStatus.FINISHED: if status is CircleStatus.FINISHED:
break break
+13
View File
@@ -12,6 +12,7 @@ LOGIN_CONFIRM = ButtonWrapper(
color=(211, 215, 220), color=(211, 215, 220),
button=(990, 354, 1232, 506), button=(990, 354, 1232, 506),
), ),
en=None,
) )
LOGIN_LOADING = ButtonWrapper( LOGIN_LOADING = ButtonWrapper(
name='LOGIN_LOADING', name='LOGIN_LOADING',
@@ -22,4 +23,16 @@ LOGIN_LOADING = ButtonWrapper(
color=(8, 66, 96), color=(8, 66, 96),
button=(34, 682, 60, 707), button=(34, 682, 60, 707),
), ),
en=None,
)
UPDATE = ButtonWrapper(
name='UPDATE',
jp=Button(
file='./assets/jp/login/UPDATE.png',
area=(526, 87, 756, 117),
search=(506, 67, 776, 137),
color=(183, 192, 201),
button=(666, 527, 872, 595),
),
en=None,
) )
+3 -2
View File
@@ -3,8 +3,7 @@ from module.exception import GameNotRunningError
from module.logger import logger from module.logger import logger
from tasks.base.page import page_main from tasks.base.page import page_main
from tasks.base.ui import UI from tasks.base.ui import UI
# from tasks.login.assets.assets_login import LOGIN_CONFIRM, USER_AGREEMENT_ACCEPT, LOGIN_LOADING from tasks.login.assets.assets_login import LOGIN_CONFIRM, LOGIN_LOADING, UPDATE
from tasks.login.assets.assets_login import LOGIN_CONFIRM, LOGIN_LOADING
class Login(UI): class Login(UI):
@@ -77,6 +76,8 @@ class Login(UI):
# continue # continue
# if self.handle_popup_confirm(): # if self.handle_popup_confirm():
# continue # continue
if self.appear_then_click(UPDATE):
continue
if self.ui_additional(): if self.ui_additional():
continue continue
+2
View File
@@ -12,6 +12,7 @@ MAIL_RECEIVE = ButtonWrapper(
color=(174, 151, 53), color=(174, 151, 53),
button=(1043, 640, 1226, 701), button=(1043, 640, 1226, 701),
), ),
en=None,
) )
MAIL_RECEIVED = ButtonWrapper( MAIL_RECEIVED = ButtonWrapper(
name='MAIL_RECEIVED', name='MAIL_RECEIVED',
@@ -22,4 +23,5 @@ MAIL_RECEIVED = ButtonWrapper(
color=(195, 196, 196), color=(195, 196, 196),
button=(1085, 657, 1186, 685), button=(1085, 657, 1186, 685),
), ),
en=None,
) )
+1
View File
@@ -12,4 +12,5 @@ SCROLL = ButtonWrapper(
color=(198, 214, 210), color=(198, 214, 210),
button=(742, 136, 1101, 671), button=(742, 136, 1101, 671),
), ),
en=None,
) )
+148
View File
@@ -0,0 +1,148 @@
from module.base.button import Button, ButtonWrapper
# This file was auto-generated, do not modify it manually. To generate:
# ``` python -m dev_tools.button_extract ```
CHECK_SWEEP = ButtonWrapper(
name='CHECK_SWEEP',
jp=Button(
file='./assets/jp/stage/sweep/CHECK_SWEEP.png',
area=(638, 188, 722, 212),
search=(618, 168, 742, 232),
color=(174, 184, 197),
button=(638, 188, 722, 212),
),
en=None,
)
ENTER = ButtonWrapper(
name='ENTER',
jp=Button(
file='./assets/jp/stage/sweep/ENTER.png',
area=(791, 514, 1080, 568),
search=(771, 494, 1100, 588),
color=(223, 207, 68),
button=(791, 514, 1080, 568),
),
en=None,
)
EXIT = ButtonWrapper(
name='EXIT',
jp=Button(
file='./assets/jp/stage/sweep/EXIT.png',
area=(1114, 127, 1141, 154),
search=(1094, 107, 1161, 174),
color=(185, 193, 203),
button=(1114, 127, 1141, 154),
),
en=None,
)
MAX = ButtonWrapper(
name='MAX',
jp=Button(
file='./assets/jp/stage/sweep/MAX.png',
area=(1054, 279, 1111, 321),
search=(1034, 259, 1131, 341),
color=(216, 222, 228),
button=(1054, 279, 1111, 321),
),
en=None,
)
MIN = ButtonWrapper(
name='MIN',
jp=Button(
file='./assets/jp/stage/sweep/MIN.png',
area=(760, 278, 816, 322),
search=(740, 258, 836, 342),
color=(194, 194, 194),
button=(760, 278, 816, 322),
),
en=None,
)
MINUS = ButtonWrapper(
name='MINUS',
jp=Button(
file='./assets/jp/stage/sweep/MINUS.png',
area=(838, 279, 876, 320),
search=(818, 259, 896, 340),
color=(221, 222, 222),
button=(838, 279, 876, 320),
),
en=None,
)
OCR_NUM = ButtonWrapper(
name='OCR_NUM',
jp=Button(
file='./assets/jp/stage/sweep/OCR_NUM.png',
area=(896, 281, 975, 323),
search=(876, 261, 995, 343),
color=(81, 94, 113),
button=(896, 281, 975, 323),
),
en=None,
)
PLUS = ButtonWrapper(
name='PLUS',
jp=Button(
file='./assets/jp/stage/sweep/PLUS.png',
area=(995, 278, 1034, 322),
search=(975, 258, 1054, 342),
color=(233, 243, 246),
button=(995, 278, 1034, 322),
),
en=None,
)
SKIP_OK_LOWER = ButtonWrapper(
name='SKIP_OK_LOWER',
jp=Button(
file='./assets/jp/stage/sweep/SKIP_OK_LOWER.png',
area=(541, 551, 740, 616),
search=(521, 531, 760, 636),
color=(112, 212, 247),
button=(541, 551, 740, 616),
),
en=None,
)
SKIP_OK_UPPER = ButtonWrapper(
name='SKIP_OK_UPPER',
jp=Button(
file='./assets/jp/stage/sweep/SKIP_OK_UPPER.png',
area=(542, 474, 738, 545),
search=(522, 454, 758, 565),
color=(112, 212, 248),
button=(542, 474, 738, 545),
),
en=None,
)
SKIP_SKIP = ButtonWrapper(
name='SKIP_SKIP',
jp=Button(
file='./assets/jp/stage/sweep/SKIP_SKIP.png',
area=(545, 475, 736, 540),
search=(525, 455, 756, 560),
color=(110, 207, 243),
button=(545, 475, 736, 540),
),
en=None,
)
SWEEP = ButtonWrapper(
name='SWEEP',
jp=Button(
file='./assets/jp/stage/sweep/SWEEP.png',
area=(796, 385, 1067, 427),
search=(776, 365, 1087, 447),
color=(109, 202, 235),
button=(796, 385, 1067, 427),
),
en=None,
)
SWEEP_CONFIRM = ButtonWrapper(
name='SWEEP_CONFIRM',
jp=Button(
file='./assets/jp/stage/sweep/SWEEP_CONFIRM.png',
area=(611, 147, 669, 177),
search=(591, 127, 689, 197),
color=(143, 156, 170),
button=(664, 470, 871, 534),
),
en=None,
)
+182
View File
@@ -0,0 +1,182 @@
import cv2
import numpy as np
from module.base.base import ModuleBase
from module.base.button import ButtonWrapper, ClickButton
from module.base.timer import Timer
from module.base.utils import area_pad, area_size, area_offset, random_rectangle_vector_opted
from module.logger import logger
from module.ocr.ocr import Ocr
class StageList:
drag_vector_range = (0.65, 0.85)
def __init__(
self,
name,
area_stage: ButtonWrapper,
area_index: ButtonWrapper,
area_item: ButtonWrapper,
button_enter: ButtonWrapper,
drag_direction: str = "down"
):
self.name = name
self.stage = area_stage
self.index_ocr = Ocr(area_index, lang='en')
self.stage_item = area_item.button
self.enter = button_enter
self.drag_direction = drag_direction
self.current_index_min = 1
self.current_index_max = 1
self.current_indexes = []
def __str__(self):
return f'StageList({self.name})'
__repr__ = __str__
def __eq__(self, other):
return str(self) == str(other)
def __hash__(self):
return hash(self.name)
@property
def _indexes(self) -> list[int]:
return list(map(lambda x: int(x.ocr_text), self.current_indexes))
def load_stage_indexes(self, main: ModuleBase):
self.current_indexes = list(
filter(lambda x: x.ocr_text.isdigit(), self.index_ocr.detect_and_ocr(main.device.image))
)
if not self.current_indexes:
logger.warning(f'No valid index in {self.index_ocr.name}')
return
indexes = self._indexes
self.current_index_min = min(indexes)
self.current_index_max = max(indexes)
logger.attr(self.index_ocr.name, f'Index range: {self.current_index_min} - {self.current_index_max}')
def drag_page(self, direction: str, main: ModuleBase, vector_range=None, reverse=False):
"""
Args:
direction: up, down
main:
vector_range (tuple[float, float]):
reverse (bool):
"""
if vector_range is None:
vector_range = self.drag_vector_range
vector = np.random.uniform(*vector_range)
width, height = area_size(self.stage.button)
if direction == 'up':
vector = (0, vector * height)
elif direction == 'down':
vector = (0, -vector * height)
else:
logger.warning(f'Unknown drag direction: {direction}')
return
if reverse:
vector = (-vector[0], -vector[1])
p1, p2 = random_rectangle_vector_opted(vector, box=self.stage.button)
main.device.drag(p1, p2, name=f'{self.name}_DRAG')
def insight_index(self, index: int, main: ModuleBase, skip_first_screenshot=True) -> bool:
"""
Args:
index:
main:
skip_first_screenshot:
Returns:
If success
"""
logger.info(f'Insight index: {index}')
last_indexes: set[int] = set()
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
main.device.screenshot()
self.load_stage_indexes(main=main)
if self.current_index_min <= index <= self.current_index_max:
break
if index < self.current_index_min:
self.drag_page(self.drag_direction, main, reverse=True)
elif index > self.current_index_max:
self.drag_page(self.drag_direction, main)
main.wait_until_stable(
self.stage.button,
timer=Timer(0, 0),
timeout=Timer(1.5, 5)
)
indexes = self._indexes
if indexes and last_indexes == set(indexes):
logger.warning(f'No more index {index}')
return False
last_indexes = set(indexes)
return True
@staticmethod
def _match_clickable_points(image, template, threshold=0.85):
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)
loc = np.where(res >= threshold)
return [point for point in zip(*loc[::-1])]
def select_index_enter(
self,
index: int,
main: ModuleBase,
insight: bool = True,
skip_first_screenshot: bool = True,
interval: int = 5
) -> bool:
if insight and not self.insight_index(index, main, skip_first_screenshot):
return False
logger.info(f'Select index: {index}')
click_interval = Timer(interval)
load_index_interval = Timer(1)
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
main.device.screenshot()
if load_index_interval.reached_and_reset():
self.load_stage_indexes(main=main)
index_box = next(filter(lambda x: int(x.ocr_text) == index, self.current_indexes), None)
if index_box is None:
logger.warning(f'No index {index} in {self.index_ocr.name}')
return False
stage_item_box = area_pad((0, 0, *area_size(self.stage_item)))
search_box = area_offset(stage_item_box, index_box.box[:2])
search_image = main.image_crop(search_box)
points = self._match_clickable_points(search_image, self.enter.matched_button.image)
if not points:
logger.warning(f'No clickable {self.enter.name}')
return False
point = area_offset((0, 0, *area_size(self.enter.button)), points[0])
click_button = ClickButton(area_offset(point, search_box[:2]), name=self.enter.name)
if click_interval.reached_and_reset():
main.device.click(click_button)
return True
+254
View File
@@ -0,0 +1,254 @@
from module.base.base import ModuleBase
from module.base.timer import Timer
from module.logger import logger
from module.ocr.ocr import Digit
from enum import Enum
from tasks.stage.assets.assets_stage_sweep import *
class SweepStatus(Enum):
SELECT = 1
START = 2
CONFIRM = 3
SKIP = 4
END = 5
FINISH = 6
class StageSweep:
def __init__(
self,
name: str,
sweep_num: int,
max_sweep: int,
):
self.name = name
self.sweep_num = sweep_num
self.check: ButtonWrapper = None
self.num: Digit = None
self.plus: ButtonWrapper = None
self.minus: ButtonWrapper = None
self.max: ButtonWrapper = None
self.min: ButtonWrapper = None
self.sweep: ButtonWrapper = None
self.sweep_confirm: ButtonWrapper = None
self.enter: ButtonWrapper = None
self.exit: ButtonWrapper = None
self.skip_skip: ButtonWrapper = None
self.skip_ok_upper: ButtonWrapper = None
self.skip_ok_lower: ButtonWrapper = None
self.set_button()
self.min_sweep = 1
self.max_sweep = max_sweep
self.current_sweep = 0
self.sweep_method = None
self.set_mode()
def __str__(self):
return f'StageSweep({self.name})'
__repr__ = __str__
def __eq__(self, other):
return str(self) == str(other)
def __hash__(self):
return hash(self.name)
def set_button(
self,
button_check: ButtonWrapper = None,
button_num: ButtonWrapper = None,
button_plus: ButtonWrapper = None,
button_minus: ButtonWrapper = None,
button_max: ButtonWrapper = None,
button_min: ButtonWrapper = None,
button_sweep: ButtonWrapper = None,
button_sweep_confirm: ButtonWrapper = None,
button_enter: ButtonWrapper = None,
button_exit: ButtonWrapper = None,
button_skip_skip: ButtonWrapper = None,
button_skip_ok_upper: ButtonWrapper = None,
button_skip_ok_lower: ButtonWrapper = None,
):
self.check = button_check if button_check else CHECK_SWEEP
self.num = Digit(button_num if button_num else OCR_NUM)
self.plus = button_plus if button_plus else PLUS
self.minus = button_minus if button_minus else MINUS
self.max = button_max if button_max else MAX
self.min = button_min if button_min else MIN
self.sweep = button_sweep if button_sweep else SWEEP
self.sweep_confirm = button_sweep_confirm if button_sweep_confirm else SWEEP_CONFIRM
self.enter = button_enter if button_enter else ENTER
self.exit = button_exit if button_exit else EXIT
self.skip_skip = button_skip_skip if button_skip_skip else SKIP_SKIP
self.skip_ok_upper = button_skip_ok_upper if button_skip_ok_upper else SKIP_OK_UPPER
self.skip_ok_lower = button_skip_ok_lower if button_skip_ok_lower else SKIP_OK_LOWER
def set_mode(self, mode: str = None):
if mode is None:
match self.sweep_num:
case 0:
self.sweep_method = self.set_sweep_min
case -1:
self.sweep_method = self.set_sweep_max
case x if x > 0:
self.sweep_method = self.set_sweep_num
case _:
logger.warning(f'Invalid sweep num: {self.sweep_num}')
return
match mode:
case 'max':
self.sweep_method = self.set_sweep_max
case 'min':
self.sweep_method = self.set_sweep_min
case _:
logger.warning(f'Invalid sweep mode: {mode}')
def check_sweep(self, main: ModuleBase):
return main.appear(self.check)
def check_skip(self, main: ModuleBase):
return main.appear(self.skip_skip) or main.appear(self.skip_ok_upper) or main.appear(self.skip_ok_lower)
def load_sweep_num(self, main: ModuleBase):
while 1:
main.device.screenshot()
ocr_result = self.num.detect_and_ocr(main.device.image)
if not ocr_result:
logger.warning(f'No valid num in {self.num.name}')
continue
if len(ocr_result) == 1:
self.current_sweep = int(ocr_result[0].ocr_text)
return
def set_sweep_num(self, main: ModuleBase, skip_first_screenshot=True) -> bool:
num = self.sweep_num
if num < self.min_sweep or num > self.max_sweep:
logger.warning(f'Invalid sweep num: {num}')
return False
logger.info(f'Set sweep num: {num}')
retry = Timer(1, 2)
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
main.device.screenshot()
self.load_sweep_num(main)
if self.current_sweep == num:
logger.info(f'Sweep num reaches {num}')
return True
elif self.current_sweep == 0:
logger.info(f'Current sweep num is 0')
return False
if retry.reached_and_reset():
diff = num - self.current_sweep
button = self.plus if diff > 0 else self.minus
main.device.multi_click(button, abs(diff), interval=(0.2, 0.3))
def set_sweep_max(self, main: ModuleBase, skip_first_screenshot=True):
logger.info(f'Set sweep max: {self.max_sweep}')
retry = Timer(1, 2)
count = 0
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
main.device.screenshot()
self.load_sweep_num(main)
if self.current_sweep == self.max_sweep:
logger.info(f'Sweep max reaches {self.max_sweep}')
return True
elif count == 1 and self.current_sweep != 1:
logger.info("Set sweep max")
return True
elif self.current_sweep == 0:
logger.info(f'Current sweep num is 0')
return False
if retry.reached_and_reset():
main.click_with_interval(self.max, interval=0)
count += 1
continue
if count > 2:
logger.info("Set sweep max")
return True
def set_sweep_min(self, main: ModuleBase, skip_first_screenshot=True):
logger.info(f'Set sweep min: {self.min_sweep}')
retry = Timer(1, 2)
while 1:
if skip_first_screenshot:
skip_first_screenshot = False
else:
main.device.screenshot()
self.load_sweep_num(main)
if self.current_sweep == self.min_sweep:
logger.info(f'Sweep min reaches {self.min_sweep}')
return True
elif self.current_sweep == 0:
logger.info(f'Current sweep num is 0')
return False
if retry.reached_and_reset():
main.click_with_interval(self.min, interval=0)
def do_sweep(self, main: ModuleBase, skip_first_screenshot=True) -> bool:
timer = Timer(0.5, 1)
timer_stable = Timer(0.5, 1).start()
status = SweepStatus.SELECT
while 1:
if not timer_stable.reached():
continue
if skip_first_screenshot:
skip_first_screenshot = False
else:
main.device.screenshot()
if timer.reached_and_reset():
logger.attr("Status", status)
match status:
case SweepStatus.SELECT:
if self.sweep_method(main, skip_first_screenshot):
status = SweepStatus.START
else:
return False
case SweepStatus.START:
main.appear_then_click(self.sweep, interval=1)
if main.appear(self.sweep_confirm):
status = SweepStatus.CONFIRM
case SweepStatus.CONFIRM:
main.appear_then_click(self.sweep_confirm, interval=1)
if self.check_skip(main):
status = SweepStatus.SKIP
case SweepStatus.SKIP:
main.appear_then_click(self.skip_skip)
main.appear_then_click(self.skip_ok_upper)
main.appear_then_click(self.skip_ok_lower)
if self.check_sweep(main):
status = SweepStatus.END
case SweepStatus.END:
main.appear_then_click(self.exit, interval=1)
if not main.appear(self.check):
status = SweepStatus.FINISH
case SweepStatus.FINISH:
pass
case _:
logger.warning(f'Invalid status: {status}')
return False
if status == SweepStatus.FINISH:
logger.info(f'Sweep finish')
return True
@@ -12,6 +12,7 @@ CHALLENGE_LOSE = ButtonWrapper(
color=(146, 158, 173), color=(146, 158, 173),
button=(548, 431, 738, 499), button=(548, 431, 738, 499),
), ),
en=None,
) )
CHALLENGE_WIN = ButtonWrapper( CHALLENGE_WIN = ButtonWrapper(
name='CHALLENGE_WIN', name='CHALLENGE_WIN',
@@ -22,6 +23,7 @@ CHALLENGE_WIN = ButtonWrapper(
color=(151, 165, 180), color=(151, 165, 180),
button=(549, 495, 737, 563), button=(549, 495, 737, 563),
), ),
en=None,
) )
GET_REWARD_CREDIT = ButtonWrapper( GET_REWARD_CREDIT = ButtonWrapper(
name='GET_REWARD_CREDIT', name='GET_REWARD_CREDIT',
@@ -32,6 +34,7 @@ GET_REWARD_CREDIT = ButtonWrapper(
color=(215, 193, 61), color=(215, 193, 61),
button=(308, 360, 397, 415), button=(308, 360, 397, 415),
), ),
en=None,
) )
GET_REWARD_DAILY = ButtonWrapper( GET_REWARD_DAILY = ButtonWrapper(
name='GET_REWARD_DAILY', name='GET_REWARD_DAILY',
@@ -42,6 +45,7 @@ GET_REWARD_DAILY = ButtonWrapper(
color=(214, 192, 60), color=(214, 192, 60),
button=(307, 440, 395, 493), button=(307, 440, 395, 493),
), ),
en=None,
) )
GOT_REWARD_CREDIT = ButtonWrapper( GOT_REWARD_CREDIT = ButtonWrapper(
name='GOT_REWARD_CREDIT', name='GOT_REWARD_CREDIT',
@@ -52,6 +56,7 @@ GOT_REWARD_CREDIT = ButtonWrapper(
color=(203, 205, 204), color=(203, 205, 204),
button=(308, 358, 398, 416), button=(308, 358, 398, 416),
), ),
en=None,
) )
GOT_REWARD_DAILY = ButtonWrapper( GOT_REWARD_DAILY = ButtonWrapper(
name='GOT_REWARD_DAILY', name='GOT_REWARD_DAILY',
@@ -62,6 +67,7 @@ GOT_REWARD_DAILY = ButtonWrapper(
color=(203, 203, 204), color=(203, 203, 204),
button=(307, 439, 397, 494), button=(307, 439, 397, 494),
), ),
en=None,
) )
OCR_TICKET = ButtonWrapper( OCR_TICKET = ButtonWrapper(
name='OCR_TICKET', name='OCR_TICKET',
@@ -72,6 +78,7 @@ OCR_TICKET = ButtonWrapper(
color=(200, 202, 204), color=(200, 202, 204),
button=(206, 479, 240, 499), button=(206, 479, 240, 499),
), ),
en=None,
) )
PLAYER_SELECT_FIRST = ButtonWrapper( PLAYER_SELECT_FIRST = ButtonWrapper(
name='PLAYER_SELECT_FIRST', name='PLAYER_SELECT_FIRST',
@@ -82,6 +89,7 @@ PLAYER_SELECT_FIRST = ButtonWrapper(
color=(191, 202, 207), color=(191, 202, 207),
button=(558, 179, 1118, 282), button=(558, 179, 1118, 282),
), ),
en=None,
) )
PLAYER_SELECT_SECOND = ButtonWrapper( PLAYER_SELECT_SECOND = ButtonWrapper(
name='PLAYER_SELECT_SECOND', name='PLAYER_SELECT_SECOND',
@@ -92,6 +100,7 @@ PLAYER_SELECT_SECOND = ButtonWrapper(
color=(192, 203, 208), color=(192, 203, 208),
button=(554, 336, 1127, 443), button=(554, 336, 1127, 443),
), ),
en=None,
) )
PLAYER_SELECT_THIRD = ButtonWrapper( PLAYER_SELECT_THIRD = ButtonWrapper(
name='PLAYER_SELECT_THIRD', name='PLAYER_SELECT_THIRD',
@@ -102,6 +111,7 @@ PLAYER_SELECT_THIRD = ButtonWrapper(
color=(194, 206, 211), color=(194, 206, 211),
button=(557, 495, 1120, 599), button=(557, 495, 1120, 599),
), ),
en=None,
) )
PREPARE_CHALLENGE = ButtonWrapper( PREPARE_CHALLENGE = ButtonWrapper(
name='PREPARE_CHALLENGE', name='PREPARE_CHALLENGE',
@@ -112,6 +122,7 @@ PREPARE_CHALLENGE = ButtonWrapper(
color=(148, 161, 175), color=(148, 161, 175),
button=(561, 548, 723, 598), button=(561, 548, 723, 598),
), ),
en=None,
) )
SKIP_OFF = ButtonWrapper( SKIP_OFF = ButtonWrapper(
name='SKIP_OFF', name='SKIP_OFF',
@@ -122,6 +133,7 @@ SKIP_OFF = ButtonWrapper(
color=(96, 137, 171), color=(96, 137, 171),
button=(1088, 582, 1250, 626), button=(1088, 582, 1250, 626),
), ),
en=None,
) )
SKIP_ON = ButtonWrapper( SKIP_ON = ButtonWrapper(
name='SKIP_ON', name='SKIP_ON',
@@ -132,6 +144,7 @@ SKIP_ON = ButtonWrapper(
color=(99, 177, 212), color=(99, 177, 212),
button=(1089, 581, 1250, 626), button=(1089, 581, 1250, 626),
), ),
en=None,
) )
START_CHALLENGE = ButtonWrapper( START_CHALLENGE = ButtonWrapper(
name='START_CHALLENGE', name='START_CHALLENGE',
@@ -142,4 +155,5 @@ START_CHALLENGE = ButtonWrapper(
color=(148, 161, 176), color=(148, 161, 176),
button=(1091, 635, 1246, 704), button=(1091, 635, 1246, 704),
), ),
en=None,
) )
@@ -116,8 +116,8 @@ class TacticalChallenge(UI):
if self.appear_then_click(CHALLENGE_LOSE): if self.appear_then_click(CHALLENGE_LOSE):
return TCStatus.LOSE return TCStatus.LOSE
case TCStatus.WIN | TCStatus.LOSE: case TCStatus.WIN | TCStatus.LOSE:
if self.appear(CHALLENGE_WIN) or self.appear(CHALLENGE_LOSE): if self.appear_then_click(CHALLENGE_WIN) or self.appear_then_click(CHALLENGE_LOSE):
return TCStatus.RESULT return status
is_valid, ticket = self._get_ticket() is_valid, ticket = self._get_ticket()
if not is_valid: if not is_valid:
return status return status
@@ -146,8 +146,8 @@ class TacticalChallenge(UI):
if not ui_timer.reached(): if not ui_timer.reached():
continue continue
if action_timer.reached_and_reset(): if action_timer.reached_and_reset():
status = self._handle_challenge(status)
logger.attr('Status', status.name) logger.attr('Status', status.name)
status = self._handle_challenge(status)
if status in (TCStatus.FINAL, TCStatus.FINISHED): if status in (TCStatus.FINAL, TCStatus.FINISHED):
break break
+2 -2
View File
@@ -1,8 +1,8 @@
Deploy: Deploy:
Git: Git:
# URL of AzurLaneAutoScript repository # URL of AzurLaneAutoScript repository
# [CN user] Use 'cn' to get update from git-over-cdn service # [CN user] Use 'cn' to get update from self-hosted git service
# [Other] Use 'global' to get update from https://github.com/TheFunny/ArisuAutoSrcipt # [Other] Use 'global' to get update from https://github.com/TheFunny/ArisuAutoSweeper
Repository: {{repository}} Repository: {{repository}}
# Branch of Alas # Branch of Alas
# [Developer] Use 'dev', 'app', etc, to try new features # [Developer] Use 'dev', 'app', etc, to try new features