package org.jrenner.fps.entity; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.TimeUtils; import org.jrenner.fps.Main; import org.jrenner.fps.Physics; import org.jrenner.fps.Player; import org.jrenner.fps.Shadow; import org.jrenner.fps.effects.BulletHit; import org.jrenner.fps.move.GroundMovement; public class DynamicEntity extends Entity { public static Array<DynamicEntity> list; public Shadow shadow; public static DynamicEntity createEntityNoId(EntityGraphicsType graphicsType) { if (!Main.isClient()) { // server does not care about graphics graphicsType = EntityGraphicsType.None; } DynamicEntity dynEnt = new DynamicEntity(graphicsType); dynEnt.id = -1; return dynEnt; } public static DynamicEntity createEntity(int id, boolean isPlayer, EntityGraphicsType graphicsType) { DynamicEntity dynEnt = createEntityNoId(graphicsType); assignEntityID(dynEnt, id); if (Main.isClient() && isPlayer) { dynEnt.setPlayer(new Player(dynEnt)); } return dynEnt; } public DynamicEntity(EntityGraphicsType graphicsType) { super(graphicsType); DynamicEntity.list.add(this); // TODO move dimensions to constructor parameters dimen.set(1f, 2f, 1f); tmp.set(dimen).scl(0.5f); float radius = 0.45f * (float) Math.sqrt((dimen.x * dimen.x) + (dimen.z * dimen.z)); body = Physics.inst.createCapsuleObject(radius, dimen.y * 0.35f); bodyOffset.set(0f, 0.05f, 0f); Physics.applyDynamicEntityCollisionFlags(body); body.userData = this; movement = new GroundMovement(id); movement.setAffectedByGravity(true); movement.setAccelRate(0.03f); movement.setMaxHorizontalSpeed(0.12f); movement.setMaxVerticalSpeed(0.6f); movement.setBraking(0.9f); Physics.inst.addDynamicEntityToWorld(body); float shadowRadius = (float) Math.sqrt(dimen.x * dimen.x + dimen.z * dimen.z); if (Main.isClient()) { shadow = new Shadow(shadowRadius); } } private long lastSetDestTime; private int nextDestInterval = 10000; @Override void update(float timeStep) { boolean testSimpleAI = true; super.update(timeStep); if (testSimpleAI && this.player == null) { if (Main.isClient() && Main.inst.client.player != null) { // look at the player entity (billboard behaviour, always face the camera) lookAt(Main.inst.client.player.entity.getPosition()); } long now = TimeUtils.millis(); movement.cancelDestinationAtThreshold(3f); if ((now - lastSetDestTime) >= nextDestInterval) { float x = MathUtils.random(0f, 100f); float y = 0f; float z = MathUtils.random(0f, 100f); setDestination(tmp.set(x, y, z)); lastSetDestTime = now; nextDestInterval = MathUtils.random(5000, 15000); } } if (shooting) { if (tickCountdown <= 0) { shoot(); tickCountdown = ticksPerShot; } } if (shadow != null) { shadow.update(getPosition()); } tickCountdown--; } public Vector3 getForwardFacing(Vector3 storage) { storage.set(Vector3.Z); relativize(storage); return storage; } private static long minJumpInterval = 250; private long lastJumpTime = 0; public void jump() { if (onGround) { long now = System.currentTimeMillis(); if ((now - lastJumpTime) < minJumpInterval) { return; } lastJumpTime = now; float jumpStrength = 0.35f; adjustVelocity(tmp.set(0f, jumpStrength, 0f)); } } private int ticksPerShot = 6; private int tickCountdown; private boolean shooting; private float aimError = 0.025f; public void startShoot() { shooting = true; } public void stopShoot() { shooting = false; } public void shoot() { // self pos = ray start tmp.set(getPosition()); // store forward vector in tmp2 and keep for processing hit result // also add aim error float xErr = MathUtils.random(-aimError, aimError); float yErr = MathUtils.random(-aimError, aimError); tmp2.set(Vector3.Z).add(xErr, yErr, 0f); relativize(tmp2); float rayLen = 1000f; // get forward facing and apply aim error tmp3.set(tmp2).scl(rayLen).add(tmp); Physics.inst.castRay(tmp, tmp3); Physics.RaycastReport ray = Physics.inst.raycastReport; if (ray.hit) { // position + (forward vector * hitDistance) = hit location tmp2.scl(ray.hitDistance).add(tmp); new BulletHit(tmp2); Main.inst.server.queueBulletHit(tmp2); //Log.debug(Tools.fmt(tmp, "hit")); } else { //Log.debug("no hit"); } } @Override protected void removeFromGame() { super.removeFromGame(); if (shadow != null) { shadow.removeFromGame(); } } public boolean isPlayer() { return player != null; } }