diff --git a/client/src/main/java/nl/andrewlalis/aos_client/Client.java b/client/src/main/java/nl/andrewlalis/aos_client/Client.java index 6dc82b1..ae90fe8 100644 --- a/client/src/main/java/nl/andrewlalis/aos_client/Client.java +++ b/client/src/main/java/nl/andrewlalis/aos_client/Client.java @@ -3,6 +3,7 @@ package nl.andrewlalis.aos_client; import nl.andrewlalis.aos_client.view.GameFrame; import nl.andrewlalis.aos_client.view.GamePanel; import nl.andrewlalis.aos_core.model.Player; +import nl.andrewlalis.aos_core.model.Team; import nl.andrewlalis.aos_core.model.World; import nl.andrewlalis.aos_core.model.tools.Gun; import nl.andrewlalis.aos_core.net.data.DataTypes; @@ -73,6 +74,12 @@ public class Client { player.setGun(new Gun(this.world.getGunTypes().get(p.getGunTypeName()))); } } + for (var t : update.getTeamUpdates()) { + Team team = this.world.getTeams().get(t.getId()); + if (team != null) { + team.setScore(t.getScore()); + } + } this.soundManager.play(update.getSoundsToPlay()); } diff --git a/client/src/main/java/nl/andrewlalis/aos_client/view/GamePanel.java b/client/src/main/java/nl/andrewlalis/aos_client/view/GamePanel.java index 5df78bd..7d193a7 100644 --- a/client/src/main/java/nl/andrewlalis/aos_client/view/GamePanel.java +++ b/client/src/main/java/nl/andrewlalis/aos_client/view/GamePanel.java @@ -110,7 +110,7 @@ public class GamePanel extends JPanel { g2.fill(barricadeRect); } - for (Team t : world.getTeams()) { + for (Team t : world.getTeams().values()) { g2.setColor(t.getColor()); Ellipse2D.Double spawnCircle = new Ellipse2D.Double( t.getSpawnPoint().x() - Team.SPAWN_RADIUS, @@ -231,17 +231,11 @@ public class GamePanel extends JPanel { Gun gun = myPlayer.getGun(); g2.drawString("Clips: " + gun.getClipCount() + " / " + gun.getType().getMaxClipCount(), 5, this.getHeight() - 20); g2.drawString("Bullets: " + gun.getCurrentClipBulletCount() + " / " + gun.getType().getClipSize(), 5, this.getHeight() - 30); - if (myPlayer.getHealth() >= 66.0f) { - g2.setColor(Color.GREEN); - } else if (myPlayer.getHealth() >= 33.0f) { - g2.setColor(Color.YELLOW); - } else { - g2.setColor(Color.RED); - } - g2.drawString(String.format("Health: %.1f / %.1f", myPlayer.getHealth(), Player.MAX_HEALTH), 5, this.getHeight() - 40); + g2.setColor(Color.GREEN); + g2.drawString(String.format("Health: %.1f", myPlayer.getHealth()), 5, this.getHeight() - 40); int y = this.getHeight() - 60; - for (Team t : world.getTeams()) { + for (Team t : world.getTeams().values()) { g2.setColor(t.getColor()); g2.drawString("Team " + t.getName() + ": " + t.getScore(), 5, y); y -= 15; diff --git a/core/src/main/java/nl/andrewlalis/aos_core/model/Bullet.java b/core/src/main/java/nl/andrewlalis/aos_core/model/Bullet.java index dab8661..e5aeaca 100644 --- a/core/src/main/java/nl/andrewlalis/aos_core/model/Bullet.java +++ b/core/src/main/java/nl/andrewlalis/aos_core/model/Bullet.java @@ -3,29 +3,22 @@ package nl.andrewlalis.aos_core.model; import nl.andrewlalis.aos_core.geom.Vec2; import nl.andrewlalis.aos_core.model.tools.Gun; +/** + * Represents a single projectile bullet fired from a player's gun. When shot by + * a player, a newly-spawned bullet will be initialized with a velocity in the + * general direction of the gun, with some perturbation according to the gun's + * accuracy and player's sprinting/sneaking status. + */ public class Bullet extends PhysicsObject { private final int playerId; private final Player player; private final Gun gun; - public Bullet(Player player) { + public Bullet(Player player, float sneakAccuracyModifier, float sprintAccuracyModifier) { this.playerId = player.getId(); this.player = player; - this.setPosition(player.getPosition() - .add(player.getOrientation().mul(1.5f)) - .add(player.getOrientation().perp().mul(Player.RADIUS)) - ); - this.setOrientation(player.getOrientation()); - float accuracy = player.getGun().getType().getAccuracy(); - if (player.isSneaking()) { - accuracy *= Player.SNEAK_ACCURACY_MODIFIER; - } else if (player.isSprinting()) { - accuracy *= Player.SPRINT_ACCURACY_MODIFIER; - } - Vec2 perturbation = Vec2.random(-1, 1).mul(accuracy); - Vec2 localVelocity = this.getOrientation().add(perturbation).mul(player.getGun().getType().getBulletSpeed()); - this.setVelocity(player.getVelocity().add(localVelocity)); this.gun = player.getGun(); + this.setPhysicsProperties(sneakAccuracyModifier, sprintAccuracyModifier); } public Bullet(Vec2 position, Vec2 velocity) { @@ -35,6 +28,23 @@ public class Bullet extends PhysicsObject { this.gun = null; } + private void setPhysicsProperties(float sneakAccuracyModifier, float sprintAccuracyModifier) { + this.setPosition(player.getPosition() + .add(player.getOrientation().mul(1.5f)) + .add(player.getOrientation().perp().mul(Player.RADIUS)) + ); + this.setOrientation(player.getOrientation()); + float accuracy = player.getGun().getType().getAccuracy(); + if (player.isSneaking()) { + accuracy *= sneakAccuracyModifier; + } else if (player.isSprinting()) { + accuracy *= sprintAccuracyModifier; + } + Vec2 perturbation = Vec2.random(-1, 1).mul(accuracy); + Vec2 localVelocity = this.getOrientation().add(perturbation).mul(player.getGun().getType().getBulletSpeed()); + this.setVelocity(player.getVelocity().add(localVelocity)); + } + public int getPlayerId() { return playerId; } diff --git a/core/src/main/java/nl/andrewlalis/aos_core/model/Player.java b/core/src/main/java/nl/andrewlalis/aos_core/model/Player.java index 42915bf..3a1f9e7 100644 --- a/core/src/main/java/nl/andrewlalis/aos_core/model/Player.java +++ b/core/src/main/java/nl/andrewlalis/aos_core/model/Player.java @@ -7,18 +7,8 @@ import nl.andrewlalis.aos_core.model.tools.GunType; import java.util.Objects; public class Player extends PhysicsObject implements Comparable { - public static final float MOVEMENT_SPEED = 10; // Movement speed, in m/s - public static final float MOVEMENT_SPEED_SPRINT = 18; - public static final float MOVEMENT_SPEED_SNEAK = 5; - public static final float MOVEMENT_ACCELERATION = 60; // Acceleration speed, in m/s^2. public static final float MOVEMENT_THRESHOLD = 0.001f; // Threshold for stopping movement. Speeds slower than this are reduced to 0. - public static final float MOVEMENT_DECELERATION = 30; // Deceleration speed, in m/s^2. public static final float RADIUS = 0.5f; // Collision radius, in meters. - public static final float RESUPPLY_COOLDOWN = 30; // Seconds between allowing resupply. - public static final float MAX_HEALTH = 100.0f; - public static final float HEALTH_REGEN_RATE = 1.0f; // How quickly players regenerate health over time. - public static final float SNEAK_ACCURACY_MODIFIER = 0.5f; - public static final float SPRINT_ACCURACY_MODIFIER = 1.5f; private final int id; private final String name; @@ -32,13 +22,19 @@ public class Player extends PhysicsObject implements Comparable { private boolean reloading; private transient long lastResupply; - public Player(int id, String name, Team team, GunType gunType) { + // Stats + private transient int killCount; + private transient int deathCount; + private transient int shotCount; + private transient int resupplyCount; + + public Player(int id, String name, Team team, GunType gunType, float maxHealth) { this.id = id; this.name = name; this.team = team; this.state = new PlayerControlState(); this.gun = new Gun(gunType); - this.health = MAX_HEALTH; + this.health = maxHealth; this.useWeapon(); this.lastShot = System.currentTimeMillis(); } @@ -95,6 +91,7 @@ public class Player extends PhysicsObject implements Comparable { public void useWeapon() { this.lastShot = System.currentTimeMillis(); this.gun.decrementBulletCount(); + this.shotCount++; } public void startReloading() { @@ -116,16 +113,17 @@ public class Player extends PhysicsObject implements Comparable { return reloading; } - public boolean canResupply() { + public boolean canResupply(float resupplyCooldown) { return this.team != null && this.team.getSupplyPoint().dist(this.getPosition()) < Team.SUPPLY_POINT_RADIUS && - System.currentTimeMillis() - this.lastResupply > RESUPPLY_COOLDOWN * 1000; + System.currentTimeMillis() - this.lastResupply > resupplyCooldown * 1000; } - public void resupply() { + public void resupply(float maxHealth) { this.lastResupply = System.currentTimeMillis(); this.gun.refillClips(); - this.health = MAX_HEALTH; + this.health = maxHealth; + this.resupplyCount++; } public float getHealth() { @@ -136,8 +134,8 @@ public class Player extends PhysicsObject implements Comparable { this.health = Math.max(this.health - damage, 0.0f); } - public void respawn() { - this.resupply(); + public void respawn(float maxHealth) { + this.resupply(maxHealth); this.gun.emptyCurrentClip(); if (this.team != null) { this.setPosition(this.team.getSpawnPoint().add(Vec2.random(-Team.SPAWN_RADIUS / 2, Team.SPAWN_RADIUS / 2))); @@ -154,6 +152,37 @@ public class Player extends PhysicsObject implements Comparable { !this.state.isSneaking(); } + public int getKillCount() { + return killCount; + } + + public int getDeathCount() { + return deathCount; + } + + public int getShotCount() { + return shotCount; + } + + public int getResupplyCount() { + return resupplyCount; + } + + public void incrementDeathCount() { + this.deathCount++; + } + + public void incrementKillCount() { + this.killCount++; + } + + public void resetStats() { + this.killCount = 0; + this.deathCount = 0; + this.shotCount = 0; + this.resupplyCount = 0; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/core/src/main/java/nl/andrewlalis/aos_core/model/Team.java b/core/src/main/java/nl/andrewlalis/aos_core/model/Team.java index 8683c3a..113f3f2 100644 --- a/core/src/main/java/nl/andrewlalis/aos_core/model/Team.java +++ b/core/src/main/java/nl/andrewlalis/aos_core/model/Team.java @@ -6,12 +6,13 @@ import java.awt.*; import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import java.util.Objects; public class Team implements Serializable { public static final float SPAWN_RADIUS = 3; public static final float SUPPLY_POINT_RADIUS = 2; - public static final boolean FRIENDLY_FIRE = false; + private final byte id; private final String name; private final java.awt.Color color; private final Vec2 spawnPoint; @@ -22,7 +23,8 @@ public class Team implements Serializable { private int score; - public Team(String name, Color color, Vec2 spawnPoint, Vec2 supplyPoint, Vec2 orientation) { + public Team(byte id, String name, Color color, Vec2 spawnPoint, Vec2 supplyPoint, Vec2 orientation) { + this.id = id; this.name = name; this.color = color; this.spawnPoint = spawnPoint; @@ -32,6 +34,10 @@ public class Team implements Serializable { this.score = 0; } + public byte getId() { + return id; + } + public String getName() { return name; } @@ -67,4 +73,21 @@ public class Team implements Serializable { public void resetScore() { this.score = 0; } + + public void setScore(int score) { + this.score = score; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Team team = (Team) o; + return getId() == team.getId(); + } + + @Override + public int hashCode() { + return Objects.hash(getId()); + } } diff --git a/core/src/main/java/nl/andrewlalis/aos_core/model/World.java b/core/src/main/java/nl/andrewlalis/aos_core/model/World.java index 155b8c4..47e9485 100644 --- a/core/src/main/java/nl/andrewlalis/aos_core/model/World.java +++ b/core/src/main/java/nl/andrewlalis/aos_core/model/World.java @@ -16,7 +16,7 @@ import java.util.concurrent.CopyOnWriteArrayList; public class World implements Serializable { private final Vec2 size; - private final List teams; + private final Map teams; private final Map gunTypes; private final Map players; private final List bullets; @@ -24,7 +24,7 @@ public class World implements Serializable { public World(Vec2 size) { this.size = size; - this.teams = new ArrayList<>(); + this.teams = new ConcurrentHashMap<>(); this.gunTypes = new ConcurrentHashMap<>(); this.players = new ConcurrentHashMap<>(); this.bullets = new CopyOnWriteArrayList<>(); @@ -35,7 +35,7 @@ public class World implements Serializable { return size; } - public List getTeams() { + public Map getTeams() { return teams; } diff --git a/core/src/main/java/nl/andrewlalis/aos_core/net/data/TeamUpdate.java b/core/src/main/java/nl/andrewlalis/aos_core/net/data/TeamUpdate.java new file mode 100644 index 0000000..a4279e3 --- /dev/null +++ b/core/src/main/java/nl/andrewlalis/aos_core/net/data/TeamUpdate.java @@ -0,0 +1,44 @@ +package nl.andrewlalis.aos_core.net.data; + +import nl.andrewlalis.aos_core.model.Team; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public class TeamUpdate { + public static final int BYTES = 1 + Integer.BYTES; + + private final byte id; + private final int score; + + public TeamUpdate(Team team) { + this.id = team.getId(); + this.score = team.getScore(); + } + + public TeamUpdate(byte id, int score) { + this.id = id; + this.score = score; + } + + public byte getId() { + return id; + } + + public int getScore() { + return score; + } + + public void write(DataOutputStream out) throws IOException { + out.writeByte(this.id); + out.writeInt(this.score); + } + + public static TeamUpdate read(DataInputStream in) throws IOException { + return new TeamUpdate( + in.readByte(), + in.readInt() + ); + } +} diff --git a/core/src/main/java/nl/andrewlalis/aos_core/net/data/WorldUpdate.java b/core/src/main/java/nl/andrewlalis/aos_core/net/data/WorldUpdate.java index b59adf4..41153fe 100644 --- a/core/src/main/java/nl/andrewlalis/aos_core/net/data/WorldUpdate.java +++ b/core/src/main/java/nl/andrewlalis/aos_core/net/data/WorldUpdate.java @@ -2,6 +2,7 @@ package nl.andrewlalis.aos_core.net.data; import nl.andrewlalis.aos_core.model.Bullet; import nl.andrewlalis.aos_core.model.Player; +import nl.andrewlalis.aos_core.model.Team; import java.io.*; import java.util.ArrayList; @@ -20,21 +21,24 @@ import java.util.List; public class WorldUpdate { private final List playerUpdates; private final List bulletUpdates; + private final List teamUpdates; private final List soundsToPlay; public WorldUpdate() { - this(new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); + this(new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); } - private WorldUpdate(List playerUpdates, List bulletUpdates, List soundsToPlay) { + private WorldUpdate(List playerUpdates, List bulletUpdates, List teamUpdates, List soundsToPlay) { this.playerUpdates = playerUpdates; this.bulletUpdates = bulletUpdates; + this.teamUpdates = teamUpdates; this.soundsToPlay = soundsToPlay; } public void clear() { this.playerUpdates.clear(); this.bulletUpdates.clear(); + this.teamUpdates.clear(); this.soundsToPlay.clear(); } @@ -46,6 +50,10 @@ public class WorldUpdate { this.bulletUpdates.add(new BulletUpdate(b)); } + public void addTeam(Team team) { + this.teamUpdates.add(new TeamUpdate(team)); + } + public void addSound(Sound sound) { this.soundsToPlay.add(sound); } @@ -58,6 +66,10 @@ public class WorldUpdate { return bulletUpdates; } + public List getTeamUpdates() { + return teamUpdates; + } + public List getSoundsToPlay() { return soundsToPlay; } @@ -66,6 +78,7 @@ public class WorldUpdate { int size = 3 * Integer.BYTES + // List size integers. this.playerUpdates.size() * PlayerUpdate.BYTES + this.bulletUpdates.size() * BulletUpdate.BYTES + + this.teamUpdates.size() * TeamUpdate.BYTES + this.soundsToPlay.size() * Sound.BYTES; ByteArrayOutputStream out = new ByteArrayOutputStream(size); DataOutputStream dataOut = new DataOutputStream(out); @@ -77,6 +90,10 @@ public class WorldUpdate { for (var u : this.bulletUpdates) { u.write(dataOut); } + dataOut.writeInt(this.teamUpdates.size()); + for (var u : this.teamUpdates) { + u.write(dataOut); + } dataOut.writeInt(this.soundsToPlay.size()); for (var u : this.soundsToPlay) { u.write(dataOut); @@ -100,12 +117,17 @@ public class WorldUpdate { for (int i = 0; i < bullets; i++) { bulletUpdates.add(BulletUpdate.read(dataIn)); } + int teams = dataIn.readInt(); + List teamUpdates = new ArrayList<>(teams); + for (int i = 0; i < teams; i++) { + teamUpdates.add(TeamUpdate.read(dataIn)); + } int sounds = dataIn.readInt(); List soundsToPlay = new ArrayList<>(sounds); for (int i = 0; i < sounds; i++) { soundsToPlay.add(Sound.read(dataIn)); } - var obj = new WorldUpdate(playerUpdates, bulletUpdates, soundsToPlay); + var obj = new WorldUpdate(playerUpdates, bulletUpdates, teamUpdates, soundsToPlay); dataIn.close(); return obj; } diff --git a/server/src/main/java/nl/andrewlalis/aos_server/ChatManager.java b/server/src/main/java/nl/andrewlalis/aos_server/ChatManager.java index c870fae..b6ec4ab 100644 --- a/server/src/main/java/nl/andrewlalis/aos_server/ChatManager.java +++ b/server/src/main/java/nl/andrewlalis/aos_server/ChatManager.java @@ -7,6 +7,8 @@ import nl.andrewlalis.aos_server.command.GunsCommand; import nl.andrewlalis.aos_server.command.ResetCommand; import nl.andrewlalis.aos_server.command.chat.ChatCommand; import nl.andrewlalis.aos_server.command.chat.GunCommand; +import nl.andrewlalis.aos_server.command.chat.KillDeathRatioCommand; +import nl.andrewlalis.aos_server.command.chat.TeamChatCommand; import java.util.Arrays; import java.util.Map; @@ -27,6 +29,8 @@ public class ChatManager { this.chatCommands.put("gun", new GunCommand()); this.chatCommands.put("reset", new ResetCommand(server)); this.chatCommands.put("guns", new GunsCommand(server)); + this.chatCommands.put("kd", new KillDeathRatioCommand()); + this.chatCommands.put("t", new TeamChatCommand()); } public void handlePlayerChat(ClientHandler handler, Player player, ChatMessage msg) { diff --git a/server/src/main/java/nl/andrewlalis/aos_server/Server.java b/server/src/main/java/nl/andrewlalis/aos_server/Server.java index 97a0802..17d66ec 100644 --- a/server/src/main/java/nl/andrewlalis/aos_server/Server.java +++ b/server/src/main/java/nl/andrewlalis/aos_server/Server.java @@ -79,14 +79,16 @@ public class Server { world.getBarricades().add(new Barricade(0, 30, 10, 10)); world.getBarricades().add(new Barricade(40, 30, 10, 10)); - world.getTeams().add(new Team( + world.getTeams().put((byte) 1, new Team( + (byte) 1, "Red", Color.RED, new Vec2(3, 3), new Vec2(15, 3), new Vec2(0, 1) )); - world.getTeams().add(new Team( + world.getTeams().put((byte) 2, new Team( + (byte) 2, "Blue", Color.BLUE, new Vec2(world.getSize().x() - 3, world.getSize().y() - 3), @@ -124,14 +126,14 @@ public class Server { public Player registerNewPlayer(String name) { int id = ThreadLocalRandom.current().nextInt(1, Integer.MAX_VALUE); Team team = null; - for (Team t : this.world.getTeams()) { + for (Team t : this.world.getTeams().values()) { if (team == null) { team = t; } else if (t.getPlayers().size() < team.getPlayers().size()) { team = t; } } - Player p = new Player(id, name, team, this.world.getGunTypes().get(this.settings.getPlayerSettings().getDefaultGun())); + Player p = new Player(id, name, team, this.world.getGunTypes().get(this.settings.getPlayerSettings().getDefaultGun()), settings.getPlayerSettings().getMaxHealth()); this.world.getPlayers().put(p.getId(), p); String message = p.getName() + " connected."; this.broadcastMessage(new SystemChatMessage(SystemChatMessage.Level.INFO, message)); @@ -196,10 +198,11 @@ public class Server { } public void resetGame() { - for (Team t : this.world.getTeams()) { + for (Team t : this.world.getTeams().values()) { t.resetScore(); for (Player p : t.getPlayers()) { - p.respawn(); + p.resetStats(); + p.respawn(settings.getPlayerSettings().getMaxHealth()); } } broadcastMessage(new SystemChatMessage(SystemChatMessage.Level.INFO, "Game has been reset.")); @@ -211,6 +214,14 @@ public class Server { } } + public void sendTeamMessage(Team team, Message message) { + for (ClientHandler handler : this.clientHandlers) { + if (team.equals(handler.getPlayer().getTeam())) { + handler.send(message); + } + } + } + public void shutdown() { this.running = false; try { diff --git a/server/src/main/java/nl/andrewlalis/aos_server/WorldUpdater.java b/server/src/main/java/nl/andrewlalis/aos_server/WorldUpdater.java index 5c4f489..87ee570 100644 --- a/server/src/main/java/nl/andrewlalis/aos_server/WorldUpdater.java +++ b/server/src/main/java/nl/andrewlalis/aos_server/WorldUpdater.java @@ -72,7 +72,7 @@ public class WorldUpdater extends Thread { for (Player p : this.world.getPlayers().values()) { this.updatePlayerMovement(p, t); this.updatePlayerShooting(p); - p.setHealth(Math.min(p.getHealth() + Player.HEALTH_REGEN_RATE * t, Player.MAX_HEALTH)); + p.setHealth(Math.min(p.getHealth() + server.getSettings().getPlayerSettings().getHealthRegenRate() * t, server.getSettings().getPlayerSettings().getMaxHealth())); this.worldUpdate.addPlayer(p); } } @@ -97,31 +97,33 @@ public class WorldUpdater extends Thread { Vec2 localVelocity = p.getVelocity().rotate(-left.angle()); float vx = localVelocity.x(); float vy = localVelocity.y(); + float a = server.getSettings().getPlayerSettings().getAcceleration(); if (p.getState().isMovingForward()) { - vy -= Player.MOVEMENT_ACCELERATION * t; + vy -= a * t; } if (p.getState().isMovingBackward()) { - vy += Player.MOVEMENT_ACCELERATION * t; + vy += a * t; } if (p.getState().isMovingLeft()) { - vx -= Player.MOVEMENT_ACCELERATION * t; + vx -= a * t; } if (p.getState().isMovingRight()) { - vx += Player.MOVEMENT_ACCELERATION * t; + vx += a * t; } // Compute deceleration. + float d = server.getSettings().getPlayerSettings().getDeceleration(); if (p.getState().isMovingForward() == p.getState().isMovingBackward()) { if (vy > 0) { - vy = Math.max(0.0f, vy - Player.MOVEMENT_DECELERATION * t); + vy = Math.max(0.0f, vy - d * t); } else { - vy = Math.min(0.0f, vy + Player.MOVEMENT_DECELERATION * t); + vy = Math.min(0.0f, vy + d * t); } } if (p.getState().isMovingLeft() == p.getState().isMovingRight()) { if (vx > 0) { - vx = Math.max(0.0f, vx - Player.MOVEMENT_DECELERATION * t); + vx = Math.max(0.0f, vx - d * t); } else { - vx = Math.min(0.0f, vx + Player.MOVEMENT_DECELERATION * t); + vx = Math.min(0.0f, vx + d * t); } } @@ -132,11 +134,11 @@ public class WorldUpdater extends Thread { } float speedLimit; if (p.isSprinting()) { - speedLimit = Player.MOVEMENT_SPEED_SPRINT; + speedLimit = server.getSettings().getPlayerSettings().getSprintSpeed(); } else if (p.isSneaking()) { - speedLimit = Player.MOVEMENT_SPEED_SNEAK; + speedLimit = server.getSettings().getPlayerSettings().getSneakSpeed(); } else { - speedLimit = Player.MOVEMENT_SPEED; + speedLimit = server.getSettings().getPlayerSettings().getSpeed(); } if (newLocalVelocity.mag() > speedLimit) { newLocalVelocity = newLocalVelocity.mul(speedLimit / newLocalVelocity.mag()); @@ -176,15 +178,15 @@ public class WorldUpdater extends Thread { if (ny + Player.RADIUS > this.world.getSize().y()) ny = this.world.getSize().y() - Player.RADIUS; p.setPosition(new Vec2(nx, ny)); - if (p.canResupply()) { - p.resupply(); + if (p.canResupply(server.getSettings().getPlayerSettings().getResupplyCooldown())) { + p.resupply(server.getSettings().getPlayerSettings().getMaxHealth()); } } private void updatePlayerShooting(Player p) { if (p.canUseWeapon()) { for (int i = 0; i < p.getGun().getType().getBulletsPerRound(); i++) { - Bullet b = new Bullet(p); + Bullet b = new Bullet(p, server.getSettings().getPlayerSettings().getSneakAccuracyModifier(), server.getSettings().getPlayerSettings().getSprintAccuracyModifier()); this.world.getBullets().add(b); this.worldUpdate.addBullet(b); } @@ -256,10 +258,13 @@ public class WorldUpdater extends Thread { Player shooter = this.world.getPlayers().get(b.getPlayerId()); this.server.broadcastMessage(new SystemChatMessage(SystemChatMessage.Level.SEVERE, p.getName() + " was shot by " + shooter.getName() + ".")); this.worldUpdate.addSound(new Sound(p.getPosition(), 1.0f, SoundType.DEATH)); + shooter.incrementKillCount(); if (shooter.getTeam() != null) { shooter.getTeam().incrementScore(); + this.worldUpdate.addTeam(shooter.getTeam()); } - p.respawn(); + p.incrementDeathCount(); + p.respawn(server.getSettings().getPlayerSettings().getMaxHealth()); } } } diff --git a/server/src/main/java/nl/andrewlalis/aos_server/command/ListPlayersCommand.java b/server/src/main/java/nl/andrewlalis/aos_server/command/ListPlayersCommand.java index 9163709..c71839c 100644 --- a/server/src/main/java/nl/andrewlalis/aos_server/command/ListPlayersCommand.java +++ b/server/src/main/java/nl/andrewlalis/aos_server/command/ListPlayersCommand.java @@ -26,7 +26,7 @@ public class ListPlayersCommand implements Command { player.getName(), player.getTeam() == null ? "none" : player.getTeam().getName(), player.getHealth(), - Player.MAX_HEALTH, + this.server.getSettings().getPlayerSettings().getMaxHealth(), player.getGun().getType().getName() )) .collect(Collectors.joining("\n")); diff --git a/server/src/main/java/nl/andrewlalis/aos_server/command/chat/GunCommand.java b/server/src/main/java/nl/andrewlalis/aos_server/command/chat/GunCommand.java index 0a544b4..5c46e42 100644 --- a/server/src/main/java/nl/andrewlalis/aos_server/command/chat/GunCommand.java +++ b/server/src/main/java/nl/andrewlalis/aos_server/command/chat/GunCommand.java @@ -12,7 +12,7 @@ public class GunCommand implements ChatCommand { @Override public void execute(ClientHandler handler, Player player, String[] args) { if (args.length < 1) { - handler.send(new SystemChatMessage(SystemChatMessage.Level.WARNING, "No gun name specified.")); + handler.send(new SystemChatMessage(SystemChatMessage.Level.WARNING, "No gun name specified. Use /guns to see available guns.")); return; } String gunName = String.join(" ", args); @@ -24,7 +24,7 @@ public class GunCommand implements ChatCommand { } } if (gunType == null) { - handler.send(new SystemChatMessage(SystemChatMessage.Level.WARNING, "Unknown gun name.")); + handler.send(new SystemChatMessage(SystemChatMessage.Level.WARNING, "Unknown gun name. Use /guns to see available guns.")); return; } player.setGun(new Gun(gunType)); diff --git a/server/src/main/java/nl/andrewlalis/aos_server/command/chat/KillDeathRatioCommand.java b/server/src/main/java/nl/andrewlalis/aos_server/command/chat/KillDeathRatioCommand.java new file mode 100644 index 0000000..2d86915 --- /dev/null +++ b/server/src/main/java/nl/andrewlalis/aos_server/command/chat/KillDeathRatioCommand.java @@ -0,0 +1,13 @@ +package nl.andrewlalis.aos_server.command.chat; + +import nl.andrewlalis.aos_core.model.Player; +import nl.andrewlalis.aos_core.net.chat.SystemChatMessage; +import nl.andrewlalis.aos_server.ClientHandler; + +public class KillDeathRatioCommand implements ChatCommand { + @Override + public void execute(ClientHandler handler, Player player, String[] args) { + float ratio = player.getKillCount() / ((float) player.getDeathCount()); + handler.send(new SystemChatMessage(SystemChatMessage.Level.INFO, String.format("Your Kill/Death ratio is %.2f.", ratio))); + } +} diff --git a/server/src/main/java/nl/andrewlalis/aos_server/command/chat/TeamChatCommand.java b/server/src/main/java/nl/andrewlalis/aos_server/command/chat/TeamChatCommand.java new file mode 100644 index 0000000..998991c --- /dev/null +++ b/server/src/main/java/nl/andrewlalis/aos_server/command/chat/TeamChatCommand.java @@ -0,0 +1,20 @@ +package nl.andrewlalis.aos_server.command.chat; + +import nl.andrewlalis.aos_core.model.Player; +import nl.andrewlalis.aos_core.net.chat.PlayerChatMessage; +import nl.andrewlalis.aos_core.net.chat.SystemChatMessage; +import nl.andrewlalis.aos_server.ClientHandler; + +/** + * This command, when invoked, sends a message to all team members. + */ +public class TeamChatCommand implements ChatCommand { + @Override + public void execute(ClientHandler handler, Player player, String[] args) { + if (player.getTeam() == null) { + handler.send(new SystemChatMessage(SystemChatMessage.Level.WARNING, "You're not in a team, so you can't send team chat messages.")); + return; + } + handler.getServer().sendTeamMessage(player.getTeam(), new PlayerChatMessage(player.getId(), String.join(" ", args))); + } +}