feat(task): 支持指定培育中行动优先级
This commit is contained in:
parent
d5c64dfdbc
commit
fc8c456902
|
@ -1 +1 @@
|
|||
{"definitions":{"be6836bd-ee42-432b-9166-469c74f32f0b":{"name":"InPurodyuusu.BoxWeeksUntilExam","displayName":"考试剩余周","type":"hint-box","annotationId":"be6836bd-ee42-432b-9166-469c74f32f0b","useHintRect":false,"description":"培育中左上角的下次考试剩余周数"}},"annotations":[{"id":"be6836bd-ee42-432b-9166-469c74f32f0b","type":"rect","data":{"x1":11,"y1":8,"x2":237,"y2":196}}]}
|
||||
{"definitions":{"be6836bd-ee42-432b-9166-469c74f32f0b":{"name":"InPurodyuusu.BoxWeeksUntilExam","displayName":"考试剩余周","type":"hint-box","annotationId":"be6836bd-ee42-432b-9166-469c74f32f0b","useHintRect":false,"description":"培育中左上角的下次考试剩余周数"},"d6b64759-26b7-45b1-bf8e-5c0d98611e0d":{"name":"InPurodyuusu.TextActionVocal","displayName":"Vo. レッスン","type":"template","annotationId":"d6b64759-26b7-45b1-bf8e-5c0d98611e0d","useHintRect":false,"description":"培育 行动页面 声乐课程按钮文字"},"303cccc1-c674-4d3a-8c89-19ea729fdbef":{"name":"InPurodyuusu.TextActionDance","displayName":"Da. レッスン","type":"template","annotationId":"303cccc1-c674-4d3a-8c89-19ea729fdbef","useHintRect":false,"description":"培育 行动页面 舞蹈课程按钮文字"},"cc8a495d-330d-447d-8a80-a8a6ecc409c5":{"name":"InPurodyuusu.TextActionVisual","displayName":"Vi. レッスン","type":"template","annotationId":"cc8a495d-330d-447d-8a80-a8a6ecc409c5","useHintRect":false,"description":"培育 行动页面 形象课程按钮文字"}},"annotations":[{"id":"be6836bd-ee42-432b-9166-469c74f32f0b","type":"rect","data":{"x1":11,"y1":8,"x2":237,"y2":196}},{"id":"d6b64759-26b7-45b1-bf8e-5c0d98611e0d","type":"rect","data":{"x1":116,"y1":1067,"x2":255,"y2":1097}},{"id":"303cccc1-c674-4d3a-8c89-19ea729fdbef","type":"rect","data":{"x1":288,"y1":1068,"x2":431,"y2":1096}},{"id":"cc8a495d-330d-447d-8a80-a8a6ecc409c5","type":"rect","data":{"x1":466,"y1":1067,"x2":606,"y2":1096}}]}
|
|
@ -1,7 +1,7 @@
|
|||
import math
|
||||
import time
|
||||
import logging
|
||||
from typing_extensions import deprecated
|
||||
from typing_extensions import deprecated, assert_never
|
||||
from itertools import cycle
|
||||
from typing import Generic, Iterable, Literal, NamedTuple, Callable, Generator, TypeVar, ParamSpec, cast
|
||||
|
||||
|
@ -12,7 +12,7 @@ from cv2.typing import MatLike
|
|||
|
||||
from .. import R
|
||||
from . import loading
|
||||
from ..common import conf
|
||||
from ..common import ProduceAction, conf
|
||||
from .scenes import at_home
|
||||
from .commu import handle_unread_commu
|
||||
from kotonebot.errors import UnrecoverableError
|
||||
|
@ -96,7 +96,7 @@ def handle_sp_lesson():
|
|||
return False
|
||||
|
||||
@action('执行推荐行动')
|
||||
def handle_recommended_action(final_week: bool = False) -> ActionType:
|
||||
def handle_recommended_action(final_week: bool = False) -> ProduceAction | None:
|
||||
"""
|
||||
在行动选择页面,执行推荐行动
|
||||
|
||||
|
@ -121,16 +121,20 @@ def handle_recommended_action(final_week: bool = False) -> ActionType:
|
|||
if result is None:
|
||||
logger.debug("No recommended lesson found")
|
||||
return None
|
||||
recommended = None
|
||||
if not final_week:
|
||||
if result.index == 0:
|
||||
lesson_text = regex("Da")
|
||||
recommended = ProduceAction.DANCE
|
||||
elif result.index == 1:
|
||||
lesson_text = regex("Vo|V0|VO")
|
||||
recommended = ProduceAction.VOCAL
|
||||
elif result.index == 2:
|
||||
lesson_text = regex("Vi|V1|VI")
|
||||
recommended = ProduceAction.VISUAL
|
||||
elif result.index == 3:
|
||||
rest()
|
||||
return 'rest'
|
||||
return ProduceAction.REST
|
||||
else:
|
||||
return None
|
||||
logger.info("Rec. lesson: %s", lesson_text)
|
||||
|
@ -138,19 +142,22 @@ def handle_recommended_action(final_week: bool = False) -> ActionType:
|
|||
logger.debug("Try clicking lesson...")
|
||||
lesson_ret = ocr.expect(lesson_text)
|
||||
device.double_click(lesson_ret.rect)
|
||||
return 'lesson'
|
||||
return recommended
|
||||
else:
|
||||
if result.index == 0:
|
||||
template = R.InPurodyuusu.ButtonFinalPracticeDance
|
||||
recommended = ProduceAction.DANCE
|
||||
elif result.index == 1:
|
||||
template = R.InPurodyuusu.ButtonFinalPracticeVocal
|
||||
recommended = ProduceAction.VOCAL
|
||||
elif result.index == 2:
|
||||
template = R.InPurodyuusu.ButtonFinalPracticeVisual
|
||||
recommended = ProduceAction.VISUAL
|
||||
else:
|
||||
return None
|
||||
logger.debug("Try clicking lesson...")
|
||||
device.double_click(image.expect_wait(template))
|
||||
return 'lesson'
|
||||
return recommended
|
||||
|
||||
class CardDetectResult(NamedTuple):
|
||||
type: int
|
||||
|
@ -769,43 +776,128 @@ def produce_end():
|
|||
sleep(1)
|
||||
logger.info("Produce completed.")
|
||||
|
||||
@action('执行行动')
|
||||
def handle_action(action: ProduceAction, final_week: bool = False) -> ProduceAction | None:
|
||||
"""
|
||||
执行行动
|
||||
|
||||
前置条件:位于行动选择页面\n
|
||||
结束状态:若返回 True,取决于执行的行动。若返回 False,则仍然位于行动选择页面。
|
||||
|
||||
:param action: 行动类型
|
||||
:param final_week: 是否为冲刺周
|
||||
:return: 执行的行动
|
||||
"""
|
||||
match action:
|
||||
case ProduceAction.RECOMMENDED:
|
||||
return handle_recommended_action(final_week)
|
||||
case ProduceAction.DANCE:
|
||||
# TODO: 这两个模板的名称要统一一下
|
||||
templ = R.InPurodyuusu.TextActionVisual if not final_week else R.InPurodyuusu.ButtonFinalPracticeVisual
|
||||
if button := image.find(templ):
|
||||
device.double_click(button)
|
||||
return ProduceAction.DANCE
|
||||
else:
|
||||
return None
|
||||
case ProduceAction.VOCAL:
|
||||
templ = R.InPurodyuusu.TextActionVocal if not final_week else R.InPurodyuusu.ButtonFinalPracticeVocal
|
||||
if button := image.find(templ):
|
||||
device.double_click(button)
|
||||
return ProduceAction.VOCAL
|
||||
else:
|
||||
return None
|
||||
case ProduceAction.VISUAL:
|
||||
templ = R.InPurodyuusu.TextActionDance if not final_week else R.InPurodyuusu.ButtonFinalPracticeDance
|
||||
if button := image.find(templ):
|
||||
device.double_click(button)
|
||||
return ProduceAction.VISUAL
|
||||
else:
|
||||
return None
|
||||
case ProduceAction.REST:
|
||||
if is_rest_available():
|
||||
rest()
|
||||
return ProduceAction.REST
|
||||
case ProduceAction.OUTING:
|
||||
if outing_available():
|
||||
enter_outing()
|
||||
return ProduceAction.OUTING
|
||||
case ProduceAction.STUDY:
|
||||
if study_available():
|
||||
enter_study()
|
||||
return ProduceAction.STUDY
|
||||
case ProduceAction.ALLOWANCE:
|
||||
if allowance_available():
|
||||
enter_allowance()
|
||||
return ProduceAction.ALLOWANCE
|
||||
case _:
|
||||
logger.warning("Unknown action: %s", action)
|
||||
return None
|
||||
|
||||
def week_normal(week_first: bool = False):
|
||||
until_action_scene(week_first)
|
||||
logger.info("Handling actions...")
|
||||
action: ProduceAction | None = None
|
||||
# SP 课程
|
||||
if (
|
||||
conf().produce.prefer_lesson_ap
|
||||
and handle_sp_lesson()
|
||||
):
|
||||
executed_action = 'lesson'
|
||||
action = ProduceAction.DANCE
|
||||
else:
|
||||
executed_action = handle_recommended_action()
|
||||
logger.info("Executed recommended action: %s", executed_action)
|
||||
# 推荐练习
|
||||
if executed_action == 'lesson':
|
||||
until_practice_scene()
|
||||
practice()
|
||||
# 推荐休息
|
||||
elif executed_action == 'rest':
|
||||
pass
|
||||
# 没有推荐行动
|
||||
elif executed_action is None:
|
||||
if outing_available():
|
||||
enter_outing()
|
||||
elif study_available():
|
||||
enter_study()
|
||||
elif allowance_available():
|
||||
enter_allowance()
|
||||
elif is_rest_available():
|
||||
rest()
|
||||
else:
|
||||
raise ValueError("No action available.")
|
||||
actions = conf().produce.actions_order
|
||||
for action in actions:
|
||||
logger.debug("Checking action: %s", action)
|
||||
if action := handle_action(action):
|
||||
logger.info("Action %s hit.", action)
|
||||
break
|
||||
match action:
|
||||
case (
|
||||
ProduceAction.REST |
|
||||
ProduceAction.OUTING | ProduceAction.STUDY | ProduceAction.ALLOWANCE
|
||||
):
|
||||
# 什么都不需要做
|
||||
pass
|
||||
case ProduceAction.DANCE | ProduceAction.VOCAL | ProduceAction.VISUAL:
|
||||
until_practice_scene()
|
||||
practice()
|
||||
case ProduceAction.RECOMMENDED:
|
||||
# RECOMMENDED 应当被 handle_recommended_action 转换为具体的行动
|
||||
raise ValueError("Recommended action should not be handled here.")
|
||||
case None:
|
||||
raise ValueError("Action is None.")
|
||||
case _:
|
||||
assert_never(action)
|
||||
until_action_scene()
|
||||
|
||||
def week_final_lesson():
|
||||
until_action_scene()
|
||||
if handle_recommended_action(final_week=True) != 'lesson':
|
||||
raise ValueError("Failed to enter recommended action on final week.")
|
||||
sleep(5)
|
||||
# if handle_recommended_action(final_week=True) != 'lesson':
|
||||
# raise ValueError("Failed to enter recommended action on final week.")
|
||||
# sleep(5)
|
||||
action: ProduceAction | None = None
|
||||
actions = conf().produce.actions_order
|
||||
for action in actions:
|
||||
logger.debug("Checking action: %s", action)
|
||||
if action := handle_action(action, True):
|
||||
logger.info("Action %s hit.", action)
|
||||
break
|
||||
match action:
|
||||
case (
|
||||
ProduceAction.REST |
|
||||
ProduceAction.OUTING | ProduceAction.STUDY | ProduceAction.ALLOWANCE
|
||||
):
|
||||
# 什么都不需要做
|
||||
pass
|
||||
case ProduceAction.DANCE | ProduceAction.VOCAL | ProduceAction.VISUAL:
|
||||
until_practice_scene()
|
||||
practice()
|
||||
case ProduceAction.RECOMMENDED:
|
||||
# RECOMMENDED 应当被 handle_recommended_action 转换为具体的行动
|
||||
raise ValueError("Recommended action should not be handled here.")
|
||||
case None:
|
||||
raise ValueError("Action is None.")
|
||||
case _:
|
||||
assert_never(action)
|
||||
until_practice_scene()
|
||||
practice()
|
||||
|
||||
|
@ -1050,6 +1142,7 @@ if __name__ == '__main__':
|
|||
|
||||
|
||||
# practice()
|
||||
week_mid_exam()
|
||||
# week_final_exam()
|
||||
# exam('final')
|
||||
# produce_end()
|
||||
|
@ -1058,7 +1151,7 @@ if __name__ == '__main__':
|
|||
# hajime_pro(start_from=16)
|
||||
# exam('mid')
|
||||
stage = (detect_produce_scene())
|
||||
hajime_regular_from_stage(stage, 'pro')
|
||||
hajime_regular_from_stage(stage, 'regular')
|
||||
|
||||
# click_recommended_card(card_count=skill_card_count())
|
||||
# exam('mid')
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from importlib import resources
|
||||
import os
|
||||
from typing import Literal, Dict
|
||||
from importlib import resources
|
||||
from typing import Literal, Dict, NamedTuple, Tuple, TypeVar, Generic
|
||||
from enum import IntEnum, Enum
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
@ -8,6 +8,11 @@ from pydantic import BaseModel, ConfigDict
|
|||
# TODO: from kotonebot import config (context) 会和 kotonebot.config 冲突
|
||||
from kotonebot.backend.context import config
|
||||
|
||||
T = TypeVar('T')
|
||||
class ConfigEnum(Enum):
|
||||
def display(self) -> str:
|
||||
return self.value[1]
|
||||
|
||||
class Priority(IntEnum):
|
||||
START_GAME = 1
|
||||
DEFAULT = 0
|
||||
|
@ -302,6 +307,33 @@ class ContestConfig(ConfigBaseModel):
|
|||
enabled: bool = False
|
||||
"""是否启用竞赛"""
|
||||
|
||||
class ProduceAction(Enum):
|
||||
RECOMMENDED = 'recommended'
|
||||
VISUAL = 'visual'
|
||||
VOCAL = 'vocal'
|
||||
DANCE = 'dance'
|
||||
# VISUAL_SP = 'visual_sp'
|
||||
# VOCAL_SP = 'vocal_sp'
|
||||
# DANCE_SP = 'dance_sp'
|
||||
OUTING = 'outing'
|
||||
STUDY = 'study'
|
||||
ALLOWANCE = 'allowance'
|
||||
REST = 'rest'
|
||||
|
||||
@property
|
||||
def display_name(self):
|
||||
MAP = {
|
||||
ProduceAction.RECOMMENDED: '推荐行动',
|
||||
ProduceAction.VISUAL: '形象课程',
|
||||
ProduceAction.VOCAL: '声乐课程',
|
||||
ProduceAction.DANCE: '舞蹈课程',
|
||||
ProduceAction.OUTING: '外出(おでかけ)',
|
||||
ProduceAction.STUDY: '自习(授業)',
|
||||
ProduceAction.ALLOWANCE: '活动支给(活動支給)',
|
||||
ProduceAction.REST: '休息',
|
||||
}
|
||||
return MAP[self]
|
||||
|
||||
class ProduceConfig(ConfigBaseModel):
|
||||
enabled: bool = False
|
||||
"""是否启用培育"""
|
||||
|
@ -340,6 +372,21 @@ class ProduceConfig(ConfigBaseModel):
|
|||
启用后,若出现 SP 课程,则会优先执行 SP 课程,而不是推荐课程。
|
||||
若出现多个 SP 课程,随机选择一个。
|
||||
"""
|
||||
actions_order: list[ProduceAction] = [
|
||||
ProduceAction.RECOMMENDED,
|
||||
ProduceAction.VISUAL,
|
||||
ProduceAction.VOCAL,
|
||||
ProduceAction.DANCE,
|
||||
ProduceAction.ALLOWANCE,
|
||||
ProduceAction.OUTING,
|
||||
ProduceAction.STUDY,
|
||||
ProduceAction.REST,
|
||||
]
|
||||
"""
|
||||
行动优先级
|
||||
|
||||
每一周的行动将会按这里设置的优先级执行。
|
||||
"""
|
||||
|
||||
class MissionRewardConfig(ConfigBaseModel):
|
||||
enabled: bool = False
|
||||
|
|
|
@ -16,7 +16,7 @@ from kotonebot.config.manager import load_config, save_config
|
|||
from kotonebot.tasks.common import (
|
||||
BaseConfig, APShopItems, PurchaseConfig, ActivityFundsConfig,
|
||||
PresentsConfig, AssignmentConfig, ContestConfig, ProduceConfig,
|
||||
MissionRewardConfig, PIdol, DailyMoneyShopItems
|
||||
MissionRewardConfig, PIdol, DailyMoneyShopItems, ProduceAction
|
||||
)
|
||||
from kotonebot.config.base_config import UserConfig, BackendConfig
|
||||
from kotonebot.backend.bot import KotoneBot
|
||||
|
@ -255,6 +255,7 @@ class KotoneBotUI:
|
|||
follow_producer: bool,
|
||||
self_study_lesson: Literal['dance', 'visual', 'vocal'],
|
||||
prefer_lesson_ap: bool,
|
||||
actions_order: List[str],
|
||||
mission_reward_enabled: bool,
|
||||
) -> str:
|
||||
ap_items_enum: List[Literal[0, 1, 2, 3]] = []
|
||||
|
@ -311,7 +312,8 @@ class KotoneBotUI:
|
|||
use_note_boost=use_note_boost,
|
||||
follow_producer=follow_producer,
|
||||
self_study_lesson=self_study_lesson,
|
||||
prefer_lesson_ap=prefer_lesson_ap
|
||||
prefer_lesson_ap=prefer_lesson_ap,
|
||||
actions_order=[ProduceAction(action) for action in actions_order]
|
||||
),
|
||||
mission_reward=MissionRewardConfig(
|
||||
enabled=mission_reward_enabled
|
||||
|
@ -585,6 +587,13 @@ class KotoneBotUI:
|
|||
value=self.current_config.options.produce.prefer_lesson_ap,
|
||||
info=ProduceConfig.model_fields['prefer_lesson_ap'].description
|
||||
)
|
||||
actions_order = gr.Dropdown(
|
||||
choices=[(action.display_name, action.value) for action in ProduceAction],
|
||||
value=[action.value for action in self.current_config.options.produce.actions_order],
|
||||
label="行动优先级",
|
||||
info="设置每周行动的优先级顺序",
|
||||
multiselect=True
|
||||
)
|
||||
|
||||
produce_enabled.change(
|
||||
fn=lambda x: gr.Group(visible=x),
|
||||
|
@ -597,7 +606,7 @@ class KotoneBotUI:
|
|||
inputs=[auto_set_memory],
|
||||
outputs=[memory_sets_group]
|
||||
)
|
||||
return produce_enabled, produce_mode, produce_count, produce_idols, memory_sets, auto_set_memory, auto_set_support, use_pt_boost, use_note_boost, follow_producer, self_study_lesson, prefer_lesson_ap
|
||||
return produce_enabled, produce_mode, produce_count, produce_idols, memory_sets, auto_set_memory, auto_set_support, use_pt_boost, use_note_boost, follow_producer, self_study_lesson, prefer_lesson_ap, actions_order
|
||||
|
||||
def _create_settings_tab(self) -> None:
|
||||
with gr.Tab("设置"):
|
||||
|
|
Loading…
Reference in New Issue