use crate::*;
const DPAD4_THRESHOLD: i32 = 300;
const DPAD8_THRESHOLD: i32 = 400;
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
pub struct Pad {
pub x: i32,
pub y: i32,
}
impl Pad {
pub const MAX: Pad = Pad { x: 1000, y: 1000 };
pub const MIN: Pad = Pad { x: -1000, y: -1000 };
#[must_use]
pub fn as_dpad4(&self) -> DPad4 {
let x = self.x;
let y = self.y;
if y > DPAD4_THRESHOLD && y > x.abs() {
DPad4::Up
} else if y < -DPAD4_THRESHOLD && -y > x.abs() {
DPad4::Down
} else if x > DPAD4_THRESHOLD && x > y.abs() {
DPad4::Right
} else if x < -DPAD4_THRESHOLD && -x > y.abs() {
DPad4::Left
} else {
DPad4::None
}
}
#[must_use]
pub fn as_dpad8(&self) -> DPad8 {
DPad8 {
left: self.x <= -DPAD8_THRESHOLD,
right: self.x >= DPAD8_THRESHOLD,
down: self.y <= -DPAD8_THRESHOLD,
up: self.y >= DPAD8_THRESHOLD,
}
}
#[must_use]
pub fn radius(self) -> f32 {
let r = self.x * self.x + self.y * self.y;
#[expect(clippy::cast_precision_loss)]
math::sqrt(r as f32)
}
#[must_use]
pub fn azimuth(self) -> Angle {
if self.x == 0 && self.y == 0 {
return Angle::ZERO;
}
#[expect(clippy::cast_precision_loss)]
let r = math::atan2(self.y as f32, self.x as f32);
Angle::from_radians(r)
}
}
impl From<Pad> for Point {
fn from(value: Pad) -> Self {
Self {
x: value.x,
y: value.y,
}
}
}
impl From<Point> for Pad {
fn from(value: Point) -> Self {
Self {
x: value.x,
y: value.y,
}
}
}
impl From<Pad> for Size {
fn from(value: Pad) -> Self {
Self {
width: value.x,
height: value.y,
}
}
}
impl From<Size> for Pad {
fn from(value: Size) -> Self {
Self {
x: value.width,
y: value.height,
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
pub struct DPad8 {
pub left: bool,
pub right: bool,
pub up: bool,
pub down: bool,
}
impl DPad8 {
#[must_use]
pub fn any(&self) -> bool {
self.left || self.right || self.up || self.down
}
#[must_use]
pub fn just_pressed(&self, old: &Self) -> Self {
Self {
left: self.left && !old.left,
right: self.right && !old.right,
up: self.up && !old.up,
down: self.down && !old.down,
}
}
#[must_use]
pub fn just_released(&self, old: &Self) -> Self {
Self {
left: !self.left && old.left,
right: !self.right && old.right,
up: !self.up && old.up,
down: !self.down && old.down,
}
}
#[must_use]
pub fn held(&self, old: &Self) -> Self {
Self {
left: self.left && old.left,
right: self.right && old.right,
up: self.up && old.up,
down: self.down && old.down,
}
}
}
impl From<Pad> for DPad8 {
fn from(value: Pad) -> Self {
value.as_dpad8()
}
}
impl From<Option<DPad8>> for DPad8 {
fn from(value: Option<DPad8>) -> Self {
value.unwrap_or_default()
}
}
impl From<DPad4> for DPad8 {
fn from(value: DPad4) -> Self {
let mut pad = Self::default();
match value {
DPad4::None => {}
DPad4::Left => pad.left = true,
DPad4::Right => pad.right = true,
DPad4::Up => pad.up = true,
DPad4::Down => pad.down = true,
}
pad
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
pub enum DPad4 {
#[default]
None,
Left,
Right,
Up,
Down,
}
impl DPad4 {
#[must_use]
pub fn any(self) -> bool {
self != Self::None
}
#[must_use]
pub fn just_pressed(self, old: Self) -> Self {
if self == old { Self::None } else { self }
}
#[must_use]
pub fn just_released(self, old: Self) -> Self {
if self == old { Self::None } else { old }
}
#[must_use]
pub fn held(self, old: Self) -> Self {
if self == old { self } else { Self::None }
}
}
impl From<Pad> for DPad4 {
fn from(value: Pad) -> Self {
value.as_dpad4()
}
}
impl From<Option<DPad4>> for DPad4 {
fn from(value: Option<DPad4>) -> Self {
value.unwrap_or_default()
}
}
#[derive(Default, Clone, Copy)]
pub struct Buttons {
pub s: bool,
pub e: bool,
pub w: bool,
pub n: bool,
pub menu: bool,
}
impl Buttons {
#[must_use]
pub fn any(&self) -> bool {
self.s || self.e || self.w || self.n || self.menu
}
#[must_use]
pub fn just_pressed(&self, old: &Self) -> Self {
Self {
s: self.s && !old.s,
e: self.e && !old.e,
w: self.w && !old.w,
n: self.n && !old.n,
menu: self.menu && !old.menu,
}
}
#[must_use]
pub fn just_released(&self, old: &Self) -> Self {
Self {
s: !self.s && old.s,
e: !self.e && old.e,
w: !self.w && old.w,
n: !self.n && old.n,
menu: !self.menu && old.menu,
}
}
#[must_use]
pub fn held(&self, old: &Self) -> Self {
Self {
s: self.s && old.s,
e: self.e && old.e,
w: self.w && old.w,
n: self.n && old.n,
menu: self.menu && old.menu,
}
}
}
#[must_use]
pub fn read_pad(p: Peer) -> Option<Pad> {
let p = u32::from(p.0);
let raw = unsafe { bindings::read_pad(p) };
if raw == 0xffff {
None
} else {
Some(Pad {
x: i32::from((raw >> 16) as i16),
y: i32::from(raw as i16),
})
}
}
#[must_use]
pub fn read_buttons(p: Peer) -> Buttons {
let p = u32::from(p.0);
let raw = unsafe { bindings::read_buttons(p) };
Buttons {
s: has_bit_set(raw, 0),
e: has_bit_set(raw, 1),
w: has_bit_set(raw, 2),
n: has_bit_set(raw, 3),
menu: has_bit_set(raw, 4),
}
}
#[inline]
fn has_bit_set(val: u32, bit: usize) -> bool {
(val >> bit) & 0b1 != 0
}
mod bindings {
#[link(wasm_import_module = "input")]
unsafe extern "C" {
pub(crate) fn read_pad(peer: u32) -> u32;
pub(crate) fn read_buttons(peer: u32) -> u32;
}
}