Skip to main content

siglus_scene_vm/runtime/forms/
counter.rs

1use anyhow::Result;
2
3use crate::runtime::{CommandContext, Value, globals::Counter};
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.globals.counter_lists.entry(form_id).or_default();
102        ensure_len(counters, idx);
103    }
104
105    if chain.len() == 3 {
106        let (al_id, _ret_form, rhs_value) =
107            crate::runtime::forms::prop_access::infer_assign_and_ret_ctx(ctx, chain_pos, args);
108        if al_id == Some(1) {
109            let rhs = rhs_value.as_ref().and_then(Value::as_i64).unwrap_or(0);
110            if let Some(counter) = ctx
111                .globals
112                .counter_lists
113                .get_mut(&form_id)
114                .and_then(|v| v.get_mut(idx))
115            {
116                counter.set_count(rhs);
117            }
118            ctx.push(Value::Int(0));
119        } else {
120            let value = ctx
121                .globals
122                .counter_lists
123                .get(&form_id)
124                .and_then(|v| v.get(idx))
125                .map(|c| c.get_count())
126                .unwrap_or(0);
127            ctx.push(Value::Int(value));
128        }
129        return Ok(true);
130    }
131
132    match resolve_counter_op(chain[3]) {
133        CounterOp::Set => {
134            if let Some(counter) = ctx
135                .globals
136                .counter_lists
137                .get_mut(&form_id)
138                .and_then(|v| v.get_mut(idx))
139            {
140                counter.set_count(arg_int(params, 0));
141            }
142            ctx.push(Value::Int(0));
143        }
144        CounterOp::Get => {
145            let value = ctx
146                .globals
147                .counter_lists
148                .get(&form_id)
149                .and_then(|v| v.get(idx))
150                .map(|c| c.get_count())
151                .unwrap_or(0);
152            ctx.push(Value::Int(value));
153        }
154        CounterOp::Reset => {
155            if let Some(counter) = ctx
156                .globals
157                .counter_lists
158                .get_mut(&form_id)
159                .and_then(|v| v.get_mut(idx))
160            {
161                counter.reset();
162            }
163            ctx.push(Value::Int(0));
164        }
165        CounterOp::Start => {
166            if let Some(counter) = ctx
167                .globals
168                .counter_lists
169                .get_mut(&form_id)
170                .and_then(|v| v.get_mut(idx))
171            {
172                counter.start();
173            }
174            ctx.push(Value::Int(0));
175        }
176        CounterOp::StartReal => {
177            if let Some(counter) = ctx
178                .globals
179                .counter_lists
180                .get_mut(&form_id)
181                .and_then(|v| v.get_mut(idx))
182            {
183                counter.start_real();
184            }
185            ctx.push(Value::Int(0));
186        }
187        CounterOp::StartFrame => {
188            if let Some(counter) = ctx
189                .globals
190                .counter_lists
191                .get_mut(&form_id)
192                .and_then(|v| v.get_mut(idx))
193            {
194                counter.start_frame(arg_int(params, 0), arg_int(params, 1), arg_int(params, 2));
195            }
196            ctx.push(Value::Int(0));
197        }
198        CounterOp::StartFrameReal => {
199            if let Some(counter) = ctx
200                .globals
201                .counter_lists
202                .get_mut(&form_id)
203                .and_then(|v| v.get_mut(idx))
204            {
205                counter.start_frame_real(
206                    arg_int(params, 0),
207                    arg_int(params, 1),
208                    arg_int(params, 2),
209                );
210            }
211            ctx.push(Value::Int(0));
212        }
213        CounterOp::StartFrameLoop => {
214            if let Some(counter) = ctx
215                .globals
216                .counter_lists
217                .get_mut(&form_id)
218                .and_then(|v| v.get_mut(idx))
219            {
220                counter.start_frame_loop(
221                    arg_int(params, 0),
222                    arg_int(params, 1),
223                    arg_int(params, 2),
224                );
225            }
226            ctx.push(Value::Int(0));
227        }
228        CounterOp::StartFrameLoopReal => {
229            if let Some(counter) = ctx
230                .globals
231                .counter_lists
232                .get_mut(&form_id)
233                .and_then(|v| v.get_mut(idx))
234            {
235                counter.start_frame_loop_real(
236                    arg_int(params, 0),
237                    arg_int(params, 1),
238                    arg_int(params, 2),
239                );
240            }
241            ctx.push(Value::Int(0));
242        }
243        CounterOp::Stop => {
244            if let Some(counter) = ctx
245                .globals
246                .counter_lists
247                .get_mut(&form_id)
248                .and_then(|v| v.get_mut(idx))
249            {
250                counter.stop();
251            }
252            ctx.push(Value::Int(0));
253        }
254        CounterOp::Resume => {
255            if let Some(counter) = ctx
256                .globals
257                .counter_lists
258                .get_mut(&form_id)
259                .and_then(|v| v.get_mut(idx))
260            {
261                counter.resume();
262            }
263            ctx.push(Value::Int(0));
264        }
265        CounterOp::Wait => {
266            ctx.wait
267                .wait_counter(form_id, idx, arg_int(params, 0), false, false);
268        }
269        CounterOp::WaitKey => {
270            ctx.wait
271                .wait_counter(form_id, idx, arg_int(params, 0), true, true);
272        }
273        CounterOp::CheckValue => {
274            let target = arg_int(params, 0);
275            let cur = ctx
276                .globals
277                .counter_lists
278                .get(&form_id)
279                .and_then(|v| v.get(idx))
280                .map(|c| c.get_count())
281                .unwrap_or(target);
282            let ok = cur - target >= 0;
283            if env_is_set!("SG_COUNTER_TRACE") {
284                eprintln!(
285                    "[SG_DEBUG][COUNTER] CHECK_VALUE form={} idx={} cur={} target={} ok={}",
286                    form_id, idx, cur, target, ok
287                );
288            }
289            ctx.push(Value::Int(if ok { 1 } else { 0 }));
290        }
291        CounterOp::CheckActive => {
292            let active = ctx
293                .globals
294                .counter_lists
295                .get(&form_id)
296                .and_then(|v| v.get(idx))
297                .map(|c| c.is_running())
298                .unwrap_or(false);
299            ctx.push(Value::Int(if active { 1 } else { 0 }));
300        }
301        CounterOp::Unknown => return Ok(false),
302    }
303
304    Ok(true)
305}