Skip to content

Commit 5ebc7ca

Browse files
committed
Merge tag 'v3.5.9'
2 parents f76283f + cf14ea6 commit 5ebc7ca

7 files changed

Lines changed: 190 additions & 107 deletions

File tree

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#
55
# pip-compile '.\requirements.in'
66
#
7-
ok-script==1.0.159
7+
ok-script==1.0.163
88
# via -r .\requirements.in
99
OpenCC==1.2.0
1010
onnxocr-ppocrv5==0.0.18

src/char/Phoebe.py

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ def __init__(self, *args, **kwargs):
2222
self.star_available = False
2323
self.char_zani = None
2424
self.attribute_mismatch = False
25+
self.first_rotation_done = False
2526
self.state = {
2627
"enter_status": 0,
2728
"starflash_combo": 0,
2829
"liberation": 0,
29-
"outro": 0
30+
"outro": 0,
31+
"priority_liberation_cast": 0
3032
}
3133

3234
def reset_state(self):
@@ -35,6 +37,7 @@ def reset_state(self):
3537
self.attribute = 0
3638
self.star_available = False
3739
self.char_zani = None
40+
self.first_rotation_done = False
3841

3942
def do_perform(self):
4043
self.last_outro_time = -1
@@ -44,7 +47,9 @@ def do_perform(self):
4447
if self.has_intro:
4548
self.continues_normal_attack(1.5)
4649
else:
47-
self.sleep(0.01)
50+
# 非变奏切人后会自然接一段普攻,先等它结算协奏,再抢大招。
51+
self.sleep(0.01)
52+
self._try_liberation_now()
4853

4954
if self.attribute == 1:
5055
self.click_echo(time_out=0)
@@ -73,18 +78,39 @@ def do_perform(self):
7378
self.click_liberation(send_click=True)
7479
):
7580
self.state["liberation"] += 1
81+
self.state["priority_liberation_cast"] = 1
7682
self.check_combat()
7783
if status_entered == State.SUCCESS or self.judge_forte() > 0:
7884
self.starflash_combo()
85+
# 第一次重击后检查大招(可插入),无论是否触发都继续打第二次重击
86+
self._try_liberation_now()
87+
if (self.attribute == 2 and
88+
self.state["starflash_combo"] < 2 and
89+
self.get_zani_state() != 1):
90+
self.logger.info('phoebe: try second starflash_combo')
91+
self.starflash_combo()
92+
self._try_liberation_now()
7993
if self.resonance_available():
8094
if self.attribute == 2:
81-
self.click_resonance_once()
95+
# 第一轮未完成时跳过共鸣点击,避免触发非蓝色重击打断普攻填协奏
96+
if not self.confession_ready() and self.first_rotation_done:
97+
self.click_resonance_once()
8298
else:
8399
self.click_resonance()
100+
self._ensure_first_rotation_con()
84101
return self.switch_next_char()
85102
self.continues_normal_attack(0.1)
103+
self._ensure_first_rotation_con()
86104
self.switch_next_char()
87105

106+
def _ensure_first_rotation_con(self):
107+
"""第一轮切人前确保协奏打满,之后不再限制。"""
108+
if not self.first_rotation_done:
109+
self.first_rotation_done = True
110+
if not self.is_con_full():
111+
self.logger.info('phoebe: first rotation, wait for full con')
112+
self.continues_normal_attack(5.0, until_con_full=True)
113+
88114
def zani_linkage(self):
89115
self.logger.debug('zani linkage')
90116
result = self.get_zani_state()
@@ -94,7 +120,9 @@ def zani_linkage(self):
94120
if result == 0 or self.char_zani.liberation_time_left() > 3:
95121
self.continues_normal_attack(1, interval=0.15)
96122
else:
97-
self.click_resonance(send_click=False)
123+
# 首轮跳过 E;之后 confession 已经就绪时也不要短按 E,避免打断协奏轴。
124+
if self.first_rotation_done and not self.confession_ready():
125+
self.click_resonance(send_click=False)
98126
return True
99127
if result == 1:
100128
self.cast_remaining_skills()
@@ -298,11 +326,41 @@ def absolution_or_confession(self):
298326
return State.SUCCESS
299327
return State.UNAVAILABLE
300328

329+
def _try_liberation_now(self):
330+
"""每个主要动作前检查大招,可用就立即释放,返回True表示已释放"""
331+
if (self.star_available and not self.flying()
332+
and self.liberation_available()):
333+
if self.click_liberation(send_click=True):
334+
self.state["liberation"] += 1
335+
self.state["priority_liberation_cast"] = 1
336+
self.check_combat()
337+
return True
338+
return False
339+
340+
def _try_cast_liberation_before_switch(self):
341+
# 有大放大:flying / zani_linkage / 共鸣等提前切人的分支可能带着可用大招切走,
342+
# 在此切人前补放一次。加保护:仅辅助型、不在空中、每轮不重复放
343+
if self.attribute != 2:
344+
return False
345+
if self.state.get("priority_liberation_cast"):
346+
return False
347+
if not self.star_available or self.flying():
348+
return False
349+
if not self.liberation_available():
350+
return False
351+
self.logger.info('cast available liberation before switch')
352+
if self.click_liberation(send_click=True):
353+
self.state["priority_liberation_cast"] = 1
354+
self.state["liberation"] += 1
355+
self.check_combat()
356+
return True
357+
return False
358+
301359
def switch_next_char(self, *args, **kwargs):
302-
if self.is_con_full():
303-
if self.attribute == 2:
304-
self.click_echo()
305-
self.state["outro"] += 1
360+
self._try_cast_liberation_before_switch()
361+
if self.attribute == 2 and self.is_con_full():
362+
self.click_echo()
363+
self.state["outro"] += 1
306364
return super().switch_next_char(*args, **kwargs)
307365

308366
def get_switch_priority(self, current_char=None, has_intro=False, target_low_con=False):
@@ -413,7 +471,8 @@ def reset_action(self):
413471
"enter_status": 0,
414472
"starflash_combo": 0,
415473
"liberation": 0,
416-
"outro": 0
474+
"outro": 0,
475+
"priority_liberation_cast": 0
417476
}
418477

419478
def is_forte_full(self):

src/char/Zani.py

Lines changed: 94 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,12 @@ def reset_state(self):
4444
def do_perform(self):
4545
if self.blazes_threshold == -1:
4646
self.decide_teammate()
47-
if self.has_intro:
48-
self.logger.info('has intro')
49-
self.continues_normal_attack(1.3)
50-
else:
51-
self.sleep(0.01)
47+
48+
# 等待落地/入场动画,click=False 避免打出无意义普攻
5249
self.wait_down()
5350
self.check_liber()
51+
52+
# 大招状态(branch 0):保留基线 nightfall/liber2 逻辑
5453
if self.in_liberation:
5554
self.logger.info('in liberation')
5655
self.state = 1
@@ -59,86 +58,93 @@ def do_perform(self):
5958
else:
6059
self.nightfall_combo()
6160
return self.switch_next_char()
62-
else:
63-
self.state = 0
64-
self.f_break()
6561

66-
if self.echo_available():
67-
self.click_echo(time_out=0)
62+
self.state = 0
63+
self.f_break()
64+
self.crisis_time = -1 # 清除上轮可能遗留的过期计时
65+
66+
# 读屏:焰光、forte 状态、E 图标亮度、大招可用性(缓存一次,全程使用)
67+
self.update_blazes()
68+
forte_full = self.is_e_forte_full()
69+
e_available = self.current_resonance() > 0.05
70+
liber_avail = self.liberation_available()
71+
if self.has_intro and self.blazes >= 1 and not liber_avail:
72+
self.sleep(0.2, check_combat=False)
73+
liber_avail = self.liberation_available()
74+
e_available = self.current_resonance() > 0.05
75+
predicted = float(self.blazes) + 0.1
76+
77+
self.logger.info(
78+
f'Zani entry: blazes={self.blazes} threshold={self.blazes_threshold} '
79+
f'forte_full={forte_full} e_avail={e_available} predicted={predicted:.2f} '
80+
f'liber_avail={liber_avail} has_intro={self.has_intro}'
81+
)
82+
83+
# 场景3:焰光拉满(1.0)且大招可用 → 直接开大
84+
# 0.96 can appear for both full and not-full bars; only 1.00 is safe for direct liberation.
85+
if self.blazes >= 1 and liber_avail:
86+
self.logger.info('scene3: blazes full, liberation available, direct liberation')
87+
if not self._try_liberation():
88+
self.sleep(0.1)
89+
self._try_liberation()
90+
return self.switch_next_char()
6891

69-
cast_liberation = False
70-
if self.crisis_time > 0:
71-
if self.time_elapsed_accounting_for_freeze(self.crisis_time, intro_motion_freeze=True) < 2.45:
72-
self.wait_crisis_protocol_end()
73-
if self.crisis_time_left() > - 1 and self.liberation_available() and self.is_prepared():
74-
cast_liberation = True
75-
else:
76-
self.continues_normal_attack(0.25)
77-
self.sleep(0.25)
78-
self.crisis_time = - 1
79-
if not cast_liberation:
80-
self.chair_time = -1
81-
if (not self.has_intro and
82-
not self.is_first_engage() and
83-
self.time_elapsed_accounting_for_freeze(self.last_liber2, intro_motion_freeze=True) >= 2.6
84-
):
85-
if self.time_elapsed_accounting_for_freeze(self.attack_breakthrough_time, intro_motion_freeze=True) < 4:
86-
self.continues_right_click(0.05)
87-
self.dodge_time = time.time()
88-
else:
89-
self.sleep(0.25)
90-
self.continues_normal_attack(0.25)
91-
self.chair_time = time.time()
92-
self.last_liber2 = -1
93-
self.attack_breakthrough_time = -1
94-
breakthrough_result = self.basic_attack_breakthrough_combo()
95-
if self.is_prepared():
96-
self.logger.info('is ready')
97-
if not self.has_cd('liberation'):
98-
self.logger.info('liberation no cd')
99-
result = 0
100-
if breakthrough_result == State.DONE:
101-
result = self.wait_forte_full(2.2, check_forte=True)
102-
if result == State.DONE:
103-
self.continues_right_click(0.05)
104-
self.dodge_time = time.time()
105-
if breakthrough_result == State.INTERRUPTED or result == State.INTERRUPTED:
106-
self.wait_until(lambda: not self.flying(), time_out=0.6)
107-
self.crisis_response_protocol_combo()
108-
else:
109-
self.logger.info('liberation has cd')
110-
if self.is_e_forte_full():
111-
self.crisis_response_protocol_combo()
92+
# scene4: enhanced E ready; cast once, then liberate if needed.
93+
if forte_full:
94+
self.logger.info(f'scene4: enhanced E ready, predicted={predicted:.2f}')
95+
should_liberate = predicted >= self.blazes_threshold
96+
success = self.crisis_response_protocol_combo()
97+
if success and should_liberate and self.liberation_available():
98+
self.logger.info('scene4: enhanced E -> liberate')
99+
self._try_liberation(wait_crisis=True)
100+
else:
101+
self.logger.info('scene4: enhanced E -> switch')
102+
return self.switch_next_char()
103+
104+
# 场景1:普通E 可用,焰光未满 → 完全沿用基线 crisis_response_protocol_combo
105+
# 内部已包含:E → 持续普攻/蓄力攒 forte → 强化E命中,不再需要自定义 E+一次普攻的写死序列
106+
if e_available:
107+
self.logger.info(f'scene1: normal E available, predicted={predicted:.2f}')
108+
success = self.crisis_response_protocol_combo()
109+
# combo 结束后即时检查一次大招。
110+
if success and self.blazes >= self.blazes_threshold:
112111
if self.liberation_available():
113-
cast_liberation = True
114-
if self.blazes != 1:
115-
self.wait_crisis_protocol_end()
116-
self.crisis_time = - 1
117-
else:
118-
return self.switch_next_char()
112+
self.logger.info('scene1: liberate after enhanced E')
113+
self._try_liberation(wait_crisis=True)
114+
return self.switch_next_char()
119115

120-
self.logger.info(f'cast_liberation {cast_liberation}')
116+
# 场景2:E 在 CD → 普攻直到可切人
117+
self.logger.info('scene2: E on CD, normal attack until can switch')
118+
self.normal_attack_until_can_switch()
119+
return self.switch_next_char()
121120

122-
if cast_liberation:
123-
self.check_combat()
121+
# ─── 新增辅助方法 ───────────────────────────────────────────────
122+
def _try_liberation(self, wait_crisis=False):
123+
if wait_crisis:
124+
self.wait_crisis_protocol_end()
124125
self.update_blazes()
125-
if self.click_liberation(send_click=True):
126-
self.crisis_time = - 1
127-
self.state = 1
128-
self.in_liberation = True
129-
self.liberation_time = time.time()
130-
self.check_liber()
131-
self.continues_right_click(0.05)
132-
self.continues_normal_attack(0.15)
133-
self.nightfall_combo(cancel_last_smash=True)
134-
self.sleep(0.1)
135-
if self.is_mouse_forte_full():
136-
self.nightfall_combo()
137-
return self.switch_next_char()
126+
if self.echo_available():
127+
self.click_echo(time_out=0)
128+
if self.click_liberation(send_click=True):
129+
self._liberation_followup()
130+
return True
131+
return False
138132

139-
if self.is_e_forte_full():
140-
self.crisis_response_protocol_combo()
141-
self.switch_next_char()
133+
def _liberation_followup(self):
134+
"""click_liberation 成功后进入大招态的固定收尾序列。"""
135+
self.crisis_time = -1
136+
self.state = 1
137+
self.in_liberation = True
138+
self.liberation_time = time.time()
139+
self.check_liber()
140+
self.continues_right_click(0.05)
141+
self.continues_normal_attack(0.15)
142+
self.nightfall_combo(cancel_last_smash=True)
143+
self.sleep(0.1)
144+
if self.is_mouse_forte_full():
145+
self.nightfall_combo()
146+
147+
# ─── 基线方法(原样保留)──────────────────────────────────────────
142148

143149
def basic_attack_breakthrough_combo(self):
144150
if self.is_e_forte_full():
@@ -262,7 +268,7 @@ def calculate_color_percentage_in_masked(self, target_color, box, mask_r1_ratio=
262268
if total_mask_area == 0:
263269
return 0.0
264270
return match_count / total_mask_area
265-
271+
266272
def nightfall_time_left(self):
267273
if self.nightfall_time <= 0:
268274
return 0
@@ -506,19 +512,19 @@ def get_state(self):
506512

507513

508514
zani_light_color = {
509-
'r': (245, 255), # Red range
510-
'g': (245, 255), # Green range
511-
'b': (205, 225) # Blue range
515+
'r': (245, 255),
516+
'g': (245, 255),
517+
'b': (205, 225)
512518
}
513519

514520
zani_blazes_color = {
515-
'r': (231, 257), # Red range
516-
'g': (239, 255), # Green range
517-
'b': (171, 201) # Blue range
521+
'r': (231, 257),
522+
'g': (239, 255),
523+
'b': (171, 201)
518524
}
519525

520526
zani_forte_color = {
521-
'r': (239, 255), # Red range
522-
'g': (222, 255), # Green range
523-
'b': (156, 196) # Blue range
527+
'r': (239, 255),
528+
'g': (222, 255),
529+
'b': (156, 196)
524530
}

src/task/BaseWWTask.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -963,9 +963,9 @@ def open_boss_book(self, name, after_sleep=2):
963963
y = 0.73
964964
elif name == 'zhange':
965965
y = 0.61
966-
elif name == 'mengyan':
967-
y = 0.83
968966
elif name == 'canxiang':
967+
y = 0.83
968+
elif name == 'mengyan':
969969
self.click_relative(0.356, 0.882, after_sleep=after_sleep)
970970
y = 0.86
971971
else:

0 commit comments

Comments
 (0)