Skip to main content

siglus_scene_vm/runtime/commands/
chr.rs

1use anyhow::Result;
2
3use crate::runtime::commands::util::strip_vm_meta;
4use crate::runtime::{Command, CommandContext, Value};
5
6fn arg_i64(v: &Value) -> Option<i64> {
7    match v {
8        Value::Int(i) => Some(*i),
9        _ => None,
10    }
11}
12
13fn arg_str(v: &Value) -> Option<&str> {
14    match v {
15        Value::Str(s) => Some(s.as_str()),
16        _ => None,
17    }
18}
19
20fn last_i64(args: &[Value]) -> Option<i64> {
21    args.iter().rev().find_map(arg_i64)
22}
23
24fn first_two_i64(args: &[Value]) -> Option<(i64, i64)> {
25    let mut it = args.iter().filter_map(arg_i64);
26    let a = it.next()?;
27    let b = it.next()?;
28    Some((a, b))
29}
30
31fn parse_indices(args: &[Value]) -> (i64, i64, usize) {
32    // Heuristic:
33    //   [stage, obj, ...] or [obj, ...] or [...]
34    if args.len() >= 2 {
35        if let (Some(stage), Some(obj)) = (arg_i64(&args[0]), arg_i64(&args[1])) {
36            return (stage, obj, 2);
37        }
38    }
39    if args.len() >= 1 {
40        if let Some(obj) = arg_i64(&args[0]) {
41            return (1, obj, 1);
42        }
43    }
44    (1, 0, 0)
45}
46
47fn is_chr_cmd(name: &str) -> bool {
48    let n = name.to_ascii_uppercase();
49    n == "CHR" || n.starts_with("CHR_") || n == "CHAR" || n.starts_with("CHAR_")
50}
51
52fn subcmd(name: &str) -> String {
53    let n = name.to_ascii_uppercase();
54    if n == "CHR" || n == "CHAR" {
55        return "CREATE".to_string();
56    }
57    if let Some(rest) = n.strip_prefix("CHR_") {
58        return rest.to_string();
59    }
60    if let Some(rest) = n.strip_prefix("CHAR_") {
61        return rest.to_string();
62    }
63    "CREATE".to_string()
64}
65
66pub fn handle(ctx: &mut CommandContext, cmd: &Command) -> Result<bool> {
67    if !is_chr_cmd(&cmd.name) {
68        return Ok(false);
69    }
70
71    let sub = subcmd(&cmd.name);
72    let args = strip_vm_meta(&cmd.args);
73    let (stage_idx, obj_idx, mut i) = parse_indices(args);
74
75    match sub.as_str() {
76        "CREATE" | "SET" => {
77            // Heuristic ordering: file, disp?, x?, y?, pat?, layer?, order?, alpha?
78            let mut file: Option<&str> = None;
79            if let Some(v) = args.get(i) {
80                file = arg_str(v);
81                if file.is_some() {
82                    i += 1;
83                }
84            }
85            if file.is_none() {
86                for (j, v) in args.iter().enumerate().skip(i) {
87                    if let Some(s) = arg_str(v) {
88                        file = Some(s);
89                        i = j + 1;
90                        break;
91                    }
92                }
93            }
94            let Some(file_name) = file else {
95                // Nothing we can do.
96                return Ok(true);
97            };
98
99            let disp = args.get(i).and_then(arg_i64).unwrap_or(1);
100            if args.get(i).and_then(arg_i64).is_some() {
101                i += 1;
102            }
103
104            let x = args.get(i).and_then(arg_i64).unwrap_or(0);
105            if args.get(i).and_then(arg_i64).is_some() {
106                i += 1;
107            }
108
109            let y = args.get(i).and_then(arg_i64).unwrap_or(0);
110            if args.get(i).and_then(arg_i64).is_some() {
111                i += 1;
112            }
113
114            let pat_no = args.get(i).and_then(arg_i64).unwrap_or(0);
115            if args.get(i).and_then(arg_i64).is_some() {
116                i += 1;
117            }
118
119            {
120                let (gfx, images, layers) = (&mut ctx.gfx, &mut ctx.images, &mut ctx.layers);
121                gfx.object_create(
122                    images, layers, stage_idx, obj_idx, file_name, disp, x, y, pat_no,
123                )?;
124            }
125
126            // Remaining integers can be interpreted as optional (layer_no, order, alpha).
127            let remain: Vec<i64> = args.iter().skip(i).filter_map(arg_i64).collect();
128            if remain.len() >= 1 {
129                let (gfx, images, layers) = (&mut ctx.gfx, &mut ctx.images, &mut ctx.layers);
130                gfx.object_set_layer(images, layers, stage_idx, obj_idx, remain[0])?;
131            }
132            if remain.len() >= 2 {
133                let (gfx, images, layers) = (&mut ctx.gfx, &mut ctx.images, &mut ctx.layers);
134                gfx.object_set_order(images, layers, stage_idx, obj_idx, remain[1])?;
135            }
136            if remain.len() >= 3 {
137                let (gfx, images, layers) = (&mut ctx.gfx, &mut ctx.images, &mut ctx.layers);
138                gfx.object_set_alpha(images, layers, stage_idx, obj_idx, remain[2])?;
139            }
140
141            Ok(true)
142        }
143        "POS" | "MOVE" => {
144            if let Some((x, y)) = first_two_i64(&args[i..]) {
145                let (gfx, images, layers) = (&mut ctx.gfx, &mut ctx.images, &mut ctx.layers);
146                gfx.object_set_pos(images, layers, stage_idx, obj_idx, x, y)?;
147            }
148            Ok(true)
149        }
150        "X" => {
151            if let Some(x) = last_i64(&args[i..]) {
152                let (_, y) = {
153                    let gfx = &ctx.gfx;
154                    gfx.object_get_pos(stage_idx, obj_idx).unwrap_or((0, 0))
155                };
156                let (gfx, images, layers) = (&mut ctx.gfx, &mut ctx.images, &mut ctx.layers);
157                gfx.object_set_pos(images, layers, stage_idx, obj_idx, x, y)?;
158            }
159            Ok(true)
160        }
161        "Y" => {
162            if let Some(y) = last_i64(&args[i..]) {
163                let (x, _) = {
164                    let gfx = &ctx.gfx;
165                    gfx.object_get_pos(stage_idx, obj_idx).unwrap_or((0, 0))
166                };
167                let (gfx, images, layers) = (&mut ctx.gfx, &mut ctx.images, &mut ctx.layers);
168                gfx.object_set_pos(images, layers, stage_idx, obj_idx, x, y)?;
169            }
170            Ok(true)
171        }
172        "DISP" | "SHOW" | "HIDE" => {
173            let disp = match sub.as_str() {
174                "SHOW" => 1,
175                "HIDE" => 0,
176                _ => last_i64(&args[i..]).unwrap_or(1),
177            };
178            let (gfx, images, layers) = (&mut ctx.gfx, &mut ctx.images, &mut ctx.layers);
179            gfx.object_set_disp(images, layers, stage_idx, obj_idx, disp)?;
180            Ok(true)
181        }
182        "PAT" | "PATNO" | "FRAME" => {
183            if let Some(pat_no) = last_i64(&args[i..]) {
184                let (gfx, images, layers) = (&mut ctx.gfx, &mut ctx.images, &mut ctx.layers);
185                gfx.object_set_patno(images, layers, stage_idx, obj_idx, pat_no)?;
186            }
187            Ok(true)
188        }
189        "LAYER" => {
190            if let Some(layer_no) = last_i64(&args[i..]) {
191                let (gfx, images, layers) = (&mut ctx.gfx, &mut ctx.images, &mut ctx.layers);
192                gfx.object_set_layer(images, layers, stage_idx, obj_idx, layer_no)?;
193            }
194            Ok(true)
195        }
196        "ORDER" => {
197            if let Some(order) = last_i64(&args[i..]) {
198                let (gfx, images, layers) = (&mut ctx.gfx, &mut ctx.images, &mut ctx.layers);
199                gfx.object_set_order(images, layers, stage_idx, obj_idx, order)?;
200            }
201            Ok(true)
202        }
203        "Z" => {
204            if let Some(z) = last_i64(&args[i..]) {
205                let _ = ctx.gfx.object_set_z(stage_idx, obj_idx, z);
206            }
207            Ok(true)
208        }
209        "ALPHA" | "A" => {
210            if let Some(a) = last_i64(&args[i..]) {
211                let (gfx, images, layers) = (&mut ctx.gfx, &mut ctx.images, &mut ctx.layers);
212                gfx.object_set_alpha(images, layers, stage_idx, obj_idx, a)?;
213            }
214            Ok(true)
215        }
216        "CLEAR" | "DEL" | "DELETE" => {
217            let (gfx, images, layers) = (&mut ctx.gfx, &mut ctx.images, &mut ctx.layers);
218            gfx.object_clear(images, layers, stage_idx, obj_idx)?;
219            Ok(true)
220        }
221        _ => {
222            // Unknown CHR_XXX subcommand: ignore to keep VM progressing.
223            Ok(true)
224        }
225    }
226}