wormhole-engine 0.1.0

A portable, no-editor game engine with Rust core and Crystal scripting
Documentation
// Input System - Tracks keyboard and mouse state

use std::collections::HashSet;
use winit::keyboard::{KeyCode, PhysicalKey};
use winit::event::MouseButton;

pub struct InputState {
    // Keyboard state
    keys_pressed: HashSet<KeyCode>,
    keys_just_pressed: HashSet<KeyCode>,
    keys_just_released: HashSet<KeyCode>,
    
    // Mouse state
    mouse_position: (f32, f32),
    mouse_delta: (f32, f32),  // Change in mouse position this frame
    mouse_buttons_pressed: HashSet<MouseButton>,
    mouse_buttons_just_pressed: HashSet<MouseButton>,
    mouse_buttons_just_released: HashSet<MouseButton>,
    mouse_scroll_delta: (f32, f32),
    
    // Frame tracking
    frame_ended: bool,
}

impl InputState {
    pub fn new() -> Self {
        Self {
            keys_pressed: HashSet::new(),
            keys_just_pressed: HashSet::new(),
            keys_just_released: HashSet::new(),
            mouse_position: (0.0, 0.0),
            mouse_delta: (0.0, 0.0),
            mouse_buttons_pressed: HashSet::new(),
            mouse_buttons_just_pressed: HashSet::new(),
            mouse_buttons_just_released: HashSet::new(),
            mouse_scroll_delta: (0.0, 0.0),
            frame_ended: false,
        }
    }

    /// Call at the start of each frame to clear "just pressed/released" states
    pub fn begin_frame(&mut self) {
        self.keys_just_pressed.clear();
        self.keys_just_released.clear();
        self.mouse_buttons_just_pressed.clear();
        self.mouse_buttons_just_released.clear();
        self.mouse_scroll_delta = (0.0, 0.0);
        self.mouse_delta = (0.0, 0.0);  // Clear mouse delta at start of frame
        self.frame_ended = false;
    }

    /// Call at the end of each frame
    pub fn end_frame(&mut self) {
        self.frame_ended = true;
    }

    /// Handle key pressed event
    pub fn on_key_pressed(&mut self, key_code: KeyCode) {
        if self.keys_pressed.insert(key_code) {
            // Only add to just_pressed if it wasn't already pressed
            self.keys_just_pressed.insert(key_code);
        }
    }

    /// Handle key released event
    pub fn on_key_released(&mut self, key_code: KeyCode) {
        if self.keys_pressed.remove(&key_code) {
            self.keys_just_released.insert(key_code);
        }
    }

    /// Handle mouse button pressed
    pub fn on_mouse_button_pressed(&mut self, button: MouseButton) {
        if self.mouse_buttons_pressed.insert(button) {
            self.mouse_buttons_just_pressed.insert(button);
        }
    }

    /// Handle mouse button released
    pub fn on_mouse_button_released(&mut self, button: MouseButton) {
        if self.mouse_buttons_pressed.remove(&button) {
            self.mouse_buttons_just_released.insert(button);
        }
    }

    /// Handle mouse cursor moved
    pub fn on_cursor_moved(&mut self, x: f32, y: f32) {
        // Calculate delta from current position (which is last frame's position)
        self.mouse_delta = (x - self.mouse_position.0, y - self.mouse_position.1);
        self.mouse_position = (x, y);
    }

    /// Handle mouse wheel/scroll
    pub fn on_mouse_wheel(&mut self, delta_x: f32, delta_y: f32) {
        self.mouse_scroll_delta = (delta_x, delta_y);
    }

    // Query functions
    
    /// Check if a key is currently pressed
    pub fn is_key_pressed(&self, key_code: KeyCode) -> bool {
        self.keys_pressed.contains(&key_code)
    }

    /// Check if a key was just pressed this frame
    pub fn is_key_just_pressed(&self, key_code: KeyCode) -> bool {
        self.keys_just_pressed.contains(&key_code)
    }

    /// Check if a key was just released this frame
    pub fn is_key_just_released(&self, key_code: KeyCode) -> bool {
        self.keys_just_released.contains(&key_code)
    }

    /// Get mouse position
    pub fn mouse_position(&self) -> (f32, f32) {
        self.mouse_position
    }

    /// Get mouse delta (change in position this frame)
    pub fn mouse_delta(&self) -> (f32, f32) {
        self.mouse_delta
    }

    /// Check if a mouse button is currently pressed
    pub fn is_mouse_button_pressed(&self, button: MouseButton) -> bool {
        self.mouse_buttons_pressed.contains(&button)
    }

    /// Check if a mouse button was just pressed this frame
    pub fn is_mouse_button_just_pressed(&self, button: MouseButton) -> bool {
        self.mouse_buttons_just_pressed.contains(&button)
    }

    /// Get mouse scroll delta for this frame
    pub fn mouse_scroll(&self) -> (f32, f32) {
        self.mouse_scroll_delta
    }
}

/// Convert winit KeyCode to a simple u32 for FFI
/// This uses the scancode as a simple identifier
pub fn key_code_to_u32(key_code: KeyCode) -> u32 {
    // Use the key's discriminant as a simple identifier
    // This is a simplified approach - in production you might want a more robust mapping
    match key_code {
        KeyCode::ArrowUp => 1,
        KeyCode::ArrowDown => 2,
        KeyCode::ArrowLeft => 3,
        KeyCode::ArrowRight => 4,
        KeyCode::Space => 5,
        KeyCode::Enter => 6,
        KeyCode::Escape => 7,
        KeyCode::KeyW => 8,
        KeyCode::KeyA => 9,
        KeyCode::KeyS => 10,
        KeyCode::KeyD => 11,
        _ => 0, // Unknown key
    }
}

/// Convert u32 back to KeyCode for FFI queries
pub fn u32_to_key_code(code: u32) -> Option<KeyCode> {
    match code {
        1 => Some(KeyCode::ArrowUp),
        2 => Some(KeyCode::ArrowDown),
        3 => Some(KeyCode::ArrowLeft),
        4 => Some(KeyCode::ArrowRight),
        5 => Some(KeyCode::Space),
        6 => Some(KeyCode::Enter),
        7 => Some(KeyCode::Escape),
        8 => Some(KeyCode::KeyW),
        9 => Some(KeyCode::KeyA),
        10 => Some(KeyCode::KeyS),
        11 => Some(KeyCode::KeyD),
        _ => None,
    }
}