use euclid::Vector2D;
use qwac_sys::input::{gamepad_axis, gamepad_buttons};
pub use qwac_sys::input::{GamepadAxis, GamepadButton};
#[derive(Clone, Copy, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct GamepadButtons(u64);
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
}
}
#[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()
}
pub fn frame(&mut self, frame: GamepadButtons) {
self.previous_frame = std::mem::replace(&mut self.current_frame, frame);
}
pub fn poll(&mut self) {
self.frame(GamepadButtons::poll());
}
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
}
}
#[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();
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
}
}