entity & world rewrite. rendering broken atm.

v5stable
ondra 11 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. 78
      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. 9
      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_bouncyboxes.ScreenTestBouncy;
import mightypork.rogue.screens.test_cat_sound.ScreenTestCat; import mightypork.rogue.screens.test_cat_sound.ScreenTestCat;
import mightypork.rogue.screens.test_render.ScreenTestRender; import mightypork.rogue.screens.test_render.ScreenTestRender;
import mightypork.rogue.world.WorldProvider;
import mightypork.rogue.world.entity.Entity; import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.item.Item; import mightypork.rogue.world.item.Item;
import mightypork.rogue.world.level.Level; import mightypork.rogue.world.level.Level;
@ -73,6 +74,9 @@ public final class App extends BaseApp {
@Override @Override
protected void initScreens(ScreenRegistry screens) protected void initScreens(ScreenRegistry screens)
{ {
// world provider instance is referenced by screens
WorldProvider.init(this);
screens.addScreen(new ScreenTestBouncy(this)); screens.addScreen(new ScreenTestBouncy(this));
screens.addScreen(new ScreenTestCat(this)); screens.addScreen(new ScreenTestCat(this));
screens.addScreen(new ScreenTestRender(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(Item.ION_MARK, Item.class);
Ion.registerBinary(Level.ION_MARK, Level.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 // sprites
texture = textures.loadTexture("mob", "/res/img/dudes-b.png", FilterMode.NEAREST, WrapMode.CLAMP); texture = textures.loadTexture("mob", "/res/img/dudes-b.png", FilterMode.NEAREST, WrapMode.CLAMP);
tiles = texture.grid(8, 8); 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 // small sheet
texture = textures.loadTexture("tiles16", "/res/img/tiles16.png", FilterMode.NEAREST, WrapMode.CLAMP); 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 class HudLayer extends ScreenLayer {
public HudLayer(Screen screen, World world) public HudLayer(Screen screen)
{ {
super(screen); super(screen);
@ -52,7 +52,7 @@ public class HudLayer extends ScreenLayer {
root.add(experience); 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))); mm.setRect(root.shrink(root.width().perc(5), root.height().perc(15)));
root.add(mm); root.add(mm);

@ -10,64 +10,40 @@ import mightypork.gamecore.gui.screens.LayeredScreen;
import mightypork.rogue.Paths; import mightypork.rogue.Paths;
import mightypork.rogue.world.World; import mightypork.rogue.world.World;
import mightypork.rogue.world.WorldCreator; import mightypork.rogue.world.WorldCreator;
import mightypork.rogue.world.WorldProvider;
import mightypork.util.files.ion.Ion; import mightypork.util.files.ion.Ion;
public class ScreenGame extends LayeredScreen { public class ScreenGame extends LayeredScreen {
private final World world;
public ScreenGame(AppAccess app) public ScreenGame(AppAccess app)
{ {
super(app); super(app);
this.world = obtainWorld(); addLayer(new HudLayer(this));
addLayer(new WorldLayer(this));
addLayer(new HudLayer(this, world));
addLayer(new WorldLayer(this, world));
} }
private World obtainWorld() @Override
public String getName()
{ {
return "game_screen";
// 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;
} }
@Override @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; private final MapView worldView;
public WorldLayer(Screen screen, World world) public WorldLayer(Screen screen)
{ {
super(screen); super(screen);
// render component // render component
worldView = new MapView(world); worldView = new MapView();
// map input plugins // map input plugins
worldView.addPlugin(new MIPKeyWalk()); worldView.addPlugin(new MIPKeyWalk());

@ -13,9 +13,10 @@ import mightypork.gamecore.input.Keys;
import mightypork.rogue.world.Coord; import mightypork.rogue.world.Coord;
import mightypork.rogue.world.PlayerControl; import mightypork.rogue.world.PlayerControl;
import mightypork.rogue.world.World; import mightypork.rogue.world.World;
import mightypork.rogue.world.WorldProvider;
import mightypork.rogue.world.WorldRenderer; import mightypork.rogue.world.WorldRenderer;
import mightypork.rogue.world.entity.Entity; 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.Easing;
import mightypork.util.math.constraints.num.Num; import mightypork.util.math.constraints.num.Num;
import mightypork.util.math.constraints.num.mutable.NumAnimated; 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 { public class MapView extends InputComponent implements KeyListener, MouseButtonListener, EntityMoveListener, Updateable {
protected final WorldRenderer worldRenderer; protected final WorldRenderer worldRenderer;
protected final World world;
private final PlayerControl pc; private final PlayerControl pc;
private final Set<MapInteractionPlugin> plugins = new HashSet<>(); private final Set<MapInteractionPlugin> plugins = new HashSet<>();
private Num tileSize;
private final NumAnimated zoom = new NumAnimated(0, Easing.SINE_BOTH); 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.tileSize = height().min(width()).div(8).max(32).mul(Num.make(1).sub(zoom.mul(0.66)));
this.worldRenderer = new WorldRenderer(world, this, tileSize); this.worldRenderer = new WorldRenderer(this, tileSize);
pc = world.getPlayerControl(); pc = WorldProvider.get().getPlayerControl();
pc.addMoveListener(this); 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 * Get tile coord at a screen position
* *

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

@ -1,90 +1,102 @@
package mightypork.rogue.world; package mightypork.rogue.world;
import java.util.HashSet;
import java.util.Set;
import mightypork.rogue.world.entity.Entity; import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.entity.EntityPos;
import mightypork.rogue.world.entity.PathStep; 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; import mightypork.rogue.world.level.Level;
public class PlayerControl { public abstract class PlayerControl {
protected Set<EntityMoveListener> playerMoveListeners = new HashSet<>();
private World lastWorld;
private final World world;
protected abstract World getWorld();
public PlayerControl(World w)
private World getWorld2()
{ {
this.world = w; World newWorld = getWorld();
}
if (newWorld != lastWorld) {
for (EntityMoveListener eml : playerMoveListeners) {
newWorld.getPlayerEntity().pos.addMoveListener(eml);
}
}
lastWorld = newWorld;
return newWorld;
};
public Entity getEntity() private Entity getPlayerEntity()
{ {
return world.getPlayerEntity(); if(getWorld2() == null) return null;
return getWorld2().getPlayerEntity();
} }
public void goNorth() public void goNorth()
{ {
getEntity().cancelPath(); getPlayerEntity().pos.cancelPath();
getEntity().addStep(PathStep.NORTH); getPlayerEntity().pos.addStep(PathStep.NORTH);
} }
public void goSouth() public void goSouth()
{ {
getEntity().cancelPath(); getPlayerEntity().pos.cancelPath();
getEntity().addStep(PathStep.SOUTH); getPlayerEntity().pos.addStep(PathStep.SOUTH);
} }
public void goEast() public void goEast()
{ {
getEntity().cancelPath(); getPlayerEntity().pos.cancelPath();
getEntity().addStep(PathStep.EAST); getPlayerEntity().pos.addStep(PathStep.EAST);
} }
public void goWest() public void goWest()
{ {
getEntity().cancelPath(); getPlayerEntity().pos.cancelPath();
getEntity().addStep(PathStep.WEST); getPlayerEntity().pos.addStep(PathStep.WEST);
} }
public void navigateTo(Coord pos) public void navigateTo(Coord pos)
{ {
getEntity().navigateTo(pos); getPlayerEntity().pos.navigateTo(pos);
} }
public void addMoveListener(EntityMoveListener eml) public void addMoveListener(EntityMoveListener eml)
{ {
getEntity().addMoveListener(eml); playerMoveListeners.add(eml);
} if(getPlayerEntity() != null) {
getPlayerEntity().pos.addMoveListener(eml);
}
public EntityPos getPos()
{
return getEntity().getPosition();
}
public World getWorld()
{
return world;
} }
public Level getLevel() public Level getLevel()
{ {
return world.getCurrentLevel(); return getWorld2().getCurrentLevel();
} }
public Coord getCoord() 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 final PlayerInfo playerInfo = new PlayerInfo();
private Entity playerEntity; private Entity playerEntity;
private final PlayerControl control = new PlayerControl(this);
/** World seed */ /** World seed */
private long seed; private long seed;
@ -48,6 +46,7 @@ public class World implements IonBundled, Updateable {
in.loadBundled("player", playerInfo); in.loadBundled("player", playerInfo);
playerEntity = levels.get(playerInfo.getLevel()).getEntity(playerInfo.getEID()); 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(); final int playerEid = getNewEID();
playerEntity = Entities.PLAYER.createEntity(playerEid); playerEntity = Entities.PLAYER.createEntity(playerEid);
playerEntity.setPosition(levels.get(level).getEnterPoint()); playerEntity.pos.setPosition(levels.get(level).getEnterPoint());
levels.get(level).addEntity(playerEntity); levels.get(level).addEntity(playerEntity);
playerInfo.setLevel(level); playerInfo.setLevel(level);
@ -118,12 +117,6 @@ public class World implements IonBundled, Updateable {
} }
public PlayerControl getPlayerControl()
{
return control;
}
public Entity getPlayerEntity() public Entity getPlayerEntity()
{ {
return playerEntity; 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.gamecore.render.Render;
import mightypork.rogue.Res; import mightypork.rogue.Res;
import mightypork.rogue.world.entity.Entity; 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.Level;
import mightypork.rogue.world.level.render.TileRenderContext; import mightypork.rogue.world.level.render.TileRenderContext;
import mightypork.util.math.color.RGB; import mightypork.util.math.color.RGB;
@ -22,15 +22,12 @@ import mightypork.util.math.constraints.vect.VectConst;
* *
* @author MightyPork * @author MightyPork
*/ */
public class WorldRenderer extends RectProxy implements Pollable { public class WorldRenderer extends RectProxy {
private static final boolean USE_BATCH_RENDERING = true; private static final boolean USE_BATCH_RENDERING = true;
private final Num tileSize; private final Num tileSize;
private final World world;
private final Entity player;
// can be changed // can be changed
private RectConst mapRect; private RectConst mapRect;
private Level activeLevel; private Level activeLevel;
@ -43,12 +40,9 @@ public class WorldRenderer extends RectProxy implements Pollable {
private TileRenderContext trc; private TileRenderContext trc;
public WorldRenderer(World world, Rect viewport, Num tileSize) { public WorldRenderer(Rect viewport, Num tileSize) {
super(viewport); super(viewport);
this.world = world;
this.player = world.getPlayerEntity();
this.tileSize = tileSize; this.tileSize = tileSize;
final Num grX = width().perc(30); final Num grX = width().perc(30);
@ -58,16 +52,15 @@ public class WorldRenderer extends RectProxy implements Pollable {
final Num grY = height().perc(20); final Num grY = height().perc(20);
topShadow = topEdge().growDown(grY); topShadow = topEdge().growDown(grY);
bottomShadow = bottomEdge().growUp(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; if (activeLevel == level) return;
activeLevel = level; activeLevel = level;
mapRect = Rect.make(0, 0, level.getWidth(), level.getHeight()); mapRect = Rect.make(0, 0, level.getWidth(), level.getHeight());
@ -78,24 +71,25 @@ public class WorldRenderer extends RectProxy implements Pollable {
private VectConst getOffset() private VectConst getOffset()
{ {
final EntityPos pos = player.getPosition(); Entity ent = WorldProvider.get().getPlayerEntity();
final double playerX = pos.visualX(); return Vect.make(ent.pos.getVisualPos().neg().add(0.5, 0.5)).freeze();
final double playerY = pos.visualY();
return Vect.make(-playerX-0.5, -playerY-0.5);
} }
public void render() public void render()
{ {
prepareRenderContextIfNeeded();
Render.pushMatrix(); Render.pushMatrix();
Render.setColor(RGB.WHITE); Render.setColor(RGB.WHITE);
Render.translate(center()); Render.translate(center());
Render.scaleXY(tileSize.value()); Render.scaleXY(tileSize.value());
Render.translate(getOffset()); Render.translate(getOffset());
// tiles to render // 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 w = width().value();
final double h = height().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 xtilesh = (int) (w / (ts * 2)) + 1;
final int ytilesh = (int) (h / (ts * 2)) + 1; final int ytilesh = (int) (h / (ts * 2)) + 1;
final int x1 = pos.x() - xtilesh; final int x1 = pos.x - xtilesh;
final int y1 = pos.y() - ytilesh; final int y1 = pos.y - ytilesh;
final int x2 = pos.x() + xtilesh; final int x2 = pos.x + xtilesh;
final int y2 = pos.y() + ytilesh; final int y2 = pos.y + ytilesh;
// === TILES === // === TILES ===
@ -139,8 +133,9 @@ public class WorldRenderer extends RectProxy implements Pollable {
for (final Entity e : activeLevel.getEntities()) { for (final Entity e : activeLevel.getEntities()) {
// avoid entities out of view rect // avoid entities out of view rect
final int x = (int) Math.round(e.getPosition().visualX()); Vect entPos = e.pos.getVisualPos();
final int y = (int) Math.round(e.getPosition().visualY()); 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 (x < x1 - ts || x > x2 + ts) continue;
if (y < y1 - ts || y > y2 + 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); return new Coord(v.xi() / ts, v.yi() / ts);
} }
@Override
public void poll()
{
rebuildTiles();
}
} }

@ -1,8 +1,12 @@
package mightypork.rogue.world.entity; package mightypork.rogue.world.entity;
import mightypork.rogue.world.entity.models.EntityModel; import java.io.IOException;
import mightypork.rogue.world.entity.models.PlayerModel; 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]; 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) 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; entities[id] = model;
} }
@ -31,8 +39,49 @@ public final class Entities {
{ {
final EntityModel e = entities[id]; 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; 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.io.IOException;
import java.util.ArrayList; import java.util.HashMap;
import java.util.List; 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.World;
import mightypork.rogue.world.entity.models.EntityModel; import mightypork.rogue.world.entity.modules.EntityHealthModule;
import mightypork.rogue.world.entity.models.EntityMoveListener; 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.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.rogue.world.pathfinding.PathFindingContext;
import mightypork.util.files.ion.IonBinary; import mightypork.util.annotations.DefaultImpl;
import mightypork.util.files.ion.IonBundle; import mightypork.util.error.IllegalValueException;
import mightypork.util.files.ion.IonInput; import mightypork.util.files.ion.*;
import mightypork.util.files.ion.IonOutput; import mightypork.util.timing.Updateable;
/** /**
@ -26,135 +24,51 @@ import mightypork.util.files.ion.IonOutput;
* *
* @author MightyPork * @author MightyPork
*/ */
public final class Entity implements IonBinary { public abstract class Entity implements IonBundled, Updateable {
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 Level level;
private EntityModel model; private final EntityModel model;
/** Entity ID */ /** Entity ID */
private int entityId = -1; private int entityId;
/** 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 private final Map<String, EntityModule> modules = new HashMap<>();
public int getCost(Coord from, Coord to)
{
return model.getPathCost(Entity.this, from, to);
}
// default modules
public final EntityPosModule pos = new EntityPosModule(this);
public final EntityHealthModule health = new EntityHealthModule(this);
@Override
public Coord[] getWalkSides() {
return Sides.cardinalSides;
}
};
public Entity(EntityModel model, int eid) {
public Entity(int eid, EntityModel entityModel)
{
this.entityId = eid; 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; // register modules
this.model = entityModel; modules.put("pos", pos);
} modules.put("health", health);
public Level getLevel()
{
if (level == null) throw new IllegalStateException("Entity has no level assigned.");
return level;
}
public World getWorld()
{
return getLevel().getWorld();
} }
@Override @Override
public short getIonMark() public void save(IonBundle bundle) throws IOException
{ {
return ION_MARK; bundle.put("eid", entityId);
} for (Entry<String, EntityModule> entry : modules.entrySet()) {
bundle.putBundled(entry.getKey(), entry.getValue());
}
@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 @Override
public void load(IonInput in) throws IOException public void load(IonBundle bundle) throws IOException
{ {
final IonBundle bundle = in.readBundle(); entityId = bundle.get("eid", -1);
modelId = bundle.get("model", 0); if (entityId < 0) throw new IllegalValueException("Bad entity id: " + entityId);
entityId = bundle.get("id", entityId);
bundle.loadBundled("data", data);
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() public void setLevel(Level level)
{
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)
{ {
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; return getLevel().getWorld();
final List<PathStep> path = PathFinder.findPathRelative(pfc, getPosition().getCoord(), pos);
if (path == null) return false;
this.cancelPath();
this.addSteps(path);
return true;
} }
public void addMoveListener(EntityMoveListener listener) public EntityModel getModel()
{ {
moveListeners.add(listener); return model;
} }
public void addSteps(List<PathStep> path) public abstract PathFindingContext getPathfindingContext();
{
data.path.addAll(path);
}
public void setLevel(Level level) public abstract void render(MapRenderContext context);
{
this.level = level;
model.onEnteredLevel(this);
}
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; import mightypork.rogue.world.entity.Entity;
public interface EntityMoveListener { public interface EntityMoveListener {
/** /**

@ -1,13 +1,15 @@
package mightypork.rogue.world.entity; package mightypork.rogue.world.entity.modules;
import java.io.IOException; import java.io.IOException;
import mightypork.rogue.world.Coord; import mightypork.rogue.world.Coord;
import mightypork.rogue.world.entity.PathStep;
import mightypork.util.files.ion.IonBundle; import mightypork.util.files.ion.IonBundle;
import mightypork.util.files.ion.IonBundled; import mightypork.util.files.ion.IonBundled;
import mightypork.util.math.Easing; import mightypork.util.math.Easing;
import mightypork.util.math.constraints.vect.Vect; 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.math.constraints.vect.mutable.VectAnimated;
import mightypork.util.timing.Updateable; import mightypork.util.timing.Updateable;
@ -176,4 +178,9 @@ public class EntityPos implements IonBundled, Updateable {
return true; 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 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.gamecore.render.textures.TxSheet;
import mightypork.rogue.Res; import mightypork.rogue.Res;
import mightypork.rogue.world.entity.Entity; 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.rogue.world.level.render.MapRenderContext;
import mightypork.util.math.Calc; import mightypork.util.math.Calc;
import mightypork.util.math.constraints.rect.Rect; import mightypork.util.math.constraints.rect.Rect;
public class PlayerRenderer extends EntityRenderer { public class SimpleLeftRightMobRenderer extends EntityRenderer {
TxSheet sheet; private final TxSheet sheet;
public SimpleLeftRightMobRenderer(Entity entity, String sheetKey) {
public PlayerRenderer(String sheetKey) super(entity);
{
this.sheet = Res.getTxSheet(sheetKey); this.sheet = Res.getTxSheet(sheetKey);
} }
@Override @Override
public void render(Entity entity, MapRenderContext context) public void render(MapRenderContext context)
{ {
TxQuad q = sheet.getQuad(Calc.frag(entity.getPosition().getProgress())); TxQuad q = sheet.getQuad(Calc.frag(entity.pos.getProgress()));
if (entity.renderData.lastXDir == -1) q = q.flipX();
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(); 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); spriteRect = spriteRect.shrink(w * 0.1);
Render.quadTextured(spriteRect, q); Render.quadTextured(spriteRect, q);

@ -11,6 +11,7 @@ import java.util.Set;
import mightypork.rogue.world.Coord; import mightypork.rogue.world.Coord;
import mightypork.rogue.world.Sides; import mightypork.rogue.world.Sides;
import mightypork.rogue.world.World; import mightypork.rogue.world.World;
import mightypork.rogue.world.entity.Entities;
import mightypork.rogue.world.entity.Entity; import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.pathfinding.FillContext; import mightypork.rogue.world.pathfinding.FillContext;
import mightypork.rogue.world.pathfinding.FloodFill; import mightypork.rogue.world.pathfinding.FloodFill;
@ -146,31 +147,33 @@ public class Level implements MapAccess, IonBinary {
@Override @Override
public void load(IonInput in) throws IOException public void load(IonInput in) throws IOException
{ {
// metadata // -- metadata --
final IonBundle ib = in.readBundle(); final IonBundle ib = in.readBundle();
seed = ib.get("seed", 0L); seed = ib.get("seed", 0L);
ib.loadBundled("size", size); ib.loadBundled("size", size);
ib.loadBundled("enter_point", enterPoint); 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 // -- binary data --
buildArray();
// load tiles // load tiles
buildArray();
for (final Coord c = Coord.zero(); c.x < size.x; c.x++) { for (final Coord c = Coord.zero(); c.x < size.x; c.x++) {
for (c.y = 0; c.y < size.y; c.y++) { for (c.y = 0; c.y < size.y; c.y++) {
setTile(c, Tiles.loadTile(in)); setTile(c, Tiles.loadTile(in));
} }
} }
// mark tiles as occupied
for (final Entity e : entitySet) { // load entities
occupyTile(e.getPosition().getCoord()); 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 @Override
public void save(IonOutput out) throws IOException public void save(IonOutput out) throws IOException
{ {
// metadata // -- metadata --
final IonBundle ib = new IonBundle(); final IonBundle ib = new IonBundle();
ib.put("seed", seed); ib.put("seed", seed);
ib.putBundled("size", size); ib.putBundled("size", size);
ib.putBundled("enter_point", enterPoint); ib.putBundled("enter_point", enterPoint);
ib.putSequence("entities", entitySet);
out.writeBundle(ib); 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 (final Coord c = Coord.zero(); c.x < size.x; c.x++) {
for (c.y = 0; c.y < size.y; c.y++) { for (c.y = 0; c.y < size.y; c.y++) {
Tiles.saveTile(out, getTile(c)); 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); final Tile t = getTile(pos);
@ -362,8 +367,10 @@ public class Level implements MapAccess, IonBinary {
return !t.isNull(); return !t.isNull();
} }
@Override @Override
public boolean forceSpreadStart() { public boolean forceSpreadStart()
{
return true; return true;
} }
}; };

@ -35,7 +35,7 @@ public final class TileModel {
public Tile createTile() public Tile createTile()
{ {
try { try {
return tileClass.getConstructor(int.class, TileRenderer.class).newInstance(id, renderer); return tileClass.getConstructor(TileModel.class, TileRenderer.class).newInstance(this, renderer);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("Could not instantiate a tile.", 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 public <T> void writeSequence(Collection<T> sequence) throws IOException
{ {
for (final T element : sequence) { for (final T element : sequence) {
writeMark(Ion.ENTRY); startEntry();
writeObject(element); writeObject(element);
} }
writeMark(Ion.END); endSequence();
} }
@ -223,14 +223,26 @@ public class IonOutput {
continue; continue;
} }
writeMark(Ion.ENTRY); startEntry();
writeObject(e.getKey()); writeObject(e.getKey());
writeObject(e.getValue()); writeObject(e.getValue());
} }
endSequence();
}
public void endSequence() throws IOException
{
writeMark(Ion.END); writeMark(Ion.END);
} }
public void startEntry() throws IOException
{
writeMark(Ion.ENTRY);
}
private void writeMark(int mark) throws IOException private void writeMark(int mark) throws IOException
{ {
writeIntByte(mark); writeIntByte(mark);

Loading…
Cancel
Save