Skip to main content

siglus_scene_vm/runtime/forms/
frame_action_ch.rs

1use 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}