doukutsu-rs/src/framework/gamepad.rs

480 lines
15 KiB
Rust

use std::collections::{HashMap, HashSet};
use serde::{Deserialize, Serialize};
use crate::framework::backend::BackendGamepad;
use crate::framework::error::GameResult;
use crate::game::shared_game_state::SharedGameState;
use crate::{common::Rect, engine_constants::EngineConstants, framework::context::Context};
const QUAKE_RUMBLE_LOW_FREQ: u16 = 0x3000;
const QUAKE_RUMBLE_HI_FREQ: u16 = 0;
const SUPER_QUAKE_RUMBLE_LOW_FREQ: u16 = 0x5000;
const SUPER_QUAKE_RUMBLE_HI_FREQ: u16 = 0;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum GamepadType {
Unknown,
Xbox360,
XboxOne,
PS3,
PS4,
NintendoSwitchPro,
Virtual,
PS5,
AmazonLuma,
GoogleStadia,
NVIDIAShield,
NintendoSwitchJoyConLeft,
NintendoSwitchJoyConRight,
NintendoSwitchJoyConPair,
}
impl GamepadType {
pub fn get_name(&self) -> &str {
match self {
GamepadType::Unknown => "Unknown controller",
GamepadType::Xbox360 => "Xbox 360 controller",
GamepadType::XboxOne => "Xbox One controller",
GamepadType::PS3 => "PlayStation 3 controller",
GamepadType::PS4 => "PlayStation 4 controller",
GamepadType::NintendoSwitchPro => "Nintendo Switch Pro controller",
GamepadType::Virtual => "Virtual controller",
GamepadType::PS5 => "PlayStation 5 controller",
GamepadType::AmazonLuma => "Amazon Luma controller",
GamepadType::GoogleStadia => "Google Stadia controller",
GamepadType::NVIDIAShield => "NVIDIA Shield controller",
GamepadType::NintendoSwitchJoyConLeft => "Nintendo Switch Joy-Con (left)",
GamepadType::NintendoSwitchJoyConRight => "Nintendo Switch Joy-Con (right)",
GamepadType::NintendoSwitchJoyConPair => "Nintendo Switch Joy-Con (pair)",
}
}
}
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
#[repr(u32)]
pub enum Axis {
LeftX,
LeftY,
RightX,
RightY,
TriggerLeft,
TriggerRight,
}
impl Axis {
pub fn get_rect(&self, offset: usize, constants: &EngineConstants) -> Rect<u16> {
constants.gamepad.axis_rects.get(self).unwrap()[offset]
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub enum AxisDirection {
None,
Either,
Up,
Left,
Right,
Down,
}
impl AxisDirection {
pub fn from_axis_data(axis: Axis, value: f64) -> Self {
match axis {
Axis::LeftX | Axis::RightX => {
if value < 0.0 {
AxisDirection::Left
} else {
AxisDirection::Right
}
}
Axis::LeftY | Axis::RightY => {
if value < 0.0 {
AxisDirection::Up
} else {
AxisDirection::Down
}
}
Axis::TriggerLeft | Axis::TriggerRight => AxisDirection::Either,
}
}
pub fn compare(&self, value: f64, axis_sensitivity: f64) -> bool {
match self {
AxisDirection::None => false,
AxisDirection::Either => value.abs() > 0.0,
AxisDirection::Down | AxisDirection::Right => value > axis_sensitivity,
AxisDirection::Up | AxisDirection::Left => value < -axis_sensitivity,
}
}
}
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
#[repr(u32)]
pub enum Button {
South,
East,
West,
North,
Back,
Guide,
Start,
LeftStick,
RightStick,
LeftShoulder,
RightShoulder,
DPadUp,
DPadDown,
DPadLeft,
DPadRight,
}
impl Button {
pub fn get_rect(&self, offset: usize, constants: &EngineConstants) -> Rect<u16> {
match self {
Button::Guide => Rect::new(0, 0, 0, 0),
_ => constants.gamepad.button_rects.get(self).unwrap()[offset],
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub enum PlayerControllerInputType {
ButtonInput(Button),
AxisInput(Axis, AxisDirection),
Either(Button, Axis, AxisDirection),
}
impl PlayerControllerInputType {
pub fn get_rect(&self, offset: usize, constants: &EngineConstants) -> Rect<u16> {
match self {
PlayerControllerInputType::ButtonInput(button) => button.get_rect(offset, constants),
PlayerControllerInputType::AxisInput(axis, _) => axis.get_rect(offset, constants),
PlayerControllerInputType::Either(button, axis, _) => button.get_rect(offset, constants),
}
}
}
pub struct GamepadData {
controller: Box<dyn BackendGamepad>,
controller_type: GamepadType,
left_x: f64,
left_y: f64,
right_x: f64,
right_y: f64,
trigger_left: f64,
trigger_right: f64,
axis_sensitivity: f64,
pressed_buttons_set: HashSet<Button>,
axis_values: HashMap<Axis, f64>,
}
impl GamepadData {
pub(crate) fn new(game_controller: Box<dyn BackendGamepad>, axis_sensitivity: f64) -> Self {
GamepadData {
controller: game_controller,
controller_type: GamepadType::Unknown,
left_x: 0.0,
left_y: 0.0,
right_x: 0.0,
right_y: 0.0,
trigger_left: 0.0,
trigger_right: 0.0,
axis_sensitivity,
pressed_buttons_set: HashSet::with_capacity(16),
axis_values: HashMap::with_capacity(8),
}
}
pub(crate) fn set_gamepad_type(&mut self, controller_type: GamepadType) {
self.controller_type = controller_type;
}
pub(crate) fn get_gamepad_sprite_offset(&self) -> usize {
match self.controller_type {
GamepadType::PS3 | GamepadType::PS4 | GamepadType::PS5 => 0,
GamepadType::Xbox360 | GamepadType::XboxOne => 1,
GamepadType::NintendoSwitchPro
| GamepadType::NintendoSwitchJoyConLeft
| GamepadType::NintendoSwitchJoyConRight
| GamepadType::NintendoSwitchJoyConPair => 3,
_ => 1,
}
}
pub fn get_gamepad_name(&self) -> String {
self.controller_type.get_name().to_owned()
}
pub fn set_rumble(&mut self, state: &SharedGameState, low_freq: u16, hi_freq: u16, ticks: u32) -> GameResult {
let duration_ms = (ticks as f32 / state.settings.timing_mode.get_tps() as f32 * 1000.0) as u32;
self.controller.set_rumble(low_freq, hi_freq, duration_ms)
}
}
pub struct GamepadContext {
gamepads: Vec<GamepadData>,
}
impl GamepadContext {
pub(crate) fn new() -> Self {
Self { gamepads: Vec::new() }
}
fn get_gamepad(&self, gamepad_id: u32) -> Option<&GamepadData> {
self.gamepads.iter().find(|gamepad| gamepad.controller.instance_id() == gamepad_id)
}
fn get_gamepad_by_index(&self, gamepad_index: usize) -> Option<&GamepadData> {
self.gamepads.get(gamepad_index)
}
fn get_gamepad_mut(&mut self, gamepad_id: u32) -> Option<&mut GamepadData> {
self.gamepads.iter_mut().find(|gamepad| gamepad.controller.instance_id() == gamepad_id)
}
fn get_gamepad_by_index_mut(&mut self, gamepad_index: usize) -> Option<&mut GamepadData> {
self.gamepads.get_mut(gamepad_index)
}
pub(crate) fn add_gamepad(&mut self, game_controller: Box<dyn BackendGamepad>, axis_sensitivity: f64) {
self.gamepads.push(GamepadData::new(game_controller, axis_sensitivity));
}
pub(crate) fn remove_gamepad(&mut self, gamepad_id: u32) {
self.gamepads.retain(|data| data.controller.instance_id() != gamepad_id);
}
pub(crate) fn set_gamepad_type(&mut self, gamepad_id: u32, controller_type: GamepadType) {
if let Some(gamepad) = self.get_gamepad_mut(gamepad_id) {
gamepad.set_gamepad_type(controller_type);
}
}
pub(crate) fn get_gamepad_sprite_offset(&self, gamepad_index: usize) -> usize {
if let Some(gamepad) = self.get_gamepad_by_index(gamepad_index) {
return gamepad.get_gamepad_sprite_offset();
}
1
}
pub(crate) fn set_button(&mut self, gamepad_id: u32, button: Button, pressed: bool) {
if let Some(gamepad) = self.get_gamepad_mut(gamepad_id) {
if pressed {
gamepad.pressed_buttons_set.insert(button);
} else {
gamepad.pressed_buttons_set.remove(&button);
}
}
}
pub(crate) fn set_axis_value(&mut self, gamepad_id: u32, axis: Axis, value: f64) {
if let Some(gamepad) = self.get_gamepad_mut(gamepad_id) {
gamepad.axis_values.insert(axis, value);
}
}
pub(crate) fn is_active(&self, gamepad_index: u32, input_type: &PlayerControllerInputType) -> bool {
match input_type {
PlayerControllerInputType::ButtonInput(button) => self.is_button_active(gamepad_index, *button),
PlayerControllerInputType::AxisInput(axis, axis_direction) => {
self.is_axis_active(gamepad_index, *axis, *axis_direction)
}
PlayerControllerInputType::Either(button, axis, axis_direction) => {
self.is_button_active(gamepad_index, *button)
|| self.is_axis_active(gamepad_index, *axis, *axis_direction)
}
}
}
pub(crate) fn is_button_active(&self, gamepad_index: u32, button: Button) -> bool {
if let Some(gamepad) = self.get_gamepad_by_index(gamepad_index as usize) {
return gamepad.pressed_buttons_set.contains(&button);
}
false
}
pub(crate) fn is_axis_active(&self, gamepad_index: u32, axis: Axis, direction: AxisDirection) -> bool {
if let Some(gamepad) = self.get_gamepad_by_index(gamepad_index as usize) {
return match axis {
Axis::LeftX => direction.compare(gamepad.left_x, gamepad.axis_sensitivity),
Axis::LeftY => direction.compare(gamepad.left_y, gamepad.axis_sensitivity),
Axis::RightX => direction.compare(gamepad.right_x, gamepad.axis_sensitivity),
Axis::RightY => direction.compare(gamepad.right_y, gamepad.axis_sensitivity),
Axis::TriggerLeft => direction.compare(gamepad.trigger_left, 0.0),
Axis::TriggerRight => direction.compare(gamepad.trigger_right, 0.0),
};
}
false
}
pub(crate) fn update_axes(&mut self, gamepad_id: u32) {
if let Some(gamepad) = self.get_gamepad_mut(gamepad_id) {
let mut axes = [
(&mut gamepad.left_x, Axis::LeftX),
(&mut gamepad.left_y, Axis::LeftY),
(&mut gamepad.right_x, Axis::RightX),
(&mut gamepad.right_y, Axis::RightY),
(&mut gamepad.trigger_left, Axis::TriggerLeft),
(&mut gamepad.trigger_right, Axis::TriggerRight),
];
for (axis_val, id) in axes.iter_mut() {
if let Some(axis) = gamepad.axis_values.get(id) {
**axis_val = if axis.abs() < 0.12 { 0.0 } else { *axis };
}
}
}
}
pub(crate) fn get_gamepads(&self) -> &Vec<GamepadData> {
&self.gamepads
}
pub(crate) fn pressed_buttons(&self, gamepad_index: u32) -> HashSet<Button> {
if let Some(gamepad) = self.get_gamepad_by_index(gamepad_index as usize) {
return gamepad.pressed_buttons_set.clone();
}
HashSet::new()
}
pub(crate) fn active_axes(&self, gamepad_index: u32) -> HashMap<Axis, f64> {
if let Some(gamepad) = self.get_gamepad_by_index(gamepad_index as usize) {
let mut active_axes = gamepad.axis_values.clone();
active_axes.retain(|_, v| v.abs() > gamepad.axis_sensitivity);
return active_axes;
}
HashMap::new()
}
pub(crate) fn set_rumble(
&mut self,
gamepad_index: u32,
state: &SharedGameState,
low_freq: u16,
hi_freq: u16,
ticks: u32,
) -> GameResult {
if let Some(gamepad) = self.get_gamepad_by_index_mut(gamepad_index as usize) {
gamepad.set_rumble(state, low_freq, hi_freq, ticks)?;
}
Ok(())
}
pub(crate) fn set_rumble_all(
&mut self,
state: &SharedGameState,
low_freq: u16,
hi_freq: u16,
ticks: u32,
) -> GameResult {
for gamepad in self.gamepads.iter_mut() {
gamepad.set_rumble(state, low_freq, hi_freq, ticks)?;
}
Ok(())
}
}
impl Default for GamepadContext {
fn default() -> Self {
Self::new()
}
}
pub fn add_gamepad(context: &mut Context, game_controller: Box<dyn BackendGamepad>, axis_sensitivity: f64) {
context.gamepad_context.add_gamepad(game_controller, axis_sensitivity);
}
pub fn remove_gamepad(context: &mut Context, gamepad_id: u32) {
context.gamepad_context.remove_gamepad(gamepad_id);
}
pub fn set_gamepad_type(context: &mut Context, gamepad_id: u32, controller_type: GamepadType) {
context.gamepad_context.set_gamepad_type(gamepad_id, controller_type);
}
pub fn get_gamepad_sprite_offset(context: &Context, gamepad_index: usize) -> usize {
context.gamepad_context.get_gamepad_sprite_offset(gamepad_index)
}
pub fn is_active(ctx: &Context, gamepad_index: u32, input_type: &PlayerControllerInputType) -> bool {
ctx.gamepad_context.is_active(gamepad_index, input_type)
}
pub fn is_button_active(ctx: &Context, gamepad_index: u32, button: Button) -> bool {
ctx.gamepad_context.is_button_active(gamepad_index, button)
}
pub fn is_axis_active(ctx: &Context, gamepad_index: u32, axis: Axis, direction: AxisDirection) -> bool {
ctx.gamepad_context.is_axis_active(gamepad_index, axis, direction)
}
pub fn get_gamepads(ctx: &Context) -> &Vec<GamepadData> {
ctx.gamepad_context.get_gamepads()
}
pub fn pressed_buttons(ctx: &Context, gamepad_index: u32) -> HashSet<Button> {
ctx.gamepad_context.pressed_buttons(gamepad_index)
}
pub fn active_axes(ctx: &Context, gamepad_index: u32) -> HashMap<Axis, f64> {
ctx.gamepad_context.active_axes(gamepad_index)
}
pub fn set_rumble(
ctx: &mut Context,
state: &SharedGameState,
gamepad_index: u32,
low_freq: u16,
hi_freq: u16,
ticks: u32,
) -> GameResult {
ctx.gamepad_context.set_rumble(gamepad_index, state, low_freq, hi_freq, ticks)
}
pub fn set_rumble_all(
ctx: &mut Context,
state: &SharedGameState,
low_freq: u16,
hi_freq: u16,
ticks: u32,
) -> GameResult {
ctx.gamepad_context.set_rumble_all(state, low_freq, hi_freq, ticks)
}
pub fn set_quake_rumble(ctx: &mut Context, state: &SharedGameState, gamepad_index: u32, ticks: u32) -> GameResult {
set_rumble(ctx, state, gamepad_index, QUAKE_RUMBLE_LOW_FREQ, QUAKE_RUMBLE_HI_FREQ, ticks)
}
pub fn set_quake_rumble_all(ctx: &mut Context, state: &SharedGameState, ticks: u32) -> GameResult {
set_rumble_all(ctx, state, QUAKE_RUMBLE_LOW_FREQ, QUAKE_RUMBLE_LOW_FREQ, ticks)
}
pub fn set_super_quake_rumble(
ctx: &mut Context,
state: &SharedGameState,
gamepad_index: u32,
ticks: u32,
) -> GameResult {
set_rumble(ctx, state, gamepad_index, SUPER_QUAKE_RUMBLE_LOW_FREQ, SUPER_QUAKE_RUMBLE_HI_FREQ, ticks)
}
pub fn set_super_quake_rumble_all(ctx: &mut Context, state: &SharedGameState, ticks: u32) -> GameResult {
set_rumble_all(ctx, state, SUPER_QUAKE_RUMBLE_LOW_FREQ, SUPER_QUAKE_RUMBLE_LOW_FREQ, ticks)
}