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