Compare commits

..

No commits in common. "weapons" and "main" have entirely different histories.

36 changed files with 425 additions and 836 deletions

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ace-of-shades</artifactId> <artifactId>ace-of-shades</artifactId>
<groupId>nl.andrewlalis</groupId> <groupId>nl.andrewlalis</groupId>
<version>0.6.0</version> <version>0.5.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -9,15 +9,10 @@ import nl.andrewlalis.aos_client.view.GameRenderer;
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.Team;
import nl.andrewlalis.aos_core.model.World; import nl.andrewlalis.aos_core.model.World;
import nl.andrewlalis.aos_core.model.tools.Grenade;
import nl.andrewlalis.aos_core.model.tools.Gun; import nl.andrewlalis.aos_core.model.tools.Gun;
import nl.andrewlalis.aos_core.model.tools.Knife;
import nl.andrewlalis.aos_core.net.data.DataTypes; import nl.andrewlalis.aos_core.net.data.DataTypes;
import nl.andrewlalis.aos_core.net.data.PlayerDetailUpdate; import nl.andrewlalis.aos_core.net.data.PlayerDetailUpdate;
import nl.andrewlalis.aos_core.net.data.WorldUpdate; import nl.andrewlalis.aos_core.net.data.WorldUpdate;
import nl.andrewlalis.aos_core.net.data.tool.GrenadeData;
import nl.andrewlalis.aos_core.net.data.tool.GunData;
import nl.andrewlalis.aos_core.net.data.tool.KnifeData;
import java.io.IOException; import java.io.IOException;
@ -28,7 +23,7 @@ public class Client {
private final MessageTransceiver messageTransceiver; private final MessageTransceiver messageTransceiver;
private World world; private World world;
private Player player; private Player myPlayer;
private final GameRenderer renderer; private final GameRenderer renderer;
private final SoundManager soundManager; private final SoundManager soundManager;
@ -51,7 +46,7 @@ public class Client {
this.messageTransceiver.start(); this.messageTransceiver.start();
this.chatManager.bindTransceiver(this.messageTransceiver); this.chatManager.bindTransceiver(this.messageTransceiver);
while (this.player == null || this.world == null) { while (this.myPlayer == null || this.world == null) {
try { try {
System.out.println("Waiting for server response and player registration..."); System.out.println("Waiting for server response and player registration...");
Thread.sleep(100); Thread.sleep(100);
@ -93,12 +88,10 @@ public class Client {
player.setPosition(p.getPosition()); player.setPosition(p.getPosition());
player.setOrientation(p.getOrientation()); player.setOrientation(p.getOrientation());
player.setVelocity(p.getVelocity()); player.setVelocity(p.getVelocity());
player.setGun(new Gun(this.world.getGunTypes().get(p.getGunTypeName())));
if (player.getVelocity().mag() > 0) { if (player.getVelocity().mag() > 0) {
this.soundManager.playWalking(player, null); this.soundManager.playWalking(player, myPlayer);
} }
player.getTools().clear();
player.getTools().add(p.getSelectedTool().toTool(this.world));
player.setSelectedToolIndex(0);
} }
} }
for (var t : update.getTeamUpdates()) { for (var t : update.getTeamUpdates()) {
@ -107,7 +100,7 @@ public class Client {
team.setScore(t.getScore()); team.setScore(t.getScore());
} }
} }
this.soundManager.play(update.getSoundsToPlay(), null); this.soundManager.play(update.getSoundsToPlay(), myPlayer);
} }
public void setWorld(World world) { public void setWorld(World world) {
@ -115,11 +108,11 @@ public class Client {
} }
public void setPlayer(Player player) { public void setPlayer(Player player) {
this.player = player; this.myPlayer = player;
} }
public Player getPlayer() { public Player getPlayer() {
return this.player; return myPlayer;
} }
/** /**
@ -128,34 +121,19 @@ public class Client {
* @param update The updated player information from the server. * @param update The updated player information from the server.
*/ */
public void updatePlayer(PlayerDetailUpdate update) { public void updatePlayer(PlayerDetailUpdate update) {
if (this.player != null) { if (this.myPlayer == null) return;
this.player.setHealth(update.getHealth()); this.myPlayer.setHealth(update.getHealth());
this.player.getTools().clear(); this.myPlayer.setReloading(update.isReloading());
for (var td : update.getTools()) { this.myPlayer.setGun(new Gun(this.myPlayer.getGun().getType(), update.getGunCurrentClipBulletCount(), update.getGunClipCount()));
if (td instanceof KnifeData knifeData) {
this.player.getTools().add(new Knife());
} else if (td instanceof GunData gunData) {
this.player.getTools().add(new Gun(
this.world.getGunTypeById(gunData.getTypeId()),
gunData.getCurrentClipBulletCount(),
gunData.getClipCount(),
gunData.isReloading()
));
} else if (td instanceof GrenadeData grenadeData) {
this.player.getTools().add(new Grenade(grenadeData.getGrenades(), grenadeData.getMaxGrenades()));
}
}
this.player.setSelectedToolIndex(update.getSelectedToolIndex());
}
} }
/** /**
* Sends a player control state message to the server, which indicates that * Sends a player control state message to the server, which indicates that
* the player's controls have been updated, due to a key or mouse event. * the player's controls have been updated, due to a key or mouse event.
*/ */
public void sendControlState() { public void sendPlayerState() {
try { try {
this.messageTransceiver.sendData(DataTypes.PLAYER_CONTROL_STATE, this.player.getId(), this.player.getState().toBytes()); this.messageTransceiver.sendData(DataTypes.PLAYER_CONTROL_STATE, myPlayer.getId(), myPlayer.getState().toBytes());
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }

View File

@ -28,7 +28,7 @@ public class PlayerKeyListener extends KeyAdapter {
s.setReloading(false); s.setReloading(false);
s.setSneaking(false); s.setSneaking(false);
s.setSprinting(false); s.setSprinting(false);
this.client.sendControlState(); this.client.sendPlayerState();
if (e.getKeyChar() == '/') this.chatManager.appendToChat('/'); if (e.getKeyChar() == '/') this.chatManager.appendToChat('/');
} }
} else if (this.chatManager.isChatting()) { } else if (this.chatManager.isChatting()) {
@ -63,12 +63,8 @@ public class PlayerKeyListener extends KeyAdapter {
state.setSprinting(true); state.setSprinting(true);
} else if (e.getKeyCode() == KeyEvent.VK_CONTROL) { } else if (e.getKeyCode() == KeyEvent.VK_CONTROL) {
state.setSneaking(true); state.setSneaking(true);
} else if (e.getKeyCode() == KeyEvent.VK_Q) {
state.setSelectingPreviousTool(true);
} else if (e.getKeyCode() == KeyEvent.VK_E) {
state.setSelectingNextTool(true);
} }
this.client.sendControlState(); this.client.sendPlayerState();
} }
@Override @Override
@ -89,11 +85,7 @@ public class PlayerKeyListener extends KeyAdapter {
state.setSprinting(false); state.setSprinting(false);
} else if (e.getKeyCode() == KeyEvent.VK_CONTROL) { } else if (e.getKeyCode() == KeyEvent.VK_CONTROL) {
state.setSneaking(false); state.setSneaking(false);
} else if (e.getKeyCode() == KeyEvent.VK_Q) {
state.setSelectingPreviousTool(false);
} else if (e.getKeyCode() == KeyEvent.VK_E) {
state.setSelectingNextTool(false);
} }
this.client.sendControlState(); this.client.sendPlayerState();
} }
} }

View File

@ -8,15 +8,6 @@ import javax.swing.event.MouseInputAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelEvent;
/**
* Listens for changes to the player's mouse state. This handles the following
* possible control events:
* <ul>
* <li>Pressing the mouse to use the player's weapon.</li>
* <li>Scrolling the mouse wheel to zoom in or out.</li>
* <li>Moving the mouse to change the player's orientation.</li>
* </ul>
*/
public class PlayerMouseListener extends MouseInputAdapter { public class PlayerMouseListener extends MouseInputAdapter {
private static final float MOUSE_UPDATES_PER_SECOND = 60.0f; private static final float MOUSE_UPDATES_PER_SECOND = 60.0f;
private static final long MS_PER_MOUSE_UPDATE = (long) (1000.0f / MOUSE_UPDATES_PER_SECOND); private static final long MS_PER_MOUSE_UPDATE = (long) (1000.0f / MOUSE_UPDATES_PER_SECOND);
@ -34,16 +25,16 @@ public class PlayerMouseListener extends MouseInputAdapter {
@Override @Override
public void mousePressed(MouseEvent e) { public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) { if (e.getButton() == MouseEvent.BUTTON1) {
client.getPlayer().getState().setUsingTool(true); client.getPlayer().getState().setShooting(true);
client.sendControlState(); client.sendPlayerState();
} }
} }
@Override @Override
public void mouseReleased(MouseEvent e) { public void mouseReleased(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) { if (e.getButton() == MouseEvent.BUTTON1) {
client.getPlayer().getState().setUsingTool(false); client.getPlayer().getState().setShooting(false);
client.sendControlState(); client.sendPlayerState();
} }
} }
@ -63,7 +54,7 @@ public class PlayerMouseListener extends MouseInputAdapter {
client.getPlayer().getState().setMouseLocation(centeredMouseLocation); client.getPlayer().getState().setMouseLocation(centeredMouseLocation);
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
if (now - this.lastMouseMove > MS_PER_MOUSE_UPDATE) { if (now - this.lastMouseMove > MS_PER_MOUSE_UPDATE) {
client.sendControlState(); client.sendPlayerState();
this.lastMouseMove = now; this.lastMouseMove = now;
} }
} }
@ -73,10 +64,10 @@ public class PlayerMouseListener extends MouseInputAdapter {
Vec2 c = new Vec2(this.gamePanel.getWidth() / 2.0f, this.gamePanel.getHeight() / 2.0f); Vec2 c = new Vec2(this.gamePanel.getWidth() / 2.0f, this.gamePanel.getHeight() / 2.0f);
Vec2 centeredMouseLocation = new Vec2(e.getX(), e.getY()).sub(c); Vec2 centeredMouseLocation = new Vec2(e.getX(), e.getY()).sub(c);
client.getPlayer().getState().setMouseLocation(centeredMouseLocation); client.getPlayer().getState().setMouseLocation(centeredMouseLocation);
client.getPlayer().getState().setUsingTool(true); client.getPlayer().getState().setShooting(true);
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
if (now - this.lastMouseMove > MS_PER_MOUSE_UPDATE) { if (now - this.lastMouseMove > MS_PER_MOUSE_UPDATE) {
client.sendControlState(); client.sendPlayerState();
this.lastMouseMove = now; this.lastMouseMove = now;
} }
} }

View File

@ -1,9 +1,8 @@
package nl.andrewlalis.aos_client.view; package nl.andrewlalis.aos_client.view;
import nl.andrewlalis.aos_client.Client;
import nl.andrewlalis.aos_client.net.ChatManager; import nl.andrewlalis.aos_client.net.ChatManager;
import nl.andrewlalis.aos_client.Client;
import nl.andrewlalis.aos_core.model.*; import nl.andrewlalis.aos_core.model.*;
import nl.andrewlalis.aos_core.model.tools.Grenade;
import nl.andrewlalis.aos_core.model.tools.Gun; import nl.andrewlalis.aos_core.model.tools.Gun;
import nl.andrewlalis.aos_core.net.chat.ChatMessage; import nl.andrewlalis.aos_core.net.chat.ChatMessage;
import nl.andrewlalis.aos_core.net.chat.ChatType; import nl.andrewlalis.aos_core.net.chat.ChatType;
@ -85,7 +84,7 @@ public class GamePanel extends JPanel {
* @param world The world to render. * @param world The world to render.
*/ */
private void drawWorld(Graphics2D g2, World world) { private void drawWorld(Graphics2D g2, World world) {
Player myPlayer = this.client.getPlayer(); Player myPlayer = client.getPlayer();
if (myPlayer == null) return; if (myPlayer == null) return;
double scale = this.scales[this.scaleIndex]; double scale = this.scales[this.scaleIndex];
AffineTransform pre = g2.getTransform(); AffineTransform pre = g2.getTransform();
@ -194,9 +193,7 @@ public class GamePanel extends JPanel {
Color playerColor = p.getTeam() != null ? p.getTeam().getColor() : Color.BLACK; Color playerColor = p.getTeam() != null ? p.getTeam().getColor() : Color.BLACK;
g2.setColor(playerColor); g2.setColor(playerColor);
g2.fill(dot); g2.fill(dot);
if (p.getSelectedTool() instanceof Gun gun) { this.drawGun(g2, p.getGun());
this.drawGun(g2, gun);
}
g2.setTransform(pre); g2.setTransform(pre);
} }
} }
@ -207,7 +204,7 @@ public class GamePanel extends JPanel {
* @param gun The gun to draw. * @param gun The gun to draw.
*/ */
private void drawGun(Graphics2D g2, Gun gun) { private void drawGun(Graphics2D g2, Gun gun) {
g2.setColor(Color.decode(gun.getType().color())); g2.setColor(Color.decode(gun.getType().getColor()));
Rectangle2D.Double gunBarrel = new Rectangle2D.Double( Rectangle2D.Double gunBarrel = new Rectangle2D.Double(
0, 0,
0.5, 0.5,
@ -305,35 +302,21 @@ public class GamePanel extends JPanel {
Player myPlayer = this.client.getPlayer(); Player myPlayer = this.client.getPlayer();
if (myPlayer == null) return; if (myPlayer == null) return;
int lineHeight = this.getHeight() - 10;
g2.setColor(Color.WHITE); g2.setColor(Color.WHITE);
if (myPlayer.getSelectedTool() != null) { if (myPlayer.isReloading()) {
g2.drawString(myPlayer.getSelectedTool().getName(), 5, lineHeight); g2.drawString("Reloading...", 5, this.getHeight() - 10);
lineHeight -= 10;
} }
Gun gun = myPlayer.getGun();
if (myPlayer.getSelectedTool() instanceof Gun gun) { g2.drawString("Clips: " + gun.getClipCount() + " / " + gun.getType().getMaxClipCount(), 5, this.getHeight() - 20);
if (gun.isReloading()) { g2.drawString("Bullets: " + gun.getCurrentClipBulletCount() + " / " + gun.getType().getClipSize(), 5, this.getHeight() - 30);
g2.drawString("Reloading...", 5, lineHeight);
lineHeight -= 10;
}
g2.drawString("Clips: " + gun.getClipCount() + " / " + gun.getType().maxClipCount(), 5, lineHeight);
lineHeight -= 10;
g2.drawString("Bullets: " + gun.getCurrentClipBulletCount() + " / " + gun.getType().clipSize(), 5, lineHeight);
lineHeight -= 10;
} else if (myPlayer.getSelectedTool() instanceof Grenade grenade) {
g2.drawString(grenade.getGrenadesRemaining() + " / " + grenade.getMaxGrenades() + " grenades", 5, lineHeight);
lineHeight -= 10;
}
g2.setColor(Color.GREEN); g2.setColor(Color.GREEN);
g2.drawString(String.format("Health: %.1f", myPlayer.getHealth()), 5, lineHeight); g2.drawString(String.format("Health: %.1f", myPlayer.getHealth()), 5, this.getHeight() - 40);
lineHeight -= 10;
int y = this.getHeight() - 60;
for (Team t : world.getTeams().values()) { for (Team t : world.getTeams().values()) {
g2.setColor(t.getColor()); g2.setColor(t.getColor());
g2.drawString("Team " + t.getName() + ": " + t.getScore(), 5, lineHeight); g2.drawString("Team " + t.getName() + ": " + t.getScore(), 5, y);
lineHeight -= 15; y -= 15;
} }
} }
} }

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ace-of-shades</artifactId> <artifactId>ace-of-shades</artifactId>
<groupId>nl.andrewlalis</groupId> <groupId>nl.andrewlalis</groupId>
<version>0.6.0</version> <version>0.5.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -4,10 +4,9 @@ module aos_core {
exports nl.andrewlalis.aos_core.net to aos_server, aos_client; exports nl.andrewlalis.aos_core.net to aos_server, aos_client;
exports nl.andrewlalis.aos_core.net.chat to aos_client, aos_server; exports nl.andrewlalis.aos_core.net.chat to aos_client, aos_server;
exports nl.andrewlalis.aos_core.net.data to aos_server, aos_client; exports nl.andrewlalis.aos_core.net.data to aos_server, aos_client;
exports nl.andrewlalis.aos_core.net.data.tool to aos_server, aos_client;
exports nl.andrewlalis.aos_core.model to aos_server, aos_client; exports nl.andrewlalis.aos_core.model to aos_server, aos_client;
exports nl.andrewlalis.aos_core.model.tools to aos_server, aos_client; exports nl.andrewlalis.aos_core.model.tools to aos_client, aos_server;
exports nl.andrewlalis.aos_core.geom to aos_server, aos_client; exports nl.andrewlalis.aos_core.geom to aos_server, aos_client;
exports nl.andrewlalis.aos_core.util to aos_server, aos_client; exports nl.andrewlalis.aos_core.util to aos_server, aos_client;

View File

@ -7,17 +7,17 @@ import nl.andrewlalis.aos_core.model.tools.Gun;
* Represents a single projectile bullet fired from a player's gun. When shot by * 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 * 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 * general direction of the gun, with some perturbation according to the gun's
* inaccuracy and player's sprinting/sneaking status. * 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, Gun gun, float sneakAccuracyModifier, float sprintAccuracyModifier) { public Bullet(Player player, float sneakAccuracyModifier, float sprintAccuracyModifier) {
this.playerId = player.getId(); this.playerId = player.getId();
this.player = player; this.player = player;
this.gun = gun; this.gun = player.getGun();
this.setPhysicsProperties(sneakAccuracyModifier, sprintAccuracyModifier); this.setPhysicsProperties(sneakAccuracyModifier, sprintAccuracyModifier);
} }
@ -34,14 +34,14 @@ public class Bullet extends PhysicsObject {
.add(player.getOrientation().perp().mul(Player.RADIUS)) .add(player.getOrientation().perp().mul(Player.RADIUS))
); );
this.setOrientation(player.getOrientation()); this.setOrientation(player.getOrientation());
float inaccuracy = this.gun.getType().inaccuracy(); float accuracy = player.getGun().getType().getAccuracy();
if (player.isSneaking()) { if (player.isSneaking()) {
inaccuracy *= sneakAccuracyModifier; accuracy *= sneakAccuracyModifier;
} else if (player.isSprinting()) { } else if (player.isSprinting()) {
inaccuracy *= sprintAccuracyModifier; accuracy *= sprintAccuracyModifier;
} }
Vec2 perturbation = Vec2.random(-1, 1).mul(inaccuracy); Vec2 perturbation = Vec2.random(-1, 1).mul(accuracy);
Vec2 localVelocity = this.getOrientation().add(perturbation).mul(this.gun.getType().bulletSpeed()); Vec2 localVelocity = this.getOrientation().add(perturbation).mul(player.getGun().getType().getBulletSpeed());
this.setVelocity(player.getVelocity().add(localVelocity)); this.setVelocity(player.getVelocity().add(localVelocity));
} }

View File

@ -2,38 +2,42 @@ 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;
import nl.andrewlalis.aos_core.model.tools.Knife; import nl.andrewlalis.aos_core.model.tools.GunType;
import nl.andrewlalis.aos_core.model.tools.Tool;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
public class Player extends PhysicsObject implements Comparable<Player> { public class Player extends PhysicsObject implements Comparable<Player> {
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 RADIUS = 0.5f; // Collision radius, in meters. public static final float RADIUS = 0.5f; // Collision radius, in meters.
public static final float TOOL_CHANGE_TIME = 0.5f; // Cooldown when swapping weapons, in seconds.
private final int id; private final int id;
private final String name; private final String name;
private Team team; private Team team;
private PlayerControlState state; private PlayerControlState state;
private final List<Tool> tools; private Gun gun;
private int selectedToolIndex;
private float health; private float health;
private transient long lastShot;
private transient long reloadingStartedAt;
private boolean reloading;
private transient long lastResupply; private transient long lastResupply;
private transient long lastToolChange;
public Player(int id, String name, Team team, float maxHealth) { // Stats
private transient int killCount;
private transient int deathCount;
private transient int shotCount;
private transient int resupplyCount;
private transient int killStreak;
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.health = maxHealth; this.health = maxHealth;
this.tools = new CopyOnWriteArrayList<>(); this.useWeapon();
this.tools.add(new Knife()); this.lastShot = System.currentTimeMillis();
this.selectedToolIndex = 0;
} }
public int getId() { public int getId() {
@ -60,47 +64,56 @@ public class Player extends PhysicsObject implements Comparable<Player> {
this.team = team; this.team = team;
} }
public List<Tool> getTools() { public Gun getGun() {
return this.tools; return gun;
} }
public Tool getSelectedTool() { public void setGun(Gun gun) {
if (this.tools.isEmpty()) return null; this.gun = gun;
return this.tools.get(this.selectedToolIndex);
}
public boolean canChangeSelectedTool() {
return System.currentTimeMillis() - this.lastToolChange > TOOL_CHANGE_TIME * 1000;
}
public void setSelectedToolIndex(int index) {
if (index > this.tools.size() - 1) {
index = 0;
} else if (index < 0) {
index = this.tools.size() - 1;
}
this.selectedToolIndex = index;
this.lastToolChange = System.currentTimeMillis();
}
public void selectNextTool() {
this.setSelectedToolIndex(this.selectedToolIndex + 1);
}
public void selectPreviousTool() {
this.setSelectedToolIndex(this.selectedToolIndex - 1);
} }
public void setHealth(float health) { public void setHealth(float health) {
this.health = health; this.health = health;
} }
public boolean canUseGun() { public void setReloading(boolean reloading) {
return this.state.isUsingTool() && this.reloading = reloading;
this.getSelectedTool() instanceof Gun gun && gun.isUsable() && }
public boolean canUseWeapon() {
return this.state.isShooting() &&
!this.state.isReloading() &&
!this.reloading &&
this.gun.getCurrentClipBulletCount() > 0 &&
this.lastShot + ((long) (this.gun.getType().getShotCooldownTime() * 1000)) < System.currentTimeMillis() &&
(this.getTeam() == null || this.getTeam().getSpawnPoint().dist(this.getPosition()) > Team.SPAWN_RADIUS); (this.getTeam() == null || this.getTeam().getSpawnPoint().dist(this.getPosition()) > Team.SPAWN_RADIUS);
} }
public void useWeapon() {
this.lastShot = System.currentTimeMillis();
this.gun.decrementBulletCount();
this.shotCount++;
}
public void startReloading() {
this.reloading = true;
this.reloadingStartedAt = System.currentTimeMillis();
}
public void finishReloading() {
this.gun.reload();
this.reloading = false;
}
public boolean isReloadingComplete() {
long msSinceStart = System.currentTimeMillis() - this.reloadingStartedAt;
return msSinceStart > this.gun.getType().getReloadTime() * 1000;
}
public boolean isReloading() {
return reloading;
}
public boolean canResupply(float resupplyCooldown) { 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 &&
@ -108,11 +121,10 @@ public class Player extends PhysicsObject implements Comparable<Player> {
} }
public void resupply(float maxHealth) { public void resupply(float maxHealth) {
for (Tool t : this.tools) {
t.resupply();
}
this.health = maxHealth;
this.lastResupply = System.currentTimeMillis(); this.lastResupply = System.currentTimeMillis();
this.gun.refillClips();
this.health = maxHealth;
this.resupplyCount++;
} }
public float getHealth() { public float getHealth() {
@ -125,9 +137,7 @@ public class Player extends PhysicsObject implements Comparable<Player> {
public void respawn(float maxHealth) { public void respawn(float maxHealth) {
this.resupply(maxHealth); this.resupply(maxHealth);
for (Tool t : this.tools) { this.gun.emptyCurrentClip();
t.reset();
}
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)));
} }
@ -143,6 +153,43 @@ 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 int getKillStreak() {
return killStreak;
}
public void incrementDeathCount() {
this.deathCount++;
this.killStreak = 0;
}
public void incrementKillCount() {
this.killCount++;
this.killStreak++;
}
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

@ -1,15 +1,11 @@
package nl.andrewlalis.aos_core.model; 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.net.data.DataTypes;
import java.io.Serializable; import java.io.Serializable;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
/**
* This object represents the player's controls at a given point in time. This
* object is sent by the client to the server each time the client's inputs
* change somehow.
*/
public class PlayerControlState implements Serializable { public class PlayerControlState implements Serializable {
boolean movingLeft; boolean movingLeft;
boolean movingRight; boolean movingRight;
@ -18,12 +14,9 @@ public class PlayerControlState implements Serializable {
boolean sprinting; boolean sprinting;
boolean sneaking; boolean sneaking;
boolean usingTool; boolean shooting;
boolean reloading; boolean reloading;
boolean selectingPreviousTool;
boolean selectingNextTool;
Vec2 mouseLocation; Vec2 mouseLocation;
public boolean isMovingLeft() { public boolean isMovingLeft() {
@ -74,12 +67,12 @@ public class PlayerControlState implements Serializable {
this.sneaking = sneaking; this.sneaking = sneaking;
} }
public boolean isUsingTool() { public boolean isShooting() {
return usingTool; return shooting;
} }
public void setUsingTool(boolean usingTool) { public void setShooting(boolean shooting) {
this.usingTool = usingTool; this.shooting = shooting;
} }
public boolean isReloading() { public boolean isReloading() {
@ -90,22 +83,6 @@ public class PlayerControlState implements Serializable {
this.reloading = reloading; this.reloading = reloading;
} }
public boolean isSelectingPreviousTool() {
return selectingPreviousTool;
}
public void setSelectingPreviousTool(boolean selectingPreviousTool) {
this.selectingPreviousTool = selectingPreviousTool;
}
public boolean isSelectingNextTool() {
return selectingNextTool;
}
public void setSelectingNextTool(boolean selectingNextTool) {
this.selectingNextTool = selectingNextTool;
}
public Vec2 getMouseLocation() { public Vec2 getMouseLocation() {
return mouseLocation; return mouseLocation;
} }
@ -121,12 +98,10 @@ public class PlayerControlState implements Serializable {
if (this.movingRight) flags |= 2; if (this.movingRight) flags |= 2;
if (this.movingForward) flags |= 4; if (this.movingForward) flags |= 4;
if (this.movingBackward) flags |= 8; if (this.movingBackward) flags |= 8;
if (this.usingTool) flags |= 16; if (this.shooting) flags |= 16;
if (this.reloading) flags |= 32; if (this.reloading) flags |= 32;
if (this.sprinting) flags |= 64; if (this.sprinting) flags |= 64;
if (this.sneaking) flags |= 128; if (this.sneaking) flags |= 128;
if (this.selectingPreviousTool) flags |= 256;
if (this.selectingNextTool) flags |= 512;
buffer.putInt(flags); buffer.putInt(flags);
buffer.putFloat(this.mouseLocation.x()); buffer.putFloat(this.mouseLocation.x());
buffer.putFloat(this.mouseLocation.y()); buffer.putFloat(this.mouseLocation.y());
@ -141,12 +116,10 @@ public class PlayerControlState implements Serializable {
s.movingRight = (flags & 2) > 0; s.movingRight = (flags & 2) > 0;
s.movingForward = (flags & 4) > 0; s.movingForward = (flags & 4) > 0;
s.movingBackward = (flags & 8) > 0; s.movingBackward = (flags & 8) > 0;
s.usingTool = (flags & 16) > 0; s.shooting = (flags & 16) > 0;
s.reloading = (flags & 32) > 0; s.reloading = (flags & 32) > 0;
s.sprinting = (flags & 64) > 0; s.sprinting = (flags & 64) > 0;
s.sneaking = (flags & 128) > 0; s.sneaking = (flags & 128) > 0;
s.selectingPreviousTool = (flags & 256) > 0;
s.selectingNextTool = (flags & 512) > 0;
s.mouseLocation = new Vec2(buffer.getFloat(), buffer.getFloat()); s.mouseLocation = new Vec2(buffer.getFloat(), buffer.getFloat());
return s; return s;
} }

View File

@ -14,7 +14,7 @@ public class Team implements Serializable {
private final byte id; private final byte id;
private final String name; private final String name;
private final Color color; private final java.awt.Color color;
private final Vec2 spawnPoint; private final Vec2 spawnPoint;
private final Vec2 supplyPoint; private final Vec2 supplyPoint;
private final Vec2 orientation; private final Vec2 orientation;

View File

@ -43,13 +43,6 @@ public class World implements Serializable {
return gunTypes; return gunTypes;
} }
public GunType getGunTypeById(byte id) {
for (var t : this.gunTypes.values()) {
if (t.id() == id) return t;
}
return null;
}
public Map<Integer, Player> getPlayers() { public Map<Integer, Player> getPlayers() {
return this.players; return this.players;
} }

View File

@ -1,56 +0,0 @@
package nl.andrewlalis.aos_core.model.tools;
/**
* The grenade tool, when equipped, allows the player to throw grenades into the
* world, if there are some grenades available.
*/
public class Grenade implements Tool {
private final int maxGrenades;
private int grenades;
public Grenade(int grenades, int maxGrenades) {
this.grenades = grenades;
this.maxGrenades = maxGrenades;
}
public Grenade() {
this(3, 3);
}
public int getGrenadesRemaining() {
return grenades;
}
public int getMaxGrenades() {
return maxGrenades;
}
/**
* @return The name of the tool, as it should be shown to the players.
*/
@Override
public String getName() {
return "Grenade";
}
@Override
public void use() {
this.grenades--;
}
@Override
public void resupply() {
this.grenades = this.maxGrenades;
}
@Override
public void reset() {
this.resupply();
}
@Override
public boolean isUsable() {
return this.grenades > 0;
}
}

View File

@ -1,9 +1,8 @@
package nl.andrewlalis.aos_core.model.tools; package nl.andrewlalis.aos_core.model.tools;
/** import java.io.Serializable;
* A type of tool that, when equipped, allows the player to shoot bullets.
*/ public class Gun implements Serializable {
public class Gun implements Tool {
GunType type; GunType type;
/** /**
@ -15,21 +14,14 @@ public class Gun implements Tool {
*/ */
private int clipCount; private int clipCount;
private transient long lastShot; public Gun(GunType type, int currentClipBulletCount, int clipCount) {
private transient long reloadingStartedAt;
private boolean reloading;
public Gun(GunType type, int currentClipBulletCount, int clipCount, boolean reloading) {
this.type = type; this.type = type;
this.currentClipBulletCount = currentClipBulletCount; this.currentClipBulletCount = currentClipBulletCount;
this.clipCount = clipCount; this.clipCount = clipCount;
this.reloading = reloading;
this.lastShot = System.currentTimeMillis();
} }
public Gun(GunType type) { public Gun(GunType type) {
this(type, 0, type.maxClipCount(), false); this(type, 0, type.getMaxClipCount());
} }
public GunType getType() { public GunType getType() {
@ -44,6 +36,10 @@ public class Gun implements Tool {
return clipCount; return clipCount;
} }
public void refillClips() {
this.clipCount = this.type.getMaxClipCount();
}
public void decrementBulletCount() { public void decrementBulletCount() {
this.currentClipBulletCount = Math.max(this.currentClipBulletCount - 1, 0); this.currentClipBulletCount = Math.max(this.currentClipBulletCount - 1, 0);
} }
@ -56,59 +52,10 @@ public class Gun implements Tool {
return this.clipCount > 0; return this.clipCount > 0;
} }
public boolean isReloading() {
return reloading;
}
public void startReloading() {
this.reloading = true;
this.reloadingStartedAt = System.currentTimeMillis();
}
public boolean isReloadingComplete() {
return this.reloading && (System.currentTimeMillis() - this.reloadingStartedAt) > this.type.reloadTime() * 1000;
}
public void reload() { public void reload() {
if (this.clipCount > 0) { if (this.clipCount > 0) {
this.clipCount--; this.clipCount--;
this.currentClipBulletCount = this.type.clipSize(); this.currentClipBulletCount = this.type.getClipSize();
} }
this.reloading = false;
}
/**
* @return The name of the tool, as it should be shown to the players.
*/
@Override
public String getName() {
return this.getType().name();
}
@Override
public void use() {
this.lastShot = System.currentTimeMillis();
this.currentClipBulletCount--;
}
@Override
public void resupply() {
this.clipCount = this.type.maxClipCount();
}
@Override
public boolean isUsable() {
return !this.reloading &&
this.currentClipBulletCount > 0 &&
(this.lastShot + (long) (this.type.shotCooldownTime() * 1000)) < System.currentTimeMillis();
}
@Override
public void reset() {
this.reloading = false;
this.currentClipBulletCount = this.type.clipSize();
this.clipCount = this.type.maxClipCount();
this.lastShot = 0;
this.reloadingStartedAt = 0;
} }
} }

View File

@ -3,20 +3,120 @@ package nl.andrewlalis.aos_core.model.tools;
import java.io.Serializable; import java.io.Serializable;
/** /**
* Information about a particular type of gun. * Relatively constant configuration information about a particular type of gun,
* while not including state data for any single gun.
*/ */
public record GunType ( public class GunType implements Serializable {
byte id, /**
String name, * The name of this type of gun. Should be unique among all guns in a world.
GunCategory category, */
String color, private final String name;
int maxClipCount, /**
int clipSize, * The category of gun.
int bulletsPerRound, */
float inaccuracy, private final GunCategory category;
float shotCooldownTime, /**
float reloadTime, * The color of this type of gun, in hex.
float bulletSpeed, */
float baseDamage, private final String color;
float recoil /**
) implements Serializable {} * Maximum number of clips a player can carry when using this gun.
*/
private final int maxClipCount;
/**
* Number of bullets in each clip.
*/
private final int clipSize;
/**
* Number of bullets that are fired simultaneously per round. Usually only
* shotguns fire multiple.
*/
private final int bulletsPerRound;
/**
* How accurate shots from this gun are. 0 = never miss, 1 = complete random.
*/
private final float accuracy;
/**
* How long (in seconds) to wait after each shot, before another is shot.
*/
private final float shotCooldownTime;
/**
* How long (in seconds) for reloading a new clip.
*/
private final float reloadTime;
/**
* How fast the bullet travels (in m/s).
*/
private final float bulletSpeed;
/**
* How much damage the bullet does for a direct hit.
*/
private final float baseDamage;
/**
* How fast the gun pushes the player backwards when shot (in m/s).
*/
private final float recoil;
public GunType(String name, GunCategory category, String color, int maxClipCount, int clipSize, int bulletsPerRound, float accuracy, float shotCooldownTime, float reloadTime, float bulletSpeed, float baseDamage, float recoil) {
this.name = name;
this.category = category;
this.color = color;
this.maxClipCount = maxClipCount;
this.clipSize = clipSize;
this.bulletsPerRound = bulletsPerRound;
this.accuracy = accuracy;
this.shotCooldownTime = shotCooldownTime;
this.reloadTime = reloadTime;
this.bulletSpeed = bulletSpeed;
this.baseDamage = baseDamage;
this.recoil = recoil;
}
public String getName() {
return name;
}
public GunCategory getCategory() {
return category;
}
public String getColor() {
return color;
}
public int getMaxClipCount() {
return maxClipCount;
}
public int getClipSize() {
return clipSize;
}
public int getBulletsPerRound() {
return bulletsPerRound;
}
public float getAccuracy() {
return accuracy;
}
public float getShotCooldownTime() {
return shotCooldownTime;
}
public float getReloadTime() {
return reloadTime;
}
public float getBulletSpeed() {
return bulletSpeed;
}
public float getBaseDamage() {
return baseDamage;
}
public float getRecoil() {
return recoil;
}
}

View File

@ -1,45 +0,0 @@
package nl.andrewlalis.aos_core.model.tools;
public class Knife implements Tool {
/**
* @return The name of the tool, as it should be shown to the players.
*/
@Override
public String getName() {
return "Knife";
}
/**
* Uses the tool.
*/
@Override
public void use() {
}
/**
* Resupplies the tool, when a player is resupplied at their team's area.
*/
@Override
public void resupply() {
}
/**
* Resets the tool to its preferred initial state. This is useful for things
* like respawning.
*/
@Override
public void reset() {
}
/**
* @return True if the player may use the tool to perform an action, or
* false if it's not possible to do so.
*/
@Override
public boolean isUsable() {
return true;
}
}

View File

@ -1,35 +0,0 @@
package nl.andrewlalis.aos_core.model.tools;
import java.io.Serializable;
/**
* Represents some sort of usable tool item that players can equip and use.
*/
public interface Tool extends Serializable {
/**
* @return The name of the tool, as it should be shown to the players.
*/
String getName();
/**
* Uses the tool.
*/
void use();
/**
* Resupplies the tool, when a player is resupplied at their team's area.
*/
void resupply();
/**
* Resets the tool to its preferred initial state. This is useful for things
* like respawning.
*/
void reset();
/**
* @return True if the player may use the tool to perform an action, or
* false if it's not possible to do so.
*/
boolean isUsable();
}

View File

@ -1,86 +1,105 @@
package nl.andrewlalis.aos_core.net.data; package nl.andrewlalis.aos_core.net.data;
import nl.andrewlalis.aos_core.model.Player; import nl.andrewlalis.aos_core.model.Player;
import nl.andrewlalis.aos_core.model.tools.Grenade;
import nl.andrewlalis.aos_core.model.tools.Gun;
import nl.andrewlalis.aos_core.model.tools.Knife;
import nl.andrewlalis.aos_core.model.tools.Tool;
import nl.andrewlalis.aos_core.net.data.tool.GrenadeData;
import nl.andrewlalis.aos_core.net.data.tool.GunData;
import nl.andrewlalis.aos_core.net.data.tool.KnifeData;
import nl.andrewlalis.aos_core.net.data.tool.ToolData;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
public class PlayerDetailUpdate { public class PlayerDetailUpdate {
public static final int BYTES = Float.BYTES + 1 + 5 * Integer.BYTES;
private final float health; private final float health;
private final List<ToolData> tools; private final boolean reloading;
private final int selectedToolIndex;
private final int gunMaxClipCount;
private final int gunClipSize;
private final int gunBulletsPerRound;
private final int gunCurrentClipBulletCount;
private final int gunClipCount;
public PlayerDetailUpdate(Player player) { public PlayerDetailUpdate(Player player) {
this.health = player.getHealth(); this.health = player.getHealth();
this.tools = new ArrayList<>(player.getTools().size()); this.reloading = player.isReloading();
int toolIndex = 0;
for (int i = 0; i < player.getTools().size(); i++) { this.gunMaxClipCount = player.getGun().getType().getMaxClipCount();
var t = player.getTools().get(i); this.gunClipSize = player.getGun().getType().getClipSize();
if (t instanceof Knife k) { this.gunBulletsPerRound = player.getGun().getType().getBulletsPerRound();
this.tools.add(new KnifeData(k)); this.gunCurrentClipBulletCount = player.getGun().getCurrentClipBulletCount();
} else if (t instanceof Gun g) { this.gunClipCount = player.getGun().getClipCount();
this.tools.add(new GunData(g));
} else if (t instanceof Grenade g) {
this.tools.add(new GrenadeData(g));
}
if (t.equals(player.getSelectedTool())) {
toolIndex = i;
}
}
this.selectedToolIndex = toolIndex;
} }
private PlayerDetailUpdate(float health, List<ToolData> tools, int selectedToolIndex) { private PlayerDetailUpdate(float health, boolean reloading, int gunMaxClipCount, int gunClipSize, int gunBulletsPerRound, int gunCurrentClipBulletCount, int gunClipCount) {
this.health = health; this.health = health;
this.tools = tools; this.reloading = reloading;
this.selectedToolIndex = selectedToolIndex; this.gunMaxClipCount = gunMaxClipCount;
this.gunClipSize = gunClipSize;
this.gunBulletsPerRound = gunBulletsPerRound;
this.gunCurrentClipBulletCount = gunCurrentClipBulletCount;
this.gunClipCount = gunClipCount;
} }
public float getHealth() { public float getHealth() {
return health; return health;
} }
public List<ToolData> getTools() { public boolean isReloading() {
return tools; return reloading;
} }
public int getSelectedToolIndex() { public int getGunMaxClipCount() {
return this.selectedToolIndex; return gunMaxClipCount;
}
public int getGunClipSize() {
return gunClipSize;
}
public int getGunBulletsPerRound() {
return gunBulletsPerRound;
}
public int getGunCurrentClipBulletCount() {
return gunCurrentClipBulletCount;
}
public int getGunClipCount() {
return gunClipCount;
}
@Override
public String toString() {
return "PlayerDetailUpdate{" +
"health=" + health +
", reloading=" + reloading +
", gunMaxClipCount=" + gunMaxClipCount +
", gunClipSize=" + gunClipSize +
", gunBulletsPerRound=" + gunBulletsPerRound +
", gunCurrentClipBulletCount=" + gunCurrentClipBulletCount +
", gunClipCount=" + gunClipCount +
'}';
} }
public byte[] toBytes() { public byte[] toBytes() {
int size = Float.BYTES + 2 * Integer.BYTES; ByteBuffer buffer = ByteBuffer.allocate(BYTES);
for (var td : this.tools) { buffer.putFloat(health);
size += 1 + td.getByteSize(); buffer.put((byte) (this.reloading ? 1 : 0));
} buffer.putInt(this.gunMaxClipCount);
ByteBuffer buffer = ByteBuffer.allocate(size); buffer.putInt(this.gunClipSize);
buffer.putFloat(this.health); buffer.putInt(this.gunBulletsPerRound);
buffer.putInt(this.selectedToolIndex); buffer.putInt(this.gunCurrentClipBulletCount);
buffer.putInt(this.tools.size()); buffer.putInt(this.gunClipCount);
for (var td : this.tools) {
td.write(buffer);
}
return buffer.array(); return buffer.array();
} }
public static PlayerDetailUpdate fromBytes(byte[] bytes) { public static PlayerDetailUpdate fromBytes(byte[] bytes) {
ByteBuffer buffer = ByteBuffer.wrap(bytes); ByteBuffer buffer = ByteBuffer.wrap(bytes);
float health = buffer.getFloat(); return new PlayerDetailUpdate(
int selectedToolIndex = buffer.getInt(); buffer.getFloat(),
int toolCount = buffer.getInt(); buffer.get() == 1,
List<ToolData> tools = new ArrayList<>(toolCount); buffer.getInt(),
for (int i = 0; i < toolCount; i++) { buffer.getInt(),
tools.add(ToolData.read(buffer)); buffer.getInt(),
} buffer.getInt(),
return new PlayerDetailUpdate(health, tools, selectedToolIndex); buffer.getInt()
);
} }
} }

View File

@ -2,52 +2,38 @@ package nl.andrewlalis.aos_core.net.data;
import nl.andrewlalis.aos_core.geom.Vec2; import nl.andrewlalis.aos_core.geom.Vec2;
import nl.andrewlalis.aos_core.model.Player; import nl.andrewlalis.aos_core.model.Player;
import nl.andrewlalis.aos_core.model.tools.Grenade;
import nl.andrewlalis.aos_core.model.tools.Gun;
import nl.andrewlalis.aos_core.model.tools.Knife;
import nl.andrewlalis.aos_core.net.data.tool.GrenadeData;
import nl.andrewlalis.aos_core.net.data.tool.GunData;
import nl.andrewlalis.aos_core.net.data.tool.KnifeData;
import nl.andrewlalis.aos_core.net.data.tool.ToolData;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer;
/** /**
* The data that's sent to all clients about a player, and contains only the * The data that's sent to all clients about a player, and contains only the
* information needed to render the player on the screen. * information needed to render the player on the screen.
*/ */
public class PlayerUpdate { public class PlayerUpdate {
public static final int BYTES = Integer.BYTES + 6 * Float.BYTES + 1;
private final int id; private final int id;
private final Vec2 position; private final Vec2 position;
private final Vec2 orientation; private final Vec2 orientation;
private final Vec2 velocity; private final Vec2 velocity;
private final ToolData selectedTool; private final String gunTypeName;
public PlayerUpdate(Player player) { public PlayerUpdate(Player player) {
this.id = player.getId(); this.id = player.getId();
this.position = player.getPosition(); this.position = player.getPosition();
this.orientation = player.getOrientation(); this.orientation = player.getOrientation();
this.velocity = player.getVelocity(); this.velocity = player.getVelocity();
if (player.getSelectedTool() instanceof Knife knife) { this.gunTypeName = player.getGun().getType().getName();
this.selectedTool = new KnifeData(knife);
} else if (player.getSelectedTool() instanceof Gun gun) {
this.selectedTool = new GunData(gun);
} else if (player.getSelectedTool() instanceof Grenade grenade) {
this.selectedTool = new GrenadeData(grenade);
} else {
throw new IllegalArgumentException("Invalid selected tool.");
}
} }
public PlayerUpdate(int id, Vec2 position, Vec2 orientation, Vec2 velocity, ToolData selectedTool) { public PlayerUpdate(int id, Vec2 position, Vec2 orientation, Vec2 velocity, String gunTypeName) {
this.id = id; this.id = id;
this.position = position; this.position = position;
this.orientation = orientation; this.orientation = orientation;
this.velocity = velocity; this.velocity = velocity;
this.selectedTool = selectedTool; this.gunTypeName = gunTypeName;
} }
public int getId() { public int getId() {
@ -66,8 +52,8 @@ public class PlayerUpdate {
return velocity; return velocity;
} }
public ToolData getSelectedTool() { public String getGunTypeName() {
return selectedTool; return gunTypeName;
} }
public void write(DataOutputStream out) throws IOException { public void write(DataOutputStream out) throws IOException {
@ -78,10 +64,8 @@ public class PlayerUpdate {
out.writeFloat(this.orientation.y()); out.writeFloat(this.orientation.y());
out.writeFloat(this.velocity.x()); out.writeFloat(this.velocity.x());
out.writeFloat(this.velocity.y()); out.writeFloat(this.velocity.y());
out.writeInt(1 + this.selectedTool.getByteSize()); out.writeInt(this.gunTypeName.length());
ByteBuffer buffer = ByteBuffer.allocate(1 + this.selectedTool.getByteSize()); out.writeBytes(this.gunTypeName);
this.selectedTool.write(buffer);
out.write(buffer.array());
} }
public static PlayerUpdate read(DataInputStream in) throws IOException { public static PlayerUpdate read(DataInputStream in) throws IOException {
@ -89,9 +73,8 @@ public class PlayerUpdate {
Vec2 position = Vec2.read(in); Vec2 position = Vec2.read(in);
Vec2 orientation = Vec2.read(in); Vec2 orientation = Vec2.read(in);
Vec2 velocity = Vec2.read(in); Vec2 velocity = Vec2.read(in);
int toolByteSize = in.readInt(); int gunTypeNameLength = in.readInt();
ByteBuffer buffer = ByteBuffer.wrap(in.readNBytes(toolByteSize)); String gunTypeName = new String(in.readNBytes(gunTypeNameLength));
ToolData tool = ToolData.read(buffer); return new PlayerUpdate(id, position, orientation, velocity, gunTypeName);
return new PlayerUpdate(id, position, orientation, velocity, tool);
} }
} }

View File

@ -75,7 +75,12 @@ public class WorldUpdate {
} }
public byte[] toBytes() throws IOException { public byte[] toBytes() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream(1024); 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() * SoundData.BYTES;
ByteArrayOutputStream out = new ByteArrayOutputStream(size);
DataOutputStream dataOut = new DataOutputStream(out); DataOutputStream dataOut = new DataOutputStream(out);
dataOut.writeInt(this.playerUpdates.size()); dataOut.writeInt(this.playerUpdates.size());
for (var u : this.playerUpdates) { for (var u : this.playerUpdates) {

View File

@ -1,52 +0,0 @@
package nl.andrewlalis.aos_core.net.data.tool;
import nl.andrewlalis.aos_core.model.World;
import nl.andrewlalis.aos_core.model.tools.Grenade;
import nl.andrewlalis.aos_core.model.tools.Tool;
import java.nio.ByteBuffer;
public class GrenadeData extends ToolData {
private int grenades;
private int maxGrenades;
public GrenadeData() {
super((byte) 1);
}
public GrenadeData(Grenade grenade) {
this();
this.grenades = grenade.getGrenadesRemaining();
this.maxGrenades = grenade.getMaxGrenades();
}
public int getGrenades() {
return grenades;
}
public int getMaxGrenades() {
return maxGrenades;
}
@Override
public int getByteSize() {
return 2 * Integer.BYTES;
}
@Override
protected void putData(ByteBuffer buffer) {
buffer.putInt(this.grenades);
buffer.putInt(this.maxGrenades);
}
@Override
protected void getData(ByteBuffer buffer) {
this.grenades = buffer.getInt();
this.maxGrenades = buffer.getInt();
}
@Override
public Tool toTool(World world) {
return new Grenade(this.grenades, this.maxGrenades);
}
}

View File

@ -1,92 +0,0 @@
package nl.andrewlalis.aos_core.net.data.tool;
import nl.andrewlalis.aos_core.model.World;
import nl.andrewlalis.aos_core.model.tools.Gun;
import nl.andrewlalis.aos_core.model.tools.Tool;
import java.nio.ByteBuffer;
public class GunData extends ToolData {
private byte typeId;
private boolean reloading;
private int clipCount;
private int currentClipBulletCount;
private int bulletsPerRound;
private int clipSize;
private int maxClipCount;
public GunData() {
super((byte) 0);
}
public GunData(Gun gun) {
this();
this.typeId = gun.getType().id();
this.reloading = gun.isReloading();
this.clipCount = gun.getClipCount();
this.currentClipBulletCount = gun.getCurrentClipBulletCount();
this.bulletsPerRound = gun.getType().bulletsPerRound();
this.clipSize = gun.getType().clipSize();
this.maxClipCount = gun.getType().maxClipCount();
}
public byte getTypeId() {
return typeId;
}
public boolean isReloading() {
return reloading;
}
public int getClipCount() {
return clipCount;
}
public int getCurrentClipBulletCount() {
return currentClipBulletCount;
}
public int getBulletsPerRound() {
return bulletsPerRound;
}
public int getClipSize() {
return clipSize;
}
public int getMaxClipCount() {
return maxClipCount;
}
@Override
public int getByteSize() {
return 2 * Byte.BYTES + 5 * Integer.BYTES;
}
@Override
protected void putData(ByteBuffer buffer) {
buffer.put(this.getTypeId());
buffer.put((byte) (this.reloading ? 1 : 0));
buffer.putInt(this.clipCount);
buffer.putInt(this.currentClipBulletCount);
buffer.putInt(this.bulletsPerRound);
buffer.putInt(this.clipSize);
buffer.putInt(this.maxClipCount);
}
@Override
protected void getData(ByteBuffer buffer) {
this.typeId = buffer.get();
this.reloading = buffer.get() == 1;
this.clipCount = buffer.getInt();
this.currentClipBulletCount = buffer.getInt();
this.bulletsPerRound = buffer.getInt();
this.clipSize = buffer.getInt();
this.maxClipCount = buffer.getInt();
}
@Override
public Tool toTool(World world) {
return new Gun(world.getGunTypeById(this.getTypeId()), this.currentClipBulletCount, this.clipCount, this.reloading);
}
}

View File

@ -1,37 +0,0 @@
package nl.andrewlalis.aos_core.net.data.tool;
import nl.andrewlalis.aos_core.model.World;
import nl.andrewlalis.aos_core.model.tools.Knife;
import nl.andrewlalis.aos_core.model.tools.Tool;
import java.nio.ByteBuffer;
public class KnifeData extends ToolData {
public KnifeData() {
super((byte) 2);
}
public KnifeData(Knife knife) {
super((byte) 2);
}
@Override
public int getByteSize() {
return 1;
}
@Override
protected void putData(ByteBuffer buffer) {
buffer.put((byte) 0);
}
@Override
protected void getData(ByteBuffer buffer) {
buffer.get();
}
@Override
public Tool toTool(World world) {
return new Knife();
}
}

View File

@ -1,66 +0,0 @@
package nl.andrewlalis.aos_core.net.data.tool;
import nl.andrewlalis.aos_core.model.World;
import nl.andrewlalis.aos_core.model.tools.Tool;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
public abstract class ToolData {
private static final Map<Byte, Class<? extends ToolData>> dataMapping = new HashMap<>();
static {
dataMapping.put((byte) 0, GunData.class);
dataMapping.put((byte) 1, GrenadeData.class);
dataMapping.put((byte) 2, KnifeData.class);
}
private final byte toolType;
public ToolData(byte toolType) {
this.toolType = toolType;
}
public void write(ByteBuffer buffer) {
buffer.put(this.toolType);
this.putData(buffer);
}
public static ToolData read(ByteBuffer buffer) {
byte type = buffer.get();
var dataClass = dataMapping.get(type);
if (dataClass == null) {
System.err.println("Invalid tool data type byte.");
return null;
}
try {
var data = dataClass.getConstructor().newInstance();
data.getData(buffer);
return data;
} catch (ReflectiveOperationException e) {
e.printStackTrace();
return null;
}
}
public abstract int getByteSize();
/**
* Writes data for this tool data object into the given buffer.
* @param buffer The byte buffer to write data into.
*/
protected abstract void putData(ByteBuffer buffer);
/**
* Reads data for this tool data object from the given buffer.
* @param buffer The byte buffer to read data from.
*/
protected abstract void getData(ByteBuffer buffer);
/**
* Converts this data back into a Tool instance.
* @param world The world in which the tool is being used.
* @return The tool that this data represents.
*/
public abstract Tool toTool(World world);
}

View File

@ -1,8 +0,0 @@
# Networking Protocol
In order to make Ace of Shades as performant as possible, the application makes use of a custom, rather low-level system of communication between the server and client. This document explains how this protocol works.
## Tick Updates
Every time the server computes one game tick, it sends a packet to each client. This packet includes the following information:
- Updated position, velocity, and orientation data for all physics objects.
-

View File

@ -7,7 +7,7 @@
<groupId>nl.andrewlalis</groupId> <groupId>nl.andrewlalis</groupId>
<artifactId>ace-of-shades</artifactId> <artifactId>ace-of-shades</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<version>0.6.0</version> <version>0.5.0</version>
<modules> <modules>
<module>server</module> <module>server</module>
<module>client</module> <module>client</module>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ace-of-shades</artifactId> <artifactId>ace-of-shades</artifactId>
<groupId>nl.andrewlalis</groupId> <groupId>nl.andrewlalis</groupId>
<version>0.6.0</version> <version>0.5.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>ace-of-shades</artifactId> <artifactId>ace-of-shades</artifactId>
<groupId>nl.andrewlalis</groupId> <groupId>nl.andrewlalis</groupId>
<version>0.6.0</version> <version>0.5.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -1,12 +1,7 @@
package nl.andrewlalis.aos_server; package nl.andrewlalis.aos_server;
import nl.andrewlalis.aos_core.geom.Vec2; import nl.andrewlalis.aos_core.geom.Vec2;
import nl.andrewlalis.aos_core.model.Barricade; import nl.andrewlalis.aos_core.model.*;
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.Grenade;
import nl.andrewlalis.aos_core.model.tools.Gun;
import nl.andrewlalis.aos_core.model.tools.GunCategory; import nl.andrewlalis.aos_core.model.tools.GunCategory;
import nl.andrewlalis.aos_core.model.tools.GunType; import nl.andrewlalis.aos_core.model.tools.GunType;
import nl.andrewlalis.aos_core.net.Message; import nl.andrewlalis.aos_core.net.Message;
@ -14,7 +9,6 @@ import nl.andrewlalis.aos_core.net.PlayerUpdateMessage;
import nl.andrewlalis.aos_core.net.Type; import nl.andrewlalis.aos_core.net.Type;
import nl.andrewlalis.aos_core.net.chat.SystemChatMessage; import nl.andrewlalis.aos_core.net.chat.SystemChatMessage;
import nl.andrewlalis.aos_core.net.data.DataTypes; import nl.andrewlalis.aos_core.net.data.DataTypes;
import nl.andrewlalis.aos_core.model.PlayerControlState;
import nl.andrewlalis.aos_core.net.data.PlayerDetailUpdate; import nl.andrewlalis.aos_core.net.data.PlayerDetailUpdate;
import nl.andrewlalis.aos_core.net.data.WorldUpdate; import nl.andrewlalis.aos_core.net.data.WorldUpdate;
import nl.andrewlalis.aos_core.util.ByteUtils; import nl.andrewlalis.aos_core.util.ByteUtils;
@ -65,10 +59,8 @@ public class Server {
} }
private void initWorld() { private void initWorld() {
byte gunTypeId = 0;
for (var gs : this.settings.getGunSettings()) { for (var gs : this.settings.getGunSettings()) {
this.world.getGunTypes().put(gs.getName(), new GunType( this.world.getGunTypes().put(gs.getName(), new GunType(
gunTypeId++,
gs.getName(), gs.getName(),
GunCategory.valueOf(gs.getCategory().toUpperCase()), GunCategory.valueOf(gs.getCategory().toUpperCase()),
gs.getColor(), gs.getColor(),
@ -144,9 +136,7 @@ public class Server {
team = t; team = t;
} }
} }
Player p = new Player(id, name, team, settings.getPlayerSettings().getMaxHealth()); Player p = new Player(id, name, team, this.world.getGunTypes().get(this.settings.getPlayerSettings().getDefaultGun()), settings.getPlayerSettings().getMaxHealth());
p.getTools().add(new Gun(this.world.getGunTypes().get(this.settings.getPlayerSettings().getDefaultGun())));
p.getTools().add(new Grenade(3, 3));
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));
@ -214,6 +204,7 @@ public class Server {
for (Team t : this.world.getTeams().values()) { for (Team t : this.world.getTeams().values()) {
t.resetScore(); t.resetScore();
for (Player p : t.getPlayers()) { for (Player p : t.getPlayers()) {
p.resetStats();
p.respawn(settings.getPlayerSettings().getMaxHealth()); p.respawn(settings.getPlayerSettings().getMaxHealth());
} }
} }

View File

@ -2,7 +2,6 @@ package nl.andrewlalis.aos_server;
import nl.andrewlalis.aos_core.geom.Vec2; import nl.andrewlalis.aos_core.geom.Vec2;
import nl.andrewlalis.aos_core.model.*; import nl.andrewlalis.aos_core.model.*;
import nl.andrewlalis.aos_core.model.tools.Gun;
import nl.andrewlalis.aos_core.model.tools.GunCategory; import nl.andrewlalis.aos_core.model.tools.GunCategory;
import nl.andrewlalis.aos_core.net.chat.SystemChatMessage; import nl.andrewlalis.aos_core.net.chat.SystemChatMessage;
import nl.andrewlalis.aos_core.net.data.SoundData; import nl.andrewlalis.aos_core.net.data.SoundData;
@ -72,7 +71,7 @@ public class WorldUpdater extends Thread {
private void updatePlayers(float t) { private void updatePlayers(float t) {
for (Player p : this.world.getPlayers().values()) { for (Player p : this.world.getPlayers().values()) {
this.updatePlayerMovement(p, t); this.updatePlayerMovement(p, t);
this.updatePlayerInteraction(p); this.updatePlayerShooting(p);
p.setHealth(Math.min(p.getHealth() + server.getSettings().getPlayerSettings().getHealthRegenRate() * t, server.getSettings().getPlayerSettings().getMaxHealth())); p.setHealth(Math.min(p.getHealth() + server.getSettings().getPlayerSettings().getHealthRegenRate() * t, server.getSettings().getPlayerSettings().getMaxHealth()));
this.worldUpdate.addPlayer(p); this.worldUpdate.addPlayer(p);
} }
@ -184,40 +183,33 @@ public class WorldUpdater extends Thread {
} }
} }
private void updatePlayerInteraction(Player p) { private void updatePlayerShooting(Player p) {
if ((p.getState().isSelectingNextTool() ^ p.getState().isSelectingPreviousTool()) && p.canChangeSelectedTool()) { if (p.canUseWeapon()) {
if (p.getState().isSelectingNextTool()) p.selectNextTool(); for (int i = 0; i < p.getGun().getType().getBulletsPerRound(); i++) {
if (p.getState().isSelectingPreviousTool()) p.selectPreviousTool(); Bullet b = new Bullet(p, server.getSettings().getPlayerSettings().getSneakAccuracyModifier(), server.getSettings().getPlayerSettings().getSprintAccuracyModifier());
return; // Don't immediately do any action with the newly-selected tool.
}
if (p.getSelectedTool() instanceof Gun gun) {
if (p.canUseGun()) {
for (int i = 0; i < gun.getType().bulletsPerRound(); i++) {
Bullet b = new Bullet(p, gun, 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);
} }
SoundType soundType = SoundType.SHOT_SMG; SoundType soundType = SoundType.SHOT_SMG;
if (gun.getType().category() == GunCategory.RIFLE) { if (p.getGun().getType().getCategory() == GunCategory.RIFLE) {
soundType = SoundType.SHOT_RIFLE; soundType = SoundType.SHOT_RIFLE;
} else if (gun.getType().category() == GunCategory.SHOTGUN) { } else if (p.getGun().getType().getCategory() == GunCategory.SHOTGUN) {
soundType = SoundType.SHOT_SHOTGUN; soundType = SoundType.SHOT_SHOTGUN;
} else if (gun.getType().category() == GunCategory.MACHINE) { } else if (p.getGun().getType().getCategory() == GunCategory.MACHINE) {
soundType = ThreadLocalRandom.current().nextFloat() < 0.8f ? SoundType.SHOT_MACHINE_GUN_1 : SoundType.SHOT_MACHINE_GUN_2; soundType = ThreadLocalRandom.current().nextFloat() < 0.8f ? SoundType.SHOT_MACHINE_GUN_1 : SoundType.SHOT_MACHINE_GUN_2;
} }
this.worldUpdate.addSound(new SoundData(p.getPosition(), 1.0f, soundType)); this.worldUpdate.addSound(new SoundData(p.getPosition(), 1.0f, soundType));
gun.use(); p.useWeapon();
p.setVelocity(p.getVelocity().add(p.getOrientation().mul(-1 * gun.getType().recoil()))); p.setVelocity(p.getVelocity().add(p.getOrientation().mul(-1 * p.getGun().getType().getRecoil())));
} }
if (p.getState().isReloading() && !gun.isReloading() && gun.canReload()) { if (p.getState().isReloading() && !p.isReloading() && p.getGun().canReload()) {
gun.startReloading(); p.startReloading();
} }
if (gun.isReloading() && gun.isReloadingComplete()) { if (p.isReloading() && p.isReloadingComplete()) {
gun.reload(); p.finishReloading();
this.worldUpdate.addSound(new SoundData(p.getPosition(), 1.0f, SoundType.RELOAD)); this.worldUpdate.addSound(new SoundData(p.getPosition(), 1.0f, SoundType.RELOAD));
} }
} }
}
private void updateBullets(float t) { private void updateBullets(float t) {
List<Bullet> bulletsToRemove = new ArrayList<>(); List<Bullet> bulletsToRemove = new ArrayList<>();
@ -263,16 +255,18 @@ public class WorldUpdater extends Thread {
if (dist < Player.RADIUS && (p.getTeam() == null || p.getTeam().getSpawnPoint().dist(p.getPosition()) > Team.SPAWN_RADIUS)) { if (dist < Player.RADIUS && (p.getTeam() == null || p.getTeam().getSpawnPoint().dist(p.getPosition()) > Team.SPAWN_RADIUS)) {
// Player was shot! // Player was shot!
float damage = (float) (((Player.RADIUS - dist) / Player.RADIUS) * b.getGun().getType().baseDamage()); float damage = (float) (((Player.RADIUS - dist) / Player.RADIUS) * b.getGun().getType().getBaseDamage());
p.takeDamage(damage); p.takeDamage(damage);
if (p.getHealth() == 0.0f) { if (p.getHealth() == 0.0f) {
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 SoundData(p.getPosition(), 1.0f, SoundType.DEATH)); this.worldUpdate.addSound(new SoundData(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()); this.worldUpdate.addTeam(shooter.getTeam());
} }
p.incrementDeathCount();
p.respawn(server.getSettings().getPlayerSettings().getMaxHealth()); p.respawn(server.getSettings().getPlayerSettings().getMaxHealth());
} }
} }

View File

@ -19,13 +19,13 @@ public class GunsCommand implements Command, ChatCommand {
@Override @Override
public void execute(String[] args) { public void execute(String[] args) {
for (var gunType : this.server.getWorld().getGunTypes().values()) { for (var gunType : this.server.getWorld().getGunTypes().values()) {
System.out.println(gunType.name()); System.out.println(gunType.getName());
} }
} }
@Override @Override
public void execute(ClientHandler handler, Player player, String[] args) { public void execute(ClientHandler handler, Player player, String[] args) {
String msg = handler.getServer().getWorld().getGunTypes().values().stream().map(GunType::name).collect(Collectors.joining(", ")); String msg = handler.getServer().getWorld().getGunTypes().values().stream().map(GunType::getName).collect(Collectors.joining(", "));
handler.send(new SystemChatMessage(SystemChatMessage.Level.INFO, msg)); handler.send(new SystemChatMessage(SystemChatMessage.Level.INFO, msg));
} }
} }

View File

@ -1,5 +1,6 @@
package nl.andrewlalis.aos_server.command; package nl.andrewlalis.aos_server.command;
import nl.andrewlalis.aos_core.model.Player;
import nl.andrewlalis.aos_server.Server; import nl.andrewlalis.aos_server.Server;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -20,12 +21,13 @@ public class ListPlayersCommand implements Command {
String message = this.server.getWorld().getPlayers().values().stream() String message = this.server.getWorld().getPlayers().values().stream()
.sorted() .sorted()
.map(player -> String.format( .map(player -> String.format(
"%d | %s Team: %s, Health: %.1f / %.1f", "%d | %s Team: %s, Health: %.1f / %.1f, Gun: %s",
player.getId(), player.getId(),
player.getName(), player.getName(),
player.getTeam() == null ? "none" : player.getTeam().getName(), player.getTeam() == null ? "none" : player.getTeam().getName(),
player.getHealth(), player.getHealth(),
this.server.getSettings().getPlayerSettings().getMaxHealth() this.server.getSettings().getPlayerSettings().getMaxHealth(),
player.getGun().getType().getName()
)) ))
.collect(Collectors.joining("\n")); .collect(Collectors.joining("\n"));
System.out.println(message); System.out.println(message);

View File

@ -6,6 +6,8 @@ import nl.andrewlalis.aos_core.model.tools.GunType;
import nl.andrewlalis.aos_core.net.chat.SystemChatMessage; import nl.andrewlalis.aos_core.net.chat.SystemChatMessage;
import nl.andrewlalis.aos_server.ClientHandler; import nl.andrewlalis.aos_server.ClientHandler;
import java.util.Locale;
public class GunCommand implements ChatCommand { 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) {
@ -16,7 +18,7 @@ public class GunCommand implements ChatCommand {
String gunName = String.join(" ", args); String gunName = String.join(" ", args);
GunType gunType = null; GunType gunType = null;
for (GunType type : handler.getServer().getWorld().getGunTypes().values()) { for (GunType type : handler.getServer().getWorld().getGunTypes().values()) {
if (type.name().equalsIgnoreCase(gunName)) { if (type.getName().equalsIgnoreCase(gunName)) {
gunType = type; gunType = type;
break; break;
} }
@ -25,16 +27,7 @@ public class GunCommand implements ChatCommand {
handler.send(new SystemChatMessage(SystemChatMessage.Level.WARNING, "Unknown gun name. Use /guns to see available guns.")); handler.send(new SystemChatMessage(SystemChatMessage.Level.WARNING, "Unknown gun name. Use /guns to see available guns."));
return; return;
} }
boolean gunSet = false; player.setGun(new Gun(gunType));
for (int i = 0; i < player.getTools().size(); i++) { handler.send(new SystemChatMessage(SystemChatMessage.Level.INFO, "Changed gun to " + player.getGun().getType().getName() + "."));
if (player.getTools().get(i) instanceof Gun) {
player.getTools().set(i, new Gun(gunType));
gunSet = true;
}
}
if (!gunSet) {
player.getTools().add(new Gun(gunType));
}
handler.send(new SystemChatMessage(SystemChatMessage.Level.INFO, "Changed gun to " + gunType.name() + "."));
} }
} }

View File

@ -7,8 +7,7 @@ import nl.andrewlalis.aos_server.ClientHandler;
public class KillDeathRatioCommand implements ChatCommand { public class KillDeathRatioCommand implements ChatCommand {
@Override @Override
public void execute(ClientHandler handler, Player player, String[] args) { public void execute(ClientHandler handler, Player player, String[] args) {
// float ratio = player.getKillCount() / ((float) player.getDeathCount()); float ratio = player.getKillCount() / ((float) player.getDeathCount());
// handler.send(new SystemChatMessage(SystemChatMessage.Level.INFO, String.format("Your Kill/Death ratio is %.2f.", ratio))); handler.send(new SystemChatMessage(SystemChatMessage.Level.INFO, String.format("Your Kill/Death ratio is %.2f.", ratio)));
handler.send(new SystemChatMessage(SystemChatMessage.Level.WARNING, "K/D command not yet implemented."));
} }
} }

View File

@ -1,9 +0,0 @@
package nl.andrewlalis.aos_server.model;
public class PlayerStatistics {
private transient int killCount;
private transient int deathCount;
private transient int shotCount;
private transient int resupplyCount;
private transient int killStreak;
}

View File

@ -52,9 +52,9 @@ player-settings:
max-health: 100 max-health: 100
# How quickly players regenerate health, in points per second. Set to 0 to disable regeneration. # How quickly players regenerate health, in points per second. Set to 0 to disable regeneration.
health-regen-rate: 1.0 health-regen-rate: 1.0
# How much sneaking affects gun inaccuracy. Values less than 0 increase inaccuracy, and greater than 0 decrease inaccuracy. # How much sneaking affects gun accuracy. Values less than 0 increase accuracy, and greater than 0 decrease accuracy.
sneak-accuracy-modifier: 0.5 sneak-accuracy-modifier: 0.5
# How much sprinting affects gun inaccuracy. Values less than 0 increase inaccuracy, and greater than 0 decrease inaccuracy. # How much sprinting affects gun accuracy. Values less than 0 increase accuracy, and greater than 0 decrease accuracy.
sprint-accuracy-modifier: 1.5 sprint-accuracy-modifier: 1.5
# Should be the name of one of the guns defined in "gun-settings". # Should be the name of one of the guns defined in "gun-settings".
default-gun: M1 Garand default-gun: M1 Garand
@ -78,7 +78,7 @@ gun-settings:
max-clip-count: 4 # The maximum number of clips which a player can hold for this gun. max-clip-count: 4 # The maximum number of clips which a player can hold for this gun.
clip-size: 30 # The number of rounds in each clip. clip-size: 30 # The number of rounds in each clip.
bullets-per-round: 1 # The number of bullets spawned for each round fired. bullets-per-round: 1 # The number of bullets spawned for each round fired.
inaccuracy: 0.10 # The inaccuracy of the gun, or rather deviation. Increase this to decrease inaccuracy. accuracy: 0.10 # The accuracy of the gun, or rather deviation. Increase this to decrease accuracy.
shot-cooldown-time: 0.05 # How many seconds to wait after shooting before you can shoot again. shot-cooldown-time: 0.05 # How many seconds to wait after shooting before you can shoot again.
reload-time: 1.2 # How many seconds to wait while reloading. reload-time: 1.2 # How many seconds to wait while reloading.
bullet-speed: 90 # How fast the bullets from this gun fly, in meters per second. bullet-speed: 90 # How fast the bullets from this gun fly, in meters per second.
@ -91,7 +91,7 @@ gun-settings:
max-clip-count: 5 max-clip-count: 5
clip-size: 16 clip-size: 16
bullets-per-round: 1 bullets-per-round: 1
inaccuracy: 0.20 accuracy: 0.20
shot-cooldown-time: 0.25 shot-cooldown-time: 0.25
reload-time: 1.0 reload-time: 1.0
bullet-speed: 40 bullet-speed: 40
@ -104,7 +104,7 @@ gun-settings:
max-clip-count: 3 max-clip-count: 3
clip-size: 500 clip-size: 500
bullets-per-round: 1 bullets-per-round: 1
inaccuracy: 0.10 accuracy: 0.10
shot-cooldown-time: 0.04 shot-cooldown-time: 0.04
reload-time: 3 reload-time: 3
bullet-speed: 60 bullet-speed: 60
@ -117,7 +117,7 @@ gun-settings:
max-clip-count: 3 max-clip-count: 3
clip-size: 100 clip-size: 100
bullets-per-round: 1 bullets-per-round: 1
inaccuracy: 0.08 accuracy: 0.08
shot-cooldown-time: 0.03 shot-cooldown-time: 0.03
reload-time: 3.5 reload-time: 3.5
bullet-speed: 80 bullet-speed: 80
@ -130,7 +130,7 @@ gun-settings:
max-clip-count: 10 max-clip-count: 10
clip-size: 8 clip-size: 8
bullets-per-round: 1 bullets-per-round: 1
inaccuracy: 0.02 accuracy: 0.02
shot-cooldown-time: 0.75 shot-cooldown-time: 0.75
reload-time: 0.75 reload-time: 0.75
bullet-speed: 150 bullet-speed: 150
@ -143,7 +143,7 @@ gun-settings:
max-clip-count: 8 max-clip-count: 8
clip-size: 6 clip-size: 6
bullets-per-round: 5 bullets-per-round: 5
inaccuracy: 0.15 accuracy: 0.15
shot-cooldown-time: 0.5 shot-cooldown-time: 0.5
reload-time: 2.0 reload-time: 2.0
bullet-speed: 75 bullet-speed: 75