Skip to content

Commit a00c12f

Browse files
committed
Merge branch 'master' of https://github.com/LmeSzinc/AzurLaneAutoScript into dev
2 parents 3efc813 + e02bf6d commit a00c12f

11 files changed

Lines changed: 114 additions & 63 deletions

File tree

assets/jp/combat/GET_ITEMS_1.png

-4.34 KB
Loading

assets/jp/combat/GET_ITEMS_2.png

-3.63 KB
Loading

assets/jp/combat/GET_ITEMS_3.png

-3.32 KB
Loading
6.32 KB
Loading
6.63 KB
Loading

campaign/Readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,4 +301,4 @@ To add a new event, add a new row in here, and run `python -m module.config.conf
301301
| 20260528 | event 20220818 cn | Operation Convergence | - | - | - | 復刻遠匯點作戰 |
302302
| 20260605 | event 20260520 cn | Alliance Before the Hagiobull | - | - | - | 聖印前的同盟 |
303303
| 20260618 | event 20240521 cn | Light of the Martyrium Rerun | 复刻绽放于辉光之城 | Light of the Martyrium Rerun | 赫輝のマルティリウム(復刻) | - |
304-
| 20260625 | event 20260625 cn | - | 美梦巡演:奇妙夜 | - | - | - |
304+
| 20260625 | event 20260625 cn | Miracle by Midnight | 美梦巡演:奇妙夜 | - | 幻夢のカヴァルカード | - |

module/combat/assets.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@
2929
EXP_INFO_C = Button(area={'cn': (332, 56, 345, 107), 'en': (332, 56, 345, 107), 'jp': (332, 56, 345, 107), 'tw': (332, 56, 345, 107)}, color={'cn': (198, 208, 198), 'en': (198, 208, 198), 'jp': (198, 208, 198), 'tw': (198, 208, 198)}, button={'cn': (1133, 634, 1262, 650), 'en': (1133, 634, 1262, 650), 'jp': (1133, 634, 1262, 650), 'tw': (1133, 634, 1262, 650)}, file={'cn': './assets/cn/combat/EXP_INFO_C.png', 'en': './assets/en/combat/EXP_INFO_C.png', 'jp': './assets/jp/combat/EXP_INFO_C.png', 'tw': './assets/tw/combat/EXP_INFO_C.png'})
3030
EXP_INFO_D = Button(area={'cn': (328, 45, 341, 119), 'en': (328, 45, 341, 119), 'jp': (328, 45, 341, 119), 'tw': (328, 45, 341, 119)}, color={'cn': (199, 208, 199), 'en': (199, 208, 199), 'jp': (199, 208, 199), 'tw': (199, 208, 199)}, button={'cn': (1133, 634, 1262, 650), 'en': (1133, 634, 1262, 650), 'jp': (1133, 634, 1262, 650), 'tw': (1133, 634, 1262, 650)}, file={'cn': './assets/cn/combat/EXP_INFO_D.png', 'en': './assets/en/combat/EXP_INFO_D.png', 'jp': './assets/jp/combat/EXP_INFO_D.png', 'tw': './assets/tw/combat/EXP_INFO_D.png'})
3131
EXP_INFO_S = Button(area={'cn': (342, 107, 389, 119), 'en': (342, 107, 389, 119), 'jp': (342, 107, 389, 119), 'tw': (342, 107, 389, 119)}, color={'cn': (233, 242, 127), 'en': (233, 242, 127), 'jp': (233, 242, 127), 'tw': (233, 242, 127)}, button={'cn': (1133, 634, 1262, 650), 'en': (1133, 634, 1262, 650), 'jp': (1133, 634, 1262, 650), 'tw': (1133, 634, 1262, 650)}, file={'cn': './assets/cn/combat/EXP_INFO_S.png', 'en': './assets/en/combat/EXP_INFO_S.png', 'jp': './assets/jp/combat/EXP_INFO_S.png', 'tw': './assets/tw/combat/EXP_INFO_S.png'})
32-
GET_ITEMS_1 = Button(area={'cn': (538, 217, 741, 253), 'en': (551, 223, 736, 250), 'jp': (548, 217, 741, 253), 'tw': (539, 217, 742, 253)}, color={'cn': (160, 192, 248), 'en': (166, 194, 235), 'jp': (144, 183, 250), 'tw': (155, 190, 248)}, button={'cn': (1000, 631, 1055, 689), 'en': (999, 630, 1047, 691), 'jp': (1000, 631, 1055, 689), 'tw': (1000, 631, 1055, 689)}, file={'cn': './assets/cn/combat/GET_ITEMS_1.png', 'en': './assets/en/combat/GET_ITEMS_1.png', 'jp': './assets/jp/combat/GET_ITEMS_1.png', 'tw': './assets/tw/combat/GET_ITEMS_1.png'})
32+
GET_ITEMS_1 = Button(area={'cn': (538, 217, 741, 253), 'en': (551, 223, 736, 250), 'jp': (539, 220, 741, 252), 'tw': (539, 217, 742, 253)}, color={'cn': (160, 192, 248), 'en': (166, 194, 235), 'jp': (146, 184, 249), 'tw': (155, 190, 248)}, button={'cn': (1000, 631, 1055, 689), 'en': (999, 630, 1047, 691), 'jp': (1000, 631, 1055, 689), 'tw': (1000, 631, 1055, 689)}, file={'cn': './assets/cn/combat/GET_ITEMS_1.png', 'en': './assets/en/combat/GET_ITEMS_1.png', 'jp': './assets/jp/combat/GET_ITEMS_1.png', 'tw': './assets/tw/combat/GET_ITEMS_1.png'})
3333
GET_ITEMS_1_RYZA = Button(area={'cn': (564, 217, 721, 245), 'en': (577, 211, 704, 239), 'jp': (566, 217, 719, 244), 'tw': (564, 218, 723, 246)}, color={'cn': (176, 199, 243), 'en': (172, 199, 246), 'jp': (179, 201, 243), 'tw': (173, 197, 242)}, button={'cn': (1000, 631, 1055, 689), 'en': (1000, 631, 1055, 689), 'jp': (1000, 631, 1055, 689), 'tw': (1000, 631, 1055, 689)}, file={'cn': './assets/cn/combat/GET_ITEMS_1_RYZA.png', 'en': './assets/en/combat/GET_ITEMS_1_RYZA.png', 'jp': './assets/jp/combat/GET_ITEMS_1_RYZA.png', 'tw': './assets/tw/combat/GET_ITEMS_1_RYZA.png'})
34-
GET_ITEMS_2 = Button(area={'cn': (538, 146, 742, 182), 'en': (551, 149, 735, 175), 'jp': (547, 143, 742, 179), 'tw': (538, 148, 741, 182)}, color={'cn': (160, 192, 248), 'en': (167, 195, 235), 'jp': (145, 183, 250), 'tw': (155, 190, 248)}, button={'cn': (1000, 631, 1055, 689), 'en': (999, 630, 1047, 691), 'jp': (1000, 631, 1055, 689), 'tw': (1000, 631, 1055, 689)}, file={'cn': './assets/cn/combat/GET_ITEMS_2.png', 'en': './assets/en/combat/GET_ITEMS_2.png', 'jp': './assets/jp/combat/GET_ITEMS_2.png', 'tw': './assets/tw/combat/GET_ITEMS_2.png'})
35-
GET_ITEMS_3 = Button(area={'cn': (539, 143, 742, 179), 'en': (548, 136, 740, 172), 'jp': (547, 143, 742, 179), 'tw': (546, 145, 742, 178)}, color={'cn': (161, 193, 248), 'en': (152, 185, 237), 'jp': (145, 183, 250), 'tw': (156, 190, 248)}, button={'cn': (1000, 631, 1055, 689), 'en': (999, 630, 1047, 691), 'jp': (1000, 631, 1055, 689), 'tw': (1000, 631, 1055, 689)}, file={'cn': './assets/cn/combat/GET_ITEMS_3.png', 'en': './assets/en/combat/GET_ITEMS_3.png', 'jp': './assets/jp/combat/GET_ITEMS_3.png', 'tw': './assets/tw/combat/GET_ITEMS_3.png'})
34+
GET_ITEMS_2 = Button(area={'cn': (538, 146, 742, 182), 'en': (551, 149, 735, 175), 'jp': (536, 146, 741, 182), 'tw': (538, 148, 741, 182)}, color={'cn': (160, 192, 248), 'en': (167, 195, 235), 'jp': (145, 182, 249), 'tw': (155, 190, 248)}, button={'cn': (1000, 631, 1055, 689), 'en': (999, 630, 1047, 691), 'jp': (1000, 631, 1055, 689), 'tw': (1000, 631, 1055, 689)}, file={'cn': './assets/cn/combat/GET_ITEMS_2.png', 'en': './assets/en/combat/GET_ITEMS_2.png', 'jp': './assets/jp/combat/GET_ITEMS_2.png', 'tw': './assets/tw/combat/GET_ITEMS_2.png'})
35+
GET_ITEMS_3 = Button(area={'cn': (539, 143, 742, 179), 'en': (548, 136, 740, 172), 'jp': (540, 143, 742, 179), 'tw': (546, 145, 742, 178)}, color={'cn': (161, 193, 248), 'en': (152, 185, 237), 'jp': (145, 182, 248), 'tw': (156, 190, 248)}, button={'cn': (1000, 631, 1055, 689), 'en': (999, 630, 1047, 691), 'jp': (1000, 631, 1055, 689), 'tw': (1000, 631, 1055, 689)}, file={'cn': './assets/cn/combat/GET_ITEMS_3.png', 'en': './assets/en/combat/GET_ITEMS_3.png', 'jp': './assets/jp/combat/GET_ITEMS_3.png', 'tw': './assets/tw/combat/GET_ITEMS_3.png'})
3636
GET_ITEMS_3_CHECK = Button(area={'cn': (335, 184, 947, 203), 'en': (335, 184, 947, 203), 'jp': (335, 184, 947, 203), 'tw': (335, 184, 947, 203)}, color={'cn': (84, 95, 109), 'en': (84, 95, 109), 'jp': (84, 95, 109), 'tw': (84, 95, 109)}, button={'cn': (335, 184, 947, 203), 'en': (335, 184, 947, 203), 'jp': (335, 184, 947, 203), 'tw': (335, 184, 947, 203)}, file={'cn': './assets/cn/combat/GET_ITEMS_3_CHECK.png', 'en': './assets/en/combat/GET_ITEMS_3_CHECK.png', 'jp': './assets/jp/combat/GET_ITEMS_3_CHECK.png', 'tw': './assets/tw/combat/GET_ITEMS_3_CHECK.png'})
3737
GET_SHIP = Button(area={'cn': (1104, 610, 1110, 630), 'en': (1104, 610, 1110, 630), 'jp': (1104, 610, 1110, 630), 'tw': (1104, 610, 1110, 630)}, color={'cn': (255, 255, 255), 'en': (255, 255, 255), 'jp': (255, 255, 255), 'tw': (255, 255, 255)}, button={'cn': (1000, 631, 1055, 689), 'en': (999, 630, 1047, 691), 'jp': (1000, 631, 1055, 689), 'tw': (1000, 631, 1055, 689)}, file={'cn': './assets/cn/combat/GET_SHIP.png', 'en': './assets/en/combat/GET_SHIP.png', 'jp': './assets/jp/combat/GET_SHIP.png', 'tw': './assets/tw/combat/GET_SHIP.png'})
3838
LOADING_BAR = Button(area={'cn': (33, 676, 1247, 680), 'en': (33, 676, 1247, 680), 'jp': (33, 676, 1247, 680), 'tw': (33, 676, 1247, 680)}, color={'cn': (172, 205, 232), 'en': (172, 205, 232), 'jp': (172, 205, 232), 'tw': (172, 205, 232)}, button={'cn': (33, 676, 1247, 680), 'en': (33, 676, 1247, 680), 'jp': (33, 676, 1247, 680), 'tw': (33, 676, 1247, 680)}, file={'cn': './assets/cn/combat/LOADING_BAR.png', 'en': './assets/en/combat/LOADING_BAR.png', 'jp': './assets/jp/combat/LOADING_BAR.png', 'tw': './assets/tw/combat/LOADING_BAR.png'})

module/commission/project.py

Lines changed: 97 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from datetime import datetime, timedelta
22

3-
import module.config.server as server
43
from module.base.decorator import Config
54
from module.base.filter import Filter
65
from module.base.utils import *
@@ -23,36 +22,56 @@
2322
)
2423

2524

26-
class SuffixOcr(Ocr):
27-
"""后缀 OCR 识别器,用于识别委托名称末尾的罗马数字后缀
25+
def crop_suffix_image(image, area):
26+
"""裁剪委托名称右侧的罗马数字后缀图像
2827
29-
预处理时裁剪掉图像右侧空白区域,只保留后缀部分以提高识别准确率。
30-
"""
31-
32-
def pre_process(self, image):
33-
"""预处理图像,裁剪右侧空白区域以聚焦后缀字符。
34-
35-
通过检测每列像素最小值定位文本右边界,再向左回退若干像素
36-
以确保完整保留后缀字符。日服字符较宽,需要更大的回退量。
37-
38-
Args:
39-
image: 输入的灰度图像。
40-
41-
Returns:
42-
裁剪后的图像。
43-
"""
44-
image = super().pre_process(image)
28+
Args:
29+
image: 游戏截图。
30+
area: 委托名称区域。
4531
46-
left = np.where(np.min(image[5:-5, :], axis=0) < 85)[0]
47-
# 日服字符较宽,需要回退更多像素
48-
if server.server in ['jp']:
49-
look_back = 21
50-
else:
51-
look_back = 18
52-
if len(left):
53-
image = image[:, left[-1] - look_back:]
32+
Returns:
33+
后缀裁剪图,黑字白底;未检测到文字时返回 None。
34+
"""
35+
name_image = crop(image, area)
36+
name_image = extract_letters(name_image, letter=(255, 255, 255), threshold=128).astype(np.uint8)
37+
38+
line = cv2.reduce(name_image[5:-5, :], 0, cv2.REDUCE_AVG).flatten()
39+
columns = np.where(line < 250)[0]
40+
if not len(columns):
41+
return None
42+
43+
# 从最右侧文字向左回看,尽量完整包含罗马数字后缀。
44+
threshold = 250
45+
look_back = 10
46+
for i in range(columns[-1], 0, -1):
47+
if line[i] > threshold:
48+
if columns[-1] - i > look_back:
49+
look_back = columns[-1] - i
50+
break
51+
52+
left = columns[-1] - look_back
53+
right = columns[-1] + 1
54+
x1, y1 = area[0:2]
55+
suffix_area = area_offset((left - 3, -3, right + 3, name_image.shape[0] + 3), (x1, y1))
56+
image = crop(image, suffix_area)
57+
image = extract_letters(image, letter=(255, 255, 255), threshold=128).astype(np.uint8)
58+
return image
59+
60+
61+
def image_hash(image):
62+
"""计算图像哈希,用于日志输出。
63+
64+
Args:
65+
image: 输入图像。
66+
67+
Returns:
68+
图像 MD5;图像为空时返回空字符串。
69+
"""
70+
if image is None:
71+
return ''
5472

55-
return image
73+
import hashlib
74+
return hashlib.md5(image.tobytes()).hexdigest()
5675

5776

5877
class Commission:
@@ -68,9 +87,10 @@ class Commission:
6887
name: str
6988
# 委托名称是否解析成功
7089
valid: bool
71-
# 罗马数字后缀,委托无后缀时可能识别错误
72-
# 值: ⅠⅡⅢⅤⅣⅥ
73-
suffix: str
90+
# 裁剪出的后缀图像,黑字白底;无后缀时为 None
91+
suffix_image: np.ndarray
92+
# 后缀图像哈希,仅用于日志;无后缀时为空字符串
93+
suffix_hash: str
7494
# 委托类型名称,定义在 project_data.py 中
7595
# 值: major_comm, daily_resource, urgent_cube, ...
7696
genre: str
@@ -146,9 +166,9 @@ def commission_parse(self):
146166
self.name = result
147167
self.genre = self.commission_name_parse(self.name)
148168

149-
# 后缀识别
150-
ocr = SuffixOcr(button, lang='azur_lane', letter=(255, 255, 255), threshold=128, alphabet='IV')
151-
self.suffix = self.beautify_name(ocr.ocr(self.image))
169+
# 后缀图像识别
170+
self.suffix_image = crop_suffix_image(self.image, self.button.area)
171+
self.suffix_hash = image_hash(self.suffix_image)
152172

153173
# 执行时长
154174
area = area_offset((290, 68, 390, 95), self.area[0:2])
@@ -198,9 +218,9 @@ def commission_parse(self):
198218
self.name = result
199219
self.genre = self.commission_name_parse(self.name)
200220

201-
# 后缀识别
202-
ocr = SuffixOcr(button, lang='azur_lane', letter=(255, 255, 255), threshold=128, alphabet='IV')
203-
self.suffix = self.beautify_name(ocr.ocr(self.image))
221+
# 后缀图像识别
222+
self.suffix_image = crop_suffix_image(self.image, self.button.area)
223+
self.suffix_hash = image_hash(self.suffix_image)
204224

205225
# 执行时长
206226
area = area_offset((290, 68, 390, 95), self.area[0:2])
@@ -254,9 +274,9 @@ def commission_parse(self):
254274
self.name = result
255275
self.genre = self.commission_name_parse(self.name)
256276

257-
# 后缀识别
258-
ocr = SuffixOcr(button, lang='azur_lane', letter=(255, 255, 255), threshold=128, alphabet='IV')
259-
self.suffix = self.beautify_name(ocr.ocr(self.image))
277+
# 后缀图像识别
278+
self.suffix_image = crop_suffix_image(self.image, self.button.area)
279+
self.suffix_hash = image_hash(self.suffix_image)
260280

261281
# 执行时长
262282
area = area_offset((290, 68, 390, 95), self.area[0:2])
@@ -292,7 +312,7 @@ def commission_parse(self):
292312
def commission_parse(self):
293313
"""解析委托信息(CN 服务器,默认回退)。
294314
295-
CN 服后缀直接从名称末尾提取罗马数字,不使用独立 OCR
315+
CN 服同样裁剪名称右侧后缀图像,用于后续相似度匹配
296316
解析内容:名称、后缀、时长、过期时间、状态。
297317
"""
298318
# 名称识别
@@ -306,8 +326,9 @@ def commission_parse(self):
306326
self.name = result
307327
self.genre = self.commission_name_parse(self.name)
308328

309-
# 后缀——直接从名称末尾提取罗马数字
310-
self.suffix = self.beautify_name(''.join(c for c in result[-4:] if c in 'IV'))
329+
# 后缀图像识别
330+
self.suffix_image = crop_suffix_image(self.image, self.button.area)
331+
self.suffix_hash = image_hash(self.suffix_image)
311332

312333
# 执行时长
313334
area = area_offset((290, 68, 390, 95), self.area[0:2])
@@ -341,7 +362,7 @@ def commission_parse(self):
341362

342363
def __str__(self):
343364
"""返回委托的可读字符串表示,包含名称、类型、状态和时长。"""
344-
name = f'{self.name} | {self.suffix}'
365+
name = f'{self.name} | {self.suffix_hash}' if self.suffix_hash else self.name
345366
if not self.valid:
346367
return f'{name} (Invalid)'
347368
info = {'Genre': self.genre, 'Status': self.status, 'Duration': self.duration}
@@ -372,7 +393,7 @@ def __eq__(self, other):
372393
if self.genre != other.genre or self.status != other.status:
373394
return False
374395
if self.category_str == 'daily':
375-
if self.suffix != other.suffix:
396+
if not self.suffix_match(other):
376397
return False
377398
if self.genre == 'urgent_box':
378399
for tag in ['NYB', 'BIW']:
@@ -389,7 +410,7 @@ def __eq__(self, other):
389410
return False
390411
if self.repeat_count != other.repeat_count:
391412
return False
392-
if self.genre in ['extra_oil', 'night_oil'] and self.suffix != other.suffix:
413+
if self.genre in ['extra_oil', 'night_oil'] and not self.suffix_match(other):
393414
return False
394415

395416
return True
@@ -398,6 +419,36 @@ def __hash__(self):
398419
"""返回委托的哈希值,基于类型和名称。"""
399420
return hash(f'{self.genre}_{self.name}')
400421

422+
def suffix_match(self, other, similarity=0.75):
423+
"""判断两个委托的后缀图像是否匹配。
424+
425+
Args:
426+
other: 要比较的委托对象。
427+
similarity: 相似度阈值,范围 0-1。
428+
429+
Returns:
430+
后缀是否匹配。
431+
"""
432+
if self.suffix_image is None and other.suffix_image is None:
433+
return True
434+
if self.suffix_image is None or other.suffix_image is None:
435+
return False
436+
437+
def match(image, template):
438+
template = crop(template, (3, 3, template.shape[1] - 3, template.shape[0] - 3), copy=False)
439+
if image.shape[0] < template.shape[0] or image.shape[1] < template.shape[1]:
440+
return 0.0
441+
442+
res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)
443+
_, sim, _, _ = cv2.minMaxLoc(res)
444+
return sim
445+
446+
sim = max(
447+
match(self.suffix_image, other.suffix_image),
448+
match(other.suffix_image, self.suffix_image)
449+
)
450+
return sim >= similarity
451+
401452
def parse_time(self, string):
402453
"""解析时间字符串为 timedelta 对象。
403454

module/config/argument/args.json

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2291,7 +2291,7 @@
22912291
"event_20240521_cn"
22922292
],
22932293
"option_jp": [
2294-
"event_20240521_cn"
2294+
"event_20260625_cn"
22952295
],
22962296
"option_tw": [
22972297
"event_20260520_cn"
@@ -2800,7 +2800,7 @@
28002800
"event_20240521_cn"
28012801
],
28022802
"option_jp": [
2803-
"event_20240521_cn"
2803+
"event_20260625_cn"
28042804
],
28052805
"option_tw": [
28062806
"event_20260520_cn"
@@ -3738,7 +3738,7 @@
37383738
"event_20240521_cn"
37393739
],
37403740
"option_jp": [
3741-
"event_20240521_cn"
3741+
"event_20260625_cn"
37423742
],
37433743
"option_tw": [
37443744
"event_20260520_cn"
@@ -4179,7 +4179,7 @@
41794179
"event_20240521_cn"
41804180
],
41814181
"option_jp": [
4182-
"event_20240521_cn"
4182+
"event_20260625_cn"
41834183
],
41844184
"option_tw": [
41854185
"event_20260520_cn"
@@ -4620,7 +4620,7 @@
46204620
"event_20240521_cn"
46214621
],
46224622
"option_jp": [
4623-
"event_20240521_cn"
4623+
"event_20260625_cn"
46244624
],
46254625
"option_tw": [
46264626
"event_20260520_cn"
@@ -7163,7 +7163,7 @@
71637163
"event_20240521_cn"
71647164
],
71657165
"option_jp": [
7166-
"event_20240521_cn"
7166+
"event_20260625_cn"
71677167
],
71687168
"option_tw": [
71697169
"event_20260520_cn"
@@ -7621,7 +7621,7 @@
76217621
"event_20240521_cn"
76227622
],
76237623
"option_jp": [
7624-
"event_20240521_cn"
7624+
"event_20260625_cn"
76257625
],
76267626
"option_tw": [
76277627
"event_20260520_cn"
@@ -8079,7 +8079,7 @@
80798079
"event_20240521_cn"
80808080
],
80818081
"option_jp": [
8082-
"event_20240521_cn"
8082+
"event_20260625_cn"
80838083
],
80848084
"option_tw": [
80858085
"event_20260520_cn"
@@ -8537,7 +8537,7 @@
85378537
"event_20240521_cn"
85388538
],
85398539
"option_jp": [
8540-
"event_20240521_cn"
8540+
"event_20260625_cn"
85418541
],
85428542
"option_tw": [
85438543
"event_20260520_cn"
@@ -8985,7 +8985,7 @@
89858985
"event_20240521_cn"
89868986
],
89878987
"option_jp": [
8988-
"event_20240521_cn"
8988+
"event_20260625_cn"
89898989
],
89908990
"option_tw": [
89918991
"event_20260520_cn"

module/config/i18n/ja-JP.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1072,7 +1072,7 @@
10721072
"event_20260326_cn": "ワンダラー募集計画",
10731073
"event_20260417_cn": "バケーションレーン・きらめく砂浜",
10741074
"event_20260520_cn": "聖印前の同盟",
1075-
"event_20260625_cn": "美梦巡演:奇妙夜",
1075+
"event_20260625_cn": "幻夢のカヴァルカード",
10761076
"raid_20200624": "特別演習超空強襲波(復刻)",
10771077
"raid_20210708": "交錯する新たな波 (復刻)",
10781078
"raid_20220127": "秘密事件調査",

0 commit comments

Comments
 (0)