@@ -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
508514zani_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
514520zani_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
520526zani_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}
0 commit comments