Skip to main content

siglus_scene_vm/runtime/forms/
joypad.rs

1use anyhow::{Result, bail};
2
3use crate::runtime::input::JOYPAD_KEY_COUNT;
4use crate::runtime::{CommandContext, Value};
5
6/// Newer SiglusEngine builds expose GLOBAL.188 as the raw joypad-key array.
7///
8/// Recovered bytecode shape:
9///     [188, 0, ELM_ARRAY, key_no, KEY_OP]
10///
11/// The terminal operations are the ordinary KEY BUTTON queries.  Like
12/// `tnm_command_proc_key` in the original engine they CHECK edge stock; they do
13/// not consume it.
14pub fn dispatch(ctx: &mut CommandContext, _args: &[Value]) -> Result<bool> {
15    let Some(vm_call) = ctx.vm_call.as_ref() else {
16        return Ok(false);
17    };
18    let chain = &vm_call.element;
19    if chain.len() < 4 || chain[0] != ctx.ids.form_global_joypad as i32 {
20        return Ok(false);
21    }
22
23    // Newer registration exposes property 0 as the joypad KEY array. Its raw
24    // button index range follows the 32-button WinMM JOYINFOEX backend domain.
25    if chain[1] != 0 || chain[2] != ctx.ids.elm_array {
26        return Ok(false);
27    }
28
29    // Keep property traversal usable by the VM in the same way as KEYLIST:
30    // JOYPAD.KEY[-1,index] without a terminal KEY command denotes the item.
31    if chain.len() == 4 {
32        ctx.push(Value::Element(chain.to_vec()));
33        return Ok(true);
34    }
35
36    let key_no = chain[3];
37    let op = chain[4] as i64;
38    if key_no < 0 || key_no as usize >= JOYPAD_KEY_COUNT {
39        bail!("joypad key index out of range: {key_no}");
40    }
41    let mut key_no = key_no as usize;
42    // config.sav v1.4 stores two independent controller face-button swaps.
43    // WinMM/Xbox face-button numbering is A=0, B=1, X=2, Y=3 in the
44    // zero-based script-visible JOYPAD array. Apply the swap before every KEY
45    // state query so held and edge-stock semantics remain identical.
46    let config = &ctx.globals.syscom.original_config;
47    key_no = match key_no {
48        0 if config.joypad_swap_ab => 1,
49        1 if config.joypad_swap_ab => 0,
50        2 if config.joypad_swap_xy => 3,
51        3 if config.joypad_swap_xy => 2,
52        _ => key_no,
53    };
54
55    let v = if op == ctx.ids.key_op_on_down as i64 {
56        ctx.script_input.joypad_down_stock(key_no)
57    } else if op == ctx.ids.key_op_on_up as i64 {
58        ctx.script_input.joypad_up_stock(key_no)
59    } else if op == ctx.ids.key_op_on_down_up as i64 {
60        ctx.script_input.joypad_down_up_stock(key_no)
61    } else if op == ctx.ids.key_op_is_down as i64 {
62        ctx.script_input.joypad_is_down(key_no)
63    } else if op == ctx.ids.key_op_is_up as i64 {
64        !ctx.script_input.joypad_is_down(key_no)
65    } else {
66        bail!("unsupported joypad key operation: {op}");
67    };
68
69    ctx.push(Value::Int(if v { 1 } else { 0 }));
70    Ok(true)
71}