siglus_scene_vm/runtime/forms/
int_event.rs1use 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}