Skip to main content

siglus_scene_vm/runtime/forms/
counter.rs

1use anyhow::Result;
2
3use crate::runtime::{globals::Counter, CommandContext, Value};
4
5fn ensure_len(v: &mut Vec<Counter>, idx: usize) {
6    if v.len() <= idx {
7        v.resize(idx + 1, Counter::default());
8    }
9}
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12enum CounterListOp {
13    GetSize,
14    Unknown,
15}
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18enum CounterOp {
19    Set,
20    Get,
21    Reset,
22    Start,
23    StartReal,
24    StartFrame,
25    StartFrameReal,
26    StartFrameLoop,
27    StartFrameLoopReal,
28    Stop,
29    Resume,
30    Wait,
31    WaitKey,
32    CheckValue,
33    CheckActive,
34    Unknown,
35}
36
37fn resolve_counter_list_op(op: i32) -> CounterListOp {
38    if op == crate::runtime::constants::COUNTERLIST_GET_SIZE {
39        CounterListOp::GetSize
40    } else {
41        CounterListOp::Unknown
42    }
43}
44
45fn resolve_counter_op(op: i32) -> CounterOp {
46    match op {
47        crate::runtime::constants::COUNTER_SET => CounterOp::Set,
48        crate::runtime::constants::COUNTER_GET => CounterOp::Get,
49        crate::runtime::constants::COUNTER_RESET => CounterOp::Reset,
50        crate::runtime::constants::COUNTER_START => CounterOp::Start,
51        crate::runtime::constants::COUNTER_START_REAL => CounterOp::StartReal,
52        crate::runtime::constants::COUNTER_START_FRAME => CounterOp::StartFrame,
53        crate::runtime::constants::COUNTER_START_FRAME_REAL => CounterOp::StartFrameReal,
54        crate::runtime::constants::COUNTER_START_FRAME_LOOP => CounterOp::StartFrameLoop,
55        crate::runtime::constants::COUNTER_START_FRAME_LOOP_REAL => CounterOp::StartFrameLoopReal,
56        crate::runtime::constants::COUNTER_STOP => CounterOp::Stop,
57        crate::runtime::constants::COUNTER_RESUME => CounterOp::Resume,
58        crate::runtime::constants::COUNTER_WAIT => CounterOp::Wait,
59        crate::runtime::constants::COUNTER_WAIT_KEY => CounterOp::WaitKey,
60        crate::runtime::constants::COUNTER_CHECK_VALUE => CounterOp::CheckValue,
61        crate::runtime::constants::COUNTER_CHECK_ACTIVE => CounterOp::CheckActive,
62        _ => CounterOp::Unknown,
63    }
64}
65
66fn arg_int(args: &[Value], idx: usize) -> i64 {
67    args.get(idx).and_then(Value::as_i64).unwrap_or(0)
68}
69
70pub fn dispatch(ctx: &mut CommandContext, form_id: u32, args: &[Value]) -> Result<bool> {
71    let Some((chain_pos, chain_ref)) =
72        crate::runtime::forms::prop_access::parse_element_chain_ctx(ctx, form_id, args)
73    else {
74        return Ok(false);
75    };
76    let chain = chain_ref.to_vec();
77
78    if chain.len() >= 2 && chain[1] != ctx.ids.elm_array {
79        match resolve_counter_list_op(chain[1]) {
80            CounterListOp::GetSize => {
81                let size = ctx
82                    .globals
83                    .counter_lists
84                    .get(&form_id)
85                    .map(|v| v.len() as i64)
86                    .unwrap_or(0);
87                ctx.push(Value::Int(size));
88                return Ok(true);
89            }
90            CounterListOp::Unknown => return Ok(false),
91        }
92    }
93
94    if chain.len() < 3 || chain[1] != ctx.ids.elm_array {
95        return Ok(false);
96    }
97    let idx = chain[2].max(0) as usize;
98    let params = crate::runtime::forms::prop_access::script_args(args, chain_pos);
99
100    {
101        let counters = ctx
102            .globals
103            .counter_lists
104            .entry(form_id)
105            .or_insert_with(Vec::new);
106        ensure_len(counters, idx);
107    }
108
109    if chain.len() == 3 {
110        let (al_id, _ret_form, rhs_value) =
111            crate::runtime::forms::prop_access::infer_assign_and_ret_ctx(ctx, chain_pos, args);
112        if al_id == Some(1) {
113            let rhs = rhs_value.as_ref().and_then(Value::as_i64).unwrap_or(0);
114            if let Some(counter) = ctx
115                .globals
116                .counter_lists
117                .get_mut(&form_id)
118                .and_then(|v| v.get_mut(idx))
119            {
120                counter.set_count(rhs);
121            }
122            ctx.push(Value::Int(0));
123        } else {
124            let value = ctx
125                .globals
126                .counter_lists
127                .get(&form_id)
128                .and_then(|v| v.get(idx))
129                .map(|c| c.get_count())
130                .unwrap_or(0);
131            ctx.push(Value::Int(value));
132        }
133        return Ok(true);
134    }
135
136    match resolve_counter_op(chain[3]) {
137        CounterOp::Set => {
138            if let Some(counter) = ctx
139                .globals
140                .counter_lists
141                .get_mut(&form_id)
142                .and_then(|v| v.get_mut(idx))
143            {
144                counter.set_count(arg_int(params, 0));
145            }
146            ctx.push(Value::Int(0));
147        }
148        CounterOp::Get => {
149            let value = ctx
150                .globals
151                .counter_lists
152                .get(&form_id)
153                .and_then(|v| v.get(idx))
154                .map(|c| c.get_count())
155                .unwrap_or(0);
156            ctx.push(Value::Int(value));
157        }
158        CounterOp::Reset => {
159            if let Some(counter) = ctx
160                .globals
161                .counter_lists
162                .get_mut(&form_id)
163                .and_then(|v| v.get_mut(idx))
164            {
165                counter.reset();
166            }
167            ctx.push(Value::Int(0));
168        }
169        CounterOp::Start => {
170            if let Some(counter) = ctx
171                .globals
172                .counter_lists
173                .get_mut(&form_id)
174                .and_then(|v| v.get_mut(idx))
175            {
176                counter.start();
177            }
178            ctx.push(Value::Int(0));
179        }
180        CounterOp::StartReal => {
181            if let Some(counter) = ctx
182                .globals
183                .counter_lists
184                .get_mut(&form_id)
185                .and_then(|v| v.get_mut(idx))
186            {
187                counter.start_real();
188            }
189            ctx.push(Value::Int(0));
190        }
191        CounterOp::StartFrame => {
192            if let Some(counter) = ctx
193                .globals
194                .counter_lists
195                .get_mut(&form_id)
196                .and_then(|v| v.get_mut(idx))
197            {
198                counter.start_frame(arg_int(params, 0), arg_int(params, 1), arg_int(params, 2));
199            }
200            ctx.push(Value::Int(0));
201        }
202        CounterOp::StartFrameReal => {
203            if let Some(counter) = ctx
204                .globals
205                .counter_lists
206                .get_mut(&form_id)
207                .and_then(|v| v.get_mut(idx))
208            {
209                counter.start_frame_real(
210                    arg_int(params, 0),
211                    arg_int(params, 1),
212                    arg_int(params, 2),
213                );
214            }
215            ctx.push(Value::Int(0));
216        }
217        CounterOp::StartFrameLoop => {
218            if let Some(counter) = ctx
219                .globals
220                .counter_lists
221                .get_mut(&form_id)
222                .and_then(|v| v.get_mut(idx))
223            {
224                counter.start_frame_loop(
225                    arg_int(params, 0),
226                    arg_int(params, 1),
227                    arg_int(params, 2),
228                );
229            }
230            ctx.push(Value::Int(0));
231        }
232        CounterOp::StartFrameLoopReal => {
233            if let Some(counter) = ctx
234                .globals
235                .counter_lists
236                .get_mut(&form_id)
237                .and_then(|v| v.get_mut(idx))
238            {
239                counter.start_frame_loop_real(
240                    arg_int(params, 0),
241                    arg_int(params, 1),
242                    arg_int(params, 2),
243                );
244            }
245            ctx.push(Value::Int(0));
246        }
247        CounterOp::Stop => {
248            if let Some(counter) = ctx
249                .globals
250                .counter_lists
251                .get_mut(&form_id)
252                .and_then(|v| v.get_mut(idx))
253            {
254                counter.stop();
255            }
256            ctx.push(Value::Int(0));
257        }
258        CounterOp::Resume => {
259            if let Some(counter) = ctx
260                .globals
261                .counter_lists
262                .get_mut(&form_id)
263                .and_then(|v| v.get_mut(idx))
264            {
265                counter.resume();
266            }
267            ctx.push(Value::Int(0));
268        }
269        CounterOp::Wait => {
270            ctx.wait
271                .wait_counter(form_id, idx, arg_int(params, 0), false, false);
272        }
273        CounterOp::WaitKey => {
274            ctx.wait
275                .wait_counter(form_id, idx, arg_int(params, 0), true, true);
276        }
277        CounterOp::CheckValue => {
278            let target = arg_int(params, 0);
279            let cur = ctx
280                .globals
281                .counter_lists
282                .get(&form_id)
283                .and_then(|v| v.get(idx))
284                .map(|c| c.get_count())
285                .unwrap_or(target);
286            let ok = cur - target >= 0;
287            if std::env::var_os("SG_COUNTER_TRACE").is_some() {
288                eprintln!(
289                    "[SG_DEBUG][COUNTER] CHECK_VALUE form={} idx={} cur={} target={} ok={}",
290                    form_id, idx, cur, target, ok
291                );
292            }
293            ctx.push(Value::Int(if ok { 1 } else { 0 }));
294        }
295        CounterOp::CheckActive => {
296            let active = ctx
297                .globals
298                .counter_lists
299                .get(&form_id)
300                .and_then(|v| v.get(idx))
301                .map(|c| c.is_running())
302                .unwrap_or(false);
303            ctx.push(Value::Int(if active { 1 } else { 0 }));
304        }
305        CounterOp::Unknown => return Ok(false),
306    }
307
308    Ok(true)
309}