1pub type HashMap<K, V> = std::collections::HashMap<K, V, rustc_hash::FxBuildHasher>;
5pub type HashSet<K> = std::collections::HashSet<K, rustc_hash::FxBuildHasher>;
6use std::sync::Arc;
7use std::sync::atomic::{AtomicU32, Ordering};
8
9use crate::assets::RgbaImage;
10use crate::platform_time::{Duration, Instant};
11use crate::runtime::gan::GanState;
12use crate::runtime::int_event::IntEvent;
13
14use crate::image_manager::ImageHandle;
15use crate::layer::{LayerId, SpriteId};
16
17#[derive(Debug, Clone)]
23pub struct WipeState {
24 pub stage_form_id: u32,
32 pub mask_file: Option<String>,
33 pub mask_image_id: Option<ImageHandle>,
34 pub wipe_type: i32,
35 pub wipe_time_ms: i32,
36 pub speed_mode: i32,
37 pub start_time_ms: i32,
38 pub option: Vec<i32>,
39
40 pub begin_order: i32,
41 pub end_order: i32,
42 pub begin_layer: i32,
43 pub end_layer: i32,
44
45 pub wait_flag: bool,
46 pub key_wait_mode: i32,
47 pub with_low_order: i32,
48
49 pub random_seed: u32,
51
52 step: i32,
55 cur_time_ms: i64,
56 progress_value: f32,
57 done: bool,
58}
59
60impl WipeState {
61 #[allow(clippy::too_many_arguments)]
62 pub fn new(
63 stage_form_id: u32,
64 mask_file: Option<String>,
65 mask_image_id: Option<ImageHandle>,
66 wipe_type: i32,
67 wipe_time_ms: i32,
68 start_time_ms: i32,
69 speed_mode: i32,
70 option: Vec<i32>,
71 begin_order: i32,
72 end_order: i32,
73 begin_layer: i32,
74 end_layer: i32,
75 wait_flag: bool,
76 key_wait_mode: i32,
77 with_low_order: i32,
78 ) -> Self {
79 static NEXT_WIPE_SEED: AtomicU32 = AtomicU32::new(0x6d2b_79f5);
80 let seq = NEXT_WIPE_SEED.fetch_add(0x9e37_79b9, Ordering::Relaxed);
81 let random_seed = seq
82 ^ (wipe_type as u32).rotate_left(7)
83 ^ (wipe_time_ms as u32).rotate_left(17)
84 ^ (start_time_ms as u32).rotate_left(25);
85
86 Self {
87 stage_form_id,
88 mask_file,
89 mask_image_id,
90 wipe_type,
91 wipe_time_ms,
92 speed_mode,
93 start_time_ms,
94 option,
95 begin_order,
96 end_order,
97 begin_layer,
98 end_layer,
99 wait_flag,
100 key_wait_mode,
101 with_low_order,
102 random_seed,
103 step: 0,
104 cur_time_ms: 0,
105 progress_value: 0.0,
106 done: false,
107 }
108 }
109
110 pub fn advance(&mut self, past_time_ms: i32) {
115 if self.done {
116 return;
117 }
118 self.cur_time_ms = self.cur_time_ms.saturating_add(past_time_ms.max(0) as i64);
119
120 if self.step == 0 {
121 self.step = 1;
122 return;
123 }
124 if self.step == 1 {
125 self.cur_time_ms = self.start_time_ms as i64;
126 self.step = 2;
127 }
128
129 let end = self.wipe_time_ms as f64;
130 let cur = self.cur_time_ms as f64;
131 let raw = if self.wipe_time_ms <= 0 {
132 1.0
133 } else {
134 match self.speed_mode {
135 1 => (cur * cur) / (end * end),
136 2 => 1.0 - ((cur - end) * (cur - end)) / (end * end),
137 _ => cur / end,
138 }
139 };
140 self.progress_value = raw.clamp(0.0, 1.0) as f32;
141 if self.cur_time_ms >= self.wipe_time_ms as i64 {
142 self.progress_value = 1.0;
143 self.done = true;
144 }
145 }
146
147 pub fn is_done(&self) -> bool {
148 self.done
149 }
150
151 pub fn progress(&self) -> f32 {
152 self.progress_value
153 }
154
155 #[allow(dead_code)]
156 pub fn remaining_ms(&self) -> u64 {
157 if self.done {
158 0
159 } else {
160 (self.wipe_time_ms as i64 - self.cur_time_ms).max(0) as u64
161 }
162 }
163}
164
165#[derive(Debug, Clone)]
166pub struct ScriptRuntimeState {
167 pub cur_koe_no: i64,
170 pub cur_chr_no: i64,
171 pub cur_read_flag_scn_no: i64,
172 pub cur_read_flag_flag_no: i64,
173 pub msg_back_save_cntr: i64,
174 pub multi_msg_mode: bool,
175
176 pub dont_set_save_point: bool,
177 pub skip_disable: bool,
178 pub ctrl_disable: bool,
179 pub not_stop_skip_by_click: bool,
180 pub not_skip_msg_by_click: bool,
181 pub skip_unread_message: bool,
182
183 pub auto_mode_flag: bool,
184 pub auto_mode_moji_wait: i64,
185 pub auto_mode_min_wait: i64,
186 pub auto_mode_moji_cnt: i64,
187
188 pub mouse_cursor_hide_onoff: i64,
189 pub mouse_cursor_hide_time: i64,
190
191 pub msg_speed: i64,
192 pub msg_nowait: bool,
193 pub async_msg_mode: bool,
194 pub async_msg_mode_once: bool,
195
196 pub hide_mwnd_disable: bool,
197 pub msg_back_disable: bool,
198 pub msg_back_off: bool,
199 pub msg_back_disp_off: bool,
200
201 pub cursor_disp_off: bool,
202 pub cursor_move_by_key_disable: bool,
203 pub cursor_runtime_visible: bool,
204 pub key_disable: HashSet<u8>,
205
206 pub mwnd_anime_off_flag: bool,
207 pub mwnd_anime_on_flag: bool,
208 pub mwnd_disp_off_flag: bool,
209
210 pub koe_dont_stop_on_flag: bool,
211 pub koe_dont_stop_off_flag: bool,
212
213 pub shortcut_disable: bool,
214 pub quake_stop_flag: bool,
215 pub emote_mouth_stop_flag: bool,
216 pub bgmfade_flag: bool,
217 pub wait_display_vsync_off_flag: bool,
218 pub skip_trigger: bool,
219 pub ignore_r_flag: bool,
220 pub cursor_no: i64,
221
222 pub time_stop_flag: bool,
223 pub counter_time_stop_flag: bool,
224 pub frame_action_time_stop_flag: bool,
225 pub stage_time_stop_flag: bool,
226
227 pub joypad_mode_override: i64,
230
231 pub font_name: String,
232 pub font_bold: i64,
233 pub font_shadow: i64,
234}
235
236impl Default for ScriptRuntimeState {
237 fn default() -> Self {
238 Self {
239 cur_koe_no: -1,
240 cur_chr_no: -1,
241 cur_read_flag_scn_no: -1,
242 cur_read_flag_flag_no: -1,
243 msg_back_save_cntr: 0,
244 multi_msg_mode: false,
245 dont_set_save_point: false,
246 skip_disable: false,
247 ctrl_disable: false,
248 not_stop_skip_by_click: false,
249 not_skip_msg_by_click: false,
250 skip_unread_message: false,
251 auto_mode_flag: false,
252 auto_mode_moji_wait: -1,
253 auto_mode_min_wait: -1,
254 auto_mode_moji_cnt: 0,
255 mouse_cursor_hide_onoff: -1,
256 mouse_cursor_hide_time: -1,
257 msg_speed: -1,
258 msg_nowait: false,
259 async_msg_mode: false,
260 async_msg_mode_once: false,
261 hide_mwnd_disable: false,
262 msg_back_disable: false,
263 msg_back_off: false,
264 msg_back_disp_off: false,
265 cursor_disp_off: false,
266 cursor_move_by_key_disable: false,
267 cursor_runtime_visible: true,
268 key_disable: HashSet::default(),
269 mwnd_anime_off_flag: false,
270 mwnd_anime_on_flag: false,
271 mwnd_disp_off_flag: false,
272 koe_dont_stop_on_flag: false,
273 koe_dont_stop_off_flag: false,
274 shortcut_disable: false,
275 quake_stop_flag: false,
276 emote_mouth_stop_flag: false,
277 bgmfade_flag: false,
278 wait_display_vsync_off_flag: false,
279 skip_trigger: false,
280 ignore_r_flag: false,
281 cursor_no: -1,
282 time_stop_flag: false,
283 counter_time_stop_flag: false,
284 frame_action_time_stop_flag: false,
285 stage_time_stop_flag: false,
286 joypad_mode_override: -1,
287 font_name: String::new(),
288 font_bold: -1,
289 font_shadow: -1,
290 }
291 }
292}
293
294#[derive(Debug, Clone, Default)]
295pub struct SystemMessageBoxRecord {
296 pub kind: i32,
297 pub text: String,
298 pub debug_only: bool,
299}
300
301#[derive(Debug, Clone)]
302pub struct SystemMessageBoxButton {
303 pub label: String,
304 pub value: i64,
305}
306
307#[derive(Debug, Clone)]
308pub struct SystemMessageBoxModalState {
309 pub request_id: u64,
310 pub kind: i32,
311 pub text: String,
312 pub debug_only: bool,
313 pub buttons: Vec<SystemMessageBoxButton>,
314 pub cursor: usize,
315 pub native_pending: bool,
316 pub complete_wait_with_value: bool,
317}
318
319impl SystemMessageBoxModalState {
320 pub fn selected_value(&self) -> i64 {
321 self.buttons
322 .get(self.cursor.min(self.buttons.len().saturating_sub(1)))
323 .map(|b| b.value)
324 .unwrap_or(0)
325 }
326
327 pub fn cancel_value(&self) -> i64 {
328 self.buttons
329 .last()
330 .map(|b| b.value)
331 .unwrap_or_else(|| self.selected_value())
332 }
333}
334
335#[derive(Debug, Clone)]
336pub struct SystemRuntimeState {
337 pub active_flag: bool,
338 pub debug_flag: bool,
339 pub language_code: String,
340 pub debug_logs: Vec<String>,
341 pub dummy_checks: HashSet<String>,
342 pub bench_dialogs: Vec<String>,
343 pub messagebox_history: Vec<SystemMessageBoxRecord>,
344 pub messagebox_response_queue: Vec<i64>,
345 pub messagebox_modal: Option<SystemMessageBoxModalState>,
346 pub messagebox_modal_result: Option<i64>,
347 pub chihaya_display_adapter_name: String,
348}
349
350impl Default for SystemRuntimeState {
351 fn default() -> Self {
352 Self {
353 active_flag: true,
354 debug_flag: false,
355 language_code: std::env::var("SIGLUS_LANGUAGE").unwrap_or_else(|_| "JP".to_string()),
356 debug_logs: Vec::new(),
357 dummy_checks: HashSet::default(),
358 bench_dialogs: Vec::new(),
359 messagebox_history: Vec::new(),
360 messagebox_response_queue: Vec::new(),
361 messagebox_modal: None,
362 messagebox_modal_result: None,
363 chihaya_display_adapter_name: String::new(),
364 }
365 }
366}
367
368#[derive(Debug, Clone, Copy, Default)]
369pub struct ToggleFeatureState {
370 pub onoff: bool,
371 pub enable: bool,
372 pub exist: bool,
373}
374
375impl ToggleFeatureState {
376 pub fn check_enabled(&self) -> i64 {
377 if self.enable && self.exist { 1 } else { 0 }
378 }
379}
380
381#[derive(Debug, Clone, Copy, Default)]
382pub struct ValueFeatureState {
383 pub value: i64,
384 pub enable: bool,
385 pub exist: bool,
386}
387
388impl ValueFeatureState {
389 pub fn check_enabled(&self) -> i64 {
390 if self.enable && self.exist { 1 } else { 0 }
391 }
392}
393
394#[derive(Debug, Clone, Default)]
395pub struct SaveSlotState {
396 pub header_cache_valid: bool,
400 pub exist: bool,
401 pub year: i64,
402 pub month: i64,
403 pub day: i64,
404 pub weekday: i64,
405 pub hour: i64,
406 pub minute: i64,
407 pub second: i64,
408 pub millisecond: i64,
409 pub title: String,
410 pub message: String,
411 pub full_message: String,
412 pub comment: String,
413 pub comment2: String,
416 pub packed_data_size: usize,
417 pub append_dir: String,
418 pub append_name: String,
419 pub values: HashMap<i32, i64>,
420}
421
422#[derive(Debug, Clone, Copy, PartialEq, Eq)]
423pub enum SyscomPendingProcKind {
424 EndGame,
425 ReturnToSel,
426 ReturnToMenu,
427 RestartScene,
428 Save,
429 Load,
430 QuickSave,
431 QuickLoad,
432 BacklogLoad,
433 MsgBack,
434 OpenSyscomMenu,
435 OpenSave,
436 OpenLoad,
437 OpenConfig,
438 OpenConfigDialog,
440}
441
442#[derive(Debug, Clone)]
443pub struct SyscomPendingProc {
444 pub kind: SyscomPendingProcKind,
445 pub warning: bool,
446 pub se_play: bool,
447 pub fade_out: bool,
448 pub leave_msgbk: bool,
449 pub save_id: i64,
450}
451
452#[derive(Debug, Clone, Copy, PartialEq, Eq)]
458pub enum SyscomFallbackDialogKind {
459 SystemMenu,
460 SaveMenu,
461 LoadMenu,
462 ConfigRoot,
463 ConfigWindow,
464 ConfigVolumeRoot,
465 ConfigVolume(usize),
466 ConfigMessage,
467 ConfigAuto,
468 ConfigFont,
469 ConfigOther,
470 Notice,
471}
472
473#[derive(Debug, Clone)]
474pub struct SyscomFallbackDialogState {
475 pub kind: SyscomFallbackDialogKind,
476 pub page: usize,
477 pub awaiting_result: bool,
481 pub return_kind: Option<SyscomFallbackDialogKind>,
482}
483
484#[derive(Debug, Clone, Copy, PartialEq, Eq)]
485pub struct ConfigChrKoeState {
486 pub onoff: bool,
487 pub volume: i64,
488}
489
490impl Default for ConfigChrKoeState {
491 fn default() -> Self {
492 Self {
493 onoff: true,
494 volume: 255,
495 }
496 }
497}
498
499#[derive(Debug, Clone, PartialEq, Eq)]
500pub struct OriginalConfigRuntimeState {
501 pub screen_size_mode: i64,
502 pub screen_size_mode_window: i64,
503 pub screen_size_scale: (i64, i64),
504 pub screen_size_free: (i64, i64),
505 pub fullscreen_change_resolution: bool,
506 pub fullscreen_display_cnt: i64,
507 pub fullscreen_display_no: i64,
508 pub fullscreen_resolution_cnt: i64,
509 pub fullscreen_resolution_no: i64,
510 pub fullscreen_resolution: (i64, i64),
511 pub fullscreen_mode: i64,
512 pub fullscreen_scale: (i64, i64),
513 pub fullscreen_scale_sync_switch: bool,
514 pub fullscreen_move: (i64, i64),
515 pub all_sound_user_volume: i64,
516 pub sound_user_volume: [i64; 32],
517 pub play_all_sound_check: bool,
518 pub play_sound_check: [bool; 32],
519 pub bgmfade_volume: i64,
520 pub bgmfade_use_check: bool,
521 pub filter_color_argb: u32,
522 pub font_proportional: bool,
523 pub font_name: String,
524 pub font_shadow: i64,
525 pub font_futoku: bool,
526 pub message_speed: i64,
527 pub message_speed_nowait: bool,
528 pub auto_mode_onoff: bool,
529 pub auto_mode_moji_wait: i64,
530 pub auto_mode_min_wait: i64,
531 pub mouse_cursor_hide_onoff: bool,
532 pub mouse_cursor_hide_time: i64,
533 pub jitan_normal_onoff: bool,
534 pub jitan_auto_mode_onoff: bool,
535 pub jitan_msgbk_onoff: bool,
536 pub jitan_speed: i64,
537 pub koe_mode: i64,
538 pub chrkoe: Vec<ConfigChrKoeState>,
539 pub message_chrcolor_flag: bool,
540 pub object_disp_flag: Vec<bool>,
541 pub global_extra_switch_flag: Vec<bool>,
542 pub global_extra_mode_flag: Vec<i64>,
543 pub sleep_flag: bool,
544 pub no_wipe_anime_flag: bool,
545 pub skip_wipe_anime_flag: bool,
546 pub no_mwnd_anime_flag: bool,
547 pub wheel_next_message_flag: bool,
548 pub koe_dont_stop_flag: bool,
549 pub skip_unread_message_flag: bool,
550 pub saveload_alert_flag: bool,
551 pub saveload_dblclick_flag: bool,
552 pub ss_path: String,
553 pub editor_path: String,
554 pub koe_path: String,
555 pub koe_tool_path: String,
556 pub joypad_swap_ab: bool,
558 pub joypad_swap_xy: bool,
559}
560
561impl Default for OriginalConfigRuntimeState {
562 fn default() -> Self {
563 Self {
564 screen_size_mode: 0,
565 screen_size_mode_window: 0,
566 screen_size_scale: (100, 100),
567 screen_size_free: (0, 0),
568 fullscreen_change_resolution: false,
569 fullscreen_display_cnt: 0,
570 fullscreen_display_no: 0,
571 fullscreen_resolution_cnt: 0,
572 fullscreen_resolution_no: 0,
573 fullscreen_resolution: (0, 0),
574 fullscreen_mode: 0,
575 fullscreen_scale: (100, 100),
576 fullscreen_scale_sync_switch: true,
577 fullscreen_move: (0, 0),
578 all_sound_user_volume: 255,
579 sound_user_volume: [255; 32],
580 play_all_sound_check: true,
581 play_sound_check: [true; 32],
582 bgmfade_volume: 192,
583 bgmfade_use_check: true,
584 filter_color_argb: 0x8000_0000,
585 font_proportional: false,
586 font_name: "MS ゴシック".to_string(),
587 font_shadow: 2,
588 font_futoku: false,
589 message_speed: 20,
590 message_speed_nowait: false,
591 auto_mode_onoff: false,
592 auto_mode_moji_wait: 70,
593 auto_mode_min_wait: 300,
594 mouse_cursor_hide_onoff: false,
595 mouse_cursor_hide_time: 5000,
596 jitan_normal_onoff: false,
597 jitan_auto_mode_onoff: false,
598 jitan_msgbk_onoff: false,
599 jitan_speed: 100,
600 koe_mode: 0,
601 chrkoe: vec![ConfigChrKoeState::default(); 64],
602 message_chrcolor_flag: true,
603 object_disp_flag: vec![true; 4],
604 global_extra_switch_flag: vec![true; 4],
605 global_extra_mode_flag: vec![0; 4],
606 sleep_flag: false,
607 no_wipe_anime_flag: false,
608 skip_wipe_anime_flag: true,
609 no_mwnd_anime_flag: false,
610 wheel_next_message_flag: true,
611 koe_dont_stop_flag: false,
612 skip_unread_message_flag: false,
613 saveload_alert_flag: true,
614 saveload_dblclick_flag: false,
615 ss_path: String::new(),
616 editor_path: String::new(),
617 koe_path: String::new(),
618 koe_tool_path: String::new(),
619 joypad_swap_ab: false,
620 joypad_swap_xy: false,
621 }
622 }
623}
624
625#[derive(Debug, Clone)]
626pub struct SyscomRuntimeState {
627 pub syscom_menu_disable: bool,
628 pub menu_open: bool,
629 pub menu_kind: Option<i32>,
630 pub menu_result: Option<i64>,
631 pub menu_cursor: usize,
632 pub font_list: Vec<String>,
633 pub mwnd_btn_disable_all: bool,
634 pub mwnd_btn_touch_disable: bool,
635 pub mwnd_btn_disable: HashMap<i64, bool>,
636 pub read_skip: ToggleFeatureState,
637 pub unread_skip: ToggleFeatureState,
638 pub auto_skip: ToggleFeatureState,
639 pub auto_mode: ToggleFeatureState,
640 pub hide_mwnd: ToggleFeatureState,
641 pub local_extra_switch: ToggleFeatureState,
642 pub local_extra_mode: ValueFeatureState,
643 pub local_extra_switches: [ToggleFeatureState; 4],
644 pub local_extra_modes: [ValueFeatureState; 4],
645 pub msg_back: ToggleFeatureState,
646 pub msg_back_open: bool,
647 pub msg_back_view_pos: usize,
648 pub msg_back_scroll_pos: i32,
649 pub msg_back_slider_pos: i32,
650 pub msg_back_target_no: isize,
651 pub msg_back_mouse_target_no: isize,
652 pub msg_back_msg_total_height: i32,
653 pub msg_back_proc_initialized: bool,
654 pub msg_back_slider_dragging: bool,
655 pub msg_back_slider_drag_start_mouse: i32,
656 pub msg_back_slider_drag_start_pos: i32,
657 pub msg_back_content_dragging: bool,
658 pub msg_back_content_drag_start_mouse: i32,
659 pub msg_back_content_drag_start_scroll_pos: i32,
660 pub return_to_sel: ToggleFeatureState,
661 pub config_feature: ToggleFeatureState,
662 pub manual_feature: ToggleFeatureState,
663 pub version_feature: ToggleFeatureState,
664 pub return_to_menu: ToggleFeatureState,
665 pub end_game: ToggleFeatureState,
666 pub cancel_feature: ToggleFeatureState,
667 pub save_feature: ToggleFeatureState,
668 pub load_feature: ToggleFeatureState,
669 pub replay_koe: Option<(i64, i64)>,
670 pub current_save_scene_title: String,
671 pub current_save_message: String,
672 pub current_save_full_message: String,
673 pub total_play_time: i64,
674 pub save_slots: Vec<SaveSlotState>,
675 pub quick_save_slots: Vec<SaveSlotState>,
676 pub inner_save_exists: bool,
677 pub inner_save_streams: Vec<Vec<u8>>,
678 pub sel_save_stock_stream: Vec<u8>,
679 pub sel_save_ids: Vec<[u16; 7]>,
680 pub end_save_exists: bool,
681 pub last_menu_call: i32,
682 pub system_extra_int_value: i64,
683 pub system_extra_str_value: String,
684 pub config_int: HashMap<i32, i64>,
685 pub config_str: HashMap<i32, String>,
686 pub original_config: OriginalConfigRuntimeState,
687 pub chrkoe_look_flags: HashMap<String, bool>,
688 pub capture_buffer: Option<RgbaImage>,
689 pub capture_size: Option<(u32, u32)>,
690 pub return_scene_once: Option<(String, i64)>,
691 pub pending_proc: Option<SyscomPendingProc>,
692 pub msg_back_load_tid: i64,
693 pub fallback_dialog: Option<SyscomFallbackDialogState>,
694 pub fallback_origin: Option<SyscomFallbackDialogKind>,
695}
696
697impl Default for SyscomRuntimeState {
698 fn default() -> Self {
699 Self {
700 syscom_menu_disable: false,
701 menu_open: false,
702 menu_kind: None,
703 menu_result: None,
704 menu_cursor: 0,
705 font_list: Vec::new(),
706 mwnd_btn_disable_all: false,
707 mwnd_btn_touch_disable: false,
708 mwnd_btn_disable: HashMap::default(),
709 read_skip: ToggleFeatureState {
710 onoff: false,
711 enable: true,
712 exist: true,
713 },
714 unread_skip: ToggleFeatureState {
715 onoff: false,
716 enable: true,
717 exist: true,
718 },
719 auto_skip: ToggleFeatureState {
720 onoff: false,
721 enable: true,
722 exist: true,
723 },
724 auto_mode: ToggleFeatureState {
725 onoff: false,
726 enable: true,
727 exist: true,
728 },
729 hide_mwnd: ToggleFeatureState {
730 onoff: false,
731 enable: true,
732 exist: true,
733 },
734 local_extra_switch: ToggleFeatureState {
735 onoff: false,
736 enable: true,
737 exist: true,
738 },
739 local_extra_mode: ValueFeatureState {
740 value: 0,
741 enable: true,
742 exist: true,
743 },
744 local_extra_switches: [ToggleFeatureState {
745 onoff: false,
746 enable: true,
747 exist: true,
748 }; 4],
749 local_extra_modes: [ValueFeatureState {
750 value: 0,
751 enable: true,
752 exist: true,
753 }; 4],
754 msg_back: ToggleFeatureState {
755 onoff: false,
756 enable: true,
757 exist: true,
758 },
759 msg_back_open: false,
760 msg_back_view_pos: 0,
761 msg_back_scroll_pos: 0,
762 msg_back_slider_pos: 0,
763 msg_back_target_no: -1,
764 msg_back_mouse_target_no: -1,
765 msg_back_msg_total_height: 0,
766 msg_back_proc_initialized: false,
767 msg_back_slider_dragging: false,
768 msg_back_slider_drag_start_mouse: 0,
769 msg_back_slider_drag_start_pos: 0,
770 msg_back_content_dragging: false,
771 msg_back_content_drag_start_mouse: 0,
772 msg_back_content_drag_start_scroll_pos: 0,
773 return_to_sel: ToggleFeatureState {
774 onoff: false,
775 enable: true,
776 exist: true,
777 },
778 config_feature: ToggleFeatureState {
779 onoff: false,
780 enable: true,
781 exist: true,
782 },
783 manual_feature: ToggleFeatureState {
784 onoff: false,
785 enable: true,
786 exist: true,
787 },
788 version_feature: ToggleFeatureState {
789 onoff: false,
790 enable: true,
791 exist: true,
792 },
793 return_to_menu: ToggleFeatureState {
794 onoff: false,
795 enable: true,
796 exist: true,
797 },
798 end_game: ToggleFeatureState {
799 onoff: false,
800 enable: true,
801 exist: true,
802 },
803 cancel_feature: ToggleFeatureState {
804 onoff: false,
805 enable: true,
806 exist: true,
807 },
808 save_feature: ToggleFeatureState {
809 onoff: false,
810 enable: true,
811 exist: true,
812 },
813 load_feature: ToggleFeatureState {
814 onoff: false,
815 enable: true,
816 exist: true,
817 },
818 replay_koe: None,
819 current_save_scene_title: String::new(),
820 current_save_message: String::new(),
821 current_save_full_message: String::new(),
822 total_play_time: 0,
823 save_slots: Vec::new(),
824 quick_save_slots: Vec::new(),
825 inner_save_exists: false,
826 inner_save_streams: Vec::new(),
827 sel_save_stock_stream: Vec::new(),
828 sel_save_ids: Vec::new(),
829 end_save_exists: false,
830 last_menu_call: 0,
831 system_extra_int_value: 0,
832 system_extra_str_value: String::new(),
833 config_int: HashMap::default(),
834 config_str: HashMap::default(),
835 original_config: OriginalConfigRuntimeState::default(),
836 chrkoe_look_flags: HashMap::default(),
837 capture_buffer: None,
838 capture_size: None,
839 return_scene_once: None,
840 pending_proc: None,
841 msg_back_load_tid: 0,
842 fallback_dialog: None,
843 fallback_origin: None,
844 }
845 }
846}
847#[derive(Debug, Clone, Copy, PartialEq, Eq)]
852pub enum LightType {
853 None = -1,
854 Directional = 0,
855 Point = 1,
856 Spot = 2,
857 ShadowMapSpot = 3,
858}
859
860pub const WORLD_LIGHT_MAX: usize = 128;
861pub const OBJ_DIRECTIONAL_LIGHT_MAX: usize = 4;
862pub const OBJ_POINT_LIGHT_MAX: usize = 4;
863pub const OBJ_SPOT_LIGHT_MAX: usize = 4;
864
865#[derive(Debug, Clone)]
866pub struct LightState {
867 pub id: i32,
868 pub kind: LightType,
869 pub diffuse: [f32; 4],
870 pub ambient: [f32; 4],
871 pub specular: [f32; 4],
872 pub pos: [f32; 3],
873 pub dir: [f32; 3],
874 pub attenuation0: f32,
875 pub attenuation1: f32,
876 pub attenuation2: f32,
877 pub range: f32,
878 pub theta_deg: f32,
879 pub phi_deg: f32,
880 pub falloff: f32,
881}
882
883impl LightState {
884 pub fn directional(id: i32, dir: [f32; 3]) -> Self {
885 Self {
886 id,
887 kind: LightType::Directional,
888 diffuse: [1.0, 1.0, 1.0, 1.0],
889 ambient: [0.18, 0.18, 0.18, 1.0],
890 specular: [0.0, 0.0, 0.0, 1.0],
891 pos: [0.0, 0.0, 0.0],
892 dir,
893 attenuation0: 1.0,
894 attenuation1: 0.0,
895 attenuation2: 0.0,
896 range: 5000.0,
897 theta_deg: 20.0,
898 phi_deg: 40.0,
899 falloff: 1.0,
900 }
901 }
902}
903
904impl Default for LightState {
905 fn default() -> Self {
906 Self::directional(0, [0.0, 0.0, -1.0])
907 }
908}
909
910#[derive(Debug, Clone)]
911pub struct FogGlobalState {
912 pub enabled: bool,
913 pub name: String,
914 pub near: f32,
915 pub far: f32,
916 pub color: [f32; 4],
917 pub scroll_x: f32,
918 pub x_event: IntEvent,
919 pub texture_image_id: Option<ImageHandle>,
920}
921
922impl Default for FogGlobalState {
923 fn default() -> Self {
924 Self {
925 enabled: false,
926 name: String::new(),
927 near: 0.0,
928 far: 0.0,
929 color: [0.62, 0.62, 0.62, 1.0],
930 scroll_x: 0.0,
931 x_event: IntEvent::new(0),
932 texture_image_id: None,
933 }
934 }
935}
936
937impl FogGlobalState {
938 pub fn set_x(&mut self, x: i32) {
939 self.x_event.set_value(x);
940 self.x_event.frame();
941 self.scroll_x = self.x_event.get_total_value() as f32;
942 }
943
944 pub fn update_time(&mut self, past_game_time: i32, past_real_time: i32) {
945 self.x_event.update_time(past_game_time, past_real_time);
946 }
947
948 pub fn frame(&mut self) {
949 self.x_event.frame();
950 self.scroll_x = self.x_event.get_total_value() as f32;
951 }
952}
953
954#[derive(Debug, Clone)]
960pub struct PcmChPersistentState {
961 pub pcm_name: String,
962 pub bgm_name: String,
963 pub koe_no: i64,
964 pub se_no: i64,
965 pub volume_type: i64,
966 pub chara_no: i64,
967 pub volume: i64,
968 pub delay_time: i64,
969 pub fade_in_time: i64,
970 pub loop_flag: bool,
971 pub bgm_fade_target_flag: bool,
972 pub bgm_fade2_target_flag: bool,
973 pub bgm_fade_source_flag: bool,
974 pub ready_flag: bool,
975}
976
977impl Default for PcmChPersistentState {
978 fn default() -> Self {
979 Self {
980 pcm_name: String::new(),
981 bgm_name: String::new(),
982 koe_no: -1,
983 se_no: -1,
984 volume_type: 2, chara_no: -1,
986 volume: 255,
987 delay_time: 0,
988 fade_in_time: 0,
989 loop_flag: false,
990 bgm_fade_target_flag: false,
991 bgm_fade2_target_flag: false,
992 bgm_fade_source_flag: false,
993 ready_flag: false,
994 }
995 }
996}
997
998#[derive(Debug, Clone)]
999pub struct SoundRoutingState {
1000 pub koe_chara_no: i64,
1004 pub koe_ex_flag: bool,
1005 pub bgmfade2_need_flag: bool,
1008 pub bgmfade_flag: bool,
1009 pub bgmfade_cur_time: i64,
1010 pub bgmfade_start_value: i64,
1011 pub bgmfade_total_volume: i64,
1012 pub bgmfade2_flag: bool,
1013 pub bgmfade2_cur_time: i64,
1014 pub bgmfade2_start_value: i64,
1015 pub bgmfade2_total_volume: i64,
1016 pub chrkoe_chara_lists_cache: Arc<Vec<Vec<i64>>>,
1020}
1021
1022impl Default for SoundRoutingState {
1023 fn default() -> Self {
1024 Self {
1025 koe_chara_no: -1,
1026 koe_ex_flag: false,
1027 bgmfade2_need_flag: false,
1028 bgmfade_flag: false,
1029 bgmfade_cur_time: 0,
1030 bgmfade_start_value: 255,
1031 bgmfade_total_volume: 255,
1032 bgmfade2_flag: false,
1033 bgmfade2_cur_time: 0,
1034 bgmfade2_start_value: 255,
1035 bgmfade2_total_volume: 255,
1036 chrkoe_chara_lists_cache: Arc::new(Vec::new()),
1037 }
1038 }
1039}
1040
1041#[derive(Debug, Clone)]
1042pub struct GlobalState {
1043 pub int_lists: HashMap<u32, Vec<i64>>,
1045 pub str_lists: HashMap<u32, Vec<String>>,
1047 pub counter_lists: HashMap<u32, Vec<Counter>>,
1049 pub pcm_event_lists: HashMap<u32, Vec<PcmEventState>>,
1051 pub pcmch_persistent: Vec<PcmChPersistentState>,
1053 pub sound_routing: SoundRoutingState,
1055 pub read_flags: HashMap<i64, Vec<u8>>,
1058
1059 pub int_event_roots: HashMap<u32, IntEvent>,
1061 pub int_event_lists: HashMap<u32, Vec<IntEvent>>,
1063
1064 pub int_props: HashMap<u32, HashMap<i32, i64>>,
1066 pub str_props: HashMap<u32, HashMap<i32, String>>,
1068
1069 pub cg_table_off: bool,
1071
1072 pub database_off: bool,
1074
1075 pub g00buf: Vec<Option<ImageHandle>>,
1077 pub g00buf_names: Vec<Option<String>>,
1079
1080 pub rng_state: u32,
1082
1083 pub mask_lists: HashMap<u32, MaskListState>,
1085 pub editbox_lists: HashMap<u32, EditBoxListState>,
1087 pub focused_editbox: Option<(u32, usize)>,
1089 pub editbox_clipboard: String,
1091 pub change_display_mode_proc_cnt: i32,
1093
1094 pub frame_actions: HashMap<u32, ObjectFrameActionState>,
1096 pub frame_action_lists: HashMap<u32, Vec<ObjectFrameActionState>>,
1098 pub pending_frame_action_finishes: Vec<PendingFrameActionFinish>,
1100 pub pending_button_actions: Vec<PendingButtonAction>,
1102
1103 pub stage_forms: HashMap<u32, Box<StageFormState>>,
1107 pub focused_stage_group: Option<(u32, i64, usize)>,
1109 pub focused_stage_mwnd: Option<(u32, i64, usize)>,
1111 pub current_mwnd_element: Vec<i32>,
1115 pub current_sel_mwnd_element: Vec<i32>,
1116 pub last_mwnd_element: Vec<i32>,
1117 pub current_mwnd_no: Option<usize>,
1118 pub current_mwnd_stage_idx: i64,
1119 pub current_sel_mwnd_no: Option<usize>,
1120 pub current_sel_mwnd_stage_idx: i64,
1121 pub last_mwnd_no: Option<usize>,
1122 pub last_mwnd_stage_idx: i64,
1123
1124 pub local_real_time: i64,
1126 pub local_game_time: i64,
1127 pub local_wipe_time: i64,
1128
1129 pub local_flag_h: Vec<i64>,
1131 pub local_flag_i: Vec<i64>,
1132 pub local_flag_j: Vec<i64>,
1133 pub selbtn: BtnSelectRuntimeState,
1135 pub current_stage_object: Option<(i64, usize)>,
1138 pub current_object_chain: Option<Vec<i32>>,
1139
1140 pub screen_forms: HashMap<u32, ScreenFormState>,
1142
1143 pub msgbk_forms: HashMap<u32, MsgBackState>,
1145
1146 pub script: ScriptRuntimeState,
1148
1149 pub system: SystemRuntimeState,
1151
1152 pub syscom: SyscomRuntimeState,
1154 pub mov: GlobalMovieState,
1156
1157 pub capture_image: Option<RgbaImage>,
1161 pub capture_for_tweet_pending: bool,
1164 #[cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))]
1165 pub twitter: crate::runtime::twitter::TwitterState,
1166 #[cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))]
1167 pub twitter_dialog_request: Option<crate::runtime::twitter::TwitterDialogRequest>,
1168 pub capture_for_object_image: Option<RgbaImage>,
1170 pub save_thumb_capture_image: Option<RgbaImage>,
1172 pub save_thumb_capture_prior: i32,
1174
1175 pub append_dir: String,
1177 pub append_name: String,
1179
1180 pub bgm_table_listened: HashMap<String, bool>,
1182 pub bgm_table_flags: Vec<bool>,
1184 pub bgm_table_all_flag: bool,
1186
1187 pub wipe: Option<WipeState>,
1189
1190 pub lights: HashMap<i32, LightState>,
1192 pub fog_global: FogGlobalState,
1194
1195 pub render_frame: u64,
1197}
1198
1199impl Default for GlobalState {
1200 fn default() -> Self {
1201 Self {
1202 int_lists: HashMap::default(),
1203 str_lists: HashMap::default(),
1204 counter_lists: HashMap::default(),
1205 pcm_event_lists: HashMap::default(),
1206 pcmch_persistent: Vec::new(),
1207 sound_routing: SoundRoutingState::default(),
1208 read_flags: HashMap::default(),
1209 int_event_roots: HashMap::default(),
1210 int_event_lists: HashMap::default(),
1211 int_props: HashMap::default(),
1212 str_props: HashMap::default(),
1213 cg_table_off: false,
1214 database_off: false,
1215 g00buf: Vec::new(),
1216 g00buf_names: Vec::new(),
1217 rng_state: 0,
1218 mask_lists: HashMap::default(),
1219 editbox_lists: HashMap::default(),
1220 focused_editbox: None,
1221 editbox_clipboard: String::new(),
1222 change_display_mode_proc_cnt: 0,
1223
1224 frame_actions: HashMap::default(),
1225 frame_action_lists: HashMap::default(),
1226 pending_frame_action_finishes: Vec::new(),
1227 pending_button_actions: Vec::new(),
1228 stage_forms: HashMap::default(),
1229 focused_stage_group: None,
1230 focused_stage_mwnd: None,
1231 current_mwnd_element: vec![
1232 crate::runtime::forms::codes::ELM_GLOBAL_FRONT,
1233 crate::runtime::forms::codes::ELM_STAGE_MWND,
1234 crate::runtime::forms::codes::ELM_ARRAY,
1235 0,
1236 ],
1237 current_sel_mwnd_element: vec![
1238 crate::runtime::forms::codes::ELM_GLOBAL_FRONT,
1239 crate::runtime::forms::codes::ELM_STAGE_MWND,
1240 crate::runtime::forms::codes::ELM_ARRAY,
1241 1,
1242 ],
1243 last_mwnd_element: Vec::new(),
1244 current_mwnd_no: Some(0),
1245 current_mwnd_stage_idx: 1,
1246 current_sel_mwnd_no: Some(1),
1247 current_sel_mwnd_stage_idx: 1,
1248 last_mwnd_no: None,
1249 last_mwnd_stage_idx: 1,
1250 local_real_time: 0,
1251 local_game_time: 0,
1252 local_wipe_time: 0,
1253 local_flag_h: Vec::new(),
1254 local_flag_i: Vec::new(),
1255 local_flag_j: Vec::new(),
1256 selbtn: BtnSelectRuntimeState::default(),
1257 current_stage_object: None,
1258 current_object_chain: None,
1259
1260 screen_forms: HashMap::default(),
1261 msgbk_forms: HashMap::default(),
1262
1263 script: ScriptRuntimeState::default(),
1264 system: SystemRuntimeState::default(),
1265 syscom: SyscomRuntimeState::default(),
1266 mov: GlobalMovieState::default(),
1267 capture_image: None,
1268 capture_for_tweet_pending: false,
1269 #[cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))]
1270 twitter: crate::runtime::twitter::TwitterState::default(),
1271 #[cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))]
1272 twitter_dialog_request: None,
1273 capture_for_object_image: None,
1274 save_thumb_capture_image: None,
1275 save_thumb_capture_prior: 0,
1276 append_dir: String::new(),
1277 append_name: String::new(),
1278
1279 bgm_table_listened: HashMap::default(),
1280 bgm_table_flags: Vec::new(),
1281 bgm_table_all_flag: false,
1282
1283 wipe: None,
1284 lights: HashMap::default(),
1285 fog_global: FogGlobalState::default(),
1286 render_frame: 0,
1287 }
1288 }
1289}
1290
1291#[derive(Debug, Clone)]
1292pub struct BtnSelectChoiceState {
1293 pub template_no: i64,
1296 pub base_file: String,
1297 pub filter_file: String,
1298 pub text: String,
1299 pub item_type: i64,
1300 pub color: i64,
1301 pub pos: (i64, i64),
1304 pub size: (i64, i64),
1305 pub glyphs: Vec<MwndGlyphState>,
1306}
1307
1308impl Default for BtnSelectChoiceState {
1309 fn default() -> Self {
1310 Self {
1311 template_no: -1,
1312 base_file: String::new(),
1313 filter_file: String::new(),
1314 text: String::new(),
1315 item_type: 1,
1316 color: -1,
1317 pos: (0, 0),
1318 size: (0, 0),
1319 glyphs: Vec::new(),
1320 }
1321 }
1322}
1323
1324#[derive(Debug, Clone)]
1325pub struct BtnSelectRuntimeState {
1326 pub template_no: i64,
1327 pub saved_cur_param: Option<[i64; 28]>,
1331 pub choices: Vec<BtnSelectChoiceState>,
1332 pub cursor: usize,
1333 pub pressed_index: Option<usize>,
1337 pub pressed_inside: bool,
1338 pub cancel_enable: bool,
1339 pub capture_flag: bool,
1340 pub started: bool,
1342 pub result: i64,
1343 pub sync_type: i64,
1344 pub read_flag_scene_no: i64,
1345 pub read_flag_flag_no: i64,
1346 pub sel_start_call_scn: String,
1347 pub sel_start_call_z_no: i64,
1348
1349 pub appear_flag: bool,
1353 pub open_anime_type: i64,
1354 pub open_anime_time: i64,
1355 pub open_anime_cur_time: i64,
1356 pub close_anime_type: i64,
1357 pub close_anime_time: i64,
1358 pub close_anime_cur_time: i64,
1359 pub decide_anime_type: i64,
1360 pub decide_anime_time: i64,
1361 pub decide_anime_cur_time: i64,
1362 pub decide_sel_no: i64,
1363 pub processing_flag_0: bool,
1364 pub processing_flag_1: bool,
1365 pub processing_flag_2: bool,
1366 pub capture_now_flag: bool,
1367 pub result_delivered: bool,
1368}
1369
1370impl Default for BtnSelectRuntimeState {
1371 fn default() -> Self {
1372 Self {
1373 template_no: -1,
1374 saved_cur_param: None,
1375 choices: Vec::new(),
1376 cursor: 0,
1377 pressed_index: None,
1378 pressed_inside: false,
1379 cancel_enable: false,
1380 capture_flag: false,
1381 started: false,
1382 result: 0,
1383 sync_type: 0,
1384 read_flag_scene_no: -1,
1385 read_flag_flag_no: -1,
1386 sel_start_call_scn: String::new(),
1387 sel_start_call_z_no: -1,
1388 appear_flag: false,
1389 open_anime_type: 0,
1390 open_anime_time: 0,
1391 open_anime_cur_time: 0,
1392 close_anime_type: 0,
1393 close_anime_time: 0,
1394 close_anime_cur_time: 0,
1395 decide_anime_type: 0,
1396 decide_anime_time: 0,
1397 decide_anime_cur_time: 0,
1398 decide_sel_no: -1,
1399 processing_flag_0: false,
1400 processing_flag_1: false,
1401 processing_flag_2: false,
1402 capture_now_flag: false,
1403 result_delivered: false,
1404 }
1405 }
1406}
1407
1408#[derive(Debug, Clone, Default)]
1409pub struct PendingFrameActionFinish {
1410 pub frame_action_chain: Vec<i32>,
1411 pub object_chain: Option<Vec<i32>>,
1412 pub snapshot: ObjectFrameActionState,
1416 pub reinit_after_finish: bool,
1420 pub scn_name: String,
1421 pub cmd_name: String,
1422 pub end_time: i64,
1423 pub args: Vec<crate::runtime::Value>,
1424}
1425
1426#[derive(Debug, Clone)]
1427pub enum PendingButtonActionKind {
1428 UserCall {
1429 scn_name: String,
1430 cmd_name: String,
1431 z_no: i64,
1432 },
1433 Syscom {
1434 sys_type: i64,
1435 sys_type_opt: i64,
1436 mode: i64,
1437 },
1438}
1439
1440impl Default for PendingButtonActionKind {
1441 fn default() -> Self {
1442 Self::UserCall {
1443 scn_name: String::new(),
1444 cmd_name: String::new(),
1445 z_no: -1,
1446 }
1447 }
1448}
1449
1450#[derive(Debug, Clone, Default)]
1451pub struct PendingButtonAction {
1452 pub kind: PendingButtonActionKind,
1453}
1454
1455#[derive(Debug, Clone, Default)]
1461pub struct GlobalMovieState {
1462 pub file_name: Option<String>,
1463 pub playing: bool,
1464 pub key_skip_flag: bool,
1465 pub timer_ms: u64,
1466 pub total_ms: Option<u64>,
1467 pub x: i32,
1468 pub y: i32,
1469 pub width: u32,
1470 pub height: u32,
1471 pub layer_id: Option<LayerId>,
1472 pub sprite_id: Option<SpriteId>,
1473 pub image_id: Option<ImageHandle>,
1474 pub last_frame_idx: Option<usize>,
1475 pub audio_id: Option<u64>,
1476 pub audio_start_attempted: bool,
1477}
1478
1479impl GlobalMovieState {
1480 pub fn start(
1481 &mut self,
1482 file_name: String,
1483 x: i32,
1484 y: i32,
1485 width: u32,
1486 height: u32,
1487 total_ms: Option<u64>,
1488 key_skip_flag: bool,
1489 ) {
1490 self.file_name = Some(file_name);
1491 self.playing = true;
1492 self.key_skip_flag = key_skip_flag;
1493 self.timer_ms = 0;
1494 self.total_ms = total_ms;
1495 self.x = x;
1496 self.y = y;
1497 self.width = width;
1498 self.height = height;
1499 self.last_frame_idx = None;
1500 self.audio_id = None;
1501 self.audio_start_attempted = false;
1502 }
1503
1504 pub fn stop(&mut self) {
1505 self.file_name = None;
1506 self.playing = false;
1507 self.key_skip_flag = false;
1508 self.timer_ms = 0;
1509 self.total_ms = None;
1510 self.last_frame_idx = None;
1511 self.audio_id = None;
1512 self.audio_start_attempted = false;
1513 }
1514
1515 pub fn tick(&mut self, past_real_time: i32) {
1516 if !self.playing || self.audio_id.is_some() {
1517 return;
1520 }
1521 let add = past_real_time.max(0) as u64;
1522 if add == 0 {
1523 return;
1524 }
1525 self.timer_ms = self.timer_ms.saturating_add(add);
1526 if let Some(total) = self.total_ms
1527 && total > 0
1528 && self.timer_ms >= total
1529 {
1530 self.timer_ms = total;
1531 self.playing = false;
1532 }
1533 }
1534}
1535
1536#[derive(Debug, Clone, Copy, Default)]
1537pub struct Counter {
1538 cur_time: i64,
1539 is_running: bool,
1540 real_flag: bool,
1541 frame_mode: bool,
1542 frame_loop_flag: bool,
1543 frame_start_value: i64,
1544 frame_end_value: i64,
1545 frame_time: i64,
1546}
1547
1548impl Counter {
1549 fn limit(min: i64, value: i64, max: i64) -> i64 {
1550 if value < min {
1551 min
1552 } else if value > max {
1553 max
1554 } else {
1555 value
1556 }
1557 }
1558
1559 pub fn reinit(&mut self) {
1560 *self = Self::default();
1561 }
1562
1563 pub fn reset(&mut self) {
1564 self.is_running = false;
1565 self.real_flag = false;
1566 self.frame_mode = false;
1567 self.cur_time = 0;
1568 }
1569
1570 pub fn set_count(&mut self, value: i64) {
1571 if self.frame_mode {
1572 if self.frame_end_value == self.frame_start_value {
1573 self.cur_time = 0;
1574 return;
1575 }
1576
1577 let denom = self.frame_end_value - self.frame_start_value;
1578 let frame_time = self.frame_time;
1579 let mut cur_time = (value - self.frame_start_value) * frame_time / denom;
1580 if self.frame_loop_flag {
1581 cur_time = Self::limit(0, cur_time, frame_time - 1);
1582 } else {
1583 cur_time = Self::limit(0, cur_time, frame_time);
1584 }
1585 self.cur_time = cur_time;
1586 } else {
1587 self.cur_time = value;
1588 }
1589 }
1590
1591 pub fn start(&mut self) {
1592 self.is_running = true;
1593 self.real_flag = false;
1594 self.frame_mode = false;
1595 self.cur_time = 0;
1596 }
1597
1598 pub fn start_real(&mut self) {
1599 self.is_running = true;
1600 self.real_flag = true;
1601 self.frame_mode = false;
1602 self.cur_time = 0;
1603 }
1604
1605 pub fn start_frame(&mut self, from: i64, to: i64, frame_time: i64) {
1606 self.is_running = true;
1607 self.real_flag = false;
1608 self.frame_mode = true;
1609 self.frame_loop_flag = false;
1610 self.frame_start_value = from;
1611 self.frame_end_value = to;
1612 self.frame_time = frame_time;
1613 self.cur_time = 0;
1614 }
1615
1616 pub fn start_frame_real(&mut self, from: i64, to: i64, frame_time: i64) {
1617 self.is_running = true;
1618 self.real_flag = true;
1619 self.frame_mode = true;
1620 self.frame_loop_flag = false;
1621 self.frame_start_value = from;
1622 self.frame_end_value = to;
1623 self.frame_time = frame_time;
1624 self.cur_time = 0;
1625 }
1626
1627 pub fn start_frame_loop(&mut self, from: i64, to: i64, frame_time: i64) {
1628 self.is_running = true;
1629 self.real_flag = false;
1630 self.frame_mode = true;
1631 self.frame_loop_flag = true;
1632 self.frame_start_value = from;
1633 self.frame_end_value = to;
1634 self.frame_time = frame_time;
1635 self.cur_time = 0;
1636 }
1637
1638 pub fn start_frame_loop_real(&mut self, from: i64, to: i64, frame_time: i64) {
1639 self.is_running = true;
1640 self.real_flag = true;
1641 self.frame_mode = true;
1642 self.frame_loop_flag = true;
1643 self.frame_start_value = from;
1644 self.frame_end_value = to;
1645 self.frame_time = frame_time;
1646 self.cur_time = 0;
1647 }
1648
1649 pub fn stop(&mut self) {
1650 self.is_running = false;
1651 }
1652
1653 pub fn resume(&mut self) {
1654 self.is_running = true;
1655 }
1656
1657 pub fn update_time(&mut self, past_game_time: i32, past_real_time: i32) {
1658 if self.is_running {
1659 let add = if self.real_flag {
1660 past_real_time
1661 } else {
1662 past_game_time
1663 };
1664 self.cur_time = self.cur_time.saturating_add(add as i64);
1665 }
1666
1667 if self.frame_mode && !self.frame_loop_flag && self.cur_time >= self.frame_time {
1668 self.is_running = false;
1669 }
1670 }
1671
1672 pub fn get_count(&self) -> i64 {
1673 if self.frame_mode {
1674 if self.frame_time <= 0 {
1675 return self.frame_end_value;
1676 }
1677 if self.frame_start_value == self.frame_end_value {
1678 return self.frame_end_value;
1679 }
1680
1681 let span = self.frame_end_value - self.frame_start_value;
1682 let mut value = span * self.cur_time / self.frame_time;
1683 if self.frame_loop_flag {
1684 value %= span;
1685 value += self.frame_start_value;
1686 } else {
1687 value += self.frame_start_value;
1688 if self.frame_start_value > self.frame_end_value {
1689 value = Self::limit(self.frame_end_value, value, self.frame_start_value);
1690 } else {
1691 value = Self::limit(self.frame_start_value, value, self.frame_end_value);
1692 }
1693 }
1694 value
1695 } else {
1696 self.cur_time
1697 }
1698 }
1699
1700 pub fn get_count_with_frame(&self, _current_frame: i64) -> i64 {
1701 self.get_count()
1702 }
1703
1704 pub fn is_running(&self) -> bool {
1705 self.is_running
1706 }
1707
1708 pub(crate) fn save_parts(&self) -> (bool, bool, bool, bool, i64, i64, i64, i64) {
1709 (
1710 self.is_running,
1711 self.real_flag,
1712 self.frame_mode,
1713 self.frame_loop_flag,
1714 self.frame_start_value,
1715 self.frame_end_value,
1716 self.frame_time,
1717 self.cur_time,
1718 )
1719 }
1720
1721 pub(crate) fn from_save_parts(
1722 is_running: bool,
1723 real_flag: bool,
1724 frame_mode: bool,
1725 frame_loop_flag: bool,
1726 frame_start_value: i64,
1727 frame_end_value: i64,
1728 frame_time: i64,
1729 cur_time: i64,
1730 ) -> Self {
1731 Self {
1732 cur_time,
1733 is_running,
1734 real_flag,
1735 frame_mode,
1736 frame_loop_flag,
1737 frame_start_value,
1738 frame_end_value,
1739 frame_time,
1740 }
1741 }
1742}
1743
1744#[derive(Debug, Clone, Default)]
1745pub struct PcmEventLine {
1746 pub file_name: String,
1747 pub probability: i32,
1748 pub min_time: i32,
1749 pub max_time: i32,
1750}
1751
1752pub const PCM_EVENT_TYPE_NONE: i32 = -1;
1753pub const PCM_EVENT_TYPE_ONESHOT: i32 = 0;
1754pub const PCM_EVENT_TYPE_LOOP: i32 = 1;
1755pub const PCM_EVENT_TYPE_RANDOM: i32 = 2;
1756
1757#[derive(Debug, Clone)]
1758pub struct PcmEventState {
1759 pub event_type: i32,
1760 pub pcm_buf_no: i32,
1761 pub volume_type: i32,
1762 pub chara_no: i32,
1763 pub bgm_fade_target_flag: bool,
1764 pub bgm_fade2_target_flag: bool,
1765 pub bgm_fade_source_flag: bool,
1766 pub real_flag: bool,
1767 pub time_type: bool,
1768 pub lines: Vec<PcmEventLine>,
1769
1770 pub cur_time: i64,
1774 pub cur_line_no: i32,
1775 pub next_time: i64,
1776 pub last_line_no: i32,
1777}
1778
1779impl Default for PcmEventState {
1780 fn default() -> Self {
1781 Self {
1782 event_type: PCM_EVENT_TYPE_NONE,
1783 pcm_buf_no: -1,
1784 volume_type: 2, chara_no: -1,
1786 bgm_fade_target_flag: false,
1787 bgm_fade2_target_flag: false,
1788 bgm_fade_source_flag: false,
1789 real_flag: false,
1790 time_type: false,
1791 lines: Vec::new(),
1792 cur_time: 0,
1793 cur_line_no: -1,
1794 next_time: 0,
1795 last_line_no: -1,
1796 }
1797 }
1798}
1799
1800impl PcmEventState {
1801 pub fn reinit(&mut self) {
1802 *self = Self::default();
1803 }
1804
1805 pub fn is_active(&self) -> bool {
1806 self.event_type != PCM_EVENT_TYPE_NONE
1807 }
1808
1809 pub fn start(
1810 &mut self,
1811 event_type: i32,
1812 pcm_buf_no: i32,
1813 volume_type: i32,
1814 chara_no: i32,
1815 bgm_fade_target_flag: bool,
1816 bgm_fade2_target_flag: bool,
1817 bgm_fade_source_flag: bool,
1818 real_flag: bool,
1819 time_type: bool,
1820 ) {
1821 self.event_type = event_type;
1822 self.pcm_buf_no = pcm_buf_no;
1823 self.volume_type = volume_type;
1824 self.chara_no = chara_no;
1825 self.bgm_fade_target_flag = bgm_fade_target_flag;
1826 self.bgm_fade2_target_flag = bgm_fade2_target_flag;
1827 self.bgm_fade_source_flag = bgm_fade_source_flag;
1828 self.real_flag = real_flag;
1829 self.time_type = time_type;
1830 self.cur_time = 0;
1831 self.cur_line_no = -1;
1832 self.next_time = 0;
1833 self.last_line_no = -1;
1834 }
1835}
1836
1837#[derive(Debug, Clone)]
1839pub struct MaskState {
1840 pub name: Option<String>,
1841 pub x_event: IntEvent,
1842 pub y_event: IntEvent,
1843 pub extra_int: HashMap<i32, i32>,
1844 pub script_events: HashMap<i32, IntEvent>,
1845}
1846
1847impl Default for MaskState {
1848 fn default() -> Self {
1849 Self::new()
1850 }
1851}
1852
1853impl MaskState {
1854 pub fn new() -> Self {
1855 Self {
1856 name: None,
1857 x_event: IntEvent::new(0),
1858 y_event: IntEvent::new(0),
1859 extra_int: HashMap::default(),
1860 script_events: HashMap::default(),
1861 }
1862 }
1863
1864 pub fn reinit(&mut self) {
1865 self.name = None;
1866 self.x_event.reinit();
1867 self.y_event.reinit();
1868 self.extra_int.clear();
1869 self.script_events.clear();
1870 }
1871}
1872
1873#[derive(Debug, Clone)]
1874pub struct MaskListState {
1875 pub masks: Vec<MaskState>,
1876}
1877
1878pub const EDITBOX_ACTION_NOT_DECIDED: i32 = 0;
1879pub const EDITBOX_ACTION_DECIDED: i32 = 1;
1880pub const EDITBOX_ACTION_CANCELED: i32 = -1;
1881
1882const EDITBOX_UNDO_LIMIT: usize = 64;
1883const EDITBOX_TEXT_PADDING_X: i32 = 4;
1884
1885#[derive(Debug, Clone, PartialEq, Eq)]
1886struct EditBoxUndoSnapshot {
1887 text: String,
1888 cursor_pos: usize,
1889 selection_anchor: Option<usize>,
1890}
1891
1892#[derive(Debug, Clone)]
1893pub struct EditBoxState {
1894 pub created: bool,
1895 pub visible: bool,
1896 pub text: String,
1897 pub cursor_pos: usize,
1899 pub selection_anchor: Option<usize>,
1901 pub composition_text: String,
1903 pub composition_cursor: Option<(usize, usize)>,
1905 pub composition_range: Option<(usize, usize)>,
1907 pub composition_clear_pending: bool,
1910 pub scroll_x_px: i32,
1912 pub mouse_selecting: bool,
1914 pub action_flag: i32,
1915 pub moji_size: i32,
1916 pub rect_x: i32,
1917 pub rect_y: i32,
1918 pub rect_w: i32,
1919 pub rect_h: i32,
1920 pub design_screen_w: i32,
1921 pub design_screen_h: i32,
1922 pub window_x: i32,
1923 pub window_y: i32,
1924 pub window_w: i32,
1925 pub window_h: i32,
1926 pub window_moji_size: i32,
1927 undo_stack: Vec<EditBoxUndoSnapshot>,
1928 redo_stack: Vec<EditBoxUndoSnapshot>,
1929}
1930
1931impl Default for EditBoxState {
1932 fn default() -> Self {
1933 Self {
1934 created: false,
1935 visible: false,
1936 text: String::new(),
1937 cursor_pos: 0,
1938 selection_anchor: None,
1939 composition_text: String::new(),
1940 composition_cursor: None,
1941 composition_range: None,
1942 composition_clear_pending: false,
1943 scroll_x_px: 0,
1944 mouse_selecting: false,
1945 action_flag: EDITBOX_ACTION_NOT_DECIDED,
1946 moji_size: 0,
1947 rect_x: 0,
1948 rect_y: 0,
1949 rect_w: 0,
1950 rect_h: 0,
1951 design_screen_w: 0,
1952 design_screen_h: 0,
1953 window_x: 0,
1954 window_y: 0,
1955 window_w: 0,
1956 window_h: 0,
1957 window_moji_size: 0,
1958 undo_stack: Vec::new(),
1959 redo_stack: Vec::new(),
1960 }
1961 }
1962}
1963
1964impl EditBoxState {
1965 pub fn create_like(
1966 &mut self,
1967 x: i32,
1968 y: i32,
1969 w: i32,
1970 h: i32,
1971 moji_size: i32,
1972 design_screen_w: i32,
1973 design_screen_h: i32,
1974 ) {
1975 self.created = true;
1976 self.visible = false;
1977 self.text.clear();
1978 self.cursor_pos = 0;
1979 self.selection_anchor = None;
1980 self.clear_composition();
1981 self.scroll_x_px = 0;
1982 self.mouse_selecting = false;
1983 self.undo_stack.clear();
1984 self.redo_stack.clear();
1985 self.action_flag = EDITBOX_ACTION_NOT_DECIDED;
1986 self.rect_x = x;
1987 self.rect_y = y;
1988 self.rect_w = w;
1989 self.rect_h = h;
1990 self.moji_size = moji_size;
1991 self.design_screen_w = design_screen_w.max(1);
1992 self.design_screen_h = design_screen_h.max(1);
1993 self.window_x = 0;
1994 self.window_y = 0;
1995 self.window_w = 0;
1996 self.window_h = 0;
1997 self.window_moji_size = 0;
1998 }
1999
2000 pub fn destroy_like(&mut self) {
2001 *self = Self::default();
2002 }
2003
2004 pub fn set_text_like(&mut self, text: String) {
2005 self.text = text;
2006 self.cursor_pos = self.text.len();
2007 self.selection_anchor = None;
2008 self.clear_composition();
2009 self.scroll_x_px = 0;
2010 self.mouse_selecting = false;
2011 self.undo_stack.clear();
2012 self.redo_stack.clear();
2013 self.ensure_caret_visible();
2014 }
2015
2016 pub fn insert_text_at_cursor(&mut self, text: &str) {
2017 self.commit_text(text);
2018 }
2019
2020 pub fn commit_text(&mut self, text: &str) {
2021 let filtered: String = text.chars().filter(|ch| !ch.is_control()).collect();
2022 if filtered.is_empty() {
2023 self.cancel_composition();
2024 return;
2025 }
2026 let range = self
2027 .composition_range
2028 .take()
2029 .or_else(|| self.selection_range())
2030 .unwrap_or_else(|| {
2031 let p = self.normalized_cursor();
2032 (p, p)
2033 });
2034 self.push_undo_snapshot();
2035 self.replace_range_without_undo(range, &filtered);
2036 self.selection_anchor = None;
2037 self.clear_composition();
2038 self.ensure_caret_visible();
2039 }
2040
2041 pub fn set_ime_preedit(&mut self, text: &str, cursor: Option<(usize, usize)>) {
2042 if text.is_empty() {
2043 if self.composition_range.is_some() {
2044 self.composition_text.clear();
2045 self.composition_cursor = None;
2046 self.composition_clear_pending = true;
2047 self.ensure_caret_visible();
2048 }
2049 return;
2050 }
2051 if self.composition_clear_pending {
2052 self.cancel_composition();
2053 }
2054 if self.composition_range.is_none() {
2055 let range = self.selection_range().unwrap_or_else(|| {
2056 let p = self.normalized_cursor();
2057 (p, p)
2058 });
2059 self.composition_range = Some(range);
2060 self.cursor_pos = range.0;
2061 self.selection_anchor = None;
2062 }
2063 self.composition_text.clear();
2064 self.composition_text.push_str(text);
2065 self.composition_clear_pending = false;
2066 self.composition_cursor = cursor.map(|(a, b)| {
2067 (
2068 Self::normalize_boundary(&self.composition_text, a),
2069 Self::normalize_boundary(&self.composition_text, b),
2070 )
2071 });
2072 self.ensure_caret_visible();
2073 }
2074
2075 pub fn cancel_composition(&mut self) {
2076 let had_composition = self.composition_range.is_some() || !self.composition_text.is_empty();
2077 if !had_composition {
2078 return;
2079 }
2080 if let Some((start, _)) = self.composition_range {
2081 self.cursor_pos = Self::normalize_boundary(&self.text, start);
2082 }
2083 self.clear_composition();
2084 self.selection_anchor = None;
2085 self.ensure_caret_visible();
2086 }
2087
2088 pub fn clear_composition(&mut self) {
2089 self.composition_text.clear();
2090 self.composition_cursor = None;
2091 self.composition_range = None;
2092 self.composition_clear_pending = false;
2093 }
2094
2095 pub fn cancel_pending_composition_clear(&mut self) {
2096 if self.composition_clear_pending {
2097 self.cancel_composition();
2098 }
2099 }
2100
2101 pub fn is_composing(&self) -> bool {
2102 self.composition_range.is_some()
2103 }
2104
2105 pub fn selection_range(&self) -> Option<(usize, usize)> {
2106 let cursor = self.normalized_cursor();
2107 let anchor = self
2108 .selection_anchor
2109 .map(|p| Self::normalize_boundary(&self.text, p))?;
2110 if anchor == cursor {
2111 None
2112 } else if anchor < cursor {
2113 Some((anchor, cursor))
2114 } else {
2115 Some((cursor, anchor))
2116 }
2117 }
2118
2119 pub fn selected_text(&self) -> Option<String> {
2120 let (start, end) = self.selection_range()?;
2121 Some(self.text[start..end].to_string())
2122 }
2123
2124 pub fn select_all(&mut self) {
2125 self.cancel_composition();
2126 self.selection_anchor = Some(0);
2127 self.cursor_pos = self.text.len();
2128 self.ensure_caret_visible();
2129 }
2130
2131 pub fn cut_selection(&mut self) -> Option<String> {
2132 let selected = self.selected_text()?;
2133 self.delete_selection();
2134 Some(selected)
2135 }
2136
2137 pub fn backspace_like(&mut self) {
2138 self.delete_backward(false);
2139 }
2140
2141 pub fn delete_backward(&mut self, by_word: bool) {
2142 if self.is_composing() {
2143 return;
2144 }
2145 if self.delete_selection() {
2146 return;
2147 }
2148 let cursor = self.normalized_cursor();
2149 if cursor == 0 {
2150 return;
2151 }
2152 let start = if by_word {
2153 self.word_left_boundary(cursor)
2154 } else {
2155 Self::previous_boundary(&self.text, cursor)
2156 };
2157 self.push_undo_snapshot();
2158 self.replace_range_without_undo((start, cursor), "");
2159 self.ensure_caret_visible();
2160 }
2161
2162 pub fn delete_forward(&mut self, by_word: bool) {
2163 if self.is_composing() {
2164 return;
2165 }
2166 if self.delete_selection() {
2167 return;
2168 }
2169 let cursor = self.normalized_cursor();
2170 if cursor >= self.text.len() {
2171 return;
2172 }
2173 let end = if by_word {
2174 self.word_right_boundary(cursor)
2175 } else {
2176 Self::next_boundary(&self.text, cursor)
2177 };
2178 self.push_undo_snapshot();
2179 self.replace_range_without_undo((cursor, end), "");
2180 self.ensure_caret_visible();
2181 }
2182
2183 pub fn move_cursor_left(&mut self, extend: bool, by_word: bool) {
2184 if self.is_composing() {
2185 return;
2186 }
2187 if !extend && let Some((start, _)) = self.selection_range() {
2188 self.cursor_pos = start;
2189 self.selection_anchor = None;
2190 self.ensure_caret_visible();
2191 return;
2192 }
2193 let old = self.normalized_cursor();
2194 let next = if by_word {
2195 self.word_left_boundary(old)
2196 } else {
2197 Self::previous_boundary(&self.text, old)
2198 };
2199 self.set_cursor_with_selection(old, next, extend);
2200 }
2201
2202 pub fn move_cursor_right(&mut self, extend: bool, by_word: bool) {
2203 if self.is_composing() {
2204 return;
2205 }
2206 if !extend && let Some((_, end)) = self.selection_range() {
2207 self.cursor_pos = end;
2208 self.selection_anchor = None;
2209 self.ensure_caret_visible();
2210 return;
2211 }
2212 let old = self.normalized_cursor();
2213 let next = if by_word {
2214 self.word_right_boundary(old)
2215 } else {
2216 Self::next_boundary(&self.text, old)
2217 };
2218 self.set_cursor_with_selection(old, next, extend);
2219 }
2220
2221 pub fn move_cursor_home(&mut self, extend: bool) {
2222 if self.is_composing() {
2223 return;
2224 }
2225 let old = self.normalized_cursor();
2226 self.set_cursor_with_selection(old, 0, extend);
2227 }
2228
2229 pub fn move_cursor_end(&mut self, extend: bool) {
2230 if self.is_composing() {
2231 return;
2232 }
2233 let old = self.normalized_cursor();
2234 self.set_cursor_with_selection(old, self.text.len(), extend);
2235 }
2236
2237 pub fn set_cursor_from_window_x(&mut self, window_x: i32, extend: bool) {
2238 self.cancel_composition();
2239 let target_x = window_x
2240 .saturating_sub(self.window_x)
2241 .saturating_sub(EDITBOX_TEXT_PADDING_X)
2242 .saturating_add(self.scroll_x_px)
2243 .max(0);
2244 let mut x = 0i32;
2245 let mut target = self.text.len();
2246 for (idx, ch) in self.text.char_indices() {
2247 let width = crate::text_render::editbox_cell_width_px(ch, self.font_px());
2248 if target_x < x.saturating_add((width + 1) / 2) {
2249 target = idx;
2250 break;
2251 }
2252 x = x.saturating_add(width);
2253 target = idx + ch.len_utf8();
2254 }
2255 let old = self.normalized_cursor();
2256 self.set_cursor_with_selection(old, target, extend);
2257 }
2258
2259 pub fn caret_window_x(&self) -> i32 {
2260 let content_x = self.display_caret_content_x();
2261 self.window_x
2262 .saturating_add(EDITBOX_TEXT_PADDING_X)
2263 .saturating_add(content_x.saturating_sub(self.scroll_x_px))
2264 }
2265
2266 pub fn ensure_caret_visible_for_focus(&mut self) {
2267 self.ensure_caret_visible();
2268 }
2269
2270 pub fn undo(&mut self) {
2271 self.cancel_composition();
2272 let Some(snapshot) = self.undo_stack.pop() else {
2273 return;
2274 };
2275 let current = self.snapshot();
2276 self.redo_stack.push(current);
2277 self.restore_snapshot(snapshot);
2278 }
2279
2280 pub fn redo(&mut self) {
2281 self.cancel_composition();
2282 let Some(snapshot) = self.redo_stack.pop() else {
2283 return;
2284 };
2285 let current = self.snapshot();
2286 self.undo_stack.push(current);
2287 self.restore_snapshot(snapshot);
2288 }
2289
2290 pub fn update_rect(&mut self, screen_w: i32, screen_h: i32) {
2291 let base_w = self.design_screen_w.max(1);
2292 let base_h = self.design_screen_h.max(1);
2293 let sw = screen_w.max(1);
2294 let sh = screen_h.max(1);
2295 self.window_x = self.rect_x.saturating_mul(sw) / base_w;
2296 self.window_y = self.rect_y.saturating_mul(sh) / base_h;
2297 self.window_w = self.rect_w.saturating_mul(sw) / base_w;
2298 self.window_h = self.rect_h.saturating_mul(sh) / base_h;
2299 self.window_moji_size = self.moji_size.saturating_mul(sh) / base_h;
2300 self.ensure_caret_visible();
2301 }
2302
2303 pub fn frame(&mut self, display_mode_change_proc_cnt: i32) {
2304 self.visible = self.created && display_mode_change_proc_cnt == 0;
2305 if !self.visible {
2306 self.mouse_selecting = false;
2307 self.clear_composition();
2308 }
2309 }
2310
2311 pub fn clear_input(&mut self) {
2312 self.action_flag = EDITBOX_ACTION_NOT_DECIDED;
2313 }
2314
2315 pub fn is_decided(&self) -> bool {
2316 self.action_flag == EDITBOX_ACTION_DECIDED
2317 }
2318
2319 pub fn is_canceled(&self) -> bool {
2320 self.action_flag == EDITBOX_ACTION_CANCELED
2321 }
2322
2323 pub fn contains_point(&self, x: i32, y: i32) -> bool {
2324 self.created
2325 && self.visible
2326 && self.window_w > 0
2327 && self.window_h > 0
2328 && x >= self.window_x
2329 && y >= self.window_y
2330 && x < self.window_x.saturating_add(self.window_w)
2331 && y < self.window_y.saturating_add(self.window_h)
2332 }
2333
2334 fn font_px(&self) -> i32 {
2335 self.window_moji_size.max(1)
2336 }
2337
2338 fn normalized_cursor(&self) -> usize {
2339 Self::normalize_boundary(&self.text, self.cursor_pos)
2340 }
2341
2342 fn normalize_boundary(text: &str, pos: usize) -> usize {
2343 let mut pos = pos.min(text.len());
2344 while pos > 0 && !text.is_char_boundary(pos) {
2345 pos -= 1;
2346 }
2347 pos
2348 }
2349
2350 fn previous_boundary(text: &str, pos: usize) -> usize {
2351 let pos = Self::normalize_boundary(text, pos);
2352 text[..pos]
2353 .char_indices()
2354 .next_back()
2355 .map(|(idx, _)| idx)
2356 .unwrap_or(0)
2357 }
2358
2359 fn next_boundary(text: &str, pos: usize) -> usize {
2360 let pos = Self::normalize_boundary(text, pos);
2361 text[pos..]
2362 .chars()
2363 .next()
2364 .map(|ch| pos + ch.len_utf8())
2365 .unwrap_or(text.len())
2366 }
2367
2368 fn word_class(ch: char) -> u8 {
2369 if ch.is_whitespace() {
2370 0
2371 } else if ch.is_alphanumeric() || ch == '_' {
2372 1
2373 } else {
2374 2
2375 }
2376 }
2377
2378 fn word_left_boundary(&self, pos: usize) -> usize {
2379 let mut cursor = Self::normalize_boundary(&self.text, pos);
2380 while cursor > 0 {
2381 let prev = Self::previous_boundary(&self.text, cursor);
2382 let ch = self.text[prev..cursor].chars().next().unwrap_or(' ');
2383 if Self::word_class(ch) != 0 {
2384 break;
2385 }
2386 cursor = prev;
2387 }
2388 let Some(class) = (cursor > 0).then(|| {
2389 let prev = Self::previous_boundary(&self.text, cursor);
2390 let ch = self.text[prev..cursor].chars().next().unwrap_or(' ');
2391 Self::word_class(ch)
2392 }) else {
2393 return 0;
2394 };
2395 while cursor > 0 {
2396 let prev = Self::previous_boundary(&self.text, cursor);
2397 let ch = self.text[prev..cursor].chars().next().unwrap_or(' ');
2398 if Self::word_class(ch) != class {
2399 break;
2400 }
2401 cursor = prev;
2402 }
2403 cursor
2404 }
2405
2406 fn word_right_boundary(&self, pos: usize) -> usize {
2407 let mut cursor = Self::normalize_boundary(&self.text, pos);
2408 if cursor >= self.text.len() {
2409 return self.text.len();
2410 }
2411 let class = self.text[cursor..]
2412 .chars()
2413 .next()
2414 .map(Self::word_class)
2415 .unwrap_or(0);
2416 while cursor < self.text.len() {
2417 let next = Self::next_boundary(&self.text, cursor);
2418 let ch = self.text[cursor..next].chars().next().unwrap_or(' ');
2419 if Self::word_class(ch) != class {
2420 break;
2421 }
2422 cursor = next;
2423 }
2424 while cursor < self.text.len() {
2425 let next = Self::next_boundary(&self.text, cursor);
2426 let ch = self.text[cursor..next].chars().next().unwrap_or(' ');
2427 if Self::word_class(ch) != 0 {
2428 break;
2429 }
2430 cursor = next;
2431 }
2432 cursor
2433 }
2434
2435 fn set_cursor_with_selection(&mut self, old: usize, next: usize, extend: bool) {
2436 if extend {
2437 if self.selection_anchor.is_none() {
2438 self.selection_anchor = Some(old);
2439 }
2440 } else {
2441 self.selection_anchor = None;
2442 }
2443 self.cursor_pos = Self::normalize_boundary(&self.text, next);
2444 if self.selection_anchor == Some(self.cursor_pos) {
2445 self.selection_anchor = None;
2446 }
2447 self.ensure_caret_visible();
2448 }
2449
2450 fn delete_selection(&mut self) -> bool {
2451 let Some(range) = self.selection_range() else {
2452 return false;
2453 };
2454 self.push_undo_snapshot();
2455 self.replace_range_without_undo(range, "");
2456 self.selection_anchor = None;
2457 self.ensure_caret_visible();
2458 true
2459 }
2460
2461 fn replace_range_without_undo(&mut self, range: (usize, usize), replacement: &str) {
2462 let start = Self::normalize_boundary(&self.text, range.0.min(range.1));
2463 let end = Self::normalize_boundary(&self.text, range.0.max(range.1));
2464 self.text.replace_range(start..end, replacement);
2465 self.cursor_pos = start.saturating_add(replacement.len()).min(self.text.len());
2466 self.cursor_pos = Self::normalize_boundary(&self.text, self.cursor_pos);
2467 }
2468
2469 fn snapshot(&self) -> EditBoxUndoSnapshot {
2470 EditBoxUndoSnapshot {
2471 text: self.text.clone(),
2472 cursor_pos: self.normalized_cursor(),
2473 selection_anchor: self
2474 .selection_anchor
2475 .map(|p| Self::normalize_boundary(&self.text, p)),
2476 }
2477 }
2478
2479 fn restore_snapshot(&mut self, snapshot: EditBoxUndoSnapshot) {
2480 self.text = snapshot.text;
2481 self.cursor_pos = Self::normalize_boundary(&self.text, snapshot.cursor_pos);
2482 self.selection_anchor = snapshot
2483 .selection_anchor
2484 .map(|p| Self::normalize_boundary(&self.text, p));
2485 self.clear_composition();
2486 self.ensure_caret_visible();
2487 }
2488
2489 fn push_undo_snapshot(&mut self) {
2490 let snapshot = self.snapshot();
2491 if self.undo_stack.last() == Some(&snapshot) {
2492 return;
2493 }
2494 if self.undo_stack.len() >= EDITBOX_UNDO_LIMIT {
2495 self.undo_stack.remove(0);
2496 }
2497 self.undo_stack.push(snapshot);
2498 self.redo_stack.clear();
2499 }
2500
2501 fn display_width_before(&self, byte_pos: usize) -> i32 {
2502 let pos = Self::normalize_boundary(&self.text, byte_pos);
2503 self.text[..pos].chars().fold(0i32, |sum, ch| {
2504 sum.saturating_add(crate::text_render::editbox_cell_width_px(
2505 ch,
2506 self.font_px(),
2507 ))
2508 })
2509 }
2510
2511 fn display_caret_content_x(&self) -> i32 {
2512 if let Some((start, _end)) = self.composition_range {
2513 let mut x = self.display_width_before(start);
2514 let comp_cursor = self
2515 .composition_cursor
2516 .map(|(_, end)| Self::normalize_boundary(&self.composition_text, end))
2517 .unwrap_or(self.composition_text.len());
2518 for ch in self.composition_text[..comp_cursor].chars() {
2519 x = x.saturating_add(crate::text_render::editbox_cell_width_px(
2520 ch,
2521 self.font_px(),
2522 ));
2523 }
2524 x
2525 } else {
2526 self.display_width_before(self.normalized_cursor())
2527 }
2528 }
2529
2530 fn display_total_width(&self) -> i32 {
2531 if let Some((start, end)) = self.composition_range {
2532 let start = Self::normalize_boundary(&self.text, start);
2533 let end = Self::normalize_boundary(&self.text, end);
2534 let mut width = self.display_width_before(start);
2535 for ch in self.composition_text.chars() {
2536 width = width.saturating_add(crate::text_render::editbox_cell_width_px(
2537 ch,
2538 self.font_px(),
2539 ));
2540 }
2541 for ch in self.text[end..].chars() {
2542 width = width.saturating_add(crate::text_render::editbox_cell_width_px(
2543 ch,
2544 self.font_px(),
2545 ));
2546 }
2547 width
2548 } else {
2549 self.display_width_before(self.text.len())
2550 }
2551 }
2552
2553 fn ensure_caret_visible(&mut self) {
2554 let available = self
2555 .window_w
2556 .saturating_sub(EDITBOX_TEXT_PADDING_X.saturating_mul(2))
2557 .max(1);
2558 let caret = self.display_caret_content_x();
2559 if caret < self.scroll_x_px {
2560 self.scroll_x_px = caret;
2561 } else if caret > self.scroll_x_px.saturating_add(available) {
2562 self.scroll_x_px = caret.saturating_sub(available);
2563 }
2564 let max_scroll = self.display_total_width().saturating_sub(available).max(0);
2565 self.scroll_x_px = self.scroll_x_px.clamp(0, max_scroll);
2566 }
2567}
2568
2569#[derive(Debug, Clone)]
2570pub struct EditBoxListState {
2571 pub boxes: Vec<EditBoxState>,
2572}
2573
2574impl EditBoxListState {
2575 pub fn new(cnt: usize) -> Self {
2576 Self {
2577 boxes: vec![EditBoxState::default(); cnt],
2578 }
2579 }
2580
2581 pub fn ensure_size(&mut self, cnt: usize) {
2582 if self.boxes.len() < cnt {
2583 self.boxes
2584 .extend((0..(cnt - self.boxes.len())).map(|_| EditBoxState::default()));
2585 } else if self.boxes.len() > cnt {
2586 self.boxes.truncate(cnt);
2587 }
2588 }
2589}
2590
2591#[cfg(test)]
2592mod editbox_state_tests {
2593 use super::EditBoxState;
2594
2595 fn editbox_with_text(text: &str) -> EditBoxState {
2596 let mut editbox = EditBoxState::default();
2597 editbox.create_like(0, 0, 320, 32, 20, 1280, 720);
2598 editbox.update_rect(1280, 720);
2599 editbox.frame(0);
2600 editbox.set_text_like(text.to_string());
2601 editbox
2602 }
2603
2604 #[test]
2605 fn unicode_backspace_and_delete_keep_utf8_boundaries() {
2606 let mut editbox = editbox_with_text("A猫😀B");
2607 editbox.move_cursor_left(false, false);
2608 editbox.delete_backward(false);
2609 assert_eq!(editbox.text, "A猫B");
2610 assert!(editbox.text.is_char_boundary(editbox.cursor_pos));
2611
2612 editbox.move_cursor_home(false);
2613 editbox.move_cursor_right(false, false);
2614 editbox.delete_forward(false);
2615 assert_eq!(editbox.text, "AB");
2616 assert!(editbox.text.is_char_boundary(editbox.cursor_pos));
2617 }
2618
2619 #[test]
2620 fn committed_text_replaces_active_selection() {
2621 let mut editbox = editbox_with_text("abcdef");
2622 editbox.move_cursor_home(false);
2623 editbox.move_cursor_right(false, false);
2624 editbox.move_cursor_right(true, false);
2625 editbox.move_cursor_right(true, false);
2626 assert_eq!(editbox.selection_range(), Some((1, 3)));
2627
2628 editbox.commit_text("猫");
2629 assert_eq!(editbox.text, "a猫def");
2630 assert_eq!(editbox.selection_range(), None);
2631 assert_eq!(editbox.cursor_pos, "a猫".len());
2632 }
2633
2634 #[test]
2635 fn ime_preedit_is_transient_until_commit() {
2636 let mut editbox = editbox_with_text("abcd");
2637 editbox.move_cursor_home(false);
2638 editbox.move_cursor_right(false, false);
2639 editbox.move_cursor_right(true, false);
2640 editbox.move_cursor_right(true, false);
2641
2642 editbox.set_ime_preedit("日本", Some((3, 6)));
2643 assert_eq!(editbox.text, "abcd");
2644 assert_eq!(editbox.composition_range, Some((1, 3)));
2645 assert!(editbox.is_composing());
2646
2647 editbox.set_ime_preedit("", None);
2650 assert!(editbox.composition_clear_pending);
2651 assert_eq!(editbox.composition_range, Some((1, 3)));
2652
2653 editbox.commit_text("日本");
2654 assert_eq!(editbox.text, "a日本d");
2655 assert!(!editbox.is_composing());
2656 assert_eq!(editbox.cursor_pos, "a日本".len());
2657 }
2658
2659 #[test]
2660 fn canceled_empty_preedit_does_not_replace_later_input() {
2661 let mut editbox = editbox_with_text("abcd");
2662 editbox.move_cursor_home(false);
2663 editbox.move_cursor_right(false, false);
2664 editbox.move_cursor_right(true, false);
2665 editbox.set_ime_preedit("x", Some((1, 1)));
2666 editbox.set_ime_preedit("", None);
2667 editbox.cancel_pending_composition_clear();
2668 editbox.commit_text("Z");
2669 assert_eq!(editbox.text, "aZbcd");
2670 }
2671
2672 #[test]
2673 fn undo_and_redo_restore_text_cursor_and_selection() {
2674 let mut editbox = editbox_with_text("one");
2675 editbox.commit_text(" two");
2676 assert_eq!(editbox.text, "one two");
2677
2678 editbox.undo();
2679 assert_eq!(editbox.text, "one");
2680 assert_eq!(editbox.cursor_pos, 3);
2681
2682 editbox.redo();
2683 assert_eq!(editbox.text, "one two");
2684 assert_eq!(editbox.cursor_pos, 7);
2685 }
2686
2687 #[test]
2688 fn mouse_drag_keeps_the_initial_selection_anchor() {
2689 let mut editbox = editbox_with_text("abcdef");
2690 editbox.set_cursor_from_window_x(editbox.window_x + 4, false);
2691 let anchor = editbox.cursor_pos;
2692 editbox.set_cursor_from_window_x(editbox.window_x + 80, true);
2693 assert_eq!(editbox.selection_anchor, Some(anchor));
2694 editbox.set_cursor_from_window_x(editbox.window_x + 120, true);
2695 assert_eq!(editbox.selection_anchor, Some(anchor));
2696 assert!(editbox.selection_range().is_some());
2697 }
2698}
2699
2700#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2705pub enum WorldListOpKind {
2706 GetSize,
2707 Create,
2708 Destroy,
2709 Unknown,
2710}
2711
2712#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2713pub enum WorldOpKind {
2714 Init,
2715 GetNo,
2716 CameraEyeX,
2717 CameraEyeY,
2718 CameraEyeZ,
2719 CameraPintX,
2720 CameraPintY,
2721 CameraPintZ,
2722 CameraUpX,
2723 CameraUpY,
2724 CameraUpZ,
2725 CameraEyeXEve,
2726 CameraEyeYEve,
2727 CameraEyeZEve,
2728 CameraPintXEve,
2729 CameraPintYEve,
2730 CameraPintZEve,
2731 CameraUpXEve,
2732 CameraUpYEve,
2733 CameraUpZEve,
2734 CameraViewAngle,
2735 SetCameraEye,
2736 CalcCameraEye,
2737 SetCameraPint,
2738 CalcCameraPint,
2739 SetCameraUp,
2740 Mono,
2741 SetCameraEveXzRotate,
2742 Order,
2743 Layer,
2744 WipeCopy,
2745 WipeErase,
2746 Unknown,
2747}
2748
2749#[derive(Debug, Clone, Copy)]
2750pub struct WorldRotateEvent {
2751 pub loop_type: i32,
2752 pub cur_time: i32,
2753 pub end_time: i32,
2754 pub delay_time: i32,
2755 pub speed_type: i32,
2756 pub start_x: i32,
2757 pub start_z: i32,
2758 pub end_x: i32,
2759 pub end_z: i32,
2760}
2761
2762impl Default for WorldRotateEvent {
2763 fn default() -> Self {
2764 Self::new()
2765 }
2766}
2767
2768impl WorldRotateEvent {
2769 pub fn new() -> Self {
2770 Self {
2771 loop_type: -1,
2772 cur_time: 0,
2773 end_time: 0,
2774 delay_time: 0,
2775 speed_type: 0,
2776 start_x: 0,
2777 start_z: 0,
2778 end_x: 0,
2779 end_z: 0,
2780 }
2781 }
2782
2783 pub fn is_active(&self) -> bool {
2784 self.loop_type != -1
2785 }
2786}
2787
2788#[derive(Debug, Clone)]
2789pub struct WorldState {
2790 pub world_no: i32,
2791 pub mode: i32,
2792 pub camera_eye_x: IntEvent,
2793 pub camera_eye_y: IntEvent,
2794 pub camera_eye_z: IntEvent,
2795 pub camera_pint_x: IntEvent,
2796 pub camera_pint_y: IntEvent,
2797 pub camera_pint_z: IntEvent,
2798 pub camera_up_x: IntEvent,
2799 pub camera_up_y: IntEvent,
2800 pub camera_up_z: IntEvent,
2801 pub camera_view_angle: i32,
2802 pub mono: i32,
2803 pub order: i32,
2804 pub layer: i32,
2805 pub wipe_copy: i32,
2806 pub wipe_erase: i32,
2807 pub camera_eye_xz_eve: WorldRotateEvent,
2808 pub script_events: HashMap<i32, IntEvent>,
2809 pub extra_int: HashMap<i32, i64>,
2810 pub extra_str: HashMap<i32, String>,
2811}
2812
2813impl WorldState {
2814 pub fn new(world_no: i32) -> Self {
2815 let mut out = Self {
2816 world_no,
2817 mode: 1,
2818 camera_eye_x: IntEvent::new(0),
2819 camera_eye_y: IntEvent::new(0),
2820 camera_eye_z: IntEvent::new(-1000),
2821 camera_pint_x: IntEvent::new(0),
2822 camera_pint_y: IntEvent::new(0),
2823 camera_pint_z: IntEvent::new(0),
2824 camera_up_x: IntEvent::new(0),
2825 camera_up_y: IntEvent::new(1),
2826 camera_up_z: IntEvent::new(0),
2827 camera_view_angle: 450,
2828 mono: 0,
2829 order: 0,
2830 layer: 0,
2831 wipe_copy: 0,
2832 wipe_erase: 0,
2833 camera_eye_xz_eve: WorldRotateEvent::new(),
2834 script_events: HashMap::default(),
2835 extra_int: HashMap::default(),
2836 extra_str: HashMap::default(),
2837 };
2838 out.reinit();
2839 out
2840 }
2841
2842 pub fn reinit(&mut self) {
2843 self.mode = 1;
2844 self.camera_eye_x = IntEvent::new(0);
2845 self.camera_eye_y = IntEvent::new(0);
2846 self.camera_eye_z = IntEvent::new(-1000);
2847 self.camera_pint_x = IntEvent::new(0);
2848 self.camera_pint_y = IntEvent::new(0);
2849 self.camera_pint_z = IntEvent::new(0);
2850 self.camera_up_x = IntEvent::new(0);
2851 self.camera_up_y = IntEvent::new(1);
2852 self.camera_up_z = IntEvent::new(0);
2853 self.camera_view_angle = 450;
2854 self.mono = 0;
2855 self.order = 0;
2856 self.layer = 0;
2857 self.wipe_copy = 0;
2858 self.wipe_erase = 0;
2859 self.camera_eye_xz_eve = WorldRotateEvent::new();
2860 }
2861
2862 pub fn update_time(&mut self, past_game_time: i32, past_real_time: i32) {
2863 self.camera_eye_x
2864 .update_time(past_game_time, past_real_time);
2865 self.camera_eye_y
2866 .update_time(past_game_time, past_real_time);
2867 self.camera_eye_z
2868 .update_time(past_game_time, past_real_time);
2869 self.camera_pint_x
2870 .update_time(past_game_time, past_real_time);
2871 self.camera_pint_y
2872 .update_time(past_game_time, past_real_time);
2873 self.camera_pint_z
2874 .update_time(past_game_time, past_real_time);
2875 self.camera_up_x.update_time(past_game_time, past_real_time);
2876 self.camera_up_y.update_time(past_game_time, past_real_time);
2877 self.camera_up_z.update_time(past_game_time, past_real_time);
2878 if self.camera_eye_xz_eve.is_active() {
2879 self.camera_eye_xz_eve.cur_time = self
2880 .camera_eye_xz_eve
2881 .cur_time
2882 .saturating_add(past_game_time);
2883 }
2884 }
2885
2886 pub fn frame(&mut self) {
2887 self.camera_eye_x.frame();
2888 self.camera_eye_y.frame();
2889 self.camera_eye_z.frame();
2890 self.camera_pint_x.frame();
2891 self.camera_pint_y.frame();
2892 self.camera_pint_z.frame();
2893 self.camera_up_x.frame();
2894 self.camera_up_y.frame();
2895 self.camera_up_z.frame();
2896
2897 if self.camera_eye_xz_eve.is_active() {
2898 self.frame_xz_rotate();
2899 }
2900 }
2901
2902 fn frame_xz_rotate(&mut self) {
2903 let mut cur_time = self.camera_eye_xz_eve.cur_time - self.camera_eye_xz_eve.delay_time;
2904 let end_time = self.camera_eye_xz_eve.end_time;
2905
2906 if self.camera_eye_xz_eve.loop_type == 0 && cur_time - end_time >= 0 {
2907 self.camera_eye_xz_eve.loop_type = -1;
2908 return;
2909 }
2910
2911 if cur_time <= 0 {
2912 self.camera_eye_x.cur_value = self.camera_eye_x.start_value;
2913 self.camera_eye_z.cur_value = self.camera_eye_z.start_value;
2914 return;
2915 }
2916
2917 if end_time <= 0 {
2918 return;
2919 }
2920
2921 if self.camera_eye_xz_eve.loop_type == 1 {
2922 cur_time %= end_time;
2923 }
2924 if self.camera_eye_xz_eve.loop_type == 2 {
2925 cur_time %= end_time * 2;
2926 if cur_time - end_time > 0 {
2927 cur_time = end_time - (cur_time - end_time);
2928 }
2929 }
2930
2931 match self.camera_eye_xz_eve.speed_type {
2932 1 => {
2933 cur_time = (cur_time as f64 * cur_time as f64 / end_time as f64) as i32;
2934 }
2935 2 => {
2936 let ct = (cur_time - end_time) as f64;
2937 let et = end_time as f64;
2938 cur_time = (-ct * ct / et + et) as i32;
2939 }
2940 _ => {}
2941 }
2942
2943 let px = self.camera_pint_x.get_total_value() as f64;
2944 let pz = self.camera_pint_z.get_total_value() as f64;
2945 let sx = self.camera_eye_x.start_value as f64;
2946 let sz = self.camera_eye_z.start_value as f64;
2947 let ex = self.camera_eye_x.end_value as f64;
2948 let ez = self.camera_eye_z.end_value as f64;
2949
2950 let sdx = sx - px;
2951 let sdz = sz - pz;
2952 let edx = ex - px;
2953 let edz = ez - pz;
2954
2955 let s_len = (sdx * sdx + sdz * sdz).sqrt();
2956 let e_len = (edx * edx + edz * edz).sqrt();
2957 let t_len = linear(cur_time, s_len, end_time, e_len);
2958
2959 let mut s_theta = sdz.atan2(sdx);
2960 let mut e_theta = edz.atan2(edx);
2961 if (s_theta - e_theta).abs() > std::f64::consts::PI {
2962 if e_theta < 0.0 {
2963 e_theta += std::f64::consts::PI * 2.0;
2964 } else {
2965 e_theta -= std::f64::consts::PI * 2.0;
2966 }
2967 }
2968 let t_theta = linear(cur_time, s_theta, end_time, e_theta);
2969
2970 let tmp_x = t_len * t_theta.cos() + px;
2971 let tmp_z = t_len * t_theta.sin() + pz;
2972
2973 self.camera_eye_x.cur_value = tmp_x as i32;
2974 self.camera_eye_z.cur_value = tmp_z as i32;
2975 }
2976}
2977
2978fn linear(cur: i32, start_value: f64, end_time: i32, end_value: f64) -> f64 {
2979 if end_time <= 0 {
2980 return end_value;
2981 }
2982 let t = cur as f64 / end_time as f64;
2983 start_value + (end_value - start_value) * t
2984}
2985
2986#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2987pub enum ObjectListOpKind {
2988 GetSize,
2989 Resize,
2990 Unknown,
2991}
2992
2993#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2994pub enum ObjectOpKind {
2995 Init,
2996 Free,
2997 InitParam,
2998 CreatePct,
2999 CreateRect,
3000 CreateString,
3001 CreateCopyFrom,
3003 SetPos,
3005 SetCenter,
3007 SetScale,
3009 SetRotate,
3011 SetClip,
3013 SetSrcClip,
3015 ClearButton,
3017 SetButton,
3019 SetButtonGroup,
3021 RepIntList,
3023 Unknown,
3024}
3025
3026#[derive(Debug, Clone, PartialEq, Eq)]
3027pub struct StringGlyphBackend {
3028 pub glyph_index: usize,
3030 pub shadow_local_x: i32,
3033 pub shadow_local_y: i32,
3034 pub fuchi_local_x: i32,
3035 pub fuchi_local_y: i32,
3036 pub body_local_x: i32,
3037 pub body_local_y: i32,
3038 pub shadow_sprite_id: SpriteId,
3039 pub fuchi_sprite_id: SpriteId,
3040 pub body_sprite_id: SpriteId,
3041 pub shadow_image_id: Option<ImageHandle>,
3042 pub fuchi_image_id: Option<ImageHandle>,
3043 pub body_image_id: Option<ImageHandle>,
3044}
3045
3046#[derive(Debug, Clone, PartialEq, Eq, Default)]
3047pub enum ObjectBackend {
3048 #[default]
3049 None,
3050 Gfx,
3052 Rect {
3054 layer_id: LayerId,
3055 sprite_id: SpriteId,
3056 width: u32,
3057 height: u32,
3058 },
3059 String {
3061 layer_id: LayerId,
3062 shadow_sprite_id: SpriteId,
3063 fuchi_sprite_id: SpriteId,
3064 sprite_id: SpriteId,
3065 shadow_image_id: Option<ImageHandle>,
3066 fuchi_image_id: Option<ImageHandle>,
3067 image_id: Option<ImageHandle>,
3068 glyphs: Vec<StringGlyphBackend>,
3072 mwnd_layer_reps: bool,
3075 width: u32,
3076 height: u32,
3077 },
3078 Number {
3080 layer_id: LayerId,
3081 sprite_ids: Vec<SpriteId>,
3082 },
3083 Weather {
3085 layer_id: LayerId,
3086 sprite_ids: Vec<SpriteId>,
3087 },
3088 Movie {
3090 layer_id: LayerId,
3091 sprite_id: SpriteId,
3092 image_id: Option<ImageHandle>,
3093 width: u32,
3094 height: u32,
3095 },
3096}
3097
3098pub const OBJECT_NESTED_SLOT_KEY: i32 = i32::MIN + 1;
3099
3100#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3102pub enum BaseIntField {
3103 Alpha,
3104 AlphaBlend,
3105 AlphaTest,
3106 Blend,
3107 ClickDisable,
3108 ClipUse,
3109 Culling,
3110 Disp,
3111 FogUse,
3112 Layer,
3113 LightNo,
3114 MaskNo,
3115 Order,
3116 SrcClipUse,
3117 TonecurveNo,
3118 WipeCopy,
3119 WipeErase,
3120 World,
3121}
3122
3123impl ObjectBaseState {
3124 fn int_field(&self, field: BaseIntField) -> i64 {
3125 match field {
3126 BaseIntField::Alpha => self.alpha,
3127 BaseIntField::AlphaBlend => self.alpha_blend,
3128 BaseIntField::AlphaTest => self.alpha_test,
3129 BaseIntField::Blend => self.blend,
3130 BaseIntField::ClickDisable => self.click_disable,
3131 BaseIntField::ClipUse => self.clip_use,
3132 BaseIntField::Culling => self.culling,
3133 BaseIntField::Disp => self.disp,
3134 BaseIntField::FogUse => self.fog_use,
3135 BaseIntField::Layer => self.layer,
3136 BaseIntField::LightNo => self.light_no,
3137 BaseIntField::MaskNo => self.mask_no,
3138 BaseIntField::Order => self.order,
3139 BaseIntField::SrcClipUse => self.src_clip_use,
3140 BaseIntField::TonecurveNo => self.tonecurve_no,
3141 BaseIntField::WipeCopy => self.wipe_copy,
3142 BaseIntField::WipeErase => self.wipe_erase,
3143 BaseIntField::World => self.world,
3144 }
3145 }
3146}
3147
3148#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3150pub enum FixedIntProp {
3151 Base(BaseIntField),
3152 EventTotal(ObjectEventTarget),
3154}
3155
3156fn fixed_int_prop_pairs(
3159 ids: &crate::runtime::constants::RuntimeConstants,
3160) -> Vec<(i32, FixedIntProp)> {
3161 vec![
3162 (ids.obj_disp, FixedIntProp::Base(BaseIntField::Disp)),
3163 (
3164 ids.obj_wipe_copy,
3165 FixedIntProp::Base(BaseIntField::WipeCopy),
3166 ),
3167 (
3168 ids.obj_wipe_erase,
3169 FixedIntProp::Base(BaseIntField::WipeErase),
3170 ),
3171 (
3172 ids.obj_click_disable,
3173 FixedIntProp::Base(BaseIntField::ClickDisable),
3174 ),
3175 (
3176 ids.obj_patno,
3177 FixedIntProp::EventTotal(ObjectEventTarget::Patno),
3178 ),
3179 (ids.obj_world, FixedIntProp::Base(BaseIntField::World)),
3180 (ids.obj_order, FixedIntProp::Base(BaseIntField::Order)),
3181 (ids.obj_layer, FixedIntProp::Base(BaseIntField::Layer)),
3182 (ids.obj_x, FixedIntProp::EventTotal(ObjectEventTarget::X)),
3183 (ids.obj_y, FixedIntProp::EventTotal(ObjectEventTarget::Y)),
3184 (ids.obj_z, FixedIntProp::EventTotal(ObjectEventTarget::Z)),
3185 (
3186 ids.obj_center_x,
3187 FixedIntProp::EventTotal(ObjectEventTarget::CenterX),
3188 ),
3189 (
3190 ids.obj_center_y,
3191 FixedIntProp::EventTotal(ObjectEventTarget::CenterY),
3192 ),
3193 (
3194 ids.obj_center_z,
3195 FixedIntProp::EventTotal(ObjectEventTarget::CenterZ),
3196 ),
3197 (
3198 ids.obj_center_rep_x,
3199 FixedIntProp::EventTotal(ObjectEventTarget::CenterRepX),
3200 ),
3201 (
3202 ids.obj_center_rep_y,
3203 FixedIntProp::EventTotal(ObjectEventTarget::CenterRepY),
3204 ),
3205 (
3206 ids.obj_center_rep_z,
3207 FixedIntProp::EventTotal(ObjectEventTarget::CenterRepZ),
3208 ),
3209 (
3210 ids.obj_scale_x,
3211 FixedIntProp::EventTotal(ObjectEventTarget::ScaleX),
3212 ),
3213 (
3214 ids.obj_scale_y,
3215 FixedIntProp::EventTotal(ObjectEventTarget::ScaleY),
3216 ),
3217 (
3218 ids.obj_scale_z,
3219 FixedIntProp::EventTotal(ObjectEventTarget::ScaleZ),
3220 ),
3221 (
3222 ids.obj_rotate_x,
3223 FixedIntProp::EventTotal(ObjectEventTarget::RotateX),
3224 ),
3225 (
3226 ids.obj_rotate_y,
3227 FixedIntProp::EventTotal(ObjectEventTarget::RotateY),
3228 ),
3229 (
3230 ids.obj_rotate_z,
3231 FixedIntProp::EventTotal(ObjectEventTarget::RotateZ),
3232 ),
3233 (ids.obj_clip_use, FixedIntProp::Base(BaseIntField::ClipUse)),
3234 (
3235 ids.obj_clip_left,
3236 FixedIntProp::EventTotal(ObjectEventTarget::ClipLeft),
3237 ),
3238 (
3239 ids.obj_clip_top,
3240 FixedIntProp::EventTotal(ObjectEventTarget::ClipTop),
3241 ),
3242 (
3243 ids.obj_clip_right,
3244 FixedIntProp::EventTotal(ObjectEventTarget::ClipRight),
3245 ),
3246 (
3247 ids.obj_clip_bottom,
3248 FixedIntProp::EventTotal(ObjectEventTarget::ClipBottom),
3249 ),
3250 (
3251 ids.obj_src_clip_use,
3252 FixedIntProp::Base(BaseIntField::SrcClipUse),
3253 ),
3254 (
3255 ids.obj_src_clip_left,
3256 FixedIntProp::EventTotal(ObjectEventTarget::SrcClipLeft),
3257 ),
3258 (
3259 ids.obj_src_clip_top,
3260 FixedIntProp::EventTotal(ObjectEventTarget::SrcClipTop),
3261 ),
3262 (
3263 ids.obj_src_clip_right,
3264 FixedIntProp::EventTotal(ObjectEventTarget::SrcClipRight),
3265 ),
3266 (
3267 ids.obj_src_clip_bottom,
3268 FixedIntProp::EventTotal(ObjectEventTarget::SrcClipBottom),
3269 ),
3270 (ids.obj_alpha, FixedIntProp::Base(BaseIntField::Alpha)),
3271 (ids.obj_tr, FixedIntProp::EventTotal(ObjectEventTarget::Tr)),
3272 (
3273 ids.obj_mono,
3274 FixedIntProp::EventTotal(ObjectEventTarget::Mono),
3275 ),
3276 (
3277 ids.obj_reverse,
3278 FixedIntProp::EventTotal(ObjectEventTarget::Reverse),
3279 ),
3280 (
3281 ids.obj_bright,
3282 FixedIntProp::EventTotal(ObjectEventTarget::Bright),
3283 ),
3284 (
3285 ids.obj_dark,
3286 FixedIntProp::EventTotal(ObjectEventTarget::Dark),
3287 ),
3288 (
3289 ids.obj_color_r,
3290 FixedIntProp::EventTotal(ObjectEventTarget::ColorR),
3291 ),
3292 (
3293 ids.obj_color_g,
3294 FixedIntProp::EventTotal(ObjectEventTarget::ColorG),
3295 ),
3296 (
3297 ids.obj_color_b,
3298 FixedIntProp::EventTotal(ObjectEventTarget::ColorB),
3299 ),
3300 (
3301 ids.obj_color_rate,
3302 FixedIntProp::EventTotal(ObjectEventTarget::ColorRate),
3303 ),
3304 (
3305 ids.obj_color_add_r,
3306 FixedIntProp::EventTotal(ObjectEventTarget::ColorAddR),
3307 ),
3308 (
3309 ids.obj_color_add_g,
3310 FixedIntProp::EventTotal(ObjectEventTarget::ColorAddG),
3311 ),
3312 (
3313 ids.obj_color_add_b,
3314 FixedIntProp::EventTotal(ObjectEventTarget::ColorAddB),
3315 ),
3316 (ids.obj_mask_no, FixedIntProp::Base(BaseIntField::MaskNo)),
3317 (
3318 ids.obj_tonecurve_no,
3319 FixedIntProp::Base(BaseIntField::TonecurveNo),
3320 ),
3321 (ids.obj_light_no, FixedIntProp::Base(BaseIntField::LightNo)),
3322 (ids.obj_fog_use, FixedIntProp::Base(BaseIntField::FogUse)),
3323 (ids.obj_culling, FixedIntProp::Base(BaseIntField::Culling)),
3324 (
3325 ids.obj_alpha_test,
3326 FixedIntProp::Base(BaseIntField::AlphaTest),
3327 ),
3328 (
3329 ids.obj_alpha_blend,
3330 FixedIntProp::Base(BaseIntField::AlphaBlend),
3331 ),
3332 (ids.obj_blend, FixedIntProp::Base(BaseIntField::Blend)),
3333 ]
3334}
3335
3336#[derive(Debug, Default)]
3340pub struct ObjectOpTables {
3341 events: HashMap<i32, ObjectEventTarget>,
3343 props: HashMap<i32, ObjectEventTarget>,
3345 fixed_int_props: HashMap<i32, FixedIntProp>,
3347}
3348
3349impl ObjectOpTables {
3350 pub fn new(ids: &crate::runtime::constants::RuntimeConstants) -> Self {
3351 fn table(pairs: &[(i32, ObjectEventTarget)]) -> HashMap<i32, ObjectEventTarget> {
3354 let mut map = HashMap::default();
3355 for &(id, target) in pairs {
3356 if id != 0 {
3357 map.entry(id).or_insert(target);
3358 }
3359 }
3360 map
3361 }
3362 let mut fixed_int_props = HashMap::default();
3363 for (index, (id, prop)) in fixed_int_prop_pairs(ids).into_iter().enumerate() {
3364 if id != 0 || index == 0 {
3366 fixed_int_props.entry(id).or_insert(prop);
3367 }
3368 }
3369 Self {
3370 fixed_int_props,
3371 events: table(&[
3372 (ids.obj_x_eve, ObjectEventTarget::X),
3373 (ids.obj_y_eve, ObjectEventTarget::Y),
3374 (ids.obj_x_rep_eve, ObjectEventTarget::XRep),
3375 (ids.obj_y_rep_eve, ObjectEventTarget::YRep),
3376 (ids.obj_z_rep_eve, ObjectEventTarget::ZRep),
3377 (ids.obj_tr_eve, ObjectEventTarget::Tr),
3378 (ids.obj_tr_rep_eve, ObjectEventTarget::TrRep),
3379 (ids.obj_patno_eve, ObjectEventTarget::Patno),
3380 (ids.obj_z_eve, ObjectEventTarget::Z),
3381 (ids.obj_center_x_eve, ObjectEventTarget::CenterX),
3382 (ids.obj_center_y_eve, ObjectEventTarget::CenterY),
3383 (ids.obj_center_z_eve, ObjectEventTarget::CenterZ),
3384 (ids.obj_center_rep_x_eve, ObjectEventTarget::CenterRepX),
3385 (ids.obj_center_rep_y_eve, ObjectEventTarget::CenterRepY),
3386 (ids.obj_center_rep_z_eve, ObjectEventTarget::CenterRepZ),
3387 (ids.obj_scale_x_eve, ObjectEventTarget::ScaleX),
3388 (ids.obj_scale_y_eve, ObjectEventTarget::ScaleY),
3389 (ids.obj_scale_z_eve, ObjectEventTarget::ScaleZ),
3390 (ids.obj_rotate_x_eve, ObjectEventTarget::RotateX),
3391 (ids.obj_rotate_y_eve, ObjectEventTarget::RotateY),
3392 (ids.obj_rotate_z_eve, ObjectEventTarget::RotateZ),
3393 (ids.obj_clip_left_eve, ObjectEventTarget::ClipLeft),
3394 (ids.obj_clip_top_eve, ObjectEventTarget::ClipTop),
3395 (ids.obj_clip_right_eve, ObjectEventTarget::ClipRight),
3396 (ids.obj_clip_bottom_eve, ObjectEventTarget::ClipBottom),
3397 (ids.obj_src_clip_left_eve, ObjectEventTarget::SrcClipLeft),
3398 (ids.obj_src_clip_top_eve, ObjectEventTarget::SrcClipTop),
3399 (ids.obj_src_clip_right_eve, ObjectEventTarget::SrcClipRight),
3400 (
3401 ids.obj_src_clip_bottom_eve,
3402 ObjectEventTarget::SrcClipBottom,
3403 ),
3404 (ids.obj_mono_eve, ObjectEventTarget::Mono),
3405 (ids.obj_reverse_eve, ObjectEventTarget::Reverse),
3406 (ids.obj_bright_eve, ObjectEventTarget::Bright),
3407 (ids.obj_dark_eve, ObjectEventTarget::Dark),
3408 (ids.obj_color_rate_eve, ObjectEventTarget::ColorRate),
3409 (ids.obj_color_add_r_eve, ObjectEventTarget::ColorAddR),
3410 (ids.obj_color_add_g_eve, ObjectEventTarget::ColorAddG),
3411 (ids.obj_color_add_b_eve, ObjectEventTarget::ColorAddB),
3412 (ids.obj_color_r_eve, ObjectEventTarget::ColorR),
3413 (ids.obj_color_g_eve, ObjectEventTarget::ColorG),
3414 (ids.obj_color_b_eve, ObjectEventTarget::ColorB),
3415 ]),
3416 props: table(&[
3417 (ids.obj_patno, ObjectEventTarget::Patno),
3418 (ids.obj_x, ObjectEventTarget::X),
3419 (ids.obj_y, ObjectEventTarget::Y),
3420 (ids.obj_z, ObjectEventTarget::Z),
3421 (ids.obj_center_x, ObjectEventTarget::CenterX),
3422 (ids.obj_center_y, ObjectEventTarget::CenterY),
3423 (ids.obj_center_z, ObjectEventTarget::CenterZ),
3424 (ids.obj_center_rep_x, ObjectEventTarget::CenterRepX),
3425 (ids.obj_center_rep_y, ObjectEventTarget::CenterRepY),
3426 (ids.obj_center_rep_z, ObjectEventTarget::CenterRepZ),
3427 (ids.obj_scale_x, ObjectEventTarget::ScaleX),
3428 (ids.obj_scale_y, ObjectEventTarget::ScaleY),
3429 (ids.obj_scale_z, ObjectEventTarget::ScaleZ),
3430 (ids.obj_rotate_x, ObjectEventTarget::RotateX),
3431 (ids.obj_rotate_y, ObjectEventTarget::RotateY),
3432 (ids.obj_rotate_z, ObjectEventTarget::RotateZ),
3433 (ids.obj_clip_left, ObjectEventTarget::ClipLeft),
3434 (ids.obj_clip_top, ObjectEventTarget::ClipTop),
3435 (ids.obj_clip_right, ObjectEventTarget::ClipRight),
3436 (ids.obj_clip_bottom, ObjectEventTarget::ClipBottom),
3437 (ids.obj_src_clip_left, ObjectEventTarget::SrcClipLeft),
3438 (ids.obj_src_clip_top, ObjectEventTarget::SrcClipTop),
3439 (ids.obj_src_clip_right, ObjectEventTarget::SrcClipRight),
3440 (ids.obj_src_clip_bottom, ObjectEventTarget::SrcClipBottom),
3441 (ids.obj_tr, ObjectEventTarget::Tr),
3442 (ids.obj_mono, ObjectEventTarget::Mono),
3443 (ids.obj_reverse, ObjectEventTarget::Reverse),
3444 (ids.obj_bright, ObjectEventTarget::Bright),
3445 (ids.obj_dark, ObjectEventTarget::Dark),
3446 (ids.obj_color_r, ObjectEventTarget::ColorR),
3447 (ids.obj_color_g, ObjectEventTarget::ColorG),
3448 (ids.obj_color_b, ObjectEventTarget::ColorB),
3449 (ids.obj_color_rate, ObjectEventTarget::ColorRate),
3450 (ids.obj_color_add_r, ObjectEventTarget::ColorAddR),
3451 (ids.obj_color_add_g, ObjectEventTarget::ColorAddG),
3452 (ids.obj_color_add_b, ObjectEventTarget::ColorAddB),
3453 ]),
3454 }
3455 }
3456
3457 pub fn fixed_int_prop(&self, op: i32) -> Option<FixedIntProp> {
3458 self.fixed_int_props.get(&op).copied()
3459 }
3460
3461 pub fn event_target(&self, op: i32) -> ObjectEventTarget {
3462 self.events
3463 .get(&op)
3464 .copied()
3465 .unwrap_or(ObjectEventTarget::Unknown)
3466 }
3467
3468 pub fn prop_target(&self, op: i32) -> ObjectEventTarget {
3470 match self.props.get(&op) {
3471 Some(&target) => target,
3472 None => self.event_target(op),
3473 }
3474 }
3475}
3476
3477#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3478pub enum ObjectEventTarget {
3479 X,
3480 Y,
3481 XRep,
3482 YRep,
3483 ZRep,
3484 Alpha,
3485 Patno,
3486 Order,
3487 Layer,
3488 Z,
3489 CenterX,
3490 CenterY,
3491 CenterZ,
3492 CenterRepX,
3493 CenterRepY,
3494 CenterRepZ,
3495 ScaleX,
3496 ScaleY,
3497 ScaleZ,
3498 RotateX,
3499 RotateY,
3500 RotateZ,
3501 TrRep,
3502 ClipLeft,
3503 ClipTop,
3504 ClipRight,
3505 ClipBottom,
3506 SrcClipLeft,
3507 SrcClipTop,
3508 SrcClipRight,
3509 SrcClipBottom,
3510 Tr,
3511 Mono,
3512 Reverse,
3513 Bright,
3514 Dark,
3515 ColorRate,
3516 ColorAddR,
3517 ColorAddG,
3518 ColorAddB,
3519 ColorR,
3520 ColorG,
3521 ColorB,
3522 Unknown,
3523}
3524
3525#[derive(Debug, Clone)]
3526pub struct ObjectButtonState {
3527 pub enabled: bool,
3528 pub button_no: i64,
3529 pub group_no: i64,
3530 pub cut_no: i64,
3536 pub group_idx_override: Option<usize>,
3538 pub action_no: i64,
3539 pub se_no: i64,
3540 pub sys_type: i64,
3541 pub sys_type_opt: i64,
3542 pub mode: i64,
3543 pub push_keep: bool,
3544 pub alpha_test: bool,
3545 pub state: i64,
3547 pub hit: bool,
3548 pub pushed: bool,
3549
3550 pub decided_action_scn_name: String,
3552 pub decided_action_cmd_name: String,
3553 pub decided_action_z_no: i64,
3554 pub last_hit: bool,
3556 pub last_pushed: bool,
3558}
3559
3560impl Default for ObjectButtonState {
3561 fn default() -> Self {
3562 Self {
3563 enabled: false,
3564 button_no: 0,
3565 group_no: -1,
3566 cut_no: 0,
3567 group_idx_override: None,
3568 action_no: -1,
3569 se_no: -1,
3570 sys_type: 0,
3571 sys_type_opt: 0,
3572 mode: 0,
3573 push_keep: false,
3574 alpha_test: false,
3575 state: 0,
3576 hit: false,
3577 pushed: false,
3578 decided_action_scn_name: String::new(),
3579 decided_action_cmd_name: String::new(),
3580 decided_action_z_no: -1,
3581 last_hit: false,
3582 last_pushed: false,
3583 }
3584 }
3585}
3586
3587impl ObjectButtonState {
3588 pub fn clear(&mut self) {
3589 *self = Self::default();
3590 }
3591
3592 pub fn group_idx(&self) -> Option<usize> {
3593 if !self.enabled {
3594 return None;
3595 }
3596 if let Some(i) = self.group_idx_override {
3597 return Some(i);
3598 }
3599 if self.group_no < 0 {
3600 return None;
3601 }
3602 Some(self.group_no as usize)
3603 }
3604
3605 pub fn is_disabled(&self) -> bool {
3606 self.enabled && self.state == 4
3607 }
3608}
3609
3610#[derive(Debug, Clone)]
3611pub struct ObjectStringParam {
3612 pub moji_size: i64,
3613 pub moji_space_x: i64,
3614 pub moji_space_y: i64,
3615 pub moji_cnt: i64,
3616 pub moji_color: i64,
3617 pub shadow_color: i64,
3618 pub fuchi_color: i64,
3619 pub shadow_mode: i64,
3621}
3622
3623impl Default for ObjectStringParam {
3624 fn default() -> Self {
3625 Self {
3626 moji_size: 12,
3627 moji_space_x: 0,
3628 moji_space_y: 0,
3629 moji_cnt: 0,
3630 moji_color: 0,
3631 shadow_color: 1,
3632 fuchi_color: 1,
3633 shadow_mode: -1,
3634 }
3635 }
3636}
3637
3638#[derive(Debug, Default, Clone)]
3639pub struct ObjectNumberParam {
3640 pub keta_max: i64,
3641 pub disp_zero: i64,
3642 pub disp_sign: i64,
3643 pub tumeru_sign: i64,
3644 pub space_mod: i64,
3645 pub space: i64,
3646}
3647
3648#[derive(Debug, Default, Clone)]
3649pub struct ObjectWeatherParam {
3650 pub weather_type: i64,
3652 pub cnt: i64,
3653 pub pat_mode: i64,
3654 pub pat_no_00: i64,
3655 pub pat_no_01: i64,
3656 pub pat_time: i64,
3657 pub move_time_x: i64,
3658 pub move_time_y: i64,
3659 pub sin_time_x: i64,
3660 pub sin_power_x: i64,
3661 pub sin_time_y: i64,
3662 pub sin_power_y: i64,
3663 pub center_x: i64,
3664 pub center_y: i64,
3665 pub appear_range: i64,
3666 pub move_time: i64,
3667 pub center_rotate: i64,
3668 pub zoom_min: i64,
3669 pub zoom_max: i64,
3670 pub scale_x: i64,
3671 pub scale_y: i64,
3672 pub active_time: i64,
3673 pub real_time_flag: bool,
3674}
3675
3676#[derive(Debug, Default, Clone)]
3677pub struct ObjectWeatherWorkSub {
3678 pub state: i64,
3679 pub state_cur_time: i64,
3680 pub state_time_len: i64,
3681 pub move_start_pos_x: i64,
3682 pub move_start_pos_y: i64,
3683 pub move_start_distance: i64,
3684 pub move_start_degree: i64,
3685 pub move_time_x: i64,
3686 pub move_time_y: i64,
3687 pub move_cur_time: i64,
3688 pub sin_time_x: i64,
3689 pub sin_time_y: i64,
3690 pub sin_power_x: i64,
3691 pub sin_power_y: i64,
3692 pub sin_cur_time: i64,
3693 pub center_rotate: i64,
3694 pub zoom_min: i64,
3695 pub zoom_max: i64,
3696 pub scale_x: i64,
3697 pub scale_y: i64,
3698 pub active_time_len: i64,
3699 pub real_time_flag: bool,
3700 pub restruct_flag: bool,
3701}
3702
3703#[derive(Debug, Clone)]
3704pub struct ObjectWeatherWorkState {
3705 pub cnt_max: usize,
3706 pub sub: Vec<ObjectWeatherWorkSub>,
3707 rand_seed: u32,
3708}
3709
3710impl Default for ObjectWeatherWorkState {
3711 fn default() -> Self {
3712 Self {
3713 cnt_max: 0,
3714 sub: Vec::new(),
3715 rand_seed: 0x1234_abcd,
3716 }
3717 }
3718}
3719
3720impl ObjectWeatherWorkState {
3721 fn next_rand(&mut self) -> i64 {
3722 self.rand_seed = self.rand_seed.wrapping_mul(1103515245).wrapping_add(12345);
3723 ((self.rand_seed >> 16) & 0x7fff) as i64
3724 }
3725
3726 pub fn rand_mod(&mut self, modulo: i64) -> i64 {
3727 if modulo <= 0 {
3728 0
3729 } else {
3730 self.next_rand() % modulo
3731 }
3732 }
3733}
3734
3735#[derive(Debug, Clone)]
3736pub struct ObjectMovieState {
3737 pub loop_flag: bool,
3738 pub auto_free_flag: bool,
3739 pub real_time_flag: bool,
3740 pub pause_flag: bool,
3741
3742 pub timer_ms: u64,
3744 pub total_ms: Option<u64>,
3746 pub width: u32,
3750 pub height: u32,
3751
3752 pub playing: bool,
3753 pub last_tick: Option<Instant>,
3754 pub last_frame_idx: Option<usize>,
3755 pub audio_id: Option<u64>,
3756 pub audio_started_once: bool,
3757 pub frame_image_ids: [Option<ImageHandle>; 2],
3762 pub frame_image_cursor: usize,
3763 pub just_finished: bool,
3764 pub just_looped: bool,
3765 pub seeked: bool,
3766}
3767
3768impl Default for ObjectMovieState {
3769 fn default() -> Self {
3770 Self {
3771 loop_flag: false,
3772 auto_free_flag: true,
3773 real_time_flag: true,
3774 pause_flag: false,
3775 timer_ms: 0,
3776 total_ms: None,
3777 width: 0,
3778 height: 0,
3779 playing: false,
3780 last_tick: None,
3781 last_frame_idx: None,
3782 audio_id: None,
3783 audio_started_once: false,
3784 frame_image_ids: [None, None],
3785 frame_image_cursor: 0,
3786 just_finished: false,
3787 just_looped: false,
3788 seeked: false,
3789 }
3790 }
3791}
3792
3793impl ObjectMovieState {
3794 pub fn reset(&mut self) {
3795 *self = Self::default();
3796 }
3797
3798 pub fn start(
3799 &mut self,
3800 total_ms: Option<u64>,
3801 loop_flag: bool,
3802 auto_free_flag: bool,
3803 real_time_flag: bool,
3804 ready_only: bool,
3805 ) {
3806 self.total_ms = total_ms;
3807 self.loop_flag = loop_flag;
3808 self.auto_free_flag = auto_free_flag;
3809 self.real_time_flag = real_time_flag;
3810 self.pause_flag = ready_only;
3811 self.timer_ms = 0;
3812 self.playing = !ready_only;
3813 self.last_tick = Some(Instant::now());
3814 self.last_frame_idx = None;
3815 self.audio_id = None;
3816 self.audio_started_once = false;
3817 self.frame_image_ids = [None, None];
3818 self.frame_image_cursor = 0;
3819 self.just_finished = false;
3820 self.just_looped = false;
3821 self.seeked = false;
3822 }
3823
3824 pub fn tick(&mut self, past_game_time: i32, past_real_time: i32) {
3825 self.just_finished = false;
3826 self.just_looped = false;
3827 if !self.playing || self.pause_flag || self.audio_id.is_some() {
3828 return;
3832 }
3833 let add = if self.real_time_flag {
3834 past_real_time.max(0) as u64
3835 } else {
3836 past_game_time.max(0) as u64
3837 };
3838 if add == 0 {
3839 return;
3840 }
3841 self.timer_ms = self.timer_ms.saturating_add(add);
3842 if let Some(total) = self.total_ms
3843 && total > 0
3844 && self.timer_ms >= total
3845 {
3846 if self.loop_flag {
3847 self.timer_ms %= total;
3848 self.just_looped = true;
3849 } else {
3850 self.playing = false;
3851 self.just_finished = true;
3852 }
3853 }
3854 }
3855
3856 pub fn seek(&mut self, time_ms: u64) {
3857 self.timer_ms = time_ms;
3860 self.last_tick = Some(Instant::now());
3861 self.last_frame_idx = None;
3862 self.audio_started_once = false;
3863 self.seeked = true;
3864 }
3865
3866 pub fn get_seek_time(&self) -> u64 {
3867 if let Some(total) = self.total_ms
3868 && total > 0
3869 {
3870 return self.timer_ms % total;
3871 }
3872 0
3873 }
3874
3875 pub fn check_movie(&self) -> bool {
3876 self.playing
3877 }
3878}
3879
3880#[derive(Debug, Clone)]
3881pub struct ObjectEmoteParam {
3882 pub width: i64,
3883 pub height: i64,
3884 pub file_name: Option<String>,
3885 pub timeline_names: [String; 8],
3886 pub timeline_options: [i64; 8],
3887 pub koe_chara_no: i64,
3888 pub koe_mouth_volume: i64,
3889 pub rep_x: i64,
3890 pub rep_y: i64,
3891 pub runtime: Option<crate::emote::SiglusEmoteRuntime>,
3892}
3893
3894impl Default for ObjectEmoteParam {
3895 fn default() -> Self {
3896 Self {
3897 width: 0,
3898 height: 0,
3899 file_name: None,
3900 timeline_names: std::array::from_fn(|_| String::new()),
3901 timeline_options: [0; 8],
3902 koe_chara_no: -1,
3903 koe_mouth_volume: 0,
3904 rep_x: 0,
3905 rep_y: 0,
3906 runtime: None,
3907 }
3908 }
3909}
3910
3911impl ObjectEmoteParam {
3912 pub fn clear_runtime(&mut self) {
3913 self.runtime = None;
3914 }
3915
3916 pub fn tick(&mut self, past_game_time: i32) {
3917 if let Some(runtime) = self.runtime.as_mut()
3918 && let Err(err) = runtime.progress_ms(past_game_time)
3919 {
3920 log::error!("Emote Progress failed: {err:#}");
3921 }
3922 }
3923
3924 pub fn is_animating(&self) -> bool {
3925 if let Some(runtime) = self.runtime.as_ref() {
3926 return runtime.is_animating();
3927 }
3928 false
3929 }
3930
3931 pub fn clone_player_for_object(&mut self) {
3932 if let Some(runtime) = self.runtime.as_ref() {
3933 self.runtime = Some(runtime.clone_for_object());
3934 }
3935 }
3936}
3937
3938#[derive(Debug, Clone, Default)]
3939pub struct ObjectFrameActionState {
3940 pub scn_name: String,
3941 pub cmd_name: String,
3942 pub counter: Counter,
3943 pub end_time: i64,
3944 pub real_time_flag: bool,
3945 pub end_flag: bool,
3946 pub args: Vec<crate::runtime::Value>,
3947}
3948
3949impl ObjectFrameActionState {
3950 pub fn reinit_without_finish(&mut self) {
3953 let end_time = self.end_time;
3954 *self = Self::default();
3955 self.end_time = end_time;
3956 }
3957}
3958
3959#[derive(Debug, Clone)]
3960pub struct ObjectBaseState {
3961 pub wipe_copy: i64,
3962 pub wipe_erase: i64,
3963 pub click_disable: i64,
3964 pub disp: i64,
3965 pub patno: i64,
3966 pub world: i64,
3967 pub order: i64,
3968 pub layer: i64,
3969 pub x: i64,
3970 pub y: i64,
3971 pub z: i64,
3972 pub center_x: i64,
3973 pub center_y: i64,
3974 pub center_z: i64,
3975 pub center_rep_x: i64,
3976 pub center_rep_y: i64,
3977 pub center_rep_z: i64,
3978 pub scale_x: i64,
3979 pub scale_y: i64,
3980 pub scale_z: i64,
3981 pub rotate_x: i64,
3982 pub rotate_y: i64,
3983 pub rotate_z: i64,
3984 pub clip_use: i64,
3985 pub clip_left: i64,
3986 pub clip_top: i64,
3987 pub clip_right: i64,
3988 pub clip_bottom: i64,
3989 pub src_clip_use: i64,
3990 pub src_clip_left: i64,
3991 pub src_clip_top: i64,
3992 pub src_clip_right: i64,
3993 pub src_clip_bottom: i64,
3994 pub alpha: i64,
3995 pub tr: i64,
3996 pub mono: i64,
3997 pub reverse: i64,
3998 pub bright: i64,
3999 pub dark: i64,
4000 pub color_r: i64,
4001 pub color_g: i64,
4002 pub color_b: i64,
4003 pub color_rate: i64,
4004 pub color_add_r: i64,
4005 pub color_add_g: i64,
4006 pub color_add_b: i64,
4007 pub mask_no: i64,
4008 pub tonecurve_no: i64,
4009 pub light_no: i64,
4010 pub fog_use: i64,
4011 pub culling: i64,
4012 pub alpha_test: i64,
4013 pub alpha_blend: i64,
4014 pub blend: i64,
4015 pub child_sort_type: i64,
4016 pub no_event_hint: bool,
4017}
4018
4019impl Default for ObjectBaseState {
4020 fn default() -> Self {
4021 Self {
4022 wipe_copy: 0,
4023 wipe_erase: 0,
4024 click_disable: 0,
4025 disp: 0,
4026 patno: 0,
4027 world: -1,
4028 order: 0,
4029 layer: 0,
4030 x: 0,
4031 y: 0,
4032 z: 0,
4033 center_x: 0,
4034 center_y: 0,
4035 center_z: 0,
4036 center_rep_x: 0,
4037 center_rep_y: 0,
4038 center_rep_z: 0,
4039 scale_x: 1000,
4040 scale_y: 1000,
4041 scale_z: 1000,
4042 rotate_x: 0,
4043 rotate_y: 0,
4044 rotate_z: 0,
4045 clip_use: 0,
4046 clip_left: 0,
4047 clip_top: 0,
4048 clip_right: 0,
4049 clip_bottom: 0,
4050 src_clip_use: 0,
4051 src_clip_left: 0,
4052 src_clip_top: 0,
4053 src_clip_right: 0,
4054 src_clip_bottom: 0,
4055 alpha: 255,
4056 tr: 255,
4057 mono: 0,
4058 reverse: 0,
4059 bright: 0,
4060 dark: 0,
4061 color_r: 0,
4062 color_g: 0,
4063 color_b: 0,
4064 color_rate: 0,
4065 color_add_r: 0,
4066 color_add_g: 0,
4067 color_add_b: 0,
4068 mask_no: -1,
4069 tonecurve_no: -1,
4070 light_no: -1,
4071 fog_use: 0,
4072 culling: 0,
4073 alpha_test: 1,
4074 alpha_blend: 1,
4075 blend: 0,
4076 child_sort_type: 0,
4077 no_event_hint: false,
4078 }
4079 }
4080}
4081
4082#[derive(Debug, Clone)]
4083pub struct ObjectPropEvents {
4084 pub patno: IntEvent,
4085 pub x: IntEvent,
4086 pub y: IntEvent,
4087 pub z: IntEvent,
4088 pub center_x: IntEvent,
4089 pub center_y: IntEvent,
4090 pub center_z: IntEvent,
4091 pub center_rep_x: IntEvent,
4092 pub center_rep_y: IntEvent,
4093 pub center_rep_z: IntEvent,
4094 pub scale_x: IntEvent,
4095 pub scale_y: IntEvent,
4096 pub scale_z: IntEvent,
4097 pub rotate_x: IntEvent,
4098 pub rotate_y: IntEvent,
4099 pub rotate_z: IntEvent,
4100 pub clip_left: IntEvent,
4101 pub clip_top: IntEvent,
4102 pub clip_right: IntEvent,
4103 pub clip_bottom: IntEvent,
4104 pub src_clip_left: IntEvent,
4105 pub src_clip_top: IntEvent,
4106 pub src_clip_right: IntEvent,
4107 pub src_clip_bottom: IntEvent,
4108 pub tr: IntEvent,
4109 pub mono: IntEvent,
4110 pub reverse: IntEvent,
4111 pub bright: IntEvent,
4112 pub dark: IntEvent,
4113 pub color_r: IntEvent,
4114 pub color_g: IntEvent,
4115 pub color_b: IntEvent,
4116 pub color_rate: IntEvent,
4117 pub color_add_r: IntEvent,
4118 pub color_add_g: IntEvent,
4119 pub color_add_b: IntEvent,
4120}
4121
4122impl Default for ObjectPropEvents {
4123 fn default() -> Self {
4124 Self {
4125 patno: IntEvent::new(0),
4126 x: IntEvent::new(0),
4127 y: IntEvent::new(0),
4128 z: IntEvent::new(0),
4129 center_x: IntEvent::new(0),
4130 center_y: IntEvent::new(0),
4131 center_z: IntEvent::new(0),
4132 center_rep_x: IntEvent::new(0),
4133 center_rep_y: IntEvent::new(0),
4134 center_rep_z: IntEvent::new(0),
4135 scale_x: IntEvent::new(1000),
4136 scale_y: IntEvent::new(1000),
4137 scale_z: IntEvent::new(1000),
4138 rotate_x: IntEvent::new(0),
4139 rotate_y: IntEvent::new(0),
4140 rotate_z: IntEvent::new(0),
4141 clip_left: IntEvent::new(0),
4142 clip_top: IntEvent::new(0),
4143 clip_right: IntEvent::new(0),
4144 clip_bottom: IntEvent::new(0),
4145 src_clip_left: IntEvent::new(0),
4146 src_clip_top: IntEvent::new(0),
4147 src_clip_right: IntEvent::new(0),
4148 src_clip_bottom: IntEvent::new(0),
4149 tr: IntEvent::new(255),
4150 mono: IntEvent::new(0),
4151 reverse: IntEvent::new(0),
4152 bright: IntEvent::new(0),
4153 dark: IntEvent::new(0),
4154 color_r: IntEvent::new(0),
4155 color_g: IntEvent::new(0),
4156 color_b: IntEvent::new(0),
4157 color_rate: IntEvent::new(0),
4158 color_add_r: IntEvent::new(0),
4159 color_add_g: IntEvent::new(0),
4160 color_add_b: IntEvent::new(0),
4161 }
4162 }
4163}
4164
4165impl ObjectPropEvents {
4166 pub fn clear(&mut self) {
4167 self.patno.reinit();
4168 self.x.reinit();
4169 self.y.reinit();
4170 self.z.reinit();
4171 self.center_x.reinit();
4172 self.center_y.reinit();
4173 self.center_z.reinit();
4174 self.center_rep_x.reinit();
4175 self.center_rep_y.reinit();
4176 self.center_rep_z.reinit();
4177 self.scale_x.reinit();
4178 self.scale_y.reinit();
4179 self.scale_z.reinit();
4180 self.rotate_x.reinit();
4181 self.rotate_y.reinit();
4182 self.rotate_z.reinit();
4183 self.clip_left.reinit();
4184 self.clip_top.reinit();
4185 self.clip_right.reinit();
4186 self.clip_bottom.reinit();
4187 self.src_clip_left.reinit();
4188 self.src_clip_top.reinit();
4189 self.src_clip_right.reinit();
4190 self.src_clip_bottom.reinit();
4191 self.tr.reinit();
4192 self.mono.reinit();
4193 self.reverse.reinit();
4194 self.bright.reinit();
4195 self.dark.reinit();
4196 self.color_r.reinit();
4197 self.color_g.reinit();
4198 self.color_b.reinit();
4199 self.color_rate.reinit();
4200 self.color_add_r.reinit();
4201 self.color_add_g.reinit();
4202 self.color_add_b.reinit();
4203 }
4204
4205 pub fn update_time(&mut self, past_game_time: i32, past_real_time: i32) {
4206 self.patno.update_time(past_game_time, past_real_time);
4207 self.x.update_time(past_game_time, past_real_time);
4208 self.y.update_time(past_game_time, past_real_time);
4209 self.z.update_time(past_game_time, past_real_time);
4210 self.center_x.update_time(past_game_time, past_real_time);
4211 self.center_y.update_time(past_game_time, past_real_time);
4212 self.center_z.update_time(past_game_time, past_real_time);
4213 self.center_rep_x
4214 .update_time(past_game_time, past_real_time);
4215 self.center_rep_y
4216 .update_time(past_game_time, past_real_time);
4217 self.center_rep_z
4218 .update_time(past_game_time, past_real_time);
4219 self.scale_x.update_time(past_game_time, past_real_time);
4220 self.scale_y.update_time(past_game_time, past_real_time);
4221 self.scale_z.update_time(past_game_time, past_real_time);
4222 self.rotate_x.update_time(past_game_time, past_real_time);
4223 self.rotate_y.update_time(past_game_time, past_real_time);
4224 self.rotate_z.update_time(past_game_time, past_real_time);
4225 self.clip_left.update_time(past_game_time, past_real_time);
4226 self.clip_top.update_time(past_game_time, past_real_time);
4227 self.clip_right.update_time(past_game_time, past_real_time);
4228 self.clip_bottom.update_time(past_game_time, past_real_time);
4229 self.src_clip_left
4230 .update_time(past_game_time, past_real_time);
4231 self.src_clip_top
4232 .update_time(past_game_time, past_real_time);
4233 self.src_clip_right
4234 .update_time(past_game_time, past_real_time);
4235 self.src_clip_bottom
4236 .update_time(past_game_time, past_real_time);
4237 self.tr.update_time(past_game_time, past_real_time);
4238 self.mono.update_time(past_game_time, past_real_time);
4239 self.reverse.update_time(past_game_time, past_real_time);
4240 self.bright.update_time(past_game_time, past_real_time);
4241 self.dark.update_time(past_game_time, past_real_time);
4242 self.color_r.update_time(past_game_time, past_real_time);
4243 self.color_g.update_time(past_game_time, past_real_time);
4244 self.color_b.update_time(past_game_time, past_real_time);
4245 self.color_rate.update_time(past_game_time, past_real_time);
4246 self.color_add_r.update_time(past_game_time, past_real_time);
4247 self.color_add_g.update_time(past_game_time, past_real_time);
4248 self.color_add_b.update_time(past_game_time, past_real_time);
4249 }
4250
4251 pub fn frame(&mut self) {
4252 self.patno.frame();
4253 self.x.frame();
4254 self.y.frame();
4255 self.z.frame();
4256 self.center_x.frame();
4257 self.center_y.frame();
4258 self.center_z.frame();
4259 self.center_rep_x.frame();
4260 self.center_rep_y.frame();
4261 self.center_rep_z.frame();
4262 self.scale_x.frame();
4263 self.scale_y.frame();
4264 self.scale_z.frame();
4265 self.rotate_x.frame();
4266 self.rotate_y.frame();
4267 self.rotate_z.frame();
4268 self.clip_left.frame();
4269 self.clip_top.frame();
4270 self.clip_right.frame();
4271 self.clip_bottom.frame();
4272 self.src_clip_left.frame();
4273 self.src_clip_top.frame();
4274 self.src_clip_right.frame();
4275 self.src_clip_bottom.frame();
4276 self.tr.frame();
4277 self.mono.frame();
4278 self.reverse.frame();
4279 self.bright.frame();
4280 self.dark.frame();
4281 self.color_r.frame();
4282 self.color_g.frame();
4283 self.color_b.frame();
4284 self.color_rate.frame();
4285 self.color_add_r.frame();
4286 self.color_add_g.frame();
4287 self.color_add_b.frame();
4288 }
4289
4290 pub fn tick(&mut self, delta: i32) {
4291 self.update_time(delta, delta);
4292 self.frame();
4293 }
4294
4295 pub fn any_active(&self) -> bool {
4296 self.patno.check_event()
4297 || self.x.check_event()
4298 || self.y.check_event()
4299 || self.z.check_event()
4300 || self.center_x.check_event()
4301 || self.center_y.check_event()
4302 || self.center_z.check_event()
4303 || self.center_rep_x.check_event()
4304 || self.center_rep_y.check_event()
4305 || self.center_rep_z.check_event()
4306 || self.scale_x.check_event()
4307 || self.scale_y.check_event()
4308 || self.scale_z.check_event()
4309 || self.rotate_x.check_event()
4310 || self.rotate_y.check_event()
4311 || self.rotate_z.check_event()
4312 || self.clip_left.check_event()
4313 || self.clip_top.check_event()
4314 || self.clip_right.check_event()
4315 || self.clip_bottom.check_event()
4316 || self.src_clip_left.check_event()
4317 || self.src_clip_top.check_event()
4318 || self.src_clip_right.check_event()
4319 || self.src_clip_bottom.check_event()
4320 || self.tr.check_event()
4321 || self.mono.check_event()
4322 || self.reverse.check_event()
4323 || self.bright.check_event()
4324 || self.dark.check_event()
4325 || self.color_r.check_event()
4326 || self.color_g.check_event()
4327 || self.color_b.check_event()
4328 || self.color_rate.check_event()
4329 || self.color_add_r.check_event()
4330 || self.color_add_g.check_event()
4331 || self.color_add_b.check_event()
4332 }
4333
4334 pub fn end_all(&mut self) {
4335 self.patno.end_event();
4336 self.x.end_event();
4337 self.y.end_event();
4338 self.z.end_event();
4339 self.center_x.end_event();
4340 self.center_y.end_event();
4341 self.center_z.end_event();
4342 self.center_rep_x.end_event();
4343 self.center_rep_y.end_event();
4344 self.center_rep_z.end_event();
4345 self.scale_x.end_event();
4346 self.scale_y.end_event();
4347 self.scale_z.end_event();
4348 self.rotate_x.end_event();
4349 self.rotate_y.end_event();
4350 self.rotate_z.end_event();
4351 self.clip_left.end_event();
4352 self.clip_top.end_event();
4353 self.clip_right.end_event();
4354 self.clip_bottom.end_event();
4355 self.src_clip_left.end_event();
4356 self.src_clip_top.end_event();
4357 self.src_clip_right.end_event();
4358 self.src_clip_bottom.end_event();
4359 self.tr.end_event();
4360 self.mono.end_event();
4361 self.reverse.end_event();
4362 self.bright.end_event();
4363 self.dark.end_event();
4364 self.color_r.end_event();
4365 self.color_g.end_event();
4366 self.color_b.end_event();
4367 self.color_rate.end_event();
4368 self.color_add_r.end_event();
4369 self.color_add_g.end_event();
4370 self.color_add_b.end_event();
4371 }
4372
4373 pub fn get(&self, target: ObjectEventTarget) -> Option<&IntEvent> {
4374 match target {
4375 ObjectEventTarget::Patno => Some(&self.patno),
4376 ObjectEventTarget::X => Some(&self.x),
4377 ObjectEventTarget::Y => Some(&self.y),
4378 ObjectEventTarget::Z => Some(&self.z),
4379 ObjectEventTarget::CenterX => Some(&self.center_x),
4380 ObjectEventTarget::CenterY => Some(&self.center_y),
4381 ObjectEventTarget::CenterZ => Some(&self.center_z),
4382 ObjectEventTarget::CenterRepX => Some(&self.center_rep_x),
4383 ObjectEventTarget::CenterRepY => Some(&self.center_rep_y),
4384 ObjectEventTarget::CenterRepZ => Some(&self.center_rep_z),
4385 ObjectEventTarget::ScaleX => Some(&self.scale_x),
4386 ObjectEventTarget::ScaleY => Some(&self.scale_y),
4387 ObjectEventTarget::ScaleZ => Some(&self.scale_z),
4388 ObjectEventTarget::RotateX => Some(&self.rotate_x),
4389 ObjectEventTarget::RotateY => Some(&self.rotate_y),
4390 ObjectEventTarget::RotateZ => Some(&self.rotate_z),
4391 ObjectEventTarget::ClipLeft => Some(&self.clip_left),
4392 ObjectEventTarget::ClipTop => Some(&self.clip_top),
4393 ObjectEventTarget::ClipRight => Some(&self.clip_right),
4394 ObjectEventTarget::ClipBottom => Some(&self.clip_bottom),
4395 ObjectEventTarget::SrcClipLeft => Some(&self.src_clip_left),
4396 ObjectEventTarget::SrcClipTop => Some(&self.src_clip_top),
4397 ObjectEventTarget::SrcClipRight => Some(&self.src_clip_right),
4398 ObjectEventTarget::SrcClipBottom => Some(&self.src_clip_bottom),
4399 ObjectEventTarget::Tr => Some(&self.tr),
4400 ObjectEventTarget::Mono => Some(&self.mono),
4401 ObjectEventTarget::Reverse => Some(&self.reverse),
4402 ObjectEventTarget::Bright => Some(&self.bright),
4403 ObjectEventTarget::Dark => Some(&self.dark),
4404 ObjectEventTarget::ColorR => Some(&self.color_r),
4405 ObjectEventTarget::ColorG => Some(&self.color_g),
4406 ObjectEventTarget::ColorB => Some(&self.color_b),
4407 ObjectEventTarget::ColorRate => Some(&self.color_rate),
4408 ObjectEventTarget::ColorAddR => Some(&self.color_add_r),
4409 ObjectEventTarget::ColorAddG => Some(&self.color_add_g),
4410 ObjectEventTarget::ColorAddB => Some(&self.color_add_b),
4411 ObjectEventTarget::XRep
4412 | ObjectEventTarget::YRep
4413 | ObjectEventTarget::ZRep
4414 | ObjectEventTarget::TrRep
4415 | ObjectEventTarget::Alpha
4416 | ObjectEventTarget::Order
4417 | ObjectEventTarget::Layer
4418 | ObjectEventTarget::Unknown => None,
4419 }
4420 }
4421
4422 pub fn get_mut(&mut self, target: ObjectEventTarget) -> Option<&mut IntEvent> {
4423 match target {
4424 ObjectEventTarget::Patno => Some(&mut self.patno),
4425 ObjectEventTarget::X => Some(&mut self.x),
4426 ObjectEventTarget::Y => Some(&mut self.y),
4427 ObjectEventTarget::Z => Some(&mut self.z),
4428 ObjectEventTarget::CenterX => Some(&mut self.center_x),
4429 ObjectEventTarget::CenterY => Some(&mut self.center_y),
4430 ObjectEventTarget::CenterZ => Some(&mut self.center_z),
4431 ObjectEventTarget::CenterRepX => Some(&mut self.center_rep_x),
4432 ObjectEventTarget::CenterRepY => Some(&mut self.center_rep_y),
4433 ObjectEventTarget::CenterRepZ => Some(&mut self.center_rep_z),
4434 ObjectEventTarget::ScaleX => Some(&mut self.scale_x),
4435 ObjectEventTarget::ScaleY => Some(&mut self.scale_y),
4436 ObjectEventTarget::ScaleZ => Some(&mut self.scale_z),
4437 ObjectEventTarget::RotateX => Some(&mut self.rotate_x),
4438 ObjectEventTarget::RotateY => Some(&mut self.rotate_y),
4439 ObjectEventTarget::RotateZ => Some(&mut self.rotate_z),
4440 ObjectEventTarget::ClipLeft => Some(&mut self.clip_left),
4441 ObjectEventTarget::ClipTop => Some(&mut self.clip_top),
4442 ObjectEventTarget::ClipRight => Some(&mut self.clip_right),
4443 ObjectEventTarget::ClipBottom => Some(&mut self.clip_bottom),
4444 ObjectEventTarget::SrcClipLeft => Some(&mut self.src_clip_left),
4445 ObjectEventTarget::SrcClipTop => Some(&mut self.src_clip_top),
4446 ObjectEventTarget::SrcClipRight => Some(&mut self.src_clip_right),
4447 ObjectEventTarget::SrcClipBottom => Some(&mut self.src_clip_bottom),
4448 ObjectEventTarget::Tr => Some(&mut self.tr),
4449 ObjectEventTarget::Mono => Some(&mut self.mono),
4450 ObjectEventTarget::Reverse => Some(&mut self.reverse),
4451 ObjectEventTarget::Bright => Some(&mut self.bright),
4452 ObjectEventTarget::Dark => Some(&mut self.dark),
4453 ObjectEventTarget::ColorR => Some(&mut self.color_r),
4454 ObjectEventTarget::ColorG => Some(&mut self.color_g),
4455 ObjectEventTarget::ColorB => Some(&mut self.color_b),
4456 ObjectEventTarget::ColorRate => Some(&mut self.color_rate),
4457 ObjectEventTarget::ColorAddR => Some(&mut self.color_add_r),
4458 ObjectEventTarget::ColorAddG => Some(&mut self.color_add_g),
4459 ObjectEventTarget::ColorAddB => Some(&mut self.color_add_b),
4460 ObjectEventTarget::XRep
4461 | ObjectEventTarget::YRep
4462 | ObjectEventTarget::ZRep
4463 | ObjectEventTarget::TrRep
4464 | ObjectEventTarget::Alpha
4465 | ObjectEventTarget::Order
4466 | ObjectEventTarget::Layer
4467 | ObjectEventTarget::Unknown => None,
4468 }
4469 }
4470}
4471
4472#[derive(Debug, Default, Clone)]
4473pub struct ObjectPropEventLists {
4474 pub x_rep: Vec<IntEvent>,
4475 pub y_rep: Vec<IntEvent>,
4476 pub z_rep: Vec<IntEvent>,
4477 pub tr_rep: Vec<IntEvent>,
4478}
4479
4480impl ObjectPropEventLists {
4481 pub fn clear(&mut self) {
4482 self.x_rep.clear();
4483 self.y_rep.clear();
4484 self.z_rep.clear();
4485 self.tr_rep.clear();
4486 }
4487
4488 pub fn update_time(&mut self, past_game_time: i32, past_real_time: i32) {
4489 for ev in &mut self.x_rep {
4490 ev.update_time(past_game_time, past_real_time);
4491 }
4492 for ev in &mut self.y_rep {
4493 ev.update_time(past_game_time, past_real_time);
4494 }
4495 for ev in &mut self.z_rep {
4496 ev.update_time(past_game_time, past_real_time);
4497 }
4498 for ev in &mut self.tr_rep {
4499 ev.update_time(past_game_time, past_real_time);
4500 }
4501 }
4502
4503 pub fn frame(&mut self) {
4504 for ev in &mut self.x_rep {
4505 ev.frame();
4506 }
4507 for ev in &mut self.y_rep {
4508 ev.frame();
4509 }
4510 for ev in &mut self.z_rep {
4511 ev.frame();
4512 }
4513 for ev in &mut self.tr_rep {
4514 ev.frame();
4515 }
4516 }
4517
4518 pub fn tick(&mut self, delta: i32) {
4519 self.update_time(delta, delta);
4520 self.frame();
4521 }
4522
4523 pub fn any_active(&self) -> bool {
4524 self.x_rep.iter().any(|e| e.check_event())
4525 || self.y_rep.iter().any(|e| e.check_event())
4526 || self.z_rep.iter().any(|e| e.check_event())
4527 || self.tr_rep.iter().any(|e| e.check_event())
4528 }
4529
4530 pub fn end_all(&mut self) {
4531 for ev in &mut self.x_rep {
4532 ev.end_event();
4533 }
4534 for ev in &mut self.y_rep {
4535 ev.end_event();
4536 }
4537 for ev in &mut self.z_rep {
4538 ev.end_event();
4539 }
4540 for ev in &mut self.tr_rep {
4541 ev.end_event();
4542 }
4543 }
4544}
4545
4546#[derive(Debug, Clone, Default)]
4547pub struct ObjectPropLists {
4548 pub x_rep: Vec<i64>,
4549 pub y_rep: Vec<i64>,
4550 pub z_rep: Vec<i64>,
4551 pub tr_rep: Vec<i64>,
4552 pub f: Vec<i64>,
4553}
4554
4555impl ObjectPropLists {
4556 pub fn clear(&mut self) {
4557 self.x_rep.clear();
4558 self.y_rep.clear();
4559 self.z_rep.clear();
4560 self.tr_rep.clear();
4561 self.f.clear();
4566 }
4567}
4568
4569#[cfg(test)]
4570mod object_prop_lists_tests {
4571 use super::ObjectPropLists;
4572
4573 #[test]
4574 fn clear_restores_object_f_default_size_zero() {
4575 let mut lists = ObjectPropLists {
4576 x_rep: vec![1, 2],
4577 y_rep: vec![3],
4578 z_rep: vec![4],
4579 tr_rep: vec![5],
4580 f: vec![10, 20, 30, 40, 50, 60, 70],
4581 };
4582
4583 lists.clear();
4584
4585 assert!(lists.x_rep.is_empty());
4586 assert!(lists.y_rep.is_empty());
4587 assert!(lists.z_rep.is_empty());
4588 assert!(lists.tr_rep.is_empty());
4589 assert!(lists.f.is_empty());
4590 }
4591}
4592
4593#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
4594pub struct ObjectRectParam {
4595 pub left: i64,
4596 pub top: i64,
4597 pub right: i64,
4598 pub bottom: i64,
4599 pub color_argb: i64,
4601}
4602
4603#[derive(Debug, Default, Clone)]
4604pub struct ObjectRuntimeState {
4605 pub number_sprite_offsets: Vec<Option<i32>>,
4607 pub explicit_int_props: HashSet<i32>,
4608 pub explicit_str_props: HashSet<i32>,
4609 pub prop_events: ObjectPropEvents,
4610 pub prop_event_lists: ObjectPropEventLists,
4611 pub prop_lists: ObjectPropLists,
4612 pub child_objects: Vec<ObjectState>,
4613}
4614
4615#[derive(Debug, Default, Clone)]
4616pub struct ObjectState {
4617 pub used: bool,
4624 pub backend: ObjectBackend,
4625 pub file_name: Option<String>,
4626 pub string_value: Option<String>,
4627
4628 pub object_type: i64,
4630
4631 pub rect_param: ObjectRectParam,
4633
4634 pub number_value: i64,
4636
4637 pub string_param: ObjectStringParam,
4639
4640 pub number_param: ObjectNumberParam,
4642
4643 pub weather_param: ObjectWeatherParam,
4645 pub weather_work: ObjectWeatherWorkState,
4646
4647 pub thumb_save_no: i64,
4649
4650 pub movie: ObjectMovieState,
4652
4653 pub emote: ObjectEmoteParam,
4655
4656 pub gan_file: Option<String>,
4658 pub gan: GanState,
4660
4661 pub frame_action: ObjectFrameActionState,
4663 pub frame_action_ch: Vec<ObjectFrameActionState>,
4665
4666 pub base: ObjectBaseState,
4667
4668 pub button: ObjectButtonState,
4669
4670 pub runtime: ObjectRuntimeState,
4671
4672 pub mesh_animation_state: crate::mesh3d::MeshAnimationState,
4673 pub backend_runtime_slot: Option<usize>,
4679 pub nested_runtime_slot: Option<usize>,
4680}
4681
4682fn normalize_object_int_prop(
4683 ids: &crate::runtime::constants::RuntimeConstants,
4684 op: i32,
4685 value: i64,
4686) -> i64 {
4687 let byte_range = [
4688 ids.obj_tr,
4689 ids.obj_mono,
4690 ids.obj_reverse,
4691 ids.obj_bright,
4692 ids.obj_dark,
4693 ids.obj_color_r,
4694 ids.obj_color_g,
4695 ids.obj_color_b,
4696 ids.obj_color_rate,
4697 ids.obj_color_add_r,
4698 ids.obj_color_add_g,
4699 ids.obj_color_add_b,
4700 ];
4701 if byte_range.iter().any(|&id| id != 0 && op == id) {
4702 return value.clamp(0, 255);
4703 }
4704
4705 if op == ids.obj_disp {
4706 return if value != 0 { 1 } else { 0 };
4707 }
4708
4709 let boolean = [
4710 ids.obj_clip_use,
4711 ids.obj_src_clip_use,
4712 ids.obj_fog_use,
4713 ids.obj_culling,
4714 ids.obj_alpha_test,
4715 ids.obj_alpha_blend,
4716 ids.obj_wipe_copy,
4717 ids.obj_wipe_erase,
4718 ids.obj_click_disable,
4719 ];
4720 if boolean.iter().any(|&id| id != 0 && op == id) {
4721 return if value != 0 { 1 } else { 0 };
4722 }
4723
4724 value
4725}
4726
4727impl ObjectState {
4728 pub fn clone_emote_players_for_object_tree(&mut self) {
4733 if self.object_type == 12 {
4734 self.emote.clone_player_for_object();
4735 }
4736 for child in &mut self.runtime.child_objects {
4737 child.clone_emote_players_for_object_tree();
4738 }
4739 }
4740
4741 pub fn contains_emote_in_object_tree(&self) -> bool {
4742 self.object_type == 12
4743 || self
4744 .runtime
4745 .child_objects
4746 .iter()
4747 .any(ObjectState::contains_emote_in_object_tree)
4748 }
4749
4750 fn sync_event_backed_prop_value(
4751 &mut self,
4752 ids: &crate::runtime::constants::RuntimeConstants,
4753 op: i32,
4754 value: i64,
4755 ) {
4756 let target = ids.object_op_tables().prop_target(op);
4757
4758 let Some(ev) = self.runtime.prop_events.get_mut(target) else {
4759 return;
4760 };
4761 ev.set_value(value as i32);
4762 if !ev.check_event() {
4763 ev.cur_value = value as i32;
4764 }
4765 }
4766
4767 pub fn init_type_like(&mut self) {
4771 self.backend = ObjectBackend::None;
4772 self.file_name = None;
4773 self.string_value = None;
4774 self.object_type = 0;
4775
4776 self.rect_param = ObjectRectParam::default();
4777 self.number_value = 0;
4778 self.string_param = ObjectStringParam::default();
4779 self.number_param = ObjectNumberParam::default();
4780 self.weather_param = ObjectWeatherParam::default();
4781 self.weather_work = ObjectWeatherWorkState::default();
4782 self.thumb_save_no = -1;
4783
4784 self.movie.reset();
4785 self.emote = ObjectEmoteParam::default();
4786
4787 self.gan_file = None;
4788 self.gan.reset();
4789 self.mesh_animation_state = crate::mesh3d::MeshAnimationState::default();
4790 }
4791
4792 fn weather_rand_percent(&mut self, base: i64, span: i64) -> i64 {
4793 base + self.weather_work.rand_mod(span)
4794 }
4795
4796 fn setup_weather_sub(&mut self, idx: usize, init_state: i64, screen_w: i64, screen_h: i64) {
4797 if idx >= self.weather_work.sub.len() {
4798 return;
4799 }
4800 let param = self.weather_param.clone();
4801 let screen_w = screen_w.max(1);
4802 let screen_h = screen_h.max(1);
4803 let mut sub = ObjectWeatherWorkSub {
4804 state: init_state,
4805 ..Default::default()
4806 };
4807
4808 if param.weather_type == 1 {
4809 sub.move_start_pos_x = self.weather_work.rand_mod(screen_w);
4810 sub.move_start_pos_y = self.weather_work.rand_mod(screen_h);
4811 sub.move_time_x = param.move_time_x * self.weather_rand_percent(90, 20) / 100;
4812 sub.move_time_y = param.move_time_y * self.weather_rand_percent(90, 20) / 100;
4813 sub.move_cur_time = self.weather_work.next_rand();
4814 sub.sin_time_x = param.sin_time_x * self.weather_rand_percent(90, 20) / 100;
4815 sub.sin_time_y = param.sin_time_y * self.weather_rand_percent(90, 20) / 100;
4816 sub.sin_power_x = param.sin_power_x * self.weather_rand_percent(90, 20) / 100;
4817 sub.sin_power_y = param.sin_power_y * self.weather_rand_percent(90, 20) / 100;
4818 sub.sin_cur_time = self.weather_work.next_rand();
4819 sub.scale_x = param.scale_x;
4820 sub.scale_y = param.scale_y;
4821 sub.active_time_len = param.active_time * self.weather_rand_percent(80, 40) / 100;
4822 sub.state_time_len = if init_state == 0 {
4823 self.weather_work.rand_mod(3000)
4824 } else {
4825 sub.active_time_len
4826 };
4827 sub.real_time_flag = param.real_time_flag;
4828 } else if param.weather_type == 2 {
4829 let max_distance_x = if param.center_x > screen_w / 2 {
4830 param.center_x
4831 } else {
4832 screen_w - param.center_x
4833 };
4834 let max_distance_y = if param.center_y > screen_h / 2 {
4835 param.center_y
4836 } else {
4837 screen_h - param.center_y
4838 };
4839 let max_distance =
4840 (((max_distance_x * max_distance_x + max_distance_y * max_distance_y) as f64)
4841 .sqrt()) as i64;
4842 sub.move_start_distance =
4843 self.weather_work.rand_mod(max_distance.max(1)) * param.appear_range / 100;
4844 sub.move_start_degree = self.weather_work.rand_mod(3600);
4845 sub.move_time_x = param.move_time_x.abs() * self.weather_rand_percent(80, 40) / 100;
4846 sub.move_time_y = param.move_time_y.abs() * self.weather_rand_percent(80, 40) / 100;
4847 sub.sin_time_x = param.sin_time_x * self.weather_rand_percent(90, 20) / 100;
4848 sub.sin_time_y = param.sin_time_y * self.weather_rand_percent(90, 20) / 100;
4849 sub.sin_power_x = param.sin_power_x * self.weather_rand_percent(90, 20) / 100;
4850 sub.sin_power_y = param.sin_power_y * self.weather_rand_percent(90, 20) / 100;
4851 sub.sin_cur_time = self.weather_work.next_rand();
4852 sub.center_rotate = param.center_rotate * self.weather_rand_percent(90, 20) / 100;
4853 sub.zoom_min = param.zoom_min;
4854 sub.zoom_max = param.zoom_max;
4855 sub.scale_x = param.scale_x;
4856 sub.scale_y = param.scale_y;
4857 sub.active_time_len = param.move_time_x.abs() * max_distance / 1000 - 1000;
4858 sub.state_time_len = if init_state == 0 {
4859 self.weather_work
4860 .rand_mod((1000 + sub.active_time_len + 1000).max(1))
4861 } else {
4862 sub.active_time_len
4863 };
4864 sub.move_cur_time = if sub.state_time_len == 0 {
4865 0
4866 } else {
4867 self.weather_work.rand_mod(sub.state_time_len.abs().max(1))
4868 };
4869 sub.real_time_flag = param.real_time_flag;
4870 }
4871 sub.restruct_flag = false;
4872 self.weather_work.sub[idx] = sub;
4873 }
4874
4875 pub fn weather_sprite_count(&self) -> usize {
4876 match self.weather_param.weather_type {
4877 1 => self.weather_work.cnt_max.saturating_mul(4),
4878 2 => self.weather_work.cnt_max,
4879 _ => 0,
4880 }
4881 }
4882
4883 pub fn restruct_weather_work(&mut self, screen_w: i64, screen_h: i64) {
4884 if self.object_type != 4 {
4885 return;
4886 }
4887 for sub in &mut self.weather_work.sub {
4888 sub.restruct_flag = true;
4889 }
4890 let cnt = self.weather_param.cnt.max(0) as usize;
4891 let old_cnt = self.weather_work.sub.len();
4892 if cnt > old_cnt {
4893 self.weather_work
4894 .sub
4895 .resize_with(cnt, ObjectWeatherWorkSub::default);
4896 for idx in old_cnt..cnt {
4897 self.setup_weather_sub(idx, 0, screen_w, screen_h);
4898 }
4899 }
4900 if cnt > self.weather_work.cnt_max {
4901 self.weather_work.cnt_max = cnt;
4902 }
4903 }
4904
4905 pub fn init_param_like(&mut self) {
4906 self.base = ObjectBaseState::default();
4907 self.button.clear();
4908 self.runtime.explicit_int_props.clear();
4909 self.runtime.explicit_str_props.clear();
4910 self.runtime.prop_events.clear();
4911 self.runtime.prop_lists.clear();
4912 self.runtime.prop_event_lists.clear();
4913 self.frame_action = ObjectFrameActionState::default();
4914 self.frame_action_ch.clear();
4915 self.gan_file = None;
4916 self.gan.reset();
4917 }
4918
4919 pub fn clear_runtime_only(&mut self) {
4920 self.runtime.explicit_int_props.clear();
4921 self.runtime.explicit_str_props.clear();
4922 self.runtime.prop_events.clear();
4923 self.runtime.prop_lists.clear();
4924 self.runtime.prop_event_lists.clear();
4925 self.frame_action = ObjectFrameActionState::default();
4926 self.frame_action_ch.clear();
4927 }
4928
4929 pub fn set_int_prop(
4930 &mut self,
4931 ids: &crate::runtime::constants::RuntimeConstants,
4932 op: i32,
4933 value: i64,
4934 ) {
4935 let value = normalize_object_int_prop(ids, op, value);
4936 self.runtime.explicit_int_props.insert(op);
4937 let ok =
4938 self.sync_fixed_int_prop(ids, op, value) || self.sync_special_int_prop(ids, op, value);
4939 assert!(ok, "unknown object int property op {}", op);
4940 }
4941
4942 pub fn set_int_prop_from_event_frame(
4943 &mut self,
4944 ids: &crate::runtime::constants::RuntimeConstants,
4945 op: i32,
4946 value: i64,
4947 ) {
4948 self.runtime.explicit_int_props.insert(op);
4949 macro_rules! set_if {
4950 ($id:expr, $field:ident) => {
4951 if $id != 0 && op == $id {
4952 self.base.$field = value;
4953 return;
4954 }
4955 };
4956 }
4957 set_if!(ids.obj_patno, patno);
4958 set_if!(ids.obj_x, x);
4959 set_if!(ids.obj_y, y);
4960 set_if!(ids.obj_z, z);
4961 set_if!(ids.obj_center_x, center_x);
4962 set_if!(ids.obj_center_y, center_y);
4963 set_if!(ids.obj_center_z, center_z);
4964 set_if!(ids.obj_center_rep_x, center_rep_x);
4965 set_if!(ids.obj_center_rep_y, center_rep_y);
4966 set_if!(ids.obj_center_rep_z, center_rep_z);
4967 set_if!(ids.obj_scale_x, scale_x);
4968 set_if!(ids.obj_scale_y, scale_y);
4969 set_if!(ids.obj_scale_z, scale_z);
4970 set_if!(ids.obj_rotate_x, rotate_x);
4971 set_if!(ids.obj_rotate_y, rotate_y);
4972 set_if!(ids.obj_rotate_z, rotate_z);
4973 set_if!(ids.obj_clip_left, clip_left);
4974 set_if!(ids.obj_clip_top, clip_top);
4975 set_if!(ids.obj_clip_right, clip_right);
4976 set_if!(ids.obj_clip_bottom, clip_bottom);
4977 set_if!(ids.obj_src_clip_left, src_clip_left);
4978 set_if!(ids.obj_src_clip_top, src_clip_top);
4979 set_if!(ids.obj_src_clip_right, src_clip_right);
4980 set_if!(ids.obj_src_clip_bottom, src_clip_bottom);
4981 set_if!(ids.obj_alpha, alpha);
4982 set_if!(ids.obj_tr, tr);
4983 set_if!(ids.obj_mono, mono);
4984 set_if!(ids.obj_reverse, reverse);
4985 set_if!(ids.obj_bright, bright);
4986 set_if!(ids.obj_dark, dark);
4987 set_if!(ids.obj_color_r, color_r);
4988 set_if!(ids.obj_color_g, color_g);
4989 set_if!(ids.obj_color_b, color_b);
4990 set_if!(ids.obj_color_rate, color_rate);
4991 set_if!(ids.obj_color_add_r, color_add_r);
4992 set_if!(ids.obj_color_add_g, color_add_g);
4993 set_if!(ids.obj_color_add_b, color_add_b);
4994 panic!("unknown object event-backed int property op {}", op);
4995 }
4996
4997 pub fn has_int_prop(&self, op: i32) -> bool {
4998 self.runtime.explicit_int_props.contains(&op)
4999 }
5000
5001 pub fn remove_int_prop(&mut self, op: i32) {
5002 self.runtime.explicit_int_props.remove(&op);
5003 }
5004
5005 pub fn set_str_prop(
5006 &mut self,
5007 ids: &crate::runtime::constants::RuntimeConstants,
5008 op: i32,
5009 value: String,
5010 ) {
5011 self.runtime.explicit_str_props.insert(op);
5012 let ok = self.sync_special_str_prop(ids, op, value);
5013 assert!(ok, "unknown object string property op {}", op);
5014 }
5015
5016 pub fn lookup_str_prop(
5017 &self,
5018 ids: &crate::runtime::constants::RuntimeConstants,
5019 op: i32,
5020 ) -> Option<String> {
5021 self.special_str_prop(ids, op)
5022 }
5023
5024 pub fn has_str_prop(&self, op: i32) -> bool {
5025 self.runtime.explicit_str_props.contains(&op)
5026 }
5027
5028 pub fn remove_str_prop(&mut self, ids: &crate::runtime::constants::RuntimeConstants, op: i32) {
5029 self.runtime.explicit_str_props.remove(&op);
5030 if ids.obj_mesh_anim_clip_name != 0 && op == ids.obj_mesh_anim_clip_name {
5031 let mut next = self.mesh_animation_state.clone();
5032 next.clip_name = None;
5033 self.set_mesh_animation_state(next);
5034 } else if ids.obj_mesh_anim_blend_clip_name != 0 && op == ids.obj_mesh_anim_blend_clip_name
5035 {
5036 let mut next = self.mesh_animation_state.clone();
5037 next.blend_clip_name = None;
5038 self.set_mesh_animation_state(next);
5039 }
5040 }
5041
5042 pub fn lookup_int_prop(
5043 &self,
5044 ids: &crate::runtime::constants::RuntimeConstants,
5045 op: i32,
5046 ) -> Option<i64> {
5047 self.fixed_int_prop(ids, op)
5048 .or_else(|| self.special_int_prop(ids, op))
5049 }
5050
5051 pub fn get_int_prop(&self, ids: &crate::runtime::constants::RuntimeConstants, op: i32) -> i64 {
5052 self.lookup_int_prop(ids, op).unwrap_or(0)
5053 }
5054
5055 pub fn runtime_slot_or(&self, fallback: usize) -> usize {
5056 self.nested_runtime_slot
5057 .or(self.backend_runtime_slot)
5058 .unwrap_or(fallback)
5059 }
5060
5061 pub fn ensure_runtime_slot(&mut self, next_slot: &mut usize) -> usize {
5062 if let Some(slot) = self.nested_runtime_slot {
5063 return slot;
5064 }
5065 let slot = *next_slot;
5066 *next_slot += 1;
5067 self.nested_runtime_slot = Some(slot);
5068 slot
5069 }
5070
5071 pub fn int_list_by_op<'a>(
5072 &'a self,
5073 ids: &crate::runtime::constants::RuntimeConstants,
5074 op: i32,
5075 ) -> Option<&'a Vec<i64>> {
5076 if ids.obj_f != 0 && op == ids.obj_f {
5077 Some(&self.runtime.prop_lists.f)
5078 } else {
5079 None
5080 }
5081 }
5082
5083 pub fn int_list_by_op_mut<'a>(
5084 &'a mut self,
5085 ids: &crate::runtime::constants::RuntimeConstants,
5086 op: i32,
5087 ) -> Option<&'a mut Vec<i64>> {
5088 if ids.obj_f != 0 && op == ids.obj_f {
5089 Some(&mut self.runtime.prop_lists.f)
5090 } else {
5091 None
5092 }
5093 }
5094
5095 pub fn rep_int_event_list_by_rep_op<'a>(
5096 &'a self,
5097 ids: &crate::runtime::constants::RuntimeConstants,
5098 op: i32,
5099 ) -> Option<&'a Vec<IntEvent>> {
5100 if ids.obj_x_rep != 0 && op == ids.obj_x_rep {
5101 Some(&self.runtime.prop_event_lists.x_rep)
5102 } else if ids.obj_y_rep != 0 && op == ids.obj_y_rep {
5103 Some(&self.runtime.prop_event_lists.y_rep)
5104 } else if ids.obj_z_rep != 0 && op == ids.obj_z_rep {
5105 Some(&self.runtime.prop_event_lists.z_rep)
5106 } else if ids.obj_tr_rep != 0 && op == ids.obj_tr_rep {
5107 Some(&self.runtime.prop_event_lists.tr_rep)
5108 } else {
5109 None
5110 }
5111 }
5112
5113 pub fn rep_int_event_list_by_rep_op_mut<'a>(
5114 &'a mut self,
5115 ids: &crate::runtime::constants::RuntimeConstants,
5116 op: i32,
5117 ) -> Option<&'a mut Vec<IntEvent>> {
5118 if ids.obj_x_rep != 0 && op == ids.obj_x_rep {
5119 Some(&mut self.runtime.prop_event_lists.x_rep)
5120 } else if ids.obj_y_rep != 0 && op == ids.obj_y_rep {
5121 Some(&mut self.runtime.prop_event_lists.y_rep)
5122 } else if ids.obj_z_rep != 0 && op == ids.obj_z_rep {
5123 Some(&mut self.runtime.prop_event_lists.z_rep)
5124 } else if ids.obj_tr_rep != 0 && op == ids.obj_tr_rep {
5125 Some(&mut self.runtime.prop_event_lists.tr_rep)
5126 } else {
5127 None
5128 }
5129 }
5130
5131 pub fn int_event_by_op<'a>(
5132 &'a self,
5133 ids: &crate::runtime::constants::RuntimeConstants,
5134 op: i32,
5135 ) -> Option<&'a IntEvent> {
5136 self.runtime.prop_events.get(self.event_target(ids, op))
5137 }
5138
5139 pub fn int_event_by_op_mut<'a>(
5140 &'a mut self,
5141 ids: &crate::runtime::constants::RuntimeConstants,
5142 op: i32,
5143 ) -> Option<&'a mut IntEvent> {
5144 let target = self.event_target(ids, op);
5145 self.runtime.prop_events.get_mut(target)
5146 }
5147
5148 pub fn int_event_list_by_op<'a>(
5149 &'a self,
5150 ids: &crate::runtime::constants::RuntimeConstants,
5151 op: i32,
5152 ) -> Option<&'a Vec<IntEvent>> {
5153 if ids.obj_x_rep_eve != 0 && op == ids.obj_x_rep_eve {
5154 Some(&self.runtime.prop_event_lists.x_rep)
5155 } else if ids.obj_y_rep_eve != 0 && op == ids.obj_y_rep_eve {
5156 Some(&self.runtime.prop_event_lists.y_rep)
5157 } else if ids.obj_z_rep_eve != 0 && op == ids.obj_z_rep_eve {
5158 Some(&self.runtime.prop_event_lists.z_rep)
5159 } else if ids.obj_tr_rep_eve != 0 && op == ids.obj_tr_rep_eve {
5160 Some(&self.runtime.prop_event_lists.tr_rep)
5161 } else {
5162 None
5163 }
5164 }
5165
5166 pub fn int_event_list_by_op_mut<'a>(
5167 &'a mut self,
5168 ids: &crate::runtime::constants::RuntimeConstants,
5169 op: i32,
5170 ) -> Option<&'a mut Vec<IntEvent>> {
5171 if ids.obj_x_rep_eve != 0 && op == ids.obj_x_rep_eve {
5172 Some(&mut self.runtime.prop_event_lists.x_rep)
5173 } else if ids.obj_y_rep_eve != 0 && op == ids.obj_y_rep_eve {
5174 Some(&mut self.runtime.prop_event_lists.y_rep)
5175 } else if ids.obj_z_rep_eve != 0 && op == ids.obj_z_rep_eve {
5176 Some(&mut self.runtime.prop_event_lists.z_rep)
5177 } else if ids.obj_tr_rep_eve != 0 && op == ids.obj_tr_rep_eve {
5178 Some(&mut self.runtime.prop_event_lists.tr_rep)
5179 } else {
5180 None
5181 }
5182 }
5183
5184 fn sync_fixed_int_prop(
5185 &mut self,
5186 ids: &crate::runtime::constants::RuntimeConstants,
5187 op: i32,
5188 value: i64,
5189 ) -> bool {
5190 macro_rules! set_if {
5191 ($id:expr, $field:ident) => {
5192 if $id != 0 && op == $id {
5193 self.base.$field = value;
5194 self.sync_event_backed_prop_value(ids, op, value);
5195 return true;
5196 }
5197 };
5198 }
5199 if op == ids.obj_disp {
5200 self.base.disp = value;
5201 return true;
5202 }
5203 set_if!(ids.obj_wipe_copy, wipe_copy);
5204 set_if!(ids.obj_wipe_erase, wipe_erase);
5205 set_if!(ids.obj_click_disable, click_disable);
5206 set_if!(ids.obj_patno, patno);
5207 set_if!(ids.obj_world, world);
5208 set_if!(ids.obj_order, order);
5209 set_if!(ids.obj_layer, layer);
5210 set_if!(ids.obj_x, x);
5211 set_if!(ids.obj_y, y);
5212 set_if!(ids.obj_z, z);
5213 set_if!(ids.obj_center_x, center_x);
5214 set_if!(ids.obj_center_y, center_y);
5215 set_if!(ids.obj_center_z, center_z);
5216 set_if!(ids.obj_center_rep_x, center_rep_x);
5217 set_if!(ids.obj_center_rep_y, center_rep_y);
5218 set_if!(ids.obj_center_rep_z, center_rep_z);
5219 set_if!(ids.obj_scale_x, scale_x);
5220 set_if!(ids.obj_scale_y, scale_y);
5221 set_if!(ids.obj_scale_z, scale_z);
5222 set_if!(ids.obj_rotate_x, rotate_x);
5223 set_if!(ids.obj_rotate_y, rotate_y);
5224 set_if!(ids.obj_rotate_z, rotate_z);
5225 set_if!(ids.obj_clip_use, clip_use);
5226 set_if!(ids.obj_clip_left, clip_left);
5227 set_if!(ids.obj_clip_top, clip_top);
5228 set_if!(ids.obj_clip_right, clip_right);
5229 set_if!(ids.obj_clip_bottom, clip_bottom);
5230 set_if!(ids.obj_src_clip_use, src_clip_use);
5231 set_if!(ids.obj_src_clip_left, src_clip_left);
5232 set_if!(ids.obj_src_clip_top, src_clip_top);
5233 set_if!(ids.obj_src_clip_right, src_clip_right);
5234 set_if!(ids.obj_src_clip_bottom, src_clip_bottom);
5235 set_if!(ids.obj_alpha, alpha);
5236 set_if!(ids.obj_tr, tr);
5237 set_if!(ids.obj_mono, mono);
5238 set_if!(ids.obj_reverse, reverse);
5239 set_if!(ids.obj_bright, bright);
5240 set_if!(ids.obj_dark, dark);
5241 set_if!(ids.obj_color_r, color_r);
5242 set_if!(ids.obj_color_g, color_g);
5243 set_if!(ids.obj_color_b, color_b);
5244 set_if!(ids.obj_color_rate, color_rate);
5245 set_if!(ids.obj_color_add_r, color_add_r);
5246 set_if!(ids.obj_color_add_g, color_add_g);
5247 set_if!(ids.obj_color_add_b, color_add_b);
5248 set_if!(ids.obj_mask_no, mask_no);
5249 set_if!(ids.obj_tonecurve_no, tonecurve_no);
5250 set_if!(ids.obj_light_no, light_no);
5251 set_if!(ids.obj_fog_use, fog_use);
5252 set_if!(ids.obj_culling, culling);
5253 set_if!(ids.obj_alpha_test, alpha_test);
5254 set_if!(ids.obj_alpha_blend, alpha_blend);
5255 set_if!(ids.obj_blend, blend);
5256 false
5257 }
5258
5259 fn sync_special_int_prop(
5260 &mut self,
5261 ids: &crate::runtime::constants::RuntimeConstants,
5262 op: i32,
5263 value: i64,
5264 ) -> bool {
5265 if op == OBJECT_NESTED_SLOT_KEY {
5266 self.nested_runtime_slot = (value >= 0).then_some(value as usize);
5267 return true;
5268 }
5269 if ids.obj_x_rep != 0 && op == ids.obj_x_rep {
5270 if self.runtime.prop_event_lists.x_rep.is_empty() {
5271 self.runtime.prop_event_lists.x_rep.push(IntEvent::new(0));
5272 }
5273 self.runtime.prop_event_lists.x_rep[0].set_value(value as i32);
5274 return true;
5275 }
5276 if ids.obj_y_rep != 0 && op == ids.obj_y_rep {
5277 if self.runtime.prop_event_lists.y_rep.is_empty() {
5278 self.runtime.prop_event_lists.y_rep.push(IntEvent::new(0));
5279 }
5280 self.runtime.prop_event_lists.y_rep[0].set_value(value as i32);
5281 return true;
5282 }
5283 if ids.obj_z_rep != 0 && op == ids.obj_z_rep {
5284 if self.runtime.prop_event_lists.z_rep.is_empty() {
5285 self.runtime.prop_event_lists.z_rep.push(IntEvent::new(0));
5286 }
5287 self.runtime.prop_event_lists.z_rep[0].set_value(value as i32);
5288 return true;
5289 }
5290 if ids.obj_tr_rep != 0 && op == ids.obj_tr_rep {
5291 if self.runtime.prop_event_lists.tr_rep.is_empty() {
5292 self.runtime
5293 .prop_event_lists
5294 .tr_rep
5295 .push(IntEvent::new(255));
5296 }
5297 self.runtime.prop_event_lists.tr_rep[0].set_value(value as i32);
5298 return true;
5299 }
5300 if ids.obj_mesh_anim_clip != 0 && op == ids.obj_mesh_anim_clip {
5301 let mut next = self.mesh_animation_state.clone();
5302 next.change_animation_clip(None, (value >= 0).then_some(value as usize));
5303 self.set_mesh_animation_state(next);
5304 return true;
5305 }
5306 if ids.obj_mesh_anim_rate != 0 && op == ids.obj_mesh_anim_rate {
5307 let mut next = self.mesh_animation_state.clone();
5308 next.rate = (value as f32) / 1000.0;
5309 self.set_mesh_animation_state(next);
5310 return true;
5311 }
5312 if ids.obj_mesh_anim_time_offset != 0 && op == ids.obj_mesh_anim_time_offset {
5313 let mut next = self.mesh_animation_state.clone();
5314 next.time_offset_sec = (value as f32) / 1000.0;
5315 self.set_mesh_animation_state(next);
5316 return true;
5317 }
5318 if ids.obj_mesh_anim_pause != 0 && op == ids.obj_mesh_anim_pause {
5319 let mut next = self.mesh_animation_state.clone();
5320 next.paused = value != 0;
5321 next.is_anim = !next.paused;
5322 self.set_mesh_animation_state(next);
5323 return true;
5324 }
5325 if ids.obj_mesh_anim_hold_time != 0 && op == ids.obj_mesh_anim_hold_time {
5326 let mut next = self.mesh_animation_state.clone();
5327 next.hold_time_sec = ((value as f32) / 1000.0).max(0.0);
5328 next.time_sec = if next.rate > 0.0 {
5329 next.hold_time_sec / next.rate.max(0.000_001)
5330 } else {
5331 0.0
5332 };
5333 self.set_mesh_animation_state(next);
5334 return true;
5335 }
5336 if ids.obj_mesh_anim_shift_time != 0 && op == ids.obj_mesh_anim_shift_time {
5337 let mut next = self.mesh_animation_state.clone();
5338 next.set_anim_shift_time_sec(((value as f32) / 1000.0).max(0.0));
5339 self.set_mesh_animation_state(next);
5340 return true;
5341 }
5342 if ids.obj_mesh_anim_loop != 0 && op == ids.obj_mesh_anim_loop {
5343 let mut next = self.mesh_animation_state.clone();
5344 next.looped = value != 0;
5345 self.set_mesh_animation_state(next);
5346 return true;
5347 }
5348 if ids.obj_mesh_anim_blend_clip != 0 && op == ids.obj_mesh_anim_blend_clip {
5349 let mut next = self.mesh_animation_state.clone();
5350 next.blend_clip_index = (value >= 0).then_some(value as usize);
5351 next.blend_clip_name = None;
5352 self.set_mesh_animation_state(next);
5353 return true;
5354 }
5355 if ids.obj_mesh_anim_blend_weight != 0 && op == ids.obj_mesh_anim_blend_weight {
5356 let mut next = self.mesh_animation_state.clone();
5357 next.blend_weight = ((value as f32) / 1000.0).clamp(0.0, 1.0);
5358 self.set_mesh_animation_state(next);
5359 return true;
5360 }
5361 false
5362 }
5363
5364 fn special_int_prop(
5365 &self,
5366 ids: &crate::runtime::constants::RuntimeConstants,
5367 op: i32,
5368 ) -> Option<i64> {
5369 if op == OBJECT_NESTED_SLOT_KEY {
5370 return self.nested_runtime_slot.map(|v| v as i64);
5371 }
5372 if ids.obj_x_rep != 0 && op == ids.obj_x_rep {
5373 return self
5374 .runtime
5375 .prop_event_lists
5376 .x_rep
5377 .first()
5378 .map(|ev| ev.get_value() as i64);
5379 }
5380 if ids.obj_y_rep != 0 && op == ids.obj_y_rep {
5381 return self
5382 .runtime
5383 .prop_event_lists
5384 .y_rep
5385 .first()
5386 .map(|ev| ev.get_value() as i64);
5387 }
5388 if ids.obj_z_rep != 0 && op == ids.obj_z_rep {
5389 return self
5390 .runtime
5391 .prop_event_lists
5392 .z_rep
5393 .first()
5394 .map(|ev| ev.get_value() as i64);
5395 }
5396 if ids.obj_tr_rep != 0 && op == ids.obj_tr_rep {
5397 return self
5398 .runtime
5399 .prop_event_lists
5400 .tr_rep
5401 .first()
5402 .map(|ev| ev.get_value() as i64);
5403 }
5404 if ids.obj_mesh_anim_clip != 0 && op == ids.obj_mesh_anim_clip {
5405 return Some(
5406 self.mesh_animation_state
5407 .clip_index
5408 .map(|v| v as i64)
5409 .unwrap_or(-1),
5410 );
5411 }
5412 if ids.obj_mesh_anim_rate != 0 && op == ids.obj_mesh_anim_rate {
5413 return Some((self.mesh_animation_state.rate * 1000.0).round() as i64);
5414 }
5415 if ids.obj_mesh_anim_time_offset != 0 && op == ids.obj_mesh_anim_time_offset {
5416 return Some((self.mesh_animation_state.time_offset_sec * 1000.0).round() as i64);
5417 }
5418 if ids.obj_mesh_anim_pause != 0 && op == ids.obj_mesh_anim_pause {
5419 return Some(if self.mesh_animation_state.paused {
5420 1
5421 } else {
5422 0
5423 });
5424 }
5425 if ids.obj_mesh_anim_hold_time != 0 && op == ids.obj_mesh_anim_hold_time {
5426 return Some((self.mesh_animation_state.hold_time_sec * 1000.0).round() as i64);
5427 }
5428 if ids.obj_mesh_anim_shift_time != 0 && op == ids.obj_mesh_anim_shift_time {
5429 return Some((self.mesh_animation_state.anim_shift_time_sec * 1000.0).round() as i64);
5430 }
5431 if ids.obj_mesh_anim_loop != 0 && op == ids.obj_mesh_anim_loop {
5432 return Some(if self.mesh_animation_state.looped {
5433 1
5434 } else {
5435 0
5436 });
5437 }
5438 if ids.obj_mesh_anim_blend_clip != 0 && op == ids.obj_mesh_anim_blend_clip {
5439 return Some(
5440 self.mesh_animation_state
5441 .blend_clip_index
5442 .map(|v| v as i64)
5443 .unwrap_or(-1),
5444 );
5445 }
5446 if ids.obj_mesh_anim_blend_weight != 0 && op == ids.obj_mesh_anim_blend_weight {
5447 return Some((self.mesh_animation_state.blend_weight * 1000.0).round() as i64);
5448 }
5449 None
5450 }
5451
5452 fn sync_special_str_prop(
5453 &mut self,
5454 ids: &crate::runtime::constants::RuntimeConstants,
5455 op: i32,
5456 value: String,
5457 ) -> bool {
5458 if ids.obj_mesh_anim_clip_name != 0 && op == ids.obj_mesh_anim_clip_name {
5459 let mut next = self.mesh_animation_state.clone();
5460 next.change_animation_clip(Some(value), None);
5461 self.set_mesh_animation_state(next);
5462 return true;
5463 }
5464 if ids.obj_mesh_anim_blend_clip_name != 0 && op == ids.obj_mesh_anim_blend_clip_name {
5465 let mut next = self.mesh_animation_state.clone();
5466 next.blend_clip_name = Some(value);
5467 next.blend_clip_index = None;
5468 self.set_mesh_animation_state(next);
5469 return true;
5470 }
5471 false
5472 }
5473
5474 fn special_str_prop(
5475 &self,
5476 ids: &crate::runtime::constants::RuntimeConstants,
5477 op: i32,
5478 ) -> Option<String> {
5479 if ids.obj_mesh_anim_clip_name != 0 && op == ids.obj_mesh_anim_clip_name {
5480 return self.mesh_animation_state.clip_name.clone();
5481 }
5482 if ids.obj_mesh_anim_blend_clip_name != 0 && op == ids.obj_mesh_anim_blend_clip_name {
5483 return self.mesh_animation_state.blend_clip_name.clone();
5484 }
5485 None
5486 }
5487
5488 fn fixed_int_prop(
5489 &self,
5490 ids: &crate::runtime::constants::RuntimeConstants,
5491 op: i32,
5492 ) -> Option<i64> {
5493 match ids.object_op_tables().fixed_int_prop(op)? {
5494 FixedIntProp::Base(field) => Some(self.base.int_field(field)),
5495 FixedIntProp::EventTotal(target) => self
5496 .runtime
5497 .prop_events
5498 .get(target)
5499 .map(|ev| ev.get_total_value() as i64),
5500 }
5501 }
5502
5503 pub fn set_mesh_animation_state(&mut self, next: crate::mesh3d::MeshAnimationState) {
5504 self.apply_mesh_animation_state(next, None);
5505 }
5506
5507 fn apply_mesh_animation_state(
5508 &mut self,
5509 next: crate::mesh3d::MeshAnimationState,
5510 explicit_hold_override: Option<f32>,
5511 ) {
5512 let prev = self.mesh_animation_state.clone();
5513 let mut merged = next.sanitized();
5514 let clip_changed =
5515 prev.clip_name != merged.clip_name || prev.clip_index != merged.clip_index;
5516 let pause_enter = !prev.paused && merged.paused;
5517 let pause_exit = prev.paused && !merged.paused;
5518 let prev_base = prev.current_sample_base_sec();
5519
5520 merged.anim_track_no = prev.anim_track_no;
5521 merged.is_anim = !merged.paused;
5522 merged.time_sec = prev.time_sec.max(0.0);
5523 merged.hold_time_sec = prev.hold_time_sec.max(0.0);
5524 merged.prev_clip_name = prev.prev_clip_name.clone();
5525 merged.prev_clip_index = prev.prev_clip_index;
5526 merged.prev_time_sec = prev.prev_time_sec.max(0.0);
5527 merged.prev_time_offset_sec = prev.prev_time_offset_sec.max(0.0);
5528 merged.prev_rate = prev.prev_rate.max(0.0);
5529 merged.transition_elapsed_sec = prev.transition_elapsed_sec.max(0.0);
5530
5531 if clip_changed {
5532 merged.change_animation_clip(merged.clip_name.clone(), merged.clip_index);
5533 }
5534
5535 if let Some(hold_sec) = explicit_hold_override {
5536 let hold_sec = hold_sec.max(0.0);
5537 merged.hold_time_sec = hold_sec;
5538 merged.time_sec = if merged.rate > 0.0 {
5539 hold_sec / merged.rate.max(0.000_001)
5540 } else {
5541 0.0
5542 };
5543 } else if pause_enter {
5544 merged.hold_time_sec = prev_base;
5545 } else if pause_exit {
5546 merged.time_sec = if merged.rate > 0.0 {
5547 prev.hold_time_sec.max(0.0) / merged.rate.max(0.000_001)
5548 } else {
5549 prev.time_sec.max(0.0)
5550 };
5551 } else if merged.paused {
5552 merged.hold_time_sec = prev.hold_time_sec.max(0.0);
5553 } else if !clip_changed && (prev.rate - merged.rate).abs() > 0.000_001 {
5554 merged.time_sec = if merged.rate > 0.0 {
5555 prev_base / merged.rate.max(0.000_001)
5556 } else {
5557 prev.time_sec.max(0.0)
5558 };
5559 }
5560
5561 self.mesh_animation_state = merged.sanitized();
5562 }
5563
5564 pub fn sync_mesh_animation_state_from_props(
5565 &mut self,
5566 ids: &super::constants::RuntimeConstants,
5567 ) {
5568 let int_prop = |id: i32, default: i64| -> i64 {
5569 if id != 0 {
5570 self.lookup_int_prop(ids, id).unwrap_or(default)
5571 } else {
5572 default
5573 }
5574 };
5575 let str_prop = |id: i32| -> Option<String> {
5576 if id != 0 {
5577 self.lookup_str_prop(ids, id)
5578 } else {
5579 None
5580 }
5581 };
5582 let explicit_hold =
5583 ids.obj_mesh_anim_hold_time != 0 && self.has_int_prop(ids.obj_mesh_anim_hold_time);
5584 let requested_hold_sec = (int_prop(ids.obj_mesh_anim_hold_time, 0) as f32) / 1000.0;
5585 let requested_shift_sec = (int_prop(
5586 ids.obj_mesh_anim_shift_time,
5587 (self.mesh_animation_state.anim_shift_time_sec * 1000.0).round() as i64,
5588 ) as f32)
5589 / 1000.0;
5590 let next = crate::mesh3d::MeshAnimationState {
5591 clip_name: str_prop(ids.obj_mesh_anim_clip_name),
5592 clip_index: (int_prop(ids.obj_mesh_anim_clip, -1) >= 0).then_some(int_prop(
5593 ids.obj_mesh_anim_clip,
5594 -1,
5595 )
5596 as usize),
5597 blend_clip_name: str_prop(ids.obj_mesh_anim_blend_clip_name),
5598 blend_clip_index: (int_prop(ids.obj_mesh_anim_blend_clip, -1) >= 0)
5599 .then_some(int_prop(ids.obj_mesh_anim_blend_clip, -1) as usize),
5600 blend_weight: ((int_prop(ids.obj_mesh_anim_blend_weight, 0) as f32) / 1000.0)
5601 .clamp(0.0, 1.0),
5602 time_sec: self.mesh_animation_state.time_sec,
5603 rate: (int_prop(ids.obj_mesh_anim_rate, 1000) as f32) / 1000.0,
5604 time_offset_sec: (int_prop(ids.obj_mesh_anim_time_offset, 0) as f32) / 1000.0,
5605 hold_time_sec: if explicit_hold {
5606 requested_hold_sec
5607 } else {
5608 self.mesh_animation_state.hold_time_sec
5609 },
5610 paused: int_prop(ids.obj_mesh_anim_pause, 0) != 0,
5611 looped: int_prop(ids.obj_mesh_anim_loop, 1) != 0,
5612 anim_track_no: self.mesh_animation_state.anim_track_no,
5613 anim_shift_time_sec: requested_shift_sec.max(0.0),
5614 is_anim: !(int_prop(ids.obj_mesh_anim_pause, 0) != 0),
5615 prev_clip_name: self.mesh_animation_state.prev_clip_name.clone(),
5616 prev_clip_index: self.mesh_animation_state.prev_clip_index,
5617 prev_time_sec: self.mesh_animation_state.prev_time_sec,
5618 prev_time_offset_sec: self.mesh_animation_state.prev_time_offset_sec,
5619 prev_rate: self.mesh_animation_state.prev_rate,
5620 transition_elapsed_sec: self.mesh_animation_state.transition_elapsed_sec,
5621 };
5622 self.apply_mesh_animation_state(next, explicit_hold.then_some(requested_hold_sec));
5623 }
5624
5625 pub fn uses_mesh_animation_bridge_op(
5626 ids: &super::constants::RuntimeConstants,
5627 op: i32,
5628 ) -> bool {
5629 [
5630 ids.obj_mesh_anim_clip,
5631 ids.obj_mesh_anim_clip_name,
5632 ids.obj_mesh_anim_rate,
5633 ids.obj_mesh_anim_time_offset,
5634 ids.obj_mesh_anim_pause,
5635 ids.obj_mesh_anim_hold_time,
5636 ids.obj_mesh_anim_shift_time,
5637 ids.obj_mesh_anim_loop,
5638 ids.obj_mesh_anim_blend_clip,
5639 ids.obj_mesh_anim_blend_clip_name,
5640 ids.obj_mesh_anim_blend_weight,
5641 ]
5642 .into_iter()
5643 .any(|id| id != 0 && op == id)
5644 }
5645
5646 pub fn tick(&mut self, past_game_time: i32, past_real_time: i32) {
5647 let delta = past_game_time.max(0);
5648 self.runtime
5649 .prop_events
5650 .update_time(past_game_time, past_real_time);
5651 self.runtime.prop_events.frame();
5652 self.runtime
5653 .prop_event_lists
5654 .update_time(past_game_time, past_real_time);
5655 self.runtime.prop_event_lists.frame();
5656 self.frame_action
5657 .counter
5658 .update_time(past_game_time, past_real_time);
5659 for fa in &mut self.frame_action_ch {
5660 fa.counter.update_time(past_game_time, past_real_time);
5661 }
5662 for child in &mut self.runtime.child_objects {
5663 child.tick(past_game_time, past_real_time);
5664 }
5665 if self.object_type == 12 {
5666 self.emote.tick(past_game_time);
5667 }
5668 self.movie.tick(past_game_time, past_real_time);
5669 self.gan.update_time(past_game_time, past_real_time);
5670 if matches!(self.object_type, 6 | 7) {
5671 self.mesh_animation_state.advance_controller_frames(delta);
5672 }
5673
5674 if self.object_type == 9 && self.movie.just_finished && !self.movie.auto_free_flag {
5675 self.movie.pause_flag = true;
5676 }
5677 }
5678
5679 pub fn update_weather_time(
5680 &mut self,
5681 past_game_time: i32,
5682 past_real_time: i32,
5683 screen_w: i64,
5684 screen_h: i64,
5685 ) {
5686 if self.object_type != 4 || !matches!(self.weather_param.weather_type, 1 | 2) {
5687 return;
5688 }
5689 let cnt = self.weather_param.cnt.max(0) as usize;
5690 let cnt_max = self.weather_work.cnt_max.min(self.weather_work.sub.len());
5691 for idx in 0..cnt_max {
5692 let mut setup_after_sleep = false;
5693 {
5694 let sub = &mut self.weather_work.sub[idx];
5695 if idx >= cnt && sub.state == 0 {
5696 continue;
5697 }
5698 let past_time = if sub.real_time_flag {
5699 past_real_time.max(0) as i64
5700 } else {
5701 past_game_time.max(0) as i64
5702 };
5703 sub.state_cur_time = sub.state_cur_time.saturating_add(past_time);
5704 sub.move_cur_time = sub.move_cur_time.saturating_add(past_time);
5705 sub.sin_cur_time = sub.sin_cur_time.saturating_add(past_time);
5706
5707 if (idx >= cnt || sub.restruct_flag)
5708 && sub.state == 2
5709 && sub.state_time_len - sub.state_cur_time >= 3000
5710 {
5711 sub.state_cur_time = sub.state_time_len.saturating_sub(1500);
5712 }
5713
5714 while sub.state_cur_time - sub.state_time_len > 0 {
5715 let amari_time = sub.state_cur_time - sub.state_time_len;
5716 sub.state = (sub.state + 1) % 4;
5717 if sub.state == 0 {
5718 if idx >= cnt {
5719 break;
5720 }
5721 setup_after_sleep = true;
5722 break;
5723 }
5724 if sub.state == 1 {
5725 sub.move_cur_time = amari_time;
5726 }
5727 sub.state_time_len = match sub.state {
5728 1 => 1000,
5729 2 => sub.active_time_len,
5730 3 => 1000,
5731 _ => sub.state_time_len,
5732 };
5733 sub.state_cur_time = amari_time;
5734 }
5735 }
5736 if setup_after_sleep {
5737 self.setup_weather_sub(idx, 1, screen_w, screen_h);
5738 }
5739 }
5740 }
5741
5742 pub fn any_event_active(&self) -> bool {
5743 self.runtime.prop_events.any_active() || self.runtime.prop_event_lists.any_active()
5744 }
5745
5746 pub fn end_all_events(&mut self) {
5747 self.runtime.prop_events.end_all();
5748 self.runtime.prop_event_lists.end_all();
5749 }
5750
5751 pub fn event_target(
5752 &self,
5753 ids: &super::constants::RuntimeConstants,
5754 op: i32,
5755 ) -> ObjectEventTarget {
5756 ids.object_op_tables().event_target(op)
5757 }
5758}
5759
5760#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5761pub enum GroupListOpKind {
5762 Alloc,
5763 Free,
5764 Unknown,
5765}
5766
5767#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5768pub enum GroupOpKind {
5769 Sel,
5770 SelCancel,
5771 Init,
5772 Start,
5773 StartCancel,
5774 End,
5775 GetHitNo,
5776 GetPushedNo,
5777 GetDecidedNo,
5778 GetResult,
5779 GetResultButtonNo,
5780 Order,
5781 Layer,
5782 CancelPriority,
5783 Unknown,
5784}
5785
5786#[derive(Debug, Clone)]
5787pub struct GroupState {
5788 pub wait_flag: bool,
5789 pub cancel_flag: bool,
5790 pub cancel_se_no: i64,
5791 pub started: bool,
5793 pub pause_flag: bool,
5796 pub target_object: Vec<i32>,
5798
5799 pub hit_button_no: i64,
5800 pub pushed_button_no: i64,
5801 pub decided_button_no: i64,
5802 pub hit_runtime_slot: Option<usize>,
5803 pub pushed_runtime_slot: Option<usize>,
5804 pub hit_message_button: bool,
5807 pub pushed_message_button: bool,
5808 pub message_button_se_no: i64,
5809
5810 pub result: i64,
5811 pub result_button_no: i64,
5812
5813 pub order: i64,
5814 pub layer: i64,
5815 pub cancel_priority: i64,
5816 pub props: HashMap<i32, i64>,
5817 pub aux_str_props: HashMap<i32, String>,
5818}
5819
5820impl Default for GroupState {
5821 fn default() -> Self {
5822 let mut state = Self {
5823 wait_flag: false,
5824 cancel_flag: false,
5825 cancel_se_no: -1,
5826 started: false,
5827 pause_flag: false,
5828 target_object: Vec::new(),
5829 hit_button_no: -1,
5830 pushed_button_no: -1,
5831 decided_button_no: TNM_GROUP_NOT_DECIDED,
5832 hit_runtime_slot: None,
5833 pushed_runtime_slot: None,
5834 hit_message_button: false,
5835 pushed_message_button: false,
5836 message_button_se_no: -1,
5837 result: TNM_GROUP_RESULT_NONE,
5838 result_button_no: 0,
5839 order: 0,
5840 layer: 0,
5841 cancel_priority: 0,
5842 props: HashMap::default(),
5843 aux_str_props: HashMap::default(),
5844 };
5845 state.reinit();
5846 state
5847 }
5848}
5849
5850pub const TNM_GROUP_NOT_DECIDED: i64 = -2;
5851pub const TNM_GROUP_CANCELED: i64 = -1;
5852pub const TNM_GROUP_RESULT_DECIDED: i64 = 1;
5853pub const TNM_GROUP_RESULT_NONE: i64 = 0;
5854pub const TNM_GROUP_RESULT_CANCELLED: i64 = -1;
5855
5856impl GroupState {
5857 pub fn reinit(&mut self) {
5858 self.order = 0;
5859 self.layer = 0;
5860 self.cancel_priority = 0;
5861 self.cancel_se_no = -1;
5862 self.decided_button_no = TNM_GROUP_NOT_DECIDED;
5863 self.result = TNM_GROUP_RESULT_NONE;
5864 self.result_button_no = 0;
5865 self.started = false;
5866 self.pause_flag = false;
5867 self.wait_flag = false;
5868 self.cancel_flag = false;
5869 self.hit_button_no = -1;
5870 self.pushed_button_no = -1;
5871 self.hit_runtime_slot = None;
5872 self.pushed_runtime_slot = None;
5873 self.hit_message_button = false;
5874 self.pushed_message_button = false;
5875 self.message_button_se_no = -1;
5876 }
5877
5878 pub fn init_sel(&mut self) {
5879 self.cancel_priority = 0;
5880 self.cancel_se_no = -1;
5881 self.decided_button_no = TNM_GROUP_NOT_DECIDED;
5882 self.result = TNM_GROUP_RESULT_NONE;
5883 self.result_button_no = 0;
5884 self.started = false;
5885 self.pause_flag = false;
5886 self.wait_flag = false;
5887 self.cancel_flag = false;
5888 self.hit_button_no = -1;
5889 self.pushed_button_no = -1;
5890 self.hit_runtime_slot = None;
5891 self.pushed_runtime_slot = None;
5892 self.hit_message_button = false;
5893 self.pushed_message_button = false;
5894 self.message_button_se_no = -1;
5895 }
5896
5897 pub fn start(&mut self) {
5898 self.started = true;
5899 self.decided_button_no = TNM_GROUP_NOT_DECIDED;
5900 }
5901
5902 pub fn is_doing(&self) -> bool {
5903 self.started && !self.pause_flag
5904 }
5905
5906 pub fn end(&mut self) {
5907 self.started = false;
5908 self.decided_button_no = TNM_GROUP_NOT_DECIDED;
5909 }
5910
5911 pub fn decide(&mut self, button_no: i64) -> bool {
5912 if !self.started {
5913 return false;
5914 }
5915 self.started = false;
5916 self.decided_button_no = button_no;
5917 self.result = TNM_GROUP_RESULT_DECIDED;
5918 self.result_button_no = button_no;
5919 self.hit_button_no = -1;
5920 self.pushed_button_no = -1;
5921 self.hit_runtime_slot = None;
5922 self.pushed_runtime_slot = None;
5923 self.hit_message_button = false;
5924 self.pushed_message_button = false;
5925 self.message_button_se_no = -1;
5926 true
5927 }
5928
5929 pub fn cancel(&mut self) -> Option<i64> {
5930 if !self.started {
5931 return None;
5932 }
5933 let hit_button_no = self.hit_button_no;
5934 self.started = false;
5935 self.decided_button_no = TNM_GROUP_CANCELED;
5936 self.result = TNM_GROUP_RESULT_CANCELLED;
5937 self.result_button_no = hit_button_no;
5938 self.hit_button_no = -1;
5939 self.pushed_button_no = -1;
5940 self.hit_runtime_slot = None;
5941 self.pushed_runtime_slot = None;
5942 self.hit_message_button = false;
5943 self.pushed_message_button = false;
5944 self.message_button_se_no = -1;
5945 Some(hit_button_no)
5946 }
5947}
5948
5949#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5950pub enum MwndListOpKind {
5951 CloseAll,
5952 CloseAllWait,
5953 CloseAllNowait,
5954 Unknown,
5955}
5956
5957#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5958pub enum MwndOpKind {
5959 MsgBlock,
5960 OpenWait,
5961 OpenNowait,
5962 CloseWait,
5963 CloseNowait,
5964 EndClose,
5965 CheckOpen,
5966 Clear,
5967 NovelClear,
5968 Print,
5970 NewLineNoIndent,
5972 NewLineIndent,
5974 WaitMsg,
5976 Pp,
5978 R,
5980 PageWait,
5982
5983 SetName,
5984 ClearName,
5985 GetName,
5986 NextMsg,
5987 MultiMsg,
5988 Ruby,
5989 Koe,
5990 KoePlayWait,
5991 KoePlayWaitKey,
5992 Layer,
5993 World,
5994 SetMojiSize,
5995 SetMojiColor,
5996 SetIndent,
5997 ClearIndent,
5998 StartSlideMsg,
5999 EndSlideMsg,
6000 SlideMsg,
6001 InitOpenAnimeType,
6002 InitOpenAnimeTime,
6003 InitCloseAnimeType,
6004 InitCloseAnimeTime,
6005 SetOpenAnimeType,
6006 SetOpenAnimeTime,
6007 SetCloseAnimeType,
6008 SetCloseAnimeTime,
6009 GetOpenAnimeType,
6010 GetOpenAnimeTime,
6011 GetCloseAnimeType,
6012 GetCloseAnimeTime,
6013 GetDefaultOpenAnimeType,
6014 GetDefaultOpenAnimeTime,
6015 GetDefaultCloseAnimeType,
6016 GetDefaultCloseAnimeTime,
6017 Sel,
6018 SelCancel,
6019 SelMsg,
6020 SelMsgCancel,
6021
6022 AddMsgCheck,
6024 AddMsg,
6026
6027 SetWaku,
6028 InitWakuFile,
6029 SetWakuFile,
6030 GetWakuFile,
6031 InitFilterFile,
6032 SetFilterFile,
6033 GetFilterFile,
6034
6035 ClearFace,
6036 SetFace,
6037 SetRepPos,
6038 MsgBtn,
6039 InitWindowPos,
6040 InitWindowSize,
6041 SetWindowPos,
6042 SetWindowSize,
6043 GetWindowPosX,
6044 GetWindowPosY,
6045 GetWindowSizeX,
6046 GetWindowSizeY,
6047 InitWindowMojiCnt,
6048 SetWindowMojiCnt,
6049 GetWindowMojiCntX,
6050 GetWindowMojiCntY,
6051 Unknown,
6052}
6053
6054#[derive(Debug, Clone, PartialEq, Eq)]
6055pub struct MwndMessageButtonState {
6056 pub btn_no: i64,
6057 pub group_no: i64,
6058 pub action_no: i64,
6059 pub se_no: i64,
6060}
6061
6062#[derive(Debug, Clone, PartialEq, Eq)]
6063pub struct MwndGlyphState {
6064 pub moji_type: i32,
6066 pub code: i32,
6068 pub ch: char,
6069 pub x: i64,
6070 pub y: i64,
6071 pub size: i64,
6072 pub moji_color_no: i64,
6073 pub shadow_color_no: i64,
6074 pub fuchi_color_no: i64,
6075 pub shadow: bool,
6076 pub fuchi: bool,
6077 pub bold: bool,
6078 pub reveal_index: usize,
6080 pub ruby: bool,
6081 pub appeared: bool,
6083 pub message_button: Option<MwndMessageButtonState>,
6085}
6086
6087impl Default for MwndGlyphState {
6088 fn default() -> Self {
6089 Self {
6090 moji_type: 0,
6091 code: 0,
6092 ch: '\0',
6093 x: 0,
6094 y: 0,
6095 size: 0,
6096 moji_color_no: 0,
6097 shadow_color_no: 0,
6098 fuchi_color_no: -1,
6099 shadow: true,
6100 fuchi: false,
6101 bold: false,
6102 reveal_index: 1,
6103 ruby: false,
6104 appeared: false,
6105 message_button: None,
6106 }
6107 }
6108}
6109
6110#[derive(Debug, Clone, Default, PartialEq, Eq)]
6111pub struct MwndRubyPendingState {
6112 pub text: String,
6113 pub start_pos: Option<(i64, i64)>,
6114}
6115
6116#[derive(Debug, Clone, Default, PartialEq, Eq)]
6120pub struct MwndMessagePageState {
6121 pub msg_text: String,
6122 pub glyphs: Vec<MwndGlyphState>,
6123 pub disp_moji_cnt: i64,
6124 pub hide_moji_cnt: i64,
6125 pub cur_msg_type: i64,
6126 pub cur_msg_type_decided: bool,
6127 pub ruby_start_pos: (i64, i64),
6128 pub ruby_start_ready: bool,
6129 pub cursor_pos: (i64, i64),
6130 pub moji_rep_pos: (i64, i64),
6131 pub indent_pos: i64,
6132 pub indent_moji: Option<char>,
6133 pub indent_count: i64,
6134 pub line_head: bool,
6135 pub ruby_pending: Option<MwndRubyPendingState>,
6136 pub moji_size: Option<i64>,
6137 pub moji_color: Option<i64>,
6138 pub shadow_color: Option<i64>,
6139 pub fuchi_color: Option<i64>,
6140 pub chara_moji_color: Option<i64>,
6141 pub chara_shadow_color: Option<i64>,
6142 pub chara_fuchi_color: Option<i64>,
6143 pub msgbtn: Option<(i64, i64, i64, i64)>,
6144}
6145
6146#[derive(Debug, Default, Clone)]
6147pub struct MwndSelectionChoice {
6148 pub text: String,
6149 pub kind: i64,
6150 pub color: i64,
6151 pub pos: (i64, i64),
6153 pub size: (i64, i64),
6154 pub glyphs: Vec<MwndGlyphState>,
6156}
6157
6158#[derive(Debug, Default, Clone)]
6159pub struct MwndSelectionState {
6160 pub choices: Vec<MwndSelectionChoice>,
6161 pub disp_item_count: usize,
6163 pub cursor: usize,
6164 pub cancel_enable: bool,
6165 pub close_mwnd: bool,
6166 pub result: i64,
6168}
6169
6170#[derive(Debug, Default, Clone)]
6171pub struct BtnSelItemState {
6172 pub generated_objects: Vec<ObjectState>,
6173 pub object_list: Vec<ObjectState>,
6174 pub strict: bool,
6175 pub text: String,
6176 pub item_type: i64,
6177 pub color: i64,
6178 pub pos: (i64, i64),
6179 pub size: (i64, i64),
6180 pub visible: bool,
6181 pub selected: bool,
6182 pub button_action_no: i64,
6183 pub button_state: i64,
6184 pub animation_offset: (i64, i64),
6186 pub animation_tr: Option<i64>,
6188}
6189
6190#[derive(Debug, Default, Clone)]
6191pub struct MwndState {
6192 pub initialized_from_gameexe: bool,
6193 pub open: bool,
6194 pub name_text: String,
6195 pub name_glyphs: Vec<MwndGlyphState>,
6197 pub msg_text: String,
6198 pub msg_waku_no: Option<i64>,
6199 pub waku_file: String,
6200 pub filter_file: String,
6201 pub filter_margin: Option<(i64, i64, i64, i64)>,
6202 pub filter_color: Option<(u8, u8, u8, u8)>,
6203 pub filter_config_color: bool,
6204 pub filter_config_tr: bool,
6205 pub waku_extend_type: i64,
6206 pub icon_no: i64,
6207 pub page_icon_no: i64,
6208 pub key_icon_appear: bool,
6209 pub key_icon_mode: i64,
6210 pub key_icon_pos: Option<(i64, i64)>,
6211 pub icon_pos_type: i64,
6212 pub icon_pos_base: i64,
6213 pub icon_pos: Option<(i64, i64, i64)>,
6214 pub waku_button_layout: Vec<(i64, i64, i64)>,
6216 pub waku_face_pos: Vec<(i64, i64)>,
6218 pub face_file: String,
6219 pub face_no: i64,
6220 pub rep_pos: Option<(i64, i64)>,
6221 pub msgbtn: Option<(i64, i64, i64, i64)>,
6222 pub window_pos: Option<(i64, i64)>,
6223 pub window_size: Option<(i64, i64)>,
6224 pub message_pos: Option<(i64, i64)>,
6225 pub message_margin: Option<(i64, i64, i64, i64)>,
6226 pub window_moji_cnt: Option<(i64, i64)>,
6227 pub moji_space: Option<(i64, i64)>,
6228 pub mwnd_extend_type: i64,
6229 pub multi_msg: bool,
6230 pub vertical_writing: bool,
6231 pub message_pages: Vec<MwndMessagePageState>,
6234 pub glyphs: Vec<MwndGlyphState>,
6236 pub disp_moji_cnt: i64,
6238 pub hide_moji_cnt: i64,
6239 pub cur_msg_type: i64,
6240 pub cur_msg_type_decided: bool,
6241 pub ruby_start_pos: (i64, i64),
6242 pub ruby_start_ready: bool,
6243 pub cursor_pos: (i64, i64),
6244 pub moji_rep_pos: (i64, i64),
6245 pub indent_pos: i64,
6246 pub indent_moji: Option<char>,
6247 pub indent_count: i64,
6248 pub line_head: bool,
6249 pub ruby_pending: Option<MwndRubyPendingState>,
6250 pub ruby_size: i64,
6251 pub ruby_space: i64,
6252 pub default_moji_size: i64,
6253 pub default_moji_color: i64,
6254 pub default_shadow_color: i64,
6255 pub default_fuchi_color: i64,
6256 pub default_name_moji_color: i64,
6257 pub default_name_shadow_color: i64,
6258 pub default_name_fuchi_color: i64,
6259 pub koe: Option<(i64, i64)>,
6260 pub order: i64,
6266 pub layer: i64,
6267 pub world: i64,
6268 pub moji_size: Option<i64>,
6269 pub moji_color: Option<i64>,
6270 pub shadow_color: Option<i64>,
6271 pub fuchi_color: Option<i64>,
6272 pub chara_color_mod: Option<i64>,
6273 pub chara_moji_color: Option<i64>,
6274 pub chara_shadow_color: Option<i64>,
6275 pub chara_fuchi_color: Option<i64>,
6276 pub name_moji_color: Option<i64>,
6277 pub name_shadow_color: Option<i64>,
6278 pub name_fuchi_color: Option<i64>,
6279 pub indent: bool,
6280 pub slide_msg: bool,
6281 pub slide_time: i64,
6282 pub open_anime_type: i64,
6283 pub open_anime_time: i64,
6284 pub close_anime_type: i64,
6285 pub close_anime_time: i64,
6286 pub selection: Option<MwndSelectionState>,
6287 pub read_flag_stock: Vec<(i64, i64)>,
6292
6293 pub novel_mode: i64,
6295 pub name_disp_mode: i64,
6296 pub name_bracket: i64,
6297 pub name_extend_type: i64,
6298 pub name_window_align: i64,
6299 pub name_window_pos: (i64, i64),
6300 pub name_window_size: (i64, i64),
6301 pub name_window_rect: (i64, i64, i64, i64),
6302 pub name_message_pos: (i64, i64),
6303 pub name_message_pos_rep: (i64, i64),
6304 pub name_message_margin: (i64, i64, i64, i64),
6305 pub overflow_check_size: i64,
6306 pub face_hide_name: i64,
6307 pub time: i64,
6308 pub auto_proc_ready: bool,
6309 pub window_appear: bool,
6310 pub name_appear: bool,
6311 pub auto_mode_end_moji_cnt: i64,
6312 pub target_msg_no: i64,
6313 pub koe_play_flag: bool,
6314 pub open_anime_start_time: i64,
6315 pub close_anime_start_time: i64,
6316
6317 pub text_dirty: bool,
6318 pub clear_ready: bool,
6319 pub msg_block_started: bool,
6320
6321 pub button_list: Vec<ObjectState>,
6322 pub button_list_strict: bool,
6323 pub face_list: Vec<ObjectState>,
6324 pub face_list_strict: bool,
6325 pub object_list: Vec<ObjectState>,
6326 pub object_list_strict: bool,
6327 pub props: HashMap<i32, i64>,
6328 pub aux_str_props: HashMap<i32, String>,
6329}
6330
6331#[derive(Debug, Default, Clone)]
6332pub struct StageFormState {
6333 pub backend_slot_base: usize,
6337 pub initialized_from_gameexe: bool,
6339 pub group_lists: HashMap<i64, Vec<GroupState>>,
6341 pub btnselitem_lists: HashMap<i64, Vec<BtnSelItemState>>,
6343 pub btn_select_states: HashMap<i64, BtnSelectRuntimeState>,
6348 pub mwnd_lists: HashMap<i64, Vec<MwndState>>,
6350 pub world_lists: HashMap<i64, Vec<WorldState>>,
6352 pub effect_lists: HashMap<i64, Vec<ScreenEffectState>>,
6354 pub quake_lists: HashMap<i64, Vec<ScreenQuakeState>>,
6356 pub object_lists: HashMap<i64, Vec<ObjectState>>,
6359 pub object_slot_use: HashMap<i64, Vec<bool>>,
6366 pub object_list_strict: HashMap<i64, bool>,
6368 pub rect_layers: HashMap<i64, LayerId>,
6370
6371 pub embedded_object_slots: HashMap<String, usize>,
6373 pub embedded_object_slots_by_stage: HashMap<i64, HashSet<usize>>,
6377 pub next_embedded_object_slot: HashMap<i64, usize>,
6378 pub next_nested_object_slot: HashMap<i64, usize>,
6379}
6380
6381pub fn normalize_screen_effect_scalar(
6386 ids: &crate::runtime::constants::RuntimeConstants,
6387 op: i32,
6388 value: i32,
6389) -> i32 {
6390 let byte_range = [
6391 ids.effect_mono,
6392 ids.effect_mono_eve,
6393 ids.effect_reverse,
6394 ids.effect_reverse_eve,
6395 ids.effect_bright,
6396 ids.effect_bright_eve,
6397 ids.effect_dark,
6398 ids.effect_dark_eve,
6399 ids.effect_color_r,
6400 ids.effect_color_r_eve,
6401 ids.effect_color_g,
6402 ids.effect_color_g_eve,
6403 ids.effect_color_b,
6404 ids.effect_color_b_eve,
6405 ids.effect_color_rate,
6406 ids.effect_color_rate_eve,
6407 ids.effect_color_add_r,
6408 ids.effect_color_add_r_eve,
6409 ids.effect_color_add_g,
6410 ids.effect_color_add_g_eve,
6411 ids.effect_color_add_b,
6412 ids.effect_color_add_b_eve,
6413 ];
6414 if byte_range.iter().any(|&id| id != 0 && op == id) {
6415 value.clamp(0, 255)
6416 } else {
6417 value
6418 }
6419}
6420
6421#[derive(Debug, Clone)]
6422pub struct ScreenEffectState {
6423 pub x: IntEvent,
6424 pub y: IntEvent,
6425 pub z: IntEvent,
6426 pub mono: IntEvent,
6427 pub reverse: IntEvent,
6428 pub bright: IntEvent,
6429 pub dark: IntEvent,
6430 pub color_r: IntEvent,
6431 pub color_g: IntEvent,
6432 pub color_b: IntEvent,
6433 pub color_rate: IntEvent,
6434 pub color_add_r: IntEvent,
6435 pub color_add_g: IntEvent,
6436 pub color_add_b: IntEvent,
6437 pub begin_order: i32,
6438 pub begin_layer: i32,
6439 pub end_order: i32,
6440 pub end_layer: i32,
6441 pub wipe_copy: i32,
6442 pub wipe_erase: i32,
6443}
6444
6445impl Default for ScreenEffectState {
6446 fn default() -> Self {
6447 Self {
6448 x: IntEvent::new(0),
6449 y: IntEvent::new(0),
6450 z: IntEvent::new(0),
6451 mono: IntEvent::new(0),
6452 reverse: IntEvent::new(0),
6453 bright: IntEvent::new(0),
6454 dark: IntEvent::new(0),
6455 color_r: IntEvent::new(0),
6456 color_g: IntEvent::new(0),
6457 color_b: IntEvent::new(0),
6458 color_rate: IntEvent::new(0),
6459 color_add_r: IntEvent::new(0),
6460 color_add_g: IntEvent::new(0),
6461 color_add_b: IntEvent::new(0),
6462 begin_order: 0,
6463 begin_layer: i32::MIN,
6464 end_order: 0,
6465 end_layer: i32::MAX,
6466 wipe_copy: 0,
6467 wipe_erase: 0,
6468 }
6469 }
6470}
6471impl ScreenEffectState {
6472 pub fn int_event_by_op(
6473 &self,
6474 ids: &crate::runtime::constants::RuntimeConstants,
6475 op: i32,
6476 ) -> Option<&IntEvent> {
6477 match op {
6478 s if s == ids.effect_x || s == ids.effect_x_eve => Some(&self.x),
6479 s if s == ids.effect_y || s == ids.effect_y_eve => Some(&self.y),
6480 s if s == ids.effect_z || s == ids.effect_z_eve => Some(&self.z),
6481 s if s == ids.effect_mono || s == ids.effect_mono_eve => Some(&self.mono),
6482 s if s == ids.effect_reverse || s == ids.effect_reverse_eve => Some(&self.reverse),
6483 s if s == ids.effect_bright || s == ids.effect_bright_eve => Some(&self.bright),
6484 s if s == ids.effect_dark || s == ids.effect_dark_eve => Some(&self.dark),
6485 s if s == ids.effect_color_r || s == ids.effect_color_r_eve => Some(&self.color_r),
6486 s if s == ids.effect_color_g || s == ids.effect_color_g_eve => Some(&self.color_g),
6487 s if s == ids.effect_color_b || s == ids.effect_color_b_eve => Some(&self.color_b),
6488 s if s == ids.effect_color_rate || s == ids.effect_color_rate_eve => {
6489 Some(&self.color_rate)
6490 }
6491 s if s == ids.effect_color_add_r || s == ids.effect_color_add_r_eve => {
6492 Some(&self.color_add_r)
6493 }
6494 s if s == ids.effect_color_add_g || s == ids.effect_color_add_g_eve => {
6495 Some(&self.color_add_g)
6496 }
6497 s if s == ids.effect_color_add_b || s == ids.effect_color_add_b_eve => {
6498 Some(&self.color_add_b)
6499 }
6500 _ => None,
6501 }
6502 }
6503
6504 pub fn int_event_by_op_mut(
6505 &mut self,
6506 ids: &crate::runtime::constants::RuntimeConstants,
6507 op: i32,
6508 ) -> Option<&mut IntEvent> {
6509 match op {
6510 s if s == ids.effect_x || s == ids.effect_x_eve => Some(&mut self.x),
6511 s if s == ids.effect_y || s == ids.effect_y_eve => Some(&mut self.y),
6512 s if s == ids.effect_z || s == ids.effect_z_eve => Some(&mut self.z),
6513 s if s == ids.effect_mono || s == ids.effect_mono_eve => Some(&mut self.mono),
6514 s if s == ids.effect_reverse || s == ids.effect_reverse_eve => Some(&mut self.reverse),
6515 s if s == ids.effect_bright || s == ids.effect_bright_eve => Some(&mut self.bright),
6516 s if s == ids.effect_dark || s == ids.effect_dark_eve => Some(&mut self.dark),
6517 s if s == ids.effect_color_r || s == ids.effect_color_r_eve => Some(&mut self.color_r),
6518 s if s == ids.effect_color_g || s == ids.effect_color_g_eve => Some(&mut self.color_g),
6519 s if s == ids.effect_color_b || s == ids.effect_color_b_eve => Some(&mut self.color_b),
6520 s if s == ids.effect_color_rate || s == ids.effect_color_rate_eve => {
6521 Some(&mut self.color_rate)
6522 }
6523 s if s == ids.effect_color_add_r || s == ids.effect_color_add_r_eve => {
6524 Some(&mut self.color_add_r)
6525 }
6526 s if s == ids.effect_color_add_g || s == ids.effect_color_add_g_eve => {
6527 Some(&mut self.color_add_g)
6528 }
6529 s if s == ids.effect_color_add_b || s == ids.effect_color_add_b_eve => {
6530 Some(&mut self.color_add_b)
6531 }
6532 _ => None,
6533 }
6534 }
6535
6536 pub fn reinit(&mut self) {
6537 *self = Self::default();
6538 }
6539
6540 pub fn tick(&mut self, delta: i32) {
6541 self.x.tick(delta);
6542 self.y.tick(delta);
6543 self.z.tick(delta);
6544 self.mono.tick(delta);
6545 self.reverse.tick(delta);
6546 self.bright.tick(delta);
6547 self.dark.tick(delta);
6548 self.color_r.tick(delta);
6549 self.color_g.tick(delta);
6550 self.color_b.tick(delta);
6551 self.color_rate.tick(delta);
6552 self.color_add_r.tick(delta);
6553 self.color_add_g.tick(delta);
6554 self.color_add_b.tick(delta);
6555 }
6556}
6557
6558#[derive(Debug, Clone, Copy, Default, PartialEq)]
6559pub struct ScreenQuakeTransform {
6560 pub x: i32,
6561 pub y: i32,
6562 pub scale: f32,
6563 pub rotate_degrees: f32,
6564 pub center_x: i32,
6565 pub center_y: i32,
6566}
6567
6568#[derive(Debug, Clone)]
6569pub struct ScreenQuakeState {
6570 pub quake_type: i32,
6571 pub vec: i32,
6572 pub power: i32,
6573 pub cur_time: i32,
6574 pub total_time: i32,
6575 pub ending: bool,
6576 pub end_cur_time: i32,
6577 pub end_total_time: i32,
6578 pub cnt: i32,
6579 pub end_cnt: i32,
6580 pub center_x: i32,
6581 pub center_y: i32,
6582 pub begin_order: i32,
6583 pub end_order: i32,
6584}
6585
6586impl Default for ScreenQuakeState {
6587 fn default() -> Self {
6588 Self {
6589 quake_type: -1,
6590 vec: 0,
6591 power: 0,
6592 cur_time: 0,
6593 total_time: 0,
6594 ending: false,
6595 end_cur_time: 0,
6596 end_total_time: 0,
6597 cnt: 0,
6598 end_cnt: 0,
6599 center_x: 0,
6600 center_y: 0,
6601 begin_order: 0,
6602 end_order: 0,
6603 }
6604 }
6605}
6606
6607fn speed_up_limit_i32(now: i32, start: i32, start_value: i32, end: i32, end_value: i32) -> i32 {
6608 if start == end {
6609 return end_value;
6610 }
6611 let lo = start.min(end) as f64;
6612 let hi = start.max(end) as f64;
6613 let t = (now as f64).clamp(lo, hi);
6614 let start = start as f64;
6615 let end = end as f64;
6616 let start_value = start_value as f64;
6617 let end_value = end_value as f64;
6618 (((t - start) * (t - start) * (end_value - start_value) / ((end - start) * (end - start)))
6619 + start_value) as i32
6620}
6621
6622fn speed_down_limit_i32(now: i32, start: i32, start_value: i32, end: i32, end_value: i32) -> i32 {
6623 if start == end {
6624 return end_value;
6625 }
6626 let lo = start.min(end) as f64;
6627 let hi = start.max(end) as f64;
6628 let t = (now as f64).clamp(lo, hi);
6629 let start = start as f64;
6630 let end = end as f64;
6631 let start_value = start_value as f64;
6632 let end_value = end_value as f64;
6633 (-(t - end) * (t - end) * (end_value - start_value) / ((end - start) * (end - start))
6634 + end_value) as i32
6635}
6636
6637fn linear_limit_f64(now: i32, start: i32, start_value: f64, end: i32, end_value: f64) -> f64 {
6638 if start == end {
6639 return end_value;
6640 }
6641 if now <= start.min(end) {
6642 return if start < end { start_value } else { end_value };
6643 }
6644 if now >= start.max(end) {
6645 return if start < end { end_value } else { start_value };
6646 }
6647 (end_value - start_value) * (now - start) as f64 / (end - start) as f64 + start_value
6648}
6649
6650impl ScreenQuakeState {
6651 pub fn reinit(&mut self) {
6652 *self = Self::default();
6653 }
6654
6655 pub fn start_kind(&mut self, quake_type: i32, time_ms: i64, cnt: i32, end_cnt: i32) {
6656 self.quake_type = quake_type;
6657 self.cur_time = 0;
6658 self.total_time = time_ms.clamp(i32::MIN as i64, i32::MAX as i64) as i32;
6659 self.cnt = cnt;
6660 self.end_cnt = end_cnt;
6661 self.ending = false;
6662 self.end_cur_time = 0;
6663 self.end_total_time = 0;
6664 if self.total_time <= 0 || !(0..=3).contains(&self.quake_type) {
6665 self.reinit();
6666 }
6667 }
6668
6669 pub fn end_ms(&mut self, time_ms: i64) {
6670 let time_ms = time_ms.clamp(0, i32::MAX as i64) as i32;
6671 if time_ms == 0 {
6672 self.reinit();
6673 return;
6674 }
6675 if self.quake_type < 0 {
6676 return;
6677 }
6678 self.ending = true;
6679 self.end_cur_time = 0;
6680 self.end_total_time = time_ms;
6681 }
6682
6683 pub fn check_value(&self) -> i32 {
6684 if self.quake_type < 0 {
6685 0
6686 } else if self.ending {
6687 2
6688 } else {
6689 1
6690 }
6691 }
6692
6693 pub fn is_active(&self) -> bool {
6694 self.quake_type >= 0 && self.total_time > 0
6695 }
6696
6697 pub fn is_infinite(&self) -> bool {
6698 self.is_active() && self.cnt == 0 && self.end_cnt == 0 && !self.ending
6699 }
6700
6701 pub fn remaining_ms(&self) -> Option<u64> {
6702 if !self.is_active() {
6703 return Some(0);
6704 }
6705 if self.ending {
6706 return Some(self.end_total_time.saturating_sub(self.end_cur_time).max(0) as u64);
6707 }
6708 if self.cnt == 0 && self.end_cnt == 0 {
6709 return None;
6710 }
6711 Some(
6712 self.total_time
6713 .saturating_mul(self.cnt.saturating_add(self.end_cnt))
6714 .saturating_sub(self.cur_time)
6715 .max(0) as u64,
6716 )
6717 }
6718
6719 pub fn tick(&mut self, delta_ms: i32) {
6720 if !self.is_active() {
6721 return;
6722 }
6723 let delta_ms = delta_ms.max(0);
6724 self.cur_time = self.cur_time.saturating_add(delta_ms);
6725 if self.ending {
6726 self.end_cur_time = self.end_cur_time.saturating_add(delta_ms);
6727 }
6728 let loop_forever = self.cnt == 0 && self.end_cnt == 0;
6729 if (!loop_forever
6730 && self.cur_time
6731 >= self
6732 .total_time
6733 .saturating_mul(self.cnt.saturating_add(self.end_cnt)))
6734 || (self.ending && self.end_cur_time >= self.end_total_time)
6735 {
6736 self.reinit();
6737 }
6738 }
6739
6740 pub fn transform(&self) -> ScreenQuakeTransform {
6741 const SCALE_UNIT: i32 = 1000;
6742 if !self.is_active() {
6743 return ScreenQuakeTransform {
6744 scale: 1.0,
6745 ..ScreenQuakeTransform::default()
6746 };
6747 }
6748 let quarter = (self.total_time / 4).max(1);
6749 let jump = self.cur_time.rem_euclid(self.total_time.max(1));
6750 let (x_sign, y_sign) = match self.vec {
6751 0 => (0, -1),
6752 1 => (1, -1),
6753 2 => (1, 0),
6754 3 => (1, 1),
6755 4 => (0, 1),
6756 5 => (-1, 1),
6757 6 => (-1, 0),
6758 7 => (-1, -1),
6759 _ => (0, 0),
6760 };
6761 let mut x = 0i32;
6762 let mut y = 0i32;
6763 let mut scale = SCALE_UNIT;
6764 let mut rotate = 0i32;
6765 match self.quake_type {
6766 0 => {
6767 let value = if jump < self.total_time / 4 {
6768 speed_up_limit_i32(jump, 0, 0, quarter, self.power / 2)
6769 } else if jump < self.total_time / 2 {
6770 speed_down_limit_i32(
6771 jump - self.total_time / 4,
6772 0,
6773 self.power / 2,
6774 quarter,
6775 self.power,
6776 )
6777 } else if jump < self.total_time * 3 / 4 {
6778 speed_up_limit_i32(
6779 jump - self.total_time / 2,
6780 0,
6781 self.power,
6782 quarter,
6783 self.power / 2,
6784 )
6785 } else {
6786 speed_down_limit_i32(
6787 jump - self.total_time * 3 / 4,
6788 0,
6789 self.power / 2,
6790 quarter,
6791 0,
6792 )
6793 };
6794 x = value.saturating_mul(x_sign);
6795 y = value.saturating_mul(y_sign);
6796 }
6797 1 => {
6798 let value = if jump < self.total_time / 4 {
6799 speed_down_limit_i32(jump, 0, 0, quarter, self.power / 2)
6800 } else if jump < self.total_time / 2 {
6801 speed_up_limit_i32(jump - self.total_time / 4, 0, self.power / 2, quarter, 0)
6802 } else if jump < self.total_time * 3 / 4 {
6803 speed_down_limit_i32(jump - self.total_time / 2, 0, 0, quarter, -self.power / 2)
6804 } else {
6805 speed_up_limit_i32(
6806 jump - self.total_time * 3 / 4,
6807 0,
6808 -self.power / 2,
6809 quarter,
6810 0,
6811 )
6812 };
6813 x = value.saturating_mul(x_sign);
6814 y = value.saturating_mul(y_sign);
6815 }
6816 2 => {
6817 let power = self.power.clamp(0, 255);
6818 let max_scale = 256 * SCALE_UNIT / (256 - power);
6819 let half_scale = (max_scale - SCALE_UNIT) / 2 + SCALE_UNIT;
6820 scale = if jump < self.total_time / 4 {
6821 speed_up_limit_i32(jump, 0, SCALE_UNIT, quarter, half_scale)
6822 } else if jump < self.total_time / 2 {
6823 speed_down_limit_i32(
6824 jump - self.total_time / 4,
6825 0,
6826 half_scale,
6827 quarter,
6828 max_scale,
6829 )
6830 } else if jump < self.total_time * 3 / 4 {
6831 speed_up_limit_i32(
6832 jump - self.total_time / 2,
6833 0,
6834 max_scale,
6835 quarter,
6836 half_scale,
6837 )
6838 } else {
6839 speed_down_limit_i32(
6840 jump - self.total_time * 3 / 4,
6841 0,
6842 half_scale,
6843 quarter,
6844 SCALE_UNIT,
6845 )
6846 };
6847 }
6848 3 => {}
6852 _ => {}
6853 }
6854
6855 let loop_forever = self.cnt == 0 && self.end_cnt == 0;
6856 if !loop_forever && self.cur_time >= self.total_time.saturating_mul(self.cnt) {
6857 let fade = linear_limit_f64(
6858 self.cur_time,
6859 self.total_time.saturating_mul(self.cnt),
6860 1.0,
6861 self.total_time
6862 .saturating_mul(self.cnt.saturating_add(self.end_cnt)),
6863 0.0,
6864 );
6865 x = (x as f64 * fade) as i32;
6866 y = (y as f64 * fade) as i32;
6867 scale = ((scale - SCALE_UNIT) as f64 * fade + SCALE_UNIT as f64) as i32;
6868 rotate = (rotate as f64 * fade) as i32;
6869 }
6870 if self.ending && self.end_total_time > 0 {
6871 let fade = linear_limit_f64(self.end_cur_time, 0, 1.0, self.end_total_time, 0.0);
6872 x = (x as f64 * fade) as i32;
6873 y = (y as f64 * fade) as i32;
6874 scale = ((scale - SCALE_UNIT) as f64 * fade + SCALE_UNIT as f64) as i32;
6875 rotate = (rotate as f64 * fade) as i32;
6876 }
6877
6878 ScreenQuakeTransform {
6879 x,
6880 y,
6881 scale: scale as f32 / SCALE_UNIT as f32,
6882 rotate_degrees: rotate as f32 / 1000.0,
6883 center_x: self.center_x,
6884 center_y: self.center_y,
6885 }
6886 }
6887}
6888
6889#[derive(Debug, Clone)]
6890pub struct ScreenShakeState {
6891 pub shake_no: i32,
6892 pub cur_time: i32,
6893 pub cur_x: i32,
6894 pub cur_y: i32,
6895}
6896
6897impl ScreenShakeState {
6898 pub fn init() -> Self {
6899 Self {
6900 shake_no: -1,
6901 cur_time: 0,
6902 cur_x: 0,
6903 cur_y: 0,
6904 }
6905 }
6906
6907 pub fn start(&mut self, shake_no: i64, table_count: usize) -> bool {
6908 if shake_no < 0 || shake_no as usize >= table_count {
6909 return false;
6910 }
6911 self.shake_no = shake_no as i32;
6912 self.cur_time = 0;
6913 self.cur_x = 0;
6914 self.cur_y = 0;
6915 true
6916 }
6917
6918 pub fn end(&mut self) {
6919 *self = Self::init();
6920 }
6921
6922 pub fn tick(&mut self, delta_ms: i32, templates: &[Vec<crate::runtime::tables::ShakeStep>]) {
6923 if self.shake_no < 0 || self.shake_no as usize >= templates.len() {
6924 self.end();
6925 return;
6926 }
6927 self.cur_time = self.cur_time.saturating_add(delta_ms.max(0));
6928 let mut total = 0i32;
6929 let mut found = None;
6930 for step in &templates[self.shake_no as usize] {
6931 let period_time = self.cur_time.saturating_sub(total);
6932 if period_time < step.time_ms {
6933 found = Some((step.x, step.y));
6934 break;
6935 }
6936 total = total.saturating_add(step.time_ms.max(0));
6937 }
6938 if let Some((x, y)) = found {
6939 self.cur_x = x;
6940 self.cur_y = y;
6941 } else {
6942 self.end();
6943 }
6944 }
6945
6946 pub fn is_active(&self) -> bool {
6947 self.shake_no >= 0
6948 }
6949}
6950
6951impl Default for ScreenShakeState {
6952 fn default() -> Self {
6953 Self::init()
6954 }
6955}
6956
6957#[derive(Debug, Default, Clone)]
6958pub struct ScreenFormState {
6959 pub effect_list: Vec<ScreenEffectState>,
6960 pub quake_list: Vec<ScreenQuakeState>,
6961 pub shake: ScreenShakeState,
6962}
6963
6964impl ScreenFormState {
6965 pub fn ensure_effect_len(&mut self, n: usize) {
6966 if self.effect_list.len() < n {
6967 self.effect_list
6968 .extend((0..(n - self.effect_list.len())).map(|_| ScreenEffectState::default()));
6969 } else if self.effect_list.len() > n {
6970 self.effect_list.truncate(n);
6971 }
6972 }
6973
6974 pub fn ensure_quake_len(&mut self, n: usize) {
6975 if self.quake_list.len() < n {
6976 self.quake_list
6977 .extend((0..(n - self.quake_list.len())).map(|_| ScreenQuakeState::default()));
6978 } else if self.quake_list.len() > n {
6979 self.quake_list.truncate(n);
6980 }
6981 }
6982
6983 pub fn tick(&mut self, delta: i32, shake_templates: &[Vec<crate::runtime::tables::ShakeStep>]) {
6984 for effect in &mut self.effect_list {
6985 effect.tick(delta);
6986 }
6987 for quake in &mut self.quake_list {
6988 quake.tick(delta);
6989 }
6990 self.shake.tick(delta, shake_templates);
6991 }
6992}
6993
6994#[derive(Debug, Default, Clone)]
6999pub struct MsgBackEntry {
7000 pub pct_flag: bool,
7001 pub msg_str: String,
7002 pub original_name: String,
7003 pub disp_name: String,
7004 pub pct_pos_x: i32,
7005 pub pct_pos_y: i32,
7006 pub koe_no_list: Vec<i64>,
7007 pub chr_no_list: Vec<i64>,
7008 pub koe_play_no: i64,
7009 pub debug_msg: String,
7010 pub scn_no: i64,
7011 pub line_no: i64,
7012 pub save_id: i64,
7013 pub save_id_check_flag: bool,
7014}
7015
7016#[derive(Debug, Clone)]
7017pub struct MsgBackState {
7018 pub history: Vec<MsgBackEntry>,
7019 pub history_cnt_max: usize,
7020 pub history_cnt: usize,
7021 pub history_start_pos: usize,
7022 pub history_insert_pos: usize,
7023 pub history_last_pos: usize,
7024 pub new_msg_flag: bool,
7025}
7026
7027impl Default for MsgBackState {
7028 fn default() -> Self {
7029 let history_cnt_max = 256usize;
7030 Self {
7031 history: vec![
7032 MsgBackEntry {
7033 scn_no: -1,
7034 line_no: -1,
7035 ..MsgBackEntry::default()
7036 };
7037 history_cnt_max
7038 ],
7039 history_cnt_max,
7040 history_cnt: 0,
7041 history_start_pos: 0,
7042 history_insert_pos: 0,
7043 history_last_pos: 0,
7044 new_msg_flag: true,
7045 }
7046 }
7047}
7048
7049impl MsgBackState {
7050 fn reset_entry(entry: &mut MsgBackEntry) {
7051 *entry = MsgBackEntry {
7052 scn_no: -1,
7053 line_no: -1,
7054 ..MsgBackEntry::default()
7055 };
7056 }
7057
7058 fn ensure_capacity(&mut self) {
7059 if self.history_cnt_max == 0 {
7060 self.history_cnt_max = 256;
7061 }
7062 if self.history.len() != self.history_cnt_max {
7063 self.history
7064 .resize_with(self.history_cnt_max, || MsgBackEntry {
7065 scn_no: -1,
7066 line_no: -1,
7067 ..MsgBackEntry::default()
7068 });
7069 }
7070 self.history_insert_pos %= self.history_cnt_max;
7071 self.history_start_pos %= self.history_cnt_max;
7072 self.history_last_pos %= self.history_cnt_max;
7073 }
7074
7075 pub fn set_history_cnt_max(&mut self, max_count: usize) {
7076 let max_count = max_count.max(1);
7077 if max_count == self.history_cnt_max {
7078 self.ensure_capacity();
7079 return;
7080 }
7081 let mut ordered = self
7082 .ordered_history_indices()
7083 .into_iter()
7084 .filter_map(|idx| self.history.get(idx).cloned())
7085 .collect::<Vec<_>>();
7086 if ordered.len() > max_count {
7087 let drop_count = ordered.len() - max_count;
7088 ordered.drain(0..drop_count);
7089 }
7090 self.history_cnt_max = max_count;
7091 self.history = vec![
7092 MsgBackEntry {
7093 scn_no: -1,
7094 line_no: -1,
7095 ..MsgBackEntry::default()
7096 };
7097 max_count
7098 ];
7099 self.history_cnt = ordered.len();
7100 self.history_start_pos = 0;
7101 for (i, entry) in ordered.into_iter().enumerate() {
7102 self.history[i] = entry;
7103 }
7104 self.history_insert_pos = self.history_cnt % self.history_cnt_max;
7105 self.history_last_pos = self
7106 .history_cnt
7107 .saturating_sub(1)
7108 .min(self.history_cnt_max - 1);
7109 self.new_msg_flag = true;
7110 }
7111
7112 fn ready_msg(&mut self) -> &mut MsgBackEntry {
7113 self.ensure_capacity();
7114 if self.new_msg_flag {
7115 if self.history_cnt < self.history_cnt_max {
7116 self.history_cnt += 1;
7117 } else {
7118 self.history_start_pos = (self.history_start_pos + 1) % self.history_cnt_max;
7119 }
7120 Self::reset_entry(&mut self.history[self.history_insert_pos]);
7121 self.new_msg_flag = false;
7122 }
7123 &mut self.history[self.history_insert_pos]
7124 }
7125
7126 pub fn clear(&mut self) {
7127 self.ensure_capacity();
7128 for entry in &mut self.history {
7129 Self::reset_entry(entry);
7130 }
7131 self.history_cnt = 0;
7132 self.history_start_pos = 0;
7133 self.history_insert_pos = 0;
7134 self.history_last_pos = 0;
7135 self.new_msg_flag = true;
7136 }
7137
7138 pub fn ordered_history_indices(&self) -> Vec<usize> {
7139 if self.history_cnt_max == 0 || self.history_cnt == 0 {
7140 return Vec::new();
7141 }
7142 (0..self.history_cnt)
7143 .map(|i| (self.history_start_pos + i) % self.history_cnt_max)
7144 .collect()
7145 }
7146
7147 pub fn next(&mut self) {
7148 self.ensure_capacity();
7149 if self.new_msg_flag {
7150 return;
7151 }
7152 let Some(cur) = self.history.get(self.history_insert_pos) else {
7153 self.new_msg_flag = true;
7154 return;
7155 };
7156 if !cur.pct_flag && cur.msg_str.is_empty() {
7157 return;
7158 }
7159 self.history_insert_pos = (self.history_insert_pos + 1) % self.history_cnt_max;
7160 self.new_msg_flag = true;
7161 }
7162
7163 pub fn add_koe(&mut self, koe_no: i64, chara_no: i64, scn_no: i64, line_no: i64) -> bool {
7164 if koe_no < 0 {
7165 return true;
7166 }
7167 let insert_pos = self.history_insert_pos;
7168 let entry = self.ready_msg();
7169 entry.koe_no_list.push(koe_no);
7170 entry.chr_no_list.push(chara_no);
7171 entry.scn_no = scn_no;
7172 entry.line_no = line_no;
7173 self.history_last_pos = insert_pos;
7174 true
7175 }
7176
7177 pub fn add_name(
7178 &mut self,
7179 original_name: &str,
7180 disp_name: &str,
7181 scn_no: i64,
7182 line_no: i64,
7183 ) -> bool {
7184 if disp_name.is_empty() {
7185 return true;
7186 }
7187 let insert_pos = self.history_insert_pos;
7188 let entry = self.ready_msg();
7189 entry.original_name.clear();
7190 entry.original_name.push_str(original_name);
7191 entry.disp_name.clear();
7192 entry.disp_name.push_str(disp_name);
7193 entry.scn_no = scn_no;
7194 entry.line_no = line_no;
7195 self.history_last_pos = insert_pos;
7196 true
7197 }
7198
7199 pub fn add_msg(&mut self, msg: &str, debug_msg: &str, scn_no: i64, line_no: i64) -> bool {
7200 if msg.is_empty() {
7201 return true;
7202 }
7203 let insert_pos = self.history_insert_pos;
7204 let entry = self.ready_msg();
7205 entry.msg_str.push_str(msg);
7206 entry.debug_msg.clear();
7207 entry.debug_msg.push_str(debug_msg);
7208 entry.scn_no = scn_no;
7209 entry.line_no = line_no;
7210 self.history_last_pos = insert_pos;
7211 true
7212 }
7213
7214 pub fn add_new_line_indent(&mut self, scn_no: i64, line_no: i64) -> bool {
7215 let insert_pos = self.history_insert_pos;
7216 let entry = self.ready_msg();
7217 entry.msg_str.push('\n');
7218 entry.scn_no = scn_no;
7219 entry.line_no = line_no;
7220 self.history_last_pos = insert_pos;
7221 true
7222 }
7223
7224 pub fn add_new_line_no_indent(&mut self, scn_no: i64, line_no: i64) -> bool {
7225 let insert_pos = self.history_insert_pos;
7226 let entry = self.ready_msg();
7227 entry.msg_str.push('\u{0007}');
7228 entry.scn_no = scn_no;
7229 entry.line_no = line_no;
7230 self.history_last_pos = insert_pos;
7231 true
7232 }
7233
7234 pub fn add_pct(&mut self, file_name: &str, x: i32, y: i32) -> bool {
7235 if file_name.is_empty() {
7236 return false;
7237 }
7238 self.next();
7239 let insert_pos = self.history_insert_pos;
7240 let entry = self.ready_msg();
7241 entry.pct_flag = true;
7242 entry.pct_pos_x = x;
7243 entry.pct_pos_y = y;
7244 entry.msg_str.clear();
7245 entry.msg_str.push_str(file_name);
7246 self.history_last_pos = insert_pos;
7247 self.next();
7248 true
7249 }
7250
7251 pub fn current_entry(&self) -> Option<&MsgBackEntry> {
7252 self.history.get(self.history_insert_pos)
7253 }
7254}
7255
7256impl StageFormState {
7257 #[inline(always)]
7261 pub fn object_slot_is_used(&self, stage_idx: i64, slot: usize) -> bool {
7262 self.object_slot_use
7263 .get(&stage_idx)
7264 .and_then(|flags| flags.get(slot))
7265 .copied()
7266 .unwrap_or(true)
7267 }
7268
7269 pub fn ensure_group_list(&mut self, stage_idx: i64, cnt: usize) {
7270 let entry = self.group_lists.entry(stage_idx).or_default();
7271 if entry.len() < cnt {
7272 entry.extend((0..(cnt - entry.len())).map(|_| GroupState::default()));
7273 } else if entry.len() > cnt {
7274 entry.truncate(cnt);
7275 }
7276 }
7277
7278 pub fn clear_group_list(&mut self, stage_idx: i64) {
7279 self.group_lists.insert(stage_idx, Vec::new());
7280 }
7281
7282 pub fn ensure_mwnd_list(&mut self, stage_idx: i64, cnt: usize) {
7283 let entry = self.mwnd_lists.entry(stage_idx).or_default();
7284 if entry.len() < cnt {
7285 entry.extend((0..(cnt - entry.len())).map(|_| MwndState::default()));
7286 } else if entry.len() > cnt {
7287 entry.truncate(cnt);
7288 }
7289 }
7290
7291 pub fn ensure_effect_list(&mut self, stage_idx: i64, cnt: usize) {
7292 let entry = self.effect_lists.entry(stage_idx).or_default();
7293 if entry.len() < cnt {
7294 entry.extend((0..(cnt - entry.len())).map(|_| ScreenEffectState::default()));
7295 } else if entry.len() > cnt {
7296 entry.truncate(cnt);
7297 }
7298 }
7299
7300 pub fn ensure_quake_list(&mut self, stage_idx: i64, cnt: usize) {
7301 let entry = self.quake_lists.entry(stage_idx).or_default();
7302 if entry.len() < cnt {
7303 entry.extend((0..(cnt - entry.len())).map(|_| ScreenQuakeState::default()));
7304 } else if entry.len() > cnt {
7305 entry.truncate(cnt);
7306 }
7307 }
7308
7309 pub fn close_all_mwnd(&mut self, stage_idx: i64) {
7310 if let Some(list) = self.mwnd_lists.get_mut(&stage_idx) {
7311 for (idx, m) in list.iter_mut().enumerate() {
7312 let old_open = m.open;
7313 m.open = false;
7314 if env_is_set!("SG_DEBUG") {
7315 eprintln!(
7316 "[SG_DEBUG][MWND_STATE_TRACE] scene=<runtime> scene_no=- line=- reason=STAGE_CLOSE_ALL_MWND stage={} mwnd={} old_open={} new_open={} buttons={} faces={} objects={} waku={} filter={} pos={:?} size={:?} open_anim=({}, {}) close_anim=({}, {}) selection={} msg_len={} name_len={}",
7317 stage_idx,
7318 idx,
7319 old_open,
7320 m.open,
7321 m.button_list.len(),
7322 m.face_list.len(),
7323 m.object_list.len(),
7324 if m.waku_file.is_empty() {
7325 "-"
7326 } else {
7327 m.waku_file.as_str()
7328 },
7329 if m.filter_file.is_empty() {
7330 "-"
7331 } else {
7332 m.filter_file.as_str()
7333 },
7334 m.window_pos,
7335 m.window_size,
7336 m.open_anime_type,
7337 m.open_anime_time,
7338 m.close_anime_type,
7339 m.close_anime_time,
7340 m.selection.is_some(),
7341 m.msg_text.len(),
7342 m.name_text.len(),
7343 );
7344 }
7345 }
7346 }
7347 }
7348
7349 pub fn ensure_object_list(&mut self, stage_idx: i64, cnt: usize) {
7350 let entry = self.object_lists.entry(stage_idx).or_default();
7351 if entry.len() < cnt {
7352 entry.extend((0..(cnt - entry.len())).map(|_| ObjectState::default()));
7353 } else if entry.len() > cnt {
7354 entry.truncate(cnt);
7355 }
7356
7357 let slot_use = self.object_slot_use.entry(stage_idx).or_default();
7358 if slot_use.len() < cnt {
7359 slot_use.extend((0..(cnt - slot_use.len())).map(|_| true));
7360 } else if slot_use.len() > cnt {
7361 slot_use.truncate(cnt);
7362 }
7363 }
7364
7365 pub fn set_object_list_len_strict(&mut self, stage_idx: i64, cnt: usize) {
7366 self.ensure_object_list(stage_idx, cnt);
7367 self.object_list_strict.insert(stage_idx, true);
7368 }
7369
7370 pub fn object_list_len(&self, stage_idx: i64) -> usize {
7371 self.object_lists
7372 .get(&stage_idx)
7373 .map(|v| v.len())
7374 .unwrap_or(0)
7375 }
7376
7377 #[inline(always)]
7378 pub fn is_embedded_object_slot(&self, stage_idx: i64, slot: usize) -> bool {
7379 self.embedded_object_slots_by_stage
7380 .get(&stage_idx)
7381 .is_some_and(|slots| slots.contains(&slot))
7382 }
7383
7384 pub fn register_embedded_object_slot(&mut self, stage_idx: i64, key: String, slot: usize) {
7385 self.embedded_object_slots.insert(key, slot);
7386 self.embedded_object_slots_by_stage
7387 .entry(stage_idx)
7388 .or_default()
7389 .insert(slot);
7390 }
7391
7392 pub fn clear_embedded_object_slots_for_stage(&mut self, stage_idx: i64) {
7393 let prefix = format!("{stage_idx}:");
7394 self.embedded_object_slots
7395 .retain(|key, _| !key.starts_with(&prefix));
7396 self.embedded_object_slots_by_stage.remove(&stage_idx);
7397 }
7398}
7399
7400impl MaskListState {
7401 pub fn new(mask_cnt: usize) -> Self {
7402 let mut masks = Vec::with_capacity(mask_cnt);
7403 for _ in 0..mask_cnt {
7404 masks.push(MaskState::new());
7405 }
7406 Self { masks }
7407 }
7408
7409 pub fn ensure_size(&mut self, mask_cnt: usize) {
7410 if self.masks.len() < mask_cnt {
7411 self.masks.reserve(mask_cnt - self.masks.len());
7412 while self.masks.len() < mask_cnt {
7413 self.masks.push(MaskState::new());
7414 }
7415 } else if self.masks.len() > mask_cnt {
7416 self.masks.truncate(mask_cnt);
7417 }
7418 }
7419
7420 pub fn tick_frame(&mut self, delta: i32) {
7421 for m in &mut self.masks {
7422 m.x_event.tick(delta);
7423 m.y_event.tick(delta);
7424 for ev in m.script_events.values_mut() {
7425 ev.tick(delta);
7426 }
7427 }
7428 }
7429}
7430
7431#[derive(Debug, Clone, Copy)]
7432pub struct ExcallTickTargets {
7433 pub counter_list_id: u32,
7434 pub frame_action_id: u32,
7435 pub frame_action_ch_list_id: u32,
7436 pub stage_form_id: u32,
7437}
7438
7439impl GlobalState {
7440 pub fn ensure_read_flag_count(&mut self, scene_no: i64, flag_count: usize) {
7441 if scene_no < 0 {
7442 return;
7443 }
7444 let row = self.read_flags.entry(scene_no).or_default();
7445 row.resize(flag_count, 0);
7446 }
7447
7448 pub fn set_read_flag(&mut self, scene_no: i64, flag_no: i64) -> bool {
7449 if scene_no < 0 || flag_no < 0 {
7450 return false;
7451 }
7452 let Ok(flag_no) = usize::try_from(flag_no) else {
7453 return false;
7454 };
7455 let row = self.read_flags.entry(scene_no).or_default();
7456 if row.len() <= flag_no {
7457 row.resize(flag_no + 1, 0);
7461 }
7462 row[flag_no] = 1;
7463 true
7464 }
7465
7466 pub fn read_flag(&self, scene_no: i64, flag_no: i64) -> bool {
7467 if scene_no < 0 || flag_no < 0 {
7468 return false;
7469 }
7470 self.read_flags
7471 .get(&scene_no)
7472 .and_then(|row| row.get(flag_no as usize))
7473 .copied()
7474 .unwrap_or(0)
7475 != 0
7476 }
7477
7478 pub fn start_wipe(&mut self, w: WipeState) {
7479 self.wipe = Some(w);
7480 }
7481
7482 pub fn finish_wipe(&mut self) {
7483 self.wipe = None;
7484 }
7485
7486 pub fn wipe_done(&self) -> bool {
7487 self.wipe.as_ref().map(|w| w.is_done()).unwrap_or(true)
7488 }
7489
7490 pub fn tick_frame(
7491 &mut self,
7492 past_game_time: i32,
7493 past_real_time: i32,
7494 shake_templates: &[Vec<crate::runtime::tables::ShakeStep>],
7495 excall: Option<ExcallTickTargets>,
7496 ) {
7497 let wipe_active = self.wipe.is_some();
7502 self.render_frame = self.render_frame.wrapping_add(1);
7503 self.local_real_time = self
7504 .local_real_time
7505 .saturating_add(past_real_time.max(0) as i64);
7506 self.local_game_time = self
7507 .local_game_time
7508 .saturating_add(past_game_time.max(0) as i64);
7509 let past_wipe_time = past_game_time.max(0);
7510 self.local_wipe_time = self.local_wipe_time.saturating_add(past_wipe_time as i64);
7511 if let Some(wipe) = self.wipe.as_mut() {
7512 wipe.advance(past_wipe_time);
7513 }
7514 if self.change_display_mode_proc_cnt > 0 {
7517 self.change_display_mode_proc_cnt -= 1;
7518 }
7519 self.mov.tick(past_real_time);
7520
7521 let excall_counter_id = excall.map(|v| v.counter_list_id);
7522 let excall_frame_action_id = excall.map(|v| v.frame_action_id);
7523 let excall_frame_action_ch_list_id = excall.map(|v| v.frame_action_ch_list_id);
7524 let excall_stage_form_id = excall.map(|v| v.stage_form_id);
7525
7526 if !self.script.time_stop_flag {
7529 self.fog_global.update_time(past_game_time, past_real_time);
7530 self.fog_global.frame();
7531
7532 if !self.script.counter_time_stop_flag {
7533 let mut counter_ids: Vec<u32> = self.counter_lists.keys().copied().collect();
7534 counter_ids.sort_unstable();
7535 for counter_id in counter_ids {
7536 if Some(counter_id) == excall_counter_id {
7537 continue;
7538 }
7539 let Some(counters) = self.counter_lists.get_mut(&counter_id) else {
7540 continue;
7541 };
7542 for counter in counters {
7543 counter.update_time(past_game_time, past_real_time);
7544 }
7545 }
7546 }
7547
7548 if !self.script.frame_action_time_stop_flag {
7549 let mut frame_action_ids: Vec<u32> = self.frame_actions.keys().copied().collect();
7550 frame_action_ids.sort_unstable();
7551 for frame_action_id in frame_action_ids {
7552 if Some(frame_action_id) == excall_frame_action_id {
7553 continue;
7554 }
7555 let Some(fa) = self.frame_actions.get_mut(&frame_action_id) else {
7556 continue;
7557 };
7558 fa.counter.update_time(past_game_time, past_real_time);
7559 }
7560 let mut frame_action_list_ids: Vec<u32> =
7561 self.frame_action_lists.keys().copied().collect();
7562 frame_action_list_ids.sort_unstable();
7563 for frame_action_list_id in frame_action_list_ids {
7564 if Some(frame_action_list_id) == excall_frame_action_ch_list_id {
7565 continue;
7566 }
7567 let Some(list) = self.frame_action_lists.get_mut(&frame_action_list_id) else {
7568 continue;
7569 };
7570 for fa in list {
7571 fa.counter.update_time(past_game_time, past_real_time);
7572 }
7573 }
7574 }
7575
7576 let mut mask_list_ids: Vec<u32> = self.mask_lists.keys().copied().collect();
7577 mask_list_ids.sort_unstable();
7578 for mask_list_id in mask_list_ids {
7579 let Some(ml) = self.mask_lists.get_mut(&mask_list_id) else {
7580 continue;
7581 };
7582 ml.tick_frame(past_game_time.max(0));
7583 }
7584
7585 let mut screen_form_ids: Vec<u32> = self.screen_forms.keys().copied().collect();
7586 screen_form_ids.sort_unstable();
7587 for screen_form_id in screen_form_ids {
7588 let Some(sc) = self.screen_forms.get_mut(&screen_form_id) else {
7589 continue;
7590 };
7591 sc.tick(past_game_time.max(0), shake_templates);
7592 }
7593
7594 let mut int_event_root_ids: Vec<u32> = self.int_event_roots.keys().copied().collect();
7595 int_event_root_ids.sort_unstable();
7596 for int_event_root_id in int_event_root_ids {
7597 let Some(ev) = self.int_event_roots.get_mut(&int_event_root_id) else {
7598 continue;
7599 };
7600 ev.update_time(past_game_time, past_real_time);
7601 ev.frame();
7602 }
7603 let mut int_event_list_ids: Vec<u32> = self.int_event_lists.keys().copied().collect();
7604 int_event_list_ids.sort_unstable();
7605 for int_event_list_id in int_event_list_ids {
7606 let Some(events) = self.int_event_lists.get_mut(&int_event_list_id) else {
7607 continue;
7608 };
7609 for ev in events {
7610 ev.update_time(past_game_time, past_real_time);
7611 ev.frame();
7612 }
7613 }
7614
7615 if !self.script.stage_time_stop_flag {
7616 let mut stage_form_ids: Vec<u32> = self.stage_forms.keys().copied().collect();
7617 stage_form_ids.sort_unstable();
7618 for stage_form_id in stage_form_ids {
7619 if Some(stage_form_id) == excall_stage_form_id {
7620 continue;
7621 }
7622 self.tick_stage_form(
7623 stage_form_id,
7624 past_game_time,
7625 past_real_time,
7626 wipe_active,
7627 );
7628 }
7629 }
7630 }
7631
7632 if let Some(excall) = excall {
7637 if let Some(counters) = self.counter_lists.get_mut(&excall.counter_list_id) {
7638 for counter in counters {
7639 counter.update_time(past_game_time, past_real_time);
7640 }
7641 }
7642 if let Some(fa) = self.frame_actions.get_mut(&excall.frame_action_id) {
7643 fa.counter.update_time(past_game_time, past_real_time);
7644 }
7645 if let Some(list) = self
7646 .frame_action_lists
7647 .get_mut(&excall.frame_action_ch_list_id)
7648 {
7649 for fa in list {
7650 fa.counter.update_time(past_game_time, past_real_time);
7651 }
7652 }
7653 self.tick_stage_form(
7654 excall.stage_form_id,
7655 past_game_time,
7656 past_real_time,
7657 wipe_active,
7658 );
7659 }
7660 }
7661
7662 fn tick_stage_form(
7663 &mut self,
7664 stage_form_id: u32,
7665 past_game_time: i32,
7666 past_real_time: i32,
7667 wipe_active: bool,
7668 ) {
7669 let Some(st) = self.stage_forms.get_mut(&stage_form_id) else {
7670 return;
7671 };
7672
7673 let mut object_stage_ids: Vec<i64> = st.object_lists.keys().copied().collect();
7674 object_stage_ids.sort_unstable();
7675 for object_stage_id in object_stage_ids {
7676 if object_stage_id == 2 && !wipe_active {
7677 continue;
7678 }
7679 let embedded_slots = st.embedded_object_slots_by_stage.get(&object_stage_id);
7680 let Some(objs) = st.object_lists.get_mut(&object_stage_id) else {
7681 continue;
7682 };
7683 for (obj_idx, obj) in objs.iter_mut().enumerate() {
7684 if embedded_slots.is_some_and(|slots| slots.contains(&obj_idx)) {
7685 continue;
7686 }
7687 obj.tick(past_game_time, past_real_time);
7688 }
7689 }
7690
7691 let mut mwnd_stage_ids: Vec<i64> = st.mwnd_lists.keys().copied().collect();
7692 mwnd_stage_ids.sort_unstable();
7693 for mwnd_stage_id in mwnd_stage_ids {
7694 if mwnd_stage_id == 2 && !wipe_active {
7695 continue;
7696 }
7697 let Some(mwnds) = st.mwnd_lists.get_mut(&mwnd_stage_id) else {
7698 continue;
7699 };
7700 for mwnd in mwnds {
7701 for obj in &mut mwnd.button_list {
7702 obj.tick(past_game_time, past_real_time);
7703 }
7704 for obj in &mut mwnd.face_list {
7705 obj.tick(past_game_time, past_real_time);
7706 }
7707 for obj in &mut mwnd.object_list {
7708 obj.tick(past_game_time, past_real_time);
7709 }
7710 }
7711 }
7712
7713 let mut world_stage_ids: Vec<i64> = st.world_lists.keys().copied().collect();
7714 world_stage_ids.sort_unstable();
7715 for world_stage_id in world_stage_ids {
7716 if world_stage_id == 2 && !wipe_active {
7717 continue;
7718 }
7719 let Some(worlds) = st.world_lists.get_mut(&world_stage_id) else {
7720 continue;
7721 };
7722 for w in worlds {
7723 w.update_time(past_game_time, past_real_time);
7724 w.frame();
7725 }
7726 }
7727
7728 let mut effect_stage_ids: Vec<i64> = st.effect_lists.keys().copied().collect();
7729 effect_stage_ids.sort_unstable();
7730 for effect_stage_id in effect_stage_ids {
7731 if effect_stage_id == 2 && !wipe_active {
7732 continue;
7733 }
7734 let Some(effects) = st.effect_lists.get_mut(&effect_stage_id) else {
7735 continue;
7736 };
7737 for effect in effects {
7738 effect.tick(past_game_time.max(0));
7739 }
7740 }
7741
7742 let mut quake_stage_ids: Vec<i64> = st.quake_lists.keys().copied().collect();
7743 quake_stage_ids.sort_unstable();
7744 for quake_stage_id in quake_stage_ids {
7745 if quake_stage_id == 2 && !wipe_active {
7746 continue;
7747 }
7748 let Some(quakes) = st.quake_lists.get_mut(&quake_stage_id) else {
7749 continue;
7750 };
7751 for quake in quakes {
7752 quake.tick(past_game_time.max(0));
7753 }
7754 }
7755 }
7756}
7757
7758#[cfg(test)]
7759mod wipe_stage_tick_tests {
7760 use super::{ExcallTickTargets, GlobalState, StageFormState, WipeState, WorldState};
7761
7762 const TEST_STAGE_FORM_ID: u32 = 49;
7763 const NEXT_STAGE: i64 = 2;
7764
7765 fn next_world_event_time(globals: &GlobalState) -> i32 {
7766 globals.stage_forms[&TEST_STAGE_FORM_ID].world_lists[&NEXT_STAGE][0]
7767 .camera_eye_x
7768 .cur_time
7769 }
7770
7771 #[test]
7772 fn next_stage_events_advance_only_while_wipe_is_active() {
7773 let mut globals = GlobalState::default();
7774 let mut stage = StageFormState::default();
7775 let mut world = WorldState::new(0);
7776 world.camera_eye_x.set_event(100, 1_000, 0, 0, 0);
7777 stage.world_lists.insert(NEXT_STAGE, vec![world]);
7778 globals
7779 .stage_forms
7780 .insert(TEST_STAGE_FORM_ID, Box::new(stage));
7781
7782 globals.tick_frame(10, 10, &[], None);
7783 assert_eq!(next_world_event_time(&globals), 0);
7784
7785 globals.start_wipe(WipeState::new(
7786 TEST_STAGE_FORM_ID,
7787 None,
7788 None,
7789 0,
7790 1_000,
7791 0,
7792 0,
7793 Vec::new(),
7794 i32::MIN,
7795 i32::MAX,
7796 i32::MIN,
7797 i32::MAX,
7798 false,
7799 0,
7800 0,
7801 ));
7802 globals.tick_frame(10, 10, &[], None);
7803 assert_eq!(next_world_event_time(&globals), 10);
7804
7805 globals.finish_wipe();
7806 globals.tick_frame(10, 10, &[], None);
7807 assert_eq!(next_world_event_time(&globals), 10);
7808 }
7809
7810 #[test]
7811 fn main_time_stop_freezes_stage_but_not_wipe_clock() {
7812 let mut globals = GlobalState::default();
7813 let mut stage = StageFormState::default();
7814 let mut world = WorldState::new(0);
7815 world.camera_eye_x.set_event(100, 1_000, 0, 0, 0);
7816 stage.world_lists.insert(NEXT_STAGE, vec![world]);
7817 globals
7818 .stage_forms
7819 .insert(TEST_STAGE_FORM_ID, Box::new(stage));
7820 globals.start_wipe(WipeState::new(
7821 TEST_STAGE_FORM_ID,
7822 None,
7823 None,
7824 0,
7825 1_000,
7826 0,
7827 0,
7828 Vec::new(),
7829 i32::MIN,
7830 i32::MAX,
7831 i32::MIN,
7832 i32::MAX,
7833 false,
7834 0,
7835 0,
7836 ));
7837 globals.script.time_stop_flag = true;
7838
7839 globals.tick_frame(10, 10, &[], None);
7840
7841 assert_eq!(next_world_event_time(&globals), 0);
7842 assert_eq!(globals.local_wipe_time, 10);
7843 }
7844
7845 #[test]
7846 fn stage_time_stop_freezes_stage_events_during_wipe() {
7847 let mut globals = GlobalState::default();
7848 let mut stage = StageFormState::default();
7849 let mut world = WorldState::new(0);
7850 world.camera_eye_x.set_event(100, 1_000, 0, 0, 0);
7851 stage.world_lists.insert(NEXT_STAGE, vec![world]);
7852 globals
7853 .stage_forms
7854 .insert(TEST_STAGE_FORM_ID, Box::new(stage));
7855 globals.start_wipe(WipeState::new(
7856 TEST_STAGE_FORM_ID,
7857 None,
7858 None,
7859 0,
7860 1_000,
7861 0,
7862 0,
7863 Vec::new(),
7864 i32::MIN,
7865 i32::MAX,
7866 i32::MIN,
7867 i32::MAX,
7868 false,
7869 0,
7870 0,
7871 ));
7872 globals.script.stage_time_stop_flag = true;
7873
7874 globals.tick_frame(10, 10, &[], None);
7875
7876 assert_eq!(next_world_event_time(&globals), 0);
7877 }
7878
7879 #[test]
7880 fn excall_stage_advances_while_local_time_is_stopped() {
7881 const FRONT_STAGE: i64 = 1;
7882 const EXCALL_STAGE_FORM_ID: u32 = TEST_STAGE_FORM_ID ^ 0x4000;
7883
7884 let mut globals = GlobalState::default();
7885
7886 let mut normal_stage = StageFormState::default();
7887 let mut normal_world = WorldState::new(0);
7888 normal_world.camera_eye_x.set_event(100, 1_000, 0, 0, 0);
7889 normal_stage
7890 .world_lists
7891 .insert(FRONT_STAGE, vec![normal_world]);
7892 globals
7893 .stage_forms
7894 .insert(TEST_STAGE_FORM_ID, Box::new(normal_stage));
7895
7896 let mut excall_stage = StageFormState::default();
7897 let mut excall_world = WorldState::new(0);
7898 excall_world.camera_eye_x.set_event(100, 1_000, 0, 0, 0);
7899 excall_stage
7900 .world_lists
7901 .insert(FRONT_STAGE, vec![excall_world]);
7902 globals
7903 .stage_forms
7904 .insert(EXCALL_STAGE_FORM_ID, Box::new(excall_stage));
7905
7906 globals.script.time_stop_flag = true;
7907 globals.tick_frame(
7908 10,
7909 10,
7910 &[],
7911 Some(ExcallTickTargets {
7912 counter_list_id: 0x7000_0001,
7913 frame_action_id: 0x7000_0002,
7914 frame_action_ch_list_id: 0x7000_0003,
7915 stage_form_id: EXCALL_STAGE_FORM_ID,
7916 }),
7917 );
7918
7919 assert_eq!(
7920 globals.stage_forms[&TEST_STAGE_FORM_ID].world_lists[&FRONT_STAGE][0]
7921 .camera_eye_x
7922 .cur_time,
7923 0
7924 );
7925 assert_eq!(
7926 globals.stage_forms[&EXCALL_STAGE_FORM_ID].world_lists[&FRONT_STAGE][0]
7927 .camera_eye_x
7928 .cur_time,
7929 10
7930 );
7931 }
7932}