Skip to content

Commit cf364ad

Browse files
authored
Merge pull request #10 from Zwazel-Teaching-Projects/feat/tank-types
Feat/tank types
2 parents 309d8e8 + 334cd9e commit cf364ad

File tree

8 files changed

+122
-26
lines changed

8 files changed

+122
-26
lines changed

src/main/java/dev/zwazel/GameWorld.java

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import dev.zwazel.internal.InternalGameWorld;
66
import dev.zwazel.internal.PublicGameWorld;
77
import dev.zwazel.internal.connection.ConnectionManager;
8-
import dev.zwazel.internal.debug.MapVisualiser;
98
import dev.zwazel.internal.game.state.ClientState;
109
import dev.zwazel.internal.game.tank.Tank;
1110
import dev.zwazel.internal.game.tank.TankFactory;
@@ -26,16 +25,16 @@
2625
public class GameWorld implements InternalGameWorld, PublicGameWorld {
2726
private static GameWorld instance;
2827

29-
private final PropertyHandler properties = PropertyHandler.getInstance();
3028
private final BlockingQueue<MessageContainer> incomingMessages = new LinkedBlockingQueue<>();
3129
private final BlockingQueue<MessageContainer> outgoingMessages = new LinkedBlockingQueue<>();
32-
private final DebugMode debug;
30+
private DebugMode debug;
3331
private boolean immediatelyStartGame;
3432
private ConnectionManager connection;
3533
private GameState gameState;
3634
private BotInterface bot;
3735
private Tank tank;
3836
private GameConfig gameConfig;
37+
3938
/**
4039
* The predicted state of the client.
4140
* Resets every tick.
@@ -48,24 +47,23 @@ public class GameWorld implements InternalGameWorld, PublicGameWorld {
4847

4948
private GameWorld() {
5049
// Private constructor to prevent instantiation
51-
this.debug = DebugMode.valueOf(this.properties.getProperty("debug.mode").toUpperCase());
5250
}
5351

54-
public static boolean startGame(BotInterface bot, Class<? extends Tank> tankClass) {
52+
public static boolean startGame(BotInterface bot) {
5553
GameWorld gameWorld = GameWorld.getInstance();
56-
gameWorld.setup(bot, tankClass, true);
57-
if (gameWorld.connect(true)) {
54+
gameWorld.setup(bot, true);
55+
if (gameWorld.connect()) {
5856
return true;
5957
} else {
6058
System.err.println("Failed to start the game world due to connection issues.");
6159
return false;
6260
}
6361
}
6462

65-
public static boolean connectToServer(BotInterface bot, Class<? extends Tank> tankClass) {
63+
public static boolean connectToServer(BotInterface bot) {
6664
GameWorld gameWorld = GameWorld.getInstance();
67-
gameWorld.setup(bot, tankClass, false);
68-
return gameWorld.connect(false);
65+
gameWorld.setup(bot, false);
66+
return gameWorld.connect();
6967
}
7068

7169
private static GameWorld getInstance() {
@@ -76,19 +74,20 @@ private static GameWorld getInstance() {
7674
return instance;
7775
}
7876

79-
private void setup(BotInterface bot, Class<? extends Tank> tankClass, boolean immediatelyStartGame) {
77+
private void setup(BotInterface bot, boolean immediatelyStartGame) {
8078
this.bot = bot;
81-
this.tank = TankFactory.createTank(tankClass);
79+
this.debug = bot.getLocalBotConfig().debugMode().orElse(DebugMode.NONE);
80+
this.tank = TankFactory.createTank(bot.getLocalBotConfig().tankType());
8281
this.immediatelyStartGame = immediatelyStartGame;
8382
}
8483

85-
private boolean connect(boolean immediatelyStartGame) {
84+
private boolean connect() {
8685
if (connection.isConnected()) {
8786
return true;
8887
}
8988

90-
String serverIp = properties.getProperty("server.ip");
91-
int serverPort = Integer.parseInt(properties.getProperty("server.port"));
89+
String serverIp = bot.getLocalBotConfig().serverIp();
90+
int serverPort = bot.getLocalBotConfig().serverPort();
9291

9392
if (!connection.connect(serverIp, serverPort)) {
9493
System.err.println("Failed to connect to " + serverIp + ":" + serverPort);
@@ -146,7 +145,7 @@ public void startGame() {
146145
System.err.println("Game world is not running!");
147146
}
148147

149-
boolean fillEmptySlotsWithDummies = Boolean.parseBoolean(properties.getProperty("lobby.fillEmptySlots"));
148+
boolean fillEmptySlotsWithDummies = bot.getLocalBotConfig().lobbyConfig().fillEmptySlots();
150149
this.send(new MessageContainer(
151150
TO_LOBBY_DIRECTLY.get(),
152151
StartGameConfig.builder().fillEmptySlotsWithDummies(fillEmptySlotsWithDummies).build()
@@ -235,7 +234,7 @@ public BotInterface getBot() {
235234
return bot;
236235
}
237236

238-
private enum DebugMode {
237+
public enum DebugMode {
239238
NONE,
240239
INTERNAL,
241240
PUBLIC,

src/main/java/dev/zwazel/bot/BotInterface.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
package dev.zwazel.bot;
22

33
import dev.zwazel.internal.PublicGameWorld;
4+
import dev.zwazel.internal.config.LocalBotConfig;
45

56
public interface BotInterface {
7+
/**
8+
* Get the local bot configuration.
9+
* The local bot configuration is used to determine the bot's name and tank type when connecting to the game. It can not be changed after the game has started.
10+
*
11+
* @return the local bot configuration
12+
*/
13+
LocalBotConfig getLocalBotConfig();
14+
615
/**
716
* Set up the bot.
817
* Gets called once at the start of the game.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package dev.zwazel.internal.config;
2+
3+
import lombok.Builder;
4+
import lombok.NonNull;
5+
6+
import java.util.Optional;
7+
8+
@Builder
9+
public record LobbyConfig(@NonNull String lobbyName, @NonNull String mapName, @NonNull String teamName, Optional<Integer> spawnPoint,
10+
boolean fillEmptySlots) {
11+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package dev.zwazel.internal.config;
2+
3+
import dev.zwazel.GameWorld;
4+
import dev.zwazel.internal.game.tank.Tank;
5+
import lombok.Builder;
6+
import lombok.NonNull;
7+
8+
import java.util.Optional;
9+
10+
@Builder
11+
public record LocalBotConfig(Optional<GameWorld.DebugMode> debugMode, @NonNull String botName,
12+
@NonNull Class<? extends Tank> tankType, @NonNull String serverIp, @NonNull int serverPort,
13+
@NonNull LobbyConfig lobbyConfig) {
14+
}

src/main/java/dev/zwazel/internal/connection/ConnectionManager.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package dev.zwazel.internal.connection;
22

3-
import dev.zwazel.PropertyHandler;
43
import dev.zwazel.internal.InternalGameWorld;
54
import dev.zwazel.internal.PublicGameWorld;
5+
import dev.zwazel.internal.config.LocalBotConfig;
66
import dev.zwazel.internal.message.MessageContainer;
77
import dev.zwazel.internal.message.data.FirstContact;
88
import lombok.Getter;
@@ -14,7 +14,6 @@
1414

1515
public class ConnectionManager {
1616
private static ConnectionManager instance;
17-
private final PropertyHandler properties = PropertyHandler.getInstance();
1817

1918
private InternalGameWorld world;
2019
@Getter
@@ -51,16 +50,20 @@ public boolean connect(String host, int port) throws IllegalStateException {
5150
listenerThread.start();
5251

5352
PublicGameWorld publicGameWorld = this.world.getPublicGameWorld();
53+
LocalBotConfig botConfig = this.world.getBot().getLocalBotConfig();
5454
// Sending first contact message
5555
MessageContainer message = new MessageContainer(
5656
SERVER_ONLY.get(),
5757
FirstContact.builder()
58-
.lobbyName(properties.getProperty("lobby.name"))
59-
.botName(properties.getProperty("bot.name"))
60-
.mapName(properties.getProperty("lobby.map.name"))
61-
.teamName(properties.getProperty("lobby.team.name"))
62-
.botAssignedSpawnPoint(properties.getProperty("lobby.spawnPoint") != null ?
63-
Long.parseLong(properties.getProperty("lobby.spawnPoint")) : null)
58+
.lobbyName(botConfig.lobbyConfig().lobbyName())
59+
.botName(botConfig.botName())
60+
.mapName(botConfig.lobbyConfig().mapName())
61+
.teamName(botConfig.lobbyConfig().teamName())
62+
.botAssignedSpawnPoint(
63+
botConfig.lobbyConfig().spawnPoint().isPresent() ?
64+
Long.valueOf(botConfig.lobbyConfig().spawnPoint().get())
65+
: null
66+
)
6467
.clientType(FirstContact.ClientType.PLAYER)
6568
.tankType(publicGameWorld.getTank().getTankType())
6669
.build()

src/main/java/dev/zwazel/internal/game/map/MapDefinition.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ public Vec3 getClosestTileFromWorld(Vec3 worldPos) {
4343
minDistance = distance;
4444
closest = tile;
4545
}
46+
47+
assert closest != null;
48+
double closestDistance = Math.abs(closest.getX() - worldPos.getX()) + Math.abs(closest.getZ() - worldPos.getZ());
49+
if (distance < closestDistance) {
50+
closest = tile;
51+
}
4652
}
4753

4854
assert closest != null;

src/main/java/dev/zwazel/internal/game/tank/TankConfig.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@
2323
* @param armor the armor of the tank on each side (0-1, 0 = no armor, 1 = full armor)
2424
* Damage is reduced by the armor value on the side that was hit
2525
* calculated as: damage = damage * (1 - armor)
26+
* @param respawnTimer how many ticks the tank has to wait before respawning
27+
* @param projectileGravity how much the projectile is affected by gravity (if 0, no gravity)
2628
*/
2729
public record TankConfig(float moveSpeed, float bodyRotationSpeed, float turretYawRotationSpeed,
2830
float turretPitchRotationSpeed, float turretMaxPitch, float turretMinPitch, float maxSlope,
2931
Vec3 size, Long shootCooldown, float projectileDamage, float projectileSpeed,
3032
long projectileLifetime, Vec3 projectileSize, float maxHealth, HashMap<Side, Float> armor,
31-
long respawnTimer) {
33+
long respawnTimer, float projectileGravity) {
3234
}

src/main/java/dev/zwazel/internal/game/utils/Graph.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,63 @@
11
package dev.zwazel.internal.game.utils;
22

3+
import dev.zwazel.internal.game.map.MapDefinition;
34
import lombok.Data;
45

56
@Data
67
public class Graph {
78
private Node[][] nodes;
89

10+
public Graph(MapDefinition mapDefinition, boolean allowDiagonal) {
11+
nodes = new Node[(int) mapDefinition.depth()][(int) mapDefinition.width()];
12+
13+
// Create nodes
14+
for (int y = 0; y < mapDefinition.depth(); y++) {
15+
for (int x = 0; x < mapDefinition.width(); x++) {
16+
Node node = new Node(mapDefinition.tiles()[y][x], x, y);
17+
nodes[y][x] = node;
18+
}
19+
}
20+
21+
// Create edges
22+
for (Node[] row : nodes) {
23+
for (Node node : row) {
24+
int x = node.getX();
25+
int y = node.getY();
26+
27+
// Add horizontal and vertical edges
28+
if (x > 0) {
29+
node.getNeighbours().add(getNode(x - 1, y));
30+
}
31+
if (x < mapDefinition.width() - 1) {
32+
node.getNeighbours().add(getNode(x + 1, y));
33+
}
34+
if (y > 0) {
35+
node.getNeighbours().add(getNode(x, y - 1));
36+
}
37+
if (y < mapDefinition.depth() - 1) {
38+
node.getNeighbours().add(getNode(x, y + 1));
39+
}
40+
41+
// Add diagonal edges
42+
if (allowDiagonal) {
43+
if (x > 0 && y > 0) {
44+
node.getNeighbours().add(getNode(x - 1, y - 1));
45+
}
46+
if (x < mapDefinition.width() - 1 && y > 0) {
47+
node.getNeighbours().add(getNode(x + 1, y - 1));
48+
}
49+
if (x > 0 && y < mapDefinition.depth() - 1) {
50+
node.getNeighbours().add(getNode(x - 1, y + 1));
51+
}
52+
if (x < mapDefinition.width() - 1 && y < mapDefinition.depth() - 1) {
53+
node.getNeighbours().add(getNode(x + 1, y + 1));
54+
}
55+
}
56+
}
57+
}
58+
}
59+
60+
@Deprecated(forRemoval = true)
961
public Graph(float[][] heights, boolean allowDiagonal) {
1062
nodes = new Node[heights.length][heights[0].length];
1163

0 commit comments

Comments
 (0)