feat(task): 优化培育与日常流程,修复一些 bug

1. 优化工作页面重新分配后的结束动画检测逻辑
2. 修复无法处理培育中,目前已有两个饮料,一次获得两个饮料时,弹出的饮料溢出弹窗
3. 调整培育中无推荐行动时执行行动的优先级
4. 支持 PRO 培育的中断继续
5. 修复培育中外出事件时,若弹出 AP 溢出提示,会卡住的问题
6. 优化加载画面的检测
This commit is contained in:
XcantloadX 2025-02-27 11:39:19 +08:00
parent 62d5805b96
commit 145f97f148
11 changed files with 57 additions and 29 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 723 KiB

View File

@ -0,0 +1 @@
{"definitions":{"05890a1b-8764-4e9f-9d21-65d292c22e13":{"name":"InPurodyuusu.BoxGoalClearNext","displayName":"培育目标达成 NEXT 文字区域","type":"hint-box","annotationId":"05890a1b-8764-4e9f-9d21-65d292c22e13","useHintRect":false}},"annotations":[{"id":"05890a1b-8764-4e9f-9d21-65d292c22e13","type":"rect","data":{"x1":79,"y1":503,"x2":157,"y2":532}}]}

View File

Before

Width:  |  Height:  |  Size: 410 KiB

After

Width:  |  Height:  |  Size: 410 KiB

View File

@ -0,0 +1 @@
{"definitions":{"5ebcde3b-f0fd-4e5d-b3de-ada8f0b5e03b":{"name":"Common.ButtonSelect2","displayName":"選択する","type":"template","annotationId":"5ebcde3b-f0fd-4e5d-b3de-ada8f0b5e03b","useHintRect":false}},"annotations":[{"id":"5ebcde3b-f0fd-4e5d-b3de-ada8f0b5e03b","type":"rect","data":{"x1":393,"y1":1145,"x2":577,"y2":1176}}]}

View File

@ -9,6 +9,7 @@ from kotonebot import (
device,
contains,
image,
regex,
action,
sleep,
Interval,
@ -110,6 +111,7 @@ AcquisitionType = Literal[
"PItemClaim", # P物品领取
"PItemSelect", # P物品选择
"Clear", # 目标达成
"ClearNext", # 目标达成 NEXT
"NetworkError", # 网络中断弹窗
"SkipCommu", # 跳过交流
"Loading", # 加载画面
@ -143,6 +145,13 @@ def acquisitions() -> AcquisitionType | None:
logger.debug("PDrink max found")
device.screenshot()
if image.find(R.InPurodyuusu.TextPDrinkMax):
# 有对话框标题,但是没找到确认按钮
# 可能是需要勾选一个饮料
if not image.find(R.InPurodyuusu.ButtonLeave, colored=True):
logger.info("No leave button found, click checkbox")
device.click(image.expect(R.Common.CheckboxUnchecked, colored=True))
sleep(0.2)
device.screenshot()
if leave := image.find(R.InPurodyuusu.ButtonLeave, colored=True):
logger.info("Leave button found")
device.click(leave)
@ -171,16 +180,19 @@ def acquisitions() -> AcquisitionType | None:
return "PSkillCardEnhanceSelect"
# 目标达成
logger.debug("Check gloal clear...")
logger.debug("Check gloal clear (達成)...")
if image.find(R.InPurodyuusu.IconClearBlue):
logger.info("Clear found")
logger.debug("達成: clicked")
device.click_center()
sleep(5)
# TODO: 可能不存在 達成 NEXT
logger.debug("達成 NEXT: clicked") # TODO: 需要截图
logger.debug("Goal clear (達成): clicked")
device.click_center()
sleep(1)
return "Clear"
# 目标达成 NEXT
if ocr.find(regex('NEXT|next'), rect=R.InPurodyuusu.BoxGoalClearNext):
logger.info("Goal clear (達成) next found")
device.click_center()
sleep(1)
return "ClearNext"
# P物品领取
logger.debug("Check PItem claim...")
if image.find(R.InPurodyuusu.PItemIconColorful):

View File

@ -733,10 +733,10 @@ def week_normal(week_first: bool = False):
elif executed_action is None:
if outing_available():
enter_outing()
elif allowance_available():
enter_allowance()
elif study_available():
enter_study()
elif allowance_available():
enter_allowance()
elif is_rest_available():
rest()
else:
@ -856,7 +856,7 @@ ProduceStage = Literal[
]
@action('检测当前培育场景', dispatcher=True)
def detect_regular_produce_scene(ctx: DispatcherContext) -> ProduceStage:
def detect_produce_scene(ctx: DispatcherContext) -> ProduceStage:
"""
判断当前是培育的什么阶段并开始 Regular 培育
@ -896,7 +896,7 @@ def detect_regular_produce_scene(ctx: DispatcherContext) -> ProduceStage:
return 'unknown'
@action('开始 Regular 培育')
def hajime_regular_from_stage(stage: ProduceStage):
def hajime_regular_from_stage(stage: ProduceStage, type: Literal['regular', 'pro']):
"""
开始 Regular 培育
"""
@ -907,12 +907,15 @@ def hajime_regular_from_stage(stage: ProduceStage):
if not remaining_week:
raise UnrecoverableError("Failed to detect week.")
# 判断阶段
MID_WEEK = 6 if type == 'regular' else 7
FINAL_WEEK = 13 if type == 'regular' else 16
function = hajime_regular if type == 'regular' else hajime_pro
if texts.where(contains('中間')):
week = 6 - remaining_week[0]
hajime_regular(start_from=week)
week = MID_WEEK - remaining_week[0]
function(start_from=week)
elif texts.where(contains('最終')):
week = 13 - remaining_week[0]
hajime_regular(start_from=week)
week = FINAL_WEEK - remaining_week[0]
function(start_from=week)
else:
raise UnrecoverableError("Failed to detect produce stage.")
elif stage == 'exam-start':
@ -925,7 +928,7 @@ def hajime_regular_from_stage(stage: ProduceStage):
exam()
result = ocr.expect_wait(contains('中間|最終'))
if '中間' in result.text:
return hajime_regular_from_stage(detect_regular_produce_scene())
return hajime_regular_from_stage(detect_produce_scene(), type)
elif '最終' in result.text:
produce_end()
else:
@ -934,7 +937,7 @@ def hajime_regular_from_stage(stage: ProduceStage):
# TODO: 应该直接调用 week_final_exam 而不是再写一次
logger.info("Practice ongoing. Start practice.")
practice()
return hajime_regular_from_stage(detect_regular_produce_scene())
return hajime_regular_from_stage(detect_produce_scene(), type)
else:
raise UnrecoverableError(f'Cannot resume produce REGULAR from stage "{stage}".')
@ -943,7 +946,14 @@ def resume_regular_produce():
"""
继续 Regular 培育
"""
hajime_regular_from_stage(detect_regular_produce_scene())
hajime_regular_from_stage(detect_produce_scene(), 'regular')
@action('继续 PRO 培育')
def resume_pro_produce():
"""
继续 PRO 培育
"""
hajime_regular_from_stage(detect_produce_scene(), 'pro')
if __name__ == '__main__':
from logging import getLogger
@ -989,8 +999,8 @@ if __name__ == '__main__':
# hajime_pro(start_from=16)
# exam('mid')
stage = (detect_regular_produce_scene())
hajime_regular_from_stage(stage)
stage = (detect_produce_scene())
hajime_regular_from_stage(stage, 'pro')
# click_recommended_card(card_count=skill_card_count())
# exam('mid')

View File

@ -17,8 +17,8 @@ def loading() -> bool:
original_img = img.copy()
# 二值化图片
_, img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
# 裁剪上面 20%
img = img[:int(img.shape[0] * 0.2), :]
# 裁剪上面 35%
img = img[:int(img.shape[0] * 0.35), :]
# 判断图片中颜色数量是否 <= 2
# https://stackoverflow.com/questions/56606294/count-number-of-unique-colours-in-image
b,g,r = cv2.split(img)

View File

@ -196,7 +196,7 @@ def enter_outing():
elif acquisitions():
pass
# [screenshots\produce\outing_ap_confirm.png]
elif image.find(R.Common.ButtonSelect):
elif image.find(R.Common.ButtonSelect2):
logger.info("AP max out dialog found. Click to continue.")
device.click()
sleep(0.1)

View File

@ -54,13 +54,13 @@ def goto_home():
elif home := toolbar_home():
device.click(home)
logger.debug("Clicked toolbar home button.")
sleep(0.2)
sleep(1)
# 課題CLEAR [screenshots/go_home/quest_clear.png]
elif image.find(R.Common.ButtonIconClose):
device.click()
logger.debug("Clicked close button.")
sleep(0.2)
logger.debug(f"Trying to go home...")
it.wait()
@action('前往商店页面')

View File

@ -31,7 +31,7 @@ def assign(type: Literal['mini', 'online']) -> bool:
分配工作
前置条件分配工作页面 \n
结束状态工作开始动画
结束状态分配工作页面
:param type: 工作类型mini=ミニライブ online=ライブ配信
"""
@ -103,10 +103,13 @@ def assign(type: Literal['mini', 'online']) -> bool:
else:
logger.warning(f'{target_duration}時間 not found. Using default duration.')
sleep(0.5)
# 点击 决定する
device.click(confirm)
# 点击 開始する [screenshots/assignment/assign_mini_live3.png]
device.click(image.expect_wait(R.Common.ButtonStart, timeout=5))
while not at_assignment():
# 点击 决定する
if image.find(R.Common.ButtonConfirmNoIcon):
device.click()
elif image.find(R.Common.ButtonStart):
# 点击 開始する [screenshots/assignment/assign_mini_live3.png]
device.click()
return True
@action('获取剩余时间')

View File

@ -164,6 +164,7 @@ def do_produce(idol: PIdol, mode: Literal['regular', 'pro']) -> bool:
break
elif image.find(R.Produce.ButtonAutoSet):
device.click()
sleep(1)
elif image.find(R.Common.ButtonConfirm, colored=True):
device.click()
device.screenshot()