use std::borrow::Cow;
use game_toolkit::prelude::*;
use crate::sim::{Intent, Preset};
const DEADZONE: f32 = 0.25;
#[derive(Clone, Debug)]
pub struct Scheme {
pub jump: &'static [Key],
pub jump_pad: &'static [Button],
pub dash: &'static [Key],
pub dash_pad: &'static [Button],
pub grab: &'static [Key],
pub grab_pad: &'static [Button],
pub cast: &'static [Key],
pub cast_pad: &'static [Button],
pub sprint: &'static [Key],
pub sprint_pad: &'static [Button],
pub super_dash: &'static [Key],
pub super_dash_pad: &'static [Button],
pub custom: Cow<'static, [Custom]>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Custom {
pub bit: u8,
pub keys: Cow<'static, [Key]>,
pub pad: Cow<'static, [Button]>,
}
impl Custom {
pub const fn new(bit: u8, keys: &'static [Key], pad: &'static [Button]) -> Self {
Self {
bit,
keys: Cow::Borrowed(keys),
pad: Cow::Borrowed(pad),
}
}
}
impl Scheme {
pub const SNAPPY: Scheme = Scheme {
jump: &[Key::KeyZ, Key::Space],
jump_pad: &[Button::South],
dash: &[Key::KeyC],
dash_pad: &[Button::RightTrigger2],
grab: &[Key::KeyD],
grab_pad: &[Button::LeftTrigger],
cast: &[Key::KeyA],
cast_pad: &[Button::East, Button::RightTrigger],
sprint: &[Key::ShiftLeft, Key::ShiftRight],
sprint_pad: &[Button::West],
super_dash: &[Key::KeyS],
super_dash_pad: &[Button::LeftTrigger2],
custom: Cow::Borrowed(&[]),
};
pub const PRECISE: Scheme = Scheme {
jump: &[Key::KeyC, Key::Space],
jump_pad: &[Button::South, Button::North],
dash: &[Key::KeyX],
dash_pad: &[Button::West, Button::East],
grab: &[Key::KeyZ],
grab_pad: &[
Button::RightTrigger2,
Button::LeftTrigger2,
Button::RightTrigger,
Button::LeftTrigger,
],
cast: &[],
cast_pad: &[],
sprint: &[],
sprint_pad: &[],
super_dash: &[],
super_dash_pad: &[],
custom: Cow::Borrowed(&[]),
};
pub const MOMENTUM: Scheme = Scheme {
jump: &[Key::Space, Key::KeyZ],
jump_pad: &[Button::South],
dash: &[],
dash_pad: &[],
grab: &[],
grab_pad: &[],
cast: &[],
cast_pad: &[],
sprint: &[Key::ShiftLeft, Key::ShiftRight, Key::KeyX],
sprint_pad: &[Button::West, Button::RightTrigger2],
super_dash: &[],
super_dash_pad: &[],
custom: Cow::Borrowed(&[]),
};
pub fn bind(
&mut self,
bit: u8,
keys: impl Into<Cow<'static, [Key]>>,
pad: impl Into<Cow<'static, [Button]>>,
) {
let button = Custom {
bit,
keys: keys.into(),
pad: pad.into(),
};
let custom = self.custom.to_mut();
match custom.iter_mut().find(|c| c.bit == bit) {
Some(c) => *c = button,
None => custom.push(button),
}
}
pub fn of(preset: Option<Preset>) -> Self {
match preset {
Some(Preset::Precise) => Self::PRECISE,
Some(Preset::Momentum) => Self::MOMENTUM,
Some(Preset::Hybrid | Preset::Snappy | Preset::Drifter) | None => Self::SNAPPY,
}
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct Triggers {
left: bool,
right: bool,
}
const TRIGGER: f32 = 0.5;
pub fn read_intent(input: &Input, scheme: &Scheme, triggers: &mut Triggers) -> Intent {
let pressed = |ks: &[Key]| ks.iter().any(|&k| input.key_pressed(k));
let held = |ks: &[Key]| ks.iter().any(|&k| input.key_held(k));
let axis =
|neg: Key, pos: Key| (input.key_held(pos) as i32 - input.key_held(neg) as i32) as f32;
let mut i = Intent {
move_x: axis(Key::ArrowLeft, Key::ArrowRight),
move_y: axis(Key::ArrowUp, Key::ArrowDown),
jump_pressed: pressed(scheme.jump),
jump_held: held(scheme.jump),
dash_pressed: pressed(scheme.dash),
grab: held(scheme.grab),
dive_pressed: false,
sprint: held(scheme.sprint),
super_dash: held(scheme.super_dash),
custom: 0,
custom_pressed: 0,
};
for c in scheme.custom.iter() {
let bit = 1 << c.bit;
if held(&c.keys) {
i.custom |= bit;
}
if pressed(&c.keys) {
i.custom_pressed |= bit;
}
}
let mut cast = pressed(scheme.cast);
if let Some(g) = input.first_gamepad() {
let lt = g.button_held(Button::LeftTrigger2) || g.axis(Axis::LeftZ) > TRIGGER;
let rt = g.button_held(Button::RightTrigger2) || g.axis(Axis::RightZ) > TRIGGER;
let (lt_pressed, rt_pressed) = (lt && !triggers.left, rt && !triggers.right);
*triggers = Triggers {
left: lt,
right: rt,
};
let held_b = |b: Button| match b {
Button::LeftTrigger2 => lt,
Button::RightTrigger2 => rt,
_ => g.button_held(b),
};
let pressed_b = |b: Button| match b {
Button::LeftTrigger2 => lt_pressed,
Button::RightTrigger2 => rt_pressed,
_ => g.button_pressed(b),
};
let pressed = |bs: &[Button]| bs.iter().any(|&b| pressed_b(b));
let held = |bs: &[Button]| bs.iter().any(|&b| held_b(b));
let dz = |v: f32| if v.abs() < DEADZONE { 0.0 } else { v };
let dpad = |neg, pos| (g.button_held(pos) as i32 - g.button_held(neg) as i32) as f32;
let sx = dz(g.axis(Axis::LeftStickX)) + dpad(Button::DPadLeft, Button::DPadRight);
let sy = -dz(g.axis(Axis::LeftStickY)) + dpad(Button::DPadUp, Button::DPadDown);
if sx != 0.0 {
i.move_x = sx.clamp(-1.0, 1.0);
}
if sy != 0.0 {
i.move_y = sy.clamp(-1.0, 1.0);
}
i.jump_pressed |= pressed(scheme.jump_pad);
i.jump_held |= held(scheme.jump_pad);
i.dash_pressed |= pressed(scheme.dash_pad);
i.grab |= held(scheme.grab_pad);
i.sprint |= held(scheme.sprint_pad);
i.super_dash |= held(scheme.super_dash_pad);
cast |= pressed(scheme.cast_pad);
for c in scheme.custom.iter() {
let bit = 1 << c.bit;
if held(&c.pad) {
i.custom |= bit;
}
if pressed(&c.pad) {
i.custom_pressed |= bit;
}
}
}
i.dive_pressed = cast && i.down();
i
}
#[derive(Clone, Debug)]
pub struct Controls {
pub scheme: Scheme,
triggers: Triggers,
}
impl Controls {
pub fn new(scheme: Scheme) -> Self {
Self {
scheme,
triggers: Triggers::default(),
}
}
pub fn for_preset(preset: Preset) -> Self {
Self::new(Scheme::of(Some(preset)))
}
pub fn read(&mut self, input: &Input) -> Intent {
read_intent(input, &self.scheme, &mut self.triggers)
}
}
impl Default for Controls {
fn default() -> Self {
Self::new(Scheme::SNAPPY)
}
}