very initial gamepad support
This commit is contained in:
parent
6d8e58090a
commit
9932b1209f
|
@ -59,6 +59,7 @@ cpal = "0.13"
|
|||
directories = "3"
|
||||
downcast = "0.11"
|
||||
funty = "=1.1.0" # https://github.com/bitvecto-rs/bitvec/issues/105
|
||||
gilrs = { version = "0.9.0", features = ["serde-serialize"] }
|
||||
glutin = { git = "https://github.com/doukutsu-rs/glutin.git", rev = "8dd457b9adb7dbac7ade337246b6356c784272d9", optional = true, default_features = false, features = ["x11"] }
|
||||
imgui = "0.8.0"
|
||||
image = { version = "0.23", default-features = false, features = ["png", "bmp"] }
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
use crate::framework::backend::{init_backend, BackendRenderer};
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::filesystem::Filesystem;
|
||||
use crate::framework::gamepad::GamepadContext;
|
||||
use crate::framework::keyboard::KeyboardContext;
|
||||
use crate::Game;
|
||||
use crate::graphics::VSyncMode;
|
||||
use crate::Game;
|
||||
|
||||
pub struct Context {
|
||||
pub headless: bool,
|
||||
pub size_hint: (u16, u16),
|
||||
pub(crate) filesystem: Filesystem,
|
||||
pub(crate) renderer: Option<Box<dyn BackendRenderer>>,
|
||||
pub(crate) gamepad_context: GamepadContext,
|
||||
pub(crate) keyboard_context: KeyboardContext,
|
||||
pub(crate) real_screen_size: (u32, u32),
|
||||
pub(crate) screen_size: (f32, f32),
|
||||
|
@ -24,6 +26,7 @@ impl Context {
|
|||
size_hint: (640, 480),
|
||||
filesystem: Filesystem::new(),
|
||||
renderer: None,
|
||||
gamepad_context: GamepadContext::new(),
|
||||
keyboard_context: KeyboardContext::new(),
|
||||
real_screen_size: (320, 240),
|
||||
screen_size: (320.0, 240.0),
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use gilrs::{Axis, Button, Gamepad, GamepadId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{framework::context::Context, settings::PlayerControllerInputType};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum AxisDirection {
|
||||
None,
|
||||
Up,
|
||||
Left,
|
||||
Right,
|
||||
Down,
|
||||
}
|
||||
|
||||
impl AxisDirection {
|
||||
pub fn compare(&self, value: f64, axis_sensitivity: f64) -> bool {
|
||||
match self {
|
||||
AxisDirection::None => false,
|
||||
AxisDirection::Up => value > axis_sensitivity,
|
||||
AxisDirection::Left => value < -axis_sensitivity,
|
||||
AxisDirection::Right => value > axis_sensitivity,
|
||||
AxisDirection::Down => value < -axis_sensitivity,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GamepadData {
|
||||
left_x: f64,
|
||||
left_y: f64,
|
||||
right_x: f64,
|
||||
right_y: f64,
|
||||
axis_sensitivity: f64,
|
||||
}
|
||||
|
||||
impl GamepadData {
|
||||
pub(crate) fn new(axis_sensitivity: f64) -> Self {
|
||||
GamepadData { left_x: 0.0, left_y: 0.0, right_x: 0.0, right_y: 0.0, axis_sensitivity }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GamepadContext {
|
||||
gamepads: HashMap<GamepadId, GamepadData>,
|
||||
}
|
||||
|
||||
impl GamepadContext {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self { gamepads: HashMap::new() }
|
||||
}
|
||||
|
||||
fn gamepad_exists(&self, gamepad: &Gamepad) -> bool {
|
||||
self.gamepads.contains_key(&gamepad.id())
|
||||
}
|
||||
|
||||
pub(crate) fn add_gamepad(&mut self, gamepad: &Gamepad, axis_sensitivity: f64) {
|
||||
self.gamepads.insert(gamepad.id(), GamepadData::new(axis_sensitivity));
|
||||
}
|
||||
|
||||
pub(crate) fn remove_gamepad(&mut self, gamepad: &Gamepad) {
|
||||
self.gamepads.remove(&gamepad.id());
|
||||
}
|
||||
|
||||
pub(crate) fn is_active(
|
||||
&self,
|
||||
gamepad: &Gamepad,
|
||||
input_type: &PlayerControllerInputType,
|
||||
axis_direction: AxisDirection,
|
||||
) -> bool {
|
||||
match input_type {
|
||||
PlayerControllerInputType::ButtonInput(button) => self.is_button_active(gamepad, *button),
|
||||
PlayerControllerInputType::AxisInput(axis) => self.is_axis_active(gamepad, *axis, axis_direction),
|
||||
PlayerControllerInputType::Either(button, axis) => {
|
||||
self.is_button_active(gamepad, *button) || self.is_axis_active(gamepad, *axis, axis_direction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_button_active(&self, gamepad: &Gamepad, button: Button) -> bool {
|
||||
if !self.gamepad_exists(gamepad) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gamepad.is_pressed(button)
|
||||
}
|
||||
|
||||
pub(crate) fn is_axis_active(&self, gamepad: &Gamepad, axis: Axis, direction: AxisDirection) -> bool {
|
||||
if !self.gamepad_exists(gamepad) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let data = self.gamepads.get(&gamepad.id()).unwrap();
|
||||
|
||||
match axis {
|
||||
Axis::LeftStickX => direction.compare(data.left_x, data.axis_sensitivity),
|
||||
Axis::LeftStickY => direction.compare(data.left_y, data.axis_sensitivity),
|
||||
Axis::RightStickX => direction.compare(data.right_x, data.axis_sensitivity),
|
||||
Axis::RightStickY => direction.compare(data.right_y, data.axis_sensitivity),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn update_axes(&mut self, gamepad: &Gamepad) {
|
||||
if !self.gamepad_exists(gamepad) {
|
||||
return;
|
||||
}
|
||||
|
||||
let data = self.gamepads.get_mut(&gamepad.id()).unwrap();
|
||||
|
||||
let mut axes = [
|
||||
(&mut data.left_x, Axis::LeftStickX),
|
||||
(&mut data.left_y, Axis::LeftStickY),
|
||||
(&mut data.right_x, Axis::RightStickX),
|
||||
(&mut data.right_y, Axis::RightStickY),
|
||||
];
|
||||
|
||||
for (axis_val, id) in axes.iter_mut() {
|
||||
if let Some(axis) = gamepad.axis_data(*id) {
|
||||
**axis_val = if axis.value().abs() < 0.12 { 0.0 } else { axis.value() } as f64;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GamepadContext {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_gamepad(context: &mut Context, gamepad: &Gamepad, axis_sensitivity: f64) {
|
||||
context.gamepad_context.add_gamepad(gamepad, axis_sensitivity);
|
||||
}
|
||||
|
||||
pub fn remove_gamepad(context: &mut Context, gamepad: &Gamepad) {
|
||||
context.gamepad_context.remove_gamepad(gamepad);
|
||||
}
|
||||
|
||||
pub fn is_active(
|
||||
ctx: &Context,
|
||||
gamepad: &Gamepad,
|
||||
input_type: &PlayerControllerInputType,
|
||||
axis_direction: AxisDirection,
|
||||
) -> bool {
|
||||
ctx.gamepad_context.is_active(gamepad, input_type, axis_direction)
|
||||
}
|
||||
|
||||
pub fn is_button_active(ctx: &Context, gamepad: &Gamepad, button: Button) -> bool {
|
||||
ctx.gamepad_context.is_button_active(gamepad, button)
|
||||
}
|
||||
|
||||
pub fn is_axis_active(ctx: &Context, gamepad: &Gamepad, axis: Axis, direction: AxisDirection) -> bool {
|
||||
ctx.gamepad_context.is_axis_active(gamepad, axis, direction)
|
||||
}
|
||||
|
||||
pub fn update_axes(ctx: &mut Context, gamepad: &Gamepad) {
|
||||
ctx.gamepad_context.update_axes(gamepad);
|
||||
}
|
|
@ -1,16 +1,17 @@
|
|||
#![allow(unused)]
|
||||
|
||||
pub mod backend;
|
||||
pub mod backend_null;
|
||||
#[cfg(feature = "backend-glutin")]
|
||||
pub mod backend_glutin;
|
||||
#[cfg(feature = "render-opengl")]
|
||||
mod gl;
|
||||
pub mod backend_null;
|
||||
#[cfg(feature = "backend-sdl")]
|
||||
pub mod backend_sdl2;
|
||||
pub mod context;
|
||||
pub mod error;
|
||||
pub mod filesystem;
|
||||
pub mod gamepad;
|
||||
#[cfg(feature = "render-opengl")]
|
||||
mod gl;
|
||||
pub mod graphics;
|
||||
pub mod keyboard;
|
||||
#[cfg(feature = "render-opengl")]
|
||||
|
|
|
@ -0,0 +1,247 @@
|
|||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::gamepad::{self, AxisDirection};
|
||||
use crate::input::player_controller::PlayerController;
|
||||
use crate::player::TargetPlayer;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
use crate::{bitfield, settings::PlayerControllerInputType};
|
||||
|
||||
use gilrs::{Button, GamepadId};
|
||||
|
||||
bitfield! {
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct KeyState(u16);
|
||||
impl Debug;
|
||||
|
||||
pub left, set_left: 0;
|
||||
pub right, set_right: 1;
|
||||
pub up, set_up: 2;
|
||||
pub down, set_down: 3;
|
||||
pub map, set_map: 4;
|
||||
pub inventory, set_inventory: 5;
|
||||
pub jump, set_jump: 6;
|
||||
pub shoot, set_shoot: 7;
|
||||
pub next_weapon, set_next_weapon: 8;
|
||||
pub prev_weapon, set_prev_weapon: 9;
|
||||
pub escape, set_escape: 10;
|
||||
pub enter, set_enter: 11;
|
||||
pub skip, set_skip: 12;
|
||||
pub strafe, set_strafe: 13;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GamepadController {
|
||||
gamepad_id: GamepadId,
|
||||
target: TargetPlayer,
|
||||
state: KeyState,
|
||||
old_state: KeyState,
|
||||
trigger: KeyState,
|
||||
}
|
||||
|
||||
impl GamepadController {
|
||||
pub fn new(gamepad_id: GamepadId, target: TargetPlayer) -> GamepadController {
|
||||
GamepadController { gamepad_id, target, state: KeyState(0), old_state: KeyState(0), trigger: KeyState(0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl PlayerController for GamepadController {
|
||||
fn update(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
let button_map = match self.target {
|
||||
TargetPlayer::Player1 => &state.settings.player1_controller_button_map,
|
||||
TargetPlayer::Player2 => &state.settings.player2_controller_button_map,
|
||||
};
|
||||
|
||||
if let Some(gilrs) = &state.gilrs {
|
||||
if let Some(gamepad) = gilrs.connected_gamepad(self.gamepad_id) {
|
||||
gamepad::update_axes(ctx, &gamepad);
|
||||
|
||||
self.state.set_up(gamepad::is_active(ctx, &gamepad, &button_map.up, AxisDirection::Up));
|
||||
self.state.set_down(gamepad::is_active(ctx, &gamepad, &button_map.down, AxisDirection::Down));
|
||||
self.state.set_left(gamepad::is_active(ctx, &gamepad, &button_map.left, AxisDirection::Left));
|
||||
self.state.set_right(gamepad::is_active(ctx, &gamepad, &button_map.right, AxisDirection::Right));
|
||||
self.state.set_map(gamepad::is_active(ctx, &gamepad, &button_map.map, AxisDirection::None));
|
||||
self.state.set_inventory(gamepad::is_active(ctx, &gamepad, &button_map.inventory, AxisDirection::None));
|
||||
self.state.set_jump(gamepad::is_active(ctx, &gamepad, &button_map.jump, AxisDirection::None));
|
||||
self.state.set_shoot(gamepad::is_active(ctx, &gamepad, &button_map.shoot, AxisDirection::None));
|
||||
self.state.set_next_weapon(gamepad::is_active(
|
||||
ctx,
|
||||
&gamepad,
|
||||
&button_map.next_weapon,
|
||||
AxisDirection::None,
|
||||
));
|
||||
self.state.set_prev_weapon(gamepad::is_active(
|
||||
ctx,
|
||||
&gamepad,
|
||||
&button_map.prev_weapon,
|
||||
AxisDirection::None,
|
||||
));
|
||||
self.state.set_escape(gamepad::is_active(
|
||||
ctx,
|
||||
&gamepad,
|
||||
&PlayerControllerInputType::ButtonInput(Button::Start),
|
||||
AxisDirection::None,
|
||||
));
|
||||
self.state.set_enter(gamepad::is_active(ctx, &gamepad, &button_map.jump, AxisDirection::None));
|
||||
self.state.set_skip(gamepad::is_active(ctx, &gamepad, &button_map.skip, AxisDirection::None));
|
||||
self.state.set_strafe(gamepad::is_active(ctx, &gamepad, &button_map.strafe, AxisDirection::None));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_trigger(&mut self) {
|
||||
let mut trigger = self.state.0 ^ self.old_state.0;
|
||||
trigger &= self.state.0;
|
||||
self.old_state = self.state;
|
||||
self.trigger = KeyState(trigger);
|
||||
}
|
||||
|
||||
fn move_up(&self) -> bool {
|
||||
self.state.up()
|
||||
}
|
||||
|
||||
fn move_left(&self) -> bool {
|
||||
self.state.left()
|
||||
}
|
||||
|
||||
fn move_down(&self) -> bool {
|
||||
self.state.down()
|
||||
}
|
||||
|
||||
fn move_right(&self) -> bool {
|
||||
self.state.right()
|
||||
}
|
||||
|
||||
fn prev_weapon(&self) -> bool {
|
||||
self.state.prev_weapon()
|
||||
}
|
||||
|
||||
fn next_weapon(&self) -> bool {
|
||||
self.state.next_weapon()
|
||||
}
|
||||
|
||||
fn map(&self) -> bool {
|
||||
self.state.map()
|
||||
}
|
||||
|
||||
fn inventory(&self) -> bool {
|
||||
self.state.inventory()
|
||||
}
|
||||
|
||||
fn jump(&self) -> bool {
|
||||
self.state.jump()
|
||||
}
|
||||
|
||||
fn shoot(&self) -> bool {
|
||||
self.state.shoot()
|
||||
}
|
||||
|
||||
fn skip(&self) -> bool {
|
||||
self.state.skip()
|
||||
}
|
||||
|
||||
fn strafe(&self) -> bool {
|
||||
self.state.strafe()
|
||||
}
|
||||
|
||||
fn trigger_up(&self) -> bool {
|
||||
self.trigger.up()
|
||||
}
|
||||
|
||||
fn trigger_left(&self) -> bool {
|
||||
self.trigger.left()
|
||||
}
|
||||
|
||||
fn trigger_down(&self) -> bool {
|
||||
self.trigger.down()
|
||||
}
|
||||
|
||||
fn trigger_right(&self) -> bool {
|
||||
self.trigger.right()
|
||||
}
|
||||
|
||||
fn trigger_prev_weapon(&self) -> bool {
|
||||
self.trigger.prev_weapon()
|
||||
}
|
||||
|
||||
fn trigger_next_weapon(&self) -> bool {
|
||||
self.trigger.next_weapon()
|
||||
}
|
||||
|
||||
fn trigger_map(&self) -> bool {
|
||||
self.trigger.map()
|
||||
}
|
||||
|
||||
fn trigger_inventory(&self) -> bool {
|
||||
self.trigger.inventory()
|
||||
}
|
||||
|
||||
fn trigger_jump(&self) -> bool {
|
||||
self.trigger.jump()
|
||||
}
|
||||
|
||||
fn trigger_shoot(&self) -> bool {
|
||||
self.trigger.shoot()
|
||||
}
|
||||
|
||||
fn trigger_skip(&self) -> bool {
|
||||
self.trigger.skip()
|
||||
}
|
||||
|
||||
fn trigger_strafe(&self) -> bool {
|
||||
self.trigger.strafe()
|
||||
}
|
||||
|
||||
fn trigger_menu_ok(&self) -> bool {
|
||||
self.trigger.jump() || self.trigger.enter()
|
||||
}
|
||||
|
||||
fn trigger_menu_back(&self) -> bool {
|
||||
self.trigger.shoot() || self.trigger.escape()
|
||||
}
|
||||
|
||||
fn trigger_menu_pause(&self) -> bool {
|
||||
self.trigger.escape()
|
||||
}
|
||||
|
||||
fn look_up(&self) -> bool {
|
||||
self.state.up()
|
||||
}
|
||||
|
||||
fn look_left(&self) -> bool {
|
||||
self.state.left()
|
||||
}
|
||||
|
||||
fn look_down(&self) -> bool {
|
||||
self.state.down()
|
||||
}
|
||||
|
||||
fn look_right(&self) -> bool {
|
||||
self.state.right()
|
||||
}
|
||||
|
||||
fn move_analog_x(&self) -> f64 {
|
||||
if self.state.left() && self.state.right() {
|
||||
0.0
|
||||
} else if self.state.left() {
|
||||
-1.0
|
||||
} else if self.state.right() {
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
fn move_analog_y(&self) -> f64 {
|
||||
if self.state.up() && self.state.down() {
|
||||
0.0
|
||||
} else if self.state.up() {
|
||||
-1.0
|
||||
} else if self.state.down() {
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
pub mod combined_menu_controller;
|
||||
pub mod dummy_player_controller;
|
||||
pub mod gamepad_player_controller;
|
||||
pub mod keyboard_player_controller;
|
||||
pub mod player_controller;
|
||||
pub mod replay_player_controller;
|
||||
|
|
30
src/lib.rs
30
src/lib.rs
|
@ -11,6 +11,8 @@ use std::sync::Mutex;
|
|||
use std::time::{Duration, Instant};
|
||||
|
||||
use directories::ProjectDirs;
|
||||
use framework::gamepad;
|
||||
use gilrs::EventType;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use crate::builtin_fs::BuiltinFS;
|
||||
|
@ -110,6 +112,34 @@ impl Game {
|
|||
}
|
||||
|
||||
fn update(&mut self, ctx: &mut Context) -> GameResult {
|
||||
{
|
||||
let state_ref = unsafe { &mut *self.state.get() };
|
||||
|
||||
if let Some(gilrs) = &mut state_ref.gilrs {
|
||||
while let Some(e) = gilrs.next_event() {
|
||||
let gamepad = gilrs.gamepad(e.id);
|
||||
|
||||
match e.event {
|
||||
EventType::Connected => {
|
||||
log::info!("Gamepad connected: {} (ID: {})", gamepad.name(), gamepad.id());
|
||||
|
||||
let axis_sensitivity = state_ref.settings.get_gamepad_axis_sensitivity(gamepad.id());
|
||||
gamepad::add_gamepad(ctx, &gamepad, axis_sensitivity);
|
||||
|
||||
// TODO: replace the controller of all players that use this gamepad from keyboard to gamepad
|
||||
}
|
||||
EventType::Disconnected => {
|
||||
log::info!("Gamepad disconnected: {} (ID: {})", gamepad.name(), gamepad.id());
|
||||
gamepad::remove_gamepad(ctx, &gamepad);
|
||||
|
||||
// TODO: fall back to keyboard for all players that use this gamepad
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(scene) = &mut self.scene {
|
||||
let state_ref = unsafe { &mut *self.state.get() };
|
||||
|
||||
|
|
110
src/settings.rs
110
src/settings.rs
|
@ -1,8 +1,11 @@
|
|||
use gilrs::{Axis, Button, GamepadId};
|
||||
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::filesystem::{user_create, user_open};
|
||||
use crate::framework::keyboard::ScanCode;
|
||||
use crate::graphics::VSyncMode;
|
||||
use crate::input::gamepad_player_controller::GamepadController;
|
||||
use crate::input::keyboard_player_controller::KeyboardController;
|
||||
use crate::input::player_controller::PlayerController;
|
||||
use crate::input::touch_player_controller::TouchPlayerController;
|
||||
|
@ -34,10 +37,22 @@ pub struct Settings {
|
|||
pub timing_mode: TimingMode,
|
||||
#[serde(default = "default_interpolation")]
|
||||
pub organya_interpolation: InterpolationMode,
|
||||
#[serde(default = "default_controller_type")]
|
||||
pub player1_controller_type: ControllerType,
|
||||
#[serde(default = "default_controller_type")]
|
||||
pub player2_controller_type: ControllerType,
|
||||
#[serde(default = "p1_default_keymap")]
|
||||
pub player1_key_map: PlayerKeyMap,
|
||||
#[serde(default = "p2_default_keymap")]
|
||||
pub player2_key_map: PlayerKeyMap,
|
||||
#[serde(default = "player_default_controller_button_map")]
|
||||
pub player1_controller_button_map: PlayerControllerButtonMap,
|
||||
#[serde(default = "player_default_controller_button_map")]
|
||||
pub player2_controller_button_map: PlayerControllerButtonMap,
|
||||
#[serde(default = "default_controller_axis_sensitivity")]
|
||||
pub player1_controller_axis_sensitivity: f64,
|
||||
#[serde(default = "default_controller_axis_sensitivity")]
|
||||
pub player2_controller_axis_sensitivity: f64,
|
||||
#[serde(skip, default = "default_speed")]
|
||||
pub speed: f64,
|
||||
#[serde(skip)]
|
||||
|
@ -65,7 +80,7 @@ fn default_true() -> bool {
|
|||
|
||||
#[inline(always)]
|
||||
fn current_version() -> u32 {
|
||||
11
|
||||
12
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -108,6 +123,11 @@ fn default_screen_shake_intensity() -> ScreenShakeIntensity {
|
|||
ScreenShakeIntensity::Full
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn default_controller_type() -> ControllerType {
|
||||
ControllerType::Keyboard
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
pub fn load(ctx: &Context) -> GameResult<Settings> {
|
||||
if let Ok(file) = user_open(ctx, "/settings.json") {
|
||||
|
@ -170,6 +190,16 @@ impl Settings {
|
|||
self.window_mode = default_window_mode();
|
||||
}
|
||||
|
||||
if self.version == 11 {
|
||||
self.version = 12;
|
||||
self.player1_controller_type = default_controller_type();
|
||||
self.player2_controller_type = default_controller_type();
|
||||
self.player1_controller_button_map = player_default_controller_button_map();
|
||||
self.player2_controller_button_map = player_default_controller_button_map();
|
||||
self.player1_controller_axis_sensitivity = default_controller_axis_sensitivity();
|
||||
self.player2_controller_axis_sensitivity = default_controller_axis_sensitivity();
|
||||
}
|
||||
|
||||
if self.version != initial_version {
|
||||
log::info!("Upgraded configuration file from version {} to {}.", initial_version, self.version);
|
||||
}
|
||||
|
@ -189,11 +219,27 @@ impl Settings {
|
|||
return Box::new(TouchPlayerController::new());
|
||||
}
|
||||
|
||||
Box::new(KeyboardController::new(TargetPlayer::Player1))
|
||||
match self.player1_controller_type {
|
||||
ControllerType::Keyboard => Box::new(KeyboardController::new(TargetPlayer::Player1)),
|
||||
ControllerType::Gamepad(id) => Box::new(GamepadController::new(id, TargetPlayer::Player1)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_player2_controller(&self) -> Box<dyn PlayerController> {
|
||||
Box::new(KeyboardController::new(TargetPlayer::Player2))
|
||||
match self.player2_controller_type {
|
||||
ControllerType::Keyboard => Box::new(KeyboardController::new(TargetPlayer::Player2)),
|
||||
ControllerType::Gamepad(id) => Box::new(GamepadController::new(id, TargetPlayer::Player2)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_gamepad_axis_sensitivity(&self, id: GamepadId) -> f64 {
|
||||
if self.player1_controller_type == ControllerType::Gamepad(id) {
|
||||
self.player1_controller_axis_sensitivity
|
||||
} else if self.player2_controller_type == ControllerType::Gamepad(id) {
|
||||
self.player2_controller_axis_sensitivity
|
||||
} else {
|
||||
default_controller_axis_sensitivity()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,8 +259,14 @@ impl Default for Settings {
|
|||
sfx_volume: 1.0,
|
||||
timing_mode: default_timing(),
|
||||
organya_interpolation: InterpolationMode::Linear,
|
||||
player1_controller_type: default_controller_type(),
|
||||
player2_controller_type: default_controller_type(),
|
||||
player1_key_map: p1_default_keymap(),
|
||||
player2_key_map: p2_default_keymap(),
|
||||
player1_controller_button_map: player_default_controller_button_map(),
|
||||
player2_controller_button_map: player_default_controller_button_map(),
|
||||
player1_controller_axis_sensitivity: default_controller_axis_sensitivity(),
|
||||
player2_controller_axis_sensitivity: default_controller_axis_sensitivity(),
|
||||
speed: 1.0,
|
||||
god_mode: false,
|
||||
infinite_booster: false,
|
||||
|
@ -281,3 +333,55 @@ fn p2_default_keymap() -> PlayerKeyMap {
|
|||
strafe: ScanCode::RShift,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Eq, PartialEq)]
|
||||
pub enum ControllerType {
|
||||
Keyboard,
|
||||
Gamepad(GamepadId),
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub enum PlayerControllerInputType {
|
||||
ButtonInput(Button),
|
||||
AxisInput(Axis),
|
||||
Either(Button, Axis),
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct PlayerControllerButtonMap {
|
||||
pub left: PlayerControllerInputType,
|
||||
pub up: PlayerControllerInputType,
|
||||
pub right: PlayerControllerInputType,
|
||||
pub down: PlayerControllerInputType,
|
||||
pub prev_weapon: PlayerControllerInputType,
|
||||
pub next_weapon: PlayerControllerInputType,
|
||||
pub jump: PlayerControllerInputType,
|
||||
pub shoot: PlayerControllerInputType,
|
||||
pub skip: PlayerControllerInputType,
|
||||
pub inventory: PlayerControllerInputType,
|
||||
pub map: PlayerControllerInputType,
|
||||
pub strafe: PlayerControllerInputType,
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn player_default_controller_button_map() -> PlayerControllerButtonMap {
|
||||
PlayerControllerButtonMap {
|
||||
left: PlayerControllerInputType::Either(Button::DPadLeft, Axis::LeftStickX),
|
||||
up: PlayerControllerInputType::Either(Button::DPadUp, Axis::LeftStickY),
|
||||
right: PlayerControllerInputType::Either(Button::DPadRight, Axis::LeftStickX),
|
||||
down: PlayerControllerInputType::Either(Button::DPadDown, Axis::LeftStickY),
|
||||
prev_weapon: PlayerControllerInputType::ButtonInput(Button::LeftTrigger),
|
||||
next_weapon: PlayerControllerInputType::ButtonInput(Button::RightTrigger),
|
||||
jump: PlayerControllerInputType::ButtonInput(Button::East),
|
||||
shoot: PlayerControllerInputType::ButtonInput(Button::South),
|
||||
skip: PlayerControllerInputType::ButtonInput(Button::LeftTrigger2),
|
||||
strafe: PlayerControllerInputType::ButtonInput(Button::RightTrigger2),
|
||||
inventory: PlayerControllerInputType::ButtonInput(Button::North),
|
||||
map: PlayerControllerInputType::ButtonInput(Button::West),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn default_controller_axis_sensitivity() -> f64 {
|
||||
0.3
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ use crate::framework::error::GameResult;
|
|||
use crate::framework::graphics::{create_texture_mutable, set_render_target};
|
||||
use crate::framework::keyboard::ScanCode;
|
||||
use crate::framework::vfs::OpenOptions;
|
||||
use crate::framework::{filesystem, graphics};
|
||||
use crate::framework::{filesystem, gamepad, graphics};
|
||||
#[cfg(feature = "hooks")]
|
||||
use crate::hooks::init_hooks;
|
||||
use crate::i18n::Locale;
|
||||
|
@ -38,6 +38,8 @@ use crate::stage::StageData;
|
|||
use crate::texture_set::TextureSet;
|
||||
use crate::vanilla::VanillaExtractor;
|
||||
|
||||
use gilrs::Gilrs;
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub enum TimingMode {
|
||||
_50Hz,
|
||||
|
@ -287,6 +289,7 @@ pub struct SharedGameState {
|
|||
pub super_quake_counter: u16,
|
||||
pub teleporter_slots: Vec<(u16, u16)>,
|
||||
pub carets: Vec<Caret>,
|
||||
pub gilrs: Option<Gilrs>,
|
||||
pub touch_controls: TouchControls,
|
||||
pub mod_path: Option<String>,
|
||||
pub mod_list: ModList,
|
||||
|
@ -331,6 +334,16 @@ impl SharedGameState {
|
|||
let mut sound_manager = SoundManager::new(ctx)?;
|
||||
let settings = Settings::load(ctx)?;
|
||||
let mod_requirements = ModRequirements::load(ctx)?;
|
||||
let mut gilrs = Gilrs::new().ok();
|
||||
|
||||
if let Some(gilrs) = &mut gilrs {
|
||||
for (id, gamepad) in gilrs.gamepads() {
|
||||
log::info!("Found gamepad {} (ID {})", gamepad.name(), id);
|
||||
|
||||
let axis_sensitivity = settings.get_gamepad_axis_sensitivity(id);
|
||||
gamepad::add_gamepad(ctx, &gamepad, axis_sensitivity);
|
||||
}
|
||||
}
|
||||
|
||||
let vanilla_extractor = VanillaExtractor::from(ctx, "Doukutsu.exe".to_string());
|
||||
if vanilla_extractor.is_some() {
|
||||
|
@ -424,6 +437,7 @@ impl SharedGameState {
|
|||
super_quake_counter: 0,
|
||||
teleporter_slots: Vec::with_capacity(8),
|
||||
carets: Vec::with_capacity(32),
|
||||
gilrs,
|
||||
touch_controls: TouchControls::new(),
|
||||
mod_path: None,
|
||||
mod_list,
|
||||
|
|
Loading…
Reference in New Issue