Browse Source

More improvements to the EntityState-system

Tankernn 7 years ago
parent
commit
60d54c3d23
25 changed files with 502 additions and 356 deletions
  1. 37 17
      src/main/java/eu/tankernn/gameEngine/TankernnGame3D.java
  2. 24 16
      src/main/java/eu/tankernn/gameEngine/World.java
  3. 9 12
      src/main/java/eu/tankernn/gameEngine/entities/CarBehavior.java
  4. 43 28
      src/main/java/eu/tankernn/gameEngine/entities/Entity3D.java
  5. 30 0
      src/main/java/eu/tankernn/gameEngine/entities/EntityFactory.java
  6. 55 15
      src/main/java/eu/tankernn/gameEngine/entities/EntityState.java
  7. 36 0
      src/main/java/eu/tankernn/gameEngine/entities/GameContext.java
  8. 0 89
      src/main/java/eu/tankernn/gameEngine/entities/Player.java
  9. 65 0
      src/main/java/eu/tankernn/gameEngine/entities/PlayerBehavior.java
  10. 2 2
      src/main/java/eu/tankernn/gameEngine/entities/PlayerCamera.java
  11. 21 0
      src/main/java/eu/tankernn/gameEngine/entities/ai/Behavior.java
  12. 23 0
      src/main/java/eu/tankernn/gameEngine/entities/ai/CollisionBehavior.java
  13. 17 0
      src/main/java/eu/tankernn/gameEngine/entities/ai/DieOnCollisionBehavior.java
  14. 30 0
      src/main/java/eu/tankernn/gameEngine/entities/ai/FollowBehavior.java
  15. 1 1
      src/main/java/eu/tankernn/gameEngine/entities/ai/RoamingArea.java
  16. 10 4
      src/main/java/eu/tankernn/gameEngine/entities/ai/RoamingBehavior.java
  17. 0 13
      src/main/java/eu/tankernn/gameEngine/entities/npc/Behavior.java
  18. 0 41
      src/main/java/eu/tankernn/gameEngine/entities/npc/NPC.java
  19. 32 0
      src/main/java/eu/tankernn/gameEngine/entities/npc/NpcState.java
  20. 0 27
      src/main/java/eu/tankernn/gameEngine/entities/projectiles/BasicProjectile.java
  21. 0 51
      src/main/java/eu/tankernn/gameEngine/entities/projectiles/Projectile.java
  22. 48 0
      src/main/java/eu/tankernn/gameEngine/entities/projectiles/ProjectileState.java
  23. 0 38
      src/main/java/eu/tankernn/gameEngine/entities/projectiles/TargetedProjectile.java
  24. 13 0
      src/main/java/eu/tankernn/gameEngine/loader/Loader.java
  25. 6 2
      src/main/java/eu/tankernn/gameEngine/particles/ParticleSystem.java

+ 37 - 17
src/main/java/eu/tankernn/gameEngine/TankernnGame3D.java

@@ -2,6 +2,7 @@ package eu.tankernn.gameEngine;
 
 import java.io.IOException;
 import java.util.Collection;
+import java.util.stream.Collectors;
 
 import org.lwjgl.opengl.Display;
 import org.lwjgl.opengl.GL30;
@@ -9,12 +10,14 @@ import org.lwjgl.util.vector.Vector4f;
 
 import eu.tankernn.gameEngine.entities.Camera;
 import eu.tankernn.gameEngine.entities.Entity3D;
-import eu.tankernn.gameEngine.entities.Player;
+import eu.tankernn.gameEngine.entities.GameContext;
 import eu.tankernn.gameEngine.environmentMap.EnvironmentMapRenderer;
 import eu.tankernn.gameEngine.loader.Loader;
+import eu.tankernn.gameEngine.loader.models.AABB;
 import eu.tankernn.gameEngine.loader.textures.Texture;
 import eu.tankernn.gameEngine.particles.ParticleMaster;
 import eu.tankernn.gameEngine.postProcessing.PostProcessor;
+import eu.tankernn.gameEngine.renderEngine.DisplayManager;
 import eu.tankernn.gameEngine.renderEngine.Fbo;
 import eu.tankernn.gameEngine.renderEngine.MasterRenderer;
 import eu.tankernn.gameEngine.renderEngine.MultisampleMultitargetFbo;
@@ -36,18 +39,17 @@ public class TankernnGame3D extends TankernnGame {
 	protected Camera camera;
 	protected Skybox sky;
 	protected MousePicker picker;
-	
+
 	protected World world;
-	protected Player player;
-	
+	protected Entity3D player;
+
 	private MultisampleMultitargetFbo multisampleFbo = new MultisampleMultitargetFbo(Display.getWidth(),
 			Display.getHeight());
 	private Fbo outputFbo = new Fbo(Display.getWidth(), Display.getHeight(), Fbo.DEPTH_TEXTURE),
 			outputFbo2 = new Fbo(Display.getWidth(), Display.getHeight(), Fbo.DEPTH_TEXTURE);
-	
+
 	public TankernnGame3D(String name, String[] dayTextures, String[] nightTextures) {
 		super(name);
-		world = new World(loader);
 		try {
 			loader.readModelSpecification(new InternalFile("models.json"));
 		} catch (IOException e) {
@@ -59,23 +61,41 @@ public class TankernnGame3D extends TankernnGame {
 
 	public void update() {
 		super.update();
-		
-		world.update();
-		
-		player.move();
+
+		GameContext ctx = new GameContext(true, DisplayManager.getFrameTimeSeconds(),
+				world.getEntities().values().stream().map(Entity3D::getState).collect(Collectors.toSet())) {
+
+			@Override
+			public float getTerrainHeight(float x, float z) {
+				return getTerrain().getTerrainHeightByWorldPos(x, z);
+			}
+
+			@Override
+			public float getHeight(int entityId) {
+				return world.getEntities().get(entityId).getHeight();
+			}
+
+			@Override
+			public AABB getBoundingBox(int entityId) {
+				return world.getEntities().get(entityId).getBoundingBox();
+			}
+		};
+
+		world.update(ctx);
+
 		picker.update(world.getTerrainPack(), world.getEntities().values(), guiMaster.getGuis());
 		camera.update();
 		world.getTerrainPack().update(player);
 		particleMaster.update(camera);
 		DistanceSorter.sort(world.getLights(), camera);
-		
+
 		audioMaster.setListenerPosition(player.getPosition());
 	}
-	
+
 	protected void preRender() {
-		
+
 	}
-	
+
 	protected void render() {
 		Scene scene = new Scene(world.getEntities().values(), world.getTerrainPack(), world.getLights(), camera, sky);
 
@@ -90,7 +110,7 @@ public class TankernnGame3D extends TankernnGame {
 		particleMaster.renderParticles(camera);
 		floatingRenderer.render(world.getFloatTextures(), camera);
 	}
-	
+
 	protected void postRender() {
 		multisampleFbo.unbindFrameBuffer();
 
@@ -100,7 +120,7 @@ public class TankernnGame3D extends TankernnGame {
 		postProcessor.doPostProcessing(outputFbo.getColourTexture(), outputFbo2.getColourTexture());
 		super.render();
 	}
-	
+
 	public void fullRender() {
 		this.preRender();
 		this.render();
@@ -135,7 +155,7 @@ public class TankernnGame3D extends TankernnGame {
 		return world;
 	}
 
-	public Player getPlayer() {
+	public Entity3D getPlayer() {
 		return player;
 	}
 }

+ 24 - 16
src/main/java/eu/tankernn/gameEngine/World.java

@@ -6,44 +6,38 @@ import java.util.List;
 import java.util.Map;
 
 import eu.tankernn.gameEngine.entities.Entity3D;
+import eu.tankernn.gameEngine.entities.EntityFactory;
 import eu.tankernn.gameEngine.entities.EntityState;
+import eu.tankernn.gameEngine.entities.GameContext;
 import eu.tankernn.gameEngine.entities.ILight;
-import eu.tankernn.gameEngine.entities.projectiles.Projectile;
 import eu.tankernn.gameEngine.loader.Loader;
 import eu.tankernn.gameEngine.multiplayer.WorldState;
+import eu.tankernn.gameEngine.particles.ParticleMaster;
 import eu.tankernn.gameEngine.renderEngine.gui.floating.FloatingTexture;
 import eu.tankernn.gameEngine.terrains.TerrainPack;
 
 public class World {
 	private Map<Integer, Entity3D> entities = new HashMap<>();
-	private List<Projectile> projectiles = new ArrayList<>();
 	private List<ILight> lights = new ArrayList<>();
 	private List<FloatingTexture> floatTextures = new ArrayList<>();
 	private TerrainPack terrainPack;
 
-	private final Loader loader;
+	private final EntityFactory entityFactory;
 
-	public World(Loader loader) {
-		this.loader = loader;
+	public World(Loader loader, ParticleMaster particleMaster, TerrainPack terrainPack) {
+		this.entityFactory = new EntityFactory(loader, particleMaster, this);
+		this.terrainPack = terrainPack;
 	}
 
-	public void update() {
-		entities.values().forEach(Entity3D::update);
+	public void update(GameContext ctx) {
+		entities.values().forEach(e -> e.update(ctx));
 		entities.values().removeIf(Entity3D::isDead);
-
-		projectiles.forEach(Projectile::update);
-		projectiles.removeIf(Projectile::isDead);
-		projectiles.forEach((p) -> p.checkCollision(entities.values()));
 	}
 
 	public Map<Integer, Entity3D> getEntities() {
 		return entities;
 	}
 
-	public List<Projectile> getProjectiles() {
-		return projectiles;
-	}
-
 	public List<ILight> getLights() {
 		return lights;
 	}
@@ -75,7 +69,21 @@ public class World {
 		if (entities.containsKey(s.getId()))
 			entities.get(s.getId()).setState(s);
 		else
-			entities.put(s.getId(), new Entity3D(s, loader, terrainPack));
+			spawnEntity(s);
+	}
+
+	public Entity3D spawnEntity(EntityState state) {
+		if (entities.containsKey(state.getId())) {
+			throw new RuntimeException("Entity with id " + state.getId() + " has already been spawned.");
+		} else {
+			Entity3D e = entityFactory.getEntity(state);
+			entities.put(state.getId(), e);
+			return e;
+		}
+	}
+
+	public float getTerrainHeigh(float x, float z) {
+		return terrainPack.getTerrainHeightByWorldPos(x, z);
 	}
 
 }

+ 9 - 12
src/main/java/eu/tankernn/gameEngine/entities/Car.java → src/main/java/eu/tankernn/gameEngine/entities/CarBehavior.java

@@ -1,29 +1,26 @@
 package eu.tankernn.gameEngine.entities;
 
 import org.lwjgl.input.Keyboard;
-import org.lwjgl.util.vector.Vector3f;
 
-import eu.tankernn.gameEngine.loader.models.AABB;
-import eu.tankernn.gameEngine.loader.models.TexturedModel;
 import eu.tankernn.gameEngine.renderEngine.DisplayManager;
-import eu.tankernn.gameEngine.terrains.TerrainPack;
 
-public class Car extends Player {
+public class CarBehavior extends PlayerBehavior {
 
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 6701432444282126422L;
+	
 	private static final float MAX_SPEED = 100.0f, ACCELERATION = 20.0f, DECELERATION = 10.0f, BRAKE = 40.0f,
 			TURN_FORCE = 160.0f;
 
-	public Car(TexturedModel model, Vector3f position, Vector3f rotation, Vector3f scale, AABB boundingBox, TerrainPack terrainPack) {
-		super(model, position, rotation, scale, boundingBox, terrainPack);
-	}
-
 	@Override
-	public void move() {
+	public void update(GameContext impl) {
+		if (!impl.isClient())
+			return;
 		checkInputs();
-		super.move();
 	}
 
-	@Override
 	protected void checkInputs() {
 		if (Keyboard.isKeyDown(Keyboard.KEY_W)) {
 			this.currentSpeed += ACCELERATION * DisplayManager.getFrameTimeSeconds();

+ 43 - 28
src/main/java/eu/tankernn/gameEngine/entities/Entity3D.java

@@ -2,56 +2,54 @@ package eu.tankernn.gameEngine.entities;
 
 import org.lwjgl.util.vector.Vector3f;
 
+import eu.tankernn.gameEngine.World;
 import eu.tankernn.gameEngine.animation.model.AnimatedModel;
 import eu.tankernn.gameEngine.audio.Source;
-import eu.tankernn.gameEngine.loader.Loader;
 import eu.tankernn.gameEngine.loader.models.AABB;
 import eu.tankernn.gameEngine.loader.models.TexturedModel;
-import eu.tankernn.gameEngine.terrains.TerrainPack;
+import eu.tankernn.gameEngine.particles.ParticleSystem;
 import eu.tankernn.gameEngine.util.IPositionable;
 
 public class Entity3D implements IPositionable {
 
-	private TexturedModel model;
 	private EntityState state;
+	private TexturedModel model;
+	private ParticleSystem particleSystem;
 	private AABB boundingBox;
-	protected boolean dead;
-	protected TerrainPack terrain;
-	protected Source source = new Source();
+	protected World world;
 
-	public Entity3D(TexturedModel model, Vector3f position, AABB boundingBox, TerrainPack terrain) {
-		this(model, position, new Vector3f(0, 0, 0), new Vector3f(0, 0, 0), boundingBox, terrain);
-	}
+	protected Source source = new Source();
 
-	public Entity3D(TexturedModel model, Vector3f position, Vector3f rotation, Vector3f scale, AABB boundingBox,
-			TerrainPack terrain) {
+	public Entity3D(EntityState state, TexturedModel model, ParticleSystem particleSystem, AABB boundingBox,
+			World world) {
+		this.state = state;
 		this.model = model;
-		this.state = new EntityState(model == null ? -1 : model.getId(), position, rotation, new Vector3f(0, 0, 0), scale);
+		this.particleSystem = particleSystem;
 		this.boundingBox = boundingBox;
-		this.boundingBox.updatePosition(position);
-		this.terrain = terrain;
-	}
-
-	public Entity3D(EntityState state, Loader loader, TerrainPack terrain) {
-		this.state = state;
-		this.model = loader.getModel(state.getModelId());
-		this.boundingBox = loader.getBoundingBox(model.getModel().id);
-		this.boundingBox.updatePosition(state.getPosition());
-		this.terrain = terrain;
+		this.world = world;
 	}
 
-	public void update() {
-		state.update(terrain::getTerrainHeightByWorldPos);
+	public void update(GameContext ctx) {
+		state.update(ctx);
 
 		source.setPosition(getPosition());
 		source.setVelocity(getVelocity());
+		if (particleSystem != null)
+			particleSystem.setPosition(getPosition());
 		this.boundingBox.updatePosition(getPosition());
-		if (model instanceof AnimatedModel)
+		if (model instanceof AnimatedModel) {
+			float speed = state.getVelocity().length();
+			if (speed == 0) {
+				((AnimatedModel) getModel()).doAnimation("idle");
+			} else {
+				((AnimatedModel) getModel()).doAnimation("run");
+			}
 			((AnimatedModel) model).update();
+		}
 	}
 
 	public boolean isInAir() {
-		return getPosition().y > terrain.getTerrainHeightByWorldPos(getPosition().x, getPosition().z);
+		return getPosition().y > getTerrainHeight();
 	}
 
 	public TexturedModel getModel() {
@@ -90,8 +88,14 @@ public class Entity3D implements IPositionable {
 		return boundingBox;
 	}
 
+	protected void kill() {
+		if (particleSystem != null)
+			particleSystem.remove();
+		this.getState().setDead(true);
+	}
+
 	public boolean isDead() {
-		return dead;
+		return state.isDead();
 	}
 
 	@Override
@@ -118,7 +122,18 @@ public class Entity3D implements IPositionable {
 	}
 
 	public void setState(EntityState state) {
-		this.state = state;
+		//this.state = state;
+		this.getPosition().set(state.getPosition());
+		this.getState().setModelId(state.getModelId());
+		this.getState().setDead(state.isDead());
+	}
+
+	public float getHeight() {
+		return getBoundingBox().getSize().y;
+	}
+
+	public float getTerrainHeight() {
+		return world.getTerrainHeigh(getPosition().x, getPosition().z);
 	}
 
 }

+ 30 - 0
src/main/java/eu/tankernn/gameEngine/entities/EntityFactory.java

@@ -0,0 +1,30 @@
+package eu.tankernn.gameEngine.entities;
+
+import eu.tankernn.gameEngine.World;
+import eu.tankernn.gameEngine.loader.Loader;
+import eu.tankernn.gameEngine.loader.models.AABB;
+import eu.tankernn.gameEngine.loader.models.TexturedModel;
+import eu.tankernn.gameEngine.particles.ParticleMaster;
+import eu.tankernn.gameEngine.particles.ParticleSystem;
+
+public class EntityFactory {
+	private final Loader loader;
+	private final ParticleMaster particleMaster;
+	private final World world;
+
+	public EntityFactory(Loader loader, ParticleMaster particleMaster, World world) {
+		super();
+		this.loader = loader;
+		this.particleMaster = particleMaster;
+		this.world = world;
+	}
+
+	public Entity3D getEntity(EntityState state) {
+		TexturedModel model = loader.getModel(state.getModelId());
+		ParticleSystem particleSystem = loader.getParticleSystem(state.getParticleSystemId());
+		if (particleSystem != null)
+			particleMaster.addSystem(particleSystem);
+		AABB boundingBox = loader.getBoundingBox(model.getModel().id);
+		return new Entity3D(state, model, particleSystem, boundingBox, world);
+	}
+}

+ 55 - 15
src/main/java/eu/tankernn/gameEngine/entities/EntityState.java

@@ -1,11 +1,14 @@
 package eu.tankernn.gameEngine.entities;
 
 import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.BiFunction;
 
 import org.lwjgl.util.vector.Vector3f;
 
+import eu.tankernn.gameEngine.entities.ai.Behavior;
 import eu.tankernn.gameEngine.renderEngine.DisplayManager;
 import eu.tankernn.gameEngine.settings.Physics;
 
@@ -15,30 +18,39 @@ public class EntityState implements Serializable {
 	 */
 	private static final long serialVersionUID = -3294516796577777079L;
 
-	private static final AtomicInteger ID_GEN = new AtomicInteger();
+	protected static final AtomicInteger ID_GEN = new AtomicInteger();
 
 	private final int id;
 	private int modelId;
+	private int systemId;
 	private final Vector3f position;
 	private final Vector3f rotation;
 	private final Vector3f velocity;
 	private final Vector3f scale;
+	protected boolean dead = false;
 
-	public EntityState(int id, int modelId, Vector3f position, Vector3f rotation, Vector3f velocity, Vector3f scale) {
+	private List<Behavior> behaviors;
+
+	public EntityState(int id, int modelId, int systemId, Vector3f position, Vector3f rotation, Vector3f velocity,
+			Vector3f scale, Behavior... behaviors) {
 		this.id = id;
 		this.modelId = modelId;
-		this.position = position;
+		this.position = new Vector3f(position);
 		this.rotation = rotation;
 		this.velocity = velocity;
 		this.scale = scale;
+
+		this.behaviors = new ArrayList<Behavior>(Arrays.asList(behaviors));
+		this.behaviors.forEach(b -> b.setEntity(this));
 	}
 
-	public EntityState(int modelId, Vector3f position, Vector3f rotation, Vector3f velocity, Vector3f scale) {
-		this(ID_GEN.getAndIncrement(), modelId, position, rotation, velocity, scale);
+	public EntityState(int modelId, int systemId, Vector3f position, Vector3f rotation, Vector3f velocity,
+			Vector3f scale, Behavior... behaviors) {
+		this(ID_GEN.getAndIncrement(), modelId, systemId, position, rotation, velocity, scale, behaviors);
 	}
 
 	public EntityState() {
-		this(-1, new Vector3f(0, 0, 0), new Vector3f(0, 0, 0), new Vector3f(0, 0, 0), new Vector3f(0, 0, 0));
+		this(-1, -1, new Vector3f(0, 0, 0), new Vector3f(0, 0, 0), new Vector3f(0, 0, 0), new Vector3f(1, 1, 1));
 	}
 
 	/**
@@ -47,7 +59,7 @@ public class EntityState implements Serializable {
 	 * @param speed
 	 *            The speed with which to move the entity
 	 */
-	public void generateVelocity(float speed) {
+	public void generateVelocity(float speed, float tickTimeSeconds) {
 		getVelocity().x = (float) (speed * Math.sin(Math.toRadians(getRotation().y)));
 		getVelocity().z = (float) (speed * Math.cos(Math.toRadians(getRotation().y)));
 		getVelocity().y += Physics.GRAVITY * DisplayManager.getFrameTimeSeconds();
@@ -57,19 +69,24 @@ public class EntityState implements Serializable {
 		Vector3f.add(getRotation(), deltaRotation, getRotation());
 	}
 
-	public void update() {
-		Vector3f.add(position, (Vector3f) new Vector3f(velocity).scale(DisplayManager.getFrameTimeSeconds()), position);
-	}
+	public void update(GameContext ctx) {
+		behaviors.forEach(b -> b.update(ctx));
 
-	public void update(BiFunction<Float, Float, Float> terrainHeightByPos) {
-		this.update();
-
-		float terrainHeight = terrainHeightByPos.apply(getPosition().x, getPosition().z);
+		float terrainHeight = ctx.getTerrainHeight(position.x, position.z);
 
 		if (position.y < terrainHeight) {
 			velocity.y = 0;
 			position.y = terrainHeight;
 		}
+
+		System.out.println(this);
+
+		Vector3f.add(position, (Vector3f) new Vector3f(velocity).scale(ctx.getTickLengthSeconds()), position);
+	}
+
+	public void addBehavior(Behavior behavior) {
+		behaviors.add(behavior);
+		behavior.setEntity(this);
 	}
 
 	public int getId() {
@@ -100,4 +117,27 @@ public class EntityState implements Serializable {
 		return scale;
 	}
 
+	public boolean isDead() {
+		return dead;
+	}
+
+	public void setDead(boolean dead) {
+		this.dead = dead;
+	}
+
+	public int getParticleSystemId() {
+		return systemId;
+	}
+
+	public List<Behavior> getBehaviors() {
+		return behaviors;
+	}
+
+	public void setBehaviors(List<Behavior> behaviors) {
+		this.behaviors = behaviors;
+	}
+
+	public String toString() {
+		return String.format("Entity with id %d, modelId %d and position %s. List of behaviors: %s", id, modelId, position.toString(), behaviors.toString());
+	}
 }

+ 36 - 0
src/main/java/eu/tankernn/gameEngine/entities/GameContext.java

@@ -0,0 +1,36 @@
+package eu.tankernn.gameEngine.entities;
+
+import java.util.Collection;
+
+import eu.tankernn.gameEngine.loader.models.AABB;
+
+public abstract class GameContext {
+	private final boolean client;
+	private final float tickLengthSeconds;
+	private final Collection<EntityState> entities;
+
+	public GameContext(boolean client, float tickLengthSeconds, Collection<EntityState> entities) {
+		super();
+		this.client = client;
+		this.tickLengthSeconds = tickLengthSeconds;
+		this.entities = entities;
+	}
+
+	public boolean isClient() {
+		return client;
+	}
+
+	public float getTickLengthSeconds() {
+		return tickLengthSeconds;
+	}
+
+	public Collection<EntityState> getEntities() {
+		return entities;
+	}
+
+	public abstract float getHeight(int entityId);
+
+	public abstract float getTerrainHeight(float x, float z);
+
+	public abstract AABB getBoundingBox(int entityId);
+}

+ 0 - 89
src/main/java/eu/tankernn/gameEngine/entities/Player.java

@@ -1,89 +0,0 @@
-package eu.tankernn.gameEngine.entities;
-
-import org.lwjgl.input.Keyboard;
-import org.lwjgl.input.Mouse;
-import org.lwjgl.util.vector.Vector3f;
-
-import eu.tankernn.gameEngine.animation.model.AnimatedModel;
-import eu.tankernn.gameEngine.loader.models.AABB;
-import eu.tankernn.gameEngine.loader.models.TexturedModel;
-import eu.tankernn.gameEngine.renderEngine.DisplayManager;
-import eu.tankernn.gameEngine.terrains.Terrain;
-import eu.tankernn.gameEngine.terrains.TerrainPack;
-
-public class Player extends Entity3D {
-	
-	private static final float RUN_SPEED = 20;
-	protected static final float TURN_MAX = 160;
-	private static final float JUMP_POWER = 30;
-	
-	protected float currentSpeed = 0;
-	protected float currentTurnSpeed = 0;
-	
-	public Player(TexturedModel model, Vector3f position, Vector3f rotation, Vector3f scale, AABB boundingBox, TerrainPack terrainPack) {
-		super(model, position, rotation, scale, boundingBox, terrainPack);
-	}
-	
-	public Player(TexturedModel model, Vector3f position, AABB boundingBox, TerrainPack terrainPack) {
-		this(model, position, new Vector3f(0, 0, 0), new Vector3f(1, 1, 1), boundingBox, terrainPack);
-	}
-
-	public void move() {
-		checkInputs();
-		super.getState().increaseRotation(new Vector3f(0, currentTurnSpeed * DisplayManager.getFrameTimeSeconds(), 0));
-		super.getState().generateVelocity(currentSpeed);
-	}
-	
-	private void jump() {
-		if (!this.isInAir()) {
-			getVelocity().y = JUMP_POWER;
-			
-			// source.play(jumpSoundBuffer);
-		}
-	}
-	
-	protected void checkInputs() {
-		if (Keyboard.isKeyDown(Keyboard.KEY_W) || (Mouse.isButtonDown(0) && Mouse.isButtonDown(1))) {
-			if (this.getModel() instanceof AnimatedModel)
-				((AnimatedModel) getModel()).doAnimation("run");
-			this.currentSpeed = RUN_SPEED;
-		} else if (Keyboard.isKeyDown(Keyboard.KEY_S)) {
-			this.currentSpeed = -RUN_SPEED;
-		} else {
-			this.currentSpeed = 0;
-			if (this.getModel() instanceof AnimatedModel)
-				((AnimatedModel) getModel()).doAnimation("idle");
-		}
-		
-		if (Keyboard.isKeyDown(Keyboard.KEY_A)) {
-			this.currentTurnSpeed = TURN_MAX;
-		} else if (Keyboard.isKeyDown(Keyboard.KEY_D)) {
-			this.currentTurnSpeed = -TURN_MAX;
-		} else {
-			this.currentTurnSpeed = 0;
-		}
-		
-		if (Keyboard.isKeyDown(Keyboard.KEY_SPACE)) {
-			jump();
-		}
-		
-		if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) && Keyboard.isKeyDown(Keyboard.KEY_W)) {
-			this.currentSpeed = 5 * RUN_SPEED;
-		}
-	}
-	
-	public float getHeight() {
-		return getBoundingBox().getSize().y;
-	}
-	
-	public Terrain getCurrentTerrain() {
-		return terrain.getTerrainByWorldPos(this.getPosition().x, this.getPosition().z);
-	}
-	
-	public Vector3f get2dRotation() {
-		float dx = (float) (Math.sin(Math.toRadians(super.getRotation().y)));
-		float dz = (float) (Math.cos(Math.toRadians(super.getRotation().y)));
-		return new Vector3f(dx, 0, dz);
-	}
-	
-}

+ 65 - 0
src/main/java/eu/tankernn/gameEngine/entities/PlayerBehavior.java

@@ -0,0 +1,65 @@
+package eu.tankernn.gameEngine.entities;
+
+import org.lwjgl.input.Keyboard;
+import org.lwjgl.input.Mouse;
+import org.lwjgl.util.vector.Vector3f;
+
+import eu.tankernn.gameEngine.entities.ai.Behavior;
+import eu.tankernn.gameEngine.renderEngine.DisplayManager;
+
+public class PlayerBehavior extends Behavior {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -361242008858358717L;
+	
+	private static final float RUN_SPEED = 20;
+	protected static final float TURN_MAX = 160;
+	private static final float JUMP_POWER = 30;
+
+	protected float currentSpeed = 0;
+	protected float currentTurnSpeed = 0;
+
+	public void update(GameContext impl) {
+		if (!impl.isClient())
+			return;
+		checkInputs(impl);
+		entity.increaseRotation(new Vector3f(0, currentTurnSpeed * DisplayManager.getFrameTimeSeconds(), 0));
+		entity.generateVelocity(currentSpeed, impl.getTickLengthSeconds());
+	}
+
+	private void jump(float terrainHeight) {
+		if (entity.getPosition().y <= terrainHeight) {
+			entity.getVelocity().y = JUMP_POWER;
+
+			// source.play(jumpSoundBuffer);
+		}
+	}
+
+	protected void checkInputs(GameContext impl) {
+		if (Keyboard.isKeyDown(Keyboard.KEY_W) || (Mouse.isButtonDown(0) && Mouse.isButtonDown(1))) {
+			this.currentSpeed = RUN_SPEED;
+		} else if (Keyboard.isKeyDown(Keyboard.KEY_S)) {
+			this.currentSpeed = -RUN_SPEED;
+		} else {
+			this.currentSpeed = 0;
+		}
+
+		if (Keyboard.isKeyDown(Keyboard.KEY_A)) {
+			this.currentTurnSpeed = TURN_MAX;
+		} else if (Keyboard.isKeyDown(Keyboard.KEY_D)) {
+			this.currentTurnSpeed = -TURN_MAX;
+		} else {
+			this.currentTurnSpeed = 0;
+		}
+
+		if (Keyboard.isKeyDown(Keyboard.KEY_SPACE)) {
+			jump(impl.getTerrainHeight(entity.getPosition().x, entity.getPosition().z));
+		}
+
+		if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) && Keyboard.isKeyDown(Keyboard.KEY_W)) {
+			this.currentSpeed = 5 * RUN_SPEED;
+		}
+	}
+}

+ 2 - 2
src/main/java/eu/tankernn/gameEngine/entities/PlayerCamera.java

@@ -17,10 +17,10 @@ public class PlayerCamera extends Camera {
 	private float lockedPosition = 0;
 	private boolean isLocked = false;
 	
-	private Player player;
+	private Entity3D player;
 	private TerrainPack terrainPack;
 	
-	public PlayerCamera(Player player, TerrainPack terrainPack) {
+	public PlayerCamera(Entity3D player, TerrainPack terrainPack) {
 		super();
 		this.player = player;
 		this.terrainPack = terrainPack;

+ 21 - 0
src/main/java/eu/tankernn/gameEngine/entities/ai/Behavior.java

@@ -0,0 +1,21 @@
+package eu.tankernn.gameEngine.entities.ai;
+
+import java.io.Serializable;
+
+import eu.tankernn.gameEngine.entities.GameContext;
+import eu.tankernn.gameEngine.entities.EntityState;
+
+public abstract class Behavior implements Serializable {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -3510420608770918702L;
+	
+	protected EntityState entity;
+	
+	public abstract void update(GameContext impl);
+	
+	public void setEntity(EntityState npcState) {
+		this.entity = npcState;
+	}
+}

+ 23 - 0
src/main/java/eu/tankernn/gameEngine/entities/ai/CollisionBehavior.java

@@ -0,0 +1,23 @@
+package eu.tankernn.gameEngine.entities.ai;
+
+import eu.tankernn.gameEngine.entities.EntityState;
+import eu.tankernn.gameEngine.entities.GameContext;
+import eu.tankernn.gameEngine.loader.models.AABB;
+
+public abstract class CollisionBehavior extends Behavior {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -7412981353484426775L;
+
+	@Override
+	public void update(GameContext impl) {
+		impl.getEntities().stream().filter(e -> !e.equals(entity))
+				.filter((e) -> AABB.collides(impl.getBoundingBox(e.getId()), impl.getBoundingBox(entity.getId())))
+				.forEach(this::onCollision);
+	}
+
+	public abstract void onCollision(EntityState entity);
+
+}

+ 17 - 0
src/main/java/eu/tankernn/gameEngine/entities/ai/DieOnCollisionBehavior.java

@@ -0,0 +1,17 @@
+package eu.tankernn.gameEngine.entities.ai;
+
+import eu.tankernn.gameEngine.entities.EntityState;
+
+public class DieOnCollisionBehavior extends CollisionBehavior {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -8921319921640580619L;
+
+	@Override
+	public void onCollision(EntityState entity) {
+		entity.setDead(true);
+	}
+
+}

+ 30 - 0
src/main/java/eu/tankernn/gameEngine/entities/ai/FollowBehavior.java

@@ -0,0 +1,30 @@
+package eu.tankernn.gameEngine.entities.ai;
+
+import org.lwjgl.util.vector.Vector3f;
+
+import eu.tankernn.gameEngine.entities.GameContext;
+import eu.tankernn.gameEngine.util.IPositionable;
+
+public class FollowBehavior extends Behavior {
+	
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 1890567568320815924L;
+	
+	private IPositionable target;
+	private float speed;
+	
+	public FollowBehavior(IPositionable target, float speed) {
+		this.target = target;
+		this.speed = speed;
+	}
+	
+	@Override
+	public void update(GameContext impl) {
+		// Store direction in velocity vector
+		Vector3f.sub(target.getPosition(), entity.getPosition(), entity.getVelocity());
+		// Normalize and scale velocity vector
+		entity.getVelocity().normalise().scale(speed);
+	}
+}

+ 1 - 1
src/main/java/eu/tankernn/gameEngine/entities/npc/RoamingArea.java → src/main/java/eu/tankernn/gameEngine/entities/ai/RoamingArea.java

@@ -1,4 +1,4 @@
-package eu.tankernn.gameEngine.entities.npc;
+package eu.tankernn.gameEngine.entities.ai;
 
 import java.util.Random;
 

+ 10 - 4
src/main/java/eu/tankernn/gameEngine/entities/npc/RoamingBehavior.java → src/main/java/eu/tankernn/gameEngine/entities/ai/RoamingBehavior.java

@@ -1,11 +1,17 @@
-package eu.tankernn.gameEngine.entities.npc;
+package eu.tankernn.gameEngine.entities.ai;
 
 import org.lwjgl.util.vector.Vector2f;
 
+import eu.tankernn.gameEngine.entities.GameContext;
 import eu.tankernn.gameEngine.renderEngine.DisplayManager;
 
 public class RoamingBehavior extends Behavior {
 	
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -6198784236364774890L;
+	
 	private Vector2f targetPosition;
 	private RoamingArea area;
 	private float standbyTime, speed;
@@ -16,9 +22,9 @@ public class RoamingBehavior extends Behavior {
 	}
 	
 	@Override
-	public void update() {
+	public void update(GameContext impl) {
 		if (targetPosition == null) {
-			entity.getState().generateVelocity(0);
+			entity.generateVelocity(0, impl.getTickLengthSeconds());
 			if (standbyTime > 0f)
 				standbyTime -= DisplayManager.getFrameTimeSeconds();
 			else
@@ -30,7 +36,7 @@ public class RoamingBehavior extends Behavior {
 				standbyTime = (float) (Math.random() * 10);
 			}
 			entity.getRotation().y = (float) Math.toDegrees(Math.atan2(direction.x, direction.y));
-			entity.getState().generateVelocity(speed);
+			entity.generateVelocity(speed, impl.getTickLengthSeconds());
 		}
 	}
 	

+ 0 - 13
src/main/java/eu/tankernn/gameEngine/entities/npc/Behavior.java

@@ -1,13 +0,0 @@
-package eu.tankernn.gameEngine.entities.npc;
-
-import eu.tankernn.gameEngine.entities.Entity3D;
-
-public abstract class Behavior {
-	protected Entity3D entity;
-	
-	public abstract void update();
-	
-	public void setEntity(Entity3D entity) {
-		this.entity = entity;
-	}
-}

+ 0 - 41
src/main/java/eu/tankernn/gameEngine/entities/npc/NPC.java

@@ -1,41 +0,0 @@
-package eu.tankernn.gameEngine.entities.npc;
-
-import java.util.Arrays;
-import java.util.List;
-
-import org.lwjgl.util.vector.Vector3f;
-
-import eu.tankernn.gameEngine.entities.Entity3D;
-import eu.tankernn.gameEngine.loader.models.AABB;
-import eu.tankernn.gameEngine.loader.models.TexturedModel;
-import eu.tankernn.gameEngine.terrains.TerrainPack;
-
-public class NPC extends Entity3D {
-	
-	private int health;
-	private final int maxHealth;
-	
-	private List<Behavior> behaviors;
-	
-	public NPC(TexturedModel model, Vector3f position, int health, AABB boundingBox, TerrainPack terrainPack, Behavior... behaviors) {
-		super(model, position, boundingBox, terrainPack);
-		this.health = this.maxHealth = health;
-		this.behaviors = Arrays.asList(behaviors);
-		this.behaviors.forEach(b -> b.setEntity(this));
-	}
-	
-	@Override
-	public void update() {
-		if (this.health <= 0)
-			this.dead = true;
-		
-		behaviors.forEach(Behavior::update);
-		
-		super.update();
-	}
-	
-	public void fullHeal() {
-		this.health = this.maxHealth;
-	}
-	
-}

+ 32 - 0
src/main/java/eu/tankernn/gameEngine/entities/npc/NpcState.java

@@ -0,0 +1,32 @@
+package eu.tankernn.gameEngine.entities.npc;
+
+import org.lwjgl.util.vector.Vector3f;
+
+import eu.tankernn.gameEngine.entities.EntityState;
+import eu.tankernn.gameEngine.entities.ai.Behavior;
+
+public class NpcState extends EntityState {
+	
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 8182776911367238544L;
+	
+	private int health;
+	private final int maxHealth;
+	
+	public NpcState(int modelId, int systemId, Vector3f position, Vector3f rotation, Vector3f velocity, Vector3f scale, Behavior... behaviors) {
+		super(modelId, systemId, position, rotation, velocity, scale, behaviors);
+		this.health = this.maxHealth = health;
+	}
+	
+	@Override
+	public boolean isDead() {
+		return health <= 0;
+	}
+	
+	public void fullHeal() {
+		this.health = this.maxHealth;
+	}
+	
+}

+ 0 - 27
src/main/java/eu/tankernn/gameEngine/entities/projectiles/BasicProjectile.java

@@ -1,27 +0,0 @@
-package eu.tankernn.gameEngine.entities.projectiles;
-
-import org.lwjgl.util.vector.Vector3f;
-
-import eu.tankernn.gameEngine.entities.Entity3D;
-import eu.tankernn.gameEngine.loader.models.AABB;
-import eu.tankernn.gameEngine.particles.ParticleSystem;
-import eu.tankernn.gameEngine.terrains.TerrainPack;
-
-public class BasicProjectile extends Projectile {
-	
-	private Entity3D creator;
-	
-	public BasicProjectile(TerrainPack terrain, Entity3D creator, Vector3f position, Vector3f velocity, float range, ParticleSystem particleSystem) {
-		super(terrain, null, position, velocity, range, new AABB(new Vector3f(0, 0, 0), new Vector3f(1, 1, 1)), particleSystem);
-		this.creator = creator;
-	}
-	
-	@Override
-	public void onCollision(Entity3D entity) {
-		if (entity.equals(creator))
-			return;
-		System.out.println("Collision!");
-		this.kill();
-	}
-	
-}

+ 0 - 51
src/main/java/eu/tankernn/gameEngine/entities/projectiles/Projectile.java

@@ -1,51 +0,0 @@
-package eu.tankernn.gameEngine.entities.projectiles;
-
-import java.util.Collection;
-
-import org.lwjgl.util.vector.Vector3f;
-
-import eu.tankernn.gameEngine.entities.Entity3D;
-import eu.tankernn.gameEngine.loader.models.AABB;
-import eu.tankernn.gameEngine.loader.models.TexturedModel;
-import eu.tankernn.gameEngine.particles.ParticleSystem;
-import eu.tankernn.gameEngine.terrains.TerrainPack;
-
-public abstract class Projectile extends Entity3D {
-	
-	private ParticleSystem particleSystem;
-	private final float range;
-	private final Vector3f startPosition;
-	
-	public Projectile(TerrainPack terrain, TexturedModel model, Vector3f position, Vector3f velocity, float range, AABB boundingBox, ParticleSystem particleSystem) {
-		super(model, position, boundingBox, terrain);
-		this.particleSystem = particleSystem;
-		this.getVelocity().set(velocity);
-		this.range = range;
-		this.startPosition = new Vector3f(position);
-	}
-	
-	public void update() {
-		super.update();
-		particleSystem.setPosition(this.getPosition());
-		
-		if (this.terrain != null) {
-			getPosition().y = Math.max(getPosition().y, 5 + terrain.getTerrainHeightByWorldPos(getPosition().x, getPosition().z));
-		}
-		
-		Vector3f distance = Vector3f.sub(getPosition(), startPosition, null);
-		if (distance.length() > range) {
-			kill();
-		}
-	}
-	
-	public void checkCollision(Collection<Entity3D> collection) {
-		collection.stream().filter((e) -> AABB.collides(e.getBoundingBox(), Projectile.this.getBoundingBox())).forEach(this::onCollision);
-	}
-
-	protected void kill() {
-		particleSystem.remove();
-		this.dead = true;
-	}
-	
-	public abstract void onCollision(Entity3D entity);
-}

+ 48 - 0
src/main/java/eu/tankernn/gameEngine/entities/projectiles/ProjectileState.java

@@ -0,0 +1,48 @@
+package eu.tankernn.gameEngine.entities.projectiles;
+
+import java.util.function.BiFunction;
+
+import org.lwjgl.util.vector.Vector3f;
+
+import eu.tankernn.gameEngine.entities.EntityState;
+
+public class ProjectileState extends EntityState {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -2926349294733353572L;
+	
+	private final float range;
+	private final Vector3f startPosition;
+
+	public ProjectileState(int id, int modelId, int systemId, Vector3f position, Vector3f rotation, Vector3f velocity,
+			Vector3f scale, float range) {
+		super(id, modelId, systemId, position, rotation, velocity, scale);
+		this.range = range;
+		this.startPosition = new Vector3f(position);
+	}
+	
+	public ProjectileState(int modelId, int systemId, Vector3f position, Vector3f rotation, Vector3f velocity,
+			Vector3f scale, float range) {
+		this(ID_GEN.getAndIncrement(), modelId, systemId, position, rotation, velocity, scale, range);
+	}
+
+	public void update(BiFunction<Float, Float, Float> terrain) {
+		getPosition().y = Math.max(getPosition().y, 5 + terrain.apply(getPosition().x, getPosition().z));
+
+		Vector3f distance = Vector3f.sub(getPosition(), startPosition, null);
+		if (distance.length() > range) {
+			setDead(true);
+		}
+	}
+
+	public float getRange() {
+		return range;
+	}
+
+	public Vector3f getStartPosition() {
+		return startPosition;
+	}
+
+}

+ 0 - 38
src/main/java/eu/tankernn/gameEngine/entities/projectiles/TargetedProjectile.java

@@ -1,38 +0,0 @@
-package eu.tankernn.gameEngine.entities.projectiles;
-
-import org.lwjgl.util.vector.Vector3f;
-
-import eu.tankernn.gameEngine.entities.Entity3D;
-import eu.tankernn.gameEngine.loader.models.AABB;
-import eu.tankernn.gameEngine.loader.models.TexturedModel;
-import eu.tankernn.gameEngine.particles.ParticleSystem;
-import eu.tankernn.gameEngine.terrains.TerrainPack;
-import eu.tankernn.gameEngine.util.IPositionable;
-
-public class TargetedProjectile extends Projectile {
-	
-	private IPositionable target;
-	private float speed;
-	
-	public TargetedProjectile(TerrainPack terrain, TexturedModel model, Vector3f position, IPositionable target, float speed, AABB boundingBox, ParticleSystem particleSystem) {
-		super(terrain, model, position, new Vector3f(0, 0, 0), Float.MAX_VALUE, boundingBox, particleSystem);
-		this.target = target;
-		this.speed = speed;
-	}
-	
-	@Override
-	public void update() {
-		// Store direction in velocity vector
-		Vector3f.sub(target.getPosition(), this.getPosition(), this.getVelocity());
-		// Normalize and scale velocity vector
-		getVelocity().normalise().scale(speed);
-		super.update();
-	}
-	
-	@Override
-	public void onCollision(Entity3D entity) {
-		if (entity.equals(target))
-			this.kill();
-	}
-	
-}

+ 13 - 0
src/main/java/eu/tankernn/gameEngine/loader/Loader.java

@@ -11,6 +11,7 @@ import java.util.Map;
 
 import org.json.JSONArray;
 import org.json.JSONObject;
+import org.lwjgl.util.vector.Vector3f;
 
 import eu.tankernn.gameEngine.animation.model.AnimatedModel;
 import eu.tankernn.gameEngine.animation.model.Joint;
@@ -26,6 +27,7 @@ import eu.tankernn.gameEngine.loader.models.obj.ObjLoader;
 import eu.tankernn.gameEngine.loader.textures.ModelTexture;
 import eu.tankernn.gameEngine.loader.textures.Texture;
 import eu.tankernn.gameEngine.loader.textures.TextureAtlas;
+import eu.tankernn.gameEngine.particles.ParticleSystem;
 import eu.tankernn.gameEngine.renderEngine.Vao;
 import eu.tankernn.gameEngine.util.InternalFile;
 
@@ -41,6 +43,7 @@ public class Loader {
 	private List<TextureAtlas> textures = new ArrayList<>();
 	private Map<Integer, TexturedModel> models = new HashMap<>();
 	private Map<Integer, AABB> boundingBoxes = new HashMap<>();
+	private Map<Integer, ParticleSystem> particleSystems = new HashMap<>();
 	
 	public Vao loadToVAO(float[] vertices, float[] textureCoords, float[] normals, int[] indices) {
 		return loadToVAO(vertices, textureCoords, normals, null, null, null, indices);
@@ -265,8 +268,18 @@ public class Loader {
 	}
 	
 	public AABB getBoundingBox(int vaoId) {
+		if (vaoId == -1)
+			return new AABB(new Vector3f(0, 0, 0), new Vector3f(0.1f, 0.1f, 0.1f));
 		if (!boundingBoxes.containsKey(vaoId))
 			throw new NullPointerException("Unable to find bounding box for vaoId " + vaoId);
 		return boundingBoxes.get(vaoId);
 	}
+	
+	public void registerParticleSystem(int id, ParticleSystem system) {
+		particleSystems.put(id, system);
+	}
+	
+	public ParticleSystem getParticleSystem(int particleSystemId) {
+		return particleSystems.get(particleSystemId);
+	}
 }

+ 6 - 2
src/main/java/eu/tankernn/gameEngine/particles/ParticleSystem.java

@@ -8,7 +8,7 @@ import org.lwjgl.util.vector.Vector3f;
 import eu.tankernn.gameEngine.renderEngine.DisplayManager;
 
 public class ParticleSystem {
-	private float pps, speed, gravityComplient, lifeLength;
+	private final float pps, speed, gravityComplient, lifeLength;
 	private Vector3f position;
 	
 	private ParticleTexture texture;
@@ -22,8 +22,12 @@ public class ParticleSystem {
 		this.lifeLength = lifeLength;
 	}
 	
+	public ParticleSystem(ParticleSystem system) {
+		this(system.texture, system.pps, system.speed, system.gravityComplient, system.lifeLength);
+	}
+	
 	public void setPosition(Vector3f systemCenter) {
-		this.position = systemCenter;
+		this.position = new Vector3f(systemCenter);
 	}
 	
 	public List<Particle> generateParticles() {