1use crate::platform_time::{Duration, Instant};
10
11use crate::audio::{BgmEngine, KoeEngine, PcmEngine, SeEngine};
12
13use super::constants::RuntimeConstants;
14use super::globals::{GlobalState, ObjectState, StageFormState};
15use super::int_event::IntEvent;
16use super::Value;
17
18fn anim_skip_trace_enabled() -> bool {
19 std::env::var_os("SG_DEBUG").is_some()
20}
21
22fn anim_skip_trace(msg: impl AsRef<str>) {
23 if anim_skip_trace_enabled() {
24 eprintln!("[SG_DEBUG][ANIM_SKIP_TRACE][WAIT] {}", msg.as_ref());
25 }
26}
27
28fn int_event_state(ev: &IntEvent) -> String {
29 format!(
30 "def={} value={} cur={} start={} end={} cur_time={} end_time={} delay={} loop_type={} speed={} real={} active={}",
31 ev.def_value,
32 ev.value,
33 ev.cur_value,
34 ev.start_value,
35 ev.end_value,
36 ev.cur_time,
37 ev.end_time,
38 ev.delay_time,
39 ev.loop_type,
40 ev.speed_type,
41 ev.real_flag,
42 ev.check_event(),
43 )
44}
45
46fn object_event_op_name(ids: &RuntimeConstants, op: i32) -> &'static str {
47 if ids.obj_patno_eve != 0 && op == ids.obj_patno_eve { return "PATNO_EVE"; }
48 if ids.obj_x_eve != 0 && op == ids.obj_x_eve { return "X_EVE"; }
49 if ids.obj_y_eve != 0 && op == ids.obj_y_eve { return "Y_EVE"; }
50 if ids.obj_z_eve != 0 && op == ids.obj_z_eve { return "Z_EVE"; }
51 if ids.obj_center_x_eve != 0 && op == ids.obj_center_x_eve { return "CENTER_X_EVE"; }
52 if ids.obj_center_y_eve != 0 && op == ids.obj_center_y_eve { return "CENTER_Y_EVE"; }
53 if ids.obj_center_z_eve != 0 && op == ids.obj_center_z_eve { return "CENTER_Z_EVE"; }
54 if ids.obj_center_rep_x_eve != 0 && op == ids.obj_center_rep_x_eve { return "CENTER_REP_X_EVE"; }
55 if ids.obj_center_rep_y_eve != 0 && op == ids.obj_center_rep_y_eve { return "CENTER_REP_Y_EVE"; }
56 if ids.obj_center_rep_z_eve != 0 && op == ids.obj_center_rep_z_eve { return "CENTER_REP_Z_EVE"; }
57 if ids.obj_scale_x_eve != 0 && op == ids.obj_scale_x_eve { return "SCALE_X_EVE"; }
58 if ids.obj_scale_y_eve != 0 && op == ids.obj_scale_y_eve { return "SCALE_Y_EVE"; }
59 if ids.obj_scale_z_eve != 0 && op == ids.obj_scale_z_eve { return "SCALE_Z_EVE"; }
60 if ids.obj_rotate_x_eve != 0 && op == ids.obj_rotate_x_eve { return "ROTATE_X_EVE"; }
61 if ids.obj_rotate_y_eve != 0 && op == ids.obj_rotate_y_eve { return "ROTATE_Y_EVE"; }
62 if ids.obj_rotate_z_eve != 0 && op == ids.obj_rotate_z_eve { return "ROTATE_Z_EVE"; }
63 if ids.obj_clip_left_eve != 0 && op == ids.obj_clip_left_eve { return "CLIP_LEFT_EVE"; }
64 if ids.obj_clip_top_eve != 0 && op == ids.obj_clip_top_eve { return "CLIP_TOP_EVE"; }
65 if ids.obj_clip_right_eve != 0 && op == ids.obj_clip_right_eve { return "CLIP_RIGHT_EVE"; }
66 if ids.obj_clip_bottom_eve != 0 && op == ids.obj_clip_bottom_eve { return "CLIP_BOTTOM_EVE"; }
67 if ids.obj_src_clip_left_eve != 0 && op == ids.obj_src_clip_left_eve { return "SRC_CLIP_LEFT_EVE"; }
68 if ids.obj_src_clip_top_eve != 0 && op == ids.obj_src_clip_top_eve { return "SRC_CLIP_TOP_EVE"; }
69 if ids.obj_src_clip_right_eve != 0 && op == ids.obj_src_clip_right_eve { return "SRC_CLIP_RIGHT_EVE"; }
70 if ids.obj_src_clip_bottom_eve != 0 && op == ids.obj_src_clip_bottom_eve { return "SRC_CLIP_BOTTOM_EVE"; }
71 if ids.obj_tr_eve != 0 && op == ids.obj_tr_eve { return "TR_EVE"; }
72 if ids.obj_mono_eve != 0 && op == ids.obj_mono_eve { return "MONO_EVE"; }
73 if ids.obj_reverse_eve != 0 && op == ids.obj_reverse_eve { return "REVERSE_EVE"; }
74 if ids.obj_bright_eve != 0 && op == ids.obj_bright_eve { return "BRIGHT_EVE"; }
75 if ids.obj_dark_eve != 0 && op == ids.obj_dark_eve { return "DARK_EVE"; }
76 if ids.obj_color_r_eve != 0 && op == ids.obj_color_r_eve { return "COLOR_R_EVE"; }
77 if ids.obj_color_g_eve != 0 && op == ids.obj_color_g_eve { return "COLOR_G_EVE"; }
78 if ids.obj_color_b_eve != 0 && op == ids.obj_color_b_eve { return "COLOR_B_EVE"; }
79 if ids.obj_color_rate_eve != 0 && op == ids.obj_color_rate_eve { return "COLOR_RATE_EVE"; }
80 if ids.obj_color_add_r_eve != 0 && op == ids.obj_color_add_r_eve { return "COLOR_ADD_R_EVE"; }
81 if ids.obj_color_add_g_eve != 0 && op == ids.obj_color_add_g_eve { return "COLOR_ADD_G_EVE"; }
82 if ids.obj_color_add_b_eve != 0 && op == ids.obj_color_add_b_eve { return "COLOR_ADD_B_EVE"; }
83 if ids.obj_x_rep_eve != 0 && op == ids.obj_x_rep_eve { return "X_REP_EVE"; }
84 if ids.obj_y_rep_eve != 0 && op == ids.obj_y_rep_eve { return "Y_REP_EVE"; }
85 if ids.obj_z_rep_eve != 0 && op == ids.obj_z_rep_eve { return "Z_REP_EVE"; }
86 if ids.obj_tr_rep_eve != 0 && op == ids.obj_tr_rep_eve { return "TR_REP_EVE"; }
87 "UNKNOWN_EVE"
88}
89
90#[derive(Debug, Clone, Copy)]
91pub enum AudioWait {
92 Bgm,
93 BgmFade,
94 KoeAny,
95 SeAny,
96 PcmAny,
97 PcmSlot(u8),
98}
99
100#[derive(Debug, Clone)]
101pub enum EventWait {
102 ObjectAll {
103 stage_form_id: u32,
104 stage_idx: i64,
105 runtime_slot: usize,
106 },
107 ObjectOne {
108 stage_form_id: u32,
109 stage_idx: i64,
110 runtime_slot: usize,
111 op: i32,
112 },
113 ObjectList {
114 stage_form_id: u32,
115 stage_idx: i64,
116 runtime_slot: usize,
117 list_op: i32,
118 list_idx: usize,
119 },
120 GenericIntEvent {
121 form_id: u32,
122 index: Option<usize>,
123 },
124 FogX,
125 CounterThreshold {
126 form_id: u32,
127 index: usize,
128 target: i64,
129 },
130}
131
132#[derive(Debug, Clone, Copy)]
133pub struct MovieWait {
134 pub stage_form_id: u32,
135 pub stage_idx: i64,
136 pub runtime_slot: usize,
137 pub return_value_flag: bool,
138}
139
140fn object_runtime_slot(idx: usize, obj: &ObjectState) -> usize {
141 obj.runtime_slot_or(idx)
142}
143
144fn find_object_by_runtime_slot<'a>(
145 objects: &'a [ObjectState],
146 runtime_slot: usize,
147) -> Option<&'a ObjectState> {
148 for (idx, obj) in objects.iter().enumerate() {
149 if object_runtime_slot(idx, obj) == runtime_slot {
150 return Some(obj);
151 }
152 if let Some(found) = find_object_by_runtime_slot(&obj.runtime.child_objects, runtime_slot) {
153 return Some(found);
154 }
155 }
156 None
157}
158
159fn find_object_by_runtime_slot_mut<'a>(
160 mut objects: &'a mut [ObjectState],
161 runtime_slot: usize,
162) -> Option<&'a mut ObjectState> {
163 let mut idx = 0usize;
164 while let Some((obj, tail)) = objects.split_first_mut() {
165 if object_runtime_slot(idx, obj) == runtime_slot {
166 return Some(obj);
167 }
168 if let Some(found) =
169 find_object_by_runtime_slot_mut(&mut obj.runtime.child_objects, runtime_slot)
170 {
171 return Some(found);
172 }
173 objects = tail;
174 idx += 1;
175 }
176 None
177}
178
179fn object_event_list_for_wait<'a>(
180 obj: &'a ObjectState,
181 ids: &RuntimeConstants,
182 op: i32,
183) -> Option<&'a Vec<IntEvent>> {
184 obj.int_event_list_by_op(ids, op)
185 .or_else(|| obj.rep_int_event_list_by_rep_op(ids, op))
186}
187
188fn object_event_list_for_wait_mut<'a>(
189 obj: &'a mut ObjectState,
190 ids: &RuntimeConstants,
191 op: i32,
192) -> Option<&'a mut Vec<IntEvent>> {
193 if ids.obj_x_rep_eve != 0 && op == ids.obj_x_rep_eve {
194 Some(&mut obj.runtime.prop_event_lists.x_rep)
195 } else if ids.obj_y_rep_eve != 0 && op == ids.obj_y_rep_eve {
196 Some(&mut obj.runtime.prop_event_lists.y_rep)
197 } else if ids.obj_z_rep_eve != 0 && op == ids.obj_z_rep_eve {
198 Some(&mut obj.runtime.prop_event_lists.z_rep)
199 } else if ids.obj_tr_rep_eve != 0 && op == ids.obj_tr_rep_eve {
200 Some(&mut obj.runtime.prop_event_lists.tr_rep)
201 } else if ids.obj_x_rep != 0 && op == ids.obj_x_rep {
202 Some(&mut obj.runtime.prop_event_lists.x_rep)
203 } else if ids.obj_y_rep != 0 && op == ids.obj_y_rep {
204 Some(&mut obj.runtime.prop_event_lists.y_rep)
205 } else if ids.obj_z_rep != 0 && op == ids.obj_z_rep {
206 Some(&mut obj.runtime.prop_event_lists.z_rep)
207 } else if ids.obj_tr_rep != 0 && op == ids.obj_tr_rep {
208 Some(&mut obj.runtime.prop_event_lists.tr_rep)
209 } else {
210 None
211 }
212}
213
214fn object_active_in_stage_state_by_runtime_slot(
215 st: &StageFormState,
216 stage_idx: i64,
217 runtime_slot: usize,
218) -> Option<&ObjectState> {
219 if let Some(obj) = st
220 .object_lists
221 .get(&stage_idx)
222 .and_then(|list| find_object_by_runtime_slot(list, runtime_slot))
223 {
224 return Some(obj);
225 }
226
227 if let Some(items) = st.btnselitem_lists.get(&stage_idx) {
228 for item in items {
229 if let Some(obj) = find_object_by_runtime_slot(&item.object_list, runtime_slot) {
230 return Some(obj);
231 }
232 }
233 }
234
235 if let Some(mwnds) = st.mwnd_lists.get(&stage_idx) {
236 for mwnd in mwnds {
237 if let Some(obj) = find_object_by_runtime_slot(&mwnd.button_list, runtime_slot) {
238 return Some(obj);
239 }
240 if let Some(obj) = find_object_by_runtime_slot(&mwnd.face_list, runtime_slot) {
241 return Some(obj);
242 }
243 if let Some(obj) = find_object_by_runtime_slot(&mwnd.object_list, runtime_slot) {
244 return Some(obj);
245 }
246 }
247 }
248
249 None
250}
251
252fn object_active_by_runtime_slot(
253 globals: &GlobalState,
254 stage_form_id: u32,
255 stage_idx: i64,
256 runtime_slot: usize,
257) -> Option<&ObjectState> {
258 globals
259 .stage_forms
260 .get(&stage_form_id)
261 .and_then(|st| object_active_in_stage_state_by_runtime_slot(st, stage_idx, runtime_slot))
262}
263
264fn find_object_by_runtime_slot_mut_ptr(
265 objects: &mut [ObjectState],
266 runtime_slot: usize,
267) -> Option<*mut ObjectState> {
268 find_object_by_runtime_slot_mut(objects, runtime_slot).map(|obj| obj as *mut ObjectState)
269}
270
271fn object_active_by_runtime_slot_mut(
272 globals: &mut GlobalState,
273 stage_form_id: u32,
274 stage_idx: i64,
275 runtime_slot: usize,
276) -> Option<&mut ObjectState> {
277 let st = globals.stage_forms.get_mut(&stage_form_id)?;
278
279 if let Some(ptr) = st
280 .object_lists
281 .get_mut(&stage_idx)
282 .and_then(|list| find_object_by_runtime_slot_mut_ptr(list, runtime_slot))
283 {
284 return unsafe { Some(&mut *ptr) };
285 }
286
287 if let Some(items) = st.btnselitem_lists.get_mut(&stage_idx) {
288 for item in items {
289 if let Some(ptr) =
290 find_object_by_runtime_slot_mut_ptr(&mut item.object_list, runtime_slot)
291 {
292 return unsafe { Some(&mut *ptr) };
293 }
294 }
295 }
296
297 if let Some(mwnds) = st.mwnd_lists.get_mut(&stage_idx) {
298 for mwnd in mwnds {
299 if let Some(ptr) =
300 find_object_by_runtime_slot_mut_ptr(&mut mwnd.button_list, runtime_slot)
301 {
302 return unsafe { Some(&mut *ptr) };
303 }
304 if let Some(ptr) =
305 find_object_by_runtime_slot_mut_ptr(&mut mwnd.face_list, runtime_slot)
306 {
307 return unsafe { Some(&mut *ptr) };
308 }
309 if let Some(ptr) =
310 find_object_by_runtime_slot_mut_ptr(&mut mwnd.object_list, runtime_slot)
311 {
312 return unsafe { Some(&mut *ptr) };
313 }
314 }
315 }
316
317 None
318}
319
320fn finish_wait_skipped_event(ev: &mut IntEvent) {
321 let before = if anim_skip_trace_enabled() {
322 Some(int_event_state(ev))
323 } else {
324 None
325 };
326 ev.end_event();
327 ev.frame();
328 if let Some(before) = before {
329 anim_skip_trace(format!(
330 "finish_event before=[{}] after=[{}]",
331 before,
332 int_event_state(ev)
333 ));
334 }
335}
336
337fn event_prop_pairs(ids: &RuntimeConstants) -> [(i32, i32); 36] {
338 [
339 (ids.obj_patno_eve, ids.obj_patno),
340 (ids.obj_x_eve, ids.obj_x),
341 (ids.obj_y_eve, ids.obj_y),
342 (ids.obj_z_eve, ids.obj_z),
343 (ids.obj_center_x_eve, ids.obj_center_x),
344 (ids.obj_center_y_eve, ids.obj_center_y),
345 (ids.obj_center_z_eve, ids.obj_center_z),
346 (ids.obj_center_rep_x_eve, ids.obj_center_rep_x),
347 (ids.obj_center_rep_y_eve, ids.obj_center_rep_y),
348 (ids.obj_center_rep_z_eve, ids.obj_center_rep_z),
349 (ids.obj_scale_x_eve, ids.obj_scale_x),
350 (ids.obj_scale_y_eve, ids.obj_scale_y),
351 (ids.obj_scale_z_eve, ids.obj_scale_z),
352 (ids.obj_rotate_x_eve, ids.obj_rotate_x),
353 (ids.obj_rotate_y_eve, ids.obj_rotate_y),
354 (ids.obj_rotate_z_eve, ids.obj_rotate_z),
355 (ids.obj_clip_left_eve, ids.obj_clip_left),
356 (ids.obj_clip_top_eve, ids.obj_clip_top),
357 (ids.obj_clip_right_eve, ids.obj_clip_right),
358 (ids.obj_clip_bottom_eve, ids.obj_clip_bottom),
359 (ids.obj_src_clip_left_eve, ids.obj_src_clip_left),
360 (ids.obj_src_clip_top_eve, ids.obj_src_clip_top),
361 (ids.obj_src_clip_right_eve, ids.obj_src_clip_right),
362 (ids.obj_src_clip_bottom_eve, ids.obj_src_clip_bottom),
363 (ids.obj_tr_eve, ids.obj_tr),
364 (ids.obj_mono_eve, ids.obj_mono),
365 (ids.obj_reverse_eve, ids.obj_reverse),
366 (ids.obj_bright_eve, ids.obj_bright),
367 (ids.obj_dark_eve, ids.obj_dark),
368 (ids.obj_color_r_eve, ids.obj_color_r),
369 (ids.obj_color_g_eve, ids.obj_color_g),
370 (ids.obj_color_b_eve, ids.obj_color_b),
371 (ids.obj_color_rate_eve, ids.obj_color_rate),
372 (ids.obj_color_add_r_eve, ids.obj_color_add_r),
373 (ids.obj_color_add_g_eve, ids.obj_color_add_g),
374 (ids.obj_color_add_b_eve, ids.obj_color_add_b),
375 ]
376}
377
378fn object_prop_op_for_event_op(ids: &RuntimeConstants, event_op: i32) -> Option<i32> {
379 event_prop_pairs(ids)
380 .into_iter()
381 .find_map(|(ev_op, prop_op)| (ev_op != 0 && event_op == ev_op).then_some(prop_op))
382}
383
384fn finish_wait_skipped_object_event_by_op(
385 obj: &mut ObjectState,
386 ids: &RuntimeConstants,
387 event_op: i32,
388) {
389 let file = obj.file_name.as_deref().unwrap_or("-").to_string();
390 let runtime_slot = obj.runtime_slot_or(usize::MAX);
391 let event_name = object_event_op_name(ids, event_op);
392 let Some(value) = obj.int_event_by_op_mut(ids, event_op).map(|ev| {
393 anim_skip_trace(format!(
394 "finish_object_event begin slot={} file={} op={}({}) state=[{}]",
395 runtime_slot,
396 file,
397 event_op,
398 event_name,
399 int_event_state(ev)
400 ));
401 finish_wait_skipped_event(ev);
402 anim_skip_trace(format!(
403 "finish_object_event event_done slot={} file={} op={}({}) state=[{}]",
404 runtime_slot,
405 file,
406 event_op,
407 event_name,
408 int_event_state(ev)
409 ));
410 ev.get_total_value() as i64
411 }) else {
412 anim_skip_trace(format!(
413 "finish_object_event missing slot={} file={} op={}({})",
414 runtime_slot, file, event_op, event_name
415 ));
416 return;
417 };
418 if let Some(prop_op) = object_prop_op_for_event_op(ids, event_op) {
419 obj.set_int_prop(ids, prop_op, value);
420 anim_skip_trace(format!(
421 "finish_object_event prop_write slot={} file={} event_op={}({}) prop_op={} value={} obj_tr={} obj_alpha={} obj_pos=({}, {})",
422 runtime_slot,
423 file,
424 event_op,
425 event_name,
426 prop_op,
427 value,
428 obj.get_int_prop(ids, ids.obj_tr),
429 obj.base.alpha,
430 obj.get_int_prop(ids, ids.obj_x),
431 obj.get_int_prop(ids, ids.obj_y),
432 ));
433 } else {
434 anim_skip_trace(format!(
435 "finish_object_event no_prop_map slot={} file={} event_op={}({}) value={}",
436 runtime_slot, file, event_op, event_name, value
437 ));
438 }
439}
440
441fn finish_wait_skipped_object_events(obj: &mut ObjectState, ids: &RuntimeConstants) {
442 let file = obj.file_name.as_deref().unwrap_or("-").to_string();
443 let runtime_slot = obj.runtime_slot_or(usize::MAX);
444 anim_skip_trace(format!(
445 "finish_object_all begin slot={} file={} any_active={} tr={} alpha={} pos=({}, {})",
446 runtime_slot,
447 file,
448 obj.any_event_active(),
449 obj.get_int_prop(ids, ids.obj_tr),
450 obj.base.alpha,
451 obj.get_int_prop(ids, ids.obj_x),
452 obj.get_int_prop(ids, ids.obj_y),
453 ));
454 let mut final_values = Vec::new();
455 for (event_op, prop_op) in event_prop_pairs(ids) {
456 if event_op == 0 || prop_op == 0 {
457 continue;
458 }
459 if let Some(ev) = obj.int_event_by_op_mut(ids, event_op) {
460 if ev.check_event() {
461 anim_skip_trace(format!(
462 "finish_object_all active slot={} file={} op={}({}) state=[{}]",
463 runtime_slot,
464 file,
465 event_op,
466 object_event_op_name(ids, event_op),
467 int_event_state(ev)
468 ));
469 }
470 finish_wait_skipped_event(ev);
471 final_values.push((event_op, prop_op, ev.get_total_value() as i64));
472 }
473 }
474 obj.runtime.prop_event_lists.end_all();
475 obj.runtime.prop_event_lists.frame();
476 for (event_op, prop_op, value) in final_values {
477 obj.set_int_prop(ids, prop_op, value);
478 anim_skip_trace(format!(
479 "finish_object_all prop_write slot={} file={} event_op={}({}) prop_op={} value={}",
480 runtime_slot,
481 file,
482 event_op,
483 object_event_op_name(ids, event_op),
484 prop_op,
485 value
486 ));
487 }
488 anim_skip_trace(format!(
489 "finish_object_all end slot={} file={} any_active={} tr={} alpha={} pos=({}, {})",
490 runtime_slot,
491 file,
492 obj.any_event_active(),
493 obj.get_int_prop(ids, ids.obj_tr),
494 obj.base.alpha,
495 obj.get_int_prop(ids, ids.obj_x),
496 obj.get_int_prop(ids, ids.obj_y),
497 ));
498}
499
500fn finish_event_wait_by_key(w: &EventWait, globals: &mut GlobalState, ids: &RuntimeConstants) {
501 match w {
502 EventWait::ObjectAll {
503 stage_form_id,
504 stage_idx,
505 runtime_slot,
506 } => {
507 if let Some(obj) = object_active_by_runtime_slot_mut(
508 globals,
509 *stage_form_id,
510 *stage_idx,
511 *runtime_slot,
512 ) {
513 finish_wait_skipped_object_events(obj, ids);
514 }
515 }
516 EventWait::ObjectOne {
517 stage_form_id,
518 stage_idx,
519 runtime_slot,
520 op,
521 } => {
522 if let Some(obj) = object_active_by_runtime_slot_mut(
523 globals,
524 *stage_form_id,
525 *stage_idx,
526 *runtime_slot,
527 ) {
528 finish_wait_skipped_object_event_by_op(obj, ids, *op);
529 }
530 }
531 EventWait::ObjectList {
532 stage_form_id,
533 stage_idx,
534 runtime_slot,
535 list_op,
536 list_idx,
537 } => {
538 if let Some(obj) = object_active_by_runtime_slot_mut(
539 globals,
540 *stage_form_id,
541 *stage_idx,
542 *runtime_slot,
543 ) {
544 let file = obj.file_name.as_deref().unwrap_or("-").to_string();
545 if let Some(ev) = object_event_list_for_wait_mut(obj, ids, *list_op)
546 .and_then(|v| v.get_mut(*list_idx))
547 {
548 anim_skip_trace(format!(
549 "finish_object_list_event begin stage_form={} stage={} slot={} file={} list_op={}({}) list_idx={} state=[{}]",
550 stage_form_id,
551 stage_idx,
552 runtime_slot,
553 file,
554 list_op,
555 object_event_op_name(ids, *list_op),
556 list_idx,
557 int_event_state(ev)
558 ));
559 finish_wait_skipped_event(ev);
560 anim_skip_trace(format!(
561 "finish_object_list_event end stage_form={} stage={} slot={} file={} list_op={}({}) list_idx={} state=[{}]",
562 stage_form_id,
563 stage_idx,
564 runtime_slot,
565 file,
566 list_op,
567 object_event_op_name(ids, *list_op),
568 list_idx,
569 int_event_state(ev)
570 ));
571 } else {
572 anim_skip_trace(format!(
573 "finish_object_list_event missing stage_form={} stage={} slot={} file={} list_op={}({}) list_idx={}",
574 stage_form_id,
575 stage_idx,
576 runtime_slot,
577 file,
578 list_op,
579 object_event_op_name(ids, *list_op),
580 list_idx
581 ));
582 }
583 }
584 }
585 EventWait::GenericIntEvent { form_id, index } => match index {
586 Some(i) => {
587 if let Some(ev) = globals
588 .int_event_lists
589 .get_mut(form_id)
590 .and_then(|v| v.get_mut(*i))
591 {
592 anim_skip_trace(format!(
593 "finish_generic_int_event begin form_id={} index={} state=[{}]",
594 form_id, i, int_event_state(ev)
595 ));
596 finish_wait_skipped_event(ev);
597 anim_skip_trace(format!(
598 "finish_generic_int_event end form_id={} index={} state=[{}]",
599 form_id, i, int_event_state(ev)
600 ));
601 }
602 }
603 None => {
604 if let Some(ev) = globals.int_event_roots.get_mut(form_id) {
605 anim_skip_trace(format!(
606 "finish_generic_int_event begin form_id={} index=None state=[{}]",
607 form_id, int_event_state(ev)
608 ));
609 finish_wait_skipped_event(ev);
610 anim_skip_trace(format!(
611 "finish_generic_int_event end form_id={} index=None state=[{}]",
612 form_id, int_event_state(ev)
613 ));
614 }
615 }
616 },
617 EventWait::FogX => {
618 finish_wait_skipped_event(&mut globals.fog_global.x_event);
619 globals.fog_global.scroll_x = globals.fog_global.x_event.get_total_value() as f32;
620 }
621 EventWait::CounterThreshold { .. } => {}
622 }
623}
624
625#[derive(Debug, Default, Clone)]
626pub struct VmWait {
627 until: Option<Instant>,
628 until_frame: Option<u64>,
629 waiting_for_key: bool,
630 skip_time_on_key: bool,
632
633 audio: Option<AudioWait>,
634 audio_return_value: bool,
635
636 event: Option<EventWait>,
637 event_key_skip: bool,
638 event_return_value: bool,
639
640 movie: Option<MovieWait>,
641 movie_key_skip: bool,
642
643 global_movie: bool,
644 global_movie_key_skip: bool,
645 global_movie_return_value: bool,
646
647 movie_skip_info: Option<MovieWait>,
648 pending_value: Option<Value>,
649
650 system_modal: bool,
652
653 wipe: bool,
654 wipe_key_skip: bool,
655
656 block_generation: u64,
657}
658
659impl VmWait {
660 pub fn block_generation(&self) -> u64 {
661 self.block_generation
662 }
663
664 pub fn needs_runtime_poll(&self) -> bool {
665 self.until.is_some()
666 || self.until_frame.is_some()
667 || self.audio.is_some()
668 || self.event.is_some()
669 || self.movie.is_some()
670 || self.global_movie
671 || self.wipe
672 }
673
674 fn mark_block_request(&mut self) {
675 self.block_generation = self.block_generation.wrapping_add(1);
676 }
677
678 pub fn poll(
679 &mut self,
680 stack: &mut Vec<Value>,
681 bgm: &mut BgmEngine,
682 koe: &mut KoeEngine,
683 se: &mut SeEngine,
684 pcm: &mut PcmEngine,
685 globals: &mut GlobalState,
686 ids: &RuntimeConstants,
687 ) -> bool {
688 let blocked = self.is_blocked(bgm, koe, se, pcm, globals, ids);
689 if !blocked {
690 if let Some(v) = self.pending_value.take() {
691 stack.push(v);
692 }
693 }
694 blocked
695 }
696
697 pub fn is_blocked(
698 &mut self,
699 bgm: &mut BgmEngine,
700 koe: &mut KoeEngine,
701 se: &mut SeEngine,
702 pcm: &mut PcmEngine,
703 globals: &mut GlobalState,
704 ids: &RuntimeConstants,
705 ) -> bool {
706 if let Some(t) = self.until {
708 if Instant::now() >= t {
709 let key_skippable_timewait = self.skip_time_on_key;
710 self.until = None;
711 self.skip_time_on_key = false;
712 if key_skippable_timewait {
713 anim_skip_trace("timewait_key naturally finished pending=0");
714 self.pending_value = Some(Value::Int(0));
715 }
716 }
717 }
718
719 if let Some(frame) = self.until_frame {
720 if globals.render_frame >= frame {
721 self.until_frame = None;
722 }
723 }
724
725 if let Some(w) = self.audio {
727 let done = match w {
728 AudioWait::Bgm => !bgm.is_playing(),
729 AudioWait::BgmFade => !bgm.is_fade_out_doing(),
730 AudioWait::KoeAny => !koe.is_playing_any(),
731 AudioWait::SeAny => !se.is_playing_any(),
732 AudioWait::PcmAny => !pcm.is_playing_any(),
733 AudioWait::PcmSlot(s) => !pcm.is_playing_slot(s as usize),
734 };
735 if done {
736 self.audio = None;
737 if self.audio_return_value {
738 self.pending_value = Some(Value::Int(0));
739 }
740 self.audio_return_value = false;
741 }
742 }
743
744 let event_done = if let Some(w) = self.event.as_ref() {
746 match w {
747 EventWait::ObjectAll {
748 stage_form_id,
749 stage_idx,
750 runtime_slot,
751 } => object_active_by_runtime_slot(
752 globals,
753 *stage_form_id,
754 *stage_idx,
755 *runtime_slot,
756 )
757 .map(|obj| !obj.used || !obj.any_event_active())
758 .unwrap_or(true),
759 EventWait::ObjectOne {
760 stage_form_id,
761 stage_idx,
762 runtime_slot,
763 op,
764 } => object_active_by_runtime_slot(
765 globals,
766 *stage_form_id,
767 *stage_idx,
768 *runtime_slot,
769 )
770 .map(|obj| {
771 !obj.used
772 || !obj
773 .int_event_by_op(ids, *op)
774 .map(|e| e.check_event())
775 .unwrap_or(false)
776 })
777 .unwrap_or(true),
778 EventWait::ObjectList {
779 stage_form_id,
780 stage_idx,
781 runtime_slot,
782 list_op,
783 list_idx,
784 } => object_active_by_runtime_slot(
785 globals,
786 *stage_form_id,
787 *stage_idx,
788 *runtime_slot,
789 )
790 .map(|obj| {
791 let active = object_event_list_for_wait(obj, ids, *list_op)
792 .and_then(|v| v.get(*list_idx))
793 .map(|e| e.check_event())
794 .unwrap_or(false);
795 !obj.used || !active
796 })
797 .unwrap_or(true),
798 EventWait::GenericIntEvent { form_id, index } => match index {
799 Some(i) => globals
800 .int_event_lists
801 .get(form_id)
802 .and_then(|v| v.get(*i))
803 .map(|e| !e.check_event())
804 .unwrap_or(true),
805 None => globals
806 .int_event_roots
807 .get(form_id)
808 .map(|e| !e.check_event())
809 .unwrap_or(true),
810 },
811 EventWait::FogX => !globals.fog_global.x_event.check_event(),
812 EventWait::CounterThreshold {
813 form_id,
814 index,
815 target,
816 } => globals
817 .counter_lists
818 .get(form_id)
819 .and_then(|v| v.get(*index))
820 .map(|c| c.get_count() - *target >= 0)
821 .unwrap_or(true),
822 }
823 } else {
824 false
825 };
826 if event_done {
827 let was_event_key_skip = self.event_key_skip;
828 anim_skip_trace(format!(
829 "event_wait naturally finished event={:?} key_skip={} return_value={}",
830 self.event.as_ref(), was_event_key_skip, self.event_return_value
831 ));
832 self.event = None;
833 self.event_key_skip = false;
834 if was_event_key_skip {
835 self.waiting_for_key = false;
836 }
837 if self.event_return_value {
838 self.pending_value = Some(Value::Int(0));
839 }
840 self.event_return_value = false;
841 }
842
843 if self.global_movie {
845 if !globals.mov.playing {
846 if self.global_movie_return_value {
847 self.pending_value = Some(Value::Int(0));
848 }
849 self.global_movie = false;
850 self.global_movie_key_skip = false;
851 self.global_movie_return_value = false;
852 }
853 }
854
855 if let Some(w) = self.movie {
857 let done = object_active_by_runtime_slot(
858 globals,
859 w.stage_form_id,
860 w.stage_idx,
861 w.runtime_slot,
862 )
863 .map(|obj| !obj.used || !obj.movie.check_movie())
864 .unwrap_or(true);
865
866 if done {
867 if w.return_value_flag {
868 self.pending_value = Some(Value::Int(0));
869 }
870 self.movie = None;
871 self.movie_key_skip = false;
872 }
873 }
874
875 if self.wipe {
877 if globals.wipe_done() {
878 self.wipe = false;
879 self.wipe_key_skip = false;
880 }
881 }
882
883 self.waiting_for_key
884 || self.until.is_some()
885 || self.until_frame.is_some()
886 || self.audio.is_some()
887 || self.event.is_some()
888 || self.movie.is_some()
889 || self.global_movie
890 || self.system_modal
891 || self.wipe
892 }
893
894 pub fn wait_system_modal(&mut self) {
895 self.mark_block_request();
896 self.system_modal = true;
897 }
898
899 pub fn finish_system_modal(&mut self, value: Value) {
900 if self.system_modal {
901 self.system_modal = false;
902 self.pending_value = Some(value);
903 }
904 }
905
906 pub fn finish_system_modal_void(&mut self) {
907 if self.system_modal {
908 self.system_modal = false;
909 self.pending_value = None;
910 }
911 }
912
913 pub fn system_modal_active(&self) -> bool {
914 self.system_modal
915 }
916
917 pub fn wait_ms(&mut self, ms: u64) {
918 if ms == 0 {
919 return;
920 }
921 self.mark_block_request();
922 self.until = Some(Instant::now() + Duration::from_millis(ms));
923 self.skip_time_on_key = false;
924 }
925
926 pub fn wait_next_frame(&mut self, current_frame: u64) {
927 self.mark_block_request();
928 self.until_frame = Some(current_frame.saturating_add(1));
929 self.skip_time_on_key = false;
930 }
931
932 pub fn wait_ms_key(&mut self, ms: u64) {
934 if ms == 0 {
935 anim_skip_trace("wait_ms_key ignored ms=0");
936 return;
937 }
938 self.mark_block_request();
939 self.until = Some(Instant::now() + Duration::from_millis(ms));
940 self.skip_time_on_key = true;
941 anim_skip_trace(format!("wait_ms_key start ms={} block_generation={}", ms, self.block_generation));
942 }
943
944 pub fn wait_key(&mut self) {
945 self.mark_block_request();
946 self.waiting_for_key = true;
947 }
948
949 pub fn wait_audio(&mut self, w: AudioWait, key: bool) {
950 self.wait_audio_with_return(w, key, false);
951 }
952
953 pub fn wait_audio_with_return(&mut self, w: AudioWait, key: bool, return_value_flag: bool) {
954 self.mark_block_request();
955 self.audio = Some(w);
956 self.audio_return_value = return_value_flag;
957 if key {
958 self.waiting_for_key = true;
959 }
960 }
961
962 pub fn wait_object_all_events(
963 &mut self,
964 stage_form_id: u32,
965 stage_idx: i64,
966 runtime_slot: usize,
967 key_skip: bool,
968 ) {
969 self.mark_block_request();
970 self.event = Some(EventWait::ObjectAll {
971 stage_form_id,
972 stage_idx,
973 runtime_slot,
974 });
975 anim_skip_trace(format!(
976 "wait_object_all_events start stage_form={} stage={} slot={} key_skip={} block_generation={}",
977 stage_form_id, stage_idx, runtime_slot, key_skip, self.block_generation
978 ));
979 self.event_key_skip = key_skip;
980 self.event_return_value = false;
981 if key_skip {
982 self.waiting_for_key = true;
983 }
984 }
985
986 pub fn wait_object_event(
987 &mut self,
988 stage_form_id: u32,
989 stage_idx: i64,
990 runtime_slot: usize,
991 op: i32,
992 key_skip: bool,
993 return_value_flag: bool,
994 ) {
995 self.mark_block_request();
996 self.event = Some(EventWait::ObjectOne {
997 stage_form_id,
998 stage_idx,
999 runtime_slot,
1000 op,
1001 });
1002 anim_skip_trace(format!(
1003 "wait_object_event start stage_form={} stage={} slot={} op={} key_skip={} return_value={} block_generation={}",
1004 stage_form_id, stage_idx, runtime_slot, op, key_skip, return_value_flag, self.block_generation
1005 ));
1006 self.event_key_skip = key_skip;
1007 self.event_return_value = return_value_flag;
1008 if key_skip {
1009 self.waiting_for_key = true;
1010 }
1011 }
1012
1013 pub fn wait_object_event_list(
1014 &mut self,
1015 stage_form_id: u32,
1016 stage_idx: i64,
1017 runtime_slot: usize,
1018 list_op: i32,
1019 list_idx: usize,
1020 key_skip: bool,
1021 return_value_flag: bool,
1022 ) {
1023 self.mark_block_request();
1024 self.event = Some(EventWait::ObjectList {
1025 stage_form_id,
1026 stage_idx,
1027 runtime_slot,
1028 list_op,
1029 list_idx,
1030 });
1031 anim_skip_trace(format!(
1032 "wait_object_event_list start stage_form={} stage={} slot={} list_op={} list_idx={} key_skip={} return_value={} block_generation={}",
1033 stage_form_id, stage_idx, runtime_slot, list_op, list_idx, key_skip, return_value_flag, self.block_generation
1034 ));
1035 self.event_key_skip = key_skip;
1036 self.event_return_value = return_value_flag;
1037 if key_skip {
1038 self.waiting_for_key = true;
1039 }
1040 }
1041
1042 pub fn wait_global_movie(&mut self, key_skip: bool, return_value_flag: bool) {
1043 self.mark_block_request();
1044 self.global_movie = true;
1045 self.global_movie_key_skip = key_skip;
1046 self.global_movie_return_value = return_value_flag;
1047 if key_skip {
1048 self.waiting_for_key = true;
1049 }
1050 }
1051
1052 pub fn wait_object_movie(
1053 &mut self,
1054 stage_form_id: u32,
1055 stage_idx: i64,
1056 runtime_slot: usize,
1057 key_skip: bool,
1058 return_value_flag: bool,
1059 ) {
1060 self.mark_block_request();
1061 self.movie = Some(MovieWait {
1062 stage_form_id,
1063 stage_idx,
1064 runtime_slot,
1065 return_value_flag,
1066 });
1067 self.movie_key_skip = key_skip;
1068 if key_skip {
1069 self.waiting_for_key = true;
1070 }
1071 }
1072
1073 pub fn wait_generic_int_event(
1074 &mut self,
1075 form_id: u32,
1076 index: Option<usize>,
1077 key_skip: bool,
1078 return_value_flag: bool,
1079 ) {
1080 self.mark_block_request();
1081 self.event = Some(EventWait::GenericIntEvent { form_id, index });
1082 anim_skip_trace(format!(
1083 "wait_generic_int_event start form_id={} index={:?} key_skip={} return_value={} block_generation={}",
1084 form_id, index, key_skip, return_value_flag, self.block_generation
1085 ));
1086 self.event_key_skip = key_skip;
1087 self.event_return_value = return_value_flag;
1088 if key_skip {
1089 self.waiting_for_key = true;
1090 }
1091 }
1092
1093 pub fn wait_fog_x_event(&mut self, key_skip: bool, return_value_flag: bool) {
1094 self.mark_block_request();
1095 self.event = Some(EventWait::FogX);
1096 self.event_key_skip = key_skip;
1097 self.event_return_value = return_value_flag;
1098 if key_skip {
1099 self.waiting_for_key = true;
1100 }
1101 }
1102
1103 pub fn wait_counter(
1104 &mut self,
1105 form_id: u32,
1106 index: usize,
1107 target: i64,
1108 key_skip: bool,
1109 return_value_flag: bool,
1110 ) {
1111 self.mark_block_request();
1112 self.event = Some(EventWait::CounterThreshold {
1113 form_id,
1114 index,
1115 target,
1116 });
1117 self.event_key_skip = key_skip;
1118 self.event_return_value = return_value_flag;
1119 if key_skip {
1120 self.waiting_for_key = true;
1121 }
1122 }
1123
1124 pub fn wait_wipe(&mut self, key_skip: bool) {
1125 self.mark_block_request();
1126 self.wipe = true;
1127 self.wipe_key_skip = key_skip;
1128 if key_skip {
1129 self.waiting_for_key = true;
1130 }
1131 }
1132
1133 pub fn notify_key(&mut self, _globals: &mut GlobalState, _ids: &RuntimeConstants) -> bool {
1137 let wipe_skipped = self.wipe && self.wipe_key_skip;
1138 self.waiting_for_key = false;
1139 if self.audio.is_some() && self.audio_return_value {
1140 self.pending_value = Some(Value::Int(1));
1141 }
1142 self.audio = None;
1143 self.audio_return_value = false;
1144 if self.skip_time_on_key {
1149 anim_skip_trace("notify_key skipped TIMEWAIT_KEY pending=1");
1150 self.until = None;
1151 self.skip_time_on_key = false;
1152 self.pending_value = Some(Value::Int(1));
1153 }
1154
1155 if wipe_skipped {
1156 self.wipe = false;
1157 self.wipe_key_skip = false;
1158 }
1159
1160 wipe_skipped
1161 }
1162
1163 pub fn notify_movie_down_up(
1169 &mut self,
1170 globals: &mut GlobalState,
1171 ids: &RuntimeConstants,
1172 result: i64,
1173 ) -> bool {
1174 let mut skipped = false;
1175 if result == 1 && self.event_key_skip {
1176 if let Some(w) = self.event.take() {
1177 anim_skip_trace(format!(
1178 "notify_movie_down_up skip event result={} event={:?} return_value={}",
1179 result, w, self.event_return_value
1180 ));
1181 finish_event_wait_by_key(&w, globals, ids);
1182 if self.event_return_value {
1183 self.pending_value = Some(Value::Int(1));
1184 }
1185 skipped = true;
1186 } else {
1187 anim_skip_trace(format!(
1188 "notify_movie_down_up event_key_skip without event result={}",
1189 result
1190 ));
1191 }
1192 self.event_key_skip = false;
1193 self.event_return_value = false;
1194 }
1195 if self.global_movie && self.global_movie_key_skip {
1196 globals.mov.playing = false;
1200 if self.global_movie_return_value {
1201 self.pending_value = Some(Value::Int(result));
1202 }
1203 self.global_movie = false;
1204 self.global_movie_key_skip = false;
1205 self.global_movie_return_value = false;
1206 skipped = true;
1207 }
1208 if self.movie_key_skip && result == 1 {
1211 if let Some(w) = self.movie.take() {
1212 if w.return_value_flag {
1213 self.pending_value = Some(Value::Int(1));
1214 }
1215 self.movie_skip_info = Some(w);
1216 skipped = true;
1217 }
1218 self.movie_key_skip = false;
1219 }
1220 if skipped {
1221 self.waiting_for_key = false;
1222 }
1223 skipped
1224 }
1225
1226 pub fn take_movie_skip(&mut self) -> Option<MovieWait> {
1228 self.movie_skip_info.take()
1229 }
1230
1231 pub fn clear(&mut self) {
1232 self.until = None;
1233 self.waiting_for_key = false;
1234 self.skip_time_on_key = false;
1235 self.audio = None;
1236 self.audio_return_value = false;
1237 self.event = None;
1238 self.event_key_skip = false;
1239 self.event_return_value = false;
1240 self.movie = None;
1241 self.movie_key_skip = false;
1242 self.global_movie = false;
1243 self.global_movie_key_skip = false;
1244 self.global_movie_return_value = false;
1245 self.movie_skip_info = None;
1246 self.pending_value = None;
1247 self.system_modal = false;
1248 self.wipe = false;
1249 self.wipe_key_skip = false;
1250 }
1251}