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) {
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}