use crate::input::backends::{ControllerType, RawInput};
use crate::input::controller::{GameCubeButtons, GameCubeInput};
#[derive(Debug, Clone)]
pub struct GameCubeMapping {
pub controller_type: ControllerType,
pub button_mappings: ButtonMappings,
pub stick_mappings: StickMappings,
pub trigger_mappings: TriggerMappings,
pub dead_zones: DeadZones,
pub sensitivity: Sensitivity,
}
#[derive(Debug, Clone)]
pub struct ButtonMappings {
pub a: ButtonMapping,
pub b: ButtonMapping,
pub x: ButtonMapping,
pub y: ButtonMapping,
pub start: ButtonMapping,
pub d_up: ButtonMapping,
pub d_down: ButtonMapping,
pub d_left: ButtonMapping,
pub d_right: ButtonMapping,
pub l: ButtonMapping,
pub r: ButtonMapping,
pub z: ButtonMapping,
}
#[derive(Debug, Clone)]
pub enum ButtonMapping {
Button(usize),
AxisPositive(usize),
AxisNegative(usize),
Trigger(usize, f32), None,
}
#[derive(Debug, Clone)]
pub struct StickMappings {
pub left_stick: AxisMapping,
pub right_stick: AxisMapping,
}
#[derive(Debug, Clone)]
pub struct AxisMapping {
pub x_axis: usize,
pub y_axis: usize,
pub invert_x: bool,
pub invert_y: bool,
}
#[derive(Debug, Clone)]
pub struct TriggerMappings {
pub left_trigger: usize,
pub right_trigger: usize,
}
#[derive(Debug, Clone)]
pub struct DeadZones {
pub left_stick: f32,
pub right_stick: f32,
pub left_trigger: f32,
pub right_trigger: f32,
}
#[derive(Debug, Clone)]
pub struct Sensitivity {
pub left_stick: f32,
pub right_stick: f32,
}
impl GameCubeMapping {
pub fn default_for_controller(
controller_info: &crate::input::backends::ControllerInfo,
) -> Result<Self> {
match controller_info.controller_type {
ControllerType::Xbox => Ok(Self::xbox_default()),
ControllerType::PlayStation => Ok(Self::playstation_default()),
ControllerType::SwitchPro => Ok(Self::switch_pro_default()),
_ => Ok(Self::generic_default()),
}
}
pub fn xbox_default() -> Self {
Self {
controller_type: ControllerType::Xbox,
button_mappings: ButtonMappings {
a: ButtonMapping::Button(0), b: ButtonMapping::Button(1), x: ButtonMapping::Button(2), y: ButtonMapping::Button(3), start: ButtonMapping::Button(6), d_up: ButtonMapping::Button(11), d_down: ButtonMapping::Button(12), d_left: ButtonMapping::Button(13), d_right: ButtonMapping::Button(14), l: ButtonMapping::Trigger(4, 0.3), r: ButtonMapping::Trigger(5, 0.3), z: ButtonMapping::Button(4), },
stick_mappings: StickMappings {
left_stick: AxisMapping {
x_axis: 0,
y_axis: 1,
invert_x: false,
invert_y: true, },
right_stick: AxisMapping {
x_axis: 2,
y_axis: 3,
invert_x: false,
invert_y: true,
},
},
trigger_mappings: TriggerMappings {
left_trigger: 4,
right_trigger: 5,
},
dead_zones: DeadZones {
left_stick: 0.15,
right_stick: 0.15,
left_trigger: 0.1,
right_trigger: 0.1,
},
sensitivity: Sensitivity {
left_stick: 1.0,
right_stick: 1.0,
},
}
}
pub fn playstation_default() -> Self {
let mut xbox = Self::xbox_default();
xbox.controller_type = ControllerType::PlayStation;
xbox.button_mappings.start = ButtonMapping::Button(9); xbox
}
pub fn switch_pro_default() -> Self {
let mut xbox = Self::xbox_default();
xbox.controller_type = ControllerType::SwitchPro;
xbox
}
pub fn generic_default() -> Self {
Self::xbox_default()
}
pub fn map_to_gamecube(&self, input: &RawInput) -> GameCubeInput {
let mut buttons = GameCubeButtons::default();
buttons.a = self.get_button_state(&self.button_mappings.a, input);
buttons.b = self.get_button_state(&self.button_mappings.b, input);
buttons.x = self.get_button_state(&self.button_mappings.x, input);
buttons.y = self.get_button_state(&self.button_mappings.y, input);
buttons.start = self.get_button_state(&self.button_mappings.start, input);
buttons.d_up = self.get_button_state(&self.button_mappings.d_up, input);
buttons.d_down = self.get_button_state(&self.button_mappings.d_down, input);
buttons.d_left = self.get_button_state(&self.button_mappings.d_left, input);
buttons.d_right = self.get_button_state(&self.button_mappings.d_right, input);
buttons.l = self.get_button_state(&self.button_mappings.l, input);
buttons.r = self.get_button_state(&self.button_mappings.r, input);
buttons.z = self.get_button_state(&self.button_mappings.z, input);
let left_stick = self.map_stick(
&self.stick_mappings.left_stick,
&self.dead_zones.left_stick,
self.sensitivity.left_stick,
input,
);
let right_stick = self.map_stick(
&self.stick_mappings.right_stick,
&self.dead_zones.right_stick,
self.sensitivity.right_stick,
input,
);
let left_trigger = self.map_trigger(
self.trigger_mappings.left_trigger,
self.dead_zones.left_trigger,
input,
);
let right_trigger = self.map_trigger(
self.trigger_mappings.right_trigger,
self.dead_zones.right_trigger,
input,
);
GameCubeInput {
buttons,
left_stick,
right_stick,
left_trigger,
right_trigger,
}
}
fn get_button_state(&self, mapping: &ButtonMapping, input: &RawInput) -> bool {
match mapping {
ButtonMapping::Button(idx) => input.buttons.get(*idx).copied().unwrap_or(false),
ButtonMapping::AxisPositive(idx) => {
input.axes.get(*idx).map(|&v| v > 0.5).unwrap_or(false)
}
ButtonMapping::AxisNegative(idx) => {
input.axes.get(*idx).map(|&v| v < -0.5).unwrap_or(false)
}
ButtonMapping::Trigger(idx, threshold) => input
.triggers
.get(*idx)
.map(|&v| v > *threshold)
.unwrap_or(false),
ButtonMapping::None => false,
}
}
fn map_stick(
&self,
axis_mapping: &AxisMapping,
dead_zone: &f32,
sensitivity: f32,
input: &RawInput,
) -> (f32, f32) {
let x = input.axes.get(axis_mapping.x_axis).copied().unwrap_or(0.0);
let y = input.axes.get(axis_mapping.y_axis).copied().unwrap_or(0.0);
let mut x = if axis_mapping.invert_x { -x } else { x };
let mut y = if axis_mapping.invert_y { -y } else { y };
let magnitude = (x * x + y * y).sqrt();
if magnitude < *dead_zone {
return (0.0, 0.0);
}
if magnitude > 1.0 {
x /= magnitude;
y /= magnitude;
}
x *= sensitivity;
y *= sensitivity;
(x, y)
}
fn map_trigger(&self, trigger_idx: usize, dead_zone: f32, input: &RawInput) -> f32 {
let value = input.triggers.get(trigger_idx).copied().unwrap_or(0.0);
if value < dead_zone {
0.0
} else {
(value - dead_zone) / (1.0 - dead_zone)
}
}
}
use anyhow::Result;