entity & world rewrite. rendering broken atm.

v5stable
ondra 10 years ago
parent ab1c3b3a74
commit 19d13c7903
  1. 12
      src/mightypork/rogue/App.java
  2. 2
      src/mightypork/rogue/Res.java
  3. 4
      src/mightypork/rogue/screens/gamescreen/HudLayer.java
  4. 54
      src/mightypork/rogue/screens/gamescreen/ScreenGame.java
  5. 4
      src/mightypork/rogue/screens/gamescreen/WorldLayer.java
  6. 21
      src/mightypork/rogue/screens/gamescreen/world/MapView.java
  7. 20
      src/mightypork/rogue/screens/gamescreen/world/Minimap.java
  8. 80
      src/mightypork/rogue/world/PlayerControl.java
  9. 11
      src/mightypork/rogue/world/World.java
  10. 115
      src/mightypork/rogue/world/WorldProvider.java
  11. 52
      src/mightypork/rogue/world/WorldRenderer.java
  12. 61
      src/mightypork/rogue/world/entity/Entities.java
  13. 282
      src/mightypork/rogue/world/entity/Entity.java
  14. 48
      src/mightypork/rogue/world/entity/EntityData.java
  15. 55
      src/mightypork/rogue/world/entity/EntityModel.java
  16. 49
      src/mightypork/rogue/world/entity/EntityPathfindingContext.java
  17. 14
      src/mightypork/rogue/world/entity/EntityRenderData.java
  18. 25
      src/mightypork/rogue/world/entity/SimpleEntityPathFindingContext.java
  19. 55
      src/mightypork/rogue/world/entity/entities/PlayerEntity.java
  20. 108
      src/mightypork/rogue/world/entity/models/EntityModel.java
  21. 95
      src/mightypork/rogue/world/entity/models/PlayerModel.java
  22. 92
      src/mightypork/rogue/world/entity/modules/EntityHealthModule.java
  23. 10
      src/mightypork/rogue/world/entity/modules/EntityModule.java
  24. 5
      src/mightypork/rogue/world/entity/modules/EntityMoveListener.java
  25. 11
      src/mightypork/rogue/world/entity/modules/EntityPos.java
  26. 199
      src/mightypork/rogue/world/entity/modules/EntityPosModule.java
  27. 9
      src/mightypork/rogue/world/entity/renderers/EntityRenderer.java
  28. 16
      src/mightypork/rogue/world/entity/renderers/NullEntityRenderer.java
  29. 24
      src/mightypork/rogue/world/entity/renderers/SimpleLeftRightMobRenderer.java
  30. 39
      src/mightypork/rogue/world/level/Level.java
  31. 2
      src/mightypork/rogue/world/tile/TileModel.java
  32. 29
      src/mightypork/util/error/IllegalValueException.java
  33. 29
      src/mightypork/util/error/YouFuckedUpException.java
  34. 18
      src/mightypork/util/files/ion/IonOutput.java

@ -22,6 +22,7 @@ import mightypork.rogue.screens.main_menu.ScreenMainMenu;
import mightypork.rogue.screens.test_bouncyboxes.ScreenTestBouncy;
import mightypork.rogue.screens.test_cat_sound.ScreenTestCat;
import mightypork.rogue.screens.test_render.ScreenTestRender;
import mightypork.rogue.world.WorldProvider;
import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.item.Item;
import mightypork.rogue.world.level.Level;
@ -73,6 +74,9 @@ public final class App extends BaseApp {
@Override
protected void initScreens(ScreenRegistry screens)
{
// world provider instance is referenced by screens
WorldProvider.init(this);
screens.addScreen(new ScreenTestBouncy(this));
screens.addScreen(new ScreenTestCat(this));
screens.addScreen(new ScreenTestRender(this));
@ -108,7 +112,13 @@ public final class App extends BaseApp {
{
Ion.registerBinary(Item.ION_MARK, Item.class);
Ion.registerBinary(Level.ION_MARK, Level.class);
Ion.registerBinary(Entity.ION_MARK, Entity.class);
}
@Override
protected void postInit()
{
// TODO tmp
WorldProvider.get().createWorld(37);
}

@ -82,7 +82,7 @@ public final class Res {
// sprites
texture = textures.loadTexture("mob", "/res/img/dudes-b.png", FilterMode.NEAREST, WrapMode.CLAMP);
tiles = texture.grid(8, 8);
textures.addSheet("player", tiles.makeSheet(0, 0, 4, 1));
textures.addSheet("sprite.player", tiles.makeSheet(0, 0, 4, 1));
// small sheet
texture = textures.loadTexture("tiles16", "/res/img/tiles16.png", FilterMode.NEAREST, WrapMode.CLAMP);

@ -19,7 +19,7 @@ import mightypork.util.math.constraints.rect.Rect;
public class HudLayer extends ScreenLayer {
public HudLayer(Screen screen, World world)
public HudLayer(Screen screen)
{
super(screen);
@ -52,7 +52,7 @@ public class HudLayer extends ScreenLayer {
root.add(experience);
final Minimap mm = new Minimap(world);
final Minimap mm = new Minimap();
mm.setRect(root.shrink(root.width().perc(5), root.height().perc(15)));
root.add(mm);

@ -10,64 +10,40 @@ import mightypork.gamecore.gui.screens.LayeredScreen;
import mightypork.rogue.Paths;
import mightypork.rogue.world.World;
import mightypork.rogue.world.WorldCreator;
import mightypork.rogue.world.WorldProvider;
import mightypork.util.files.ion.Ion;
public class ScreenGame extends LayeredScreen {
private final World world;
public ScreenGame(AppAccess app)
{
super(app);
this.world = obtainWorld();
addLayer(new HudLayer(this, world));
addLayer(new WorldLayer(this, world));
addLayer(new HudLayer(this));
addLayer(new WorldLayer(this));
}
private World obtainWorld()
@Override
public String getName()
{
// FIXME just temporary test here
final Random rand = new Random();
final File f = new File(Paths.WORKDIR, "test-world.ion");
// SAVE
final World world = WorldCreator.createWorld(rand.nextLong());
addChildClient(world);
try {
Ion.toFile(f, world);
} catch (final IOException e) {
e.printStackTrace();
}
// LOAD
// final World w;
//
// try {
// world = Ion.fromFile(f, World.class);
// } catch (IOException e) {
// e.printStackTrace();
// System.exit(1);
// return;
// }
return world;
return "game_screen";
}
@Override
public String getName()
protected void onScreenEnter()
{
return "game_screen";
super.onScreenEnter();
WorldProvider.get().setListening(true);
}
@Override
protected void onScreenLeave()
{
super.onScreenLeave();
WorldProvider.get().setListening(false);
}
}

@ -15,13 +15,13 @@ public class WorldLayer extends ScreenLayer {
private final MapView worldView;
public WorldLayer(Screen screen, World world)
public WorldLayer(Screen screen)
{
super(screen);
// render component
worldView = new MapView(world);
worldView = new MapView();
// map input plugins
worldView.addPlugin(new MIPKeyWalk());

@ -13,9 +13,10 @@ import mightypork.gamecore.input.Keys;
import mightypork.rogue.world.Coord;
import mightypork.rogue.world.PlayerControl;
import mightypork.rogue.world.World;
import mightypork.rogue.world.WorldProvider;
import mightypork.rogue.world.WorldRenderer;
import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.entity.models.EntityMoveListener;
import mightypork.rogue.world.entity.modules.EntityMoveListener;
import mightypork.util.math.Easing;
import mightypork.util.math.constraints.num.Num;
import mightypork.util.math.constraints.num.mutable.NumAnimated;
@ -26,20 +27,19 @@ import mightypork.util.timing.Updateable;
public class MapView extends InputComponent implements KeyListener, MouseButtonListener, EntityMoveListener, Updateable {
protected final WorldRenderer worldRenderer;
protected final World world;
private final PlayerControl pc;
private final Set<MapInteractionPlugin> plugins = new HashSet<>();
private Num tileSize;
private final NumAnimated zoom = new NumAnimated(0, Easing.SINE_BOTH);
private final Num tileSize;
public MapView(World world)
public MapView()
{
this.world = world;
this.tileSize = height().min(width()).div(8).max(32).mul(Num.make(1).sub(zoom.mul(0.66)));
this.worldRenderer = new WorldRenderer(world, this, tileSize);
pc = world.getPlayerControl();
this.worldRenderer = new WorldRenderer(this, tileSize);
pc = WorldProvider.get().getPlayerControl();
pc.addMoveListener(this);
}
@ -51,13 +51,6 @@ public class MapView extends InputComponent implements KeyListener, MouseButtonL
}
@Override
public void updateLayout()
{
worldRenderer.poll(); // update sizing
}
/**
* Get tile coord at a screen position
*

@ -7,8 +7,9 @@ import mightypork.gamecore.gui.components.InputComponent;
import mightypork.gamecore.render.Render;
import mightypork.rogue.world.Coord;
import mightypork.rogue.world.World;
import mightypork.rogue.world.WorldProvider;
import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.entity.EntityPos;
import mightypork.rogue.world.entity.modules.EntityPos;
import mightypork.rogue.world.level.Level;
import mightypork.rogue.world.tile.Tile;
import mightypork.util.math.color.Color;
@ -23,29 +24,24 @@ import org.lwjgl.opengl.GL11;
public class Minimap extends InputComponent implements MouseButtonListener {
private final World world;
private final RectMutable bounds = Rect.makeVar();
private int unit = 0;
private final Num translucency = Num.make(0.8);
private final Color playerColor = RGB.RED;
public Minimap(World w) {
this.world = w;
}
@Override
protected void renderComponent()
{
Color.pushAlpha(translucency);
final Level lvl = world.getCurrentLevel();
final Level lvl = WorldProvider.get().getCurrentLevel();
unit = (int) Math.min(Math.max(2, Math.ceil((height().value()/2) / (lvl.getHeight() + 2))), 6);
final World w = lvl.getWorld();
final Entity e = w.getPlayerEntity();
final EntityPos plCoord = e.getPosition();
final Vect plCoord = e.pos.getVisualPos();
final int lw = lvl.getWidth();
final int lh = lvl.getHeight();
@ -80,8 +76,8 @@ public class Minimap extends InputComponent implements MouseButtonListener {
// player
Render.setColor(playerColor);
final double plx = tl.xi() + plCoord.visualX() * unit;
final double ply = tl.yi() + plCoord.visualY() * unit;
final double plx = tl.xi() + plCoord.x() * unit;
final double ply = tl.yi() + plCoord.y() * unit;
GL11.glVertex2d(plx, ply);
GL11.glVertex2d(plx + unit, ply);
@ -101,10 +97,10 @@ public class Minimap extends InputComponent implements MouseButtonListener {
if (event.isUp()) {
final Vect relative = event.getPos().sub(bounds.origin());
final Coord actual = Coord.make(relative.xi() / unit, relative.yi() / unit);
final Entity player = world.getPlayerEntity();
final Entity player = WorldProvider.get().getPlayerEntity();
if (player.getLevel().getTile(actual).isExplored()) {
player.navigateTo(actual);
player.pos.navigateTo(actual);
}
}

@ -1,90 +1,102 @@
package mightypork.rogue.world;
import java.util.HashSet;
import java.util.Set;
import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.entity.EntityPos;
import mightypork.rogue.world.entity.PathStep;
import mightypork.rogue.world.entity.models.EntityMoveListener;
import mightypork.rogue.world.entity.modules.EntityMoveListener;
import mightypork.rogue.world.entity.modules.EntityPos;
import mightypork.rogue.world.level.Level;
public class PlayerControl {
public abstract class PlayerControl {
private final World world;
protected Set<EntityMoveListener> playerMoveListeners = new HashSet<>();
private World lastWorld;
public PlayerControl(World w)
{
this.world = w;
}
protected abstract World getWorld();
public Entity getEntity()
private World getWorld2()
{
World newWorld = getWorld();
if (newWorld != lastWorld) {
for (EntityMoveListener eml : playerMoveListeners) {
newWorld.getPlayerEntity().pos.addMoveListener(eml);
}
}
lastWorld = newWorld;
return newWorld;
};
private Entity getPlayerEntity()
{
return world.getPlayerEntity();
if(getWorld2() == null) return null;
return getWorld2().getPlayerEntity();
}
public void goNorth()
{
getEntity().cancelPath();
getEntity().addStep(PathStep.NORTH);
getPlayerEntity().pos.cancelPath();
getPlayerEntity().pos.addStep(PathStep.NORTH);
}
public void goSouth()
{
getEntity().cancelPath();
getEntity().addStep(PathStep.SOUTH);
getPlayerEntity().pos.cancelPath();
getPlayerEntity().pos.addStep(PathStep.SOUTH);
}
public void goEast()
{
getEntity().cancelPath();
getEntity().addStep(PathStep.EAST);
getPlayerEntity().pos.cancelPath();
getPlayerEntity().pos.addStep(PathStep.EAST);
}
public void goWest()
{
getEntity().cancelPath();
getEntity().addStep(PathStep.WEST);
getPlayerEntity().pos.cancelPath();
getPlayerEntity().pos.addStep(PathStep.WEST);
}
public void navigateTo(Coord pos)
{
getEntity().navigateTo(pos);
getPlayerEntity().pos.navigateTo(pos);
}
public void addMoveListener(EntityMoveListener eml)
{
getEntity().addMoveListener(eml);
}
public EntityPos getPos()
{
return getEntity().getPosition();
}
public World getWorld()
{
return world;
playerMoveListeners.add(eml);
if(getPlayerEntity() != null) {
getPlayerEntity().pos.addMoveListener(eml);
}
}
public Level getLevel()
{
return world.getCurrentLevel();
return getWorld2().getCurrentLevel();
}
public Coord getCoord()
{
return getEntity().getCoord();
return getPlayerEntity().pos.getCoord();
}
}

@ -24,8 +24,6 @@ public class World implements IonBundled, Updateable {
private final PlayerInfo playerInfo = new PlayerInfo();
private Entity playerEntity;
private final PlayerControl control = new PlayerControl(this);
/** World seed */
private long seed;
@ -48,6 +46,7 @@ public class World implements IonBundled, Updateable {
in.loadBundled("player", playerInfo);
playerEntity = levels.get(playerInfo.getLevel()).getEntity(playerInfo.getEID());
if(playerEntity == null) throw new RuntimeException("Player entity not found in the world.");
}
@ -104,7 +103,7 @@ public class World implements IonBundled, Updateable {
final int playerEid = getNewEID();
playerEntity = Entities.PLAYER.createEntity(playerEid);
playerEntity.setPosition(levels.get(level).getEnterPoint());
playerEntity.pos.setPosition(levels.get(level).getEnterPoint());
levels.get(level).addEntity(playerEntity);
playerInfo.setLevel(level);
@ -118,12 +117,6 @@ public class World implements IonBundled, Updateable {
}
public PlayerControl getPlayerControl()
{
return control;
}
public Entity getPlayerEntity()
{
return playerEntity;

@ -0,0 +1,115 @@
package mightypork.rogue.world;
import java.io.File;
import java.io.IOException;
import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.entity.modules.EntityMoveListener;
import mightypork.rogue.world.level.Level;
import mightypork.util.control.eventbus.BusAccess;
import mightypork.util.control.eventbus.clients.BusNode;
import mightypork.util.control.eventbus.clients.RootBusNode;
import mightypork.util.files.ion.Ion;
import mightypork.util.timing.Updateable;
public class WorldProvider extends RootBusNode implements Updateable {
public static synchronized void init(BusAccess busAccess)
{
if (inst == null) {
inst = new WorldProvider(busAccess);
}
}
public WorldProvider(BusAccess busAccess) {
super(busAccess);
setListening(false);
}
private static WorldProvider inst;
public static WorldProvider get()
{
if (inst == null) {
throw new IllegalStateException("World provider not initialized.");
}
return inst;
}
private World world;
private final PlayerControl playerControl = new PlayerControl() {
@Override
protected World getWorld()
{
return world;
}
};
public void createWorld(long seed)
{
if (world != null) removeChildClient(world);
world = WorldCreator.createWorld(seed);
addChildClient(world);
}
public World getWorld()
{
return world;
}
public void loadWorld(File file) throws IOException
{
world = Ion.fromFile(file, World.class);
}
public void saveWorld(File file) throws IOException
{
if (world == null) throw new IllegalStateException("Trying to save a NULL world.");
Ion.toFile(file, world);
}
public Level getCurrentLevel()
{
return getWorld().getCurrentLevel();
}
public Entity getPlayerEntity()
{
return getWorld().getPlayerEntity();
}
/**
* @return constant player control (world independent)
*/
public PlayerControl getPlayerControl()
{
return playerControl;
}
@Override
public void update(double delta)
{
world.update(delta);
}
@Override
protected void deinit()
{
}
}

@ -4,7 +4,7 @@ package mightypork.rogue.world;
import mightypork.gamecore.render.Render;
import mightypork.rogue.Res;
import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.entity.EntityPos;
import mightypork.rogue.world.entity.modules.EntityPos;
import mightypork.rogue.world.level.Level;
import mightypork.rogue.world.level.render.TileRenderContext;
import mightypork.util.math.color.RGB;
@ -22,15 +22,12 @@ import mightypork.util.math.constraints.vect.VectConst;
*
* @author MightyPork
*/
public class WorldRenderer extends RectProxy implements Pollable {
public class WorldRenderer extends RectProxy {
private static final boolean USE_BATCH_RENDERING = true;
private final Num tileSize;
private final World world;
private final Entity player;
// can be changed
private RectConst mapRect;
private Level activeLevel;
@ -43,12 +40,9 @@ public class WorldRenderer extends RectProxy implements Pollable {
private TileRenderContext trc;
public WorldRenderer(World world, Rect viewport, Num tileSize) {
public WorldRenderer(Rect viewport, Num tileSize) {
super(viewport);
this.world = world;
this.player = world.getPlayerEntity();
this.tileSize = tileSize;
final Num grX = width().perc(30);
@ -58,16 +52,15 @@ public class WorldRenderer extends RectProxy implements Pollable {
final Num grY = height().perc(20);
topShadow = topEdge().growDown(grY);
bottomShadow = bottomEdge().growUp(grY);
rebuildTiles();
}
private void rebuildTiles()
private void prepareRenderContextIfNeeded()
{
final Level level = world.getCurrentLevel();
final Level level = WorldProvider.get().getCurrentLevel();
if (activeLevel == level) return;
activeLevel = level;
mapRect = Rect.make(0, 0, level.getWidth(), level.getHeight());
@ -78,24 +71,25 @@ public class WorldRenderer extends RectProxy implements Pollable {
private VectConst getOffset()
{
final EntityPos pos = player.getPosition();
final double playerX = pos.visualX();
final double playerY = pos.visualY();
return Vect.make(-playerX-0.5, -playerY-0.5);
Entity ent = WorldProvider.get().getPlayerEntity();
return Vect.make(ent.pos.getVisualPos().neg().add(0.5, 0.5)).freeze();
}
public void render()
{
prepareRenderContextIfNeeded();
Render.pushMatrix();
Render.setColor(RGB.WHITE);
Render.translate(center());
Render.scaleXY(tileSize.value());
Render.translate(getOffset());
// tiles to render
final EntityPos pos = player.getPosition();
Entity ent = WorldProvider.get().getPlayerEntity();
final Coord pos = ent.pos.getCoord();
final double w = width().value();
final double h = height().value();
@ -104,10 +98,10 @@ public class WorldRenderer extends RectProxy implements Pollable {
final int xtilesh = (int) (w / (ts * 2)) + 1;
final int ytilesh = (int) (h / (ts * 2)) + 1;
final int x1 = pos.x() - xtilesh;
final int y1 = pos.y() - ytilesh;
final int x2 = pos.x() + xtilesh;
final int y2 = pos.y() + ytilesh;
final int x1 = pos.x - xtilesh;
final int y1 = pos.y - ytilesh;
final int x2 = pos.x + xtilesh;
final int y2 = pos.y + ytilesh;
// === TILES ===
@ -139,8 +133,9 @@ public class WorldRenderer extends RectProxy implements Pollable {
for (final Entity e : activeLevel.getEntities()) {
// avoid entities out of view rect
final int x = (int) Math.round(e.getPosition().visualX());
final int y = (int) Math.round(e.getPosition().visualY());
Vect entPos = e.pos.getVisualPos();
final int x = (int) Math.round(entPos.x());
final int y = (int) Math.round(entPos.y());
if (x < x1 - ts || x > x2 + ts) continue;
if (y < y1 - ts || y > y2 + ts) continue;
@ -167,11 +162,4 @@ public class WorldRenderer extends RectProxy implements Pollable {
return new Coord(v.xi() / ts, v.yi() / ts);
}
@Override
public void poll()
{
rebuildTiles();
}
}

@ -1,8 +1,12 @@
package mightypork.rogue.world.entity;
import mightypork.rogue.world.entity.models.EntityModel;
import mightypork.rogue.world.entity.models.PlayerModel;
import java.io.IOException;
import java.util.Collection;
import mightypork.rogue.world.entity.entities.PlayerEntity;
import mightypork.util.files.ion.IonInput;
import mightypork.util.files.ion.IonOutput;
/**
@ -14,14 +18,18 @@ public final class Entities {
private static final EntityModel[] entities = new EntityModel[256];
public static final EntityModel PLAYER = new PlayerModel(0);
public static final EntityModel PLAYER = new EntityModel(1, PlayerEntity.class);
public static void register(int id, EntityModel model)
{
if (id < 0 || id >= entities.length) { throw new IllegalArgumentException("Entity model ID " + id + " is out of range."); }
if (id < 0 || id >= entities.length) {
throw new IllegalArgumentException("Entity model ID " + id + " is out of range.");
}
if (entities[id] != null) { throw new IllegalArgumentException("Entity model ID " + id + " already in use."); }
if (entities[id] != null) {
throw new IllegalArgumentException("Entity model ID " + id + " already in use.");
}
entities[id] = model;
}
@ -31,8 +39,49 @@ public final class Entities {
{
final EntityModel e = entities[id];
if (e == null) { throw new IllegalArgumentException("No entity model with ID " + id + "."); }
if (e == null) {
throw new IllegalArgumentException("No entity model with ID " + id + ".");
}
return e;
}
public static void loadEntities(IonInput in, Collection<Entity> entities) throws IOException
{
entities.clear();
while (in.hasNextEntry()) {
entities.add(loadEntity(in));
}
}
public static void saveEntities(IonOutput out, Collection<Entity> entities) throws IOException
{
for (Entity entity : entities) {
out.startEntry();
saveEntity(out, entity);
}
out.endSequence();
}
public static Entity loadEntity(IonInput in) throws IOException
{
int id = in.readIntByte();
EntityModel model = get(id);
return model.loadEntity(in);
}
public static void saveEntity(IonOutput out, Entity entity) throws IOException
{
EntityModel model = entity.getModel();
out.writeIntByte(model.id);
model.saveEntity(out, entity);
}
}

@ -2,23 +2,21 @@ package mightypork.rogue.world.entity;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import mightypork.rogue.world.Coord;
import mightypork.rogue.world.Sides;
import mightypork.rogue.world.World;
import mightypork.rogue.world.entity.models.EntityModel;
import mightypork.rogue.world.entity.models.EntityMoveListener;
import mightypork.rogue.world.entity.modules.EntityHealthModule;
import mightypork.rogue.world.entity.modules.EntityModule;
import mightypork.rogue.world.entity.modules.EntityPosModule;
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;
import mightypork.util.annotations.DefaultImpl;
import mightypork.util.error.IllegalValueException;
import mightypork.util.files.ion.*;
import mightypork.util.timing.Updateable;
/**
@ -26,135 +24,51 @@ import mightypork.util.files.ion.IonOutput;
*
* @author MightyPork
*/
public final class Entity implements IonBinary {
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;
public abstract class Entity implements IonBundled, Updateable {
private Level level;
private EntityModel model;
private final EntityModel model;
/** Entity ID */
private int entityId = -1;
private int entityId;
/** Model ID */
private int modelId = -1;
private final Map<String, EntityModule> modules = new HashMap<>();
/** Entity data holder */
private final EntityData data = new EntityData();
// default modules
public final EntityPosModule pos = new EntityPosModule(this);
public final EntityHealthModule health = new EntityHealthModule(this);
/** 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(EntityModel model, int eid) {
@Override
public Coord[] getWalkSides() {
return Sides.cardinalSides;
}
};
public Entity(int eid, EntityModel entityModel)
{
this.entityId = eid;
setModel(entityModel);
this.model = model;
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;
// register modules
modules.put("pos", pos);
modules.put("health", health);
}
@Override
public void save(IonOutput out) throws IOException
public void save(IonBundle bundle) throws IOException
{
final IonBundle bundle = new IonBundle();
bundle.put("model", modelId);
bundle.put("id", entityId);
bundle.putBundled("data", data);
out.writeBundle(bundle);
bundle.put("eid", entityId);
for (Entry<String, EntityModule> entry : modules.entrySet()) {
bundle.putBundled(entry.getKey(), entry.getValue());
}
}
@Override
public void load(IonInput in) throws IOException
public void load(IonBundle bundle) throws IOException
{
final IonBundle bundle = in.readBundle();
modelId = bundle.get("model", 0);
entityId = bundle.get("id", entityId);
bundle.loadBundled("data", data);
entityId = bundle.get("eid", -1);
if (entityId < 0) throw new IllegalValueException("Bad entity id: " + entityId);
setModel(Entities.get(modelId));
for (Entry<String, EntityModule> entry : modules.entrySet()) {
bundle.loadBundled(entry.getKey(), entry.getValue());
}
}
@ -167,143 +81,47 @@ public final class Entity implements IonBinary {
}
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());
for (final EntityMoveListener l : moveListeners) {
l.onStepFinished(this);
}
if (data.path.isEmpty()) {
for (final EntityMoveListener l : moveListeners) {
l.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();
for (final EntityMoveListener l : moveListeners) {
l.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)
public void setLevel(Level level)
{
data.path.add(step);
this.level = level;
}
public void cancelPath()
public Level getLevel()
{
data.path.clear();
return level;
}
public boolean navigateTo(Coord pos)
public World getWorld()
{
if(pos.equals(getCoord())) return true;
final List<PathStep> path = PathFinder.findPathRelative(pfc, getPosition().getCoord(), pos);
if (path == null) return false;
this.cancelPath();
this.addSteps(path);
return true;
return getLevel().getWorld();
}
public void addMoveListener(EntityMoveListener listener)
public EntityModel getModel()
{
moveListeners.add(listener);
return model;
}
public void addSteps(List<PathStep> path)
{
data.path.addAll(path);
}
public abstract PathFindingContext getPathfindingContext();
public void setLevel(Level level)
{
this.level = level;
model.onEnteredLevel(this);
}
public abstract void render(MapRenderContext context);
public Coord getCoord()
@Override
public void update(double delta)
{
return data.position.getCoord();
for (Entry<String, EntityModule> entry : modules.entrySet()) {
entry.getValue().update(delta);
}
}
public EntityModel getModel()
@DefaultImpl
public void onKilled()
{
return model;
}
}

@ -1,48 +0,0 @@
package mightypork.rogue.world.entity;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;
import mightypork.util.files.ion.IonBundle;
import mightypork.util.files.ion.IonBundled;
public class EntityData implements IonBundled {
public int health = 1;
public int maxHealth = 1;
public boolean dead = false;
public final IonBundle extra = new IonBundle();
public final Queue<PathStep> path = new LinkedList<>();
public final EntityPos position = new EntityPos();
@Override
public void save(IonBundle bundle) throws IOException
{
bundle.put("health", health);
bundle.put("max_health", maxHealth);
bundle.put("dead", dead);
bundle.putSequence("steps", path);
bundle.putBundled("pos", position);
bundle.put("extra", extra);
}
@Override
public void load(IonBundle bundle) throws IOException
{
health = bundle.get("health", health);
maxHealth = bundle.get("max_health", maxHealth);
dead = bundle.get("dead", dead);
bundle.loadSequence("path", path);
bundle.loadBundled("pos", position);
bundle.loadBundle("extra", extra);
}
}

@ -0,0 +1,55 @@
package mightypork.rogue.world.entity;
import java.io.IOException;
import mightypork.util.files.ion.IonBundle;
import mightypork.util.files.ion.IonInput;
import mightypork.util.files.ion.IonOutput;
/**
* Entity model
*
* @author MightyPork
*/
public final class EntityModel {
/** Model ID */
public final int id;
public final Class<? extends Entity> tileClass;
public EntityModel(int id, Class<? extends Entity> entity)
{
Entities.register(id, this);
this.id = id;
this.tileClass = entity;
}
public Entity createEntity(int eid)
{
try {
return tileClass.getConstructor(EntityModel.class, int.class).newInstance(this, eid);
} catch (Exception e) {
throw new RuntimeException("Could not instantiate a tile.", e);
}
}
public Entity loadEntity(IonInput in) throws IOException
{
IonBundle bundle = in.readBundle();
Entity ent = createEntity(-1);
ent.load(bundle);
return ent;
}
public void saveEntity(IonOutput out, Entity entity) throws IOException
{
IonBundle bundle = new IonBundle();
entity.save(bundle);
out.writeBundle(bundle);
}
}

@ -0,0 +1,49 @@
package mightypork.rogue.world.entity;
import mightypork.rogue.world.Coord;
import mightypork.rogue.world.Sides;
import mightypork.rogue.world.pathfinding.Heuristic;
import mightypork.rogue.world.pathfinding.PathFinder;
import mightypork.rogue.world.pathfinding.PathFindingContext;
public abstract class EntityPathfindingContext implements PathFindingContext {
protected final Entity entity;
public EntityPathfindingContext(Entity entity) {
this.entity = entity;
}
@Override
public boolean isAccessible(Coord pos)
{
return entity.getLevel().isWalkable(pos);
}
@Override
public abstract int getCost(Coord from, Coord to);
@Override
public abstract int getMinCost();
@Override
public Heuristic getHeuristic()
{
return PathFinder.DIAGONAL_HEURISTIC;
}
@Override
public Coord[] getWalkSides()
{
return Sides.cardinalSides;
}
}

@ -1,14 +0,0 @@
package mightypork.rogue.world.entity;
/**
* Data storage for renderer / entity.
*
* @author MightyPork
*/
public class EntityRenderData {
public int lastXDir = 1;
public int lastYDir = 1;
}

@ -0,0 +1,25 @@
package mightypork.rogue.world.entity;
import mightypork.rogue.world.Coord;
public class SimpleEntityPathFindingContext extends EntityPathfindingContext {
public SimpleEntityPathFindingContext(Entity entity) {
super(entity);
}
@Override
public int getCost(Coord from, Coord to)
{
return 10;
}
@Override
public int getMinCost()
{
return 10;
}
}

@ -0,0 +1,55 @@
package mightypork.rogue.world.entity.entities;
import mightypork.rogue.world.Coord;
import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.entity.EntityModel;
import mightypork.rogue.world.entity.EntityPathfindingContext;
import mightypork.rogue.world.entity.SimpleEntityPathFindingContext;
import mightypork.rogue.world.entity.renderers.EntityRenderer;
import mightypork.rogue.world.entity.renderers.SimpleLeftRightMobRenderer;
import mightypork.rogue.world.level.render.MapRenderContext;
import mightypork.rogue.world.pathfinding.PathFindingContext;
public class PlayerEntity extends Entity {
private final EntityPathfindingContext pathfc = new SimpleEntityPathFindingContext(this) {
@Override
public int getCost(Coord from, Coord to)
{
if (!getLevel().getTile(pos.getCoord()).isExplored()) {
return 1000;
}
return super.getCost(from, to);
};
};
private final EntityRenderer renderer = new SimpleLeftRightMobRenderer(this, "sprite.player");
public PlayerEntity(EntityModel model, int eid) {
super(model, eid);
// init default values
pos.setStepTime(0.25);
}
@Override
public PathFindingContext getPathfindingContext()
{
return pathfc;
}
@Override
public void render(MapRenderContext context)
{
renderer.render(context);
}
}

@ -1,108 +0,0 @@
package mightypork.rogue.world.entity.models;
import mightypork.rogue.world.Coord;
import mightypork.rogue.world.entity.Entities;
import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.entity.EntityData;
import mightypork.rogue.world.entity.renderers.EntityRenderer;
import mightypork.rogue.world.pathfinding.Heuristic;
import mightypork.rogue.world.pathfinding.PathFinder;
import mightypork.util.annotations.DefaultImpl;
/**
* Entity model
*
* @author MightyPork
*/
public abstract class EntityModel implements EntityMoveListener {
/** Model ID */
public final int id;
public EntityRenderer renderer = EntityRenderer.NONE;
public EntityModel(int id) {
Entities.register(id, this);
this.id = id;
}
public EntityModel setRenderer(EntityRenderer renderer)
{
this.renderer = renderer;
return this;
}
/**
* @return new tile of this type; if 100% invariant, can return cached one.
*/
public Entity createEntity(int eid)
{
return new Entity(eid, this);
}
/**
* Entity is idle, waiting for action.
*/
public abstract void update(Entity entity, double delta);
/**
* Get one path step duration (in seconds)
*/
public abstract double getStepTime(Entity entity);
@Override
public abstract void onStepFinished(Entity entity);
@Override
public abstract void onPathFinished(Entity entity);
@Override
public abstract void onPathInterrupted(Entity entity);
@DefaultImpl
public boolean canWalkInto(Entity entity, Coord pos)
{
return entity.getLevel().canWalkInto(pos);
}
@DefaultImpl
public int getPathMinCost()
{
return 10;
}
@DefaultImpl
public Heuristic getPathHeuristic()
{
return PathFinder.DIAGONAL_HEURISTIC;
}
@DefaultImpl
public int getPathCost(Entity entity, Coord from, Coord to)
{
return 10;
}
public abstract void initMetadata(EntityData metadata);
@DefaultImpl
public void onEnteredLevel(Entity entity)
{
}
}

@ -1,95 +0,0 @@
package mightypork.rogue.world.entity.models;
import mightypork.rogue.world.Coord;
import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.entity.EntityData;
import mightypork.rogue.world.entity.renderers.PlayerRenderer;
import mightypork.rogue.world.tile.Tile;
/**
* Player info
*
* @author MightyPork
*/
public class PlayerModel extends EntityModel {
private static final double STEP_TIME = 0.25;
public PlayerModel(int id) {
super(id);
setRenderer(new PlayerRenderer("player"));
}
@Override
public void update(Entity entity, double delta)
{
}
@Override
public double getStepTime(Entity entity)
{
return STEP_TIME;
}
@Override
public void onStepFinished(Entity entity)
{
exploreSurroundings(entity);
}
private void exploreSurroundings(Entity entity)
{
entity.getLevel().explore(entity.getCoord());
}
@Override
public void onEnteredLevel(Entity entity)
{
exploreSurroundings(entity);
}
@Override
public void onPathFinished(Entity entity)
{
}
@Override
public void onPathInterrupted(Entity entity)
{
}
@Override
public void initMetadata(EntityData metadata)
{
}
@Override
public boolean canWalkInto(Entity entity, Coord pos)
{
final Tile t = entity.getLevel().getTile(pos);
return t.isWalkable();
}
@Override
public int getPathCost(Entity entity, Coord from, Coord to)
{
if (!entity.getLevel().getTile(entity.getCoord()).isExplored()) {
return 1000;
}
return super.getPathCost(entity, from, to);
}
}

@ -0,0 +1,92 @@
package mightypork.rogue.world.entity.modules;
import java.io.IOException;
import mightypork.rogue.world.entity.Entity;
import mightypork.util.error.IllegalValueException;
import mightypork.util.files.ion.IonBundle;
import mightypork.util.files.ion.IonBundled;
import mightypork.util.math.Calc;
public class EntityHealthModule implements EntityModule {
public EntityHealthModule(Entity entity) {
this.entity = entity;
}
private final Entity entity;
protected int health = 1;
protected int maxHealth = 1;
protected boolean dead = false;
@Override
public void load(IonBundle bundle) throws IOException
{
health = bundle.get("health", health);
maxHealth = bundle.get("max_health", maxHealth);
dead = bundle.get("dead", dead);
}
@Override
public void save(IonBundle bundle) throws IOException
{
bundle.put("health", health);
bundle.put("max_health", maxHealth);
bundle.put("dead", dead);
}
@Override
public void update(double delta)
{
}
public int getHealth()
{
return health;
}
public void setHealth(int health)
{
this.health = Calc.clamp(health, 0, maxHealth);
if (health <= 0) {
setDead(true);
entity.onKilled();
}
}
public int getMaxHealth()
{
return maxHealth;
}
public void setMaxHealth(int maxHealth)
{
if (maxHealth <= 0) throw new IllegalValueException("Max health out of allowed range: " + maxHealth);
this.maxHealth = maxHealth;
}
public boolean isDead()
{
return dead;
}
public void setDead(boolean dead)
{
this.dead = dead;
}
}

@ -0,0 +1,10 @@
package mightypork.rogue.world.entity.modules;
import mightypork.util.files.ion.IonBundled;
import mightypork.util.timing.Updateable;
public interface EntityModule extends IonBundled, Updateable {
}

@ -1,9 +1,10 @@
package mightypork.rogue.world.entity.models;
package mightypork.rogue.world.entity.modules;
import mightypork.rogue.world.entity.Entity;
public interface EntityMoveListener {
/**

@ -1,13 +1,15 @@
package mightypork.rogue.world.entity;
package mightypork.rogue.world.entity.modules;
import java.io.IOException;
import mightypork.rogue.world.Coord;
import mightypork.rogue.world.entity.PathStep;
import mightypork.util.files.ion.IonBundle;
import mightypork.util.files.ion.IonBundled;
import mightypork.util.math.Easing;
import mightypork.util.math.constraints.vect.Vect;
import mightypork.util.math.constraints.vect.VectConst;
import mightypork.util.math.constraints.vect.mutable.VectAnimated;
import mightypork.util.timing.Updateable;
@ -175,5 +177,10 @@ public class EntityPos implements IonBundled, Updateable {
} else if (!coord.equals(other.coord)) return false;
return true;
}
public VectConst getVisualPos()
{
return Vect.make(walkOffset.x()+coord.x, walkOffset.y()+coord.y);
}
}

@ -0,0 +1,199 @@
package mightypork.rogue.world.entity.modules;
import java.io.IOException;
import java.util.*;
import mightypork.rogue.world.Coord;
import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.entity.PathStep;
import mightypork.rogue.world.pathfinding.PathFinder;
import mightypork.rogue.world.pathfinding.PathFindingContext;
import mightypork.util.files.ion.IonBundle;
import mightypork.util.math.constraints.vect.VectConst;
public class EntityPosModule implements EntityModule {
private final Entity entity;
/** Last pos, will be freed upon finishing move */
private final Coord lastPos = new Coord(0, 0);
private boolean walking = false;
private final Queue<PathStep> path = new LinkedList<>();
private final EntityPos entityPos = new EntityPos();
private double stepTime = 0.5;
// marks for simple renderers
public int lastXDir = 1;
public int lastYDir = 1;
private final Set<EntityMoveListener> moveListeners = new LinkedHashSet<>();
public EntityPosModule(Entity entity) {
this.entity = entity;
}
@Override
public void save(IonBundle bundle) throws IOException
{
bundle.putSequence("path", path);
bundle.putBundled("pos", entityPos);
bundle.put("step_time", stepTime);
}
@Override
public void load(IonBundle bundle) throws IOException
{
bundle.loadSequence("path", path);
bundle.loadBundled("pos", entityPos);
stepTime = bundle.get("step_time", stepTime);
}
public void setPosition(Coord coord)
{
entityPos.setTo(coord);
lastPos.setTo(coord);
cancelPath(); // discard remaining steps
}
/**
* @param delta delta time
*/
@Override
public void update(double delta)
{
if (!entityPos.isFinished()) {
entityPos.update(delta);
}
if (walking && entityPos.isFinished()) {
walking = false;
entity.getLevel().freeTile(lastPos);
for (final EntityMoveListener l : moveListeners) {
l.onStepFinished(entity);
}
if (path.isEmpty()) {
for (final EntityMoveListener l : moveListeners) {
l.onPathFinished(entity);
}
}
}
if (!walking && !path.isEmpty()) {
walking = true;
final PathStep step = path.poll();
final Coord planned = entityPos.getCoord().add(step.toCoord());
if (!entity.getLevel().isWalkable(planned)) {
cancelPath();
for (final EntityMoveListener l : moveListeners) {
l.onPathInterrupted(entity);
}
walking = false;
} else {
// tmp for renderer
if (step.x != 0) this.lastXDir = step.x;
if (step.y != 0) this.lastYDir = step.y;
lastPos.setTo(entityPos.getCoord());
entityPos.walk(step, stepTime);
entity.getLevel().occupyTile(planned);
}
}
}
public boolean isPathFinished()
{
return entityPos.isFinished() && path.isEmpty();
}
public void addStep(PathStep step)
{
path.add(step);
}
public void cancelPath()
{
path.clear();
}
public boolean navigateTo(Coord target)
{
if (target.equals(getCoord())) return true;
PathFindingContext pfc = entity.getPathfindingContext();
final List<PathStep> path = PathFinder.findPathRelative(pfc, entityPos.getCoord(), target);
if (path == null) return false;
this.cancelPath();
this.addSteps(path);
return true;
}
/**
* Add a move listener. If already present, do nothing.
*/
public void addMoveListener(EntityMoveListener listener)
{
moveListeners.add(listener);
}
public void addSteps(List<PathStep> path)
{
path.addAll(path);
}
public Coord getCoord()
{
return entityPos.getCoord();
}
public double getStepTime()
{
return stepTime;
}
public void setStepTime(double stepTime)
{
this.stepTime = stepTime;
}
public double getProgress()
{
return entityPos.getProgress();
}
public VectConst getVisualPos()
{
return entityPos.getVisualPos();
}
}

@ -7,9 +7,14 @@ import mightypork.rogue.world.level.render.MapRenderContext;
public abstract class EntityRenderer {
public static final EntityRenderer NONE = new NullEntityRenderer();
protected final Entity entity;
public abstract void render(Entity entity, MapRenderContext context);
public EntityRenderer(Entity entity) {
this.entity = entity;
}
public abstract void render(MapRenderContext context);
}

@ -1,16 +0,0 @@
package mightypork.rogue.world.entity.renderers;
import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.level.render.MapRenderContext;
public class NullEntityRenderer extends EntityRenderer {
@Override
public void render(Entity entity, MapRenderContext context)
{
// hell no
}
}

@ -6,36 +6,32 @@ import mightypork.gamecore.render.textures.TxQuad;
import mightypork.gamecore.render.textures.TxSheet;
import mightypork.rogue.Res;
import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.entity.EntityPos;
import mightypork.rogue.world.entity.modules.EntityPos;
import mightypork.rogue.world.level.render.MapRenderContext;
import mightypork.util.math.Calc;
import mightypork.util.math.constraints.rect.Rect;
public class PlayerRenderer extends EntityRenderer {
public class SimpleLeftRightMobRenderer extends EntityRenderer {
TxSheet sheet;
private final TxSheet sheet;
public PlayerRenderer(String sheetKey)
{
public SimpleLeftRightMobRenderer(Entity entity, String sheetKey) {
super(entity);
this.sheet = Res.getTxSheet(sheetKey);
}
@Override
public void render(Entity entity, MapRenderContext context)
public void render(MapRenderContext context)
{
TxQuad q = sheet.getQuad(Calc.frag(entity.getPosition().getProgress()));
if (entity.renderData.lastXDir == -1) q = q.flipX();
TxQuad q = sheet.getQuad(Calc.frag(entity.pos.getProgress()));
final EntityPos pos = entity.getPosition();
if (entity.pos.lastXDir == -1) q = q.flipX();
final Rect tileRect = context.getRectForTile(pos.getCoord());
final Rect tileRect = context.getRectForTile(entity.pos.getCoord());
final double w = tileRect.width().value();
Rect spriteRect = tileRect.move(pos.visualXOffset() * w, pos.visualYOffset() * w);
Rect spriteRect = tileRect.move(entity.pos.getVisualPos().mul(w));
spriteRect = spriteRect.shrink(w * 0.1);
Render.quadTextured(spriteRect, q);

@ -11,6 +11,7 @@ import java.util.Set;
import mightypork.rogue.world.Coord;
import mightypork.rogue.world.Sides;
import mightypork.rogue.world.World;
import mightypork.rogue.world.entity.Entities;
import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.pathfinding.FillContext;
import mightypork.rogue.world.pathfinding.FloodFill;
@ -146,31 +147,33 @@ public class Level implements MapAccess, IonBinary {
@Override
public void load(IonInput in) throws IOException
{
// metadata
// -- metadata --
final IonBundle ib = in.readBundle();
seed = ib.get("seed", 0L);
ib.loadBundled("size", size);
ib.loadBundled("enter_point", enterPoint);
ib.loadSequence("entities", entitySet);
for (final Entity ent : entitySet) {
ent.setLevel(this);
entityMap.put(ent.getEntityId(), ent);
}
// init array of size
buildArray();
// -- binary data --
// load tiles
buildArray();
for (final Coord c = Coord.zero(); c.x < size.x; c.x++) {
for (c.y = 0; c.y < size.y; c.y++) {
setTile(c, Tiles.loadTile(in));
}
}
// mark tiles as occupied
for (final Entity e : entitySet) {
occupyTile(e.getPosition().getCoord());
// load entities
Entities.loadEntities(in, entitySet);
// prepare entities
for (final Entity ent : entitySet) {
ent.setLevel(this);
occupyTile(ent.pos.getCoord());
entityMap.put(ent.getEntityId(), ent);
}
}
@ -178,21 +181,23 @@ public class Level implements MapAccess, IonBinary {
@Override
public void save(IonOutput out) throws IOException
{
// metadata
// -- metadata --
final IonBundle ib = new IonBundle();
ib.put("seed", seed);
ib.putBundled("size", size);
ib.putBundled("enter_point", enterPoint);
ib.putSequence("entities", entitySet);
out.writeBundle(ib);
// tiles (writing this way to save space)
// -- binary data --
// tiles
for (final Coord c = Coord.zero(); c.x < size.x; c.x++) {
for (c.y = 0; c.y < size.y; c.y++) {
Tiles.saveTile(out, getTile(c));
}
}
Entities.saveEntities(out, entitySet);
}
@ -264,7 +269,7 @@ public class Level implements MapAccess, IonBinary {
}
public boolean canWalkInto(Coord pos)
public boolean isWalkable(Coord pos)
{
final Tile t = getTile(pos);
@ -362,8 +367,10 @@ public class Level implements MapAccess, IonBinary {
return !t.isNull();
}
@Override
public boolean forceSpreadStart() {
public boolean forceSpreadStart()
{
return true;
}
};

@ -35,7 +35,7 @@ public final class TileModel {
public Tile createTile()
{
try {
return tileClass.getConstructor(int.class, TileRenderer.class).newInstance(id, renderer);
return tileClass.getConstructor(TileModel.class, TileRenderer.class).newInstance(this, renderer);
} catch (Exception e) {
throw new RuntimeException("Could not instantiate a tile.", e);
}

@ -0,0 +1,29 @@
package mightypork.util.error;
public class IllegalValueException extends RuntimeException {
public IllegalValueException() {
}
public IllegalValueException(String message) {
super(message);
}
public IllegalValueException(Throwable cause) {
super(cause);
}
public IllegalValueException(String message, Throwable cause) {
super(message, cause);
}
public IllegalValueException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

@ -1,29 +0,0 @@
package mightypork.util.error;
public class YouFuckedUpException extends RuntimeException {
public YouFuckedUpException()
{
super();
}
public YouFuckedUpException(String message, Throwable cause)
{
super(message, cause);
}
public YouFuckedUpException(String message)
{
super(message);
}
public YouFuckedUpException(Throwable cause)
{
super(cause);
}
}

@ -209,10 +209,10 @@ public class IonOutput {
public <T> void writeSequence(Collection<T> sequence) throws IOException
{
for (final T element : sequence) {
writeMark(Ion.ENTRY);
startEntry();
writeObject(element);
}
writeMark(Ion.END);
endSequence();
}
@ -223,12 +223,24 @@ public class IonOutput {
continue;
}
writeMark(Ion.ENTRY);
startEntry();
writeObject(e.getKey());
writeObject(e.getValue());
}
endSequence();
}
public void endSequence() throws IOException
{
writeMark(Ion.END);
}
public void startEntry() throws IOException
{
writeMark(Ion.ENTRY);
}
private void writeMark(int mark) throws IOException

Loading…
Cancel
Save