package net.kennux.cubicworld.entity;
import net.kennux.cubicworld.CubicWorld;
import net.kennux.cubicworld.CubicWorldConfiguration;
import net.kennux.cubicworld.serialization.BitReader;
import net.kennux.cubicworld.serialization.BitWriter;
import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g3d.ModelBatch;
import com.badlogic.gdx.graphics.g3d.decals.DecalBatch;
import com.badlogic.gdx.math.Vector3;
/**
* <pre>
* Abstract entity class.
* EVERY entity has:
*
* - id
* - name
* - position
* - rotation
* - master (EntityManager)
*
* This class implements basic entity syncing.
* It synchronizes entity position and rotation every frame.
*
* This class does not implement physics, if you need a character entity with
* physics and so on, you may better use ACharacterEntity or AMobEntity / AModelMobEntity (+ model rendering).
* It is an extended version of the AEntity class.
* </pre>
*
* @author KennuX
*
*/
public abstract class AEntity
{
/**
* The last time recievedUpdate() was called.
*/
private long lastTimeUpdated = 0;
/**
* The entity position. This position gets used for rendering and collision
* checks.
*/
protected Vector3 position = new Vector3();
/**
* The entity euler angles. This angles get used for rendering and collision
* checks.
*/
protected Vector3 eulerAngles = new Vector3();
/**
* The target position used for rendering interpolation.
*/
protected Vector3 targetPosition = new Vector3();
/**
* The target euler angles used for rendering interpolation.
*/
protected Vector3 targetEulerAngles = new Vector3();
/**
* The entity id
*/
protected int entityId;
/**
* The entity manager which manages this entity.
*/
protected EntityManager master;
/**
* <pre>
* The entity name.
* On normal mob entities it is the mob type's name.
* Players or bigger mobs like bosses should contain their name in here.
*
* You can use this for rendering gui or a nameplate over the entities head.
* </pre>
*/
protected String entityName = "";
/**
* <pre>
* Reads serialized data obtained from a server packet.
* This implementation will just read position and eulerAngles as vector3
* (in this order) from the stream.
* Override this method if you want to extend the synchronization.
* </pre>
*
* @param reader
*/
public void deserialize(BitReader reader)
{
this.setPosition(reader.readVector3());
this.setEulerAngles(reader.readVector3());
}
/**
* Deserializes this entity's initial state.
*
* @see AEntity#deserialize(BitReader)
* @param reader
*/
public void deserializeInitial(BitReader reader)
{
this.deserialize(reader);
}
/**
* <pre>
* This function calls the CubicWorldServer.destroyEntity() function on the
* server.
*
* On client it will only remove the entity from the entity manger.
* Normally you do not execute this on the client, it could cause some very
* strange things!
* </pre>
*
*/
public void die()
{
// If server, send out destroy packets
if (this.master.isServer())
CubicWorld.getServer().destroyEntity(this);
// If master initialized remove entity
if (this.master != null)
this.master.remove(this.getEntityId());
}
// Getters and setters
public int getEntityId()
{
return this.entityId;
}
public String getEntityName()
{
return this.entityName;
}
public Vector3 getEulerAngles()
{
return eulerAngles;
}
public long getLastUpdateTime()
{
return this.lastTimeUpdated;
}
public Vector3 getPosition()
{
return position;
}
/**
* Returns the size in bytes a state sync needs.
* Standard: 24 (pos + euler).
*/
public int getStateSyncSize()
{
return 24;
}
/**
* Initialize all resources of your entity in here.
* This function is called right after setMaster() got called.
*/
public abstract void init();
/**
* <pre>
* Performs a position interpolation. If you call setPosition() the target
* position will get overwritten.
*
* If direct is set the position will instantly be overwritten. Otherwise a
* lerp() is performed.
* </pre>
*
*/
public void interpolatePosition(boolean direct)
{
if (direct)
{
this.position = this.targetPosition;
this.eulerAngles = this.targetEulerAngles;
}
else
{
this.position.lerp(this.targetPosition, 0.8f);
this.eulerAngles.lerp(this.targetEulerAngles, 0.8f);
}
}
/**
* Returns true if the distance from this entity to the given player is less
* than CubicWorldConfiguration.entityCullDistance.
*
* @param player
* @return
*/
public boolean isInEntityViewDistance(AEntity entity)
{
return new Vector3(entity.getPosition()).sub(this.getPosition()).len() < CubicWorldConfiguration.entityCullDistance;
}
/**
* Returns true if the distance from this entity to the given player is less
* than CubicWorldConfiguration.entityCullDistance.
*
* @param position
* @return
*/
public boolean isInEntityViewDistance(Vector3 position)
{
return new Vector3(position).sub(this.getPosition()).len() < CubicWorldConfiguration.entityCullDistance;
}
/**
* Call this after your entity recieved a position update from the server.
* It will set the current time to the last update time.
*/
public void recievedUpdate()
{
this.lastTimeUpdated = System.currentTimeMillis();
}
/**
* Render this entity in this function. It will only get called on the
* client.
*/
public abstract void render(Camera camera, ModelBatch modelBatch, DecalBatch decalBatch, SpriteBatch spriteBatch, BitmapFont bitmapFont);
/**
* <pre>
* Serializes the entity.
* This implementation will just write position and eulerAngles as vector3
* (in this order) to the stream.
* Override this method if you want to extend the synchronization.
* </pre>
*
* @param writer
*/
public void serialize(BitWriter writer)
{
writer.writeVector3(this.position);
writer.writeVector3(this.eulerAngles);
}
/**
* Serializes this entity's initial state.
*
* @see AEntity#serialize(BitWriter)
* @param writer
*/
public void serializeInitial(BitWriter writer)
{
this.serialize(writer);
}
public void setEntityId(int id)
{
this.entityId = id;
}
public void setEntityName(String name)
{
this.entityName = name;
}
public void setEulerAngles(Vector3 euler)
{
this.targetEulerAngles = new Vector3(euler);
}
public void setMaster(EntityManager entityManager)
{
this.master = entityManager;
}
public void setPosition(Vector3 position)
{
this.targetPosition = new Vector3(position);
}
/**
* <pre>
* Simulate your entity behaviour in this function.
* Remember to keep all the logic on the server.
* Use this.master.isServer() or this.master.isClient() to check where you
* currently are.
* </pre>
*
*/
public abstract void update();
}