Skip to main content

siglus_scene_vm/runtime/forms/
key.rs

1use anyhow::Result;
2
3use crate::runtime::{CommandContext, Value};
4
5const VK_LBUTTON: u8 = 0x01;
6const VK_RBUTTON: u8 = 0x02;
7const VK_RETURN: u8 = 0x0D;
8const VK_ESCAPE: u8 = 0x1B;
9const VK_Z: u8 = 0x5A;
10const VK_X: u8 = 0x58;
11
12pub fn query(ctx: &mut CommandContext, vk_code: i64, op: i64) -> i64 {
13    if vk_code == ctx.ids.exkey_decide as i64 {
14        return query_ex(ctx, true, op);
15    }
16    if vk_code == ctx.ids.exkey_cancel as i64 {
17        return query_ex(ctx, false, op);
18    }
19    if !(0..=255).contains(&vk_code) {
20        return 0;
21    }
22    query_vk(ctx, vk_code as u8, op)
23}
24
25fn query_ex(ctx: &mut CommandContext, decide: bool, op: i64) -> i64 {
26    let keys: &[u8] = if decide {
27        &[VK_LBUTTON, VK_RETURN, VK_X]
28    } else {
29        &[VK_RBUTTON, VK_ESCAPE, VK_Z]
30    };
31    match op {
32        o if o == ctx.ids.key_op_on_down as i64 => bool_i64(
33            keys.iter()
34                .any(|&k| key_enabled(ctx, k) && ctx.script_input.vk_down_stock(k)),
35        ),
36        o if o == ctx.ids.key_op_on_up as i64 => bool_i64(
37            keys.iter()
38                .any(|&k| key_enabled(ctx, k) && ctx.script_input.vk_up_stock(k)),
39        ),
40        o if o == ctx.ids.key_op_on_down_up as i64 => bool_i64(
41            keys.iter()
42                .any(|&k| key_enabled(ctx, k) && ctx.script_input.vk_down_up_stock(k)),
43        ),
44        o if o == ctx.ids.key_op_is_down as i64 => bool_i64(
45            keys.iter()
46                .any(|&k| key_enabled(ctx, k) && ctx.script_input.vk_is_down(k)),
47        ),
48        o if o == ctx.ids.key_op_is_up as i64 => bool_i64(
49            keys.iter()
50                .all(|&k| !key_enabled(ctx, k) || !ctx.script_input.vk_is_down(k)),
51        ),
52        _ => 0,
53    }
54}
55
56fn query_vk(ctx: &mut CommandContext, vk: u8, op: i64) -> i64 {
57    if !key_enabled(ctx, vk) {
58        return if op == ctx.ids.key_op_is_up as i64 {
59            1
60        } else {
61            0
62        };
63    }
64    match op {
65        o if o == ctx.ids.key_op_dir as i64 => dir_mask_filtered(ctx),
66        o if o == ctx.ids.key_op_on_down as i64 => bool_i64(ctx.script_input.vk_down_stock(vk)),
67        o if o == ctx.ids.key_op_on_up as i64 => bool_i64(ctx.script_input.vk_up_stock(vk)),
68        o if o == ctx.ids.key_op_on_down_up as i64 => {
69            bool_i64(ctx.script_input.vk_down_up_stock(vk))
70        }
71        o if o == ctx.ids.key_op_is_down as i64 => bool_i64(ctx.script_input.vk_is_down(vk)),
72        o if o == ctx.ids.key_op_is_up as i64 => bool_i64(!ctx.script_input.vk_is_down(vk)),
73        o if o == ctx.ids.key_op_on_flick as i64 => bool_i64(ctx.script_input.vk_flick_stock(vk)),
74        o if o == ctx.ids.key_op_flick as i64 => ctx.script_input.vk_flick_pixel(vk).trunc() as i64,
75        o if o == ctx.ids.key_op_flick_angle as i64 => {
76            let angle = ctx.script_input.vk_flick_angle(vk) as f64;
77            let deg = 180.0 - angle.to_degrees();
78            (deg * 10.0).trunc() as i64
79        }
80        _ => 0,
81    }
82}
83
84fn key_enabled(ctx: &CommandContext, vk: u8) -> bool {
85    // Original input command handling does not route mouse buttons through the
86    // script key-disable table. Title/menu scripts still query `mouse.left.*`
87    // and `mouse.right.*` even while keyboard shortcuts are masked.
88    if matches!(vk, VK_LBUTTON | VK_RBUTTON | 0x04) {
89        return true;
90    }
91    !ctx.globals.script.key_disable.contains(&vk)
92}
93
94fn dir_mask_filtered(ctx: &CommandContext) -> i64 {
95    let mut m = 0;
96    if key_enabled(ctx, 0x25) && ctx.script_input.vk_is_down(0x25) {
97        m |= 1;
98    }
99    if key_enabled(ctx, 0x27) && ctx.script_input.vk_is_down(0x27) {
100        m |= 2;
101    }
102    if key_enabled(ctx, 0x26) && ctx.script_input.vk_is_down(0x26) {
103        m |= 4;
104    }
105    if key_enabled(ctx, 0x28) && ctx.script_input.vk_is_down(0x28) {
106        m |= 8;
107    }
108    m
109}
110
111fn bool_i64(b: bool) -> i64 {
112    if b {
113        1
114    } else {
115        0
116    }
117}
118
119pub fn dispatch(ctx: &mut CommandContext, _args: &[Value]) -> Result<bool> {
120    let vm_call = match ctx.vm_call.as_ref() {
121        Some(v) => v,
122        None => return Ok(false),
123    };
124    let chain = &vm_call.element;
125    if chain.len() < 3 || chain[0] != ctx.ids.form_global_key as i32 {
126        return Ok(false);
127    }
128
129    let (vk, op) = if chain[1] == ctx.ids.elm_array {
130        if chain.len() < 4 {
131            return Ok(false);
132        }
133        (chain[2] as i64, chain[3] as i64)
134    } else {
135        (chain[1] as i64, chain[2] as i64)
136    };
137
138    let v = query(ctx, vk, op);
139    ctx.push(Value::Int(v));
140    Ok(true)
141}