Added KD command, team chat command, and synchronized team scores over network.

This commit is contained in:
Andrew Lalis 2021-06-29 14:07:53 +02:00
parent f50a2d72a3
commit 93e623d5d4
15 changed files with 258 additions and 76 deletions

View File

@ -3,6 +3,7 @@ package nl.andrewlalis.aos_client;
import nl.andrewlalis.aos_client.view.GameFrame; import nl.andrewlalis.aos_client.view.GameFrame;
import nl.andrewlalis.aos_client.view.GamePanel; import nl.andrewlalis.aos_client.view.GamePanel;
import nl.andrewlalis.aos_core.model.Player; 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.World;
import nl.andrewlalis.aos_core.model.tools.Gun; import nl.andrewlalis.aos_core.model.tools.Gun;
import nl.andrewlalis.aos_core.net.data.DataTypes; 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()))); 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()); this.soundManager.play(update.getSoundsToPlay());
} }

View File

@ -110,7 +110,7 @@ public class GamePanel extends JPanel {
g2.fill(barricadeRect); g2.fill(barricadeRect);
} }
for (Team t : world.getTeams()) { for (Team t : world.getTeams().values()) {
g2.setColor(t.getColor()); g2.setColor(t.getColor());
Ellipse2D.Double spawnCircle = new Ellipse2D.Double( Ellipse2D.Double spawnCircle = new Ellipse2D.Double(
t.getSpawnPoint().x() - Team.SPAWN_RADIUS, t.getSpawnPoint().x() - Team.SPAWN_RADIUS,
@ -231,17 +231,11 @@ public class GamePanel extends JPanel {
Gun gun = myPlayer.getGun(); Gun gun = myPlayer.getGun();
g2.drawString("Clips: " + gun.getClipCount() + " / " + gun.getType().getMaxClipCount(), 5, this.getHeight() - 20); g2.drawString("Clips: " + gun.getClipCount() + " / " + gun.getType().getMaxClipCount(), 5, this.getHeight() - 20);
g2.drawString("Bullets: " + gun.getCurrentClipBulletCount() + " / " + gun.getType().getClipSize(), 5, this.getHeight() - 30); g2.drawString("Bullets: " + gun.getCurrentClipBulletCount() + " / " + gun.getType().getClipSize(), 5, this.getHeight() - 30);
if (myPlayer.getHealth() >= 66.0f) { g2.setColor(Color.GREEN);
g2.setColor(Color.GREEN); g2.drawString(String.format("Health: %.1f", myPlayer.getHealth()), 5, this.getHeight() - 40);
} 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);
int y = this.getHeight() - 60; int y = this.getHeight() - 60;
for (Team t : world.getTeams()) { for (Team t : world.getTeams().values()) {
g2.setColor(t.getColor()); g2.setColor(t.getColor());
g2.drawString("Team " + t.getName() + ": " + t.getScore(), 5, y); g2.drawString("Team " + t.getName() + ": " + t.getScore(), 5, y);
y -= 15; y -= 15;

View File

@ -3,29 +3,22 @@ package nl.andrewlalis.aos_core.model;
import nl.andrewlalis.aos_core.geom.Vec2; import nl.andrewlalis.aos_core.geom.Vec2;
import nl.andrewlalis.aos_core.model.tools.Gun; 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 { public class Bullet extends PhysicsObject {
private final int playerId; private final int playerId;
private final Player player; private final Player player;
private final Gun gun; private final Gun gun;
public Bullet(Player player) { public Bullet(Player player, float sneakAccuracyModifier, float sprintAccuracyModifier) {
this.playerId = player.getId(); this.playerId = player.getId();
this.player = player; 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.gun = player.getGun();
this.setPhysicsProperties(sneakAccuracyModifier, sprintAccuracyModifier);
} }
public Bullet(Vec2 position, Vec2 velocity) { public Bullet(Vec2 position, Vec2 velocity) {
@ -35,6 +28,23 @@ public class Bullet extends PhysicsObject {
this.gun = null; 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() { public int getPlayerId() {
return playerId; return playerId;
} }

View File

@ -7,18 +7,8 @@ import nl.andrewlalis.aos_core.model.tools.GunType;
import java.util.Objects; import java.util.Objects;
public class Player extends PhysicsObject implements Comparable<Player> { public class Player extends PhysicsObject implements Comparable<Player> {
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_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 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 int id;
private final String name; private final String name;
@ -32,13 +22,19 @@ public class Player extends PhysicsObject implements Comparable<Player> {
private boolean reloading; private boolean reloading;
private transient long lastResupply; 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.id = id;
this.name = name; this.name = name;
this.team = team; this.team = team;
this.state = new PlayerControlState(); this.state = new PlayerControlState();
this.gun = new Gun(gunType); this.gun = new Gun(gunType);
this.health = MAX_HEALTH; this.health = maxHealth;
this.useWeapon(); this.useWeapon();
this.lastShot = System.currentTimeMillis(); this.lastShot = System.currentTimeMillis();
} }
@ -95,6 +91,7 @@ public class Player extends PhysicsObject implements Comparable<Player> {
public void useWeapon() { public void useWeapon() {
this.lastShot = System.currentTimeMillis(); this.lastShot = System.currentTimeMillis();
this.gun.decrementBulletCount(); this.gun.decrementBulletCount();
this.shotCount++;
} }
public void startReloading() { public void startReloading() {
@ -116,16 +113,17 @@ public class Player extends PhysicsObject implements Comparable<Player> {
return reloading; return reloading;
} }
public boolean canResupply() { public boolean canResupply(float resupplyCooldown) {
return this.team != null && return this.team != null &&
this.team.getSupplyPoint().dist(this.getPosition()) < Team.SUPPLY_POINT_RADIUS && 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.lastResupply = System.currentTimeMillis();
this.gun.refillClips(); this.gun.refillClips();
this.health = MAX_HEALTH; this.health = maxHealth;
this.resupplyCount++;
} }
public float getHealth() { public float getHealth() {
@ -136,8 +134,8 @@ public class Player extends PhysicsObject implements Comparable<Player> {
this.health = Math.max(this.health - damage, 0.0f); this.health = Math.max(this.health - damage, 0.0f);
} }
public void respawn() { public void respawn(float maxHealth) {
this.resupply(); this.resupply(maxHealth);
this.gun.emptyCurrentClip(); this.gun.emptyCurrentClip();
if (this.team != null) { if (this.team != null) {
this.setPosition(this.team.getSpawnPoint().add(Vec2.random(-Team.SPAWN_RADIUS / 2, Team.SPAWN_RADIUS / 2))); 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<Player> {
!this.state.isSneaking(); !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 @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;

View File

@ -6,12 +6,13 @@ import java.awt.*;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
public class Team implements Serializable { public class Team implements Serializable {
public static final float SPAWN_RADIUS = 3; public static final float SPAWN_RADIUS = 3;
public static final float SUPPLY_POINT_RADIUS = 2; 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 String name;
private final java.awt.Color color; private final java.awt.Color color;
private final Vec2 spawnPoint; private final Vec2 spawnPoint;
@ -22,7 +23,8 @@ public class Team implements Serializable {
private int score; 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.name = name;
this.color = color; this.color = color;
this.spawnPoint = spawnPoint; this.spawnPoint = spawnPoint;
@ -32,6 +34,10 @@ public class Team implements Serializable {
this.score = 0; this.score = 0;
} }
public byte getId() {
return id;
}
public String getName() { public String getName() {
return name; return name;
} }
@ -67,4 +73,21 @@ public class Team implements Serializable {
public void resetScore() { public void resetScore() {
this.score = 0; 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());
}
} }

View File

@ -16,7 +16,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
public class World implements Serializable { public class World implements Serializable {
private final Vec2 size; private final Vec2 size;
private final List<Team> teams; private final Map<Byte, Team> teams;
private final Map<String, GunType> gunTypes; private final Map<String, GunType> gunTypes;
private final Map<Integer, Player> players; private final Map<Integer, Player> players;
private final List<Bullet> bullets; private final List<Bullet> bullets;
@ -24,7 +24,7 @@ public class World implements Serializable {
public World(Vec2 size) { public World(Vec2 size) {
this.size = size; this.size = size;
this.teams = new ArrayList<>(); this.teams = new ConcurrentHashMap<>();
this.gunTypes = new ConcurrentHashMap<>(); this.gunTypes = new ConcurrentHashMap<>();
this.players = new ConcurrentHashMap<>(); this.players = new ConcurrentHashMap<>();
this.bullets = new CopyOnWriteArrayList<>(); this.bullets = new CopyOnWriteArrayList<>();
@ -35,7 +35,7 @@ public class World implements Serializable {
return size; return size;
} }
public List<Team> getTeams() { public Map<Byte, Team> getTeams() {
return teams; return teams;
} }

View File

@ -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()
);
}
}

View File

@ -2,6 +2,7 @@ package nl.andrewlalis.aos_core.net.data;
import nl.andrewlalis.aos_core.model.Bullet; import nl.andrewlalis.aos_core.model.Bullet;
import nl.andrewlalis.aos_core.model.Player; import nl.andrewlalis.aos_core.model.Player;
import nl.andrewlalis.aos_core.model.Team;
import java.io.*; import java.io.*;
import java.util.ArrayList; import java.util.ArrayList;
@ -20,21 +21,24 @@ import java.util.List;
public class WorldUpdate { public class WorldUpdate {
private final List<PlayerUpdate> playerUpdates; private final List<PlayerUpdate> playerUpdates;
private final List<BulletUpdate> bulletUpdates; private final List<BulletUpdate> bulletUpdates;
private final List<TeamUpdate> teamUpdates;
private final List<Sound> soundsToPlay; private final List<Sound> soundsToPlay;
public WorldUpdate() { public WorldUpdate() {
this(new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); this(new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
} }
private WorldUpdate(List<PlayerUpdate> playerUpdates, List<BulletUpdate> bulletUpdates, List<Sound> soundsToPlay) { private WorldUpdate(List<PlayerUpdate> playerUpdates, List<BulletUpdate> bulletUpdates, List<TeamUpdate> teamUpdates, List<Sound> soundsToPlay) {
this.playerUpdates = playerUpdates; this.playerUpdates = playerUpdates;
this.bulletUpdates = bulletUpdates; this.bulletUpdates = bulletUpdates;
this.teamUpdates = teamUpdates;
this.soundsToPlay = soundsToPlay; this.soundsToPlay = soundsToPlay;
} }
public void clear() { public void clear() {
this.playerUpdates.clear(); this.playerUpdates.clear();
this.bulletUpdates.clear(); this.bulletUpdates.clear();
this.teamUpdates.clear();
this.soundsToPlay.clear(); this.soundsToPlay.clear();
} }
@ -46,6 +50,10 @@ public class WorldUpdate {
this.bulletUpdates.add(new BulletUpdate(b)); this.bulletUpdates.add(new BulletUpdate(b));
} }
public void addTeam(Team team) {
this.teamUpdates.add(new TeamUpdate(team));
}
public void addSound(Sound sound) { public void addSound(Sound sound) {
this.soundsToPlay.add(sound); this.soundsToPlay.add(sound);
} }
@ -58,6 +66,10 @@ public class WorldUpdate {
return bulletUpdates; return bulletUpdates;
} }
public List<TeamUpdate> getTeamUpdates() {
return teamUpdates;
}
public List<Sound> getSoundsToPlay() { public List<Sound> getSoundsToPlay() {
return soundsToPlay; return soundsToPlay;
} }
@ -66,6 +78,7 @@ public class WorldUpdate {
int size = 3 * Integer.BYTES + // List size integers. int size = 3 * Integer.BYTES + // List size integers.
this.playerUpdates.size() * PlayerUpdate.BYTES + this.playerUpdates.size() * PlayerUpdate.BYTES +
this.bulletUpdates.size() * BulletUpdate.BYTES + this.bulletUpdates.size() * BulletUpdate.BYTES +
this.teamUpdates.size() * TeamUpdate.BYTES +
this.soundsToPlay.size() * Sound.BYTES; this.soundsToPlay.size() * Sound.BYTES;
ByteArrayOutputStream out = new ByteArrayOutputStream(size); ByteArrayOutputStream out = new ByteArrayOutputStream(size);
DataOutputStream dataOut = new DataOutputStream(out); DataOutputStream dataOut = new DataOutputStream(out);
@ -77,6 +90,10 @@ public class WorldUpdate {
for (var u : this.bulletUpdates) { for (var u : this.bulletUpdates) {
u.write(dataOut); u.write(dataOut);
} }
dataOut.writeInt(this.teamUpdates.size());
for (var u : this.teamUpdates) {
u.write(dataOut);
}
dataOut.writeInt(this.soundsToPlay.size()); dataOut.writeInt(this.soundsToPlay.size());
for (var u : this.soundsToPlay) { for (var u : this.soundsToPlay) {
u.write(dataOut); u.write(dataOut);
@ -100,12 +117,17 @@ public class WorldUpdate {
for (int i = 0; i < bullets; i++) { for (int i = 0; i < bullets; i++) {
bulletUpdates.add(BulletUpdate.read(dataIn)); bulletUpdates.add(BulletUpdate.read(dataIn));
} }
int teams = dataIn.readInt();
List<TeamUpdate> teamUpdates = new ArrayList<>(teams);
for (int i = 0; i < teams; i++) {
teamUpdates.add(TeamUpdate.read(dataIn));
}
int sounds = dataIn.readInt(); int sounds = dataIn.readInt();
List<Sound> soundsToPlay = new ArrayList<>(sounds); List<Sound> soundsToPlay = new ArrayList<>(sounds);
for (int i = 0; i < sounds; i++) { for (int i = 0; i < sounds; i++) {
soundsToPlay.add(Sound.read(dataIn)); soundsToPlay.add(Sound.read(dataIn));
} }
var obj = new WorldUpdate(playerUpdates, bulletUpdates, soundsToPlay); var obj = new WorldUpdate(playerUpdates, bulletUpdates, teamUpdates, soundsToPlay);
dataIn.close(); dataIn.close();
return obj; return obj;
} }

View File

@ -7,6 +7,8 @@ import nl.andrewlalis.aos_server.command.GunsCommand;
import nl.andrewlalis.aos_server.command.ResetCommand; import nl.andrewlalis.aos_server.command.ResetCommand;
import nl.andrewlalis.aos_server.command.chat.ChatCommand; import nl.andrewlalis.aos_server.command.chat.ChatCommand;
import nl.andrewlalis.aos_server.command.chat.GunCommand; 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.Arrays;
import java.util.Map; import java.util.Map;
@ -27,6 +29,8 @@ public class ChatManager {
this.chatCommands.put("gun", new GunCommand()); this.chatCommands.put("gun", new GunCommand());
this.chatCommands.put("reset", new ResetCommand(server)); this.chatCommands.put("reset", new ResetCommand(server));
this.chatCommands.put("guns", new GunsCommand(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) { public void handlePlayerChat(ClientHandler handler, Player player, ChatMessage msg) {

View File

@ -79,14 +79,16 @@ public class Server {
world.getBarricades().add(new Barricade(0, 30, 10, 10)); world.getBarricades().add(new Barricade(0, 30, 10, 10));
world.getBarricades().add(new Barricade(40, 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", "Red",
Color.RED, Color.RED,
new Vec2(3, 3), new Vec2(3, 3),
new Vec2(15, 3), new Vec2(15, 3),
new Vec2(0, 1) new Vec2(0, 1)
)); ));
world.getTeams().add(new Team( world.getTeams().put((byte) 2, new Team(
(byte) 2,
"Blue", "Blue",
Color.BLUE, Color.BLUE,
new Vec2(world.getSize().x() - 3, world.getSize().y() - 3), new Vec2(world.getSize().x() - 3, world.getSize().y() - 3),
@ -124,14 +126,14 @@ public class Server {
public Player registerNewPlayer(String name) { public Player registerNewPlayer(String name) {
int id = ThreadLocalRandom.current().nextInt(1, Integer.MAX_VALUE); int id = ThreadLocalRandom.current().nextInt(1, Integer.MAX_VALUE);
Team team = null; Team team = null;
for (Team t : this.world.getTeams()) { for (Team t : this.world.getTeams().values()) {
if (team == null) { if (team == null) {
team = t; team = t;
} else if (t.getPlayers().size() < team.getPlayers().size()) { } else if (t.getPlayers().size() < team.getPlayers().size()) {
team = t; 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); this.world.getPlayers().put(p.getId(), p);
String message = p.getName() + " connected."; String message = p.getName() + " connected.";
this.broadcastMessage(new SystemChatMessage(SystemChatMessage.Level.INFO, message)); this.broadcastMessage(new SystemChatMessage(SystemChatMessage.Level.INFO, message));
@ -196,10 +198,11 @@ public class Server {
} }
public void resetGame() { public void resetGame() {
for (Team t : this.world.getTeams()) { for (Team t : this.world.getTeams().values()) {
t.resetScore(); t.resetScore();
for (Player p : t.getPlayers()) { for (Player p : t.getPlayers()) {
p.respawn(); p.resetStats();
p.respawn(settings.getPlayerSettings().getMaxHealth());
} }
} }
broadcastMessage(new SystemChatMessage(SystemChatMessage.Level.INFO, "Game has been reset.")); 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() { public void shutdown() {
this.running = false; this.running = false;
try { try {

View File

@ -72,7 +72,7 @@ public class WorldUpdater extends Thread {
for (Player p : this.world.getPlayers().values()) { for (Player p : this.world.getPlayers().values()) {
this.updatePlayerMovement(p, t); this.updatePlayerMovement(p, t);
this.updatePlayerShooting(p); 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); this.worldUpdate.addPlayer(p);
} }
} }
@ -97,31 +97,33 @@ public class WorldUpdater extends Thread {
Vec2 localVelocity = p.getVelocity().rotate(-left.angle()); Vec2 localVelocity = p.getVelocity().rotate(-left.angle());
float vx = localVelocity.x(); float vx = localVelocity.x();
float vy = localVelocity.y(); float vy = localVelocity.y();
float a = server.getSettings().getPlayerSettings().getAcceleration();
if (p.getState().isMovingForward()) { if (p.getState().isMovingForward()) {
vy -= Player.MOVEMENT_ACCELERATION * t; vy -= a * t;
} }
if (p.getState().isMovingBackward()) { if (p.getState().isMovingBackward()) {
vy += Player.MOVEMENT_ACCELERATION * t; vy += a * t;
} }
if (p.getState().isMovingLeft()) { if (p.getState().isMovingLeft()) {
vx -= Player.MOVEMENT_ACCELERATION * t; vx -= a * t;
} }
if (p.getState().isMovingRight()) { if (p.getState().isMovingRight()) {
vx += Player.MOVEMENT_ACCELERATION * t; vx += a * t;
} }
// Compute deceleration. // Compute deceleration.
float d = server.getSettings().getPlayerSettings().getDeceleration();
if (p.getState().isMovingForward() == p.getState().isMovingBackward()) { if (p.getState().isMovingForward() == p.getState().isMovingBackward()) {
if (vy > 0) { if (vy > 0) {
vy = Math.max(0.0f, vy - Player.MOVEMENT_DECELERATION * t); vy = Math.max(0.0f, vy - d * t);
} else { } 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 (p.getState().isMovingLeft() == p.getState().isMovingRight()) {
if (vx > 0) { if (vx > 0) {
vx = Math.max(0.0f, vx - Player.MOVEMENT_DECELERATION * t); vx = Math.max(0.0f, vx - d * t);
} else { } 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; float speedLimit;
if (p.isSprinting()) { if (p.isSprinting()) {
speedLimit = Player.MOVEMENT_SPEED_SPRINT; speedLimit = server.getSettings().getPlayerSettings().getSprintSpeed();
} else if (p.isSneaking()) { } else if (p.isSneaking()) {
speedLimit = Player.MOVEMENT_SPEED_SNEAK; speedLimit = server.getSettings().getPlayerSettings().getSneakSpeed();
} else { } else {
speedLimit = Player.MOVEMENT_SPEED; speedLimit = server.getSettings().getPlayerSettings().getSpeed();
} }
if (newLocalVelocity.mag() > speedLimit) { if (newLocalVelocity.mag() > speedLimit) {
newLocalVelocity = newLocalVelocity.mul(speedLimit / newLocalVelocity.mag()); 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; if (ny + Player.RADIUS > this.world.getSize().y()) ny = this.world.getSize().y() - Player.RADIUS;
p.setPosition(new Vec2(nx, ny)); p.setPosition(new Vec2(nx, ny));
if (p.canResupply()) { if (p.canResupply(server.getSettings().getPlayerSettings().getResupplyCooldown())) {
p.resupply(); p.resupply(server.getSettings().getPlayerSettings().getMaxHealth());
} }
} }
private void updatePlayerShooting(Player p) { private void updatePlayerShooting(Player p) {
if (p.canUseWeapon()) { if (p.canUseWeapon()) {
for (int i = 0; i < p.getGun().getType().getBulletsPerRound(); i++) { 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.world.getBullets().add(b);
this.worldUpdate.addBullet(b); this.worldUpdate.addBullet(b);
} }
@ -256,10 +258,13 @@ public class WorldUpdater extends Thread {
Player shooter = this.world.getPlayers().get(b.getPlayerId()); Player shooter = this.world.getPlayers().get(b.getPlayerId());
this.server.broadcastMessage(new SystemChatMessage(SystemChatMessage.Level.SEVERE, p.getName() + " was shot by " + shooter.getName() + ".")); 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)); this.worldUpdate.addSound(new Sound(p.getPosition(), 1.0f, SoundType.DEATH));
shooter.incrementKillCount();
if (shooter.getTeam() != null) { if (shooter.getTeam() != null) {
shooter.getTeam().incrementScore(); shooter.getTeam().incrementScore();
this.worldUpdate.addTeam(shooter.getTeam());
} }
p.respawn(); p.incrementDeathCount();
p.respawn(server.getSettings().getPlayerSettings().getMaxHealth());
} }
} }
} }

View File

@ -26,7 +26,7 @@ public class ListPlayersCommand implements Command {
player.getName(), player.getName(),
player.getTeam() == null ? "none" : player.getTeam().getName(), player.getTeam() == null ? "none" : player.getTeam().getName(),
player.getHealth(), player.getHealth(),
Player.MAX_HEALTH, this.server.getSettings().getPlayerSettings().getMaxHealth(),
player.getGun().getType().getName() player.getGun().getType().getName()
)) ))
.collect(Collectors.joining("\n")); .collect(Collectors.joining("\n"));

View File

@ -12,7 +12,7 @@ public class GunCommand implements ChatCommand {
@Override @Override
public void execute(ClientHandler handler, Player player, String[] args) { public void execute(ClientHandler handler, Player player, String[] args) {
if (args.length < 1) { 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; return;
} }
String gunName = String.join(" ", args); String gunName = String.join(" ", args);
@ -24,7 +24,7 @@ public class GunCommand implements ChatCommand {
} }
} }
if (gunType == null) { 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; return;
} }
player.setGun(new Gun(gunType)); player.setGun(new Gun(gunType));

View File

@ -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)));
}
}

View File

@ -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)));
}
}