Skip to content

Commit cd98970

Browse files
authored
feat: 青春杯更新 (#191)
* fix: 修复了青春杯队名默认值设置错误的问题 * feat: 识别青春杯友情并且作为训练选择权重的一部分 * fix: 修复了选择胡萝卜队时数组越界的问题 * refactor: 重构青春杯对手选择的代码, 防止模拟器卡顿或者网络卡顿造成的选择对手失败 * fix: 修复了青春杯识别日期截图区域错误的问题 * fix: minor fix
1 parent 94175a0 commit cd98970

File tree

19 files changed

+957
-655
lines changed

19 files changed

+957
-655
lines changed

bot/base/localization.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ class LocalizationMap:
6464
localization_map = {attr: value for attr, value in vars(LocalizationMap).items()
6565
if not callable(value) and not attr.startswith('_')}
6666

67+
localization_map.update({
68+
'True': '是',
69+
'False': '否'
70+
})
71+
6772
def _localization_single(string):
6873
for name, value in localization_map.items():
6974
string = string.replace(name, value)

bot/base/log.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ def emit(self, record):
4040

4141
def get_task_log(self, task_id):
4242
with self.lock:
43-
logs = list(self.buffer[task_id])
43+
if task_id in self.buffer:
44+
logs = list(self.buffer[task_id])
45+
else:
46+
logs = []
4447
return logs
4548

4649
task_log_handler = TaskLogHandler()

module/umamusume/asset/ui.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
AOHARUHAI_RACE_INRACE = UI("AOHARUHAI_RACE_INRACE", [template.UI_AOHARUHAI_RACE_INRACE], [])
2727
AOHARUHAI_RACE_END = UI("AOHARUHAI_RACE_END", [template.UI_AOHARUHAI_RACE_END], [])
2828
AOHARUHAI_RACE_SCHEDULE = UI("AOHARUHAI_RACE_SCHEDULE", [template.UI_AOHARUHAI_RACE_SCHEDULE], [])
29+
AOHARUHAI_RACE_FINAL_START = UI("AOHARUHAI_RACE_FINAL_START", [template.UI_AOHARUHAI_RACE_FINAL_START], [])
30+
AOHARUHAI_RACE_SELECT_OPPONENT = UI("AOHARUHAI_RACE_SELECT_OPPONENT", [template.UI_AOHARUHAI_RACE_SELECT_OPPONENT], [])
2931

3032

3133
CULTIVATE_RACE_LIST = UI("CULTIVATE_RACE_LIST", [template.UI_CULTIVATE_RACE_LIST_1, template.UI_CULTIVATE_RACE_LIST_2], [])
@@ -73,7 +75,7 @@
7375
CULTIVATE_SUPPORT_CARD_SELECT, CULTIVATE_FOLLOW_SUPPORT_CARD_SELECT, CULTIVATE_FINAL_CHECK, INFO,
7476
CULTIVATE_MAIN_MENU, CULTIVATE_TRAINING_SELECT, CULTIVATE_EXTEND, CULTIVATE_CATCH_DOLL_GAME,
7577
CULTIVATE_CATCH_DOLL_GAME_RESULT, CULTIVATE_LEVEL_RESULT,
76-
AOHARUHAI_RACE, AOHARUHAI_RACE_INRACE, AOHARUHAI_RACE_END, AOHARUHAI_RACE_SCHEDULE,
78+
AOHARUHAI_RACE_FINAL_START, AOHARUHAI_RACE_SELECT_OPPONENT, AOHARUHAI_RACE, AOHARUHAI_RACE_INRACE, AOHARUHAI_RACE_END, AOHARUHAI_RACE_SCHEDULE,
7779
CULTIVATE_GOAL_RACE, CULTIVATE_RACE_LIST, CULTIVATE_RESULT, CULTIVATE_RESULT_1, CULTIVATE_RESULT_2,
7880
CULTIVATE_LEARN_SKILL, RECEIVE_CUP,
7981
CULTIVATE_URA_RACE_1, CULTIVATE_URA_RACE_2,CULTIVATE_URA_RACE_3,

module/umamusume/hook.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def after_hook(ctx: UmamusumeContext):
2424
if ctx.cultivate_detail and ctx.cultivate_detail.turn_info is not None:
2525
if ctx.cultivate_detail.turn_info.parse_train_info_finish and ctx.cultivate_detail.turn_info.parse_main_menu_finish:
2626
if not ctx.cultivate_detail.turn_info.turn_info_logged:
27-
ctx.cultivate_detail.turn_info.log_turn_info()
27+
ctx.cultivate_detail.turn_info.log_turn_info(ctx.task.detail.scenario)
2828
ctx.cultivate_detail.turn_info.turn_info_logged = True
2929
if ctx.cultivate_detail.turn_info.turn_operation is None:
3030
ctx.cultivate_detail.turn_info.turn_operation = get_operation(ctx)

module/umamusume/manifest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
CULTIVATE_EVENT_UMAMUSUME: script_cultivate_event,
2828
CULTIVATE_EVENT_SUPPORT_CARD: script_cultivate_event,
2929
CULTIVATE_EVENT_SCENARIO: script_cultivate_event,
30+
AOHARUHAI_RACE_FINAL_START: script_aoharuhai_race_final_start,
31+
AOHARUHAI_RACE_SELECT_OPPONENT: script_aoharuhai_race_select_oponent,
3032
AOHARUHAI_RACE: script_aoharuhai_race,
3133
AOHARUHAI_RACE_INRACE: script_aoharuhai_race_inrace,
3234
AOHARUHAI_RACE_END: script_aoharuhai_race_end,

module/umamusume/scenario/aoharuhai_scenario.py

Lines changed: 110 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def scenario_name(self) -> str:
2323
return "青春杯"
2424

2525
def get_date_img(self, img: any) -> any:
26-
return img[40:70, 160:280]
26+
return img[40:70, 160:370]
2727

2828
def get_turn_to_race_img(self, img) -> any:
2929
return img[70:120, 30:90]
@@ -98,18 +98,21 @@ def parse_training_result(self, img: any) -> list[int]:
9898
skill_point_incr = (0 if skill_point_incr_text == "" else int(skill_point_incr_text)) + (0 if skill_point_incr_extra_text == "" else int(skill_point_incr_extra_text))
9999

100100
return [speed_icr, stamina_incr, power_incr, will_incr, intelligence_incr, skill_point_incr]
101-
102-
def parse_training_support_cord(self, img: any) -> list[SupportCardInfo]:
103-
# TODO: 目前没有识别青春杯参数和青春杯友情条, 也没有将其作为训练权重的一部分,
104-
base_x = 590
101+
102+
def parse_training_support_card(self, img: any) -> list[SupportCardInfo]:
103+
base_x = 550
105104
base_y = 177
106105
inc = 115
107106
support_card_list_info_result: list[SupportCardInfo] = []
108107
for i in range(5):
109-
support_card_icon = img[base_y:base_y + inc, base_x: base_x + 105]
108+
support_card_icon = img[base_y:base_y + inc, base_x: base_x + 145]
109+
110+
# 有青春杯训练, 且青春杯友情未满
111+
can_incr_aoharu_train = detect_aoharu_train_arrow(support_card_icon) and aoharu_train_not_full(support_card_icon)
112+
110113
# 判断好感度
111114
support_card_icon = cv2.cvtColor(support_card_icon, cv2.COLOR_BGR2RGB)
112-
favor_process_check_list = [support_card_icon[106, 16], support_card_icon[106, 20]]
115+
favor_process_check_list = [support_card_icon[106, 56], support_card_icon[106, 60]]
113116
support_card_favor_process = SupportCardFavorLevel.SUPPORT_CARD_FAVOR_LEVEL_UNKNOWN
114117
for support_card_favor_process_pos in favor_process_check_list:
115118
if compare_color_equal(support_card_favor_process_pos, [255, 235, 120]):
@@ -124,13 +127,6 @@ def parse_training_support_cord(self, img: any) -> list[SupportCardInfo]:
124127
if support_card_favor_process != SupportCardFavorLevel.SUPPORT_CARD_FAVOR_LEVEL_UNKNOWN:
125128
break
126129

127-
# 判断是否有事件
128-
support_card_event_pos = support_card_icon[5, 83]
129-
support_card_event_available = False
130-
if (support_card_event_pos[0] >= 250
131-
and 55 <= support_card_event_pos[1] <= 90
132-
and 115 <= support_card_event_pos[2] <= 150):
133-
support_card_event_available = True
134130
# 判断支援卡类型
135131
support_card_type = SupportCardType.SUPPORT_CARD_TYPE_UNKNOWN
136132
support_card_icon = cv2.cvtColor(support_card_icon, cv2.COLOR_RGB2GRAY)
@@ -146,11 +142,107 @@ def parse_training_support_cord(self, img: any) -> list[SupportCardInfo]:
146142
support_card_type = SupportCardType.SUPPORT_CARD_TYPE_INTELLIGENCE
147143
elif image_match(support_card_icon, REF_SUPPORT_CARD_TYPE_FRIEND).find_match:
148144
support_card_type = SupportCardType.SUPPORT_CARD_TYPE_FRIEND
149-
if support_card_favor_process is not SupportCardFavorLevel.SUPPORT_CARD_FAVOR_LEVEL_UNKNOWN:
145+
if (can_incr_aoharu_train) or \
146+
(support_card_favor_process is not SupportCardFavorLevel.SUPPORT_CARD_FAVOR_LEVEL_UNKNOWN):
150147
info = SupportCardInfo(card_type=support_card_type,
151-
favor=support_card_favor_process,
152-
has_event=support_card_event_available)
148+
favor=support_card_favor_process,
149+
can_incr_aoharu_train=can_incr_aoharu_train)
153150
support_card_list_info_result.append(info)
154151
base_y += inc
155152

156-
return support_card_list_info_result
153+
return support_card_list_info_result
154+
155+
# 检测支援卡右上角是否有箭头图标, 同时排除感叹号防止false positive
156+
# 输入的图片必须是彩色的
157+
def detect_aoharu_train_arrow(support_card_icon):
158+
support_card_icon = cv2.cvtColor(support_card_icon, cv2.COLOR_BGR2RGB)
159+
# 定义右上角检测区域
160+
arrow_region_x_start = 110
161+
arrow_region_x_end = 145
162+
arrow_region_y_start = 0
163+
arrow_region_y_end = 40
164+
165+
arrow_region = support_card_icon[arrow_region_y_start:arrow_region_y_end,
166+
arrow_region_x_start:arrow_region_x_end]
167+
168+
# 定义箭头可能的颜色范围 (检查橙色)
169+
orange_lower = [240, 100, 50]
170+
orange_upper = [255, 180, 100]
171+
172+
# 定义红色像素范围(用于检测感叹号)
173+
red_lower = [180, 30,50]
174+
red_upper = [255, 100, 150]
175+
176+
# 计算橙色像素和红色像素的数量
177+
orange_pixels = 0
178+
red_pixels = 0
179+
total_pixels = arrow_region.shape[0] * arrow_region.shape[1]
180+
181+
for y in range(arrow_region.shape[0]):
182+
for x in range(arrow_region.shape[1]):
183+
pixel = arrow_region[y, x]
184+
185+
# 检测橙色像素
186+
if (orange_lower[0] <= pixel[0] <= orange_upper[0] and
187+
orange_lower[1] <= pixel[1] <= orange_upper[1] and
188+
orange_lower[2] <= pixel[2] <= orange_upper[2]):
189+
orange_pixels += 1
190+
# 检测红色像素
191+
elif (red_lower[0] <= pixel[0] <= red_upper[0] and
192+
red_lower[1] <= pixel[1] <= red_upper[1] and
193+
red_lower[2] <= pixel[2] <= red_upper[2]):
194+
red_pixels += 1
195+
196+
orange_ratio = orange_pixels / total_pixels if total_pixels > 0 else 0
197+
red_ratio = red_pixels / total_pixels if total_pixels > 0 else 0
198+
199+
has_arrow = False
200+
201+
# 首先排除感叹号:如果红色像素比例过高,判断为感叹号
202+
if red_ratio > 0.2:
203+
has_arrow = False
204+
# 如果橙色像素比例超过阈值
205+
elif (orange_ratio > 0.05):
206+
has_arrow = True
207+
208+
return has_arrow
209+
210+
211+
# 检测左下角青春杯训练值是否未满
212+
# 如果已满或者不存在UI(比如已经触发了魂爆, 则返回false)
213+
# 否则返回true
214+
def aoharu_train_not_full(support_card_icon) -> bool:
215+
support_card_icon = cv2.cvtColor(support_card_icon, cv2.COLOR_BGR2RGB)
216+
avatar_region_x_start = 5
217+
avatar_region_x_end = 45
218+
avatar_region_y_start = 70
219+
avatar_region_y_end = 110
220+
221+
avatar_region = support_card_icon[avatar_region_y_start:avatar_region_y_end,
222+
avatar_region_x_start:avatar_region_x_end]
223+
224+
total_pixels = avatar_region.shape[0] * avatar_region.shape[1]
225+
if total_pixels == 0:
226+
return False
227+
228+
# 检测灰色
229+
grey_lower = [100, 100, 100]
230+
grey_upper = [150, 150, 150]
231+
grey_pixels = 0
232+
233+
for y in range(avatar_region.shape[0]):
234+
for x in range(avatar_region.shape[1]):
235+
pixel = avatar_region[y, x]
236+
if (grey_lower[0] <= pixel[0] <= grey_upper[0] and
237+
grey_lower[1] <= pixel[1] <= grey_upper[1] and
238+
grey_lower[2] <= pixel[2] <= grey_upper[2]):
239+
grey_pixels += 1
240+
241+
grey_ratio = grey_pixels / total_pixels
242+
243+
if grey_ratio > 0.05:
244+
status = True
245+
else:
246+
status = False
247+
248+
return status

module/umamusume/scenario/base_scenario.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ def parse_training_result(self, img: any) -> list[int]:
3232
pass
3333

3434
@abstractmethod
35-
def parse_training_support_cord(self, img: any) -> list[SupportCardInfo]:
35+
def parse_training_support_card(self, img: any) -> list[SupportCardInfo]:
3636
"""从屏幕上获取每一张支援卡的信息"""
3737
pass

module/umamusume/scenario/ura_scenario.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def parse_training_result(self, img: any) -> list[int]:
6363

6464
return [speed_icr, stamina_incr, power_incr, will_incr, intelligence_incr, skill_point_incr]
6565

66-
def parse_training_support_cord(self, img: any) -> list[SupportCardInfo]:
66+
def parse_training_support_card(self, img: any) -> list[SupportCardInfo]:
6767
base_x = 590
6868
base_y = 190
6969
inc = 120

module/umamusume/script/cultivate_task/cultivate.py

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -234,17 +234,6 @@ def script_cultivate_event(ctx: UmamusumeContext):
234234
log.debug("未出现选项")
235235

236236
def script_aoharuhai_race(ctx: UmamusumeContext):
237-
def select_opponent (race_index: int):
238-
match race_index:
239-
case 1:
240-
ctx.ctrl.click(360, 290, "选择第一个对手")
241-
case 2:
242-
ctx.ctrl.click(360, 560, "选择第二个对手")
243-
case 3:
244-
ctx.ctrl.click(360, 830, "选择第三个对手")
245-
time.sleep(2)
246-
ctx.ctrl.click(360, 1080, "开始对战")
247-
248237
img = ctx.ctrl.get_screen(to_gray=True)
249238
if image_match(img, UI_AOHARUHAI_RACE_1).find_match:
250239
race_index = 0
@@ -260,22 +249,24 @@ def select_opponent (race_index: int):
260249
ctx.ctrl.click(360, 1180, "确认比赛结果")
261250
return
262251

252+
ctx.cultivate_detail.turn_info.aoharu_race_index = race_index
263253
ctx.ctrl.click(360, 1080, "开始青春杯对战")
264254

265-
if race_index == 4:
266-
while True:
267-
time.sleep(1)
268-
img = ctx.ctrl.get_screen(to_gray=True)
269-
if image_match(img, UI_AOHARUHAI_RACE_FINAL_START).find_match:
270-
break
271-
ctx.ctrl.click(360, 980, "确认决赛对手")
272-
else:
273-
while True:
274-
time.sleep(1)
275-
img = ctx.ctrl.get_screen(to_gray=True)
276-
if image_match(img, UI_AOHARUHAI_RACE_SELECT_OPPONENT).find_match:
277-
break
278-
select_opponent(ctx.task.detail.scenario_config.aoharu_config.get_opponent(race_index))
255+
def script_aoharuhai_race_final_start(ctx: UmamusumeContext):
256+
ctx.ctrl.click(360, 980, "确认决赛对手")
257+
258+
def script_aoharuhai_race_select_oponent(ctx: UmamusumeContext):
259+
def select_opponent (race_index: int):
260+
match race_index:
261+
case 1:
262+
ctx.ctrl.click(360, 290, "选择第一个对手")
263+
case 2:
264+
ctx.ctrl.click(360, 560, "选择第二个对手")
265+
case 3:
266+
ctx.ctrl.click(360, 830, "选择第三个对手")
267+
time.sleep(2)
268+
ctx.ctrl.click(360, 1080, "开始对战")
269+
select_opponent(ctx.task.detail.scenario_config.aoharu_config.get_opponent(ctx.cultivate_detail.turn_info.aoharu_race_index))
279270

280271
def script_aoharuhai_race_confirm(ctx: UmamusumeContext):
281272
ctx.ctrl.click(520, 920, "确认对战")

module/umamusume/script/cultivate_task/event/scenario_event.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ def aoharuhai_team_name_event(ctx: UmamusumeContext) -> int:
3737
match_result.matched_area[0][0]:match_result.matched_area[1][0]] = 0
3838
else:
3939
break
40+
41+
if ctx.task.detail.scenario_config.aoharu_config.aoharu_team_name_selection == 4:
42+
log.debug("使用选项<胡萝卜>队")
43+
return len(event_selector_list)
44+
4045
event_selector_list.sort(key=lambda x: x.center_point[1])
4146
for i in range(len(event_selector_list)):
4247
event = event_selector_list[i]

0 commit comments

Comments
 (0)