From 78005a94f59818da9bbd4a95229f2c244c0de63e Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 30 Mar 2026 05:41:55 -0700
Subject: [PATCH] Added an option to manually control air brakes
Note that this invalidates existing replays because of a new control inserted in the middle of the enum
---
Data/UI/game.xml | 10 ++++++++++
game/Maelstrom_Globals.h | 1 +
game/controls.cpp | 17 +++++++++++++++++
game/controls.h | 15 +++++++++------
game/game.cpp | 2 ++
game/gameinfo.cpp | 3 +++
game/gameinfo.h | 6 +++++-
game/main.cpp | 4 ++++
game/player.cpp | 40 ++++++++++++++++++++++++++++------------
game/player.h | 20 +++++++++++++++++++-
game/replay.h | 2 +-
11 files changed, 99 insertions(+), 21 deletions(-)
diff --git a/Data/UI/game.xml b/Data/UI/game.xml
index 5fcef5ac..93e7c86c 100644
--- a/Data/UI/game.xml
+++ b/Data/UI/game.xml
@@ -137,6 +137,11 @@
<Anchor anchorFrom="TOP" anchorTo="TOP"/>
<Action action_enter="CONTROL_DOWN_THRUST" action_leave="CONTROL_UP_THRUST"/>
</Thumbstick>
+ <Thumbstick name="brake">
+ <Size w="30" h="30"/>
+ <Anchor anchorFrom="BOTTOM" anchorTo="BOTTOM"/>
+ <Action action_enter="CONTROL_DOWN_BRAKE" action_leave="CONTROL_UP_BRAKE"/>
+ </Thumbstick>
</Elements>
</Image>
@@ -208,6 +213,11 @@
<Anchor anchorFrom="TOP" anchorTo="TOP"/>
<Action action_enter="CONTROL_DOWN_THRUST" action_leave="CONTROL_UP_THRUST"/>
</Thumbstick>
+ <Thumbstick name="brake">
+ <Size w="45" h="45"/>
+ <Anchor anchorFrom="BOTTOM" anchorTo="BOTTOM"/>
+ <Action action_enter="CONTROL_DOWN_BRAKE" action_leave="CONTROL_UP_BRAKE"/>
+ </Thumbstick>
</Elements>
</Image>
diff --git a/game/Maelstrom_Globals.h b/game/Maelstrom_Globals.h
index 69b239e0..23c8e683 100644
--- a/game/Maelstrom_Globals.h
+++ b/game/Maelstrom_Globals.h
@@ -82,6 +82,7 @@ extern void SetStar(int which);
// External variables...
// in main.cpp :
extern Bool gInitializing;
+extern Bool gControlBrakes;
extern Bool gNetworkAvailable;
extern Bool gUpdateBuffer;
extern Bool gRunning;
diff --git a/game/controls.cpp b/game/controls.cpp
index 9db9dc15..03975e70 100644
--- a/game/controls.cpp
+++ b/game/controls.cpp
@@ -34,6 +34,7 @@ Controls::Controls() :
gPauseControl("Controls.PauseControl", SDLK_PAUSE),
gShieldControl("Controls.ShieldControl", SDLK_SPACE),
gThrustControl("Controls.ThrustControl", SDLK_UP),
+ gBrakeControl("Controls.BrakeControl", SDLK_DOWN),
gTurnRControl("Controls.TurnRControl", SDLK_RIGHT),
gTurnLControl("Controls.TurnLControl", SDLK_LEFT),
gFireControl("Controls.FireControl", SDLK_TAB),
@@ -47,6 +48,7 @@ Controls::Bind(Prefs *prefs)
gPauseControl.Bind(prefs);
gShieldControl.Bind(prefs);
gThrustControl.Bind(prefs);
+ gBrakeControl.Bind(prefs);
gTurnRControl.Bind(prefs);
gTurnLControl.Bind(prefs);
gFireControl.Bind(prefs);
@@ -183,6 +185,8 @@ ControlsDialogDelegate::GetKeycode(int index)
return m_controls.gFireControl;
case THRUST_CTL:
return m_controls.gThrustControl;
+ case BRAKE_CTL:
+ return m_controls.gBrakeControl;
case SHIELD_CTL:
return m_controls.gShieldControl;
case TURNR_CTL:
@@ -208,6 +212,9 @@ ControlsDialogDelegate::SetKeycode(int index, SDL_Keycode keycode)
case THRUST_CTL:
m_controls.gThrustControl = keycode;
break;
+ case BRAKE_CTL:
+ m_controls.gBrakeControl = keycode;
+ break;
case SHIELD_CTL:
m_controls.gShieldControl = keycode;
break;
@@ -348,6 +355,12 @@ static void UpdateControl(Player *player)
SDL_GetGamepadAxis(gamepad->gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -16000) {
keys[THRUST_KEY] = true;
}
+
+ if (SDL_GetGamepadButton(gamepad->gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP) ||
+ SDL_GetGamepadAxis(gamepad->gamepad, SDL_GAMEPAD_AXIS_LEFTY) <= -16000 ||
+ SDL_GetGamepadAxis(gamepad->gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -16000) {
+ keys[BRAKE_KEY] = true;
+ }
}
if (!gamepad->sessionID) {
@@ -377,6 +390,9 @@ static void UpdateControl(Player *player)
if (keystate[SDL_GetScancodeFromKey(controls.gThrustControl, SDL_KMOD_NONE)]) {
keys[THRUST_KEY] = true;
}
+ if (keystate[SDL_GetScancodeFromKey(controls.gBrakeControl, SDL_KMOD_NONE)]) {
+ keys[BRAKE_KEY] = true;
+ }
}
player->SetControl(FIRE_KEY, keys[FIRE_KEY]);
@@ -384,6 +400,7 @@ static void UpdateControl(Player *player)
player->SetControl(LEFT_KEY, keys[LEFT_KEY]);
player->SetControl(RIGHT_KEY, keys[RIGHT_KEY]);
player->SetControl(THRUST_KEY, keys[THRUST_KEY]);
+ player->SetControl(BRAKE_KEY, keys[BRAKE_KEY]);
}
void HandleEvent(SDL_Event *event)
diff --git a/game/controls.h b/game/controls.h
index 7bd06383..d843abb5 100644
--- a/game/controls.h
+++ b/game/controls.h
@@ -37,12 +37,13 @@ extern void HandleEvent(SDL_Event *event);
/* Generic key control definitions */
#define THRUST_KEY 0x01
-#define RIGHT_KEY 0x02
-#define LEFT_KEY 0x03
-#define SHIELD_KEY 0x04
-#define FIRE_KEY 0x05
-#define PAUSE_KEY 0x06
-#define ABORT_KEY 0x07
+#define BRAKE_KEY 0x02
+#define RIGHT_KEY 0x03
+#define LEFT_KEY 0x04
+#define SHIELD_KEY 0x05
+#define FIRE_KEY 0x06
+#define PAUSE_KEY 0x07
+#define ABORT_KEY 0x08
/* The controls structure */
class Controls
@@ -56,6 +57,7 @@ class Controls
PrefsVariable<SDL_Keycode> gPauseControl;
PrefsVariable<SDL_Keycode> gShieldControl;
PrefsVariable<SDL_Keycode> gThrustControl;
+ PrefsVariable<SDL_Keycode> gBrakeControl;
PrefsVariable<SDL_Keycode> gTurnRControl;
PrefsVariable<SDL_Keycode> gTurnLControl;
PrefsVariable<SDL_Keycode> gFireControl;
@@ -92,6 +94,7 @@ class ControlsDialogDelegate : public UIDialogDelegate
enum {
FIRE_CTL,
THRUST_CTL,
+ BRAKE_CTL,
SHIELD_CTL,
TURNR_CTL,
TURNL_CTL,
diff --git a/game/game.cpp b/game/game.cpp
index 22ecdafc..cd81849f 100644
--- a/game/game.cpp
+++ b/game/game.cpp
@@ -545,6 +545,8 @@ GamePanelDelegate::OnAction(UIBaseElement *sender, const char *action)
unsigned char control;
if (SDL_strcasecmp(action, "THRUST") == 0) {
control = THRUST_KEY;
+ } else if (SDL_strcasecmp(action, "BRAKE") == 0) {
+ control = BRAKE_KEY;
} else if (SDL_strcasecmp(action, "RIGHT") == 0) {
control = RIGHT_KEY;
} else if (SDL_strcasecmp(action, "LEFT") == 0) {
diff --git a/game/gameinfo.cpp b/game/gameinfo.cpp
index 7de98e1e..8e328801 100644
--- a/game/gameinfo.cpp
+++ b/game/gameinfo.cpp
@@ -62,6 +62,9 @@ GameInfo::SetHost(Uint8 wave, Uint8 lives, Uint8 turbo, Uint8 deathMatch, bool k
if (kidMode) {
this->gameMode |= GAME_MODE_KIDS;
}
+ if (gControlBrakes) {
+ this->gameMode |= GAME_MODE_CONTROL_BRAKES;
+ }
this->deathMatch = deathMatch;
// We are the host node
diff --git a/game/gameinfo.h b/game/gameinfo.h
index ff311e13..0a680782 100644
--- a/game/gameinfo.h
+++ b/game/gameinfo.h
@@ -44,7 +44,8 @@ enum PLAYER_CONTROL {
};
enum GAME_MODE {
- GAME_MODE_KIDS = 0x01,
+ GAME_MODE_KIDS = 0x01,
+ GAME_MODE_CONTROL_BRAKES = 0x02,
};
#define IS_LOCAL_CONTROL(X) (X & CONTROL_LOCAL)
@@ -195,6 +196,9 @@ class GameInfo
bool IsKidMode() const {
return (gameMode & GAME_MODE_KIDS) != 0;
}
+ bool ControlBrakes() const {
+ return (gameMode & GAME_MODE_CONTROL_BRAKES) != 0;
+ }
void SetNodeState(int index, Uint8 state);
Uint8 GetNodeState(int index) const;
diff --git a/game/main.cpp b/game/main.cpp
index 40dae1b0..0bdac632 100644
--- a/game/main.cpp
+++ b/game/main.cpp
@@ -57,6 +57,7 @@ static const char *Version =
// Global variables set in this file...
Bool gInitializing = false;
+Bool gControlBrakes = false;
Bool gNetworkAvailable = false;
Bool gUpdateBuffer = false;
Bool gDelaySound = false;
@@ -140,6 +141,7 @@ void PrintUsage(const char *progname)
" --fullscreen # Run Maelstrom in full-screen mode\n"
" --windowed # Run Maelstrom in windowed mode\n"
" --geometry WxH # Set the window size to WxH\n"
+" --control-brakes # Allow manual brake control\n"
);
}
@@ -180,6 +182,8 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
PrintUsage(argv[0]);
return SDL_APP_FAILURE;
}
+ } else if ( strcmp(argv[i], "--control-brakes") == 0 ) {
+ gControlBrakes = true;
} else if ( strcmp(argv[i], "-NSDocumentRevisionsDebugMode") == 0 && argv[i+1] ) {
// Ignore Xcode debug option
++i;
diff --git a/game/player.cpp b/game/player.cpp
index 5859e162..2f5840a0 100644
--- a/game/player.cpp
+++ b/game/player.cpp
@@ -33,8 +33,11 @@
static void ThrustCallback(Uint8 theChannel)
{
for (int i = 0; i < MAX_PLAYERS; ++i) {
- if ( gPlayers[i]->IsThrusting() ) {
- sound->PlaySound(gThrusterSound,1,theChannel,ThrustCallback);
+ if ( !gPlayers[i]->IsValid() ) {
+ continue;
+ }
+ if ( gPlayers[i]->IsThrusting() || gPlayers[i]->IsManualBraking() ) {
+ sound->PlaySound(gThrusterSound, 1, theChannel, ThrustCallback);
break;
}
}
@@ -129,6 +132,7 @@ Player::NewWave(void)
);
xvec = yvec = 0;
Thrusting = 0;
+ Braking = 0;
NoThrust = 0;
ThrustBlit = gThrust1;
Shooting = 0;
@@ -165,8 +169,9 @@ Player::NewShip(void)
Sphase = 0;
xvec = yvec = 0;
Thrusting = 0;
- WasThrusting = 0;
ThrustBlit = gThrust1;
+ Braking = 0;
+ WasThrustingOrManualBraking = 0;
Shooting = 0;
WasShooting = 0;
Rotating = 0;
@@ -361,6 +366,7 @@ Player::Explode(void)
/* Finish our explosion */
Exploding = 1;
Thrusting = 0;
+ Braking = 0;
Shooting = 0;
ShieldOn = 0;
solid = 0;
@@ -463,7 +469,7 @@ printf("\n");
/* Update our status... :-) */
if ( Alive() && ! Exploding ) {
/* Airbrakes slow us down. :) */
- if ( special & AIR_BRAKES ) {
+ if ( IsBraking() ) {
if ( yvec > 0 )
--yvec;
else if ( yvec < 0 )
@@ -477,19 +483,23 @@ printf("\n");
/* Thrust speeds us up! :) */
if ( Thrusting ) {
- if ( ! WasThrusting ) {
- sound->PlaySound(gThrusterSound,
- 1, 3, ThrustCallback);
- WasThrusting = 1;
- }
-
/* -- The thrust key is down, increase the thrusters! */
if ( ! NoThrust ) {
Accelerate(gVelocityTable[phase].h,
gVelocityTable[phase].v);
}
- } else
- WasThrusting = 0;
+ }
+
+ if ( Thrusting || IsManualBraking() ) {
+ if ( ! WasThrustingOrManualBraking ) {
+ sound->PlaySound(gThrusterSound,
+ 1, 3, ThrustCallback);
+ WasThrustingOrManualBraking = 1;
+ }
+
+ } else {
+ WasThrustingOrManualBraking = 0;
+ }
/* Shoot baby, shoot. */
if ( Shooting ) {
@@ -600,6 +610,9 @@ Player::HandleKeys(void)
case THRUST_KEY:
Thrusting = 1;
break;
+ case BRAKE_KEY:
+ Braking = 1;
+ break;
case RIGHT_KEY:
Rotating |= 0x01;
break;
@@ -622,6 +635,9 @@ Player::HandleKeys(void)
if ( sound->Playing(gThrusterSound) )
sound->HaltSound(3);
break;
+ case BRAKE_KEY:
+ Braking = 0;
+ break;
case RIGHT_KEY:
Rotating &= ~0x01;
break;
diff --git a/game/player.h b/game/player.h
index 62215a23..c41cfe72 100644
--- a/game/player.h
+++ b/game/player.h
@@ -122,6 +122,23 @@ class Player : public Object {
virtual int IsThrusting(void) {
return(Thrusting);
}
+ int IsBraking() {
+ if ( GetSpecial( AIR_BRAKES ) ) {
+ if ( gGameInfo.ControlBrakes() ) {
+ return(Braking);
+ } else {
+ return(1);
+ }
+ } else {
+ return(0);
+ }
+ }
+ int IsManualBraking() {
+ if ( GetSpecial(AIR_BRAKES) && gGameInfo.ControlBrakes() ) {
+ return(Braking && (xvec || yvec));
+ }
+ return(0);
+ }
virtual void SetSpecial(unsigned char Spec) {
special |= Spec;
}
@@ -164,7 +181,8 @@ class Player : public Object {
int Thrusting;
int NoThrust;
Blit *ThrustBlit;
- int WasThrusting;
+ int Braking;
+ int WasThrustingOrManualBraking;
int Shooting;
int WasShooting;
int Rotating;
diff --git a/game/replay.h b/game/replay.h
index f5181028..7e5184b8 100644
--- a/game/replay.h
+++ b/game/replay.h
@@ -31,7 +31,7 @@
// Examples of this would be changing the game play area, game info structure,
// game logic, etc.
//
-#define REPLAY_VERSION 1
+#define REPLAY_VERSION 2
#define REPLAY_DIRECTORY "Games"
#define REPLAY_FILETYPE "mreplay"
#define LAST_REPLAY "LastGame." REPLAY_FILETYPE