qwac 0.29.0

Rust client crate for making qwac games
Documentation
use euclid::Vector2D;
use qwac_sys::input::{gamepad_axis, gamepad_buttons};
pub use qwac_sys::input::{GamepadAxis, GamepadButton};

/// Get and check the state of all buttons on the gamepad.
#[derive(Clone, Copy, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct GamepadButtons(u64);

/// Get the bit for the given button and player.
const fn button_bit(player: u8, button: GamepadButton) -> u64 {
    1u64 << player * 16 + button as u8
}

impl GamepadButtons {
    pub fn poll() -> Self {
        Self(unsafe { gamepad_buttons() } as u64)
    }

    pub fn pressed(&self, player: u8, button: GamepadButton) -> bool {
        (self.0 & button_bit(player, button)) != 0
    }
}

/// Tracks button state from frame to frame.
#[derive(Clone, Copy, Debug)]
pub struct ButtonTracker {
    previous_frame: GamepadButtons,
    current_frame: GamepadButtons,
}

impl Default for ButtonTracker {
    fn default() -> Self {
        Self {
            previous_frame: GamepadButtons(0),
            current_frame: GamepadButtons(0),
        }
    }
}

impl ButtonTracker {
    pub fn new() -> Self {
        Default::default()
    }

    /// Set the current frame explicitly.
    pub fn frame(&mut self, frame: GamepadButtons) {
        self.previous_frame = std::mem::replace(&mut self.current_frame, frame);
    }

    /// Set the current frame from the current button state
    pub fn poll(&mut self) {
        self.frame(GamepadButtons::poll());
    }

    /// Get the state of the previous and current frame, in that order.
    pub fn state(&self, player: u8, button: GamepadButton) -> (bool, bool) {
        (
            self.previous_frame.pressed(player, button),
            self.current_frame.pressed(player, button),
        )
    }

    pub fn pressed(&self, player: u8, button: GamepadButton) -> bool {
        self.current_frame.pressed(player, button)
    }

    pub fn just_pressed(&self, player: u8, button: GamepadButton) -> bool {
        let (previous, current) = self.state(player, button);
        !previous && current
    }

    pub fn just_released(&self, player: u8, button: GamepadButton) -> bool {
        let (previous, current) = self.state(player, button);
        previous && !current
    }

    pub fn held(&self, player: u8, button: GamepadButton) -> bool {
        let (previous, current) = self.state(player, button);
        previous && current
    }
}

/// Tilt amount unit.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Tilt;

#[derive(Clone, Copy, Debug)]
pub struct GamepadSticks {
    left: Vector2D<f32, Tilt>,
    right: Vector2D<f32, Tilt>,
}

fn with_dead_zone(
    stick: Vector2D<f32, Tilt>,
    dead_zone: f32,
    square_dead_zone: f32,
) -> Vector2D<f32, Tilt> {
    let square_length = stick.square_length();
    if square_length == 0.0 || square_length < square_dead_zone {
        Vector2D::zero()
    } else {
        let length = square_length.sqrt();
        // inverse lerp of the magnitude.
        let t = (length - dead_zone) / (1.0 - dead_zone);
        let new_length = t.clamp(0.0, 1.0);
        let normalized = stick / length;
        normalized * new_length
    }
}

impl GamepadSticks {
    pub fn poll(player: u8) -> Self {
        let player = player as i32;
        let left_up = unsafe { gamepad_axis(player, GamepadAxis::LeftUp as i32) };
        let left_down = unsafe { gamepad_axis(player, GamepadAxis::LeftDown as i32) };
        let left_left = unsafe { gamepad_axis(player, GamepadAxis::LeftLeft as i32) };
        let left_right = unsafe { gamepad_axis(player, GamepadAxis::LeftRight as i32) };
        let right_up = unsafe { gamepad_axis(player, GamepadAxis::RightUp as i32) };
        let right_down = unsafe { gamepad_axis(player, GamepadAxis::RightDown as i32) };
        let right_left = unsafe { gamepad_axis(player, GamepadAxis::RightLeft as i32) };
        let right_right = unsafe { gamepad_axis(player, GamepadAxis::RightRight as i32) };
        Self {
            left: Vector2D::new(left_right - left_left, left_up - left_down),
            right: Vector2D::new(right_right - right_left, right_up - right_down),
        }
    }

    pub fn apply_dead_zone(&self, dead_zone: f32) -> Self {
        if dead_zone == 0.0 {
            self.clone()
        } else {
            let square_dead_zone = dead_zone.powi(2);
            Self {
                left: with_dead_zone(self.left, dead_zone, square_dead_zone),
                right: with_dead_zone(self.right, dead_zone, square_dead_zone),
            }
        }
    }

    pub fn left(&self) -> Vector2D<f32, Tilt> {
        self.left
    }

    pub fn right(&self) -> Vector2D<f32, Tilt> {
        self.right
    }
}