feat(task): 培育中支持处理咨询
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
Before Width: | Height: | Size: 953 KiB After Width: | Height: | Size: 953 KiB |
|
@ -0,0 +1 @@
|
|||
{"definitions":{"8ded6c98-85ea-4858-a66d-4fc8caecb7c5":{"name":"InPurodyuusu.ButtonIconOuting","displayName":"行动按钮图标 外出(おでかけ)","type":"template","annotationId":"8ded6c98-85ea-4858-a66d-4fc8caecb7c5","useHintRect":false},"d83f338d-dde3-494b-9bea-cae511e3517c":{"name":"InPurodyuusu.ButtonIconConsult","displayName":"行动按钮图标 咨询(相談)","type":"template","annotationId":"d83f338d-dde3-494b-9bea-cae511e3517c","useHintRect":false}},"annotations":[{"id":"8ded6c98-85ea-4858-a66d-4fc8caecb7c5","type":"rect","data":{"x1":233,"y1":962,"x2":316,"y2":1037}},{"id":"d83f338d-dde3-494b-9bea-cae511e3517c","type":"rect","data":{"x1":405,"y1":963,"x2":488,"y2":1043}}]}
|
|
@ -1 +0,0 @@
|
|||
{"definitions":{"8ded6c98-85ea-4858-a66d-4fc8caecb7c5":{"name":"InPurodyuusu.ButtonIconOuting","displayName":"行动按钮图标 外出(おでかけ)","type":"template","annotationId":"8ded6c98-85ea-4858-a66d-4fc8caecb7c5","useHintRect":false}},"annotations":[{"id":"8ded6c98-85ea-4858-a66d-4fc8caecb7c5","type":"rect","data":{"x1":233,"y1":962,"x2":316,"y2":1037}}]}
|
After Width: | Height: | Size: 601 KiB |
|
@ -0,0 +1 @@
|
|||
{"definitions":{"23d88465-65d9-4718-8725-8dbf0a98a5a4":{"name":"InPurodyuusu.IconTitleConsult","displayName":"「相談」页面左上角图标","type":"template","annotationId":"23d88465-65d9-4718-8725-8dbf0a98a5a4","useHintRect":false},"1fb9bd7a-52f6-43c6-9b13-a05b66ecae42":{"name":"InPurodyuusu.PointConsultFirstItem","displayName":"「相談」中第一个物品位置","type":"hint-point","annotationId":"1fb9bd7a-52f6-43c6-9b13-a05b66ecae42","useHintRect":false},"9fd0753f-c607-4d49-82d1-40bda27e014f":{"name":"InPurodyuusu.ButtonEndConsult","displayName":"相談 结束按钮","type":"template","annotationId":"9fd0753f-c607-4d49-82d1-40bda27e014f","useHintRect":false}},"annotations":[{"id":"23d88465-65d9-4718-8725-8dbf0a98a5a4","type":"rect","data":{"x1":74,"y1":79,"x2":150,"y2":145}},{"id":"1fb9bd7a-52f6-43c6-9b13-a05b66ecae42","type":"point","data":{"x":123,"y":550}},{"id":"9fd0753f-c607-4d49-82d1-40bda27e014f","type":"rect","data":{"x1":587,"y1":1062,"x2":703,"y2":1109}}]}
|
After Width: | Height: | Size: 622 KiB |
|
@ -0,0 +1 @@
|
|||
{"definitions":{"4096cffa-a889-4622-852e-760fc7022d93":{"name":"InPurodyuusu.ButtonIconExchange","displayName":"交换按钮的图标","type":"template","annotationId":"4096cffa-a889-4622-852e-760fc7022d93","useHintRect":false}},"annotations":[{"id":"4096cffa-a889-4622-852e-760fc7022d93","type":"rect","data":{"x1":258,"y1":1066,"x2":303,"y2":1108}}]}
|
After Width: | Height: | Size: 314 KiB |
|
@ -0,0 +1 @@
|
|||
{"definitions":{"25f00ee3-8dfe-42d1-a67e-191fa5c3df4b":{"name":"InPurodyuusu.TextExchangeConfirm","displayName":"交換確認","type":"template","annotationId":"25f00ee3-8dfe-42d1-a67e-191fa5c3df4b","useHintRect":false,"description":"咨询中,购买确认对话框的标题"}},"annotations":[{"id":"25f00ee3-8dfe-42d1-a67e-191fa5c3df4b","type":"rect","data":{"x1":53,"y1":612,"x2":191,"y2":652}}]}
|
After Width: | Height: | Size: 1.1 MiB |
|
@ -283,6 +283,7 @@ class ProduceAction(Enum):
|
|||
STUDY = 'study'
|
||||
ALLOWANCE = 'allowance'
|
||||
REST = 'rest'
|
||||
CONSULT = 'consult'
|
||||
|
||||
@property
|
||||
def display_name(self):
|
||||
|
@ -295,6 +296,7 @@ class ProduceAction(Enum):
|
|||
ProduceAction.STUDY: '文化课(授業)',
|
||||
ProduceAction.ALLOWANCE: '活动支给(活動支給)',
|
||||
ProduceAction.REST: '休息',
|
||||
ProduceAction.CONSULT: '咨询(相談)',
|
||||
}
|
||||
return MAP[self]
|
||||
|
||||
|
@ -323,7 +325,6 @@ class ProduceConfig(ConfigBaseModel):
|
|||
idols: list[str] = []
|
||||
"""
|
||||
要培育偶像的 IdolCardSkin.id。将会按顺序循环选择培育。
|
||||
若未选择任何偶像,则使用游戏默认选择的偶像(为上次培育偶像)。
|
||||
"""
|
||||
memory_sets: list[int] = []
|
||||
"""要使用的回忆编成编号,从 1 开始。将会按顺序循环选择使用。"""
|
||||
|
|
|
@ -18,7 +18,8 @@ from ..produce.non_lesson_actions import (
|
|||
enter_allowance, allowance_available,
|
||||
study_available, enter_study,
|
||||
is_rest_available, rest,
|
||||
outing_available, enter_outing
|
||||
outing_available, enter_outing,
|
||||
consult_available, enter_consult
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -67,6 +68,7 @@ def handle_recommended_action(final_week: bool = False) -> ProduceAction | None:
|
|||
R.InPurodyuusu.TextSenseiTipVocal,
|
||||
R.InPurodyuusu.TextSenseiTipVisual,
|
||||
R.InPurodyuusu.TextSenseiTipRest,
|
||||
R.InPurodyuusu.TextSenseiTipConsult,
|
||||
]):
|
||||
break
|
||||
it.wait()
|
||||
|
@ -94,6 +96,9 @@ def handle_recommended_action(final_week: bool = False) -> ProduceAction | None:
|
|||
elif result.index == 3:
|
||||
rest()
|
||||
return ProduceAction.REST
|
||||
elif result.index == 4:
|
||||
enter_consult()
|
||||
return ProduceAction.CONSULT
|
||||
else:
|
||||
return None
|
||||
# 点击课程
|
||||
|
@ -469,6 +474,10 @@ def handle_action(action: ProduceAction, final_week: bool = False) -> ProduceAct
|
|||
if allowance_available():
|
||||
enter_allowance()
|
||||
return ProduceAction.ALLOWANCE
|
||||
case ProduceAction.CONSULT:
|
||||
if consult_available():
|
||||
enter_consult()
|
||||
return ProduceAction.CONSULT
|
||||
case _:
|
||||
logger.warning("Unknown action: %s", action)
|
||||
return None
|
||||
|
@ -493,7 +502,7 @@ def week_normal(week_first: bool = False):
|
|||
match action:
|
||||
case (
|
||||
ProduceAction.REST |
|
||||
ProduceAction.OUTING | ProduceAction.STUDY | ProduceAction.ALLOWANCE
|
||||
ProduceAction.OUTING | ProduceAction.STUDY | ProduceAction.ALLOWANCE | ProduceAction.CONSULT
|
||||
):
|
||||
# 什么都不需要做
|
||||
pass
|
||||
|
@ -521,7 +530,8 @@ def week_final_lesson():
|
|||
match action:
|
||||
case (
|
||||
ProduceAction.REST |
|
||||
ProduceAction.OUTING | ProduceAction.STUDY | ProduceAction.ALLOWANCE
|
||||
ProduceAction.OUTING | ProduceAction.STUDY | ProduceAction.ALLOWANCE |
|
||||
ProduceAction.CONSULT
|
||||
):
|
||||
# 什么都不需要做
|
||||
pass
|
||||
|
@ -828,44 +838,4 @@ if __name__ == '__main__':
|
|||
init_context(config_type=BaseConfig)
|
||||
manual_context().begin()
|
||||
debug.auto_save_to_folder = 'dumps'
|
||||
debug.enabled = True
|
||||
|
||||
# hajime_regular(start_from=1)
|
||||
|
||||
# pf = Profiler('profiler')
|
||||
# pf.begin()
|
||||
# # do_produce(conf().produce.idols[0], 'pro')
|
||||
# practice()
|
||||
# hajime_pro(start_from=16)
|
||||
# pf.end()
|
||||
# pf.snakeviz()
|
||||
|
||||
|
||||
# while True:
|
||||
# cards = obtain_cards()
|
||||
# print(cards)
|
||||
# sleep(1)
|
||||
|
||||
|
||||
# practice()
|
||||
# week_mid_exam()
|
||||
# week_final_exam()
|
||||
# exam('final')
|
||||
# produce_end()
|
||||
|
||||
|
||||
# hajime_pro(start_from=16)
|
||||
# exam('mid')
|
||||
stage = (detect_produce_scene())
|
||||
hajime_from_stage(stage, 'pro', 0)
|
||||
|
||||
# click_recommended_card(card_count=skill_card_count())
|
||||
# exam('mid')
|
||||
|
||||
# hajime_regular(start_from=7)
|
||||
|
||||
# import cv2
|
||||
# while True:
|
||||
# img = device.screenshot()
|
||||
# cv2.imshow('123', img)
|
||||
# cv2.waitKey(1)
|
||||
debug.enabled = True
|
|
@ -5,12 +5,14 @@
|
|||
"""
|
||||
from logging import getLogger
|
||||
|
||||
from kotonebot.tasks.game_ui import dialog
|
||||
|
||||
|
||||
from .. import R
|
||||
from ..common import conf
|
||||
from ..produce.common import fast_acquisitions
|
||||
from ..game_ui.commu_event_buttons import CommuEventButtonUI
|
||||
from kotonebot.util import Interval
|
||||
from kotonebot.util import Countdown, Interval
|
||||
from kotonebot.errors import UnrecoverableError
|
||||
from kotonebot import device, image, action, sleep
|
||||
from kotonebot.backend.dispatch import SimpleDispatcher
|
||||
|
@ -32,6 +34,14 @@ def study_available():
|
|||
# [screenshots/produce/action_study1.png]
|
||||
return image.find(R.InPurodyuusu.ButtonIconStudy) is not None
|
||||
|
||||
@action('检测是否可以执行相談')
|
||||
def consult_available():
|
||||
"""
|
||||
判断是否可以执行相談。
|
||||
"""
|
||||
return image.find(R.InPurodyuusu.ButtonIconConsult) is not None
|
||||
|
||||
# TODO: 把进入授業的逻辑和执行授業的逻辑分离
|
||||
@action('执行授業')
|
||||
def enter_study():
|
||||
"""
|
||||
|
@ -131,6 +141,87 @@ def enter_allowance():
|
|||
it.wait()
|
||||
logger.info("活動支給 completed.")
|
||||
|
||||
# TODO: 将逻辑用循环改写
|
||||
@action('执行相談', screenshot_mode='manual-inherit')
|
||||
def enter_consult():
|
||||
"""
|
||||
执行相談。
|
||||
|
||||
前置条件:位于行动页面,且所有行动按钮清晰可见 \n
|
||||
结束状态:位于行动页面
|
||||
"""
|
||||
logger.info("Executing 相談.")
|
||||
logger.info("Double clicking on 相談.")
|
||||
device.screenshot()
|
||||
device.double_click(image.expect(R.InPurodyuusu.ButtonIconConsult), interval=1)
|
||||
|
||||
# 等待进入页面
|
||||
while not image.find(R.InPurodyuusu.IconTitleConsult):
|
||||
device.screenshot()
|
||||
logger.debug("Waiting for 相談 screen.")
|
||||
fast_acquisitions()
|
||||
# # 尝试固定购买第一个物品
|
||||
# device.click(R.InPurodyuusu.PointConsultFirstItem)
|
||||
# sleep(0.5)
|
||||
# device.click(image.expect(R.InPurodyuusu.ButtonIconExchange))
|
||||
# # 等待弹窗
|
||||
# timeout_cd = Countdown(sec=5).start()
|
||||
# while not timeout_cd.expired():
|
||||
# if dialog.yes():
|
||||
# break
|
||||
# # 结束
|
||||
# while not image.find(R.InPurodyuusu.ButtonEndConsult):
|
||||
# fast_acquisitions()
|
||||
# device.click(image.expect_wait(R.InPurodyuusu.ButtonEndConsult))
|
||||
# # 可能会弹出确认对话框
|
||||
# timeout_cd.reset().start()
|
||||
# while not timeout_cd.expired():
|
||||
# dialog.yes()
|
||||
device.click(R.InPurodyuusu.PointConsultFirstItem)
|
||||
sleep(0.3)
|
||||
it = Interval()
|
||||
wait_purchase_cd = Countdown(sec=5)
|
||||
exit_cd = Countdown(sec=5)
|
||||
purchase_clicked = False
|
||||
purchase_confirmed = False
|
||||
exit_clicked = False
|
||||
while True:
|
||||
device.screenshot()
|
||||
it.wait()
|
||||
if wait_purchase_cd.expired():
|
||||
# 等待购买确认对话框超时后直接认为购买完成
|
||||
purchase_confirmed = True
|
||||
|
||||
if dialog.yes():
|
||||
if purchase_clicked:
|
||||
purchase_confirmed = True
|
||||
continue
|
||||
elif purchase_confirmed:
|
||||
continue
|
||||
elif exit_clicked:
|
||||
break
|
||||
if image.find(R.InPurodyuusu.ButtonIconExchange, colored=True):
|
||||
device.click()
|
||||
purchase_clicked = True
|
||||
continue
|
||||
if purchase_confirmed and image.find(R.InPurodyuusu.ButtonEndConsult):
|
||||
device.click()
|
||||
exit_clicked = True
|
||||
exit_cd.start()
|
||||
continue
|
||||
|
||||
# 等待退出对话框超时,直接退出
|
||||
if exit_cd.expired():
|
||||
break
|
||||
|
||||
if not purchase_confirmed:
|
||||
device.click(R.InPurodyuusu.PointConsultFirstItem)
|
||||
# 处理不能购买的情况(超时)
|
||||
# TODO: 应当检测画面文字/图标而不是用超时
|
||||
wait_purchase_cd.start()
|
||||
|
||||
logger.info("相談 completed.")
|
||||
|
||||
@action('判断是否可以休息')
|
||||
def is_rest_available():
|
||||
"""
|
||||
|
@ -213,22 +304,4 @@ if __name__ == '__main__':
|
|||
from kotonebot.backend.context import manual_context, init_context
|
||||
init_context()
|
||||
manual_context().begin()
|
||||
# 获取三个选项的内容
|
||||
ui = CommuEventButtonUI()
|
||||
buttons = ui.all()
|
||||
if not buttons:
|
||||
raise UnrecoverableError("Failed to find any buttons.")
|
||||
# 选中 +30 的选项
|
||||
target_btn = next((btn for btn in buttons if btn.description == '+30'), None)
|
||||
if target_btn is None:
|
||||
logger.error("Failed to find +30 option. Pick the first button instead.")
|
||||
target_btn = buttons[0]
|
||||
# 固定点击 Vi. 选项
|
||||
logger.debug('Clicking "%s".', target_btn.description)
|
||||
if target_btn.selected:
|
||||
device.click(target_btn)
|
||||
else:
|
||||
device.double_click(target_btn)
|
||||
while fast_acquisitions() is None:
|
||||
logger.info("Waiting for acquisitions finished.")
|
||||
logger.info("授業 completed.")
|
||||
enter_consult()
|
|
@ -217,6 +217,7 @@ class Countdown:
|
|||
|
||||
def reset(self):
|
||||
self.start_time = time.time()
|
||||
return self
|
||||
|
||||
class Stopwatch:
|
||||
def __init__(self):
|
||||
|
|