Skip to content

Commit 6850279

Browse files
committed
Android is better now
1 parent de851ad commit 6850279

7 files changed

Lines changed: 116 additions & 50 deletions

File tree

core/src/main/java/com/codeheadsystems/mazer/MazerGame.java

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,23 @@
22

33
import com.badlogic.gdx.Game;
44
import com.badlogic.gdx.Gdx;
5+
import com.badlogic.gdx.Preferences;
56
import com.codeheadsystems.mazer.platform.DefaultPlatformServices;
67
import com.codeheadsystems.mazer.platform.PlatformServices;
78
import com.codeheadsystems.mazer.screen.MenuScreen;
89

910
/**
10-
* Main game entry point. Manages screen transitions and Android lifecycle.
11+
* Main game entry point. Manages screen transitions, Android lifecycle,
12+
* and persistent user preferences.
1113
*/
1214
public class MazerGame extends Game {
1315

16+
private static final String PREFS_NAME = "mazer-prefs";
17+
private static final String PREF_PLAYER_NAME = "playerName";
18+
private static final String PREF_HOST_IP = "hostIp";
19+
1420
private final PlatformServices platformServices;
21+
private Preferences prefs;
1522

1623
public MazerGame() {
1724
this(new DefaultPlatformServices());
@@ -25,20 +32,37 @@ public PlatformServices getPlatformServices() {
2532
return platformServices;
2633
}
2734

35+
public String getLastPlayerName() {
36+
return prefs.getString(PREF_PLAYER_NAME, "Player");
37+
}
38+
39+
public void setLastPlayerName(String name) {
40+
prefs.putString(PREF_PLAYER_NAME, name);
41+
prefs.flush();
42+
}
43+
44+
public String getLastHostIp() {
45+
return prefs.getString(PREF_HOST_IP, "192.168.1.");
46+
}
47+
48+
public void setLastHostIp(String ip) {
49+
prefs.putString(PREF_HOST_IP, ip);
50+
prefs.flush();
51+
}
52+
2853
@Override
2954
public void create() {
55+
prefs = Gdx.app.getPreferences(PREFS_NAME);
3056
setScreen(new MenuScreen(this));
3157
}
3258

3359
@Override
3460
public void pause() {
3561
super.pause();
36-
Gdx.app.log("MazerGame", "Game paused");
3762
}
3863

3964
@Override
4065
public void resume() {
4166
super.resume();
42-
Gdx.app.log("MazerGame", "Game resumed");
4367
}
4468
}

core/src/main/java/com/codeheadsystems/mazer/net/HostServer.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,12 @@ public void startGame(int mazeWidth, int mazeHeight) {
124124
maze = MazeGenerator.generate(mazeWidth, mazeHeight, CELL_SIZE, seed);
125125

126126
List<Protocol.PlayerInfo> players = new ArrayList<>(playerInfoMap.values());
127+
// Sort by ID for deterministic ordering
128+
players.sort((a, b) -> Integer.compare(a.id, b.id));
127129
Random random = new Random(seed + 42);
128130

131+
int[] playerIds = new int[players.size()];
132+
int[] shapeIndices = new int[players.size()];
129133
float[] spawnX = new float[players.size()];
130134
float[] spawnZ = new float[players.size()];
131135
float[] spawnAngle = new float[players.size()];
@@ -149,6 +153,8 @@ public void startGame(int mazeWidth, int mazeHeight) {
149153
usedCells.add(new int[]{cx, cy});
150154

151155
Vector2 pos = maze.cellToWorld(cx, cy);
156+
playerIds[i] = players.get(i).id;
157+
shapeIndices[i] = players.get(i).shapeIndex;
152158
spawnX[i] = pos.x;
153159
spawnZ[i] = pos.y;
154160
spawnAngle[i] = random.nextFloat() * MathUtils.PI2;
@@ -158,6 +164,8 @@ public void startGame(int mazeWidth, int mazeHeight) {
158164
msg.mazeSeed = seed;
159165
msg.mazeWidth = mazeWidth;
160166
msg.mazeHeight = mazeHeight;
167+
msg.playerIds = playerIds;
168+
msg.shapeIndices = shapeIndices;
161169
msg.spawnX = spawnX;
162170
msg.spawnZ = spawnZ;
163171
msg.spawnAngle = spawnAngle;

core/src/main/java/com/codeheadsystems/mazer/net/Protocol.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public static void register(Kryo kryo) {
2424
kryo.register(ReadyToggle.class);
2525
kryo.register(StartGame.class);
2626
kryo.register(float[].class);
27+
kryo.register(int[].class);
2728
kryo.register(PlayerInput.class);
2829
kryo.register(GameSnapshot.class);
2930
kryo.register(PlayerSnapshot.class);
@@ -72,6 +73,8 @@ public static class StartGame {
7273
public long mazeSeed;
7374
public int mazeWidth;
7475
public int mazeHeight;
76+
public int[] playerIds;
77+
public int[] shapeIndices;
7578
public float[] spawnX;
7679
public float[] spawnZ;
7780
public float[] spawnAngle;

core/src/main/java/com/codeheadsystems/mazer/screen/CountdownScreen.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,14 @@ public void show() {
5858

5959
// Position camera at local player's spawn
6060
int localId = networkManager.getLocalPlayerId();
61-
if (localId >= 0 && localId < startMsg.spawnX.length) {
62-
mazeRenderer.updateCamera(
63-
startMsg.spawnX[localId],
64-
startMsg.spawnZ[localId],
65-
startMsg.spawnAngle[localId]);
61+
for (int i = 0; i < startMsg.playerIds.length; i++) {
62+
if (startMsg.playerIds[i] == localId) {
63+
mazeRenderer.updateCamera(
64+
startMsg.spawnX[i],
65+
startMsg.spawnZ[i],
66+
startMsg.spawnAngle[i]);
67+
break;
68+
}
6669
}
6770
}
6871

core/src/main/java/com/codeheadsystems/mazer/screen/LobbyScreen.java

Lines changed: 14 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -66,56 +66,46 @@ public void show() {
6666
statusLabel = new Label("Waiting for players...", skin);
6767
root.add(statusLabel).padBottom(20).colspan(2).row();
6868

69-
// Two-column layout: player list on left, QR code on right (host only)
69+
// Single-column layout (works for portrait and landscape)
7070
Table contentTable = new Table();
7171

72-
// Player list
73-
playerListTable = new Table();
74-
playerListTable.top().left();
75-
contentTable.add(playerListTable).width(400).top().padRight(30);
76-
77-
// QR code (host only)
72+
// QR code / IP (host only, show first so joiners see it)
7873
if (networkManager.isHost()) {
7974
String connectString = networkManager.getConnectString();
8075
if (connectString != null) {
81-
Table qrTable = new Table();
82-
83-
// Extract just the IP from the connect string
8476
String ip = connectString.replace("mazer://", "").split(":")[0];
8577

8678
Label ipHeaderLabel = new Label("Join IP:", skin);
87-
qrTable.add(ipHeaderLabel).padBottom(5).row();
79+
contentTable.add(ipHeaderLabel).padBottom(3).row();
8880

8981
Label ipLabel = new Label(ip, skin, "title");
90-
qrTable.add(ipLabel).padBottom(15).row();
91-
92-
Label qrLabel = new Label("Or scan QR code:", skin);
93-
qrTable.add(qrLabel).padBottom(5).row();
82+
contentTable.add(ipLabel).padBottom(10).row();
9483

9584
qrTexture = QrCodeUtil.generateQrTexture(connectString, 200);
9685
if (qrTexture != null) {
9786
Image qrImage = new Image(new TextureRegionDrawable(
9887
new TextureRegion(qrTexture)));
99-
qrTable.add(qrImage).size(150).padBottom(5).row();
88+
contentTable.add(qrImage).size(120).padBottom(10).row();
10089
}
101-
102-
contentTable.add(qrTable).top();
10390
}
10491
}
10592

106-
root.add(contentTable).padBottom(30).colspan(2).row();
93+
// Player list
94+
playerListTable = new Table();
95+
playerListTable.top();
96+
contentTable.add(playerListTable).fillX().padBottom(10).row();
10797

108-
// Buttons
109-
Table buttonTable = new Table();
98+
root.add(contentTable).padBottom(30).colspan(2).row();
11099

100+
// Buttons (stacked vertically for portrait compatibility)
111101
TextButton readyButton = new TextButton("READY", skin);
112102
readyButton.addListener(new ClickListener() {
113103
@Override
114104
public void clicked(InputEvent event, float x, float y) {
115105
networkManager.toggleReady();
116106
}
117107
});
118-
buttonTable.add(readyButton).width(200).height(45).padRight(15);
108+
root.add(readyButton).width(250).height(45).padBottom(10).row();
119109

120110
if (networkManager.isHost()) {
121111
startButton = new TextButton("START GAME", skin);
@@ -128,7 +118,7 @@ public void clicked(InputEvent event, float x, float y) {
128118
}
129119
}
130120
});
131-
buttonTable.add(startButton).width(200).height(45).padRight(15);
121+
root.add(startButton).width(250).height(45).padBottom(10).row();
132122
}
133123

134124
TextButton backButton = new TextButton("LEAVE", skin);
@@ -139,9 +129,7 @@ public void clicked(InputEvent event, float x, float y) {
139129
game.setScreen(new MenuScreen(game));
140130
}
141131
});
142-
buttonTable.add(backButton).width(200).height(45);
143-
144-
root.add(buttonTable).colspan(2).row();
132+
root.add(backButton).width(250).height(45).padBottom(10).row();
145133

146134
// Set up network callbacks
147135
networkManager.setOnLobbyUpdate(this::onLobbyUpdate);

core/src/main/java/com/codeheadsystems/mazer/screen/MenuScreen.java

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ public class MenuScreen extends ScreenAdapter {
3030
private final MazerGame game;
3131
private Stage stage;
3232
private Skin skin;
33+
private TextField nameField;
34+
private TextField ipField;
3335

3436
public MenuScreen(MazerGame game) {
3537
this.game = game;
@@ -64,13 +66,25 @@ public void show() {
6466

6567
SelectBox<String> shapeSelect = new SelectBox<>(skin);
6668
shapeSelect.setItems("Cube", "Sphere", "Eyeball");
67-
root.add(shapeSelect).width(200).padBottom(30).row();
69+
root.add(shapeSelect).width(200).padBottom(15).row();
70+
71+
// Name field (declared early so all button handlers can access it)
72+
Label nameLabel = new Label("Name:", skin);
73+
root.add(nameLabel).padBottom(5).row();
74+
75+
nameField = new TextField(game.getLastPlayerName(), skin);
76+
root.add(nameField).width(200).padBottom(15).row();
77+
78+
// IP field (declared early so all button handlers can access it)
79+
ipField = new TextField(game.getLastHostIp(), skin);
6880

6981
// Solo button
7082
TextButton soloButton = new TextButton("SOLO PLAY", skin);
7183
soloButton.addListener(new ClickListener() {
7284
@Override
7385
public void clicked(InputEvent event, float x, float y) {
86+
game.setLastPlayerName(nameField.getText());
87+
game.setLastHostIp(ipField.getText());
7488
int[] size = parseMazeSize(sizeSelect.getSelected());
7589
PlayerModelFactory.Shape shape = PlayerModelFactory.Shape.fromIndex(
7690
shapeSelect.getSelectedIndex());
@@ -80,18 +94,13 @@ public void clicked(InputEvent event, float x, float y) {
8094
});
8195
root.add(soloButton).width(250).height(50).padBottom(15).row();
8296

83-
// Player name field
84-
Label nameLabel = new Label("Name:", skin);
85-
root.add(nameLabel).padBottom(5).row();
86-
87-
TextField nameField = new TextField("Player", skin);
88-
root.add(nameField).width(200).padBottom(20).row();
89-
9097
// Host button
9198
TextButton hostButton = new TextButton("HOST GAME", skin);
9299
hostButton.addListener(new ClickListener() {
93100
@Override
94101
public void clicked(InputEvent event, float x, float y) {
102+
game.setLastPlayerName(nameField.getText());
103+
game.setLastHostIp(ipField.getText());
95104
int[] size = parseMazeSize(sizeSelect.getSelected());
96105
NetworkManager net = new NetworkManager();
97106
String ip = net.startHost(nameField.getText(), shapeSelect.getSelectedIndex());
@@ -104,13 +113,14 @@ public void clicked(InputEvent event, float x, float y) {
104113

105114
// Join section: IP field + button
106115
Table joinTable = new Table();
107-
TextField ipField = new TextField("192.168.1.", skin);
108116
joinTable.add(ipField).width(180).padRight(10);
109117

110118
TextButton joinButton = new TextButton("JOIN", skin);
111119
joinButton.addListener(new ClickListener() {
112120
@Override
113121
public void clicked(InputEvent event, float x, float y) {
122+
game.setLastPlayerName(nameField.getText());
123+
game.setLastHostIp(ipField.getText());
114124
int[] size = parseMazeSize(sizeSelect.getSelected());
115125
NetworkManager net = new NetworkManager();
116126
net.setOnJoinResponse(resp -> {
@@ -163,6 +173,18 @@ private int[] parseMazeSize(String sizeStr) {
163173
return new int[]{Integer.parseInt(parts[0]), Integer.parseInt(parts[1])};
164174
}
165175

176+
@Override
177+
public void hide() {
178+
// Save field values when leaving this screen — ensures Android soft keyboard
179+
// text is captured even if the keyboard wasn't dismissed before tapping a button
180+
if (nameField != null) {
181+
game.setLastPlayerName(nameField.getText());
182+
}
183+
if (ipField != null) {
184+
game.setLastHostIp(ipField.getText());
185+
}
186+
}
187+
166188
@Override
167189
public void render(float delta) {
168190
Gdx.gl.glClearColor(0, 0, 0, 1);

core/src/main/java/com/codeheadsystems/mazer/screen/NetworkedPlayScreen.java

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class NetworkedPlayScreen extends ScreenAdapter {
3232

3333
private static final float CELL_SIZE = 4.0f;
3434
private static final float LERP_SPEED = 15f;
35+
private static final float GAME_OVER_DELAY = 2.0f; // seconds to show final kill before transitioning
3536

3637
private final MazerGame game;
3738
private final NetworkManager networkManager;
@@ -50,6 +51,10 @@ public class NetworkedPlayScreen extends ScreenAdapter {
5051
// Client-side snapshot state
5152
private final List<Bullet> clientBullets = new ArrayList<>();
5253

54+
// Game over delay
55+
private int pendingWinnerId = -2; // -2 = no game over pending
56+
private float gameOverTimer = 0f;
57+
5358
public NetworkedPlayScreen(MazerGame game, NetworkManager networkManager,
5459
Protocol.StartGame startMsg) {
5560
this.game = game;
@@ -67,14 +72,16 @@ public void show() {
6772
bulletRenderer = new BulletRenderer();
6873
hudRenderer = new HudRenderer(maze);
6974

70-
// Create all players from the start message
75+
// Create all players from the start message using actual player IDs and shapes
7176
int localId = networkManager.getLocalPlayerId();
72-
for (int i = 0; i < startMsg.spawnX.length; i++) {
73-
// Use index as player id (matches how HostServer assigns them)
74-
Player p = new Player(i, startMsg.spawnX[i], startMsg.spawnZ[i],
75-
startMsg.spawnAngle[i], PlayerModelFactory.Shape.CUBE);
77+
for (int i = 0; i < startMsg.playerIds.length; i++) {
78+
int playerId = startMsg.playerIds[i];
79+
PlayerModelFactory.Shape shape = PlayerModelFactory.Shape.fromIndex(
80+
startMsg.shapeIndices[i]);
81+
Player p = new Player(playerId, startMsg.spawnX[i], startMsg.spawnZ[i],
82+
startMsg.spawnAngle[i], shape);
7683
gameWorld.addPlayer(p);
77-
if (i == localId) {
84+
if (playerId == localId) {
7885
localPlayer = p;
7986
}
8087
}
@@ -144,6 +151,15 @@ public void render(float delta) {
144151
// Render HUD
145152
hudRenderer.render(mazeRenderer, localPlayer);
146153

154+
// Game over delay — keep rendering for a couple seconds before transitioning
155+
if (pendingWinnerId != -2) {
156+
gameOverTimer += delta;
157+
if (gameOverTimer >= GAME_OVER_DELAY) {
158+
game.setScreen(new GameOverScreen(game, networkManager, pendingWinnerId));
159+
return;
160+
}
161+
}
162+
147163
// Check for leave request
148164
if (hudRenderer.isLeaveRequested()) {
149165
networkManager.shutdown();
@@ -217,7 +233,9 @@ private void onPlayerEliminated(Protocol.PlayerEliminated msg) {
217233
}
218234

219235
private void onGameOver(Protocol.GameOver msg) {
220-
game.setScreen(new GameOverScreen(game, networkManager, msg.winnerPlayerId));
236+
// Start delay so the player can see the final kill before transitioning
237+
pendingWinnerId = msg.winnerPlayerId;
238+
gameOverTimer = 0f;
221239
}
222240

223241
private void onDisconnected() {

0 commit comments

Comments
 (0)