Skip to main content

siglus_scene_vm/runtime/
graphics.rs

1//! Graphics runtime: bridges VM stage/object operations to `LayerManager` + `ImageManager`.
2//!
3//! This layer maps stage/object operations onto renderable sprites while preserving
4//! stable sprite identities for the VM runtime.
5
6use anyhow::{bail, Context, Result};
7
8use crate::image_manager::{ImageId, ImageManager};
9use crate::layer::{
10    ClipRect, LayerId, LayerManager, Sprite, SpriteBlend, SpriteFit, SpriteId, SpriteSizeMode,
11};
12
13fn sg_cgm_coord_trace_enabled() -> bool {
14    std::env::var_os("SG_DEBUG").is_some()
15}
16
17fn sg_cgm_coord_trace(msg: impl AsRef<str>) {
18    if sg_cgm_coord_trace_enabled() {
19        eprintln!("[SG_DEBUG][CGM_COORD_TRACE][GFX] {}", msg.as_ref());
20    }
21}
22
23fn cgm_file_interesting(file: Option<&str>) -> bool {
24    file.map(|name| name.to_ascii_lowercase().contains("cgm_"))
25        .unwrap_or(false)
26}
27
28#[derive(Debug, Clone)]
29struct ObjectState {
30    is_bg: bool,
31
32    // Render binding for non-BG objects.
33    layer_id: Option<LayerId>,
34    sprite_id: Option<SpriteId>,
35
36    // Logical properties.
37    is_mesh: bool,
38    file: Option<String>,
39    patno: i64,
40    disp: bool,
41    x: i64,
42    y: i64,
43    layer_no: i64,
44    order: i64,
45    alpha: i64,
46    /// Stored but not used for sorting (Siglus draw order follows tree traversal).
47    z: i64,
48    center_x: i64,
49    center_y: i64,
50    scale_x: i64,
51    scale_y: i64,
52    rotate_z: i64,
53    clip_use: i64,
54    clip_left: i64,
55    clip_top: i64,
56    clip_right: i64,
57    clip_bottom: i64,
58    src_clip_use: i64,
59    src_clip_left: i64,
60    src_clip_top: i64,
61    src_clip_right: i64,
62    src_clip_bottom: i64,
63    tr: i64,
64    mono: i64,
65    reverse: i64,
66    bright: i64,
67    dark: i64,
68    color_rate: i64,
69    color_add_r: i64,
70    color_add_g: i64,
71    color_add_b: i64,
72    color_r: i64,
73    color_g: i64,
74    color_b: i64,
75    blend: i64,
76    light_no: i64,
77    fog_use: i64,
78}
79
80impl Default for ObjectState {
81    fn default() -> Self {
82        Self {
83            is_bg: false,
84            layer_id: None,
85            sprite_id: None,
86            is_mesh: false,
87            file: None,
88            patno: 0,
89            disp: false,
90            x: 0,
91            y: 0,
92            layer_no: 0,
93            order: 0,
94            alpha: 255,
95            z: 0,
96            center_x: 0,
97            center_y: 0,
98            scale_x: 1000,
99            scale_y: 1000,
100            rotate_z: 0,
101            clip_use: 0,
102            clip_left: 0,
103            clip_top: 0,
104            clip_right: 0,
105            clip_bottom: 0,
106            src_clip_use: 0,
107            src_clip_left: 0,
108            src_clip_top: 0,
109            src_clip_right: 0,
110            src_clip_bottom: 0,
111            tr: 255,
112            mono: 0,
113            reverse: 0,
114            bright: 0,
115            dark: 0,
116            color_rate: 0,
117            color_add_r: 0,
118            color_add_g: 0,
119            color_add_b: 0,
120            color_r: 0,
121            color_g: 0,
122            color_b: 0,
123            blend: 0,
124            light_no: -1,
125            fog_use: 0,
126        }
127    }
128}
129
130#[derive(Debug, Clone)]
131pub struct DebugObjectSpriteBinding {
132    pub stage: usize,
133    pub obj_idx: usize,
134    pub is_bg: bool,
135    pub layer_id: Option<LayerId>,
136    pub sprite_id: Option<SpriteId>,
137    pub file: Option<String>,
138    pub patno: i64,
139    pub disp: bool,
140    pub x: i64,
141    pub y: i64,
142    pub layer_no: i64,
143    pub order: i64,
144    pub alpha: i64,
145    pub z: i64,
146    pub tr: i64,
147    pub clip_use: i64,
148    pub clip_left: i64,
149    pub clip_top: i64,
150    pub clip_right: i64,
151    pub clip_bottom: i64,
152    pub scale_x: i64,
153    pub scale_y: i64,
154    pub rotate_z: i64,
155}
156
157#[derive(Debug, Clone)]
158struct StageState {
159    layer_id: Option<LayerId>,
160    objects: Vec<ObjectState>,
161}
162
163impl Default for StageState {
164    fn default() -> Self {
165        Self {
166            layer_id: None,
167            objects: Vec::new(),
168        }
169    }
170}
171
172#[derive(Debug)]
173pub struct GfxRuntime {
174    /// Current logical layer number selected by named commands (LAYER/LAYER_SET).
175    /// Used as a default for CHR/object operations when scripts omit an explicit layer.
176    pub current_layer: i32,
177    stages: [StageState; 3],
178}
179
180impl Default for GfxRuntime {
181    fn default() -> Self {
182        Self {
183            current_layer: 0,
184            stages: [
185                StageState::default(),
186                StageState::default(),
187                StageState::default(),
188            ],
189        }
190    }
191}
192
193impl GfxRuntime {
194    pub fn new() -> Self {
195        Self::default()
196    }
197
198    fn ensure_stage(&mut self, stage: usize) -> &mut StageState {
199        &mut self.stages[stage]
200    }
201
202    fn ensure_stage_layer(&mut self, layers: &mut LayerManager, stage: usize) -> LayerId {
203        let st = self.ensure_stage(stage);
204        if let Some(id) = st.layer_id {
205            return id;
206        }
207        let id = layers.create_layer();
208        st.layer_id = Some(id);
209        id
210    }
211
212    /// Expose stage layer allocation for non-Gfx backends (e.g., movie sprites).
213    pub fn ensure_stage_layer_id(
214        &mut self,
215        layers: &mut LayerManager,
216        stage: i64,
217    ) -> Option<LayerId> {
218        if stage < 0 || stage > 2 {
219            return None;
220        }
221        Some(self.ensure_stage_layer(layers, stage as usize))
222    }
223
224    fn ensure_object_mut(&mut self, stage: usize, obj_idx: usize) -> &mut ObjectState {
225        let st = self.ensure_stage(stage);
226        if st.objects.len() <= obj_idx {
227            st.objects.resize_with(obj_idx + 1, Default::default);
228        }
229        &mut st.objects[obj_idx]
230    }
231
232    fn object(&self, stage: usize, obj_idx: usize) -> Option<&ObjectState> {
233        self.stages.get(stage)?.objects.get(obj_idx)
234    }
235
236    fn reset_object_for_create(
237        &mut self,
238        layers: &mut LayerManager,
239        stage: usize,
240        obj_idx: usize,
241    ) {
242        let (layer_id, sprite_id) = {
243            let obj = self.ensure_object_mut(stage, obj_idx);
244            (obj.layer_id, obj.sprite_id)
245        };
246
247        {
248            let obj = self.ensure_object_mut(stage, obj_idx);
249            *obj = ObjectState::default();
250            obj.layer_id = layer_id;
251            obj.sprite_id = sprite_id;
252            obj.is_bg = stage == 0 && obj_idx == 0;
253        }
254
255        if stage == 0 && obj_idx == 0 {
256            *layers.bg_mut() = Sprite::default();
257            return;
258        }
259
260        if let (Some(lid), Some(sid)) = (layer_id, sprite_id) {
261            if let Some(sprite) = layers.layer_mut(lid).and_then(|layer| layer.sprite_mut(sid)) {
262                *sprite = Sprite::default();
263            }
264        }
265    }
266
267
268    pub fn debug_object_snapshot(
269        &self,
270        stage: usize,
271        obj_idx: usize,
272    ) -> Option<DebugObjectSpriteBinding> {
273        let obj = self.object(stage, obj_idx)?;
274        Some(DebugObjectSpriteBinding {
275            stage,
276            obj_idx,
277            is_bg: obj.is_bg,
278            layer_id: obj.layer_id,
279            sprite_id: obj.sprite_id,
280            file: obj.file.clone(),
281            patno: obj.patno,
282            disp: obj.disp,
283            x: obj.x,
284            y: obj.y,
285            layer_no: obj.layer_no,
286            order: obj.order,
287            alpha: obj.alpha,
288            z: obj.z,
289            tr: obj.tr,
290            clip_use: obj.clip_use,
291            clip_left: obj.clip_left,
292            clip_top: obj.clip_top,
293            clip_right: obj.clip_right,
294            clip_bottom: obj.clip_bottom,
295            scale_x: obj.scale_x,
296            scale_y: obj.scale_y,
297            rotate_z: obj.rotate_z,
298        })
299    }
300
301    pub fn object_sprite_binding(&self, stage: i64, obj_idx: i64) -> Option<(LayerId, SpriteId)> {
302        let stage_i = stage as isize;
303        if !(0..3).contains(&stage_i) || obj_idx < 0 {
304            return None;
305        }
306        let obj = self.object(stage_i as usize, obj_idx as usize)?;
307        if obj.is_bg {
308            return None;
309        }
310        match (obj.layer_id, obj.sprite_id) {
311            (Some(lid), Some(sid)) => Some((lid, sid)),
312            _ => None,
313        }
314    }
315
316    fn load_any_image(images: &mut ImageManager, file: &str, patno: i64) -> Result<ImageId> {
317        // Engine preference: g00 first, then bg fallback.
318        let pat_u32 = if patno < 0 { 0 } else { patno as u32 };
319        match images.load_g00(file, pat_u32) {
320            Ok(id) => Ok(id),
321            Err(_) => images
322                .load_bg(file)
323                .with_context(|| format!("failed to load image as g00/bg: {file}")),
324        }
325    }
326
327    fn ensure_bound_sprite(
328        &mut self,
329        layers: &mut LayerManager,
330        stage: usize,
331        obj_idx: usize,
332    ) -> Result<(LayerId, SpriteId)> {
333        let st_layer = self.ensure_stage_layer(layers, stage);
334        let obj = self.ensure_object_mut(stage, obj_idx);
335
336        if obj.is_bg {
337            bail!("BG object does not have a bound sprite");
338        }
339
340        if let (Some(lid), Some(sid)) = (obj.layer_id, obj.sprite_id) {
341            return Ok((lid, sid));
342        }
343
344        let sid = {
345            let layer = layers
346                .layer_mut(st_layer)
347                .context("stage layer not found")?;
348            layer.create_sprite()
349        };
350
351        // Initialize with sane defaults.
352        if let Some(layer) = layers.layer_mut(st_layer) {
353            if let Some(sprite) = layer.sprite_mut(sid) {
354                sprite.visible = true;
355                sprite.alpha = 255;
356                sprite.fit = SpriteFit::PixelRect;
357                sprite.size_mode = SpriteSizeMode::Intrinsic;
358                sprite.x = 0;
359                sprite.y = 0;
360                sprite.order = 0;
361            }
362        }
363
364        obj.layer_id = Some(st_layer);
365        obj.sprite_id = Some(sid);
366        Ok((st_layer, sid))
367    }
368
369    fn sync_object_sprite(
370        &mut self,
371        images: &mut ImageManager,
372        layers: &mut LayerManager,
373        stage: usize,
374        obj_idx: usize,
375    ) -> Result<()> {
376        let obj = self.ensure_object_mut(stage, obj_idx).clone();
377
378        if obj.is_bg {
379            let bg = layers.bg_mut();
380            bg.visible = obj.disp;
381            bg.x = obj.x as i32;
382            bg.y = obj.y as i32;
383            bg.alpha = obj.alpha.clamp(0, 255) as u8;
384            bg.fit = SpriteFit::FullScreen;
385            bg.size_mode = SpriteSizeMode::Intrinsic;
386            bg.scale_x = obj.scale_x as f32 / 1000.0;
387            bg.scale_y = obj.scale_y as f32 / 1000.0;
388            bg.rotate = obj.rotate_z as f32 * std::f32::consts::PI / 1800.0;
389            bg.pivot_x = obj.center_x as f32;
390            bg.pivot_y = obj.center_y as f32;
391            bg.dst_clip = clip_rect(
392                obj.clip_use,
393                obj.clip_left,
394                obj.clip_top,
395                obj.clip_right,
396                obj.clip_bottom,
397            );
398            bg.src_clip = clip_rect(
399                obj.src_clip_use,
400                obj.src_clip_left,
401                obj.src_clip_top,
402                obj.src_clip_right,
403                obj.src_clip_bottom,
404            );
405            bg.tr = obj.tr.clamp(0, 255) as u8;
406            bg.mono = obj.mono.clamp(0, 255) as u8;
407            bg.reverse = obj.reverse.clamp(0, 255) as u8;
408            bg.bright = obj.bright.clamp(0, 255) as u8;
409            bg.dark = obj.dark.clamp(0, 255) as u8;
410            bg.color_rate = obj.color_rate.clamp(0, 255) as u8;
411            bg.color_add_r = obj.color_add_r.clamp(0, 255) as u8;
412            bg.color_add_g = obj.color_add_g.clamp(0, 255) as u8;
413            bg.color_add_b = obj.color_add_b.clamp(0, 255) as u8;
414            bg.color_r = obj.color_r.clamp(0, 255) as u8;
415            bg.color_g = obj.color_g.clamp(0, 255) as u8;
416            bg.color_b = obj.color_b.clamp(0, 255) as u8;
417            bg.blend = SpriteBlend::from_i64(obj.blend);
418            bg.light_no = obj.light_no as i32;
419            bg.fog_use = obj.fog_use != 0;
420
421            if obj.is_mesh {
422                bg.image_id = None;
423                bg.mesh_file_name = obj.file.clone();
424                bg.mesh_kind = 1;
425                bg.camera_enabled = true;
426            } else if let Some(file) = &obj.file {
427                match Self::load_any_image(images, file, obj.patno) {
428                    Ok(img_id) => {
429                        bg.image_id = Some(img_id);
430                        bg.object_anchor = false;
431                        bg.texture_center_x = 0.0;
432                        bg.texture_center_y = 0.0;
433                    }
434                    Err(err) if is_probable_mesh_path(file) => {
435                        let _ = err;
436                        bg.image_id = None;
437                    }
438                    Err(err) => return Err(err),
439                }
440            }
441            return Ok(());
442        }
443
444        let (lid, sid) = self.ensure_bound_sprite(layers, stage, obj_idx)?;
445        let sprite = layers
446            .layer_mut(lid)
447            .and_then(|l| l.sprite_mut(sid))
448            .context("sprite not found")?;
449
450        sprite.visible = obj.disp;
451        sprite.x = obj.x as i32;
452        sprite.y = obj.y as i32;
453        sprite.alpha = obj.alpha.clamp(0, 255) as u8;
454        sprite.scale_x = obj.scale_x as f32 / 1000.0;
455        sprite.scale_y = obj.scale_y as f32 / 1000.0;
456        sprite.rotate = obj.rotate_z as f32 * std::f32::consts::PI / 1800.0;
457        sprite.pivot_x = obj.center_x as f32;
458        sprite.pivot_y = obj.center_y as f32;
459        sprite.dst_clip = clip_rect(
460            obj.clip_use,
461            obj.clip_left,
462            obj.clip_top,
463            obj.clip_right,
464            obj.clip_bottom,
465        );
466        sprite.src_clip = clip_rect(
467            obj.src_clip_use,
468            obj.src_clip_left,
469            obj.src_clip_top,
470            obj.src_clip_right,
471            obj.src_clip_bottom,
472        );
473        sprite.tr = obj.tr.clamp(0, 255) as u8;
474        sprite.mono = obj.mono.clamp(0, 255) as u8;
475        sprite.reverse = obj.reverse.clamp(0, 255) as u8;
476        sprite.bright = obj.bright.clamp(0, 255) as u8;
477        sprite.dark = obj.dark.clamp(0, 255) as u8;
478        sprite.color_rate = obj.color_rate.clamp(0, 255) as u8;
479        sprite.color_add_r = obj.color_add_r.clamp(0, 255) as u8;
480        sprite.color_add_g = obj.color_add_g.clamp(0, 255) as u8;
481        sprite.color_add_b = obj.color_add_b.clamp(0, 255) as u8;
482        sprite.color_r = obj.color_r.clamp(0, 255) as u8;
483        sprite.color_g = obj.color_g.clamp(0, 255) as u8;
484        sprite.color_b = obj.color_b.clamp(0, 255) as u8;
485        sprite.blend = SpriteBlend::from_i64(obj.blend);
486        sprite.light_no = obj.light_no as i32;
487        sprite.fog_use = obj.fog_use != 0;
488
489        // Order: stage layer_no is treated as a coarse z, order as fine z.
490        let coarse = obj.layer_no.clamp(-10000, 10000) as i32;
491        let fine = obj.order.clamp(-100000, 100000) as i32;
492        sprite.order = coarse.saturating_mul(1000).saturating_add(fine);
493
494        if obj.is_mesh {
495            sprite.image_id = None;
496            sprite.mesh_file_name = obj.file.clone();
497            sprite.mesh_kind = 1;
498            sprite.camera_enabled = true;
499            sprite.shadow_cast = true;
500            sprite.shadow_receive = true;
501            sprite.object_anchor = false;
502            sprite.texture_center_x = 0.0;
503            sprite.texture_center_y = 0.0;
504        } else if let Some(file) = &obj.file {
505            match Self::load_any_image(images, file, obj.patno) {
506                Ok(img_id) => {
507                    set_object_sprite_image(sprite, images, img_id);
508                }
509                Err(err) if is_probable_mesh_path(file) => {
510                    let _ = err;
511                    sprite.image_id = None;
512                    sprite.object_anchor = false;
513                    sprite.texture_center_x = 0.0;
514                    sprite.texture_center_y = 0.0;
515                }
516                Err(err) => return Err(err),
517            }
518        }
519
520        Ok(())
521    }
522
523    pub fn object_set_center(
524        &mut self,
525        images: &mut ImageManager,
526        layers: &mut LayerManager,
527        stage: i64,
528        obj_idx: i64,
529        x: i64,
530        y: i64,
531    ) -> Result<()> {
532        let stage_i = stage as isize;
533        if !(0..3).contains(&stage_i) || obj_idx < 0 {
534            return Ok(());
535        }
536        let stage_u = stage_i as usize;
537        let obj_u = obj_idx as usize;
538        {
539            let obj = self.ensure_object_mut(stage_u, obj_u);
540            obj.center_x = x;
541            obj.center_y = y;
542        }
543        self.sync_object_sprite(images, layers, stage_u, obj_u)
544    }
545
546    pub fn object_set_scale(
547        &mut self,
548        images: &mut ImageManager,
549        layers: &mut LayerManager,
550        stage: i64,
551        obj_idx: i64,
552        x: i64,
553        y: i64,
554    ) -> Result<()> {
555        let stage_i = stage as isize;
556        if !(0..3).contains(&stage_i) || obj_idx < 0 {
557            return Ok(());
558        }
559        let stage_u = stage_i as usize;
560        let obj_u = obj_idx as usize;
561        {
562            let obj = self.ensure_object_mut(stage_u, obj_u);
563            obj.scale_x = x;
564            obj.scale_y = y;
565        }
566        self.sync_object_sprite(images, layers, stage_u, obj_u)
567    }
568
569    pub fn object_set_rotate(
570        &mut self,
571        images: &mut ImageManager,
572        layers: &mut LayerManager,
573        stage: i64,
574        obj_idx: i64,
575        z: i64,
576    ) -> Result<()> {
577        let stage_i = stage as isize;
578        if !(0..3).contains(&stage_i) || obj_idx < 0 {
579            return Ok(());
580        }
581        let stage_u = stage_i as usize;
582        let obj_u = obj_idx as usize;
583        {
584            let obj = self.ensure_object_mut(stage_u, obj_u);
585            obj.rotate_z = z;
586        }
587        self.sync_object_sprite(images, layers, stage_u, obj_u)
588    }
589
590    pub fn object_set_clip(
591        &mut self,
592        images: &mut ImageManager,
593        layers: &mut LayerManager,
594        stage: i64,
595        obj_idx: i64,
596        use_flag: i64,
597        left: i64,
598        top: i64,
599        right: i64,
600        bottom: i64,
601    ) -> Result<()> {
602        let stage_i = stage as isize;
603        if !(0..3).contains(&stage_i) || obj_idx < 0 {
604            return Ok(());
605        }
606        let stage_u = stage_i as usize;
607        let obj_u = obj_idx as usize;
608        {
609            let obj = self.ensure_object_mut(stage_u, obj_u);
610            obj.clip_use = use_flag;
611            obj.clip_left = left;
612            obj.clip_top = top;
613            obj.clip_right = right;
614            obj.clip_bottom = bottom;
615        }
616        self.sync_object_sprite(images, layers, stage_u, obj_u)
617    }
618
619    pub fn object_set_src_clip(
620        &mut self,
621        images: &mut ImageManager,
622        layers: &mut LayerManager,
623        stage: i64,
624        obj_idx: i64,
625        use_flag: i64,
626        left: i64,
627        top: i64,
628        right: i64,
629        bottom: i64,
630    ) -> Result<()> {
631        let stage_i = stage as isize;
632        if !(0..3).contains(&stage_i) || obj_idx < 0 {
633            return Ok(());
634        }
635        let stage_u = stage_i as usize;
636        let obj_u = obj_idx as usize;
637        {
638            let obj = self.ensure_object_mut(stage_u, obj_u);
639            obj.src_clip_use = use_flag;
640            obj.src_clip_left = left;
641            obj.src_clip_top = top;
642            obj.src_clip_right = right;
643            obj.src_clip_bottom = bottom;
644        }
645        self.sync_object_sprite(images, layers, stage_u, obj_u)
646    }
647
648    pub fn stage_clear(
649        &mut self,
650        images: &mut ImageManager,
651        layers: &mut LayerManager,
652        stage: i64,
653    ) -> Result<()> {
654        let stage_i = stage as isize;
655        if !(0..3).contains(&stage_i) {
656            return Ok(());
657        }
658        let stage_u = stage_i as usize;
659        let len = self.stages[stage_u].objects.len();
660        for idx in 0..len {
661            {
662                let obj = self.ensure_object_mut(stage_u, idx);
663                obj.disp = false;
664            }
665            let _ = self.sync_object_sprite(images, layers, stage_u, idx);
666        }
667        Ok(())
668    }
669
670    fn object_create_impl(
671        &mut self,
672        images: &mut ImageManager,
673        layers: &mut LayerManager,
674        stage: i64,
675        obj_idx: i64,
676        file: &str,
677        disp: i64,
678        x: i64,
679        y: i64,
680        patno: i64,
681        reinit: bool,
682    ) -> Result<()> {
683        let stage_i = stage as isize;
684        if !(0..3).contains(&stage_i) {
685            bail!("invalid stage: {stage}");
686        }
687        if obj_idx < 0 {
688            bail!("invalid obj idx: {obj_idx}");
689        }
690
691        let stage_u = stage_i as usize;
692        let obj_u = obj_idx as usize;
693        let current_layer = self.current_layer;
694
695        if reinit {
696            self.reset_object_for_create(layers, stage_u, obj_u);
697        }
698
699        {
700            let obj = self.ensure_object_mut(stage_u, obj_u);
701            obj.is_bg = stage_u == 0 && obj_u == 0;
702            obj.is_mesh = false;
703            obj.file = Some(file.to_string());
704            obj.patno = patno;
705            obj.disp = disp != 0;
706            obj.x = x;
707            obj.y = y;
708
709            // Default layer number from current selection (can be overridden by scripts).
710            if obj.layer_no == 0 {
711                obj.layer_no = current_layer as i64;
712            }
713        }
714
715        // Ensure render binding exists for non-bg.
716        if !(stage_u == 0 && obj_u == 0) {
717            let _ = self.ensure_bound_sprite(layers, stage_u, obj_u)?;
718        }
719
720        if cgm_file_interesting(Some(file)) || (30..=59).contains(&obj_u) {
721            let obj = self.object(stage_u, obj_u);
722            sg_cgm_coord_trace(format!(
723                "object_create stage={} obj={} file={} disp={} x={} y={} patno={} reinit={} layer_no={:?} binding={:?}/{:?}",
724                stage,
725                obj_idx,
726                file,
727                disp,
728                x,
729                y,
730                patno,
731                reinit,
732                obj.map(|o| o.layer_no),
733                obj.and_then(|o| o.layer_id),
734                obj.and_then(|o| o.sprite_id)
735            ));
736        }
737
738        self.sync_object_sprite(images, layers, stage_u, obj_u)
739    }
740
741    pub fn object_create(
742        &mut self,
743        images: &mut ImageManager,
744        layers: &mut LayerManager,
745        stage: i64,
746        obj_idx: i64,
747        file: &str,
748        disp: i64,
749        x: i64,
750        y: i64,
751        patno: i64,
752    ) -> Result<()> {
753        self.object_create_impl(images, layers, stage, obj_idx, file, disp, x, y, patno, true)
754    }
755
756    /// Mirror of C++ `C_elm_object::restruct_pct` (and the `restruct_type`
757    /// dispatch around it) executed at the tail of `C_elm_object::load`. After
758    /// a save-file load, the per-object gfx runtime (sprite binding, image
759    /// asset, transform/color state) is empty - the saved stream restores
760    /// `globals::ObjectState` but the rendering side has no equivalent storage
761    /// in the save format. This rebuilds the gfx side from the loaded globals
762    /// so the next render frame sees the same picture the save captured.
763    ///
764    /// Caller filters: only invoke for Gfx-backed objects whose `file_name` is
765    /// non-empty; everything else (mesh, movie, weather, number, string) needs
766    /// its own backend-specific path and is no-op here.
767    pub fn restore_gfx_object_from_globals(
768        &mut self,
769        images: &mut ImageManager,
770        layers: &mut LayerManager,
771        stage: i64,
772        obj_idx: i64,
773        src: &crate::runtime::globals::ObjectState,
774    ) -> Result<()> {
775        let stage_i = stage as isize;
776        if !(0..3).contains(&stage_i) || obj_idx < 0 {
777            return Ok(());
778        }
779        let file = match src.file_name.as_deref() {
780            Some(f) if !f.is_empty() => f.to_string(),
781            _ => return Ok(()),
782        };
783        let stage_u = stage_i as usize;
784        let obj_u = obj_idx as usize;
785
786        let current_layer = self.current_layer;
787        self.reset_object_for_create(layers, stage_u, obj_u);
788        {
789            let pe = &src.runtime.prop_events;
790            let dst = self.ensure_object_mut(stage_u, obj_u);
791            dst.is_bg = stage_u == 0 && obj_u == 0;
792            dst.is_mesh = false;
793            dst.file = Some(file);
794            dst.patno = src.base.patno;
795            dst.disp = src.base.disp != 0;
796            dst.x = pe.x.get_total_value() as i64;
797            dst.y = pe.y.get_total_value() as i64;
798            dst.z = pe.z.get_total_value() as i64;
799            dst.layer_no = src.base.layer;
800            dst.order = src.base.order;
801            dst.center_x = pe.center_x.get_total_value() as i64;
802            dst.center_y = pe.center_y.get_total_value() as i64;
803            dst.scale_x = pe.scale_x.get_total_value() as i64;
804            dst.scale_y = pe.scale_y.get_total_value() as i64;
805            dst.rotate_z = pe.rotate_z.get_total_value() as i64;
806            dst.clip_use = src.base.clip_use;
807            dst.clip_left = pe.clip_left.get_total_value() as i64;
808            dst.clip_top = pe.clip_top.get_total_value() as i64;
809            dst.clip_right = pe.clip_right.get_total_value() as i64;
810            dst.clip_bottom = pe.clip_bottom.get_total_value() as i64;
811            dst.src_clip_use = src.base.src_clip_use;
812            dst.src_clip_left = pe.src_clip_left.get_total_value() as i64;
813            dst.src_clip_top = pe.src_clip_top.get_total_value() as i64;
814            dst.src_clip_right = pe.src_clip_right.get_total_value() as i64;
815            dst.src_clip_bottom = pe.src_clip_bottom.get_total_value() as i64;
816            let tr = pe.tr.get_total_value() as i64;
817            dst.alpha = tr;
818            dst.tr = tr;
819            dst.mono = pe.mono.get_total_value() as i64;
820            dst.reverse = pe.reverse.get_total_value() as i64;
821            dst.bright = pe.bright.get_total_value() as i64;
822            dst.dark = pe.dark.get_total_value() as i64;
823            dst.color_rate = pe.color_rate.get_total_value() as i64;
824            dst.color_add_r = pe.color_add_r.get_total_value() as i64;
825            dst.color_add_g = pe.color_add_g.get_total_value() as i64;
826            dst.color_add_b = pe.color_add_b.get_total_value() as i64;
827            dst.color_r = pe.color_r.get_total_value() as i64;
828            dst.color_g = pe.color_g.get_total_value() as i64;
829            dst.color_b = pe.color_b.get_total_value() as i64;
830            dst.blend = src.base.blend;
831            dst.light_no = src.base.light_no;
832            dst.fog_use = src.base.fog_use;
833            if dst.layer_no == 0 {
834                dst.layer_no = current_layer as i64;
835            }
836        }
837
838        if !(stage_u == 0 && obj_u == 0) {
839            let _ = self.ensure_bound_sprite(layers, stage_u, obj_u)?;
840        }
841        self.sync_object_sprite(images, layers, stage_u, obj_u)
842    }
843
844    pub fn object_change_file(
845        &mut self,
846        images: &mut ImageManager,
847        layers: &mut LayerManager,
848        stage: i64,
849        obj_idx: i64,
850        file: &str,
851        disp: i64,
852        x: i64,
853        y: i64,
854        patno: i64,
855    ) -> Result<()> {
856        self.object_create_impl(images, layers, stage, obj_idx, file, disp, x, y, patno, false)
857    }
858
859    pub fn object_create_mesh(
860        &mut self,
861        layers: &mut LayerManager,
862        stage: i64,
863        obj_idx: i64,
864        file: &str,
865        disp: i64,
866        x: i64,
867        y: i64,
868        patno: i64,
869    ) -> Result<()> {
870        let stage_i = stage as isize;
871        if !(0..3).contains(&stage_i) {
872            bail!("invalid stage: {stage}");
873        }
874        if obj_idx < 0 {
875            bail!("invalid obj idx: {obj_idx}");
876        }
877
878        let stage_u = stage_i as usize;
879        let obj_u = obj_idx as usize;
880        let current_layer = self.current_layer;
881
882        self.reset_object_for_create(layers, stage_u, obj_u);
883
884        {
885            let obj = self.ensure_object_mut(stage_u, obj_u);
886            obj.is_bg = stage_u == 0 && obj_u == 0;
887            obj.is_mesh = true;
888            obj.file = Some(file.to_string());
889            obj.patno = patno;
890            obj.disp = disp != 0;
891            obj.x = x;
892            obj.y = y;
893            if obj.layer_no == 0 {
894                obj.layer_no = current_layer as i64;
895            }
896        }
897
898        if cgm_file_interesting(Some(file)) || (30..=59).contains(&obj_u) {
899            sg_cgm_coord_trace(format!(
900                "object_create_mesh stage={} obj={} file={} disp={} x={} y={} patno={}",
901                stage, obj_idx, file, disp, x, y, patno
902            ));
903        }
904
905        if stage_u == 0 && obj_u == 0 {
906            let bg = layers.bg_mut();
907            bg.visible = disp != 0;
908            bg.image_id = None;
909            bg.x = x as i32;
910            bg.y = y as i32;
911        } else {
912            let (lid, sid) = self.ensure_bound_sprite(layers, stage_u, obj_u)?;
913            let sprite = layers
914                .layer_mut(lid)
915                .and_then(|l| l.sprite_mut(sid))
916                .context("mesh sprite not found")?;
917            sprite.visible = disp != 0;
918            sprite.image_id = None;
919            sprite.x = x as i32;
920            sprite.y = y as i32;
921            sprite.alpha = 255;
922            sprite.tr = 255;
923            sprite.fit = SpriteFit::PixelRect;
924            sprite.size_mode = SpriteSizeMode::Intrinsic;
925            sprite.mesh_file_name = Some(file.to_string());
926            sprite.mesh_kind = 1;
927            sprite.camera_enabled = true;
928            sprite.shadow_cast = true;
929            sprite.shadow_receive = true;
930        }
931
932        Ok(())
933    }
934
935    pub fn object_set_disp(
936        &mut self,
937        images: &mut ImageManager,
938        layers: &mut LayerManager,
939        stage: i64,
940        obj_idx: i64,
941        disp: i64,
942    ) -> Result<()> {
943        let stage_i = stage as isize;
944        if !(0..3).contains(&stage_i) || obj_idx < 0 {
945            return Ok(());
946        }
947        let stage_u = stage_i as usize;
948        let obj_u = obj_idx as usize;
949        {
950            let obj = self.ensure_object_mut(stage_u, obj_u);
951            obj.disp = disp != 0;
952        }
953        self.sync_object_sprite(images, layers, stage_u, obj_u)
954    }
955
956    pub fn object_set_pos(
957        &mut self,
958        images: &mut ImageManager,
959        layers: &mut LayerManager,
960        stage: i64,
961        obj_idx: i64,
962        x: i64,
963        y: i64,
964    ) -> Result<()> {
965        let stage_i = stage as isize;
966        if !(0..3).contains(&stage_i) || obj_idx < 0 {
967            return Ok(());
968        }
969        let stage_u = stage_i as usize;
970        let obj_u = obj_idx as usize;
971        let current_layer = self.current_layer;
972        {
973            let obj = self.ensure_object_mut(stage_u, obj_u);
974            obj.x = x;
975            obj.y = y;
976            if obj.layer_no == 0 {
977                obj.layer_no = current_layer as i64;
978            }
979        }
980        if let Some(obj) = self.object(stage_u, obj_u) {
981            if cgm_file_interesting(obj.file.as_deref()) || (30..=59).contains(&obj_u) {
982                sg_cgm_coord_trace(format!(
983                    "object_set_pos stage={} obj={} file={:?} x={} y={} layer_no={} binding={:?}/{:?}",
984                    stage,
985                    obj_idx,
986                    obj.file.as_deref(),
987                    x,
988                    y,
989                    obj.layer_no,
990                    obj.layer_id,
991                    obj.sprite_id
992                ));
993            }
994        }
995        self.sync_object_sprite(images, layers, stage_u, obj_u)
996    }
997
998    pub fn object_set_x(
999        &mut self,
1000        images: &mut ImageManager,
1001        layers: &mut LayerManager,
1002        stage: i64,
1003        obj_idx: i64,
1004        x: i64,
1005    ) -> Result<()> {
1006        let stage_i = stage as isize;
1007        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1008            return Ok(());
1009        }
1010        let stage_u = stage_i as usize;
1011        let obj_u = obj_idx as usize;
1012        {
1013            let obj = self.ensure_object_mut(stage_u, obj_u);
1014            obj.x = x;
1015        }
1016        self.sync_object_sprite(images, layers, stage_u, obj_u)
1017    }
1018
1019    pub fn object_set_y(
1020        &mut self,
1021        images: &mut ImageManager,
1022        layers: &mut LayerManager,
1023        stage: i64,
1024        obj_idx: i64,
1025        y: i64,
1026    ) -> Result<()> {
1027        let stage_i = stage as isize;
1028        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1029            return Ok(());
1030        }
1031        let stage_u = stage_i as usize;
1032        let obj_u = obj_idx as usize;
1033        {
1034            let obj = self.ensure_object_mut(stage_u, obj_u);
1035            obj.y = y;
1036        }
1037        self.sync_object_sprite(images, layers, stage_u, obj_u)
1038    }
1039
1040    pub fn object_set_patno(
1041        &mut self,
1042        images: &mut ImageManager,
1043        layers: &mut LayerManager,
1044        stage: i64,
1045        obj_idx: i64,
1046        patno: i64,
1047    ) -> Result<()> {
1048        let stage_i = stage as isize;
1049        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1050            return Ok(());
1051        }
1052        let stage_u = stage_i as usize;
1053        let obj_u = obj_idx as usize;
1054        {
1055            let obj = self.ensure_object_mut(stage_u, obj_u);
1056            obj.patno = patno;
1057        }
1058        self.sync_object_sprite(images, layers, stage_u, obj_u)
1059    }
1060
1061    pub fn object_set_layer(
1062        &mut self,
1063        images: &mut ImageManager,
1064        layers: &mut LayerManager,
1065        stage: i64,
1066        obj_idx: i64,
1067        layer_no: i64,
1068    ) -> Result<()> {
1069        let stage_i = stage as isize;
1070        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1071            return Ok(());
1072        }
1073        let stage_u = stage_i as usize;
1074        let obj_u = obj_idx as usize;
1075        {
1076            let obj = self.ensure_object_mut(stage_u, obj_u);
1077            obj.layer_no = layer_no;
1078        }
1079        self.sync_object_sprite(images, layers, stage_u, obj_u)
1080    }
1081
1082    pub fn object_set_order(
1083        &mut self,
1084        images: &mut ImageManager,
1085        layers: &mut LayerManager,
1086        stage: i64,
1087        obj_idx: i64,
1088        order: i64,
1089    ) -> Result<()> {
1090        let stage_i = stage as isize;
1091        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1092            return Ok(());
1093        }
1094        let stage_u = stage_i as usize;
1095        let obj_u = obj_idx as usize;
1096        {
1097            let obj = self.ensure_object_mut(stage_u, obj_u);
1098            obj.order = order;
1099        }
1100        self.sync_object_sprite(images, layers, stage_u, obj_u)
1101    }
1102
1103    pub fn object_set_alpha(
1104        &mut self,
1105        images: &mut ImageManager,
1106        layers: &mut LayerManager,
1107        stage: i64,
1108        obj_idx: i64,
1109        alpha: i64,
1110    ) -> Result<()> {
1111        let stage_i = stage as isize;
1112        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1113            return Ok(());
1114        }
1115        let stage_u = stage_i as usize;
1116        let obj_u = obj_idx as usize;
1117        {
1118            let obj = self.ensure_object_mut(stage_u, obj_u);
1119            obj.alpha = alpha;
1120        }
1121        self.sync_object_sprite(images, layers, stage_u, obj_u)
1122    }
1123
1124    pub fn object_set_tr(
1125        &mut self,
1126        images: &mut ImageManager,
1127        layers: &mut LayerManager,
1128        stage: i64,
1129        obj_idx: i64,
1130        tr: i64,
1131    ) -> Result<()> {
1132        let stage_i = stage as isize;
1133        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1134            return Ok(());
1135        }
1136        let stage_u = stage_i as usize;
1137        let obj_u = obj_idx as usize;
1138        {
1139            let obj = self.ensure_object_mut(stage_u, obj_u);
1140            obj.tr = tr;
1141        }
1142        self.sync_object_sprite(images, layers, stage_u, obj_u)
1143    }
1144
1145    pub fn object_set_mono(
1146        &mut self,
1147        images: &mut ImageManager,
1148        layers: &mut LayerManager,
1149        stage: i64,
1150        obj_idx: i64,
1151        mono: i64,
1152    ) -> Result<()> {
1153        let stage_i = stage as isize;
1154        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1155            return Ok(());
1156        }
1157        let stage_u = stage_i as usize;
1158        let obj_u = obj_idx as usize;
1159        {
1160            let obj = self.ensure_object_mut(stage_u, obj_u);
1161            obj.mono = mono;
1162        }
1163        self.sync_object_sprite(images, layers, stage_u, obj_u)
1164    }
1165
1166    pub fn object_set_reverse(
1167        &mut self,
1168        images: &mut ImageManager,
1169        layers: &mut LayerManager,
1170        stage: i64,
1171        obj_idx: i64,
1172        reverse: i64,
1173    ) -> Result<()> {
1174        let stage_i = stage as isize;
1175        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1176            return Ok(());
1177        }
1178        let stage_u = stage_i as usize;
1179        let obj_u = obj_idx as usize;
1180        {
1181            let obj = self.ensure_object_mut(stage_u, obj_u);
1182            obj.reverse = reverse;
1183        }
1184        self.sync_object_sprite(images, layers, stage_u, obj_u)
1185    }
1186
1187    pub fn object_set_bright(
1188        &mut self,
1189        images: &mut ImageManager,
1190        layers: &mut LayerManager,
1191        stage: i64,
1192        obj_idx: i64,
1193        bright: i64,
1194    ) -> Result<()> {
1195        let stage_i = stage as isize;
1196        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1197            return Ok(());
1198        }
1199        let stage_u = stage_i as usize;
1200        let obj_u = obj_idx as usize;
1201        {
1202            let obj = self.ensure_object_mut(stage_u, obj_u);
1203            obj.bright = bright;
1204        }
1205        self.sync_object_sprite(images, layers, stage_u, obj_u)
1206    }
1207
1208    pub fn object_set_dark(
1209        &mut self,
1210        images: &mut ImageManager,
1211        layers: &mut LayerManager,
1212        stage: i64,
1213        obj_idx: i64,
1214        dark: i64,
1215    ) -> Result<()> {
1216        let stage_i = stage as isize;
1217        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1218            return Ok(());
1219        }
1220        let stage_u = stage_i as usize;
1221        let obj_u = obj_idx as usize;
1222        {
1223            let obj = self.ensure_object_mut(stage_u, obj_u);
1224            obj.dark = dark;
1225        }
1226        self.sync_object_sprite(images, layers, stage_u, obj_u)
1227    }
1228
1229    pub fn object_set_color_rate(
1230        &mut self,
1231        images: &mut ImageManager,
1232        layers: &mut LayerManager,
1233        stage: i64,
1234        obj_idx: i64,
1235        rate: i64,
1236    ) -> Result<()> {
1237        let stage_i = stage as isize;
1238        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1239            return Ok(());
1240        }
1241        let stage_u = stage_i as usize;
1242        let obj_u = obj_idx as usize;
1243        {
1244            let obj = self.ensure_object_mut(stage_u, obj_u);
1245            obj.color_rate = rate;
1246        }
1247        self.sync_object_sprite(images, layers, stage_u, obj_u)
1248    }
1249
1250    pub fn object_set_color_add(
1251        &mut self,
1252        images: &mut ImageManager,
1253        layers: &mut LayerManager,
1254        stage: i64,
1255        obj_idx: i64,
1256        r: i64,
1257        g: i64,
1258        b: i64,
1259    ) -> Result<()> {
1260        let stage_i = stage as isize;
1261        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1262            return Ok(());
1263        }
1264        let stage_u = stage_i as usize;
1265        let obj_u = obj_idx as usize;
1266        {
1267            let obj = self.ensure_object_mut(stage_u, obj_u);
1268            obj.color_add_r = r;
1269            obj.color_add_g = g;
1270            obj.color_add_b = b;
1271        }
1272        self.sync_object_sprite(images, layers, stage_u, obj_u)
1273    }
1274
1275    pub fn object_set_color(
1276        &mut self,
1277        images: &mut ImageManager,
1278        layers: &mut LayerManager,
1279        stage: i64,
1280        obj_idx: i64,
1281        r: i64,
1282        g: i64,
1283        b: i64,
1284    ) -> Result<()> {
1285        let stage_i = stage as isize;
1286        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1287            return Ok(());
1288        }
1289        let stage_u = stage_i as usize;
1290        let obj_u = obj_idx as usize;
1291        {
1292            let obj = self.ensure_object_mut(stage_u, obj_u);
1293            obj.color_r = r;
1294            obj.color_g = g;
1295            obj.color_b = b;
1296        }
1297        self.sync_object_sprite(images, layers, stage_u, obj_u)
1298    }
1299
1300    pub fn object_set_blend(
1301        &mut self,
1302        images: &mut ImageManager,
1303        layers: &mut LayerManager,
1304        stage: i64,
1305        obj_idx: i64,
1306        blend: i64,
1307    ) -> Result<()> {
1308        let stage_i = stage as isize;
1309        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1310            return Ok(());
1311        }
1312        let stage_u = stage_i as usize;
1313        let obj_u = obj_idx as usize;
1314        {
1315            let obj = self.ensure_object_mut(stage_u, obj_u);
1316            obj.blend = blend;
1317        }
1318        self.sync_object_sprite(images, layers, stage_u, obj_u)
1319    }
1320
1321    pub fn object_set_light_no(
1322        &mut self,
1323        images: &mut ImageManager,
1324        layers: &mut LayerManager,
1325        stage: i64,
1326        obj_idx: i64,
1327        light_no: i64,
1328    ) -> Result<()> {
1329        let stage_i = stage as isize;
1330        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1331            return Ok(());
1332        }
1333        let stage_u = stage_i as usize;
1334        let obj_u = obj_idx as usize;
1335        {
1336            let obj = self.ensure_object_mut(stage_u, obj_u);
1337            obj.light_no = light_no;
1338        }
1339        self.sync_object_sprite(images, layers, stage_u, obj_u)
1340    }
1341
1342    pub fn object_set_fog_use(
1343        &mut self,
1344        images: &mut ImageManager,
1345        layers: &mut LayerManager,
1346        stage: i64,
1347        obj_idx: i64,
1348        fog_use: i64,
1349    ) -> Result<()> {
1350        let stage_i = stage as isize;
1351        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1352            return Ok(());
1353        }
1354        let stage_u = stage_i as usize;
1355        let obj_u = obj_idx as usize;
1356        {
1357            let obj = self.ensure_object_mut(stage_u, obj_u);
1358            obj.fog_use = fog_use;
1359        }
1360        self.sync_object_sprite(images, layers, stage_u, obj_u)
1361    }
1362
1363    pub fn object_set_z(&mut self, stage: i64, obj_idx: i64, z: i64) -> Result<()> {
1364        let stage_i = stage as isize;
1365        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1366            return Ok(());
1367        }
1368        let stage_u = stage_i as usize;
1369        let obj_u = obj_idx as usize;
1370        let obj = self.ensure_object_mut(stage_u, obj_u);
1371        obj.z = z;
1372        Ok(())
1373    }
1374
1375    pub fn object_clear(
1376        &mut self,
1377        images: &mut ImageManager,
1378        layers: &mut LayerManager,
1379        stage: i64,
1380        obj_idx: i64,
1381    ) -> Result<()> {
1382        let stage_i = stage as isize;
1383        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1384            return Ok(());
1385        }
1386        let stage_u = stage_i as usize;
1387        let obj_u = obj_idx as usize;
1388        {
1389            let obj = self.ensure_object_mut(stage_u, obj_u);
1390            obj.file = None;
1391            obj.patno = 0;
1392            obj.disp = false;
1393            obj.alpha = 255;
1394        }
1395        let _ = self.sync_object_sprite(images, layers, stage_u, obj_u);
1396        Ok(())
1397    }
1398
1399    pub fn clear_objects_in_layer_no(
1400        &mut self,
1401        images: &mut ImageManager,
1402        layers: &mut LayerManager,
1403        layer_no: i64,
1404    ) -> Result<()> {
1405        for stage in 0..3usize {
1406            let len = self.stages[stage].objects.len();
1407            for obj_idx in 0..len {
1408                let matches = self
1409                    .object(stage, obj_idx)
1410                    .map(|o| o.layer_no == layer_no)
1411                    .unwrap_or(false);
1412                if !matches {
1413                    continue;
1414                }
1415                {
1416                    let obj = self.ensure_object_mut(stage, obj_idx);
1417                    obj.disp = false;
1418                }
1419                let _ = self.sync_object_sprite(images, layers, stage, obj_idx);
1420            }
1421        }
1422        Ok(())
1423    }
1424
1425    pub fn object_get_pos(&self, stage: i64, obj_idx: i64) -> Option<(i64, i64)> {
1426        self.object_peek_pos(stage, obj_idx)
1427    }
1428
1429    pub fn object_get_disp(&self, stage: i64, obj_idx: i64) -> Option<bool> {
1430        self.object_peek_disp(stage, obj_idx).map(|v| v != 0)
1431    }
1432
1433    pub fn object_get_patno(&self, stage: i64, obj_idx: i64) -> Option<i64> {
1434        self.object_peek_patno(stage, obj_idx)
1435    }
1436
1437    pub fn object_get_layer(&self, stage: i64, obj_idx: i64) -> Option<i64> {
1438        self.object_peek_layer(stage, obj_idx)
1439    }
1440
1441    pub fn object_get_order(&self, stage: i64, obj_idx: i64) -> Option<i64> {
1442        self.object_peek_order(stage, obj_idx)
1443    }
1444
1445    pub fn object_get_alpha(&self, stage: i64, obj_idx: i64) -> Option<i64> {
1446        self.object_peek_alpha(stage, obj_idx)
1447    }
1448
1449    pub fn object_set_pat_no(
1450        &mut self,
1451        images: &mut ImageManager,
1452        layers: &mut LayerManager,
1453        stage: i64,
1454        obj_idx: i64,
1455        patno: i64,
1456    ) -> Result<()> {
1457        self.object_set_patno(images, layers, stage, obj_idx, patno)
1458    }
1459
1460    pub fn object_peek_pos(&self, stage: i64, obj_idx: i64) -> Option<(i64, i64)> {
1461        let stage_i = stage as isize;
1462        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1463            return None;
1464        }
1465        let stage_u = stage_i as usize;
1466        let obj_u = obj_idx as usize;
1467        let obj = self.object(stage_u, obj_u)?;
1468        Some((obj.x, obj.y))
1469    }
1470
1471    pub fn object_peek_disp(&self, stage: i64, obj_idx: i64) -> Option<i64> {
1472        let stage_i = stage as isize;
1473        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1474            return None;
1475        }
1476        let stage_u = stage_i as usize;
1477        let obj_u = obj_idx as usize;
1478        let obj = self.object(stage_u, obj_u)?;
1479        Some(if obj.disp { 1 } else { 0 })
1480    }
1481
1482    pub fn object_peek_patno(&self, stage: i64, obj_idx: i64) -> Option<i64> {
1483        let stage_i = stage as isize;
1484        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1485            return None;
1486        }
1487        let stage_u = stage_i as usize;
1488        let obj_u = obj_idx as usize;
1489        let obj = self.object(stage_u, obj_u)?;
1490        Some(obj.patno)
1491    }
1492
1493    pub fn object_peek_layer(&self, stage: i64, obj_idx: i64) -> Option<i64> {
1494        let stage_i = stage as isize;
1495        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1496            return None;
1497        }
1498        let stage_u = stage_i as usize;
1499        let obj_u = obj_idx as usize;
1500        let obj = self.object(stage_u, obj_u)?;
1501        Some(obj.layer_no)
1502    }
1503
1504    pub fn object_peek_order(&self, stage: i64, obj_idx: i64) -> Option<i64> {
1505        let stage_i = stage as isize;
1506        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1507            return None;
1508        }
1509        let stage_u = stage_i as usize;
1510        let obj_u = obj_idx as usize;
1511        let obj = self.object(stage_u, obj_u)?;
1512        Some(obj.order)
1513    }
1514
1515    pub fn object_peek_alpha(&self, stage: i64, obj_idx: i64) -> Option<i64> {
1516        let stage_i = stage as isize;
1517        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1518            return None;
1519        }
1520        let stage_u = stage_i as usize;
1521        let obj_u = obj_idx as usize;
1522        let obj = self.object(stage_u, obj_u)?;
1523        Some(obj.alpha)
1524    }
1525
1526    pub fn object_peek_file(&self, stage: i64, obj_idx: i64) -> Option<String> {
1527        let stage_i = stage as isize;
1528        if !(0..3).contains(&stage_i) || obj_idx < 0 {
1529            return None;
1530        }
1531        let stage_u = stage_i as usize;
1532        let obj_u = obj_idx as usize;
1533        let obj = self.object(stage_u, obj_u)?;
1534        obj.file.clone()
1535    }
1536}
1537
1538fn is_probable_mesh_path(file: &str) -> bool {
1539    let lower = file.to_ascii_lowercase();
1540    lower.ends_with(".x")
1541        || lower.ends_with(".obj")
1542        || lower.ends_with(".fbx")
1543        || lower.ends_with(".gltf")
1544        || lower.ends_with(".glb")
1545}
1546
1547
1548fn set_object_sprite_image(sprite: &mut Sprite, images: &ImageManager, image_id: ImageId) {
1549    sprite.image_id = Some(image_id);
1550    if let Some(img) = images.get(image_id) {
1551        sprite.object_anchor = true;
1552        sprite.texture_center_x = img.center_x as f32;
1553        sprite.texture_center_y = img.center_y as f32;
1554    } else {
1555        sprite.object_anchor = false;
1556        sprite.texture_center_x = 0.0;
1557        sprite.texture_center_y = 0.0;
1558    }
1559}
1560
1561fn clip_rect(use_flag: i64, left: i64, top: i64, right: i64, bottom: i64) -> Option<ClipRect> {
1562    if use_flag == 0 {
1563        return None;
1564    }
1565    Some(ClipRect {
1566        left: left as i32,
1567        top: top as i32,
1568        right: right as i32,
1569        bottom: bottom as i32,
1570    })
1571}