|
|
@ -3,20 +3,21 @@ package mightypork.rogue.world.entity; |
|
|
|
|
|
|
|
|
|
|
|
import java.io.IOException; |
|
|
|
import java.io.IOException; |
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.LinkedList; |
|
|
|
|
|
|
|
import java.util.List; |
|
|
|
import java.util.List; |
|
|
|
import java.util.Queue; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import mightypork.rogue.world.Coord; |
|
|
|
|
|
|
|
import mightypork.rogue.world.EntityPos; |
|
|
|
import mightypork.rogue.world.PathStep; |
|
|
|
import mightypork.rogue.world.PathStep; |
|
|
|
import mightypork.rogue.world.World; |
|
|
|
import mightypork.rogue.world.World; |
|
|
|
import mightypork.rogue.world.WorldPos; |
|
|
|
|
|
|
|
import mightypork.rogue.world.entity.models.EntityModel; |
|
|
|
import mightypork.rogue.world.entity.models.EntityModel; |
|
|
|
import mightypork.rogue.world.entity.models.EntityMoveListener; |
|
|
|
import mightypork.rogue.world.entity.models.EntityMoveListener; |
|
|
|
import mightypork.rogue.world.level.Level; |
|
|
|
import mightypork.rogue.world.level.Level; |
|
|
|
import mightypork.rogue.world.level.render.MapRenderContext; |
|
|
|
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.IonBinary; |
|
|
|
import mightypork.util.files.ion.IonBundle; |
|
|
|
import mightypork.util.files.ion.IonBundle; |
|
|
|
import mightypork.util.files.ion.IonBundled; |
|
|
|
|
|
|
|
import mightypork.util.files.ion.IonInput; |
|
|
|
import mightypork.util.files.ion.IonInput; |
|
|
|
import mightypork.util.files.ion.IonOutput; |
|
|
|
import mightypork.util.files.ion.IonOutput; |
|
|
|
|
|
|
|
|
|
|
@ -26,51 +27,68 @@ import mightypork.util.files.ion.IonOutput; |
|
|
|
* |
|
|
|
* |
|
|
|
* @author MightyPork |
|
|
|
* @author MightyPork |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public final class Entity implements IonBinary, IonBundled, EntityMoveListener { |
|
|
|
public final class Entity implements IonBinary, EntityMoveListener { |
|
|
|
|
|
|
|
|
|
|
|
// binary & bundled - binary stores via a bundle
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static final int ION_MARK = 52; |
|
|
|
public static final int ION_MARK = 52; |
|
|
|
|
|
|
|
|
|
|
|
private final WorldPos position = new WorldPos(); // saved
|
|
|
|
/** Last pos, will be freed upon finishing move */ |
|
|
|
private final WorldPos lastPosition = new WorldPos(); |
|
|
|
private final EntityPos lastPosition = new EntityPos(); |
|
|
|
|
|
|
|
private boolean walking = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Level level; |
|
|
|
|
|
|
|
private EntityModel model; |
|
|
|
|
|
|
|
|
|
|
|
/** Entity ID */ |
|
|
|
/** Entity ID */ |
|
|
|
private int eid = 0; // saved
|
|
|
|
private int entityId = -1; |
|
|
|
|
|
|
|
|
|
|
|
/** Model ID */ |
|
|
|
/** Model ID */ |
|
|
|
private int id; // saved
|
|
|
|
private int modelId = -1; |
|
|
|
|
|
|
|
|
|
|
|
private final Queue<PathStep> path = new LinkedList<>(); // saved
|
|
|
|
/** Entity data holder */ |
|
|
|
private EntityModel model; |
|
|
|
private final EntityData data = new EntityData(); |
|
|
|
public final IonBundle metadata = new IonBundle(); // saved
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Some infos for/by the renderer, not saved */ |
|
|
|
/** Temporary renderer's data */ |
|
|
|
public final EntityRenderData renderData = new EntityRenderData(); |
|
|
|
public final EntityRenderData renderData = new EntityRenderData(); |
|
|
|
|
|
|
|
|
|
|
|
private final List<EntityMoveListener> moveListeners = new ArrayList<>(); |
|
|
|
private final List<EntityMoveListener> moveListeners = new ArrayList<>(); |
|
|
|
|
|
|
|
|
|
|
|
// tmp flag
|
|
|
|
private final PathFindingContext pfc = new PathFindingContext() { |
|
|
|
private boolean walking = false; |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public boolean isAccessible(Coord pos) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return model.canWalkInto(Entity.this, pos); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Entity(int eid, WorldPos pos, EntityModel entityModel) |
|
|
|
@Override |
|
|
|
|
|
|
|
public int getMinCost() |
|
|
|
{ |
|
|
|
{ |
|
|
|
this.eid = eid; |
|
|
|
return model.getPathMinCost(); |
|
|
|
setPosition(pos); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
setModel(entityModel); |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public Heuristic getHeuristic() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return model.getPathHeuristic(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected final void setModel(EntityModel entityModel) |
|
|
|
@Override |
|
|
|
|
|
|
|
public int getCost(Coord from, Coord to) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// replace listener
|
|
|
|
return model.getPathCost(Entity.this, from, to); |
|
|
|
if (model != null) moveListeners.remove(model); |
|
|
|
} |
|
|
|
moveListeners.add(entityModel); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
this.id = entityModel.id; |
|
|
|
|
|
|
|
this.model = entityModel; |
|
|
|
public Entity(int eid, EntityModel entityModel) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
this.entityId = eid; |
|
|
|
|
|
|
|
setModel(entityModel); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
model.initMetadata(data); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -80,124 +98,115 @@ public final class Entity implements IonBinary, IonBundled, EntityMoveListener { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
private void setModel(EntityModel entityModel) |
|
|
|
public short getIonMark() |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
return ION_MARK; |
|
|
|
// replace listener
|
|
|
|
|
|
|
|
if (model != null) moveListeners.remove(model); |
|
|
|
|
|
|
|
moveListeners.add(entityModel); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.modelId = entityModel.id; |
|
|
|
|
|
|
|
this.model = entityModel; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
public Level getLevel() |
|
|
|
public void save(IonOutput out) throws IOException |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
final IonBundle ib = new IonBundle(); |
|
|
|
if (level == null) throw new IllegalStateException("Entity has no level assigned."); |
|
|
|
save(ib); |
|
|
|
return level; |
|
|
|
out.writeBundle(ib); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
public World getWorld() |
|
|
|
public void load(IonInput in) throws IOException |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
load(in.readBundle()); |
|
|
|
return getLevel().getWorld(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void save(IonBundle bundle) throws IOException |
|
|
|
public short getIonMark() |
|
|
|
{ |
|
|
|
{ |
|
|
|
bundle.put("id", id); |
|
|
|
return ION_MARK; |
|
|
|
bundle.putBundled("pos", position); |
|
|
|
|
|
|
|
bundle.putSequence("steps", path); |
|
|
|
|
|
|
|
bundle.put("eid", eid); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (model.hasMetadata()) { |
|
|
|
|
|
|
|
bundle.put("metadata", metadata); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void load(IonBundle bundle) throws IOException |
|
|
|
public void save(IonOutput out) throws IOException |
|
|
|
{ |
|
|
|
{ |
|
|
|
id = bundle.get("id", 0); |
|
|
|
final IonBundle bundle = new IonBundle(); |
|
|
|
|
|
|
|
bundle.put("model", modelId); |
|
|
|
|
|
|
|
bundle.put("id", entityId); |
|
|
|
|
|
|
|
bundle.putBundled("data", data); |
|
|
|
|
|
|
|
|
|
|
|
if (model == null || id != model.id) { |
|
|
|
out.writeBundle(bundle); |
|
|
|
setModel(Entities.get(id)); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bundle.loadBundled("pos", position); |
|
|
|
|
|
|
|
bundle.loadSequence("path", path); |
|
|
|
|
|
|
|
eid = bundle.get("eid", eid); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (model.hasMetadata()) { |
|
|
|
@Override |
|
|
|
metadata.clear(); |
|
|
|
public void load(IonInput in) throws IOException |
|
|
|
bundle.loadBundle("metadata", metadata); |
|
|
|
{ |
|
|
|
} |
|
|
|
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 |
|
|
|
* @return unique entity id |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public int getEID() |
|
|
|
public int getEntityId() |
|
|
|
{ |
|
|
|
|
|
|
|
return eid; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public WorldPos getPosition() |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
return position; |
|
|
|
return entityId; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public final void setPosition(WorldPos pos) |
|
|
|
public EntityPos getPosition() |
|
|
|
{ |
|
|
|
{ |
|
|
|
setPosition(pos.x, pos.y); |
|
|
|
return data.position; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void setPosition(int x, int y) |
|
|
|
public void setPosition(Coord coord) |
|
|
|
{ |
|
|
|
{ |
|
|
|
position.setTo(x, y); |
|
|
|
data.position.setTo(coord); |
|
|
|
lastPosition.setTo(x, y); |
|
|
|
lastPosition.setTo(coord); |
|
|
|
cancelPath(); // discard remaining steps
|
|
|
|
cancelPath(); // discard remaining steps
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* @param world the world |
|
|
|
|
|
|
|
* @param delta delta time |
|
|
|
* @param delta delta time |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public void update(World world, Level level, double delta) |
|
|
|
public void update(double delta) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (!position.isFinished()) { |
|
|
|
if (!data.position.isFinished()) { |
|
|
|
position.update(delta); |
|
|
|
data.position.update(delta); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (walking && position.isFinished()) { |
|
|
|
if (walking && data.position.isFinished()) { |
|
|
|
walking = false; |
|
|
|
walking = false; |
|
|
|
level.freeTile(lastPosition.x, lastPosition.y); |
|
|
|
level.freeTile(lastPosition.getCoord()); |
|
|
|
|
|
|
|
|
|
|
|
onStepFinished(this, world, level); |
|
|
|
onStepFinished(this); |
|
|
|
|
|
|
|
|
|
|
|
if (path.isEmpty()) { |
|
|
|
if (data.path.isEmpty()) { |
|
|
|
onPathFinished(this, world, level); |
|
|
|
onPathFinished(this); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!walking && !path.isEmpty()) { |
|
|
|
if (!walking && !data.path.isEmpty()) { |
|
|
|
|
|
|
|
|
|
|
|
walking = true; |
|
|
|
walking = true; |
|
|
|
|
|
|
|
|
|
|
|
final PathStep step = path.poll(); |
|
|
|
final PathStep step = data.path.poll(); |
|
|
|
|
|
|
|
|
|
|
|
final int projX = position.x + step.x, projY = position.y + step.y; |
|
|
|
final Coord planned = data.position.getCoord().add(step.toCoord()); |
|
|
|
|
|
|
|
|
|
|
|
if (!level.canWalkInto(projX, projY)) { |
|
|
|
if (!level.canWalkInto(planned)) { |
|
|
|
cancelPath(); |
|
|
|
cancelPath(); |
|
|
|
onPathInterrupted(this, world, level); |
|
|
|
onPathInterrupted(this); |
|
|
|
walking = false; |
|
|
|
walking = false; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
|
|
|
|
|
|
|
@ -205,14 +214,14 @@ public final class Entity implements IonBinary, IonBundled, EntityMoveListener { |
|
|
|
if (step.x != 0) renderData.lastXDir = step.x; |
|
|
|
if (step.x != 0) renderData.lastXDir = step.x; |
|
|
|
if (step.y != 0) renderData.lastYDir = step.y; |
|
|
|
if (step.y != 0) renderData.lastYDir = step.y; |
|
|
|
|
|
|
|
|
|
|
|
lastPosition.setTo(position); |
|
|
|
lastPosition.setTo(data.position); |
|
|
|
position.walk(step.x, step.y, getStepTime()); |
|
|
|
data.position.walk(step, model.getStepTime(this)); |
|
|
|
level.occupyTile(projX, projY); |
|
|
|
level.occupyTile(planned); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!walking) { |
|
|
|
if (!walking) { |
|
|
|
model.update(this, level, delta); |
|
|
|
model.update(this, delta); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -225,51 +234,57 @@ public final class Entity implements IonBinary, IonBundled, EntityMoveListener { |
|
|
|
|
|
|
|
|
|
|
|
public boolean isPathFinished() |
|
|
|
public boolean isPathFinished() |
|
|
|
{ |
|
|
|
{ |
|
|
|
return position.isFinished() && path.isEmpty(); |
|
|
|
return data.position.isFinished() && data.path.isEmpty(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void addStep(PathStep step) |
|
|
|
public void addStep(PathStep step) |
|
|
|
{ |
|
|
|
{ |
|
|
|
path.add(step); |
|
|
|
data.path.add(step); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void cancelPath() |
|
|
|
public void cancelPath() |
|
|
|
{ |
|
|
|
{ |
|
|
|
path.clear(); |
|
|
|
data.path.clear(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected double getStepTime() |
|
|
|
public boolean navigateTo(Coord pos) |
|
|
|
{ |
|
|
|
{ |
|
|
|
return model.getStepTime(this); |
|
|
|
final List<PathStep> path = PathFinder.findPathRelative(pfc, getPosition().getCoord(), pos); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (path == null) return false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.cancelPath(); |
|
|
|
|
|
|
|
this.addSteps(path); |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void onStepFinished(Entity entity, World world, Level level) |
|
|
|
public void onStepFinished(Entity entity) |
|
|
|
{ |
|
|
|
{ |
|
|
|
for (final EntityMoveListener l : moveListeners) { |
|
|
|
for (final EntityMoveListener l : moveListeners) { |
|
|
|
l.onStepFinished(entity, world, level); |
|
|
|
l.onStepFinished(entity); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void onPathFinished(Entity entity, World world, Level level) |
|
|
|
public void onPathFinished(Entity entity) |
|
|
|
{ |
|
|
|
{ |
|
|
|
for (final EntityMoveListener l : moveListeners) { |
|
|
|
for (final EntityMoveListener l : moveListeners) { |
|
|
|
l.onStepFinished(entity, world, level); |
|
|
|
l.onStepFinished(entity); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void onPathInterrupted(Entity entity, World world, Level level) |
|
|
|
public void onPathInterrupted(Entity entity) |
|
|
|
{ |
|
|
|
{ |
|
|
|
for (final EntityMoveListener l : moveListeners) { |
|
|
|
for (final EntityMoveListener l : moveListeners) { |
|
|
|
l.onPathInterrupted(entity, world, level); |
|
|
|
l.onPathInterrupted(entity); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -282,6 +297,18 @@ public final class Entity implements IonBinary, IonBundled, EntityMoveListener { |
|
|
|
|
|
|
|
|
|
|
|
public void addSteps(List<PathStep> path) |
|
|
|
public void addSteps(List<PathStep> path) |
|
|
|
{ |
|
|
|
{ |
|
|
|
this.path.addAll(path); |
|
|
|
data.path.addAll(path); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void setLevel(Level level) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
this.level = level; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Coord getCoord() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return data.position.getCoord(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|