Added simple commands, reloading, and new gun skins.

This commit is contained in:
Andrew Lalis 2021-06-20 10:36:33 +02:00
parent 49bb724f65
commit bd02d89995
12 changed files with 205 additions and 55 deletions

View File

@ -131,7 +131,7 @@ public class Client {
public void sendChat() {
String message = this.chatBuffer.toString().trim();
if (!message.isBlank()) {
if (!message.isBlank() && !message.equals("/")) {
try {
this.messageTransceiver.send(new PlayerChatMessage(this.playerId, message));
} catch (IOException e) {

View File

@ -0,0 +1,22 @@
package nl.andrewlalis.aos_client;
import java.io.IOException;
import java.util.concurrent.ThreadLocalRandom;
public class Tester {
private static final String[] names = {
"andrew", "john", "william", "farnsworth", "xXx_noSc0p3r_xXx"
};
public static void main(String[] args) {
for (int i = 0; i < 2; i++) {
Client client = new Client();
try {
client.connect("localhost", 8035, names[ThreadLocalRandom.current().nextInt(names.length)]);
} catch (IOException | ClassNotFoundException e) {
client.shutdown();
e.printStackTrace();
}
}
}
}

View File

@ -3,6 +3,7 @@ package nl.andrewlalis.aos_client.view;
import nl.andrewlalis.aos_client.Client;
import nl.andrewlalis.aos_core.model.*;
import nl.andrewlalis.aos_core.model.tools.Gun;
import nl.andrewlalis.aos_core.model.tools.GunType;
import nl.andrewlalis.aos_core.net.chat.ChatMessage;
import nl.andrewlalis.aos_core.net.chat.PlayerChatMessage;
import nl.andrewlalis.aos_core.net.chat.SystemChatMessage;
@ -63,7 +64,21 @@ public class GamePanel extends JPanel {
this.drawField(g2, world);
this.drawPlayers(g2, world);
this.drawBullets(g2, world);
this.drawMarkers(g2, world, myPlayer);
g2.setTransform(pre);
// Put shadow gradient.
RadialGradientPaint p = new RadialGradientPaint(
this.getWidth() / 2.0f,
this.getHeight() / 2.0f,
(float) (25 * scale),
new float[]{0.0f, 1.0f},
new Color[]{new Color(0, 0, 0, 0), new Color(0, 0, 0, 255)},
MultipleGradientPaint.CycleMethod.NO_CYCLE
);
g2.setPaint(p);
g2.fillRect(0, 0, this.getWidth(), this.getHeight());
}
private AffineTransform getWorldTransform(Player player, double scale) {
@ -97,12 +112,19 @@ public class GamePanel extends JPanel {
for (Team t : world.getTeams()) {
g2.setColor(t.getColor());
Ellipse2D.Double spawnCircle = new Ellipse2D.Double(
t.getSpawnPoint().x() - Player.RADIUS,
t.getSpawnPoint().y() - Player.RADIUS,
Player.RADIUS * 2,
Player.RADIUS * 2
t.getSpawnPoint().x() - Team.SPAWN_RADIUS,
t.getSpawnPoint().y() - Team.SPAWN_RADIUS,
Team.SPAWN_RADIUS * 2,
Team.SPAWN_RADIUS * 2
);
g2.draw(spawnCircle);
Rectangle2D.Double supplyMarker = new Rectangle2D.Double(
t.getSupplyPoint().x() - Team.SUPPLY_POINT_RADIUS,
t.getSupplyPoint().y() - Team.SUPPLY_POINT_RADIUS,
Team.SUPPLY_POINT_RADIUS * 2,
Team.SUPPLY_POINT_RADIUS * 2
);
g2.draw(supplyMarker);
}
}
@ -110,7 +132,6 @@ public class GamePanel extends JPanel {
for (Player p : world.getPlayers().values()) {
AffineTransform pre = g2.getTransform();
AffineTransform tx = g2.getTransform();
tx.translate(p.getPosition().x(), p.getPosition().y());
tx.rotate(p.getOrientation().x(), p.getOrientation().y());
g2.setTransform(tx);
@ -119,23 +140,30 @@ public class GamePanel extends JPanel {
Color playerColor = p.getTeam() != null ? p.getTeam().getColor() : Color.BLACK;
g2.setColor(playerColor);
g2.fill(dot);
this.drawGun(g2, p.getGun());
g2.setTransform(pre);
}
}
private void drawGun(Graphics2D g2, Gun gun) {
g2.setColor(Color.GRAY);
Rectangle2D.Double gun = new Rectangle2D.Double(
if (gun.getType() == GunType.RIFLE) {
g2.setColor(new Color(59, 43, 0));
} else if (gun.getType() == GunType.SHOTGUN) {
g2.setColor(new Color(18, 18, 17));
}
Rectangle2D.Double gunBarrel = new Rectangle2D.Double(
0,
0.5,
2,
0.25
);
g2.fill(gun);
g2.setTransform(pre);
}
g2.fill(gunBarrel);
}
private void drawBullets(Graphics2D g2, World world) {
g2.setColor(Color.YELLOW);
double bulletSize = 0.5;
g2.setColor(Color.BLACK);
double bulletSize = 0.25;
for (Bullet b : world.getBullets()) {
Ellipse2D.Double bulletShape = new Ellipse2D.Double(
b.getPosition().x() - bulletSize / 2,
@ -147,6 +175,21 @@ public class GamePanel extends JPanel {
}
}
private void drawMarkers(Graphics2D g2, World world, Player myPlayer) {
g2.setColor(Color.WHITE);
for (Player p : world.getPlayers().values()) {
if (p.getId() == myPlayer.getId()) continue;
AffineTransform pre = g2.getTransform();
AffineTransform tx = g2.getTransform();
tx.translate(p.getPosition().x(), p.getPosition().y());
tx.rotate(myPlayer.getTeam().getOrientation().perp().angle());
tx.scale(0.1, 0.1);
g2.setTransform(tx);
g2.drawString(p.getName(), 0, 0);
g2.setTransform(pre);
}
}
private void drawChat(Graphics2D g2, World world) {
int height = g2.getFontMetrics().getHeight();
int y = height;

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -7,6 +7,7 @@ import java.util.Objects;
public class Player extends PhysicsObject {
public static final double MOVEMENT_SPEED = 10; // Movement speed, in m/s
public static final double RADIUS = 0.5; // Collision radius, in meters.
public static final double RESUPPLY_COOLDOWN = 30; // Seconds between allowing resupply.
private final int id;
private final String name;
@ -17,6 +18,7 @@ public class Player extends PhysicsObject {
private transient long lastShot;
private transient long reloadingStartedAt;
private boolean reloading;
private transient long lastResupply;
public Player(int id, String name, Team team) {
this.id = id;
@ -24,7 +26,7 @@ public class Player extends PhysicsObject {
this.team = team;
this.state = new PlayerControlState();
this.state.setPlayerId(this.id);
this.gun = Gun.m1Garand();
this.gun = Gun.winchester();
this.useWeapon();
}
@ -69,7 +71,8 @@ public class Player extends PhysicsObject {
!this.state.isReloading() &&
!this.reloading &&
this.gun.getCurrentClipBulletCount() > 0 &&
this.lastShot + this.gun.getShotCooldownTime() * 1000 < System.currentTimeMillis();
this.lastShot + this.gun.getShotCooldownTime() * 1000 < System.currentTimeMillis() &&
(this.getTeam() == null || this.getTeam().getSpawnPoint().dist(this.getPosition()) > Team.SPAWN_RADIUS);
}
public void useWeapon() {
@ -99,6 +102,17 @@ public class Player extends PhysicsObject {
return reloading;
}
public boolean canResupply() {
return this.team != null &&
this.team.getSupplyPoint().dist(this.getPosition()) < Team.SUPPLY_POINT_RADIUS &&
System.currentTimeMillis() - this.lastResupply > RESUPPLY_COOLDOWN * 1000;
}
public void resupply() {
this.lastResupply = System.currentTimeMillis();
this.gun.refillClips();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;

View File

@ -8,17 +8,22 @@ import java.util.ArrayList;
import java.util.List;
public class Team implements Serializable {
public static final double SPAWN_RADIUS = 3;
public static final double SUPPLY_POINT_RADIUS = 2;
private final String name;
private final java.awt.Color color;
private final Vec2 spawnPoint;
private final Vec2 supplyPoint;
private final Vec2 orientation;
private final List<Player> players;
public Team(String name, Color color, Vec2 spawnPoint, Vec2 orientation) {
public Team(String name, Color color, Vec2 spawnPoint, Vec2 supplyPoint, Vec2 orientation) {
this.name = name;
this.color = color;
this.spawnPoint = spawnPoint;
this.supplyPoint = supplyPoint;
this.orientation = orientation;
this.players = new ArrayList<>();
}
@ -35,6 +40,10 @@ public class Team implements Serializable {
return spawnPoint;
}
public Vec2 getSupplyPoint() {
return supplyPoint;
}
public Vec2 getOrientation() {
return orientation;
}

View File

@ -15,6 +15,12 @@ public class Gun implements Serializable {
*/
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.
*/
@ -44,10 +50,11 @@ public class Gun implements Serializable {
*/
private int clipCount;
private Gun(GunType type, int maxClipCount, int clipSize, double accuracy, double shotCooldownTime, double reloadTime, double bulletSpeed) {
private Gun(GunType type, int maxClipCount, int clipSize, int bulletsPerRound, double accuracy, double shotCooldownTime, double reloadTime, double bulletSpeed) {
this.type = type;
this.maxClipCount = maxClipCount;
this.clipSize = clipSize;
this.bulletsPerRound = bulletsPerRound;
this.accuracy = accuracy;
this.shotCooldownTime = shotCooldownTime;
this.reloadTime = reloadTime;
@ -69,6 +76,10 @@ public class Gun implements Serializable {
return clipSize;
}
public int getBulletsPerRound() {
return bulletsPerRound;
}
public double getAccuracy() {
return accuracy;
}
@ -93,6 +104,10 @@ public class Gun implements Serializable {
return clipCount;
}
public void refillClips() {
this.clipCount = this.maxClipCount;
}
public void decrementBulletCount() {
this.currentClipBulletCount = Math.max(this.currentClipBulletCount - 1, 0);
}
@ -109,14 +124,14 @@ public class Gun implements Serializable {
}
public static Gun ak47() {
return new Gun(GunType.SMG, 4, 30, 0.10, 0.05, 1.2, 90);
return new Gun(GunType.SMG, 4, 30, 1, 0.10, 0.05, 1.2, 90);
}
public static Gun m1Garand() {
return new Gun(GunType.RIFLE, 6, 8, 0.02, 0.75, 1.5, 150);
return new Gun(GunType.RIFLE, 6, 8, 1, 0.02, 0.75, 1.5, 150);
}
public static Gun winchester() {
return new Gun(GunType.SHOTGUN, 8, 4, 0.15, 0.5, 2.0, 75);
return new Gun(GunType.SHOTGUN, 8, 4, 3, 0.15, 0.5, 2.0, 75);
}
}

View File

@ -7,8 +7,11 @@ import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ClientHandler extends Thread {
private final ExecutorService sendingQueue = Executors.newSingleThreadExecutor();
private final Server server;
private final Socket socket;
private final ObjectOutputStream out;
@ -33,9 +36,15 @@ public class ClientHandler extends Thread {
this.running = false;
}
public synchronized void send(Message message) throws IOException {
public void send(Message message) {
this.sendingQueue.submit(() -> {
try {
this.out.reset();
this.out.writeObject(message);
} catch (IOException e) {
e.printStackTrace();
}
});
}
@Override
@ -48,7 +57,7 @@ public class ClientHandler extends Thread {
IdentMessage ident = (IdentMessage) msg;
this.playerId = this.server.registerNewPlayer(ident.getName(), this);
} else if (msg.getType() == Type.CHAT) {
this.server.broadcastPlayerChat(this.playerId, (ChatMessage) msg);
this.server.handlePlayerChat(this, this.playerId, (ChatMessage) msg);
} else if (msg.getType() == Type.PLAYER_CONTROL_STATE) {
this.server.updatePlayerState(((PlayerControlStateMessage) msg).getPlayerControlState());
}

View File

@ -2,6 +2,7 @@ package nl.andrewlalis.aos_server;
import nl.andrewlalis.aos_core.geom.Vec2;
import nl.andrewlalis.aos_core.model.*;
import nl.andrewlalis.aos_core.model.tools.Gun;
import nl.andrewlalis.aos_core.net.Message;
import nl.andrewlalis.aos_core.net.PlayerRegisteredMessage;
import nl.andrewlalis.aos_core.net.WorldUpdateMessage;
@ -13,6 +14,7 @@ import java.awt.*;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.CopyOnWriteArrayList;
@ -37,8 +39,20 @@ public class Server {
world.getBarricades().add(new Barricade(0, 30, 10, 10));
world.getBarricades().add(new Barricade(40, 30, 10, 10));
world.getTeams().add(new Team("Red", Color.RED, new Vec2(3, 3), new Vec2(0, 1)));
world.getTeams().add(new Team("Blue", Color.BLUE, new Vec2(world.getSize().x() - 3, world.getSize().y() - 3), new Vec2(0, -1)));
world.getTeams().add(new Team(
"Red",
Color.RED,
new Vec2(3, 3),
new Vec2(15, 3),
new Vec2(0, 1)
));
world.getTeams().add(new Team(
"Blue",
Color.BLUE,
new Vec2(world.getSize().x() - 3, world.getSize().y() - 3),
new Vec2(world.getSize().x() - 15, world.getSize().y() - 3),
new Vec2(0, -1)
));
this.worldUpdater = new WorldUpdater(this, this.world);
System.out.println("Started AOS-Server TCP on port " + port);
@ -63,11 +77,7 @@ public class Server {
}
Player p = new Player(id, name, team);
this.world.getPlayers().put(p.getId(), p);
try {
handler.send(new PlayerRegisteredMessage(id));
} catch (IOException e) {
e.printStackTrace();
}
String message = p.getName() + " connected.";
this.broadcastMessage(new SystemChatMessage(SystemChatMessage.Level.INFO, message));
System.out.println(message);
@ -97,11 +107,7 @@ public class Server {
public void sendWorldToClients() {
for (ClientHandler handler : this.clientHandlers) {
try {
handler.send(new WorldUpdateMessage(this.world));
} catch (IOException e) {
e.printStackTrace();
}
}
}
@ -114,19 +120,40 @@ public class Server {
public void broadcastMessage(Message message) {
for (ClientHandler handler : this.clientHandlers) {
try {
handler.send(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void broadcastPlayerChat(int playerId, ChatMessage msg) {
public void handlePlayerChat(ClientHandler handler, int playerId, ChatMessage msg) {
Player p = this.world.getPlayers().get(playerId);
if (p == null) return;
if (msg.getText().startsWith("/")) {
String[] words = msg.getText().substring(1).split("\\s+");
if (words.length == 0) return;
String command = words[0];
String[] args = Arrays.copyOfRange(words, 1, words.length);
this.handleCommand(handler, p, command, args);
} else {
this.broadcastMessage(new PlayerChatMessage(p.getId(), msg.getText()));
}
}
public void handleCommand(ClientHandler handler, Player player, String command, String[] args) {
if (command.equalsIgnoreCase("gun")) {
if (args.length < 1) {
return;
}
String gunName = args[0];
if (gunName.equalsIgnoreCase("smg")) {
player.setGun(Gun.ak47());
} else if (gunName.equalsIgnoreCase("rifle")) {
player.setGun(Gun.m1Garand());
} else if (gunName.equalsIgnoreCase("shotgun")) {
player.setGun(Gun.winchester());
}
handler.send(new SystemChatMessage(SystemChatMessage.Level.INFO, "Changed gun to " + player.getGun().getType().name() + "."));
}
}

View File

@ -1,10 +1,8 @@
package nl.andrewlalis.aos_server;
import nl.andrewlalis.aos_core.geom.Vec2;
import nl.andrewlalis.aos_core.model.Barricade;
import nl.andrewlalis.aos_core.model.Bullet;
import nl.andrewlalis.aos_core.model.Player;
import nl.andrewlalis.aos_core.model.World;
import nl.andrewlalis.aos_core.model.*;
import nl.andrewlalis.aos_core.model.tools.GunType;
import nl.andrewlalis.aos_core.net.chat.SystemChatMessage;
import java.util.ArrayList;
@ -113,12 +111,24 @@ public class WorldUpdater extends Thread {
if (ny - Player.RADIUS < 0) ny = Player.RADIUS;
if (ny + Player.RADIUS > this.world.getSize().y()) ny = this.world.getSize().y() - Player.RADIUS;
p.setPosition(new Vec2(nx, ny));
if (p.canResupply()) {
p.resupply();
}
}
private void updatePlayerShooting(Player p) {
if (p.canUseWeapon()) {
for (int i = 0; i < p.getGun().getBulletsPerRound(); i++) {
this.world.getBullets().add(new Bullet(p));
this.world.getSoundsToPlay().add("ak47shot1.wav");
}
String sound = "ak47shot1.wav";
if (p.getGun().getType() == GunType.RIFLE) {
sound = "m1garand-shot1.wav";
} else if (p.getGun().getType() == GunType.SHOTGUN) {
sound = "shotgun-shot1.wav";
}
this.world.getSoundsToPlay().add(sound);
p.useWeapon();
}
if (p.getState().isReloading() && !p.isReloading() && p.getGun().canReload()) {
@ -126,6 +136,7 @@ public class WorldUpdater extends Thread {
}
if (p.isReloading() && p.isReloadingComplete()) {
p.finishReloading();
this.world.getSoundsToPlay().add("reload.wav");
}
}
@ -159,7 +170,7 @@ public class WorldUpdater extends Thread {
double n = ((p.getPosition().x() - x1) * (x2 - x1) + (p.getPosition().y() - y1) * (y2 - y1)) / lineDist;
n = Math.max(Math.min(n, 1), 0);
double dist = p.getPosition().dist(new Vec2(x1 + n * (x2 - x1), y1 + n * (y2 - y1)));
if (dist < Player.RADIUS) {
if (dist < Player.RADIUS && (p.getTeam() == null || p.getTeam().getSpawnPoint().dist(p.getPosition()) > Team.SPAWN_RADIUS)) {
Player killer = this.world.getPlayers().get(b.getPlayerId());
this.server.broadcastMessage(new SystemChatMessage(SystemChatMessage.Level.SEVERE, p.getName() + " was shot by " + killer.getName() + "."));
world.getSoundsToPlay().add("death.wav");