siglus_scene_vm/runtime/forms/
frame_action_ch.rs1use anyhow::Result;
2
3use crate::runtime::forms::prop_access;
4use crate::runtime::globals::{ObjectFrameActionState, PendingFrameActionFinish};
5use crate::runtime::{CommandContext, Value};
6
7fn as_i64(v: &Value) -> Option<i64> {
8 match v {
9 Value::Int(n) => Some(*n),
10 Value::NamedArg { value, .. } => as_i64(value),
11 _ => None,
12 }
13}
14
15fn push_ok(ctx: &mut CommandContext, ret_form: Option<i64>) {
16 let v = if matches!(ret_form, Some(rf) if rf == prop_access::FM_STR) {
17 Value::Str(String::new())
18 } else {
19 Value::Int(0)
20 };
21 ctx.push(v);
22}
23
24fn queue_finish(
25 ctx: &mut CommandContext,
26 fa: &ObjectFrameActionState,
27 frame_action_chain: Vec<i32>,
28 reinit_after_finish: bool,
29) {
30 if fa.cmd_name.is_empty() {
31 return;
32 }
33 ctx.globals
34 .pending_frame_action_finishes
35 .push(PendingFrameActionFinish {
36 frame_action_chain,
37 object_chain: None,
38 snapshot: fa.clone(),
39 reinit_after_finish,
40 scn_name: fa.scn_name.clone(),
41 cmd_name: fa.cmd_name.clone(),
42 end_time: fa.end_time,
43 args: fa.args.clone(),
44 });
45}
46
47fn apply_set_from_parts(
48 fa: &mut ObjectFrameActionState,
49 scene_name: &str,
50 end_time: i64,
51 cmd_name: String,
52 args: Vec<Value>,
53 real_time_flag: bool,
54) {
55 fa.end_time = end_time;
56 fa.cmd_name = cmd_name;
57 fa.scn_name = scene_name.to_string();
58 fa.real_time_flag = real_time_flag;
59 fa.end_flag = false;
60 fa.counter.reset();
61 if real_time_flag {
62 fa.counter.start_real();
63 } else {
64 fa.counter.start();
65 }
66 fa.args = args;
67}
68
69fn dispatch_one(
70 ctx: &mut CommandContext,
71 fa: &mut ObjectFrameActionState,
72 frame_action_chain: &[i32],
73 tail: &[i32],
74 script_args: &[Value],
75 scene_name: &str,
76 rhs: Option<&Value>,
77 al_id: Option<i64>,
78 ret_form: Option<i64>,
79) -> bool {
80 if tail.is_empty() {
81 push_ok(ctx, ret_form);
82 return true;
83 }
84 match tail[0] {
85 crate::runtime::constants::elm_value::FRAMEACTION_COUNTER => {
86 let set_v = rhs.and_then(as_i64).or_else(|| {
87 if al_id == Some(1) && script_args.len() == 1 {
88 script_args.first().and_then(as_i64)
89 } else {
90 None
91 }
92 });
93 if let Some(v) = set_v {
94 fa.counter.set_count(v);
95 push_ok(ctx, ret_form);
96 } else {
97 let count = fa.counter.get_count();
98 ctx.push(Value::Int(count));
99 }
100 true
101 }
102 crate::runtime::constants::elm_value::FRAMEACTION_START => {
103 let end_time = script_args.first().and_then(as_i64).unwrap_or(0);
104 let cmd_name = script_args
105 .get(1)
106 .and_then(|v| v.as_str())
107 .unwrap_or("")
108 .to_string();
109 let fa_args = script_args.iter().skip(2).cloned().collect();
110 queue_finish(ctx, fa, frame_action_chain.to_vec(), false);
111 apply_set_from_parts(fa, scene_name, end_time, cmd_name, fa_args, false);
112 push_ok(ctx, ret_form);
113 true
114 }
115 crate::runtime::constants::elm_value::FRAMEACTION_END => {
116 queue_finish(ctx, fa, frame_action_chain.to_vec(), true);
117 fa.reinit_without_finish();
118 push_ok(ctx, ret_form);
119 true
120 }
121 crate::runtime::constants::elm_value::FRAMEACTION_START_REAL => {
122 let end_time = script_args.first().and_then(as_i64).unwrap_or(0);
123 let cmd_name = script_args
124 .get(1)
125 .and_then(|v| v.as_str())
126 .unwrap_or("")
127 .to_string();
128 let fa_args = script_args.iter().skip(2).cloned().collect();
129 queue_finish(ctx, fa, frame_action_chain.to_vec(), false);
130 apply_set_from_parts(fa, scene_name, end_time, cmd_name, fa_args, true);
131 push_ok(ctx, ret_form);
132 true
133 }
134 crate::runtime::constants::elm_value::FRAMEACTION_IS_END_ACTION => {
135 ctx.push(Value::Int(if fa.end_flag { 1 } else { 0 }));
136 true
137 }
138 _ => false,
139 }
140}
141
142pub fn dispatch(ctx: &mut CommandContext, form_id: u32, args: &[Value]) -> Result<bool> {
143 let Some((chain_pos, chain_ref)) = prop_access::parse_current_element_chain(ctx, args) else {
144 return Ok(false);
145 };
146 let chain: Vec<i32> = chain_ref.to_vec();
147 let tail: Vec<i32> = chain[1..].to_vec();
148 let script_args: Vec<Value> = prop_access::script_args(args, chain_pos).to_vec();
149 let scene_name = ctx.current_scene_name.clone().unwrap_or_default();
150 let (al_id, ret_form) = prop_access::current_vm_meta(ctx);
151 let rhs = if al_id == Some(1) {
152 script_args.first().cloned()
153 } else {
154 None
155 };
156 if tail.is_empty() {
157 push_ok(ctx, ret_form);
158 return Ok(true);
159 }
160
161 if tail[0] == ctx.ids.elm_array || tail[0] == -1 {
162 let idx = tail.get(1).copied().unwrap_or(0).max(0) as usize;
163 let frame_action_chain = chain[..3.min(chain.len())].to_vec();
164 let mut item = {
165 let list = ctx.globals.frame_action_lists.entry(form_id).or_default();
166 if list.len() <= idx {
167 list.resize_with(idx + 1, ObjectFrameActionState::default);
168 }
169 std::mem::take(&mut list[idx])
170 };
171 let handled = dispatch_one(
172 ctx,
173 &mut item,
174 &frame_action_chain,
175 &tail[2..],
176 &script_args,
177 &scene_name,
178 rhs.as_ref(),
179 al_id,
180 ret_form,
181 );
182 ctx.globals.frame_action_lists.entry(form_id).or_default()[idx] = item;
183 return Ok(handled);
184 }
185
186 match tail[0] {
187 crate::runtime::constants::elm_value::FRAMEACTIONLIST_RESIZE => {
188 let n = script_args.first().and_then(as_i64).unwrap_or(0).max(0) as usize;
189 ctx.globals
190 .frame_action_lists
191 .entry(form_id)
192 .or_default()
193 .resize_with(n, ObjectFrameActionState::default);
194 push_ok(ctx, ret_form);
195 Ok(true)
196 }
197 crate::runtime::constants::elm_value::FRAMEACTIONLIST_GET_SIZE => {
198 let len = ctx
199 .globals
200 .frame_action_lists
201 .entry(form_id)
202 .or_default()
203 .len();
204 ctx.push(Value::Int(len as i64));
205 Ok(true)
206 }
207 _ => Ok(false),
208 }
209}