Skip to main content

siglus_scene_vm/runtime/forms/
screen.rs

1//! GLOBAL.SCREEN form handling aligned to the original screen command path.
2//!
3//! The original C++ `tnm_command_proc_screen()` dispatches strictly by the
4//! current element chain. It does not recover SCREEN selectors or setter RHS
5//! values from arbitrary argument positions.
6
7use anyhow::Result;
8
9use super::prop_access;
10use crate::runtime::forms::codes::int_event_op;
11use crate::runtime::globals::{ScreenEffectState, ScreenFormState, ScreenQuakeState};
12use crate::runtime::{CommandContext, Value};
13
14const EFFECTLIST_RESIZE_OP: i32 = 1;
15const EFFECTLIST_GET_SIZE_OP: i32 = 2;
16
17const QUAKE_START_OP: i32 = 0;
18const QUAKE_START_WAIT_OP: i32 = 1;
19const QUAKE_START_WAIT_KEY_OP: i32 = 2;
20const QUAKE_START_NOWAIT_OP: i32 = 3;
21const QUAKE_START_ALL_OP: i32 = 4;
22const QUAKE_START_ALL_WAIT_OP: i32 = 5;
23const QUAKE_START_ALL_WAIT_KEY_OP: i32 = 6;
24const QUAKE_START_ALL_NOWAIT_OP: i32 = 7;
25const QUAKE_END_OP: i32 = 8;
26const QUAKE_CHECK_OP: i32 = 9;
27const QUAKE_WAIT_OP: i32 = 10;
28const QUAKE_WAIT_KEY_OP: i32 = 11;
29
30fn as_i64(v: &Value) -> Option<i64> {
31    match v {
32        Value::Int(n) => Some(*n),
33        Value::NamedArg { value, .. } => value.as_i64(),
34        _ => None,
35    }
36}
37
38fn default_for_ret_form(ret_form: i64) -> Value {
39    if prop_access::ret_form_is_string(ret_form) {
40        Value::Str(String::new())
41    } else {
42        Value::Int(0)
43    }
44}
45
46fn anim_skip_trace_enabled() -> bool {
47    std::env::var_os("SG_DEBUG").is_some()
48}
49
50fn screen_event_state(ev: &crate::runtime::int_event::IntEvent) -> String {
51    format!(
52        "value={} cur={} start={} end={} cur_time={} end_time={} delay={} loop_type={} speed={} real={} active={}",
53        ev.value, ev.cur_value, ev.start_value, ev.end_value, ev.cur_time, ev.end_time,
54        ev.delay_time, ev.loop_type, ev.speed_type, ev.real_flag, ev.check_event()
55    )
56}
57
58fn effect_prop_ref_mut<'a>(
59    ids: &crate::runtime::constants::RuntimeConstants,
60    effect: &'a mut ScreenEffectState,
61    op: i32,
62) -> Option<&'a mut i32> {
63    match op {
64        s if s == ids.effect_wipe_copy => Some(&mut effect.wipe_copy),
65        s if s == ids.effect_wipe_erase => Some(&mut effect.wipe_erase),
66        s if s == ids.effect_begin_order => Some(&mut effect.begin_order),
67        s if s == ids.effect_begin_layer => Some(&mut effect.begin_layer),
68        s if s == ids.effect_end_order => Some(&mut effect.end_order),
69        s if s == ids.effect_end_layer => Some(&mut effect.end_layer),
70        _ => None,
71    }
72}
73
74fn effect_prop_value(
75    ids: &crate::runtime::constants::RuntimeConstants,
76    effect: &ScreenEffectState,
77    op: i32,
78) -> Option<i64> {
79    match op {
80        s if s == ids.effect_wipe_copy => Some(effect.wipe_copy as i64),
81        s if s == ids.effect_wipe_erase => Some(effect.wipe_erase as i64),
82        s if s == ids.effect_begin_order => Some(effect.begin_order as i64),
83        s if s == ids.effect_begin_layer => Some(effect.begin_layer as i64),
84        s if s == ids.effect_end_order => Some(effect.end_order as i64),
85        s if s == ids.effect_end_layer => Some(effect.end_layer as i64),
86        _ => None,
87    }
88}
89
90fn effect_event_mut<'a>(
91    ids: &crate::runtime::constants::RuntimeConstants,
92    effect: &'a mut ScreenEffectState,
93    op: i32,
94) -> Option<&'a mut crate::runtime::int_event::IntEvent> {
95    match op {
96        s if s == ids.effect_x || s == ids.effect_x_eve => Some(&mut effect.x),
97        s if s == ids.effect_y || s == ids.effect_y_eve => Some(&mut effect.y),
98        s if s == ids.effect_z || s == ids.effect_z_eve => Some(&mut effect.z),
99        s if s == ids.effect_mono || s == ids.effect_mono_eve => Some(&mut effect.mono),
100        s if s == ids.effect_reverse || s == ids.effect_reverse_eve => Some(&mut effect.reverse),
101        s if s == ids.effect_bright || s == ids.effect_bright_eve => Some(&mut effect.bright),
102        s if s == ids.effect_dark || s == ids.effect_dark_eve => Some(&mut effect.dark),
103        s if s == ids.effect_color_r || s == ids.effect_color_r_eve => Some(&mut effect.color_r),
104        s if s == ids.effect_color_g || s == ids.effect_color_g_eve => Some(&mut effect.color_g),
105        s if s == ids.effect_color_b || s == ids.effect_color_b_eve => Some(&mut effect.color_b),
106        s if s == ids.effect_color_rate || s == ids.effect_color_rate_eve => {
107            Some(&mut effect.color_rate)
108        }
109        s if s == ids.effect_color_add_r || s == ids.effect_color_add_r_eve => {
110            Some(&mut effect.color_add_r)
111        }
112        s if s == ids.effect_color_add_g || s == ids.effect_color_add_g_eve => {
113            Some(&mut effect.color_add_g)
114        }
115        s if s == ids.effect_color_add_b || s == ids.effect_color_add_b_eve => {
116            Some(&mut effect.color_add_b)
117        }
118        _ => None,
119    }
120}
121
122fn run_int_event_command(
123    ctx: &mut CommandContext,
124    ev: &mut crate::runtime::int_event::IntEvent,
125    subop: i32,
126    params: &[Value],
127    ret_form: i64,
128) {
129    match subop {
130        int_event_op::SET | int_event_op::SET_REAL => {
131            let value = params.first().and_then(as_i64).unwrap_or(0) as i32;
132            let total_time = params.get(1).and_then(as_i64).unwrap_or(0) as i32;
133            let delay_time = params.get(2).and_then(as_i64).unwrap_or(0) as i32;
134            let speed_type = params.get(3).and_then(as_i64).unwrap_or(0) as i32;
135            let real_flag = if subop == int_event_op::SET_REAL {
136                1
137            } else {
138                0
139            };
140            ev.set_event(value, total_time, delay_time, speed_type, real_flag);
141            if anim_skip_trace_enabled() {
142                eprintln!(
143                    "[SG_DEBUG][ANIM_SKIP_TRACE][SCREEN] INTEVENT.SET subop={} value={} total_time={} delay={} speed={} real={} state=[{}]",
144                    subop, value, total_time, delay_time, speed_type, real_flag, screen_event_state(ev)
145                );
146            }
147            ctx.stack.push(default_for_ret_form(ret_form));
148        }
149        int_event_op::LOOP | int_event_op::LOOP_REAL => {
150            let start_value = params.first().and_then(as_i64).unwrap_or(0) as i32;
151            let end_value = params.get(1).and_then(as_i64).unwrap_or(0) as i32;
152            let loop_time = params.get(2).and_then(as_i64).unwrap_or(0) as i32;
153            let delay_time = params.get(3).and_then(as_i64).unwrap_or(0) as i32;
154            let speed_type = params.get(4).and_then(as_i64).unwrap_or(0) as i32;
155            let real_flag = if subop == int_event_op::LOOP_REAL {
156                1
157            } else {
158                0
159            };
160            ev.loop_event(
161                start_value,
162                end_value,
163                loop_time,
164                delay_time,
165                speed_type,
166                real_flag,
167            );
168            if anim_skip_trace_enabled() {
169                eprintln!(
170                    "[SG_DEBUG][ANIM_SKIP_TRACE][SCREEN] INTEVENT.LOOP subop={} start={} end={} loop_time={} delay={} speed={} real={} state=[{}]",
171                    subop, start_value, end_value, loop_time, delay_time, speed_type, real_flag, screen_event_state(ev)
172                );
173            }
174            ctx.stack.push(default_for_ret_form(ret_form));
175        }
176        int_event_op::TURN | int_event_op::TURN_REAL => {
177            let start_value = params.first().and_then(as_i64).unwrap_or(0) as i32;
178            let end_value = params.get(1).and_then(as_i64).unwrap_or(0) as i32;
179            let loop_time = params.get(2).and_then(as_i64).unwrap_or(0) as i32;
180            let delay_time = params.get(3).and_then(as_i64).unwrap_or(0) as i32;
181            let speed_type = params.get(4).and_then(as_i64).unwrap_or(0) as i32;
182            let real_flag = if subop == int_event_op::TURN_REAL {
183                1
184            } else {
185                0
186            };
187            ev.turn_event(
188                start_value,
189                end_value,
190                loop_time,
191                delay_time,
192                speed_type,
193                real_flag,
194            );
195            if anim_skip_trace_enabled() {
196                eprintln!(
197                    "[SG_DEBUG][ANIM_SKIP_TRACE][SCREEN] INTEVENT.TURN subop={} start={} end={} loop_time={} delay={} speed={} real={} state=[{}]",
198                    subop, start_value, end_value, loop_time, delay_time, speed_type, real_flag, screen_event_state(ev)
199                );
200            }
201            ctx.stack.push(default_for_ret_form(ret_form));
202        }
203        int_event_op::END => {
204            if anim_skip_trace_enabled() {
205                eprintln!(
206                    "[SG_DEBUG][ANIM_SKIP_TRACE][SCREEN] INTEVENT.END before state=[{}]",
207                    screen_event_state(ev)
208                );
209            }
210            ev.end_event();
211            if anim_skip_trace_enabled() {
212                eprintln!(
213                    "[SG_DEBUG][ANIM_SKIP_TRACE][SCREEN] INTEVENT.END after state=[{}]",
214                    screen_event_state(ev)
215                );
216            }
217            ctx.stack.push(default_for_ret_form(ret_form));
218        }
219        int_event_op::WAIT => {
220            if anim_skip_trace_enabled() {
221                eprintln!("[SG_DEBUG][ANIM_SKIP_TRACE][SCREEN] INTEVENT.WAIT requested NOTE=current implementation routes through generic form_id=0");
222            }
223            ctx.wait.wait_generic_int_event(0, None, false, false)
224        }
225        int_event_op::WAIT_KEY => {
226            if anim_skip_trace_enabled() {
227                eprintln!("[SG_DEBUG][ANIM_SKIP_TRACE][SCREEN] INTEVENT.WAIT_KEY requested NOTE=current implementation routes through generic form_id=0");
228            }
229            ctx.wait.wait_generic_int_event(0, None, true, true)
230        }
231        int_event_op::CHECK => ctx
232            .stack
233            .push(Value::Int(if ev.check_event() { 1 } else { 0 })),
234        _ => ctx.stack.push(default_for_ret_form(ret_form)),
235    }
236}
237
238fn screen_to_effect_prop(
239    ids: &crate::runtime::constants::RuntimeConstants,
240    selector: i32,
241) -> Option<i32> {
242    match selector {
243        s if ids.screen_x != 0 && s == ids.screen_x => Some(ids.effect_x),
244        s if ids.screen_y != 0 && s == ids.screen_y => Some(ids.effect_y),
245        s if ids.screen_z != 0 && s == ids.screen_z => Some(ids.effect_z),
246        s if ids.screen_mono != 0 && s == ids.screen_mono => Some(ids.effect_mono),
247        s if ids.screen_reverse != 0 && s == ids.screen_reverse => Some(ids.effect_reverse),
248        s if ids.screen_bright != 0 && s == ids.screen_bright => Some(ids.effect_bright),
249        s if ids.screen_dark != 0 && s == ids.screen_dark => Some(ids.effect_dark),
250        s if ids.screen_color_r != 0 && s == ids.screen_color_r => Some(ids.effect_color_r),
251        s if ids.screen_color_g != 0 && s == ids.screen_color_g => Some(ids.effect_color_g),
252        s if ids.screen_color_b != 0 && s == ids.screen_color_b => Some(ids.effect_color_b),
253        s if ids.screen_color_rate != 0 && s == ids.screen_color_rate => {
254            Some(ids.effect_color_rate)
255        }
256        s if ids.screen_color_add_r != 0 && s == ids.screen_color_add_r => {
257            Some(ids.effect_color_add_r)
258        }
259        s if ids.screen_color_add_g != 0 && s == ids.screen_color_add_g => {
260            Some(ids.effect_color_add_g)
261        }
262        s if ids.screen_color_add_b != 0 && s == ids.screen_color_add_b => {
263            Some(ids.effect_color_add_b)
264        }
265        _ => None,
266    }
267}
268
269fn screen_to_effect_event(
270    ids: &crate::runtime::constants::RuntimeConstants,
271    selector: i32,
272) -> Option<i32> {
273    match selector {
274        s if ids.screen_x_eve != 0 && s == ids.screen_x_eve => Some(ids.effect_x_eve),
275        s if ids.screen_y_eve != 0 && s == ids.screen_y_eve => Some(ids.effect_y_eve),
276        s if ids.screen_z_eve != 0 && s == ids.screen_z_eve => Some(ids.effect_z_eve),
277        s if ids.screen_mono_eve != 0 && s == ids.screen_mono_eve => Some(ids.effect_mono_eve),
278        s if ids.screen_reverse_eve != 0 && s == ids.screen_reverse_eve => {
279            Some(ids.effect_reverse_eve)
280        }
281        s if ids.screen_bright_eve != 0 && s == ids.screen_bright_eve => {
282            Some(ids.effect_bright_eve)
283        }
284        s if ids.screen_dark_eve != 0 && s == ids.screen_dark_eve => Some(ids.effect_dark_eve),
285        s if ids.screen_color_r_eve != 0 && s == ids.screen_color_r_eve => {
286            Some(ids.effect_color_r_eve)
287        }
288        s if ids.screen_color_g_eve != 0 && s == ids.screen_color_g_eve => {
289            Some(ids.effect_color_g_eve)
290        }
291        s if ids.screen_color_b_eve != 0 && s == ids.screen_color_b_eve => {
292            Some(ids.effect_color_b_eve)
293        }
294        s if ids.screen_color_rate_eve != 0 && s == ids.screen_color_rate_eve => {
295            Some(ids.effect_color_rate_eve)
296        }
297        s if ids.screen_color_add_r_eve != 0 && s == ids.screen_color_add_r_eve => {
298            Some(ids.effect_color_add_r_eve)
299        }
300        s if ids.screen_color_add_g_eve != 0 && s == ids.screen_color_add_g_eve => {
301            Some(ids.effect_color_add_g_eve)
302        }
303        s if ids.screen_color_add_b_eve != 0 && s == ids.screen_color_add_b_eve => {
304            Some(ids.effect_color_add_b_eve)
305        }
306        _ => None,
307    }
308}
309
310struct ScreenCall {
311    chain: Vec<i32>,
312    al_id: i32,
313    ret_form: i64,
314    rhs: Option<Value>,
315    script_args: Vec<Value>,
316}
317
318fn parse_screen_call(ctx: &CommandContext, args: &[Value]) -> Option<ScreenCall> {
319    let vm_call = ctx.vm_call.as_ref()?;
320    if vm_call.element.is_empty() {
321        return None;
322    }
323    let (al_id, ret_form) = prop_access::current_vm_meta(ctx);
324    let al_id = al_id.unwrap_or(-1) as i32;
325    let ret_form = ret_form.unwrap_or(0);
326    let rhs = if al_id == 1 {
327        args.first().cloned()
328    } else {
329        None
330    };
331    Some(ScreenCall {
332        chain: vm_call.element.clone(),
333        al_id,
334        ret_form,
335        rhs,
336        script_args: args.to_vec(),
337    })
338}
339
340fn last_list_arg(script_args: &[Value]) -> Option<&Vec<Value>> {
341    script_args.last().and_then(|v| match v.unwrap_named() {
342        Value::List(list) => Some(list),
343        _ => None,
344    })
345}
346
347fn quake_start_flags(op: i32) -> Option<(bool, bool, bool)> {
348    match op {
349        QUAKE_START_OP => Some((false, false, false)),
350        QUAKE_START_WAIT_OP => Some((false, true, false)),
351        QUAKE_START_WAIT_KEY_OP => Some((false, true, true)),
352        QUAKE_START_NOWAIT_OP => Some((false, false, false)),
353        QUAKE_START_ALL_OP => Some((true, false, false)),
354        QUAKE_START_ALL_WAIT_OP => Some((true, true, false)),
355        QUAKE_START_ALL_WAIT_KEY_OP => Some((true, true, true)),
356        QUAKE_START_ALL_NOWAIT_OP => Some((true, false, false)),
357        _ => None,
358    }
359}
360
361fn parse_quake_command(
362    item: &mut ScreenQuakeState,
363    op: i32,
364    script_args: &[Value],
365    ctx: &mut CommandContext,
366) -> bool {
367    if let Some((all_range, wait_flag, key_flag)) = quake_start_flags(op) {
368        let quake_type = script_args.first().and_then(as_i64).unwrap_or(0) as i32;
369        let time = script_args.get(1).and_then(as_i64).unwrap_or(1000);
370        let _cnt = script_args.get(2).and_then(as_i64).unwrap_or(0) as i32;
371        let _end_cnt = script_args.get(3).and_then(as_i64).unwrap_or(0) as i32;
372        item.begin_order = if all_range {
373            script_args
374                .get(4)
375                .and_then(as_i64)
376                .unwrap_or(i32::MIN as i64) as i32
377        } else {
378            script_args.get(4).and_then(as_i64).unwrap_or(0) as i32
379        };
380        item.end_order = if all_range {
381            script_args
382                .get(5)
383                .and_then(as_i64)
384                .unwrap_or(i32::MAX as i64) as i32
385        } else {
386            script_args.get(5).and_then(as_i64).unwrap_or(0) as i32
387        };
388
389        let opt = last_list_arg(script_args);
390        item.power = opt
391            .and_then(|list| list.first())
392            .and_then(as_i64)
393            .unwrap_or(0) as i32;
394        item.vec = opt
395            .and_then(|list| list.get(1))
396            .and_then(as_i64)
397            .unwrap_or(0) as i32;
398        item.center_x = opt
399            .and_then(|list| list.get(1))
400            .and_then(as_i64)
401            .unwrap_or(0) as i32;
402        item.center_y = opt
403            .and_then(|list| list.get(2))
404            .and_then(as_i64)
405            .unwrap_or(0) as i32;
406        item.start_kind(quake_type, time);
407        if wait_flag {
408            let rem = item.remaining_ms();
409            if key_flag {
410                ctx.wait.wait_ms_key(rem);
411            } else {
412                ctx.wait.wait_ms(rem);
413            }
414        }
415        ctx.stack.push(Value::Int(0));
416        return true;
417    }
418
419    match op {
420        QUAKE_END_OP => {
421            item.end_ms(script_args.first().and_then(as_i64).unwrap_or(0));
422            ctx.stack.push(Value::Int(0));
423            true
424        }
425        QUAKE_WAIT_OP => {
426            ctx.wait.wait_ms(item.remaining_ms());
427            ctx.stack.push(Value::Int(0));
428            true
429        }
430        QUAKE_WAIT_KEY_OP => {
431            ctx.wait.wait_ms_key(item.remaining_ms());
432            ctx.stack.push(Value::Int(0));
433            true
434        }
435        QUAKE_CHECK_OP => {
436            ctx.stack.push(Value::Int(item.check_value() as i64));
437            true
438        }
439        _ => false,
440    }
441}
442
443fn get_or_set_effect_scalar(
444    ctx: &mut CommandContext,
445    effect: &mut ScreenEffectState,
446    ids: &crate::runtime::constants::RuntimeConstants,
447    op: i32,
448    al_id: i32,
449    rhs: Option<&Value>,
450    ret_form: i64,
451) -> bool {
452    if let Some(ev) = effect_event_mut(ids, effect, op) {
453        match al_id {
454            0 => {
455                ctx.stack.push(Value::Int(ev.get_total_value() as i64));
456                true
457            }
458            1 => {
459                let value = rhs.and_then(as_i64).unwrap_or(0) as i32;
460                ev.set_value(value);
461                ev.frame();
462                ctx.stack.push(default_for_ret_form(ret_form));
463                true
464            }
465            _ => false,
466        }
467    } else if let Some(slot) = effect_prop_ref_mut(ids, effect, op) {
468        match al_id {
469            0 => {
470                ctx.stack.push(Value::Int(*slot as i64));
471                true
472            }
473            1 => {
474                *slot = rhs.and_then(as_i64).unwrap_or(0) as i32;
475                ctx.stack.push(default_for_ret_form(ret_form));
476                true
477            }
478            _ => false,
479        }
480    } else if let Some(value) = effect_prop_value(ids, effect, op) {
481        if al_id == 0 {
482            ctx.stack.push(Value::Int(value));
483            true
484        } else {
485            false
486        }
487    } else {
488        false
489    }
490}
491
492pub fn dispatch(ctx: &mut CommandContext, args: &[Value]) -> Result<bool> {
493    let Some(call) = parse_screen_call(ctx, args) else {
494        return Ok(false);
495    };
496    let chain = call.chain;
497    let form_id = chain[0] as u32;
498    let mut st = ctx
499        .globals
500        .screen_forms
501        .remove(&form_id)
502        .unwrap_or_default();
503    let result = (|| {
504        if chain.len() == 1 {
505            if call.ret_form != 0 {
506                ctx.stack.push(default_for_ret_form(call.ret_form));
507            }
508            return Ok(true);
509        }
510
511        let selector = chain[1];
512        let ids = ctx.ids.clone();
513
514        if selector == ids.screen_sel_effect {
515            if chain.len() == 3 {
516                match chain[2] {
517                    EFFECTLIST_RESIZE_OP => {
518                        st.ensure_effect_len(
519                            args.first().and_then(as_i64).unwrap_or(0).max(0) as usize
520                        );
521                        ctx.stack.push(Value::Int(0));
522                        return Ok(true);
523                    }
524                    EFFECTLIST_GET_SIZE_OP => {
525                        ctx.stack.push(Value::Int(st.effect_list.len() as i64));
526                        return Ok(true);
527                    }
528                    _ => return Ok(false),
529                }
530            }
531            if chain.len() < 4 || chain[2] != ids.elm_array {
532                return Ok(false);
533            }
534            let idx = chain[3].max(0) as usize;
535            st.ensure_effect_len(idx + 1);
536            let effect = &mut st.effect_list[idx];
537            if chain.len() == 4 {
538                if call.ret_form != 0 {
539                    ctx.stack.push(default_for_ret_form(call.ret_form));
540                }
541                return Ok(true);
542            }
543            let op = chain[4];
544            if op == ids.effect_init && call.script_args.is_empty() && call.ret_form == 0 {
545                effect.reinit();
546                ctx.stack.push(Value::Int(0));
547                return Ok(true);
548            }
549            if chain.len() >= 6 {
550                if let Some(ev) = effect_event_mut(&ids, effect, op) {
551                    run_int_event_command(ctx, ev, chain[5], &call.script_args, call.ret_form);
552                    return Ok(true);
553                }
554                return Ok(false);
555            }
556            return Ok(get_or_set_effect_scalar(
557                ctx,
558                effect,
559                &ids,
560                op,
561                call.al_id,
562                call.rhs.as_ref(),
563                call.ret_form,
564            ));
565        }
566
567        if selector == ids.screen_sel_quake {
568            if chain.len() < 4 || chain[2] != ids.elm_array {
569                return Ok(false);
570            }
571            let idx = chain[3].max(0) as usize;
572            st.ensure_quake_len(idx + 1);
573            let quake = &mut st.quake_list[idx];
574            if chain.len() == 4 {
575                if call.ret_form != 0 {
576                    ctx.stack.push(default_for_ret_form(call.ret_form));
577                }
578                return Ok(true);
579            }
580            return Ok(parse_quake_command(quake, chain[4], &call.script_args, ctx));
581        }
582
583        if selector == ids.screen_sel_shake {
584            if !call.script_args.is_empty() {
585                st.shake.set_ms(call.script_args[0].as_i64().unwrap_or(0));
586                ctx.stack.push(Value::Int(0));
587                return Ok(true);
588            }
589            return Ok(false);
590        }
591
592        if let Some(effect_op) = screen_to_effect_prop(&ids, selector) {
593            st.ensure_effect_len(1);
594            let effect = &mut st.effect_list[0];
595            return Ok(get_or_set_effect_scalar(
596                ctx,
597                effect,
598                &ids,
599                effect_op,
600                call.al_id,
601                call.rhs.as_ref(),
602                call.ret_form,
603            ));
604        }
605
606        if let Some(effect_event_op) = screen_to_effect_event(&ids, selector) {
607            if chain.len() != 3 {
608                return Ok(false);
609            }
610            st.ensure_effect_len(1);
611            let effect = &mut st.effect_list[0];
612            if let Some(ev) = effect_event_mut(&ids, effect, effect_event_op) {
613                run_int_event_command(ctx, ev, chain[2], &call.script_args, call.ret_form);
614                return Ok(true);
615            }
616            return Ok(false);
617        }
618
619        Ok(false)
620    })();
621    ctx.globals.screen_forms.insert(form_id, st);
622    result
623}