Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
baac90ecf0
|
|||
|
53ec298fed
|
|||
|
b4f18f78ff
|
|||
|
eb9af42f38
|
|||
|
c29d972c6c
|
|||
|
92b34d4760
|
|||
|
04853b6c31
|
|||
|
9604e8962a
|
|||
|
03380b2d71
|
|||
|
c27bd74050
|
|||
|
c3e9945b15
|
|||
|
1dd100ac04
|
|||
|
77dca70af1
|
|||
|
b8ecd0c9d6
|
|||
|
ceb24283f3
|
|||
|
d0c591af3a
|
|||
|
589b0b08ec
|
|||
|
299bd6c687
|
|||
|
e61afaf43b
|
|||
|
30e8c8b21b
|
|||
|
f7b165f589
|
|||
|
08959e5f1c
|
|||
|
e929a1efb1
|
|||
|
8e29d7d2c0
|
|||
|
d3a1a77d6a
|
|||
|
930c741de6
|
|||
|
2c19afdf26
|
|||
|
bbf3bf7c36
|
|||
|
5e4abc147e
|
|||
|
eb8048ccd6
|
|||
|
8bec814b8d
|
|||
|
5fb2810bdc
|
|||
|
8ea95dc340
|
|||
|
9d3e581321
|
|||
|
d47e463365
|
|||
|
0a697e9398
|
|||
|
143f519adb
|
|||
|
af71e797db
|
|||
|
6ef18ed8c0
|
|||
|
8781e7830c
|
|||
|
84f78230d2
|
|||
|
f506616ba9
|
|||
|
c1d9ac4f64
|
|||
|
fb28fe297a
|
|||
|
9b17f1948a
|
|||
|
e82c4f875c
|
|||
|
51ecdf4908
|
|||
|
8d2882e752
|
|||
|
1a66e767f3
|
|||
|
04fab819b4
|
|||
|
023972682d
|
|||
|
2cefb26759
|
|||
|
7ed7cabbaa
|
|||
|
b62e301c55
|
|||
|
93e8fd1f0c
|
|||
|
b9e0cc3026
|
|||
| 6cbc4f0788 | |||
| f6736a71df | |||
| 83cc44f7b7 | |||
| 75cd7bafc4 | |||
| b3daa45c5a | |||
| 47e239aee1 | |||
| 21a08f0b7f | |||
|
7b2c3bb165
|
@@ -12,12 +12,17 @@
|
|||||||
|
|
||||||
The script is still under active development. The following features have been implemented:
|
The script is still under active development. The following features have been implemented:
|
||||||
|
|
||||||
- [x] **Cafe** Claim rewards / Interact / Second cafe
|
- [x] **Cafe** Claim rewards / Interact / Second floor
|
||||||
- [x] **Circle** Claim AP
|
- [x] **Club** Claim AP
|
||||||
- [x] **Mailbox** Claim rewards
|
- [x] **Mailbox** Claim rewards
|
||||||
|
- [x] **Bounty** Auto sweep
|
||||||
|
- [x] **Scrimmage** Auto sweep
|
||||||
- [x] **Tactical Challenge** Claim rewards / Auto battle
|
- [x] **Tactical Challenge** Claim rewards / Auto battle
|
||||||
|
|
||||||
_Currently only supports JP server._
|
Supported servers:
|
||||||
|
|
||||||
|
- [x] JP
|
||||||
|
- [x] OVERSEA - Global
|
||||||
|
|
||||||
## Relative projects
|
## Relative projects
|
||||||
|
|
||||||
@@ -38,3 +43,5 @@ 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)
|
Thanks to [Alas](https://github.com/LmeSzinc/AzurLaneAutoScript) and [SRC](https://github.com/LmeSzinc/StarRailCopilot)
|
||||||
for the development framework.
|
for the development framework.
|
||||||
|
|
||||||
|
Thanks to [RedDeadDepresso](https://github.com/RedDeadDepresso) for EN support.
|
||||||
|
|||||||
@@ -15,9 +15,22 @@
|
|||||||
- [x] **咖啡厅** 领取奖励 / 互动 / 第二咖啡厅
|
- [x] **咖啡厅** 领取奖励 / 互动 / 第二咖啡厅
|
||||||
- [x] **公会** 领取体力
|
- [x] **公会** 领取体力
|
||||||
- [x] **邮箱** 领取奖励
|
- [x] **邮箱** 领取奖励
|
||||||
|
- [x] **悬赏通缉** 自动扫荡
|
||||||
|
- [x] **学院交流会** 自动扫荡
|
||||||
- [x] **战术对抗赛** 领取奖励 / 自动战斗
|
- [x] **战术对抗赛** 领取奖励 / 自动战斗
|
||||||
|
|
||||||
_目前仅支持日服。_
|
目前支持的服务器:
|
||||||
|
|
||||||
|
- [x] 日服
|
||||||
|
- [x] 国际服 - 全球
|
||||||
|
|
||||||
|
## 已知问题
|
||||||
|
|
||||||
|
若愿意提供其他服务器支持,请开 PR 或 Issue。
|
||||||
|
|
||||||
|
- **大小月卡**:未实现自动领取,~~因为没买过~~,可能不影响使用。愿意提供图片的请开 Issue
|
||||||
|
- **月卡的额外悬赏券和学院交流券**:不太清楚月卡领取额外券的机制,~~因为没买过~~,可能影响相关任务使用券和体力的计算。愿意提供相关信息的请开
|
||||||
|
Issue
|
||||||
|
|
||||||
## 相关项目
|
## 相关项目
|
||||||
|
|
||||||
@@ -36,3 +49,5 @@ _目前仅支持日服。_
|
|||||||
|
|
||||||
感谢 [Alas](https://github.com/LmeSzinc/AzurLaneAutoScript) 以及 [SRC](https://github.com/LmeSzinc/StarRailCopilot)
|
感谢 [Alas](https://github.com/LmeSzinc/AzurLaneAutoScript) 以及 [SRC](https://github.com/LmeSzinc/StarRailCopilot)
|
||||||
提供的开发框架。
|
提供的开发框架。
|
||||||
|
|
||||||
|
感谢 [RedDeadDepresso](https://github.com/RedDeadDepresso) 提供英语支持。
|
||||||
|
|||||||
@@ -34,6 +34,14 @@ class ArisuAutoSweeper(AzurLaneAutoScript):
|
|||||||
from tasks.mail.mail import Mail
|
from tasks.mail.mail import Mail
|
||||||
Mail(config=self.config, device=self.device).run()
|
Mail(config=self.config, device=self.device).run()
|
||||||
|
|
||||||
|
def bounty(self):
|
||||||
|
from tasks.bounty.bounty import Bounty
|
||||||
|
Bounty(config=self.config, device=self.device).run()
|
||||||
|
|
||||||
|
def scrimmage(self):
|
||||||
|
from tasks.scrimmage.scrimmage import Scrimmage
|
||||||
|
Scrimmage(config=self.config, device=self.device).run()
|
||||||
|
|
||||||
def tactical_challenge(self):
|
def tactical_challenge(self):
|
||||||
from tasks.tactical_challenge.tactical_challenge import TacticalChallenge
|
from tasks.tactical_challenge.tactical_challenge import TacticalChallenge
|
||||||
TacticalChallenge(config=self.config, device=self.device).run()
|
TacticalChallenge(config=self.config, device=self.device).run()
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 9.4 KiB |
@@ -285,7 +285,7 @@ pre.rich-traceback-code {
|
|||||||
*[style*="--header-icon--"] {
|
*[style*="--header-icon--"] {
|
||||||
margin: .25rem auto .25rem;
|
margin: .25rem auto .25rem;
|
||||||
border-radius: 1.5rem;
|
border-radius: 1.5rem;
|
||||||
height: 3.5em;
|
height: 3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
*[style*="--header-text--"] {
|
*[style*="--header-text--"] {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 129 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
@@ -63,6 +63,52 @@
|
|||||||
"ServerUpdate": "04:00"
|
"ServerUpdate": "04:00"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Bounty": {
|
||||||
|
"Scheduler": {
|
||||||
|
"Enable": true,
|
||||||
|
"NextRun": "2020-01-01 00:00:00",
|
||||||
|
"Command": "Bounty",
|
||||||
|
"ServerUpdate": "04:00"
|
||||||
|
},
|
||||||
|
"Bounty": {
|
||||||
|
"OnError": "skip"
|
||||||
|
},
|
||||||
|
"Highway": {
|
||||||
|
"Stage": 1,
|
||||||
|
"Count": 2
|
||||||
|
},
|
||||||
|
"DesertRailroad": {
|
||||||
|
"Stage": 1,
|
||||||
|
"Count": 2
|
||||||
|
},
|
||||||
|
"Schoolhouse": {
|
||||||
|
"Stage": 1,
|
||||||
|
"Count": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Scrimmage": {
|
||||||
|
"Scheduler": {
|
||||||
|
"Enable": true,
|
||||||
|
"NextRun": "2020-01-01 00:00:00",
|
||||||
|
"Command": "Scrimmage",
|
||||||
|
"ServerUpdate": "04:00"
|
||||||
|
},
|
||||||
|
"Scrimmage": {
|
||||||
|
"OnError": "skip"
|
||||||
|
},
|
||||||
|
"Trinity": {
|
||||||
|
"Stage": 1,
|
||||||
|
"Count": 2
|
||||||
|
},
|
||||||
|
"Gehenna": {
|
||||||
|
"Stage": 1,
|
||||||
|
"Count": 2
|
||||||
|
},
|
||||||
|
"Millennium": {
|
||||||
|
"Stage": 1,
|
||||||
|
"Count": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
"TacticalChallenge": {
|
"TacticalChallenge": {
|
||||||
"Scheduler": {
|
"Scheduler": {
|
||||||
"Enable": true,
|
"Enable": true,
|
||||||
@@ -84,7 +130,10 @@
|
|||||||
"ItemStorage": {
|
"ItemStorage": {
|
||||||
"AP": {},
|
"AP": {},
|
||||||
"Credit": {},
|
"Credit": {},
|
||||||
"Pyroxene": {}
|
"Pyroxene": {},
|
||||||
|
"BountyTicket": {},
|
||||||
|
"ScrimmageTicket": {},
|
||||||
|
"TacticalChallengeTicket": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -179,20 +179,29 @@ def iter_assets():
|
|||||||
if image.attr != '':
|
if image.attr != '':
|
||||||
row = deep_get(data, keys=[image.module, image.assets, image.server, image.frame])
|
row = deep_get(data, keys=[image.module, image.assets, image.server, image.frame])
|
||||||
row.load_image(image)
|
row.load_image(image)
|
||||||
# Apply `search` of the first frame to all
|
# Set `search`
|
||||||
for path, frames in deep_iter(data, depth=3):
|
for path, frames in deep_iter(data, depth=3):
|
||||||
print(path, frames)
|
print(path, frames)
|
||||||
|
# If `search` attribute is set in the first frame, apply to all
|
||||||
first = frames[1]
|
first = frames[1]
|
||||||
search = first.search if first.search else DataAssets.area_to_search(first.area)
|
if first.search:
|
||||||
for frame in frames.values():
|
for frame in frames.values():
|
||||||
frame.search = search
|
frame.search = first.search
|
||||||
|
else:
|
||||||
|
for frame in frames.values():
|
||||||
|
if frame.search:
|
||||||
|
# Follow frame specific `search`
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
# Generate `search` from `area`
|
||||||
|
frame.search = DataAssets.area_to_search(frame.area)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def generate_code():
|
def generate_code():
|
||||||
all = iter_assets()
|
all_assets = iter_assets()
|
||||||
for module, module_data in all.items():
|
for module, module_data in all_assets.items():
|
||||||
path = os.path.join(AzurLaneConfig.ASSETS_MODULE, module.split('/', maxsplit=1)[0])
|
path = os.path.join(AzurLaneConfig.ASSETS_MODULE, module.split('/', maxsplit=1)[0])
|
||||||
output = os.path.join(path, 'assets.py')
|
output = os.path.join(path, 'assets.py')
|
||||||
if os.path.exists(output):
|
if os.path.exists(output):
|
||||||
@@ -204,7 +213,7 @@ def generate_code():
|
|||||||
continue
|
continue
|
||||||
os.remove(prev)
|
os.remove(prev)
|
||||||
|
|
||||||
for module, module_data in all.items():
|
for module, module_data in all_assets.items():
|
||||||
path = os.path.join(AzurLaneConfig.ASSETS_MODULE, module.split('/', maxsplit=1)[0])
|
path = os.path.join(AzurLaneConfig.ASSETS_MODULE, module.split('/', maxsplit=1)[0])
|
||||||
output = os.path.join(path, 'assets')
|
output = os.path.join(path, 'assets')
|
||||||
gen = CodeGenerator()
|
gen = CodeGenerator()
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ class Button(Resource):
|
|||||||
threshold=threshold
|
threshold=threshold
|
||||||
)
|
)
|
||||||
|
|
||||||
def match_template(self, image, similarity=0.85) -> bool:
|
def match_template(self, image, similarity=0.85, direct_match=False) -> bool:
|
||||||
"""
|
"""
|
||||||
Detects assets by template matching.
|
Detects assets by template matching.
|
||||||
|
|
||||||
@@ -83,10 +83,12 @@ class Button(Resource):
|
|||||||
Args:
|
Args:
|
||||||
image: Screenshot.
|
image: Screenshot.
|
||||||
similarity (float): 0-1.
|
similarity (float): 0-1.
|
||||||
|
direct_match: True to ignore `self.search`
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool.
|
bool.
|
||||||
"""
|
"""
|
||||||
|
if not direct_match:
|
||||||
image = crop(image, self.search, copy=False)
|
image = crop(image, self.search, copy=False)
|
||||||
res = cv2.matchTemplate(self.image, image, cv2.TM_CCOEFF_NORMED)
|
res = cv2.matchTemplate(self.image, image, cv2.TM_CCOEFF_NORMED)
|
||||||
_, sim, _, point = cv2.minMaxLoc(res)
|
_, sim, _, point = cv2.minMaxLoc(res)
|
||||||
@@ -94,7 +96,32 @@ class Button(Resource):
|
|||||||
self._button_offset = np.array(point) + self.search[:2] - self.area[:2]
|
self._button_offset = np.array(point) + self.search[:2] - self.area[:2]
|
||||||
return sim > similarity
|
return sim > similarity
|
||||||
|
|
||||||
def match_template_color(self, image, similarity=0.85, threshold=30) -> bool:
|
def match_multi_template(self, image, similarity=0.85, direct_match=False):
|
||||||
|
"""
|
||||||
|
Detects assets by template matching, return multiple reults
|
||||||
|
|
||||||
|
Args:
|
||||||
|
image: Screenshot.
|
||||||
|
similarity (float): 0-1.
|
||||||
|
direct_match: True to ignore `self.search`
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list:
|
||||||
|
"""
|
||||||
|
if not direct_match:
|
||||||
|
image = crop(image, self.search, copy=False)
|
||||||
|
res = cv2.matchTemplate(self.image, image, cv2.TM_CCOEFF_NORMED)
|
||||||
|
res = cv2.inRange(res, similarity, 1.)
|
||||||
|
try:
|
||||||
|
points = np.array(cv2.findNonZero(res))[:, 0, :]
|
||||||
|
points += self.search[:2]
|
||||||
|
return points.tolist()
|
||||||
|
except IndexError:
|
||||||
|
# Empty result
|
||||||
|
# IndexError: too many indices for array: array is 0-dimensional, but 3 were indexed
|
||||||
|
return []
|
||||||
|
|
||||||
|
def match_template_color(self, image, similarity=0.85, threshold=30, direct_match=False) -> bool:
|
||||||
"""
|
"""
|
||||||
Template match first, color match then
|
Template match first, color match then
|
||||||
|
|
||||||
@@ -102,11 +129,12 @@ class Button(Resource):
|
|||||||
image: Screenshot.
|
image: Screenshot.
|
||||||
similarity (float): 0-1.
|
similarity (float): 0-1.
|
||||||
threshold (int): Default to 10.
|
threshold (int): Default to 10.
|
||||||
|
direct_match: True to ignore `self.search`
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
bool.
|
||||||
"""
|
"""
|
||||||
matched = self.match_template(image, similarity=similarity)
|
matched = self.match_template(image, similarity=similarity, direct_match=direct_match)
|
||||||
if not matched:
|
if not matched:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -124,10 +152,10 @@ class ButtonWrapper(Resource):
|
|||||||
self.name = name
|
self.name = name
|
||||||
self.data_buttons = kwargs
|
self.data_buttons = kwargs
|
||||||
self._matched_button: t.Optional[Button] = None
|
self._matched_button: t.Optional[Button] = None
|
||||||
self.resource_add(self.name)
|
self.resource_add(f'{name}:{next(self.iter_buttons(), None)}')
|
||||||
|
|
||||||
def resource_release(self):
|
def resource_release(self):
|
||||||
del_cached_property(self, 'assets')
|
del_cached_property(self, 'buttons')
|
||||||
self._matched_button = None
|
self._matched_button = None
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@@ -144,16 +172,25 @@ class ButtonWrapper(Resource):
|
|||||||
def __bool__(self):
|
def __bool__(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def iter_buttons(self) -> t.Iterator[Button]:
|
||||||
|
for _, assets in self.data_buttons.items():
|
||||||
|
if isinstance(assets, Button):
|
||||||
|
yield assets
|
||||||
|
elif isinstance(assets, list):
|
||||||
|
for asset in assets:
|
||||||
|
yield asset
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def buttons(self) -> t.List[Button]:
|
def buttons(self) -> t.List[Button]:
|
||||||
# for trial in [server.lang, 'share', 'cn']:
|
for trial in [server.lang, 'share', 'cn']:
|
||||||
for trial in [server.lang, 'share', 'jp']:
|
try:
|
||||||
assets = self.data_buttons.get(trial, None)
|
assets = self.data_buttons[trial]
|
||||||
if assets is not None:
|
|
||||||
if isinstance(assets, Button):
|
if isinstance(assets, Button):
|
||||||
return [assets]
|
return [assets]
|
||||||
elif isinstance(assets, list):
|
elif isinstance(assets, list):
|
||||||
return assets
|
return assets
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
raise ScriptError(f'ButtonWrapper({self}) on server {server.lang} has no fallback button')
|
raise ScriptError(f'ButtonWrapper({self}) on server {server.lang} has no fallback button')
|
||||||
|
|
||||||
@@ -164,16 +201,45 @@ class ButtonWrapper(Resource):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def match_template(self, image, similarity=0.85) -> bool:
|
def match_template(self, image, similarity=0.85, direct_match=False) -> bool:
|
||||||
for assets in self.buttons:
|
for assets in self.buttons:
|
||||||
if assets.match_template(image, similarity=similarity):
|
if assets.match_template(image, similarity=similarity, direct_match=direct_match):
|
||||||
self._matched_button = assets
|
self._matched_button = assets
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def match_template_color(self, image, similarity=0.85, threshold=30) -> bool:
|
def match_multi_template(self, image, similarity=0.85, threshold=5, direct_match=False):
|
||||||
|
"""
|
||||||
|
Detects assets by template matching, return multiple results
|
||||||
|
|
||||||
|
Args:
|
||||||
|
image: Screenshot.
|
||||||
|
similarity (float): 0-1.
|
||||||
|
threshold:
|
||||||
|
direct_match: True to ignore `self.search`
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[ClickButton]:
|
||||||
|
"""
|
||||||
|
ps = []
|
||||||
for assets in self.buttons:
|
for assets in self.buttons:
|
||||||
if assets.match_template_color(image, similarity=similarity, threshold=threshold):
|
ps += assets.match_multi_template(image, similarity=similarity, direct_match=direct_match)
|
||||||
|
if not ps:
|
||||||
|
return []
|
||||||
|
|
||||||
|
from module.base.utils.points import Points
|
||||||
|
ps = Points(ps).group(threshold=threshold)
|
||||||
|
area_list = [area_offset(self.area, p - self.area[:2]) for p in ps]
|
||||||
|
button_list = [area_offset(self.button, p - self.area[:2]) for p in ps]
|
||||||
|
return [
|
||||||
|
ClickButton(area=info[0], button=info[1], name=f'{self.name}_result{i}')
|
||||||
|
for i, info in enumerate(zip(area_list, button_list))
|
||||||
|
]
|
||||||
|
|
||||||
|
def match_template_color(self, image, similarity=0.85, threshold=30, direct_match=False) -> bool:
|
||||||
|
for assets in self.buttons:
|
||||||
|
if assets.match_template_color(
|
||||||
|
image, similarity=similarity, threshold=threshold, direct_match=direct_match):
|
||||||
self._matched_button = assets
|
self._matched_button = assets
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
@@ -222,17 +288,31 @@ class ButtonWrapper(Resource):
|
|||||||
"""
|
"""
|
||||||
if isinstance(button, ButtonWrapper):
|
if isinstance(button, ButtonWrapper):
|
||||||
button = button.matched_button
|
button = button.matched_button
|
||||||
for b in self.buttons:
|
for b in self.iter_buttons():
|
||||||
b.load_offset(button)
|
b.load_offset(button)
|
||||||
|
|
||||||
def clear_offset(self):
|
def clear_offset(self):
|
||||||
for b in self.buttons:
|
for b in self.iter_buttons():
|
||||||
b.clear_offset()
|
b.clear_offset()
|
||||||
|
|
||||||
|
def load_search(self, area):
|
||||||
|
"""
|
||||||
|
Set `search` attribute.
|
||||||
|
Note that this method is irreversible.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
area:
|
||||||
|
"""
|
||||||
|
for b in self.iter_buttons():
|
||||||
|
b.search = area
|
||||||
|
|
||||||
|
|
||||||
class ClickButton:
|
class ClickButton:
|
||||||
def __init__(self, button, name='CLICK_BUTTON'):
|
def __init__(self, area, button=None, name='CLICK_BUTTON'):
|
||||||
self.area = button
|
self.area = area
|
||||||
|
if button is None:
|
||||||
|
self.button = area
|
||||||
|
else:
|
||||||
self.button = button
|
self.button = button
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
import module.config.server as server
|
from module.base.decorator import cached_property
|
||||||
from module.base.decorator import cached_property, del_cached_property
|
|
||||||
|
|
||||||
|
|
||||||
def get_assets_from_file(file, regex):
|
def get_assets_from_file(file):
|
||||||
assets = set()
|
assets = set()
|
||||||
|
regex = re.compile(r"file='(.*?)'")
|
||||||
with open(file, 'r', encoding='utf-8') as f:
|
with open(file, 'r', encoding='utf-8') as f:
|
||||||
for row in f.readlines():
|
for row in f.readlines():
|
||||||
result = regex.search(row)
|
result = regex.search(row)
|
||||||
@@ -20,11 +20,9 @@ class PreservedAssets:
|
|||||||
assets = set()
|
assets = set()
|
||||||
assets |= get_assets_from_file(
|
assets |= get_assets_from_file(
|
||||||
file='./tasks/base/assets/assets_base_page.py',
|
file='./tasks/base/assets/assets_base_page.py',
|
||||||
regex=re.compile(r'^([A-Za-z][A-Za-z0-9_]+) = ')
|
|
||||||
)
|
)
|
||||||
assets |= get_assets_from_file(
|
assets |= get_assets_from_file(
|
||||||
file='./tasks/base/assets/assets_base_popup.py',
|
file='./tasks/base/assets/assets_base_popup.py',
|
||||||
regex=re.compile(r'^([A-Za-z][A-Za-z0-9_]+) = ')
|
|
||||||
)
|
)
|
||||||
return assets
|
return assets
|
||||||
|
|
||||||
@@ -44,11 +42,13 @@ class Resource:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_loaded(cls, obj):
|
def is_loaded(cls, obj):
|
||||||
if hasattr(obj, '_image') and obj._image is None:
|
if hasattr(obj, '_image') and obj._image is not None:
|
||||||
return False
|
|
||||||
elif hasattr(obj, 'image') and obj.image is None:
|
|
||||||
return False
|
|
||||||
return True
|
return True
|
||||||
|
if hasattr(obj, 'image') and obj.image is not None:
|
||||||
|
return True
|
||||||
|
if hasattr(obj, 'buttons') and obj.buttons is not None:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def resource_show(cls):
|
def resource_show(cls):
|
||||||
@@ -56,11 +56,16 @@ class Resource:
|
|||||||
logger.hr('Show resource')
|
logger.hr('Show resource')
|
||||||
for key, obj in cls.instances.items():
|
for key, obj in cls.instances.items():
|
||||||
if cls.is_loaded(obj):
|
if cls.is_loaded(obj):
|
||||||
continue
|
|
||||||
logger.info(f'{obj}: {key}')
|
logger.info(f'{obj}: {key}')
|
||||||
|
|
||||||
|
|
||||||
def release_resources(next_task=''):
|
def release_resources(next_task=''):
|
||||||
|
# Release all OCR models
|
||||||
|
# det models take 400MB
|
||||||
|
if not next_task:
|
||||||
|
from module.ocr.models import OCR_MODEL
|
||||||
|
OCR_MODEL.resource_release()
|
||||||
|
|
||||||
# Release assets cache
|
# Release assets cache
|
||||||
# module.ui has about 80 assets and takes about 3MB
|
# module.ui has about 80 assets and takes about 3MB
|
||||||
# Alas has about 800 assets, but they are not all loaded.
|
# Alas has about 800 assets, but they are not all loaded.
|
||||||
|
|||||||
@@ -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": {
|
||||||
@@ -255,6 +257,191 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Bounty": {
|
||||||
|
"Scheduler": {
|
||||||
|
"Enable": {
|
||||||
|
"type": "checkbox",
|
||||||
|
"value": true,
|
||||||
|
"option": [
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"NextRun": {
|
||||||
|
"type": "datetime",
|
||||||
|
"value": "2020-01-01 00:00:00",
|
||||||
|
"validate": "datetime"
|
||||||
|
},
|
||||||
|
"Command": {
|
||||||
|
"type": "input",
|
||||||
|
"value": "Bounty",
|
||||||
|
"display": "hide"
|
||||||
|
},
|
||||||
|
"ServerUpdate": {
|
||||||
|
"type": "input",
|
||||||
|
"value": "04:00",
|
||||||
|
"display": "hide"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Bounty": {
|
||||||
|
"OnError": {
|
||||||
|
"type": "select",
|
||||||
|
"value": "skip",
|
||||||
|
"option": [
|
||||||
|
"stop",
|
||||||
|
"skip"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Highway": {
|
||||||
|
"Stage": {
|
||||||
|
"type": "select",
|
||||||
|
"value": 1,
|
||||||
|
"option": [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
8,
|
||||||
|
9
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Count": {
|
||||||
|
"type": "input",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"DesertRailroad": {
|
||||||
|
"Stage": {
|
||||||
|
"type": "select",
|
||||||
|
"value": 1,
|
||||||
|
"option": [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
8,
|
||||||
|
9
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Count": {
|
||||||
|
"type": "input",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Schoolhouse": {
|
||||||
|
"Stage": {
|
||||||
|
"type": "select",
|
||||||
|
"value": 1,
|
||||||
|
"option": [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
8,
|
||||||
|
9
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Count": {
|
||||||
|
"type": "input",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Scrimmage": {
|
||||||
|
"Scheduler": {
|
||||||
|
"Enable": {
|
||||||
|
"type": "checkbox",
|
||||||
|
"value": true,
|
||||||
|
"option": [
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"NextRun": {
|
||||||
|
"type": "datetime",
|
||||||
|
"value": "2020-01-01 00:00:00",
|
||||||
|
"validate": "datetime"
|
||||||
|
},
|
||||||
|
"Command": {
|
||||||
|
"type": "input",
|
||||||
|
"value": "Scrimmage",
|
||||||
|
"display": "hide"
|
||||||
|
},
|
||||||
|
"ServerUpdate": {
|
||||||
|
"type": "input",
|
||||||
|
"value": "04:00",
|
||||||
|
"display": "hide"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Scrimmage": {
|
||||||
|
"OnError": {
|
||||||
|
"type": "select",
|
||||||
|
"value": "skip",
|
||||||
|
"option": [
|
||||||
|
"stop",
|
||||||
|
"skip"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Trinity": {
|
||||||
|
"Stage": {
|
||||||
|
"type": "select",
|
||||||
|
"value": 1,
|
||||||
|
"option": [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Count": {
|
||||||
|
"type": "input",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Gehenna": {
|
||||||
|
"Stage": {
|
||||||
|
"type": "select",
|
||||||
|
"value": 1,
|
||||||
|
"option": [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Count": {
|
||||||
|
"type": "input",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Millennium": {
|
||||||
|
"Stage": {
|
||||||
|
"type": "select",
|
||||||
|
"value": 1,
|
||||||
|
"option": [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Count": {
|
||||||
|
"type": "input",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"TacticalChallenge": {
|
"TacticalChallenge": {
|
||||||
"Scheduler": {
|
"Scheduler": {
|
||||||
"Enable": {
|
"Enable": {
|
||||||
@@ -346,6 +533,30 @@
|
|||||||
"stored": "StoredInt",
|
"stored": "StoredInt",
|
||||||
"order": 3,
|
"order": 3,
|
||||||
"color": "#21befc"
|
"color": "#21befc"
|
||||||
|
},
|
||||||
|
"BountyTicket": {
|
||||||
|
"type": "stored",
|
||||||
|
"value": {},
|
||||||
|
"display": "hide",
|
||||||
|
"stored": "StoredBountyTicket",
|
||||||
|
"order": 4,
|
||||||
|
"color": "#94cb44"
|
||||||
|
},
|
||||||
|
"ScrimmageTicket": {
|
||||||
|
"type": "stored",
|
||||||
|
"value": {},
|
||||||
|
"display": "hide",
|
||||||
|
"stored": "StoredScrimmageTicket",
|
||||||
|
"order": 5,
|
||||||
|
"color": "#f86c6a"
|
||||||
|
},
|
||||||
|
"TacticalChallengeTicket": {
|
||||||
|
"type": "stored",
|
||||||
|
"value": {},
|
||||||
|
"display": "hide",
|
||||||
|
"stored": "StoredTacticalChallengeTicket",
|
||||||
|
"order": 6,
|
||||||
|
"color": "#7ac8e5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 ]
|
||||||
@@ -81,6 +81,48 @@ Cafe:
|
|||||||
AutoAdjust: true
|
AutoAdjust: true
|
||||||
SecondCafe: true
|
SecondCafe: true
|
||||||
|
|
||||||
|
Bounty:
|
||||||
|
OnError:
|
||||||
|
value: skip
|
||||||
|
option: [ stop, skip ]
|
||||||
|
|
||||||
|
Highway:
|
||||||
|
Stage:
|
||||||
|
value: 1
|
||||||
|
option: [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
|
||||||
|
Count: 2
|
||||||
|
DesertRailroad:
|
||||||
|
Stage:
|
||||||
|
value: 1
|
||||||
|
option: [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
|
||||||
|
Count: 2
|
||||||
|
Schoolhouse:
|
||||||
|
Stage:
|
||||||
|
value: 1
|
||||||
|
option: [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
|
||||||
|
Count: 2
|
||||||
|
|
||||||
|
Scrimmage:
|
||||||
|
OnError:
|
||||||
|
value: skip
|
||||||
|
option: [ stop, skip ]
|
||||||
|
|
||||||
|
Trinity:
|
||||||
|
Stage:
|
||||||
|
value: 1
|
||||||
|
option: [ 1, 2, 3, 4 ]
|
||||||
|
Count: 2
|
||||||
|
Gehenna:
|
||||||
|
Stage:
|
||||||
|
value: 1
|
||||||
|
option: [ 1, 2, 3, 4 ]
|
||||||
|
Count: 2
|
||||||
|
Millennium:
|
||||||
|
Stage:
|
||||||
|
value: 1
|
||||||
|
option: [ 1, 2, 3, 4 ]
|
||||||
|
Count: 2
|
||||||
|
|
||||||
TacticalChallenge:
|
TacticalChallenge:
|
||||||
PlayerSelect:
|
PlayerSelect:
|
||||||
value: 0
|
value: 0
|
||||||
@@ -99,3 +141,15 @@ ItemStorage:
|
|||||||
stored: StoredInt
|
stored: StoredInt
|
||||||
order: 3
|
order: 3
|
||||||
color: "#21befc"
|
color: "#21befc"
|
||||||
|
BountyTicket:
|
||||||
|
stored: StoredBountyTicket
|
||||||
|
order: 4
|
||||||
|
color: "#94cb44"
|
||||||
|
ScrimmageTicket:
|
||||||
|
stored: StoredScrimmageTicket
|
||||||
|
order: 5
|
||||||
|
color: "#f86c6a"
|
||||||
|
TacticalChallengeTicket:
|
||||||
|
stored: StoredTacticalChallengeTicket
|
||||||
|
order: 6
|
||||||
|
color: "#7ac8e5"
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ Mail:
|
|||||||
Circle:
|
Circle:
|
||||||
Scheduler:
|
Scheduler:
|
||||||
Enable: true
|
Enable: true
|
||||||
|
Bounty:
|
||||||
|
Scheduler:
|
||||||
|
Enable: true
|
||||||
|
Scrimmage:
|
||||||
|
Scheduler:
|
||||||
|
Enable: true
|
||||||
TacticalChallenge:
|
TacticalChallenge:
|
||||||
Scheduler:
|
Scheduler:
|
||||||
Enable: true
|
Enable: true
|
||||||
@@ -14,6 +14,8 @@
|
|||||||
"Cafe",
|
"Cafe",
|
||||||
"Mail",
|
"Mail",
|
||||||
"Circle",
|
"Circle",
|
||||||
|
"Bounty",
|
||||||
|
"Scrimmage",
|
||||||
"TacticalChallenge",
|
"TacticalChallenge",
|
||||||
"DataUpdate"
|
"DataUpdate"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -35,5 +35,44 @@
|
|||||||
},
|
},
|
||||||
"order": 3,
|
"order": 3,
|
||||||
"color": "#21befc"
|
"color": "#21befc"
|
||||||
|
},
|
||||||
|
"BountyTicket": {
|
||||||
|
"name": "BountyTicket",
|
||||||
|
"path": "DataUpdate.ItemStorage.BountyTicket",
|
||||||
|
"i18n": "ItemStorage.BountyTicket.name",
|
||||||
|
"stored": "StoredBountyTicket",
|
||||||
|
"attrs": {
|
||||||
|
"time": "2020-01-01 00:00:00",
|
||||||
|
"total": 6,
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"order": 4,
|
||||||
|
"color": "#94cb44"
|
||||||
|
},
|
||||||
|
"ScrimmageTicket": {
|
||||||
|
"name": "ScrimmageTicket",
|
||||||
|
"path": "DataUpdate.ItemStorage.ScrimmageTicket",
|
||||||
|
"i18n": "ItemStorage.ScrimmageTicket.name",
|
||||||
|
"stored": "StoredScrimmageTicket",
|
||||||
|
"attrs": {
|
||||||
|
"time": "2020-01-01 00:00:00",
|
||||||
|
"total": 6,
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"order": 5,
|
||||||
|
"color": "#f86c6a"
|
||||||
|
},
|
||||||
|
"TacticalChallengeTicket": {
|
||||||
|
"name": "TacticalChallengeTicket",
|
||||||
|
"path": "DataUpdate.ItemStorage.TacticalChallengeTicket",
|
||||||
|
"i18n": "ItemStorage.TacticalChallengeTicket.name",
|
||||||
|
"stored": "StoredTacticalChallengeTicket",
|
||||||
|
"attrs": {
|
||||||
|
"time": "2020-01-01 00:00:00",
|
||||||
|
"total": 5,
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"order": 6,
|
||||||
|
"color": "#7ac8e5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,6 +29,18 @@ Daily:
|
|||||||
- Scheduler
|
- Scheduler
|
||||||
Circle:
|
Circle:
|
||||||
- Scheduler
|
- Scheduler
|
||||||
|
Bounty:
|
||||||
|
- Scheduler
|
||||||
|
- Bounty
|
||||||
|
- Highway
|
||||||
|
- DesertRailroad
|
||||||
|
- Schoolhouse
|
||||||
|
Scrimmage:
|
||||||
|
- Scheduler
|
||||||
|
- Scrimmage
|
||||||
|
- Trinity
|
||||||
|
- Gehenna
|
||||||
|
- Millennium
|
||||||
TacticalChallenge:
|
TacticalChallenge:
|
||||||
- Scheduler
|
- Scheduler
|
||||||
- TacticalChallenge
|
- TacticalChallenge
|
||||||
|
|||||||
@@ -499,19 +499,6 @@ class AzurLaneConfig(ConfigUpdater, ManualConfig, GeneratedConfig, ConfigWatcher
|
|||||||
self.task_call('DailyQuest')
|
self.task_call('DailyQuest')
|
||||||
self.task_stop()
|
self.task_stop()
|
||||||
|
|
||||||
def update_battle_pass_quests(self):
|
|
||||||
"""
|
|
||||||
Raises:
|
|
||||||
TaskEnd: Call task `BattlePass` and stop current task
|
|
||||||
"""
|
|
||||||
if self.stored.BattlePassTodayQuest.is_expired():
|
|
||||||
if self.stored.BattlePassLevel.is_full():
|
|
||||||
logger.info('BattlePassLevel full, no updates')
|
|
||||||
else:
|
|
||||||
logger.info('BattlePassTodayQuest expired, call task to update')
|
|
||||||
self.task_call('BattlePass')
|
|
||||||
self.task_stop()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def DEVICE_SCREENSHOT_METHOD(self):
|
def DEVICE_SCREENSHOT_METHOD(self):
|
||||||
return self.Emulator_ScreenshotMethod
|
return self.Emulator_ScreenshotMethod
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -45,6 +45,36 @@ class GeneratedConfig:
|
|||||||
Cafe_AutoAdjust = True
|
Cafe_AutoAdjust = True
|
||||||
Cafe_SecondCafe = True
|
Cafe_SecondCafe = True
|
||||||
|
|
||||||
|
# Group `Bounty`
|
||||||
|
Bounty_OnError = 'skip' # stop, skip
|
||||||
|
|
||||||
|
# Group `Highway`
|
||||||
|
Highway_Stage = 1 # 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||||
|
Highway_Count = 2
|
||||||
|
|
||||||
|
# Group `DesertRailroad`
|
||||||
|
DesertRailroad_Stage = 1 # 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||||
|
DesertRailroad_Count = 2
|
||||||
|
|
||||||
|
# Group `Schoolhouse`
|
||||||
|
Schoolhouse_Stage = 1 # 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||||
|
Schoolhouse_Count = 2
|
||||||
|
|
||||||
|
# Group `Scrimmage`
|
||||||
|
Scrimmage_OnError = 'skip' # stop, skip
|
||||||
|
|
||||||
|
# Group `Trinity`
|
||||||
|
Trinity_Stage = 1 # 1, 2, 3, 4
|
||||||
|
Trinity_Count = 2
|
||||||
|
|
||||||
|
# Group `Gehenna`
|
||||||
|
Gehenna_Stage = 1 # 1, 2, 3, 4
|
||||||
|
Gehenna_Count = 2
|
||||||
|
|
||||||
|
# Group `Millennium`
|
||||||
|
Millennium_Stage = 1 # 1, 2, 3, 4
|
||||||
|
Millennium_Count = 2
|
||||||
|
|
||||||
# Group `TacticalChallenge`
|
# Group `TacticalChallenge`
|
||||||
TacticalChallenge_PlayerSelect = 0 # 0, 1, 2, 3
|
TacticalChallenge_PlayerSelect = 0 # 0, 1, 2, 3
|
||||||
|
|
||||||
@@ -52,3 +82,6 @@ class GeneratedConfig:
|
|||||||
ItemStorage_AP = {}
|
ItemStorage_AP = {}
|
||||||
ItemStorage_Credit = {}
|
ItemStorage_Credit = {}
|
||||||
ItemStorage_Pyroxene = {}
|
ItemStorage_Pyroxene = {}
|
||||||
|
ItemStorage_BountyTicket = {}
|
||||||
|
ItemStorage_ScrimmageTicket = {}
|
||||||
|
ItemStorage_TacticalChallengeTicket = {}
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ class ManualConfig:
|
|||||||
|
|
||||||
SCHEDULER_PRIORITY = """
|
SCHEDULER_PRIORITY = """
|
||||||
Restart
|
Restart
|
||||||
> Cafe > Circle > Mail > DataUpdate > TacticalChallenge
|
> Cafe > Circle > Mail > DataUpdate > Bounty
|
||||||
|
> Scrimmage > TacticalChallenge
|
||||||
"""
|
"""
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -458,7 +458,8 @@ class ConfigUpdater:
|
|||||||
value = deep_get(old, keys=keys, default=data['value'])
|
value = deep_get(old, keys=keys, default=data['value'])
|
||||||
typ = data['type']
|
typ = data['type']
|
||||||
display = data.get('display')
|
display = data.get('display')
|
||||||
if is_template or value is None or value == '' or typ == 'lock' or (display == 'hide' and typ != 'stored'):
|
if (is_template or value is None or value == ''
|
||||||
|
or typ in ['lock', 'state'] or (display == 'hide' and typ != 'stored')):
|
||||||
value = data['value']
|
value = data['value']
|
||||||
value = parse_value(value, data=data)
|
value = parse_value(value, data=data)
|
||||||
deep_set(new, keys=keys, value=value)
|
deep_set(new, keys=keys, value=value)
|
||||||
|
|||||||
@@ -30,6 +30,14 @@
|
|||||||
"name": "Club",
|
"name": "Club",
|
||||||
"help": ""
|
"help": ""
|
||||||
},
|
},
|
||||||
|
"Bounty": {
|
||||||
|
"name": "Bounty",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
|
"Scrimmage": {
|
||||||
|
"name": "Scrimmage",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
"TacticalChallenge": {
|
"TacticalChallenge": {
|
||||||
"name": "Tactical Challenge",
|
"name": "Tactical Challenge",
|
||||||
"help": ""
|
"help": ""
|
||||||
@@ -76,13 +84,15 @@
|
|||||||
"name": "Game Server",
|
"name": "Game Server",
|
||||||
"help": "Can't distinguish different regions of oversea servers, please select the server manually.",
|
"help": "Can't distinguish different regions of oversea servers, please select the server manually.",
|
||||||
"auto": "Auto-detect",
|
"auto": "Auto-detect",
|
||||||
"JP-Official": "[JP]-Official"
|
"JP-Official": "[JP]-Official",
|
||||||
|
"OVERSEA-Global": "[OVERSEA]-Global"
|
||||||
},
|
},
|
||||||
"GameLanguage": {
|
"GameLanguage": {
|
||||||
"name": "In-game Text Language",
|
"name": "In-game Text Language",
|
||||||
"help": "Currently, only Simplified Chinese and English are supported. Please set the text language in game to one of them.",
|
"help": "",
|
||||||
"auto": "Auto-detect",
|
"auto": "Auto-detect",
|
||||||
"jp": "Japanese"
|
"jp": "Japanese",
|
||||||
|
"en": "English"
|
||||||
},
|
},
|
||||||
"ScreenshotMethod": {
|
"ScreenshotMethod": {
|
||||||
"name": "Screenshot Method",
|
"name": "Screenshot Method",
|
||||||
@@ -203,8 +213,155 @@
|
|||||||
"help": "Auto adjust cafe interface for better student interaction"
|
"help": "Auto adjust cafe interface for better student interaction"
|
||||||
},
|
},
|
||||||
"SecondCafe": {
|
"SecondCafe": {
|
||||||
"name": "Second Cafe",
|
"name": "Second Floor",
|
||||||
"help": "Enable auto switch to second cafe and perform interaction"
|
"help": "JP server only\nEnable auto switch to second floor and perform interaction"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Bounty": {
|
||||||
|
"_info": {
|
||||||
|
"name": "Bounty Settings",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
|
"OnError": {
|
||||||
|
"name": "Error Handling",
|
||||||
|
"help": "Perform the selected action when an error occurs (ticket not enough or any invalid setting)",
|
||||||
|
"stop": "Stop script",
|
||||||
|
"skip": "Skip current task"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Highway": {
|
||||||
|
"_info": {
|
||||||
|
"name": "Overpass",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
|
"Stage": {
|
||||||
|
"name": "Select Stage",
|
||||||
|
"help": "",
|
||||||
|
"1": "01 - Overpass A",
|
||||||
|
"2": "02 - Overpass B",
|
||||||
|
"3": "03 - Overpass C",
|
||||||
|
"4": "04 - Overpass D",
|
||||||
|
"5": "05 - Overpass E",
|
||||||
|
"6": "06 - Overpass F",
|
||||||
|
"7": "07 - Overpass G",
|
||||||
|
"8": "08 - Overpass H",
|
||||||
|
"9": "09 - Overpass I"
|
||||||
|
},
|
||||||
|
"Count": {
|
||||||
|
"name": "Sweep X times",
|
||||||
|
"help": "Sweep the selected stage for X times"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"DesertRailroad": {
|
||||||
|
"_info": {
|
||||||
|
"name": "Desert Railroad",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
|
"Stage": {
|
||||||
|
"name": "Select Stage",
|
||||||
|
"help": "",
|
||||||
|
"1": "01 - Abandoned Train A",
|
||||||
|
"2": "02 - Abandoned Train B",
|
||||||
|
"3": "03 - Abandoned Train C",
|
||||||
|
"4": "04 - Abandoned Train D",
|
||||||
|
"5": "05 - Abandoned Train E",
|
||||||
|
"6": "06 - Abandoned Train F",
|
||||||
|
"7": "07 - Abandoned Train G",
|
||||||
|
"8": "08 - Abandoned Train H",
|
||||||
|
"9": "09 - Abandoned Train I"
|
||||||
|
},
|
||||||
|
"Count": {
|
||||||
|
"name": "Sweep X times",
|
||||||
|
"help": "Sweep the selected stage for X times"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Schoolhouse": {
|
||||||
|
"_info": {
|
||||||
|
"name": "Classroom",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
|
"Stage": {
|
||||||
|
"name": "Select Stage",
|
||||||
|
"help": "",
|
||||||
|
"1": "01 - Besieged Classroom A",
|
||||||
|
"2": "02 - Besieged Classroom B",
|
||||||
|
"3": "03 - Besieged Classroom C",
|
||||||
|
"4": "04 - Besieged Classroom D",
|
||||||
|
"5": "05 - Besieged Classroom E",
|
||||||
|
"6": "06 - Besieged Classroom F",
|
||||||
|
"7": "07 - Besieged Classroom G",
|
||||||
|
"8": "08 - Besieged Classroom H",
|
||||||
|
"9": "09 - Besieged Classroom I"
|
||||||
|
},
|
||||||
|
"Count": {
|
||||||
|
"name": "Sweep X times",
|
||||||
|
"help": "Sweep the selected stage for X times"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Scrimmage": {
|
||||||
|
"_info": {
|
||||||
|
"name": "Scrimmage Settings",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
|
"OnError": {
|
||||||
|
"name": "Error Handling",
|
||||||
|
"help": "Perform the selected action when an error occurs (ticket not enough or any invalid setting)",
|
||||||
|
"stop": "Stop script",
|
||||||
|
"skip": "Skip current task"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Trinity": {
|
||||||
|
"_info": {
|
||||||
|
"name": "Trinity",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
|
"Stage": {
|
||||||
|
"name": "Select Stage",
|
||||||
|
"help": "",
|
||||||
|
"1": "01 - Trinity A",
|
||||||
|
"2": "02 - Trinity B",
|
||||||
|
"3": "03 - Trinity C",
|
||||||
|
"4": "04 - Trinity D"
|
||||||
|
},
|
||||||
|
"Count": {
|
||||||
|
"name": "Sweep X times",
|
||||||
|
"help": "Sweep the selected stage for X times"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Gehenna": {
|
||||||
|
"_info": {
|
||||||
|
"name": "Gehenna",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
|
"Stage": {
|
||||||
|
"name": "Select Stage",
|
||||||
|
"help": "",
|
||||||
|
"1": "01 - Gehenna A",
|
||||||
|
"2": "02 - Gehenna B",
|
||||||
|
"3": "03 - Gehenna C",
|
||||||
|
"4": "04 - Gehenna D"
|
||||||
|
},
|
||||||
|
"Count": {
|
||||||
|
"name": "Sweep X times",
|
||||||
|
"help": "Sweep the selected stage for X times"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Millennium": {
|
||||||
|
"_info": {
|
||||||
|
"name": "Millennium",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
|
"Stage": {
|
||||||
|
"name": "Select Stage",
|
||||||
|
"help": "",
|
||||||
|
"1": "01 - Millennium A",
|
||||||
|
"2": "02 - Millennium B",
|
||||||
|
"3": "03 - Millennium C",
|
||||||
|
"4": "04 - Millennium D"
|
||||||
|
},
|
||||||
|
"Count": {
|
||||||
|
"name": "Sweep X times",
|
||||||
|
"help": "Sweep the selected stage for X times"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"TacticalChallenge": {
|
"TacticalChallenge": {
|
||||||
@@ -237,6 +394,18 @@
|
|||||||
"Pyroxene": {
|
"Pyroxene": {
|
||||||
"name": "Pyroxene",
|
"name": "Pyroxene",
|
||||||
"help": ""
|
"help": ""
|
||||||
|
},
|
||||||
|
"BountyTicket": {
|
||||||
|
"name": "Bounty",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
|
"ScrimmageTicket": {
|
||||||
|
"name": "Scrimmage",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
|
"TacticalChallengeTicket": {
|
||||||
|
"name": "Tactical Challenge",
|
||||||
|
"help": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Gui": {
|
"Gui": {
|
||||||
|
|||||||
@@ -30,6 +30,14 @@
|
|||||||
"name": "公会",
|
"name": "公会",
|
||||||
"help": "社团 / 小组"
|
"help": "社团 / 小组"
|
||||||
},
|
},
|
||||||
|
"Bounty": {
|
||||||
|
"name": "悬赏通缉",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
|
"Scrimmage": {
|
||||||
|
"name": "学院交流会",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
"TacticalChallenge": {
|
"TacticalChallenge": {
|
||||||
"name": "战术对抗赛",
|
"name": "战术对抗赛",
|
||||||
"help": "战术大赛 / 竞技场"
|
"help": "战术大赛 / 竞技场"
|
||||||
@@ -76,13 +84,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": "模拟器截图方案",
|
||||||
@@ -204,7 +214,154 @@
|
|||||||
},
|
},
|
||||||
"SecondCafe": {
|
"SecondCafe": {
|
||||||
"name": "第二咖啡厅",
|
"name": "第二咖啡厅",
|
||||||
"help": "自动切换第二咖啡厅进行互动点击"
|
"help": "仅支持日服\n自动切换第二咖啡厅进行互动点击"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Bounty": {
|
||||||
|
"_info": {
|
||||||
|
"name": "悬赏通缉设置",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
|
"OnError": {
|
||||||
|
"name": "错误处理",
|
||||||
|
"help": "当出现票券不足或设置有误等错误时,执行该操作",
|
||||||
|
"stop": "停止脚本运行",
|
||||||
|
"skip": "跳过当前任务"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Highway": {
|
||||||
|
"_info": {
|
||||||
|
"name": "高架公路",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
|
"Stage": {
|
||||||
|
"name": "选择关卡",
|
||||||
|
"help": "",
|
||||||
|
"1": "01 - 高架公路 A",
|
||||||
|
"2": "02 - 高架公路 B",
|
||||||
|
"3": "03 - 高架公路 C",
|
||||||
|
"4": "04 - 高架公路 D",
|
||||||
|
"5": "05 - 高架公路 E",
|
||||||
|
"6": "06 - 高架公路 F",
|
||||||
|
"7": "07 - 高架公路 G",
|
||||||
|
"8": "08 - 高架公路 H",
|
||||||
|
"9": "09 - 高架公路 I"
|
||||||
|
},
|
||||||
|
"Count": {
|
||||||
|
"name": "扫荡次数",
|
||||||
|
"help": "扫荡所选择关卡 X 次"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"DesertRailroad": {
|
||||||
|
"_info": {
|
||||||
|
"name": "沙漠铁路",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
|
"Stage": {
|
||||||
|
"name": "选择关卡",
|
||||||
|
"help": "",
|
||||||
|
"1": "01 - 被遗弃的列车 A",
|
||||||
|
"2": "02 - 被遗弃的列车 B",
|
||||||
|
"3": "03 - 被遗弃的列车 C",
|
||||||
|
"4": "04 - 被遗弃的列车 D",
|
||||||
|
"5": "05 - 被遗弃的列车 E",
|
||||||
|
"6": "06 - 被遗弃的列车 F",
|
||||||
|
"7": "07 - 被遗弃的列车 G",
|
||||||
|
"8": "08 - 被遗弃的列车 H",
|
||||||
|
"9": "09 - 被遗弃的列车 I"
|
||||||
|
},
|
||||||
|
"Count": {
|
||||||
|
"name": "扫荡次数",
|
||||||
|
"help": "扫荡所选择关卡 X 次"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Schoolhouse": {
|
||||||
|
"_info": {
|
||||||
|
"name": "教室",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
|
"Stage": {
|
||||||
|
"name": "选择关卡",
|
||||||
|
"help": "",
|
||||||
|
"1": "01 - 被袭击的教室 A",
|
||||||
|
"2": "02 - 被袭击的教室 B",
|
||||||
|
"3": "03 - 被袭击的教室 C",
|
||||||
|
"4": "04 - 被袭击的教室 D",
|
||||||
|
"5": "05 - 被袭击的教室 E",
|
||||||
|
"6": "06 - 被袭击的教室 F",
|
||||||
|
"7": "07 - 被袭击的教室 G",
|
||||||
|
"8": "08 - 被袭击的教室 H",
|
||||||
|
"9": "09 - 被袭击的教室 I"
|
||||||
|
},
|
||||||
|
"Count": {
|
||||||
|
"name": "扫荡次数",
|
||||||
|
"help": "扫荡所选择关卡 X 次"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Scrimmage": {
|
||||||
|
"_info": {
|
||||||
|
"name": "学院交流会设置",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
|
"OnError": {
|
||||||
|
"name": "错误处理",
|
||||||
|
"help": "当出现票券不足或设置有误等错误时,执行该操作",
|
||||||
|
"stop": "停止脚本运行",
|
||||||
|
"skip": "跳过当前任务"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Trinity": {
|
||||||
|
"_info": {
|
||||||
|
"name": "三一",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
|
"Stage": {
|
||||||
|
"name": "选择关卡",
|
||||||
|
"help": "",
|
||||||
|
"1": "01 - 三一 A",
|
||||||
|
"2": "02 - 三一 B",
|
||||||
|
"3": "03 - 三一 C",
|
||||||
|
"4": "04 - 三一 D"
|
||||||
|
},
|
||||||
|
"Count": {
|
||||||
|
"name": "扫荡次数",
|
||||||
|
"help": "扫荡所选择关卡 X 次"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Gehenna": {
|
||||||
|
"_info": {
|
||||||
|
"name": "格黑娜",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
|
"Stage": {
|
||||||
|
"name": "选择关卡",
|
||||||
|
"help": "",
|
||||||
|
"1": "01 - 格黑娜 A",
|
||||||
|
"2": "02 - 格黑娜 B",
|
||||||
|
"3": "03 - 格黑娜 C",
|
||||||
|
"4": "04 - 格黑娜 D"
|
||||||
|
},
|
||||||
|
"Count": {
|
||||||
|
"name": "扫荡次数",
|
||||||
|
"help": "扫荡所选择关卡 X 次"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Millennium": {
|
||||||
|
"_info": {
|
||||||
|
"name": "千年",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
|
"Stage": {
|
||||||
|
"name": "选择关卡",
|
||||||
|
"help": "",
|
||||||
|
"1": "01 - 千年 A",
|
||||||
|
"2": "02 - 千年 B",
|
||||||
|
"3": "03 - 千年 C",
|
||||||
|
"4": "04 - 千年 D"
|
||||||
|
},
|
||||||
|
"Count": {
|
||||||
|
"name": "扫荡次数",
|
||||||
|
"help": "扫荡所选择关卡 X 次"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"TacticalChallenge": {
|
"TacticalChallenge": {
|
||||||
@@ -237,6 +394,18 @@
|
|||||||
"Pyroxene": {
|
"Pyroxene": {
|
||||||
"name": "青辉石",
|
"name": "青辉石",
|
||||||
"help": ""
|
"help": ""
|
||||||
|
},
|
||||||
|
"BountyTicket": {
|
||||||
|
"name": "悬赏通缉",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
|
"ScrimmageTicket": {
|
||||||
|
"name": "学院交流会",
|
||||||
|
"help": ""
|
||||||
|
},
|
||||||
|
"TacticalChallengeTicket": {
|
||||||
|
"name": "战术对抗赛",
|
||||||
|
"help": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Gui": {
|
"Gui": {
|
||||||
|
|||||||
@@ -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()))
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ from functools import cached_property as functools_cached_property
|
|||||||
|
|
||||||
from module.base.decorator import cached_property
|
from module.base.decorator import cached_property
|
||||||
from module.config.utils import DEFAULT_TIME, deep_get, get_server_last_monday_update, get_server_last_update
|
from module.config.utils import DEFAULT_TIME, deep_get, get_server_last_monday_update, get_server_last_update
|
||||||
|
|
||||||
|
|
||||||
# from module.exception import ScriptError
|
# from module.exception import ScriptError
|
||||||
|
|
||||||
|
|
||||||
@@ -173,6 +175,20 @@ class StoredCounter(StoredBase):
|
|||||||
class StoredAP(StoredCounter):
|
class StoredAP(StoredCounter):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class StoredBountyTicket(StoredCounter, StoredExpiredAt0400):
|
||||||
|
FIXED_TOTAL = 6
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class StoredScrimmageTicket(StoredCounter, StoredExpiredAt0400):
|
||||||
|
FIXED_TOTAL = 6
|
||||||
|
|
||||||
|
|
||||||
|
class StoredTacticalChallengeTicket(StoredCounter, StoredExpiredAt0400):
|
||||||
|
FIXED_TOTAL = 5
|
||||||
|
|
||||||
|
|
||||||
# class StoredDailyActivity(StoredCounter, StoredExpiredAt0400):
|
# class StoredDailyActivity(StoredCounter, StoredExpiredAt0400):
|
||||||
# FIXED_TOTAL = 500
|
# FIXED_TOTAL = 500
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
from module.config.stored.classes import (
|
from module.config.stored.classes import (
|
||||||
StoredAP,
|
StoredAP,
|
||||||
StoredBase,
|
StoredBountyTicket,
|
||||||
StoredCounter,
|
|
||||||
StoredExpiredAt0400,
|
|
||||||
StoredExpiredAtMonday0400,
|
|
||||||
StoredInt,
|
StoredInt,
|
||||||
|
StoredScrimmageTicket,
|
||||||
|
StoredTacticalChallengeTicket,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -15,3 +14,6 @@ class StoredGenerated:
|
|||||||
AP = StoredAP("DataUpdate.ItemStorage.AP")
|
AP = StoredAP("DataUpdate.ItemStorage.AP")
|
||||||
Credit = StoredInt("DataUpdate.ItemStorage.Credit")
|
Credit = StoredInt("DataUpdate.ItemStorage.Credit")
|
||||||
Pyroxene = StoredInt("DataUpdate.ItemStorage.Pyroxene")
|
Pyroxene = StoredInt("DataUpdate.ItemStorage.Pyroxene")
|
||||||
|
BountyTicket = StoredBountyTicket("DataUpdate.ItemStorage.BountyTicket")
|
||||||
|
ScrimmageTicket = StoredScrimmageTicket("DataUpdate.ItemStorage.ScrimmageTicket")
|
||||||
|
TacticalChallengeTicket = StoredTacticalChallengeTicket("DataUpdate.ItemStorage.TacticalChallengeTicket")
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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,6 +1,6 @@
|
|||||||
from pponnxcr import TextSystem as TextSystem_
|
from pponnxcr import TextSystem as TextSystem_
|
||||||
|
|
||||||
from module.base.decorator import cached_property
|
from module.base.decorator import cached_property, del_cached_property
|
||||||
from module.exception import ScriptError
|
from module.exception import ScriptError
|
||||||
|
|
||||||
DIC_LANG_TO_MODEL = {
|
DIC_LANG_TO_MODEL = {
|
||||||
@@ -56,6 +56,12 @@ class OcrModel:
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
raise ScriptError(f'OCR model under lang "{lang}" does not exists')
|
raise ScriptError(f'OCR model under lang "{lang}" does not exists')
|
||||||
|
|
||||||
|
def resource_release(self):
|
||||||
|
del_cached_property(self, 'zhs')
|
||||||
|
del_cached_property(self, 'en')
|
||||||
|
del_cached_property(self, 'ja')
|
||||||
|
del_cached_property(self, 'zht')
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def zhs(self):
|
def zhs(self):
|
||||||
return TextSystem('zhs')
|
return TextSystem('zhs')
|
||||||
|
|||||||
@@ -89,12 +89,15 @@ def readable_time(before: str) -> str:
|
|||||||
elif diff < 60:
|
elif diff < 60:
|
||||||
# < 1 min
|
# < 1 min
|
||||||
return t("Gui.Dashboard.JustNow")
|
return t("Gui.Dashboard.JustNow")
|
||||||
elif diff < 3600:
|
elif diff < 5400:
|
||||||
|
# < 90 min
|
||||||
return t("Gui.Dashboard.MinutesAgo", time=int(diff // 60))
|
return t("Gui.Dashboard.MinutesAgo", time=int(diff // 60))
|
||||||
elif diff < 86400:
|
elif diff < 129600:
|
||||||
|
# < 36 hours
|
||||||
return t("Gui.Dashboard.HoursAgo", time=int(diff // 3600))
|
return t("Gui.Dashboard.HoursAgo", time=int(diff // 3600))
|
||||||
elif diff < 1296000:
|
elif diff < 1296000:
|
||||||
|
# < 15 days
|
||||||
return t("Gui.Dashboard.DaysAgo", time=int(diff // 86400))
|
return t("Gui.Dashboard.DaysAgo", time=int(diff // 86400))
|
||||||
else:
|
else:
|
||||||
# > 15 days
|
# >= 15 days
|
||||||
return t("Gui.Dashboard.LongTimeAgo")
|
return t("Gui.Dashboard.LongTimeAgo")
|
||||||
|
|||||||
@@ -9,17 +9,17 @@ from queue import Queue
|
|||||||
from typing import Callable, Generator, List
|
from typing import Callable, Generator, List
|
||||||
|
|
||||||
import pywebio
|
import pywebio
|
||||||
from module.config.utils import deep_iter
|
|
||||||
from module.logger import logger
|
|
||||||
from module.webui.setting import State
|
|
||||||
from pywebio.input import PASSWORD, input
|
from pywebio.input import PASSWORD, input
|
||||||
from pywebio.output import PopupSize, popup, put_html, toast
|
from pywebio.output import PopupSize, popup, put_html, toast
|
||||||
from pywebio.session import eval_js
|
from pywebio.session import eval_js
|
||||||
from pywebio.session import info as session_info
|
from pywebio.session import info as session_info
|
||||||
from pywebio.session import register_thread, run_js
|
from pywebio.session import register_thread, run_js
|
||||||
from rich.console import Console, ConsoleOptions
|
from rich.console import Console
|
||||||
from rich.terminal_theme import TerminalTheme
|
from rich.terminal_theme import TerminalTheme
|
||||||
|
|
||||||
|
from module.config.utils import deep_iter
|
||||||
|
from module.logger import logger
|
||||||
|
from module.webui.setting import State
|
||||||
|
|
||||||
RE_DATETIME = (
|
RE_DATETIME = (
|
||||||
r"\d{4}\-(0\d|1[0-2])\-([0-2]\d|[3][0-1]) "
|
r"\d{4}\-(0\d|1[0-2])\-([0-2]\d|[3][0-1]) "
|
||||||
@@ -455,7 +455,11 @@ def get_localstorage(key):
|
|||||||
|
|
||||||
def re_fullmatch(pattern, string):
|
def re_fullmatch(pattern, string):
|
||||||
if pattern == "datetime":
|
if pattern == "datetime":
|
||||||
pattern = RE_DATETIME
|
try:
|
||||||
|
datetime.datetime.fromisoformat(string)
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
# elif:
|
# elif:
|
||||||
return re.fullmatch(pattern=pattern, string=string)
|
return re.fullmatch(pattern=pattern, string=string)
|
||||||
|
|
||||||
|
|||||||