You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
314 lines
5.8 KiB
314 lines
5.8 KiB
package mightypork.rogue.world.entity;
|
|
|
|
|
|
import java.io.IOException;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
import mightypork.rogue.world.Coord;
|
|
import mightypork.rogue.world.EntityPos;
|
|
import mightypork.rogue.world.PathStep;
|
|
import mightypork.rogue.world.World;
|
|
import mightypork.rogue.world.entity.models.EntityModel;
|
|
import mightypork.rogue.world.entity.models.EntityMoveListener;
|
|
import mightypork.rogue.world.level.Level;
|
|
import mightypork.rogue.world.level.render.MapRenderContext;
|
|
import mightypork.rogue.world.pathfinding.Heuristic;
|
|
import mightypork.rogue.world.pathfinding.PathFinder;
|
|
import mightypork.rogue.world.pathfinding.PathFindingContext;
|
|
import mightypork.util.files.ion.IonBinary;
|
|
import mightypork.util.files.ion.IonBundle;
|
|
import mightypork.util.files.ion.IonInput;
|
|
import mightypork.util.files.ion.IonOutput;
|
|
|
|
|
|
/**
|
|
* World entity (mob or player)
|
|
*
|
|
* @author MightyPork
|
|
*/
|
|
public final class Entity implements IonBinary, EntityMoveListener {
|
|
|
|
public static final int ION_MARK = 52;
|
|
|
|
/** Last pos, will be freed upon finishing move */
|
|
private final EntityPos lastPosition = new EntityPos();
|
|
private boolean walking = false;
|
|
|
|
private Level level;
|
|
private EntityModel model;
|
|
|
|
/** Entity ID */
|
|
private int entityId = -1;
|
|
|
|
/** Model ID */
|
|
private int modelId = -1;
|
|
|
|
/** Entity data holder */
|
|
private final EntityData data = new EntityData();
|
|
|
|
/** Temporary renderer's data */
|
|
public final EntityRenderData renderData = new EntityRenderData();
|
|
|
|
private final List<EntityMoveListener> moveListeners = new ArrayList<>();
|
|
|
|
private final PathFindingContext pfc = new PathFindingContext() {
|
|
|
|
@Override
|
|
public boolean isAccessible(Coord pos)
|
|
{
|
|
return model.canWalkInto(Entity.this, pos);
|
|
}
|
|
|
|
|
|
@Override
|
|
public int getMinCost()
|
|
{
|
|
return model.getPathMinCost();
|
|
}
|
|
|
|
|
|
@Override
|
|
public Heuristic getHeuristic()
|
|
{
|
|
return model.getPathHeuristic();
|
|
}
|
|
|
|
|
|
@Override
|
|
public int getCost(Coord from, Coord to)
|
|
{
|
|
return model.getPathCost(Entity.this, from, to);
|
|
}
|
|
};
|
|
|
|
|
|
public Entity(int eid, EntityModel entityModel)
|
|
{
|
|
this.entityId = eid;
|
|
setModel(entityModel);
|
|
|
|
model.initMetadata(data);
|
|
}
|
|
|
|
|
|
public Entity()
|
|
{
|
|
// for ion
|
|
}
|
|
|
|
|
|
private void setModel(EntityModel entityModel)
|
|
{
|
|
// replace listener
|
|
if (model != null) moveListeners.remove(model);
|
|
moveListeners.add(entityModel);
|
|
|
|
this.modelId = entityModel.id;
|
|
this.model = entityModel;
|
|
}
|
|
|
|
|
|
public Level getLevel()
|
|
{
|
|
if (level == null) throw new IllegalStateException("Entity has no level assigned.");
|
|
return level;
|
|
}
|
|
|
|
|
|
public World getWorld()
|
|
{
|
|
return getLevel().getWorld();
|
|
}
|
|
|
|
|
|
@Override
|
|
public short getIonMark()
|
|
{
|
|
return ION_MARK;
|
|
}
|
|
|
|
|
|
@Override
|
|
public void save(IonOutput out) throws IOException
|
|
{
|
|
final IonBundle bundle = new IonBundle();
|
|
bundle.put("model", modelId);
|
|
bundle.put("id", entityId);
|
|
bundle.putBundled("data", data);
|
|
|
|
out.writeBundle(bundle);
|
|
}
|
|
|
|
|
|
@Override
|
|
public void load(IonInput in) throws IOException
|
|
{
|
|
final IonBundle bundle = in.readBundle();
|
|
modelId = bundle.get("model", 0);
|
|
entityId = bundle.get("id", entityId);
|
|
bundle.loadBundled("data", data);
|
|
|
|
setModel(Entities.get(modelId));
|
|
}
|
|
|
|
|
|
/**
|
|
* @return unique entity id
|
|
*/
|
|
public int getEntityId()
|
|
{
|
|
return entityId;
|
|
}
|
|
|
|
|
|
public EntityPos getPosition()
|
|
{
|
|
return data.position;
|
|
}
|
|
|
|
|
|
public void setPosition(Coord coord)
|
|
{
|
|
data.position.setTo(coord);
|
|
lastPosition.setTo(coord);
|
|
cancelPath(); // discard remaining steps
|
|
}
|
|
|
|
|
|
/**
|
|
* @param delta delta time
|
|
*/
|
|
public void update(double delta)
|
|
{
|
|
if (!data.position.isFinished()) {
|
|
data.position.update(delta);
|
|
}
|
|
|
|
if (walking && data.position.isFinished()) {
|
|
walking = false;
|
|
level.freeTile(lastPosition.getCoord());
|
|
|
|
onStepFinished(this);
|
|
|
|
if (data.path.isEmpty()) {
|
|
onPathFinished(this);
|
|
}
|
|
}
|
|
|
|
if (!walking && !data.path.isEmpty()) {
|
|
|
|
walking = true;
|
|
|
|
final PathStep step = data.path.poll();
|
|
|
|
final Coord planned = data.position.getCoord().add(step.toCoord());
|
|
|
|
if (!level.canWalkInto(planned)) {
|
|
cancelPath();
|
|
onPathInterrupted(this);
|
|
walking = false;
|
|
} else {
|
|
|
|
// tmp for renderer
|
|
if (step.x != 0) renderData.lastXDir = step.x;
|
|
if (step.y != 0) renderData.lastYDir = step.y;
|
|
|
|
lastPosition.setTo(data.position);
|
|
data.position.walk(step, model.getStepTime(this));
|
|
level.occupyTile(planned);
|
|
}
|
|
}
|
|
|
|
if (!walking) {
|
|
model.update(this, delta);
|
|
}
|
|
}
|
|
|
|
|
|
public void render(MapRenderContext context)
|
|
{
|
|
model.renderer.render(this, context);
|
|
}
|
|
|
|
|
|
public boolean isPathFinished()
|
|
{
|
|
return data.position.isFinished() && data.path.isEmpty();
|
|
}
|
|
|
|
|
|
public void addStep(PathStep step)
|
|
{
|
|
data.path.add(step);
|
|
}
|
|
|
|
|
|
public void cancelPath()
|
|
{
|
|
data.path.clear();
|
|
}
|
|
|
|
|
|
public boolean navigateTo(Coord pos)
|
|
{
|
|
final List<PathStep> path = PathFinder.findPathRelative(pfc, getPosition().getCoord(), pos);
|
|
|
|
if (path == null) return false;
|
|
|
|
this.cancelPath();
|
|
this.addSteps(path);
|
|
return true;
|
|
}
|
|
|
|
|
|
@Override
|
|
public void onStepFinished(Entity entity)
|
|
{
|
|
for (final EntityMoveListener l : moveListeners) {
|
|
l.onStepFinished(entity);
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
public void onPathFinished(Entity entity)
|
|
{
|
|
for (final EntityMoveListener l : moveListeners) {
|
|
l.onStepFinished(entity);
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
public void onPathInterrupted(Entity entity)
|
|
{
|
|
for (final EntityMoveListener l : moveListeners) {
|
|
l.onPathInterrupted(entity);
|
|
}
|
|
}
|
|
|
|
|
|
public void addMoveListener(EntityMoveListener listener)
|
|
{
|
|
moveListeners.add(listener);
|
|
}
|
|
|
|
|
|
public void addSteps(List<PathStep> path)
|
|
{
|
|
data.path.addAll(path);
|
|
}
|
|
|
|
|
|
public void setLevel(Level level)
|
|
{
|
|
this.level = level;
|
|
}
|
|
|
|
|
|
public Coord getCoord()
|
|
{
|
|
return data.position.getCoord();
|
|
}
|
|
}
|
|
|