Skip to main content

siglus_scene_vm/runtime/forms/
int_event.rs

1use anyhow::{Result, bail};
2
3use crate::runtime::forms::codes::{int_event_list_op, int_event_op};
4use crate::runtime::int_event::IntEvent;
5use crate::runtime::{CommandContext, Value};
6
7use super::prop_access;
8
9fn default_for_ret_form(ret_form: i64) -> Value {
10    if prop_access::ret_form_is_string(ret_form) {
11        Value::Str(String::new())
12    } else {
13        Value::Int(0)
14    }
15}
16
17fn parse_chain<'a>(
18    ctx: &'a CommandContext,
19    form_id: u32,
20    args: &'a [Value],
21) -> Option<(usize, &'a [i32])> {
22    prop_access::parse_element_chain_ctx(ctx, form_id, args)
23}
24
25fn collect_params(chain_pos: usize, args: &[Value]) -> &[Value] {
26    prop_access::script_args(args, chain_pos)
27}
28
29enum PostAction {
30    Push(Value),
31    Wait {
32        index: Option<usize>,
33        key_skip: bool,
34    },
35}
36
37fn apply_named_init(ev: &mut IntEvent, args: &[Value], chain_pos: usize) {
38    let start = (chain_pos + 3).min(args.len());
39    for arg in &args[start..] {
40        if let Value::NamedArg { id: 0, value } = arg
41            && let Some(v) = value.as_i64()
42        {
43            *ev = IntEvent::new(v as i32);
44        }
45    }
46}
47
48fn run_event_op(
49    ev: &mut IntEvent,
50    op: i32,
51    params: &[Value],
52    ret_form: Option<i64>,
53    args: &[Value],
54    chain_pos: usize,
55    index: Option<usize>,
56) -> Result<PostAction> {
57    let post = match op {
58        int_event_op::SET | int_event_op::SET_REAL => {
59            apply_named_init(ev, args, chain_pos);
60            let value = params.first().and_then(|v| v.as_i64()).unwrap_or(0) as i32;
61            let total_time = params.get(1).and_then(|v| v.as_i64()).unwrap_or(0) as i32;
62            let delay_time = params.get(2).and_then(|v| v.as_i64()).unwrap_or(0) as i32;
63            let speed_type = params.get(3).and_then(|v| v.as_i64()).unwrap_or(0) as i32;
64            let real_flag = if op == int_event_op::SET_REAL { 1 } else { 0 };
65            ev.set_event(value, total_time, delay_time, speed_type, real_flag);
66            PostAction::Push(default_for_ret_form(ret_form.unwrap_or(0)))
67        }
68        int_event_op::LOOP | int_event_op::LOOP_REAL => {
69            let start_value = params.first().and_then(|v| v.as_i64()).unwrap_or(0) as i32;
70            let end_value = params.get(1).and_then(|v| v.as_i64()).unwrap_or(0) as i32;
71            let loop_time = params.get(2).and_then(|v| v.as_i64()).unwrap_or(0) as i32;
72            let delay_time = params.get(3).and_then(|v| v.as_i64()).unwrap_or(0) as i32;
73            let speed_type = params.get(4).and_then(|v| v.as_i64()).unwrap_or(0) as i32;
74            let real_flag = if op == int_event_op::LOOP_REAL { 1 } else { 0 };
75            ev.loop_event(
76                start_value,
77                end_value,
78                loop_time,
79                delay_time,
80                speed_type,
81                real_flag,
82            );
83            PostAction::Push(default_for_ret_form(ret_form.unwrap_or(0)))
84        }
85        int_event_op::TURN | int_event_op::TURN_REAL => {
86            let start_value = params.first().and_then(|v| v.as_i64()).unwrap_or(0) as i32;
87            let end_value = params.get(1).and_then(|v| v.as_i64()).unwrap_or(0) as i32;
88            let loop_time = params.get(2).and_then(|v| v.as_i64()).unwrap_or(0) as i32;
89            let delay_time = params.get(3).and_then(|v| v.as_i64()).unwrap_or(0) as i32;
90            let speed_type = params.get(4).and_then(|v| v.as_i64()).unwrap_or(0) as i32;
91            let real_flag = if op == int_event_op::TURN_REAL { 1 } else { 0 };
92            ev.turn_event(
93                start_value,
94                end_value,
95                loop_time,
96                delay_time,
97                speed_type,
98                real_flag,
99            );
100            PostAction::Push(default_for_ret_form(ret_form.unwrap_or(0)))
101        }
102        int_event_op::END => {
103            ev.end_event();
104            PostAction::Push(default_for_ret_form(ret_form.unwrap_or(0)))
105        }
106        int_event_op::WAIT => PostAction::Wait {
107            index,
108            key_skip: false,
109        },
110        int_event_op::WAIT_KEY => PostAction::Wait {
111            index,
112            key_skip: true,
113        },
114        int_event_op::CHECK => PostAction::Push(Value::Int(if ev.check_event() { 1 } else { 0 })),
115        _ => bail!("unsupported INTEVENT op {}", op),
116    };
117    Ok(post)
118}
119
120pub fn dispatch(ctx: &mut CommandContext, form_id: u32, args: &[Value]) -> Result<bool> {
121    let Some((chain_pos, chain)) = parse_chain(ctx, form_id, args) else {
122        return Ok(false);
123    };
124
125    let params = collect_params(chain_pos, args);
126    let (_al_id, ret_form, rhs) =
127        super::prop_access::infer_assign_and_ret_ctx(ctx, chain_pos, args);
128
129    let post = if form_id as i32 == crate::runtime::forms::codes::FM_INTEVENTLIST {
130        if chain.len() >= 4
131            && (chain[1] == ctx.ids.elm_array
132                || chain[1] == crate::runtime::forms::codes::ELM_ARRAY)
133        {
134            let idx = chain[2].max(0) as usize;
135            let op = chain[3];
136            let list = ctx.globals.int_event_lists.entry(form_id).or_default();
137            if list.len() <= idx {
138                list.resize_with(idx + 1, || IntEvent::new(0));
139            }
140            let ev = &mut list[idx];
141            run_event_op(ev, op, params, ret_form, args, chain_pos, Some(idx))?
142        } else if chain.len() >= 2 && chain[1] == int_event_list_op::RESIZE {
143            let n = params.first().and_then(|v| v.as_i64()).unwrap_or(0).max(0) as usize;
144            let list = ctx.globals.int_event_lists.entry(form_id).or_default();
145            list.resize_with(n, || IntEvent::new(0));
146            PostAction::Push(default_for_ret_form(ret_form.unwrap_or(0)))
147        } else {
148            bail!("unsupported INTEVENTLIST op chain {:?}", chain)
149        }
150    } else {
151        let op = chain.get(1).copied().unwrap_or(0);
152        let ev = ctx
153            .globals
154            .int_event_roots
155            .entry(form_id)
156            .or_insert_with(|| IntEvent::new(0));
157        if let Some(Value::Int(v)) = rhs {
158            *ev = IntEvent::new(v as i32);
159            PostAction::Push(default_for_ret_form(ret_form.unwrap_or(0)))
160        } else {
161            run_event_op(ev, op, params, ret_form, args, chain_pos, None)?
162        }
163    };
164
165    match post {
166        PostAction::Push(v) => ctx.push(v),
167        PostAction::Wait { index, key_skip } => ctx
168            .wait
169            .wait_generic_int_event(form_id, index, key_skip, key_skip),
170    }
171    Ok(true)
172}