Skip to main content

siglus_scene_vm/runtime/forms/
int_event.rs

1use anyhow::{bail, Result};
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<'a>(chain_pos: usize, args: &'a [Value]) -> &'a [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            if let Some(v) = value.as_i64() {
42                *ev = IntEvent::new(v as i32);
43            }
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
137                .globals
138                .int_event_lists
139                .entry(form_id)
140                .or_insert_with(Vec::new);
141            if list.len() <= idx {
142                list.resize_with(idx + 1, || IntEvent::new(0));
143            }
144            let ev = &mut list[idx];
145            run_event_op(ev, op, params, ret_form, args, chain_pos, Some(idx))?
146        } else if chain.len() >= 2 && chain[1] == int_event_list_op::RESIZE {
147            let n = params.first().and_then(|v| v.as_i64()).unwrap_or(0).max(0) as usize;
148            let list = ctx
149                .globals
150                .int_event_lists
151                .entry(form_id)
152                .or_insert_with(Vec::new);
153            list.resize_with(n, || IntEvent::new(0));
154            PostAction::Push(default_for_ret_form(ret_form.unwrap_or(0)))
155        } else {
156            bail!("unsupported INTEVENTLIST op chain {:?}", chain)
157        }
158    } else {
159        let op = chain.get(1).copied().unwrap_or(0);
160        let ev = ctx
161            .globals
162            .int_event_roots
163            .entry(form_id)
164            .or_insert_with(|| IntEvent::new(0));
165        if let Some(Value::Int(v)) = rhs {
166            *ev = IntEvent::new(v as i32);
167            PostAction::Push(default_for_ret_form(ret_form.unwrap_or(0)))
168        } else {
169            run_event_op(ev, op, params, ret_form, args, chain_pos, None)?
170        }
171    };
172
173    match post {
174        PostAction::Push(v) => ctx.push(v),
175        PostAction::Wait { index, key_skip } => ctx
176            .wait
177            .wait_generic_int_event(form_id, index, key_skip, key_skip),
178    }
179    Ok(true)
180}