Skip to content

Commit 237a54e

Browse files
committed
fix(executor): 计时改用累计帧(total_elapsed_frames),支持跨周期 frame>29 的时间轴
原 _perform_action 用 GameTime(cost,tick) 比较,跨周期(cost>0)算不准。改为直接用 target_frame 和 ts.total_elapsed_frames 比较。_read_frames/_wait_until_frames/_step_to_frames 全部用累计帧。
1 parent 00353a0 commit 237a54e

1 file changed

Lines changed: 32 additions & 28 deletions

File tree

custom/maa/action/executor.py

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
from custom import config
2929
from custom.core.avatar import locate_oper
3030
from custom.core.battle.action import Action, ActionType, DirectionType
31-
from custom.core.battle.game_time import GameTime
3231
from custom.core.geometry.convert_pos import convert_position
3332
from custom.core.geometry.map_loader import load_map
3433
from custom.core.geometry.view import transform_map_to_view
@@ -101,7 +100,7 @@ def _execute(self, context: Context, params: dict) -> CustomAction.RunResult:
101100
return CustomAction.RunResult(success=True)
102101

103102
def _parse_actions(self, raw: list[dict], map_data: dict) -> list[Action]:
104-
"""解析 JSON 动作列表 → Action 对象(含投影坐标)。"""
103+
"""解析 JSON 动作列表 → Action 对象(含投影坐标 + 目标帧)。"""
105104
h, w = map_data["height"], map_data["width"]
106105
front = transform_map_to_view(map_data, side=False)
107106
side = transform_map_to_view(map_data, side=True)
@@ -113,9 +112,11 @@ def _parse_actions(self, raw: list[dict], map_data: dict) -> list[Action]:
113112
if frame is not None:
114113
cost_val = frame // config.TICK_MAX_DEFAULT
115114
tick_val = frame % config.TICK_MAX_DEFAULT
115+
target_frame = int(frame)
116116
else:
117117
cost_val = item.get("cost")
118118
tick_val = item.get("tick")
119+
target_frame = (cost_val or 0) * config.TICK_MAX_DEFAULT + (tick_val or 0)
119120

120121
a = Action(
121122
cost=cost_val,
@@ -131,6 +132,9 @@ def _parse_actions(self, raw: list[dict], map_data: dict) -> list[Action]:
131132
logger.warning("跳过无效动作: %s", a)
132133
continue
133134

135+
# 存储原始帧号用于计时
136+
a.target_frame = target_frame
137+
134138
# 棋盘坐标 → 格子 → 投影
135139
if a.pos:
136140
col, row = convert_position(a.pos, h, w)
@@ -159,30 +163,30 @@ def _perform_action(
159163
self, context: Context, ctrl: Controller, action: Action, ts: TimeSource, is_pc: bool
160164
) -> None:
161165
"""执行单个动作:逼近目标帧 → 子弹时间 → 逐帧 → 操作。"""
162-
target = action.get_game_time()
163-
bullet_threshold = GameTime(tick=config.BULLET_THRESHOLD)
166+
target_frame = action.target_frame
167+
bullet_threshold = config.BULLET_THRESHOLD
164168

165-
# 读当前时间
166-
current = self._read_time(ctrl, ts)
169+
# 读当前累计帧
170+
current = self._read_frames(ctrl, ts)
167171
if current is None:
168172
logger.warning("无法读时间,直接执行")
169-
current = target
173+
current = target_frame
170174

171-
logger.info("当前 %s目标 %s", current, target)
175+
logger.info("当前帧 %d目标帧 %d", current, target_frame)
172176

173-
# 逼近:当前 + bullet_threshold < target → 需要逼近
174-
if current + bullet_threshold < target:
175-
logger.debug("距离目标较远,等待接近")
176-
self._wait_until(
177-
ctrl, ts, target - bullet_threshold, context_tasker_stopping=lambda: False
177+
# 逼近:当前 + threshold < target → 等待
178+
if current + bullet_threshold < target_frame:
179+
logger.debug("距离目标 %d 帧,等待", target_frame - current)
180+
self._wait_until_frames(
181+
ctrl, ts, target_frame - bullet_threshold, context_tasker_stopping=lambda: False
178182
)
179183

180-
# 进入子弹时间(选中最后操作的干员)
184+
# 进入子弹时间
181185
self._enter_bullet_time(ctrl, is_pc)
182186
time.sleep(config.GENERAL_WAIT_MS / 1000)
183187

184188
# 逐帧步进到目标
185-
self._step_to_target(ctrl, ts, target, is_pc)
189+
self._step_to_frames(ctrl, ts, target_frame, is_pc)
186190

187191
# 执行动作
188192
if action.action_type == ActionType.DEPLOY:
@@ -192,25 +196,25 @@ def _perform_action(
192196
elif action.action_type == ActionType.RETREAT:
193197
self._retreat(ctrl, action, is_pc)
194198

195-
def _read_time(self, ctrl: Controller, ts: TimeSource) -> GameTime | None:
196-
"""截图 → tick → TimeSource → GameTime。"""
199+
def _read_frames(self, ctrl: Controller, ts: TimeSource) -> int | None:
200+
"""截图 → TimeSource → 累计帧。"""
197201
img = ctrl.post_screencap().wait().get()
198202
lf = ts.update(img)
199203
if lf is None:
200204
return None
201-
return GameTime(cost=0, tick=lf, time=None) # 简化:只用周期内 tick
205+
return ts.total_elapsed_frames
202206

203-
def _wait_until(
204-
self, ctrl: Controller, ts: TimeSource, target: GameTime, context_tasker_stopping
207+
def _wait_until_frames(
208+
self, ctrl: Controller, ts: TimeSource, target_frame: int, context_tasker_stopping
205209
) -> None:
206-
"""等待直到费用条时间到达 target。"""
207-
deadline = time.time() + 120 # 2 分钟超时
210+
"""等待直到累计帧到达 target_frame。"""
211+
deadline = time.time() + 120
208212
while time.time() < deadline:
209213
if context_tasker_stopping():
210214
return
211215
img = ctrl.post_screencap().wait().get()
212216
lf = ts.update(img)
213-
if lf is not None and GameTime(tick=lf) >= target:
217+
if lf is not None and ts.total_elapsed_frames >= target_frame:
214218
return
215219
time.sleep(0.016)
216220

@@ -228,18 +232,18 @@ def _enter_bullet_time(self, ctrl: Controller, is_pc: bool) -> None:
228232
).wait()
229233
time.sleep(config.GENERAL_WAIT_MS / 1000)
230234

231-
def _step_to_target(
232-
self, ctrl: Controller, ts: TimeSource, target: GameTime, is_pc: bool
235+
def _step_to_frames(
236+
self, ctrl: Controller, ts: TimeSource, target_frame: int, is_pc: bool
233237
) -> None:
234-
"""逐帧步进到精确目标帧。"""
238+
"""逐帧步进到累计帧 target_frame。"""
235239
max_steps = 60
236240
for _ in range(max_steps):
237241
img = ctrl.post_screencap().wait().get()
238242
lf = ts.update(img)
239243
if lf is None:
240244
break
241-
if GameTime(tick=lf) >= target:
242-
logger.debug("到达目标帧 %d", lf)
245+
if ts.total_elapsed_frames >= target_frame:
246+
logger.debug("到达目标帧 %d", target_frame)
243247
return
244248
self._step_one_frame(ctrl, is_pc)
245249
time.sleep(config.GENERAL_WAIT_MS / 1000)

0 commit comments

Comments
 (0)