add gamepad rumble
This commit is contained in:
parent
7b359ae4c1
commit
290068dd37
|
@ -3,8 +3,16 @@ use std::collections::{HashMap, HashSet};
|
|||
use sdl2::controller::GameController;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
use crate::{common::Rect, engine_constants::EngineConstants, framework::context::Context};
|
||||
|
||||
use crate::framework::error::GameResult;
|
||||
|
||||
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(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
|
||||
#[repr(u32)]
|
||||
pub enum Axis {
|
||||
|
@ -186,6 +194,12 @@ impl GamepadData {
|
|||
|
||||
name
|
||||
}
|
||||
|
||||
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);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GamepadContext {
|
||||
|
@ -209,6 +223,10 @@ impl GamepadContext {
|
|||
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: GameController, axis_sensitivity: f64) {
|
||||
self.gamepads.push(GamepadData::new(game_controller, axis_sensitivity));
|
||||
}
|
||||
|
@ -323,6 +341,35 @@ impl GamepadContext {
|
|||
|
||||
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 {
|
||||
|
@ -370,3 +417,45 @@ pub fn pressed_buttons(ctx: &Context, gamepad_index: u32) -> HashSet<Button> {
|
|||
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)
|
||||
}
|
||||
|
|
|
@ -166,4 +166,10 @@ impl PlayerController for CombinedPlayerController {
|
|||
fn move_analog_y(&self) -> f64 {
|
||||
self.controllers.iter().fold(0.0, |acc, cont| acc + cont.move_analog_y()) / self.controllers.len() as f64
|
||||
}
|
||||
|
||||
fn set_rumble(&mut self, low_freq: u16, hi_freq: u16, ticks: u32) {
|
||||
for cont in &mut self.controllers {
|
||||
cont.set_rumble(low_freq, hi_freq, ticks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -151,4 +151,6 @@ impl PlayerController for DummyPlayerController {
|
|||
fn move_analog_y(&self) -> f64 {
|
||||
0.0
|
||||
}
|
||||
|
||||
fn set_rumble(&mut self, _low_freq: u16, _hi_freq: u16, _ticks: u32) {}
|
||||
}
|
||||
|
|
|
@ -35,11 +35,32 @@ pub struct GamepadController {
|
|||
state: KeyState,
|
||||
old_state: KeyState,
|
||||
trigger: KeyState,
|
||||
rumble_state: Option<RumbleState>,
|
||||
rumble_enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RumbleState {
|
||||
pub low_freq: u16,
|
||||
pub hi_freq: u16,
|
||||
pub ticks: u32,
|
||||
}
|
||||
|
||||
impl GamepadController {
|
||||
pub fn new(gamepad_id: u32, target: TargetPlayer) -> GamepadController {
|
||||
GamepadController { gamepad_id, target, state: KeyState(0), old_state: KeyState(0), trigger: KeyState(0) }
|
||||
GamepadController {
|
||||
gamepad_id,
|
||||
target,
|
||||
state: KeyState(0),
|
||||
old_state: KeyState(0),
|
||||
trigger: KeyState(0),
|
||||
rumble_state: None,
|
||||
rumble_enabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_rumble_enabled(&mut self, enabled: bool) {
|
||||
self.rumble_enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,6 +91,18 @@ impl PlayerController for GamepadController {
|
|||
self.state.set_menu_ok(gamepad::is_active(ctx, self.gamepad_id, &button_map.menu_ok));
|
||||
self.state.set_menu_back(gamepad::is_active(ctx, self.gamepad_id, &button_map.menu_back));
|
||||
|
||||
if let Some(rumble_data) = &self.rumble_state {
|
||||
gamepad::set_rumble(
|
||||
ctx,
|
||||
state,
|
||||
self.gamepad_id,
|
||||
rumble_data.low_freq,
|
||||
rumble_data.hi_freq,
|
||||
rumble_data.ticks,
|
||||
)?;
|
||||
self.rumble_state = None;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -227,4 +260,16 @@ impl PlayerController for GamepadController {
|
|||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
fn set_rumble(&mut self, low_freq: u16, hi_freq: u16, ticks: u32) {
|
||||
if !self.rumble_enabled {
|
||||
return;
|
||||
}
|
||||
|
||||
if !self.rumble_state.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.rumble_state = Some(RumbleState { low_freq, hi_freq, ticks });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -225,4 +225,6 @@ impl PlayerController for KeyboardController {
|
|||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
fn set_rumble(&mut self, _low_freq: u16, _hi_freq: u16, _ticks: u32) {}
|
||||
}
|
||||
|
|
|
@ -97,6 +97,9 @@ pub trait PlayerController: PlayerControllerClone {
|
|||
/// Returns movement analog stick state in Y axis within (-1.0..=1.0) range
|
||||
/// In case of non-analog controllers this should return -1.0, 0.0 or 1.0, depending on keys pressed.
|
||||
fn move_analog_y(&self) -> f64;
|
||||
|
||||
/// Activates the rumble motors to the specified intensities for a given amount of time (in millis).
|
||||
fn set_rumble(&mut self, low_freq: u16, hi_freq: u16, ticks: u32);
|
||||
}
|
||||
|
||||
pub trait PlayerControllerClone {
|
||||
|
|
|
@ -206,4 +206,6 @@ impl PlayerController for ReplayController {
|
|||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
fn set_rumble(&mut self, _low_freq: u16, _hi_freq: u16, _ticks: u32) {}
|
||||
}
|
||||
|
|
|
@ -401,4 +401,8 @@ impl PlayerController for TouchPlayerController {
|
|||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
fn set_rumble(&mut self, _low_freq: u16, _hi_freq: u16, _ticks: u32) {
|
||||
// we could probably vibrate the phone? do mobile games do that?
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ enum MainMenuEntry {
|
|||
SelectedPlayer,
|
||||
Controller,
|
||||
Rebind,
|
||||
Rumble,
|
||||
Back,
|
||||
}
|
||||
|
||||
|
@ -210,6 +211,7 @@ impl ControlsMenu {
|
|||
self.main
|
||||
.push_entry(MainMenuEntry::Controller, MenuEntry::Active(state.t("menus.controls_menu.controller.entry")));
|
||||
self.main.push_entry(MainMenuEntry::Rebind, MenuEntry::Active(state.t("menus.controls_menu.rebind")));
|
||||
self.main.push_entry(MainMenuEntry::Rumble, MenuEntry::Hidden);
|
||||
self.main.push_entry(MainMenuEntry::Back, MenuEntry::Active(state.t("common.back")));
|
||||
|
||||
self.confirm_reset.push_entry(
|
||||
|
@ -417,14 +419,22 @@ impl ControlsMenu {
|
|||
Player::Player2 => state.settings.player2_controller_type,
|
||||
};
|
||||
|
||||
let rumble = match self.selected_player {
|
||||
Player::Player1 => state.settings.player1_rumble,
|
||||
Player::Player2 => state.settings.player2_rumble,
|
||||
};
|
||||
|
||||
if let ControllerType::Gamepad(index) = controller_type {
|
||||
if index as usize >= available_gamepads {
|
||||
self.selected_controller = ControllerType::Keyboard;
|
||||
self.main.set_entry(MainMenuEntry::Rumble, MenuEntry::Hidden);
|
||||
} else {
|
||||
self.selected_controller = controller_type;
|
||||
self.main.set_entry(MainMenuEntry::Rumble, MenuEntry::Toggle("Rumble".to_string(), rumble));
|
||||
}
|
||||
} else {
|
||||
self.selected_controller = controller_type;
|
||||
self.main.set_entry(MainMenuEntry::Rumble, MenuEntry::Hidden);
|
||||
}
|
||||
|
||||
match self.selected_controller {
|
||||
|
@ -887,6 +897,50 @@ impl ControlsMenu {
|
|||
MenuSelectionResult::Selected(MainMenuEntry::Rebind, _) => {
|
||||
self.current = CurrentMenu::RebindMenu;
|
||||
}
|
||||
MenuSelectionResult::Selected(MainMenuEntry::Rumble, toggle) => {
|
||||
if let MenuEntry::Toggle(_, value) = toggle {
|
||||
match self.selected_player {
|
||||
Player::Player1 => {
|
||||
state.settings.player1_rumble = !state.settings.player1_rumble;
|
||||
|
||||
if state.settings.player1_rumble {
|
||||
if let ControllerType::Gamepad(idx) = self.selected_controller {
|
||||
gamepad::set_rumble(
|
||||
ctx,
|
||||
state,
|
||||
idx,
|
||||
0,
|
||||
0x5000,
|
||||
(state.settings.timing_mode.get_tps() / 2) as u32,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
*value = state.settings.player1_rumble;
|
||||
}
|
||||
Player::Player2 => {
|
||||
state.settings.player2_rumble = !state.settings.player2_rumble;
|
||||
|
||||
if state.settings.player2_rumble {
|
||||
if let ControllerType::Gamepad(idx) = self.selected_controller {
|
||||
gamepad::set_rumble(
|
||||
ctx,
|
||||
state,
|
||||
idx,
|
||||
0,
|
||||
0x5000,
|
||||
(state.settings.timing_mode.get_tps() / 2) as u32,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
*value = state.settings.player2_rumble;
|
||||
}
|
||||
}
|
||||
|
||||
state.settings.save(ctx)?;
|
||||
}
|
||||
}
|
||||
MenuSelectionResult::Selected(MainMenuEntry::Back, _) | MenuSelectionResult::Canceled => exit_action(),
|
||||
_ => {}
|
||||
},
|
||||
|
|
|
@ -52,6 +52,7 @@ impl NPC {
|
|||
|
||||
state.sound_manager.play_sfx(26);
|
||||
state.quake_counter = 30;
|
||||
state.quake_rumble_counter = 30;
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
|
@ -151,6 +152,7 @@ impl NPC {
|
|||
|
||||
state.sound_manager.play_sfx(26);
|
||||
state.quake_counter = 30;
|
||||
state.quake_rumble_counter = 30;
|
||||
}
|
||||
}
|
||||
5 => {
|
||||
|
@ -276,6 +278,7 @@ impl NPC {
|
|||
self.spritesheet_id = 20; // NpcSym
|
||||
state.sound_manager.play_sfx(26);
|
||||
state.quake_counter = 30;
|
||||
state.quake_rumble_counter = 30;
|
||||
}
|
||||
}
|
||||
20 | 21 => {
|
||||
|
@ -474,6 +477,7 @@ impl NPC {
|
|||
if y <= 34 && stage.change_tile(x, y, 0) {
|
||||
state.sound_manager.play_sfx(44);
|
||||
state.super_quake_counter = 10;
|
||||
state.super_quake_rumble_counter = 10;
|
||||
|
||||
let mut npc = NPC::create(4, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
|
@ -499,6 +503,7 @@ impl NPC {
|
|||
if self.y < -32 * 0x200 {
|
||||
self.npc_type = 0;
|
||||
state.quake_counter = 30;
|
||||
state.quake_rumble_counter = 30;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
|
@ -555,6 +560,7 @@ impl NPC {
|
|||
state.sound_manager.play_sfx(12);
|
||||
state.sound_manager.play_sfx(26);
|
||||
state.quake_counter = 30;
|
||||
state.quake_rumble_counter = 30;
|
||||
|
||||
self.y += 0x1400;
|
||||
self.action_num = 1;
|
||||
|
@ -571,6 +577,7 @@ impl NPC {
|
|||
|
||||
state.sound_manager.play_sfx(26);
|
||||
state.quake_counter = 30;
|
||||
state.quake_rumble_counter = 30;
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
|
@ -751,6 +758,7 @@ impl NPC {
|
|||
state.sound_manager.play_sfx(25);
|
||||
state.sound_manager.play_sfx(26);
|
||||
state.quake_counter = 30;
|
||||
state.quake_rumble_counter = 30;
|
||||
|
||||
let mut npc_smoke = NPC::create(4, &state.npc_table);
|
||||
npc_smoke.cond.set_alive(true);
|
||||
|
@ -887,6 +895,7 @@ impl NPC {
|
|||
self.action_num = 9;
|
||||
self.anim_num = 8;
|
||||
state.quake_counter = 30;
|
||||
state.quake_rumble_counter = 30;
|
||||
state.sound_manager.play_sfx(26);
|
||||
}
|
||||
|
||||
|
@ -1107,6 +1116,7 @@ impl NPC {
|
|||
self.action_num = 9;
|
||||
self.anim_num = 8;
|
||||
state.quake_counter = 30;
|
||||
state.quake_rumble_counter = 30;
|
||||
state.sound_manager.play_sfx(26);
|
||||
}
|
||||
if self.action_counter > 7
|
||||
|
|
|
@ -658,6 +658,7 @@ impl NPC {
|
|||
{
|
||||
self.anim_num = 6;
|
||||
state.quake_counter = 10;
|
||||
state.quake_rumble_counter = 10;
|
||||
state.sound_manager.play_sfx(26);
|
||||
|
||||
player.damage(5, state, npc_list);
|
||||
|
@ -741,6 +742,7 @@ impl NPC {
|
|||
self.action_counter = 0;
|
||||
|
||||
state.quake_counter = 10;
|
||||
state.quake_rumble_counter = 10;
|
||||
state.sound_manager.play_sfx(26);
|
||||
}
|
||||
|
||||
|
@ -938,6 +940,7 @@ impl NPC {
|
|||
}
|
||||
|
||||
state.quake_counter = 2;
|
||||
state.quake_rumble_counter = 2;
|
||||
self.action_counter += 1;
|
||||
if self.action_counter % 6 == 3 {
|
||||
state.sound_manager.play_sfx(25);
|
||||
|
|
|
@ -64,6 +64,7 @@ impl NPC {
|
|||
if self.anim_num > 6 {
|
||||
self.anim_num = 5;
|
||||
state.quake_counter = 8;
|
||||
state.quake_rumble_counter = 8;
|
||||
|
||||
state.sound_manager.play_sfx(26);
|
||||
|
||||
|
@ -1069,6 +1070,7 @@ impl NPC {
|
|||
self.cond.set_explode_die(true);
|
||||
|
||||
state.quake_counter = 20;
|
||||
state.quake_rumble_counter = 20;
|
||||
state.sound_manager.play_sfx(35);
|
||||
npc_list.create_death_smoke(self.x, self.y, 0x10000, 100 as usize, state, &self.rng);
|
||||
}
|
||||
|
|
|
@ -118,6 +118,7 @@ impl NPC {
|
|||
|
||||
state.sound_manager.play_sfx(26);
|
||||
state.quake_counter = 30;
|
||||
state.quake_rumble_counter = 30;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
|
|
|
@ -809,6 +809,7 @@ impl NPC {
|
|||
self.cond.set_explode_die(true);
|
||||
|
||||
state.quake_counter = 10;
|
||||
state.quake_rumble_counter = 10;
|
||||
state.sound_manager.play_sfx(26);
|
||||
npc_list.create_death_smoke(self.x, self.y, 0x6000, 40 as usize, state, &self.rng);
|
||||
|
||||
|
|
|
@ -188,6 +188,7 @@ impl NPC {
|
|||
|
||||
state.sound_manager.play_sfx(26);
|
||||
state.quake_counter = 30;
|
||||
state.quake_rumble_counter = 30;
|
||||
|
||||
let mut npc = NPC::create(4, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
|
@ -459,6 +460,7 @@ impl NPC {
|
|||
self.action_num = 40;
|
||||
self.action_counter = 0;
|
||||
state.quake_counter = 20;
|
||||
state.quake_rumble_counter = 20;
|
||||
state.sound_manager.play_sfx(26);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -376,6 +376,7 @@ impl NPC {
|
|||
self.action_counter = 0;
|
||||
self.anim_num = 2;
|
||||
state.quake_counter = 10;
|
||||
state.quake_rumble_counter = 10;
|
||||
state.sound_manager.play_sfx(26);
|
||||
}
|
||||
}
|
||||
|
@ -396,6 +397,7 @@ impl NPC {
|
|||
self.action_counter = 0;
|
||||
self.anim_num = 2;
|
||||
state.quake_counter = 10;
|
||||
state.quake_rumble_counter = 10;
|
||||
state.sound_manager.play_sfx(72);
|
||||
self.create_xp_drop_custom(self.x, self.y, 19, state, npc_list);
|
||||
|
||||
|
|
|
@ -554,6 +554,7 @@ impl NPC {
|
|||
self.action_num = 3;
|
||||
self.action_counter = 0;
|
||||
state.quake_counter = 30;
|
||||
state.quake_rumble_counter = 30;
|
||||
|
||||
state.sound_manager.play_sfx(26);
|
||||
state.sound_manager.play_sfx(72);
|
||||
|
@ -747,6 +748,7 @@ impl NPC {
|
|||
}
|
||||
2 => {
|
||||
state.quake_counter = 2;
|
||||
state.quake_rumble_counter = 2;
|
||||
self.action_counter3 += 1;
|
||||
if self.action_counter3 > 240 {
|
||||
self.anim_num = 2;
|
||||
|
@ -891,6 +893,7 @@ impl NPC {
|
|||
if self.action_counter != 0 && self.flags.hit_bottom_wall() {
|
||||
state.sound_manager.play_sfx(35);
|
||||
state.quake_counter = 40;
|
||||
state.quake_rumble_counter = 40;
|
||||
self.action_num = 0;
|
||||
}
|
||||
|
||||
|
@ -1403,6 +1406,7 @@ impl NPC {
|
|||
state.sound_manager.play_sfx(26);
|
||||
}
|
||||
state.quake_counter = 20;
|
||||
state.quake_rumble_counter = 20;
|
||||
}
|
||||
20 => {
|
||||
let mut npc = NPC::create(4, &state.npc_table);
|
||||
|
|
|
@ -569,6 +569,7 @@ impl NPC {
|
|||
self.action_num = 221;
|
||||
self.action_counter = 0;
|
||||
state.quake_counter = 16;
|
||||
state.quake_rumble_counter = 16;
|
||||
state.sound_manager.play_sfx(26);
|
||||
self.damage = 4
|
||||
}
|
||||
|
@ -639,6 +640,7 @@ impl NPC {
|
|||
self.action_num = 331;
|
||||
self.action_counter = 0;
|
||||
state.quake_counter = 16;
|
||||
state.quake_rumble_counter = 16;
|
||||
state.sound_manager.play_sfx(26);
|
||||
}
|
||||
|
||||
|
@ -740,6 +742,7 @@ impl NPC {
|
|||
self.npc_flags.set_ignore_solidity(true);
|
||||
state.sound_manager.play_sfx(12);
|
||||
state.quake_counter = 10;
|
||||
state.quake_rumble_counter = 10;
|
||||
|
||||
let mut npc = NPC::create(4, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
|
|
|
@ -1013,6 +1013,7 @@ impl NPC {
|
|||
}
|
||||
|
||||
state.quake_counter = 10;
|
||||
state.quake_rumble_counter = 10;
|
||||
state.sound_manager.play_sfx(26);
|
||||
}
|
||||
|
||||
|
@ -1185,6 +1186,7 @@ impl NPC {
|
|||
self.action_num = 20;
|
||||
|
||||
state.quake_counter = 10;
|
||||
state.quake_rumble_counter = 10;
|
||||
state.sound_manager.play_sfx(26);
|
||||
|
||||
let mut npc = NPC::create(4, &state.npc_table);
|
||||
|
@ -1236,6 +1238,7 @@ impl NPC {
|
|||
self.action_num = 10;
|
||||
|
||||
state.quake_counter = 10;
|
||||
state.quake_rumble_counter = 10;
|
||||
state.sound_manager.play_sfx(26);
|
||||
|
||||
let mut npc = NPC::create(4, &state.npc_table);
|
||||
|
@ -1329,6 +1332,7 @@ impl NPC {
|
|||
self.action_num = 20;
|
||||
|
||||
state.quake_counter = 10;
|
||||
state.quake_rumble_counter = 10;
|
||||
state.sound_manager.play_sfx(26);
|
||||
|
||||
let mut npc = NPC::create(4, &state.npc_table);
|
||||
|
@ -1380,6 +1384,7 @@ impl NPC {
|
|||
self.action_num = 10;
|
||||
|
||||
state.quake_counter = 10;
|
||||
state.quake_rumble_counter = 10;
|
||||
state.sound_manager.play_sfx(26);
|
||||
|
||||
let mut npc = NPC::create(4, &state.npc_table);
|
||||
|
@ -1880,6 +1885,7 @@ impl NPC {
|
|||
}
|
||||
|
||||
state.quake_counter = 10;
|
||||
state.quake_rumble_counter = 10;
|
||||
state.sound_manager.play_sfx(26);
|
||||
}
|
||||
|
||||
|
@ -2085,6 +2091,7 @@ impl NPC {
|
|||
self.npc_flags.set_ignore_solidity(true);
|
||||
state.sound_manager.play_sfx(26);
|
||||
state.quake_counter = 10;
|
||||
state.quake_rumble_counter = 10;
|
||||
|
||||
let mut npc = NPC::create(4, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
|
|
|
@ -1061,6 +1061,7 @@ impl NPC {
|
|||
}
|
||||
|
||||
state.quake_counter = 20;
|
||||
state.quake_rumble_counter = 20;
|
||||
if self.action_counter % 8 == 0 {
|
||||
state.sound_manager.play_sfx(26);
|
||||
}
|
||||
|
|
|
@ -410,6 +410,7 @@ impl NPC {
|
|||
self.anim_num = 2;
|
||||
state.sound_manager.play_sfx(26);
|
||||
state.quake_counter = 20;
|
||||
state.quake_rumble_counter = 20;
|
||||
}
|
||||
}
|
||||
26 => {
|
||||
|
@ -479,6 +480,7 @@ impl NPC {
|
|||
self.anim_num = 2;
|
||||
state.sound_manager.play_sfx(26);
|
||||
state.quake_counter = 20;
|
||||
state.quake_rumble_counter = 20;
|
||||
}
|
||||
}
|
||||
102 => {
|
||||
|
|
|
@ -592,6 +592,7 @@ impl NPC {
|
|||
}
|
||||
|
||||
state.quake_counter = 2;
|
||||
state.quake_rumble_counter = 2;
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter % 20 == 0 {
|
||||
|
|
|
@ -177,6 +177,7 @@ impl BossNPC {
|
|||
}
|
||||
|
||||
state.quake_counter = 30;
|
||||
state.quake_rumble_counter = 30;
|
||||
state.sound_manager.play_sfx(26);
|
||||
}
|
||||
}
|
||||
|
@ -357,6 +358,7 @@ impl BossNPC {
|
|||
|
||||
state.sound_manager.play_sfx(26);
|
||||
state.quake_counter = 60;
|
||||
state.quake_rumble_counter = 60;
|
||||
}
|
||||
}
|
||||
130 | 131 => {
|
||||
|
@ -457,6 +459,7 @@ impl BossNPC {
|
|||
|
||||
state.sound_manager.play_sfx(26);
|
||||
state.quake_counter = 30;
|
||||
state.quake_rumble_counter = 30;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
|
|
@ -321,6 +321,7 @@ impl NPC {
|
|||
self.action_counter = 0;
|
||||
self.damage = 3;
|
||||
state.super_quake_counter = 10;
|
||||
state.super_quake_rumble_counter = 10;
|
||||
state.sound_manager.play_sfx(26);
|
||||
}
|
||||
|
||||
|
@ -392,6 +393,7 @@ impl NPC {
|
|||
let _ = npc_list.spawn(0x100, npc);
|
||||
|
||||
state.super_quake_counter = 10;
|
||||
state.super_quake_rumble_counter = 10;
|
||||
state.sound_manager.play_sfx(26);
|
||||
}
|
||||
|
||||
|
@ -950,6 +952,7 @@ impl NPC {
|
|||
self.npc_flags.set_ignore_solidity(true);
|
||||
state.sound_manager.play_sfx(12);
|
||||
state.quake_counter = 10;
|
||||
state.quake_rumble_counter = 10;
|
||||
|
||||
for _ in 0..4 {
|
||||
let mut npc = NPC::create(4, &state.npc_table);
|
||||
|
@ -1361,6 +1364,7 @@ impl NPC {
|
|||
if self.action_counter > 100 {
|
||||
self.action_counter = 0;
|
||||
state.quake_counter = 20;
|
||||
state.quake_rumble_counter = 20;
|
||||
state.sound_manager.play_sfx(26);
|
||||
state.sound_manager.play_sfx(12);
|
||||
|
||||
|
@ -1481,6 +1485,7 @@ impl BossNPC {
|
|||
self.parts[0].action_num = 103;
|
||||
self.parts[0].action_counter = 0;
|
||||
state.super_quake_counter = 30;
|
||||
state.super_quake_rumble_counter = 30;
|
||||
state.sound_manager.play_sfx(44);
|
||||
|
||||
if player.y > self.parts[0].y + 0x6000
|
||||
|
@ -1563,6 +1568,7 @@ impl BossNPC {
|
|||
self.parts[0].action_num = 201;
|
||||
self.parts[0].action_counter = 0;
|
||||
state.super_quake_counter = 30;
|
||||
state.super_quake_rumble_counter = 30;
|
||||
state.sound_manager.play_sfx(26);
|
||||
state.sound_manager.play_sfx(44);
|
||||
|
||||
|
@ -1619,6 +1625,7 @@ impl BossNPC {
|
|||
self.parts[0].action_num = 222;
|
||||
self.parts[0].action_counter = 0;
|
||||
state.super_quake_counter = 30;
|
||||
state.super_quake_rumble_counter = 30;
|
||||
state.sound_manager.play_sfx(26);
|
||||
|
||||
for _ in 0..16 {
|
||||
|
@ -1807,6 +1814,7 @@ impl BossNPC {
|
|||
self.parts[0].action_counter = 0;
|
||||
self.parts[0].anim_counter = 0;
|
||||
state.super_quake_counter = 30;
|
||||
state.super_quake_rumble_counter = 30;
|
||||
state.sound_manager.play_sfx(35);
|
||||
|
||||
self.parts[1].action_num = 102;
|
||||
|
@ -1873,6 +1881,7 @@ impl BossNPC {
|
|||
}
|
||||
1002 => {
|
||||
state.super_quake_counter = 40;
|
||||
state.super_quake_rumble_counter = 30;
|
||||
|
||||
self.parts[0].action_counter += 1;
|
||||
|
||||
|
|
|
@ -279,6 +279,7 @@ impl BossNPC {
|
|||
state.sound_manager.loop_sfx_freq(40, 1000.0 / 2205.0);
|
||||
state.sound_manager.loop_sfx_freq(41, 1100.0 / 2205.0);
|
||||
state.quake_counter = 100;
|
||||
state.quake_rumble_counter = 100;
|
||||
state.npc_super_pos.1 = 1;
|
||||
}
|
||||
|
||||
|
@ -347,6 +348,7 @@ impl BossNPC {
|
|||
self.parts[7].action_num = 200;
|
||||
|
||||
state.quake_counter = 20;
|
||||
state.quake_rumble_counter = 20;
|
||||
|
||||
state.sound_manager.stop_sfx(40);
|
||||
state.sound_manager.stop_sfx(41);
|
||||
|
@ -406,6 +408,7 @@ impl BossNPC {
|
|||
|
||||
if flag {
|
||||
state.quake_counter = 20;
|
||||
state.quake_rumble_counter = 20;
|
||||
state.sound_manager.play_sfx(26);
|
||||
|
||||
self.parts[1].action_num = 100;
|
||||
|
|
|
@ -295,6 +295,7 @@ impl BossNPC {
|
|||
self.parts[0].target_x = self.parts[0].x;
|
||||
self.parts[0].target_y = self.parts[0].y;
|
||||
state.quake_counter = 20;
|
||||
state.quake_rumble_counter = 20;
|
||||
for _ in 0..32 {
|
||||
let mut npc = NPC::create(4, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
|
|
|
@ -456,6 +456,7 @@ impl BossNPC {
|
|||
}
|
||||
1000 => {
|
||||
state.quake_counter = 2;
|
||||
state.quake_rumble_counter = 2;
|
||||
|
||||
self.parts[0].action_counter += 1;
|
||||
if self.parts[0].action_counter % 8 == 0 {
|
||||
|
@ -477,6 +478,7 @@ impl BossNPC {
|
|||
}
|
||||
1001 => {
|
||||
state.quake_counter = 40;
|
||||
state.quake_rumble_counter = 40;
|
||||
self.parts[0].action_counter += 1;
|
||||
|
||||
if self.parts[0].action_counter > 50 {
|
||||
|
|
|
@ -116,6 +116,7 @@ impl BossNPC {
|
|||
self.parts[0].y -= 0x200;
|
||||
self.parts[0].action_counter += 1;
|
||||
state.quake_counter = 2;
|
||||
state.quake_rumble_counter = 2;
|
||||
|
||||
if self.parts[0].action_counter % 4 == 0 {
|
||||
state.sound_manager.play_sfx(26);
|
||||
|
@ -215,6 +216,8 @@ impl BossNPC {
|
|||
}
|
||||
90 => {
|
||||
state.quake_counter = 2;
|
||||
state.quake_rumble_counter = 2;
|
||||
|
||||
self.parts[0].y += 0x200;
|
||||
self.parts[0].action_counter += 1;
|
||||
|
||||
|
@ -321,10 +324,12 @@ impl BossNPC {
|
|||
state.sound_manager.play_sfx(26);
|
||||
state.sound_manager.play_sfx(12);
|
||||
state.quake_counter = 30;
|
||||
state.quake_rumble_counter = 30;
|
||||
}
|
||||
}
|
||||
150 => {
|
||||
state.quake_counter = 2;
|
||||
state.quake_rumble_counter = 2;
|
||||
|
||||
self.parts[0].action_counter += 1;
|
||||
if self.parts[0].action_counter % 12 == 0 {
|
||||
|
@ -345,6 +350,7 @@ impl BossNPC {
|
|||
}
|
||||
160 => {
|
||||
state.quake_counter = 40;
|
||||
state.quake_rumble_counter = 40;
|
||||
|
||||
self.parts[0].action_counter += 1;
|
||||
if self.parts[0].action_counter > 50 {
|
||||
|
|
|
@ -526,6 +526,7 @@ impl BossNPC {
|
|||
self.parts[19].action_counter = self.parts[0].life;
|
||||
|
||||
state.quake_counter = 100;
|
||||
state.quake_rumble_counter = 100;
|
||||
}
|
||||
|
||||
self.parts[0].action_counter += 1;
|
||||
|
@ -665,6 +666,7 @@ impl BossNPC {
|
|||
self.parts[7].action_num = 5;
|
||||
|
||||
state.quake_counter = 20;
|
||||
state.quake_rumble_counter = 20;
|
||||
|
||||
let mut npc = NPC::create(4, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
|
@ -709,6 +711,7 @@ impl BossNPC {
|
|||
}
|
||||
1000 => {
|
||||
state.quake_counter = 100;
|
||||
state.quake_rumble_counter = 100;
|
||||
|
||||
self.parts[0].action_counter += 1;
|
||||
if self.parts[0].action_counter % 8 == 0 {
|
||||
|
@ -734,6 +737,8 @@ impl BossNPC {
|
|||
}
|
||||
1001 => {
|
||||
state.quake_counter = 40;
|
||||
state.quake_rumble_counter = 40;
|
||||
|
||||
self.parts[0].action_counter += 1;
|
||||
if self.parts[0].action_counter > 50 {
|
||||
for i in 0..20 {
|
||||
|
@ -749,6 +754,8 @@ impl BossNPC {
|
|||
|
||||
if v19 {
|
||||
state.quake_counter = 20;
|
||||
state.quake_rumble_counter = 20;
|
||||
|
||||
state.sound_manager.play_sfx(26);
|
||||
|
||||
if self.parts[0].action_num == 201 {
|
||||
|
|
|
@ -889,6 +889,10 @@ impl Player {
|
|||
self.stars -= 1;
|
||||
}
|
||||
|
||||
let rumble_intensity =
|
||||
(0x4000 + ((1.0 - (self.life as f32 / self.max_life as f32)) * 0x2000 as f32) as i32).min(0xFFFF) as u16;
|
||||
self.controller.set_rumble(rumble_intensity, rumble_intensity, 20);
|
||||
|
||||
self.damage = self.damage.saturating_add(final_hp as u16);
|
||||
if self.popup.value > 0 {
|
||||
self.popup.set_value(-(self.damage as i16));
|
||||
|
|
|
@ -31,7 +31,7 @@ use crate::framework::context::Context;
|
|||
use crate::framework::error::GameResult;
|
||||
use crate::framework::graphics::{draw_rect, BlendMode, FilterMode};
|
||||
use crate::framework::ui::Components;
|
||||
use crate::framework::{filesystem, graphics};
|
||||
use crate::framework::{filesystem, gamepad, graphics};
|
||||
use crate::input::touch_controls::TouchControlType;
|
||||
use crate::inventory::{Inventory, TakeExperienceResult};
|
||||
use crate::map::WaterParams;
|
||||
|
@ -1846,6 +1846,16 @@ impl Scene for GameScene {
|
|||
}
|
||||
}
|
||||
|
||||
if state.quake_rumble_counter > 0 {
|
||||
gamepad::set_quake_rumble_all(ctx, state, state.quake_rumble_counter)?;
|
||||
state.quake_rumble_counter = 0;
|
||||
}
|
||||
|
||||
if state.super_quake_rumble_counter > 0 {
|
||||
gamepad::set_super_quake_rumble_all(ctx, state, state.super_quake_rumble_counter)?;
|
||||
state.super_quake_rumble_counter = 0;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -1260,6 +1260,7 @@ impl TextScriptVM {
|
|||
let count = read_cur_varint(&mut cursor)? as u16;
|
||||
|
||||
state.quake_counter = count;
|
||||
state.quake_rumble_counter = count as u32;
|
||||
|
||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
}
|
||||
|
|
|
@ -55,6 +55,10 @@ pub struct Settings {
|
|||
pub player1_controller_axis_sensitivity: f64,
|
||||
#[serde(default = "default_controller_axis_sensitivity")]
|
||||
pub player2_controller_axis_sensitivity: f64,
|
||||
#[serde(default = "default_rumble")]
|
||||
pub player1_rumble: bool,
|
||||
#[serde(default = "default_rumble")]
|
||||
pub player2_rumble: bool,
|
||||
#[serde(skip, default = "default_speed")]
|
||||
pub speed: f64,
|
||||
#[serde(skip)]
|
||||
|
@ -82,7 +86,7 @@ fn default_true() -> bool {
|
|||
|
||||
#[inline(always)]
|
||||
fn current_version() -> u32 {
|
||||
17
|
||||
18
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -135,6 +139,11 @@ fn default_pause_on_focus_loss() -> bool {
|
|||
true
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn default_rumble() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
pub fn load(ctx: &Context) -> GameResult<Settings> {
|
||||
if let Ok(file) = user_open(ctx, "/settings.json") {
|
||||
|
@ -270,6 +279,12 @@ impl Settings {
|
|||
}
|
||||
}
|
||||
|
||||
if self.version == 17 {
|
||||
self.version = 18;
|
||||
self.player1_rumble = default_rumble();
|
||||
self.player2_rumble = default_rumble();
|
||||
}
|
||||
|
||||
if self.version != initial_version {
|
||||
log::info!("Upgraded configuration file from version {} to {}.", initial_version, self.version);
|
||||
}
|
||||
|
@ -293,7 +308,9 @@ impl Settings {
|
|||
ControllerType::Keyboard => Box::new(KeyboardController::new(TargetPlayer::Player1)),
|
||||
ControllerType::Gamepad(index) => {
|
||||
let keyboard_controller = Box::new(KeyboardController::new(TargetPlayer::Player1));
|
||||
let gamepad_controller = Box::new(GamepadController::new(index, TargetPlayer::Player1));
|
||||
|
||||
let mut gamepad_controller = Box::new(GamepadController::new(index, TargetPlayer::Player1));
|
||||
gamepad_controller.set_rumble_enabled(self.player1_rumble);
|
||||
|
||||
let mut combined_player_controller = CombinedPlayerController::new();
|
||||
combined_player_controller.add(keyboard_controller);
|
||||
|
@ -309,7 +326,9 @@ impl Settings {
|
|||
ControllerType::Keyboard => Box::new(KeyboardController::new(TargetPlayer::Player2)),
|
||||
ControllerType::Gamepad(index) => {
|
||||
let keyboard_controller = Box::new(KeyboardController::new(TargetPlayer::Player2));
|
||||
let gamepad_controller = Box::new(GamepadController::new(index, TargetPlayer::Player2));
|
||||
|
||||
let mut gamepad_controller = Box::new(GamepadController::new(index, TargetPlayer::Player2));
|
||||
gamepad_controller.set_rumble_enabled(self.player2_rumble);
|
||||
|
||||
let mut combined_player_controller = CombinedPlayerController::new();
|
||||
combined_player_controller.add(keyboard_controller);
|
||||
|
@ -356,6 +375,8 @@ impl Default for Settings {
|
|||
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(),
|
||||
player1_rumble: default_rumble(),
|
||||
player2_rumble: default_rumble(),
|
||||
speed: 1.0,
|
||||
god_mode: false,
|
||||
infinite_booster: false,
|
||||
|
|
|
@ -285,6 +285,8 @@ pub struct SharedGameState {
|
|||
pub tile_size: TileSize,
|
||||
pub quake_counter: u16,
|
||||
pub super_quake_counter: u16,
|
||||
pub quake_rumble_counter: u32,
|
||||
pub super_quake_rumble_counter: u32,
|
||||
pub teleporter_slots: Vec<(u16, u16)>,
|
||||
pub carets: Vec<Caret>,
|
||||
pub touch_controls: TouchControls,
|
||||
|
@ -422,6 +424,8 @@ impl SharedGameState {
|
|||
tile_size: TileSize::Tile16x16,
|
||||
quake_counter: 0,
|
||||
super_quake_counter: 0,
|
||||
quake_rumble_counter: 0,
|
||||
super_quake_rumble_counter: 0,
|
||||
teleporter_slots: Vec::with_capacity(8),
|
||||
carets: Vec::with_capacity(32),
|
||||
touch_controls: TouchControls::new(),
|
||||
|
|
Loading…
Reference in New Issue