feat(task): 支持 master 培育

This commit is contained in:
XcantloadX 2025-04-22 14:05:21 +08:00
parent ed1d12267f
commit e0298c892b
7 changed files with 122 additions and 39 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 KiB

View File

@ -0,0 +1 @@
{"definitions":{"3c8b477a-8eda-407e-9e9f-7540c8808f89":{"name":"Produce.ResumeDialogMaster","displayName":"培育再开对话框 MASTER","type":"template","annotationId":"3c8b477a-8eda-407e-9e9f-7540c8808f89","useHintRect":false}},"annotations":[{"id":"3c8b477a-8eda-407e-9e9f-7540c8808f89","type":"rect","data":{"x1":406,"y1":510,"x2":492,"y2":533}}]}

Binary file not shown.

After

Width:  |  Height:  |  Size: 474 KiB

View File

@ -313,10 +313,10 @@ class RecommendCardDetectionMode(Enum):
class ProduceConfig(ConfigBaseModel):
enabled: bool = False
"""是否启用培育"""
mode: Literal['regular', 'pro'] = 'regular'
mode: Literal['regular', 'pro', 'master'] = 'regular'
"""
培育模式
进行一次 REGULAR 培育需要 ~30min进行一次 PRO 培育需要 ~1h
进行一次 REGULAR 培育需要 ~30min进行一次 PRO 培育需要 ~1h具体视设备性能而定
"""
produce_count: int = 1
"""培育的次数。"""

View File

@ -707,7 +707,7 @@ class KotoneBotUI:
)
with gr.Group(visible=self.current_config.options.produce.enabled) as produce_group:
produce_mode = gr.Dropdown(
choices=["regular", "pro"],
choices=["regular", "pro", "master"],
value=self.current_config.options.produce.mode,
label="培育模式",
info=ProduceConfig.model_fields['mode'].description

View File

@ -375,7 +375,8 @@ def produce_end():
device.click(image.expect_wait(R.InPurodyuusu.ButtonComplete))
wait(0.5, before='screenshot')
break
# 培育得硬币活动时,弹出的硬币获得对话框
# 1. P任务解锁提示
# 2. 培育得硬币活动时,弹出的硬币获得对话框
elif dialog.no():
pass
@ -625,6 +626,42 @@ def hajime_pro(week: int = -1, start_from: int = 1):
logger.info("Week %d started.", i + start_from)
w()
@action("执行 MASTER 培育")
def hajime_master(week: int = -1, start_from: int = 1):
"""
MASTER 模式
:param week: 第几周从1开始-1表示全部
:param start_from: 从第几周开始从1开始
"""
weeks = [
lambda: week_normal(True), # 1
week_normal, # 2
week_normal, # 3
week_normal, # 4
week_normal, # 5
week_normal, # 6
week_final_lesson, # 7
week_mid_exam, # 8
week_normal, # 9
week_normal, # 10
week_normal, # 11
week_normal, # 12
week_normal, # 13
week_normal, # 14
week_normal, # 15
week_normal, # 16
week_final_lesson, # 17
week_final_exam, # 18
]
if week != -1:
logger.info("Week %d started.", week)
weeks[week - 1]()
else:
for i, w in enumerate(weeks[start_from-1:]):
logger.info("Week %d started.", i + start_from)
w()
@action('是否在考试场景')
def is_exam_scene():
"""是否在考试场景"""
@ -675,7 +712,7 @@ def detect_produce_scene(ctx: DispatcherContext) -> ProduceStage:
return 'unknown'
@action('开始 Hajime 培育')
def hajime_from_stage(stage: ProduceStage, type: Literal['regular', 'pro'], week: int):
def hajime_from_stage(stage: ProduceStage, type: Literal['regular', 'pro', 'master'], week: int):
"""
开始 Regular 培育
"""
@ -684,11 +721,23 @@ def hajime_from_stage(stage: ProduceStage, type: Literal['regular', 'pro'], week
# 提取周数
remaining_week = texts.squash().replace('ó', '6').numbers()
if not remaining_week:
raise UnrecoverableError("Failed to detect week.")
raise UnrecoverableError("Failed to detect week. text=" + repr(texts.squash()))
# 判断阶段
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
match type:
case 'regular':
MID_WEEK = 6
FINAL_WEEK = 13
function = hajime_regular
case 'pro':
MID_WEEK = 7
FINAL_WEEK = 16
function = hajime_pro
case 'master':
MID_WEEK = 8
FINAL_WEEK = 18
function = hajime_master
case _:
assert_never(type)
if texts.where(contains('中間')):
week = MID_WEEK - remaining_week[0]
function(start_from=week)
@ -700,27 +749,37 @@ def hajime_from_stage(stage: ProduceStage, type: Literal['regular', 'pro'], week
elif stage == 'exam-ongoing':
# TODO: 应该直接调用 week_final_exam 而不是再写一次
logger.info("Exam ongoing. Start exam.")
if type == 'regular':
if week > 6: # 第六周为期中考试
exam('final')
return produce_end()
else:
exam('mid')
return hajime_from_stage(detect_produce_scene(), type, week)
elif type == 'pro':
if week > 7:
exam('final')
return produce_end()
else:
exam('mid')
return hajime_from_stage(detect_produce_scene(), type, week)
match type:
case 'regular':
if week > 6: # 第六周为期中考试
exam('final')
return produce_end()
else:
exam('mid')
return hajime_from_stage(detect_produce_scene(), type, week)
case 'pro':
if week > 7:
exam('final')
return produce_end()
else:
exam('mid')
return hajime_from_stage(detect_produce_scene(), type, week)
case 'master':
if week > 8:
exam('final')
return produce_end()
else:
exam('mid')
return hajime_from_stage(detect_produce_scene(), type, week)
case _:
assert_never(type)
elif stage == 'practice-ongoing':
# TODO: 应该直接调用 week_final_exam 而不是再写一次
logger.info("Practice ongoing. Start practice.")
practice()
return hajime_from_stage(detect_produce_scene(), type, week)
else:
raise UnrecoverableError(f'Cannot resume produce REGULAR from stage "{stage}".')
raise UnrecoverableError(f'Cannot resume produce from stage "{stage}".')
@action('继续 Regular 培育')
def resume_regular_produce(week: int):
@ -740,6 +799,15 @@ def resume_pro_produce(week: int):
"""
hajime_from_stage(detect_produce_scene(), 'pro', week)
@action('继续 MASTER 培育')
def resume_master_produce(week: int):
"""
继续 MASTER 培育
:param week: 当前周数
"""
hajime_from_stage(detect_produce_scene(), 'master', week)
if __name__ == '__main__':
from logging import getLogger

View File

@ -1,11 +1,11 @@
import logging
from itertools import cycle
from typing import Optional, Literal
from typing_extensions import assert_never
from kotonebot.backend.context.context import wait
from kotonebot.tasks.game_ui.scrollable import Scrollable
from kotonebot.ui import user
from kotonebot.util import Countdown, Interval
from kotonebot.backend.context.context import wait
from kotonebot.backend.dispatch import SimpleDispatcher
from .. import R
@ -13,7 +13,8 @@ from ..common import conf
from ..game_ui import dialog
from ..actions.scenes import at_home, goto_home
from ..game_ui.idols_overview import locate_idol
from ..produce.in_purodyuusu import hajime_pro, hajime_regular, resume_pro_produce, resume_regular_produce
from ..produce.in_purodyuusu import hajime_pro, hajime_regular, hajime_master, resume_pro_produce, resume_regular_produce, \
resume_master_produce
from kotonebot import device, image, ocr, task, action, sleep, contains
logger = logging.getLogger(__name__)
@ -135,13 +136,16 @@ def resume_produce():
mode_result = image.find_multi([
R.Produce.ResumeDialogRegular,
R.Produce.ResumeDialogPro,
R.Produce.ResumeDialogMaster
])
if not mode_result:
raise ValueError('Failed to detect produce mode.')
if mode_result.index == 0:
mode = 'regular'
else:
elif mode_result.index == 1:
mode = 'pro'
else:
mode = 'master'
logger.info(f'Produce mode: {mode}')
retry_count = 0
max_retries = 5
@ -167,16 +171,20 @@ def resume_produce():
# [kotonebot-resource/sprites/jp/produce/produce_resume.png]
logger.info('Click resume button.')
device.click(btn_resume)
# 继续流程
if mode == 'regular':
resume_regular_produce(current_week)
else:
resume_pro_produce(current_week)
match mode:
case 'regular':
resume_regular_produce(current_week)
case 'pro':
resume_pro_produce(current_week)
case 'master':
resume_master_produce(current_week)
case _:
assert_never(mode)
@action('执行培育', screenshot_mode='manual-inherit')
def do_produce(
idol_skin_id: str,
mode: Literal['regular', 'pro'],
mode: Literal['regular', 'pro', 'master'],
memory_set_index: Optional[int] = None
) -> bool:
"""
@ -200,7 +208,8 @@ def do_produce(
return True
# 0. 进入培育页面
mode_text = 'REGULAR' if mode == 'regular' else 'PRO'
mode_text = mode.upper()
logger.info(f'Enter produce page. Mode: {mode_text}')
result = (SimpleDispatcher('enter_produce')
.click(R.Produce.ButtonProduce)
.click(contains(mode_text))
@ -292,10 +301,15 @@ def do_produce(
device.click()
if image.find(R.Common.ButtonConfirmNoIcon):
device.click()
if mode == 'regular':
hajime_regular()
else:
hajime_pro()
match mode:
case 'regular':
hajime_regular()
case 'pro':
hajime_pro()
case 'master':
hajime_master()
case _:
assert_never(mode)
return True
@task('培育')