add gamepad input icons

This commit is contained in:
Sallai József 2022-07-23 18:45:08 +03:00
parent 0415f917f8
commit fb4ac0dae8
8 changed files with 205 additions and 69 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

View File

@ -106,6 +106,10 @@ impl BuiltinFS {
include_bytes!("builtin/organya-wavetable-doukutsu.bin"),
),
FSNode::File("touch.png", include_bytes!("builtin/touch.png")),
FSNode::Directory(
"builtin_data",
vec![FSNode::File("buttons.png", include_bytes!("builtin/builtin_data/buttons.png"))],
),
FSNode::Directory(
"shaders",
vec![

View File

@ -11,6 +11,7 @@ use crate::engine_constants::npcs::NPCConsts;
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::framework::filesystem;
use crate::framework::gamepad::{Axis, Button};
use crate::i18n::Locale;
use crate::player::ControlMode;
use crate::scripting::tsc::text_script::TextScriptEncoding;
@ -287,6 +288,29 @@ impl Clone for TitleConsts {
}
}
#[derive(Debug)]
pub struct GamepadConsts {
pub button_rects: HashMap<Button, [Rect<u16>; 4]>,
pub axis_rects: HashMap<Axis, [Rect<u16>; 4]>,
}
impl Clone for GamepadConsts {
fn clone(&self) -> GamepadConsts {
GamepadConsts { button_rects: self.button_rects.clone(), axis_rects: self.axis_rects.clone() }
}
}
impl GamepadConsts {
fn rects(base: Rect<u16>) -> [Rect<u16>; 4] {
[
base,
Rect::new(base.left + 64, base.top, base.right + 64, base.bottom),
Rect::new(base.left + 128, base.top, base.right + 128, base.bottom),
Rect::new(base.left + 192, base.top, base.right + 192, base.bottom),
]
}
}
#[derive(Debug)]
pub struct EngineConstants {
pub base_paths: Vec<String>,
@ -315,6 +339,7 @@ pub struct EngineConstants {
pub string_table: HashMap<String, String>,
pub missile_flags: Vec<u16>,
pub locales: HashMap<String, Locale>,
pub gamepad: GamepadConsts,
}
impl Clone for EngineConstants {
@ -346,6 +371,7 @@ impl Clone for EngineConstants {
string_table: self.string_table.clone(),
missile_flags: self.missile_flags.clone(),
locales: self.locales.clone(),
gamepad: self.gamepad.clone(),
}
}
}
@ -1358,6 +1384,7 @@ impl EngineConstants {
"bkSunset" => (320, 240), // nxengine
"bkSunset480fix" => (480, 272), // nxengine
"bkWater" => (32, 48),
"buttons" => (256, 256),
"Bullet" => (320, 176),
"Caret" => (320, 240),
"casts" => (320, 240),
@ -1630,6 +1657,30 @@ impl EngineConstants {
string_table: HashMap::new(),
missile_flags: vec![200, 201, 202, 218, 550, 766, 880, 920, 1551],
locales: HashMap::new(),
gamepad: GamepadConsts {
button_rects: HashMap::from([
(Button::North, GamepadConsts::rects(Rect::new(0, 0, 32, 16))),
(Button::South, GamepadConsts::rects(Rect::new(0, 16, 32, 32))),
(Button::East, GamepadConsts::rects(Rect::new(0, 32, 32, 48))),
(Button::West, GamepadConsts::rects(Rect::new(0, 48, 32, 64))),
(Button::DPadDown, GamepadConsts::rects(Rect::new(0, 64, 32, 80))),
(Button::DPadUp, GamepadConsts::rects(Rect::new(0, 80, 32, 96))),
(Button::DPadRight, GamepadConsts::rects(Rect::new(0, 96, 32, 112))),
(Button::DPadLeft, GamepadConsts::rects(Rect::new(0, 112, 32, 128))),
(Button::LeftShoulder, GamepadConsts::rects(Rect::new(32, 32, 64, 48))),
(Button::RightShoulder, GamepadConsts::rects(Rect::new(32, 48, 64, 64))),
(Button::Start, GamepadConsts::rects(Rect::new(32, 96, 64, 112))),
(Button::Guide, GamepadConsts::rects(Rect::new(32, 112, 64, 128))),
]),
axis_rects: HashMap::from([
(Axis::LeftX, GamepadConsts::rects(Rect::new(32, 0, 64, 16))),
(Axis::LeftY, GamepadConsts::rects(Rect::new(32, 0, 64, 16))),
(Axis::RightX, GamepadConsts::rects(Rect::new(32, 16, 64, 32))),
(Axis::RightY, GamepadConsts::rects(Rect::new(32, 16, 64, 32))),
(Axis::TriggerLeft, GamepadConsts::rects(Rect::new(32, 64, 64, 80))),
(Axis::TriggerRight, GamepadConsts::rects(Rect::new(32, 80, 64, 96))),
]),
},
}
}
@ -1709,6 +1760,7 @@ impl EngineConstants {
pub fn rebuild_path_list(&mut self, mod_path: Option<String>, season: Season, settings: &Settings) {
self.base_paths.clear();
self.base_paths.push("/".to_owned());
self.base_paths.push("/builtin/builtin_data/".to_owned());
if self.is_cs_plus {
self.base_paths.insert(0, "/base/".to_owned());

View File

@ -306,10 +306,17 @@ impl BackendEventLoop for SDL2EventLoop {
if game_controller.is_game_controller(which) {
let controller = game_controller.open(which).unwrap();
log::info!("Connected gamepad: {} (ID: {})", controller.name(), controller.instance_id());
let id = controller.instance_id();
log::info!("Connected gamepad: {} (ID: {})", controller.name(), id);
let axis_sensitivity = state.settings.get_gamepad_axis_sensitivity(which);
ctx.gamepad_context.add_gamepad(controller, axis_sensitivity);
unsafe {
let controller_type = sdl2_sys::SDL_GameControllerTypeForIndex(id as _);
ctx.gamepad_context.set_gamepad_type(id, controller_type);
}
}
}
Event::ControllerDeviceRemoved { which, .. } => {

View File

@ -3,7 +3,7 @@ use std::collections::{HashMap, HashSet};
use sdl2::controller::GameController;
use serde::{Deserialize, Serialize};
use crate::{framework::context::Context, settings::PlayerControllerInputType};
use crate::{common::Rect, engine_constants::EngineConstants, framework::context::Context};
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
#[repr(u32)]
@ -16,7 +16,13 @@ pub enum Axis {
TriggerRight,
}
#[derive(Clone, Debug)]
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,
@ -58,8 +64,32 @@ pub enum Button {
DPadRight,
}
impl Button {
pub fn get_rect(&self, offset: usize, constants: &EngineConstants) -> Rect<u16> {
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: GameController,
controller_type: Option<sdl2_sys::SDL_GameControllerType>,
left_x: f64,
left_y: f64,
@ -78,6 +108,7 @@ impl GamepadData {
pub(crate) fn new(game_controller: GameController, axis_sensitivity: f64) -> Self {
GamepadData {
controller: game_controller,
controller_type: None,
left_x: 0.0,
left_y: 0.0,
@ -92,6 +123,26 @@ impl GamepadData {
axis_values: HashMap::with_capacity(8),
}
}
pub(crate) fn set_gamepad_type(&mut self, controller_type: sdl2_sys::SDL_GameControllerType) {
self.controller_type = Some(controller_type);
}
pub(crate) fn get_gamepad_sprite_offset(&self) -> usize {
if let Some(controller_type) = self.controller_type {
return match controller_type {
sdl2_sys::SDL_GameControllerType::SDL_CONTROLLER_TYPE_PS3
| sdl2_sys::SDL_GameControllerType::SDL_CONTROLLER_TYPE_PS4
| sdl2_sys::SDL_GameControllerType::SDL_CONTROLLER_TYPE_PS5 => 0,
sdl2_sys::SDL_GameControllerType::SDL_CONTROLLER_TYPE_XBOX360
| sdl2_sys::SDL_GameControllerType::SDL_CONTROLLER_TYPE_XBOXONE => 1,
sdl2_sys::SDL_GameControllerType::SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO => 3,
_ => 1,
};
}
1
}
}
pub struct GamepadContext {
@ -123,6 +174,20 @@ impl GamepadContext {
self.gamepads.retain(|data| data.controller.instance_id() != gamepad_id);
}
pub(crate) fn set_gamepad_type(&mut self, gamepad_id: u32, controller_type: sdl2_sys::SDL_GameControllerType) {
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 {
@ -139,18 +204,15 @@ impl GamepadContext {
}
}
pub(crate) fn is_active(
&self,
gamepad_index: u32,
input_type: &PlayerControllerInputType,
axis_direction: AxisDirection,
) -> bool {
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) => self.is_axis_active(gamepad_index, *axis, axis_direction),
PlayerControllerInputType::Either(button, axis) => {
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)
|| self.is_axis_active(gamepad_index, *axis, *axis_direction)
}
}
}
@ -212,13 +274,16 @@ pub fn remove_gamepad(context: &mut Context, gamepad_id: u32) {
context.gamepad_context.remove_gamepad(gamepad_id);
}
pub fn is_active(
ctx: &Context,
gamepad_index: u32,
input_type: &PlayerControllerInputType,
axis_direction: AxisDirection,
) -> bool {
ctx.gamepad_context.is_active(gamepad_index, input_type, axis_direction)
pub fn set_gamepad_type(context: &mut Context, gamepad_id: u32, controller_type: sdl2_sys::SDL_GameControllerType) {
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 {

View File

@ -1,10 +1,10 @@
use crate::bitfield;
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::framework::gamepad::{self, AxisDirection, Button};
use crate::framework::gamepad::{self, Button, PlayerControllerInputType};
use crate::input::player_controller::PlayerController;
use crate::player::TargetPlayer;
use crate::shared_game_state::SharedGameState;
use crate::{bitfield, settings::PlayerControllerInputType};
bitfield! {
#[derive(Clone, Copy)]
@ -49,35 +49,24 @@ impl PlayerController for GamepadController {
TargetPlayer::Player2 => &state.settings.player2_controller_button_map,
};
self.state.set_up(gamepad::is_active(ctx, self.gamepad_id, &button_map.up, AxisDirection::Up));
self.state.set_down(gamepad::is_active(ctx, self.gamepad_id, &button_map.down, AxisDirection::Down));
self.state.set_left(gamepad::is_active(ctx, self.gamepad_id, &button_map.left, AxisDirection::Left));
self.state.set_right(gamepad::is_active(ctx, self.gamepad_id, &button_map.right, AxisDirection::Right));
self.state.set_map(gamepad::is_active(ctx, self.gamepad_id, &button_map.map, AxisDirection::None));
self.state.set_inventory(gamepad::is_active(ctx, self.gamepad_id, &button_map.inventory, AxisDirection::None));
self.state.set_jump(gamepad::is_active(ctx, self.gamepad_id, &button_map.jump, AxisDirection::None));
self.state.set_shoot(gamepad::is_active(ctx, self.gamepad_id, &button_map.shoot, AxisDirection::None));
self.state.set_next_weapon(gamepad::is_active(
ctx,
self.gamepad_id,
&button_map.next_weapon,
AxisDirection::None,
));
self.state.set_prev_weapon(gamepad::is_active(
ctx,
self.gamepad_id,
&button_map.prev_weapon,
AxisDirection::None,
));
self.state.set_up(gamepad::is_active(ctx, self.gamepad_id, &button_map.up));
self.state.set_down(gamepad::is_active(ctx, self.gamepad_id, &button_map.down));
self.state.set_left(gamepad::is_active(ctx, self.gamepad_id, &button_map.left));
self.state.set_right(gamepad::is_active(ctx, self.gamepad_id, &button_map.right));
self.state.set_map(gamepad::is_active(ctx, self.gamepad_id, &button_map.map));
self.state.set_inventory(gamepad::is_active(ctx, self.gamepad_id, &button_map.inventory));
self.state.set_jump(gamepad::is_active(ctx, self.gamepad_id, &button_map.jump));
self.state.set_shoot(gamepad::is_active(ctx, self.gamepad_id, &button_map.shoot));
self.state.set_next_weapon(gamepad::is_active(ctx, self.gamepad_id, &button_map.next_weapon));
self.state.set_prev_weapon(gamepad::is_active(ctx, self.gamepad_id, &button_map.prev_weapon));
self.state.set_escape(gamepad::is_active(
ctx,
self.gamepad_id,
&PlayerControllerInputType::ButtonInput(Button::Start),
AxisDirection::None,
));
self.state.set_enter(gamepad::is_active(ctx, self.gamepad_id, &button_map.jump, AxisDirection::None));
self.state.set_skip(gamepad::is_active(ctx, self.gamepad_id, &button_map.skip, AxisDirection::Either));
self.state.set_strafe(gamepad::is_active(ctx, self.gamepad_id, &button_map.strafe, AxisDirection::Either));
self.state.set_enter(gamepad::is_active(ctx, self.gamepad_id, &button_map.jump));
self.state.set_skip(gamepad::is_active(ctx, self.gamepad_id, &button_map.skip));
self.state.set_strafe(gamepad::is_active(ctx, self.gamepad_id, &button_map.strafe));
Ok(())
}

View File

@ -46,6 +46,7 @@ use crate::scene::title_scene::TitleScene;
use crate::scene::Scene;
use crate::scripting::tsc::credit_script::CreditScriptVM;
use crate::scripting::tsc::text_script::{ScriptMode, TextScriptExecutionState, TextScriptVM};
use crate::settings::ControllerType;
use crate::shared_game_state::{Language, PlayerCount, ReplayState, SharedGameState, TileSize};
use crate::stage::{BackgroundType, Stage, StageTexturePaths};
use crate::texture_set::SpriteBatch;
@ -2170,15 +2171,30 @@ impl Scene for GameScene {
self.text_boxes.draw(state, ctx, &self.frame)?;
if self.skip_counter > 1 || state.tutorial_counter > 0 {
let text = state.tt(
"game.cutscene_skip",
HashMap::from(if !state.settings.touch_controls {
[("key".to_owned(), format!("{:?}", state.settings.player1_key_map.inventory))]
let key = {
if state.settings.touch_controls {
">>".to_owned()
} else {
[("key".to_owned(), ">>".to_owned())]
}),
);
let width = state.font.text_width(text.chars(), &state.constants);
match state.settings.player1_controller_type {
ControllerType::Keyboard => format!("{:?}", state.settings.player1_key_map.skip),
ControllerType::Gamepad(_) => "=".to_owned(),
}
}
};
let text = state.tt("game.cutscene_skip", HashMap::from([("key".to_owned(), key)]));
let gamepad_sprite_offset = match state.settings.player1_controller_type {
ControllerType::Keyboard => 1,
ControllerType::Gamepad(index) => ctx.gamepad_context.get_gamepad_sprite_offset(index as usize),
};
let rect_map = HashMap::from([(
'=',
state.settings.player1_controller_button_map.skip.get_rect(gamepad_sprite_offset, &state.constants),
)]);
let width = state.font.text_width_with_rects(text.chars(), &rect_map, &state.constants);
let pos_x = state.canvas_size.0 - width - 20.0;
let pos_y = 0.0;
let line_height = state.font.line_height(&state.constants);
@ -2199,12 +2215,14 @@ impl Scene for GameScene {
rect.right = rect.left + (w * state.scale).ceil() as isize;
draw_rect(ctx, rect, Color::from_rgb(128, 128, 160))?;
state.font.draw_text_with_shadow(
state.font.draw_text_with_shadow_and_rects(
text.chars(),
pos_x + 10.0,
pos_y + 5.0,
&state.constants,
&mut state.texture_set,
&rect_map,
Some("buttons".into()),
ctx,
)?;
}

View File

@ -1,7 +1,7 @@
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::framework::filesystem::{user_create, user_open};
use crate::framework::gamepad::{Axis, Button};
use crate::framework::gamepad::{Axis, AxisDirection, Button, PlayerControllerInputType};
use crate::framework::keyboard::ScanCode;
use crate::graphics::VSyncMode;
use crate::input::gamepad_player_controller::GamepadController;
@ -79,7 +79,7 @@ fn default_true() -> bool {
#[inline(always)]
fn current_version() -> u32 {
13
14
}
#[inline(always)]
@ -215,6 +215,14 @@ impl Settings {
self.player2_controller_button_map = player_default_controller_button_map();
}
if self.version == 13 {
self.version = 14;
// reset controller mappings again since we have new enums
self.player1_controller_button_map = player_default_controller_button_map();
self.player2_controller_button_map = player_default_controller_button_map();
}
if self.version != initial_version {
log::info!("Upgraded configuration file from version {} to {}.", initial_version, self.version);
}
@ -355,13 +363,6 @@ pub enum ControllerType {
Gamepad(u32),
}
#[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,
@ -381,16 +382,16 @@ pub struct PlayerControllerButtonMap {
#[inline(always)]
pub fn player_default_controller_button_map() -> PlayerControllerButtonMap {
PlayerControllerButtonMap {
left: PlayerControllerInputType::Either(Button::DPadLeft, Axis::LeftX),
up: PlayerControllerInputType::Either(Button::DPadUp, Axis::LeftY),
right: PlayerControllerInputType::Either(Button::DPadRight, Axis::LeftX),
down: PlayerControllerInputType::Either(Button::DPadDown, Axis::LeftY),
left: PlayerControllerInputType::Either(Button::DPadLeft, Axis::LeftX, AxisDirection::Left),
up: PlayerControllerInputType::Either(Button::DPadUp, Axis::LeftY, AxisDirection::Up),
right: PlayerControllerInputType::Either(Button::DPadRight, Axis::LeftX, AxisDirection::Right),
down: PlayerControllerInputType::Either(Button::DPadDown, Axis::LeftY, AxisDirection::Down),
prev_weapon: PlayerControllerInputType::ButtonInput(Button::LeftShoulder),
next_weapon: PlayerControllerInputType::ButtonInput(Button::RightShoulder),
jump: PlayerControllerInputType::ButtonInput(Button::South),
shoot: PlayerControllerInputType::ButtonInput(Button::East),
skip: PlayerControllerInputType::AxisInput(Axis::TriggerLeft),
strafe: PlayerControllerInputType::AxisInput(Axis::TriggerRight),
skip: PlayerControllerInputType::AxisInput(Axis::TriggerLeft, AxisDirection::Either),
strafe: PlayerControllerInputType::AxisInput(Axis::TriggerRight, AxisDirection::Either),
inventory: PlayerControllerInputType::ButtonInput(Button::North),
map: PlayerControllerInputType::ButtonInput(Button::West),
}