siglus_scene_vm/runtime/forms/
counter.rs1use 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}