1
0
Fork 0
mirror of https://github.com/doukutsu-rs/doukutsu-rs synced 2025-11-30 16:18:00 +00:00

add gamecontrollerdb, update default inputs, add reset controls option

This commit is contained in:
Sallai József 2022-08-01 03:18:43 +03:00
parent f74ec19cb5
commit 4cfbcc50ac
11 changed files with 1877 additions and 22 deletions

File diff suppressed because it is too large Load diff

View file

@ -160,7 +160,10 @@
"rebind_confirm_menu": { "rebind_confirm_menu": {
"title": "Press button for \"{control}\"", "title": "Press button for \"{control}\"",
"cancel": "(Esc to cancel)" "cancel": "(Esc to cancel)"
} },
"reset_confirm": "Reset...",
"reset_confirm_menu_title": "Reset controls?"
} }
}, },

View file

@ -152,7 +152,10 @@
"rebind_confirm_menu": { "rebind_confirm_menu": {
"title": "新しい「ジャンプ」ボタンを押す", "title": "新しい「ジャンプ」ボタンを押す",
"cancel": "(Escキーを押してキャンセル)" "cancel": "(Escキーを押してキャンセル)"
} },
"reset_confirm": "リセット",
"reset_confirm_menu_title": "ボタンをリセットしますか?"
} }
}, },

View file

@ -101,6 +101,7 @@ impl BuiltinFS {
FSNode::File("builtin_font.fnt", include_bytes!("builtin/builtin_font.fnt")), FSNode::File("builtin_font.fnt", include_bytes!("builtin/builtin_font.fnt")),
FSNode::File("builtin_font_0.png", include_bytes!("builtin/builtin_font_0.png")), FSNode::File("builtin_font_0.png", include_bytes!("builtin/builtin_font_0.png")),
FSNode::File("builtin_font_1.png", include_bytes!("builtin/builtin_font_1.png")), FSNode::File("builtin_font_1.png", include_bytes!("builtin/builtin_font_1.png")),
FSNode::File("gamecontrollerdb.txt", include_bytes!("builtin/gamecontrollerdb.txt")),
FSNode::File( FSNode::File(
"organya-wavetable-doukutsu.bin", "organya-wavetable-doukutsu.bin",
include_bytes!("builtin/organya-wavetable-doukutsu.bin"), include_bytes!("builtin/organya-wavetable-doukutsu.bin"),

View file

@ -1,12 +1,12 @@
use std::any::Any;
use imgui::DrawData; use imgui::DrawData;
use std::any::Any;
use crate::common::{Color, Rect}; use crate::common::{Color, Rect};
use crate::framework::context::Context; use crate::framework::context::Context;
use crate::framework::error::GameResult; use crate::framework::error::GameResult;
use crate::framework::graphics::BlendMode; use crate::framework::graphics::BlendMode;
use crate::Game;
use crate::graphics::VSyncMode; use crate::graphics::VSyncMode;
use crate::Game;
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@ -25,7 +25,7 @@ pub enum BackendShader {
} }
pub trait Backend { pub trait Backend {
fn create_event_loop(&self) -> GameResult<Box<dyn BackendEventLoop>>; fn create_event_loop(&self, ctx: &Context) -> GameResult<Box<dyn BackendEventLoop>>;
} }
pub trait BackendEventLoop { pub trait BackendEventLoop {
@ -41,7 +41,9 @@ pub trait BackendRenderer {
fn present(&mut self) -> GameResult; fn present(&mut self) -> GameResult;
fn set_vsync_mode(&mut self, _mode: VSyncMode) -> GameResult { Ok(()) } fn set_vsync_mode(&mut self, _mode: VSyncMode) -> GameResult {
Ok(())
}
fn prepare_draw(&mut self, _width: f32, _height: f32) -> GameResult { fn prepare_draw(&mut self, _width: f32, _height: f32) -> GameResult {
Ok(()) Ok(())

View file

@ -4,13 +4,12 @@ use std::mem;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use glutin::{Api, ContextBuilder, GlProfile, GlRequest, PossiblyCurrent, WindowedContext};
use glutin::event::{ElementState, Event, TouchPhase, VirtualKeyCode, WindowEvent}; use glutin::event::{ElementState, Event, TouchPhase, VirtualKeyCode, WindowEvent};
use glutin::event_loop::{ControlFlow, EventLoop}; use glutin::event_loop::{ControlFlow, EventLoop};
use glutin::window::WindowBuilder; use glutin::window::WindowBuilder;
use glutin::{Api, ContextBuilder, GlProfile, GlRequest, PossiblyCurrent, WindowedContext};
use imgui::{DrawCmdParams, DrawData, DrawIdx, DrawVert}; use imgui::{DrawCmdParams, DrawData, DrawIdx, DrawVert};
use crate::{Game, GAME_SUSPENDED};
use crate::common::Rect; use crate::common::Rect;
use crate::framework::backend::{Backend, BackendEventLoop, BackendRenderer, BackendTexture, SpriteBatchCommand}; use crate::framework::backend::{Backend, BackendEventLoop, BackendRenderer, BackendTexture, SpriteBatchCommand};
use crate::framework::context::Context; use crate::framework::context::Context;
@ -19,6 +18,7 @@ use crate::framework::gl;
use crate::framework::keyboard::ScanCode; use crate::framework::keyboard::ScanCode;
use crate::framework::render_opengl::{GLContext, OpenGLRenderer}; use crate::framework::render_opengl::{GLContext, OpenGLRenderer};
use crate::input::touch_controls::TouchPoint; use crate::input::touch_controls::TouchPoint;
use crate::{Game, GAME_SUSPENDED};
pub struct GlutinBackend; pub struct GlutinBackend;
@ -29,7 +29,7 @@ impl GlutinBackend {
} }
impl Backend for GlutinBackend { impl Backend for GlutinBackend {
fn create_event_loop(&self) -> GameResult<Box<dyn BackendEventLoop>> { fn create_event_loop(&self, _ctx: &Context) -> GameResult<Box<dyn BackendEventLoop>> {
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
loop { loop {
match ndk_glue::native_window().as_ref() { match ndk_glue::native_window().as_ref() {
@ -60,7 +60,8 @@ impl GlutinEventLoop {
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
let windowed_context = windowed_context.with_gl(GlRequest::Specific(Api::OpenGlEs, (2, 0))); let windowed_context = windowed_context.with_gl(GlRequest::Specific(Api::OpenGlEs, (2, 0)));
let windowed_context = windowed_context.with_gl_profile(GlProfile::Core) let windowed_context = windowed_context
.with_gl_profile(GlProfile::Core)
.with_gl_debug_flag(false) .with_gl_debug_flag(false)
.with_pixel_format(24, 8) .with_pixel_format(24, 8)
.with_vsync(true); .with_vsync(true);
@ -338,7 +339,7 @@ impl BackendEventLoop for GlutinEventLoop {
std::ptr::null() std::ptr::null()
} }
}; };
*user_data = Rc::into_raw(refs) as *mut c_void; *user_data = Rc::into_raw(refs) as *mut c_void;
result result

View file

@ -22,7 +22,7 @@ impl NullBackend {
} }
impl Backend for NullBackend { impl Backend for NullBackend {
fn create_event_loop(&self) -> GameResult<Box<dyn BackendEventLoop>> { fn create_event_loop(&self, _ctx: &Context) -> GameResult<Box<dyn BackendEventLoop>> {
Ok(Box::new(NullEventLoop)) Ok(Box::new(NullEventLoop))
} }
} }

View file

@ -37,6 +37,8 @@ use crate::Game;
use crate::GameError::RenderError; use crate::GameError::RenderError;
use crate::GAME_SUSPENDED; use crate::GAME_SUSPENDED;
use super::filesystem;
pub struct SDL2Backend { pub struct SDL2Backend {
context: Sdl, context: Sdl,
size_hint: (u16, u16), size_hint: (u16, u16),
@ -55,8 +57,8 @@ impl SDL2Backend {
} }
impl Backend for SDL2Backend { impl Backend for SDL2Backend {
fn create_event_loop(&self) -> GameResult<Box<dyn BackendEventLoop>> { fn create_event_loop(&self, ctx: &Context) -> GameResult<Box<dyn BackendEventLoop>> {
SDL2EventLoop::new(&self.context, self.size_hint) SDL2EventLoop::new(&self.context, self.size_hint, ctx)
} }
} }
@ -149,10 +151,13 @@ struct SDL2Context {
} }
impl SDL2EventLoop { impl SDL2EventLoop {
pub fn new(sdl: &Sdl, size_hint: (u16, u16)) -> GameResult<Box<dyn BackendEventLoop>> { pub fn new(sdl: &Sdl, size_hint: (u16, u16), ctx: &Context) -> GameResult<Box<dyn BackendEventLoop>> {
let event_pump = sdl.event_pump().map_err(GameError::WindowError)?; let event_pump = sdl.event_pump().map_err(GameError::WindowError)?;
let video = sdl.video().map_err(GameError::WindowError)?; let video = sdl.video().map_err(GameError::WindowError)?;
let game_controller = sdl.game_controller().map_err(GameError::GamepadError)?; let game_controller = sdl.game_controller().map_err(GameError::GamepadError)?;
let mut controller_mappings = filesystem::open(ctx, "/builtin/gamecontrollerdb.txt")?;
game_controller.load_mappings_from_read(&mut controller_mappings).unwrap();
let gl_attr = video.gl_attr(); let gl_attr = video.gl_attr();

View file

@ -37,7 +37,7 @@ impl Context {
pub fn run(&mut self, game: &mut Game) -> GameResult { pub fn run(&mut self, game: &mut Game) -> GameResult {
let backend = init_backend(self.headless, self.size_hint)?; let backend = init_backend(self.headless, self.size_hint)?;
let mut event_loop = backend.create_event_loop()?; let mut event_loop = backend.create_event_loop(self)?;
self.renderer = Some(event_loop.new_renderer(self as *mut Context)?); self.renderer = Some(event_loop.new_renderer(self as *mut Context)?);
event_loop.run(game, self); event_loop.run(game, self);

View file

@ -5,7 +5,10 @@ use crate::framework::error::GameResult;
use crate::framework::gamepad::{self, Axis, AxisDirection, Button, PlayerControllerInputType}; use crate::framework::gamepad::{self, Axis, AxisDirection, Button, PlayerControllerInputType};
use crate::framework::keyboard::ScanCode; use crate::framework::keyboard::ScanCode;
use crate::input::combined_menu_controller::CombinedMenuController; use crate::input::combined_menu_controller::CombinedMenuController;
use crate::settings::{ControllerType, PlayerControllerButtonMap, PlayerKeyMap}; use crate::settings::{
p1_default_keymap, p2_default_keymap, player_default_controller_button_map, ControllerType,
PlayerControllerButtonMap, PlayerKeyMap,
};
use crate::shared_game_state::SharedGameState; use crate::shared_game_state::SharedGameState;
use super::{ControlMenuData, Menu, MenuEntry, MenuSelectionResult}; use super::{ControlMenuData, Menu, MenuEntry, MenuSelectionResult};
@ -31,6 +34,7 @@ enum CurrentMenu {
ControllerMenu, ControllerMenu,
RebindMenu, RebindMenu,
ConfirmRebindMenu, ConfirmRebindMenu,
ConfirmResetMenu,
} }
#[derive(Debug, Clone, Copy, Eq, PartialEq)] #[derive(Debug, Clone, Copy, Eq, PartialEq)]
@ -50,6 +54,7 @@ impl Default for ControllerMenuEntry {
#[derive(Debug, Clone, Copy, Eq, PartialEq)] #[derive(Debug, Clone, Copy, Eq, PartialEq)]
enum RebindMenuEntry { enum RebindMenuEntry {
Control(ControlEntry), Control(ControlEntry),
Reset,
Back, Back,
} }
@ -59,6 +64,19 @@ impl Default for RebindMenuEntry {
} }
} }
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
enum ConfirmResetMenuEntry {
Title,
Yes,
No,
}
impl Default for ConfirmResetMenuEntry {
fn default() -> Self {
ConfirmResetMenuEntry::No
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)] #[derive(Debug, Clone, Copy, Eq, PartialEq)]
enum Player { enum Player {
Player1, Player1,
@ -118,6 +136,7 @@ pub struct ControlsMenu {
controller: Menu<ControllerMenuEntry>, controller: Menu<ControllerMenuEntry>,
rebind: Menu<RebindMenuEntry>, rebind: Menu<RebindMenuEntry>,
confirm_rebind: Menu<usize>, confirm_rebind: Menu<usize>,
confirm_reset: Menu<ConfirmResetMenuEntry>,
selected_player: Player, selected_player: Player,
selected_controller: ControllerType, selected_controller: ControllerType,
@ -136,12 +155,14 @@ impl ControlsMenu {
let controller = Menu::new(0, 0, 220, 0); let controller = Menu::new(0, 0, 220, 0);
let rebind = Menu::new(0, 0, 220, 0); let rebind = Menu::new(0, 0, 220, 0);
let confirm_rebind = Menu::new(0, 0, 220, 0); let confirm_rebind = Menu::new(0, 0, 220, 0);
let confirm_reset = Menu::new(0, 0, 160, 0);
ControlsMenu { ControlsMenu {
current: CurrentMenu::ControllerMenu, current: CurrentMenu::ControllerMenu,
controller, controller,
rebind, rebind,
confirm_rebind, confirm_rebind,
confirm_reset,
selected_player: Player::Player1, selected_player: Player::Player1,
selected_controller: ControllerType::Keyboard, selected_controller: ControllerType::Keyboard,
@ -174,6 +195,13 @@ impl ControlsMenu {
.push_entry(ControllerMenuEntry::Rebind, MenuEntry::Active(state.t("menus.controls_menu.rebind"))); .push_entry(ControllerMenuEntry::Rebind, MenuEntry::Active(state.t("menus.controls_menu.rebind")));
self.controller.push_entry(ControllerMenuEntry::Back, MenuEntry::Active(state.t("common.back"))); self.controller.push_entry(ControllerMenuEntry::Back, MenuEntry::Active(state.t("common.back")));
self.confirm_reset.push_entry(
ConfirmResetMenuEntry::Title,
MenuEntry::Disabled(state.t("menus.controls_menu.reset_confirm_menu_title")),
);
self.confirm_reset.push_entry(ConfirmResetMenuEntry::Yes, MenuEntry::Active(state.t("common.yes")));
self.confirm_reset.push_entry(ConfirmResetMenuEntry::No, MenuEntry::Active(state.t("common.no")));
self.player1_key_map = self.init_key_map(&state.settings.player1_key_map); self.player1_key_map = self.init_key_map(&state.settings.player1_key_map);
self.player2_key_map = self.init_key_map(&state.settings.player2_key_map); self.player2_key_map = self.init_key_map(&state.settings.player2_key_map);
self.player1_controller_button_map = self.player1_controller_button_map =
@ -206,6 +234,11 @@ impl ControlsMenu {
self.confirm_rebind.update_height(); self.confirm_rebind.update_height();
self.confirm_rebind.x = ((state.canvas_size.0 - self.confirm_rebind.width as f32) / 2.0).floor() as isize; self.confirm_rebind.x = ((state.canvas_size.0 - self.confirm_rebind.width as f32) / 2.0).floor() as isize;
self.confirm_rebind.y = ((state.canvas_size.1 - self.confirm_rebind.height as f32) / 2.0).floor() as isize; self.confirm_rebind.y = ((state.canvas_size.1 - self.confirm_rebind.height as f32) / 2.0).floor() as isize;
self.confirm_reset.update_width(state);
self.confirm_reset.update_height();
self.confirm_reset.x = ((state.canvas_size.0 - self.confirm_reset.width as f32) / 2.0).floor() as isize;
self.confirm_reset.y = ((state.canvas_size.1 - self.confirm_reset.height as f32) / 2.0).floor() as isize;
} }
fn init_key_map(&self, settings_key_map: &PlayerKeyMap) -> Vec<(ControlEntry, ScanCode)> { fn init_key_map(&self, settings_key_map: &PlayerKeyMap) -> Vec<(ControlEntry, ScanCode)> {
@ -319,6 +352,7 @@ impl ControlsMenu {
} }
} }
self.rebind.push_entry(RebindMenuEntry::Reset, MenuEntry::Active(state.t("menus.controls_menu.reset_confirm")));
self.rebind.push_entry(RebindMenuEntry::Back, MenuEntry::Active(state.t("common.back"))); self.rebind.push_entry(RebindMenuEntry::Back, MenuEntry::Active(state.t("common.back")));
} }
@ -395,6 +429,33 @@ impl ControlsMenu {
} }
} }
fn reset_controls(&mut self, state: &mut SharedGameState, ctx: &Context) -> GameResult {
match self.selected_player {
Player::Player1 => {
if self.selected_controller == ControllerType::Keyboard {
state.settings.player1_key_map = p1_default_keymap();
self.player1_key_map = self.init_key_map(&state.settings.player1_key_map);
} else {
state.settings.player1_controller_button_map = player_default_controller_button_map();
self.player1_controller_button_map =
self.init_controller_button_map(&state.settings.player1_controller_button_map);
}
}
Player::Player2 => {
if self.selected_controller == ControllerType::Keyboard {
state.settings.player2_key_map = p2_default_keymap();
self.player2_key_map = self.init_key_map(&state.settings.player2_key_map);
} else {
state.settings.player2_controller_button_map = player_default_controller_button_map();
self.player2_controller_button_map =
self.init_controller_button_map(&state.settings.player2_controller_button_map);
}
}
}
state.settings.save(ctx)
}
fn is_key_occupied(&self, scan_code: ScanCode) -> bool { fn is_key_occupied(&self, scan_code: ScanCode) -> bool {
let other_player_keymap = match self.selected_player { let other_player_keymap = match self.selected_player {
Player::Player1 => &self.player2_key_map, Player::Player1 => &self.player2_key_map,
@ -882,6 +943,10 @@ impl ControlsMenu {
self.current = CurrentMenu::ConfirmRebindMenu; self.current = CurrentMenu::ConfirmRebindMenu;
} }
} }
MenuSelectionResult::Selected(RebindMenuEntry::Reset, _) => {
self.confirm_reset.selected = ConfirmResetMenuEntry::default();
self.current = CurrentMenu::ConfirmResetMenu;
}
_ => {} _ => {}
}, },
CurrentMenu::ConfirmRebindMenu => match self.confirm_rebind.tick(controller, state) { CurrentMenu::ConfirmRebindMenu => match self.confirm_rebind.tick(controller, state) {
@ -986,6 +1051,19 @@ impl ControlsMenu {
} }
} }
}, },
CurrentMenu::ConfirmResetMenu => match self.confirm_reset.tick(controller, state) {
MenuSelectionResult::Selected(ConfirmResetMenuEntry::Yes, _) => {
self.reset_controls(state, ctx)?;
self.update_rebind_menu(state, ctx);
self.input_busy = true;
self.rebind.non_interactive = true;
self.current = CurrentMenu::RebindMenu;
}
MenuSelectionResult::Selected(ConfirmResetMenuEntry::No, _) | MenuSelectionResult::Canceled => {
self.current = CurrentMenu::RebindMenu;
}
_ => {}
},
} }
if self.input_busy { if self.input_busy {
@ -1016,6 +1094,7 @@ impl ControlsMenu {
CurrentMenu::ControllerMenu => self.controller.draw(state, ctx)?, CurrentMenu::ControllerMenu => self.controller.draw(state, ctx)?,
CurrentMenu::RebindMenu => self.rebind.draw(state, ctx)?, CurrentMenu::RebindMenu => self.rebind.draw(state, ctx)?,
CurrentMenu::ConfirmRebindMenu => self.confirm_rebind.draw(state, ctx)?, CurrentMenu::ConfirmRebindMenu => self.confirm_rebind.draw(state, ctx)?,
CurrentMenu::ConfirmResetMenu => self.confirm_reset.draw(state, ctx)?,
} }
Ok(()) Ok(())

View file

@ -82,7 +82,7 @@ fn default_true() -> bool {
#[inline(always)] #[inline(always)]
fn current_version() -> u32 { fn current_version() -> u32 {
16 17
} }
#[inline(always)] #[inline(always)]
@ -250,6 +250,26 @@ impl Settings {
self.player2_controller_button_map.menu_back = self.player2_controller_button_map.shoot; self.player2_controller_button_map.menu_back = self.player2_controller_button_map.shoot;
} }
if self.version == 16 {
self.version = 17;
if self.player1_controller_button_map.shoot == PlayerControllerInputType::ButtonInput(Button::East) {
self.player1_controller_button_map.shoot = PlayerControllerInputType::ButtonInput(Button::West);
}
if self.player2_controller_button_map.shoot == PlayerControllerInputType::ButtonInput(Button::East) {
self.player2_controller_button_map.shoot = PlayerControllerInputType::ButtonInput(Button::West);
}
if self.player1_controller_button_map.map == PlayerControllerInputType::ButtonInput(Button::West) {
self.player1_controller_button_map.map = PlayerControllerInputType::ButtonInput(Button::East);
}
if self.player2_controller_button_map.map == PlayerControllerInputType::ButtonInput(Button::West) {
self.player2_controller_button_map.map = PlayerControllerInputType::ButtonInput(Button::East);
}
}
if self.version != initial_version { if self.version != initial_version {
log::info!("Upgraded configuration file from version {} to {}.", initial_version, self.version); log::info!("Upgraded configuration file from version {} to {}.", initial_version, self.version);
} }
@ -370,7 +390,7 @@ pub struct PlayerKeyMap {
} }
#[inline(always)] #[inline(always)]
fn p1_default_keymap() -> PlayerKeyMap { pub fn p1_default_keymap() -> PlayerKeyMap {
PlayerKeyMap { PlayerKeyMap {
left: ScanCode::Left, left: ScanCode::Left,
up: ScanCode::Up, up: ScanCode::Up,
@ -390,7 +410,7 @@ fn p1_default_keymap() -> PlayerKeyMap {
} }
#[inline(always)] #[inline(always)]
fn p2_default_keymap() -> PlayerKeyMap { pub fn p2_default_keymap() -> PlayerKeyMap {
PlayerKeyMap { PlayerKeyMap {
left: ScanCode::Comma, left: ScanCode::Comma,
up: ScanCode::L, up: ScanCode::L,
@ -443,11 +463,11 @@ pub fn player_default_controller_button_map() -> PlayerControllerButtonMap {
prev_weapon: PlayerControllerInputType::ButtonInput(Button::LeftShoulder), prev_weapon: PlayerControllerInputType::ButtonInput(Button::LeftShoulder),
next_weapon: PlayerControllerInputType::ButtonInput(Button::RightShoulder), next_weapon: PlayerControllerInputType::ButtonInput(Button::RightShoulder),
jump: PlayerControllerInputType::ButtonInput(Button::South), jump: PlayerControllerInputType::ButtonInput(Button::South),
shoot: PlayerControllerInputType::ButtonInput(Button::East), shoot: PlayerControllerInputType::ButtonInput(Button::West),
skip: PlayerControllerInputType::AxisInput(Axis::TriggerLeft, AxisDirection::Either), skip: PlayerControllerInputType::AxisInput(Axis::TriggerLeft, AxisDirection::Either),
strafe: PlayerControllerInputType::AxisInput(Axis::TriggerRight, AxisDirection::Either), strafe: PlayerControllerInputType::AxisInput(Axis::TriggerRight, AxisDirection::Either),
inventory: PlayerControllerInputType::ButtonInput(Button::North), inventory: PlayerControllerInputType::ButtonInput(Button::North),
map: PlayerControllerInputType::ButtonInput(Button::West), map: PlayerControllerInputType::ButtonInput(Button::East),
menu_ok: PlayerControllerInputType::ButtonInput(Button::South), menu_ok: PlayerControllerInputType::ButtonInput(Button::South),
menu_back: PlayerControllerInputType::ButtonInput(Button::East), menu_back: PlayerControllerInputType::ButtonInput(Button::East),
} }