Maelstrom: Center the ship spawn location

From 70600535d43cbe6605002a90986b86a0b68d1b5d Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 3 Apr 2026 08:44:09 -0700
Subject: [PATCH] Center the ship spawn location

---
 game/player.cpp | 34 +++++++++++++++-------------------
 game/player.h   |  8 ++++++++
 game/replay.h   |  2 +-
 3 files changed, 24 insertions(+), 20 deletions(-)

diff --git a/game/player.cpp b/game/player.cpp
index e05bae54..e8854afe 100644
--- a/game/player.cpp
+++ b/game/player.cpp
@@ -126,10 +126,7 @@ Player::NewWave(void)
 	AutoShield = SAFE_TIME;
 	WasShielded = 0;
 	Sphase = 0;
-	SetPos(
-		((GAME_WIDTH/2-((gGameInfo.GetNumPlayers()/2-Index)*(2*SPRITES_WIDTH)))*SCALE_FACTOR),
-		((GAME_HEIGHT/2)*SCALE_FACTOR)
-	);
+	SetSpawnPosition();
 	xvec = yvec = 0;
 	Thrusting = 0;
 	Braking = 0;
@@ -138,11 +135,11 @@ Player::NewWave(void)
 	Shooting = 0;
 	WasShooting = 0;
 	Rotating = 0;
-	CameraX = x;
-	CameraY = y;
 	phase = 0;
-	OBJ_LOOP(i, numshots)
+	OBJ_LOOP(i, numshots) {
 		KillShot(i);
+	}
+	UpdateCamera();
 
 	NoShieldsThisLevel = (ShieldLevel == 0);
 }
@@ -181,14 +178,13 @@ Player::NewShip(void)
 	phasetime = NO_PHASE_CHANGE;
 	Dead = 0;
 	Exploding = 0;
-	CameraX = x;
-	CameraY = y;
 	Set_TTL(-1);
 	if ( ! gGameInfo.IsDeathmatch() ) {
 		if (Lives > 0) {
 			--Lives;
 		}
 	}
+	UpdateCamera();
 
 	// In Kid Mode you automatically get air brakes
 	if ( gGameInfo.IsKidMode() ) {
@@ -281,10 +277,7 @@ int
 Player::BeenTimedOut(void)
 {
 	Exploding = 0;
-	SetPos(
-		((GAME_WIDTH/2-((gGameInfo.GetNumPlayers()/2-Index)*(2*SPRITES_WIDTH)))*SCALE_FACTOR),
-		((GAME_HEIGHT/2)*SCALE_FACTOR)
-	);
+	SetSpawnPosition();
 	if ( gGameInfo.IsDeathmatch() )
 		Dead = (DEAD_DELAY/2);
 	else
@@ -567,27 +560,30 @@ printf("\n");
 void
 Player::UpdateCamera()
 {
+	int centerX = (x + ((xsize / 2) << SPRITE_PRECISION));
+	int centerY = (y + ((ysize / 2) << SPRITE_PRECISION));
+
 	if ( Dead ) {
 		// Pan the camera over to our new position
 		const float CAMERA_SPEED = (float)(16 << SPRITE_PRECISION);
-		float deltaX = (float)(x - CameraX);
-		float deltaY = (float)(y - CameraY);
+		float deltaX = (float)(centerX - CameraX);
+		float deltaY = (float)(centerY - CameraY);
 		float length = SDL_sqrtf(deltaX * deltaX + deltaY * deltaY);
 		float velocityX = (deltaX / length) * CAMERA_SPEED;
 		float velocityY = (deltaY / length) * CAMERA_SPEED;
 		if (SDL_fabs(velocityX) < SDL_fabs(deltaX)) {
 			CameraX += (int)SDL_truncf(velocityX);
 		} else {
-			CameraX = x;
+			CameraX = centerX;
 		}
 		if (SDL_fabs(velocityY) < SDL_fabs(deltaY)) {
 			CameraY += (int)SDL_truncf(velocityY);
 		} else {
-			CameraY = y;
+			CameraY = centerY;
 		}
 	} else {
-		CameraX = x;
-		CameraY = y;
+		CameraX = centerX;
+		CameraY = centerY;
 	}
 }
 
diff --git a/game/player.h b/game/player.h
index fefa4fe8..51ea5d7a 100644
--- a/game/player.h
+++ b/game/player.h
@@ -167,6 +167,14 @@ class Player : public Object {
 	}
 	bool CanGetAchievement();
 
+	void SetSpawnPosition() {
+		int offset = gGameInfo.GetNumPlayers() == 2 ? SPRITES_WIDTH : 0;
+		SetPos(
+			(((GAME_WIDTH/2-((gGameInfo.GetNumPlayers()/2-Index)*(2*SPRITES_WIDTH)))+offset-SPRITES_WIDTH/2)*SCALE_FACTOR),
+			(((GAME_HEIGHT/2)-SPRITES_WIDTH/2)*SCALE_FACTOR) - SPRITES_WIDTH
+		);
+	}
+
 	void GetCameraPos(int *X, int *Y) {
 		*X = CameraX;
 		*Y = CameraY;
diff --git a/game/replay.h b/game/replay.h
index 68e45008..79cb6db7 100644
--- a/game/replay.h
+++ b/game/replay.h
@@ -33,7 +33,7 @@
 //
 // Examples of this would be changing the game play area, game logic, etc.
 //
-#define REPLAY_VERSION	1
+#define REPLAY_VERSION	2
 
 #define REPLAY_DIRECTORY "Games"
 #define REPLAY_FILETYPE "mreplay"