diff --git a/src/mightypork/rogue/App.java b/src/mightypork/rogue/App.java index 05d778a..1f1ec40 100644 --- a/src/mightypork/rogue/App.java +++ b/src/mightypork/rogue/App.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); } diff --git a/src/mightypork/rogue/Res.java b/src/mightypork/rogue/Res.java index 93078f4..1538316 100644 --- a/src/mightypork/rogue/Res.java +++ b/src/mightypork/rogue/Res.java @@ -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); diff --git a/src/mightypork/rogue/screens/gamescreen/HudLayer.java b/src/mightypork/rogue/screens/gamescreen/HudLayer.java index f2f418a..550dbce 100644 --- a/src/mightypork/rogue/screens/gamescreen/HudLayer.java +++ b/src/mightypork/rogue/screens/gamescreen/HudLayer.java @@ -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); diff --git a/src/mightypork/rogue/screens/gamescreen/ScreenGame.java b/src/mightypork/rogue/screens/gamescreen/ScreenGame.java index 513503b..2ad6b91 100644 --- a/src/mightypork/rogue/screens/gamescreen/ScreenGame.java +++ b/src/mightypork/rogue/screens/gamescreen/ScreenGame.java @@ -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); + } } diff --git a/src/mightypork/rogue/screens/gamescreen/WorldLayer.java b/src/mightypork/rogue/screens/gamescreen/WorldLayer.java index 9f82606..3b6c463 100644 --- a/src/mightypork/rogue/screens/gamescreen/WorldLayer.java +++ b/src/mightypork/rogue/screens/gamescreen/WorldLayer.java @@ -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()); diff --git a/src/mightypork/rogue/screens/gamescreen/world/MapView.java b/src/mightypork/rogue/screens/gamescreen/world/MapView.java index 112abff..4153f1f 100644 --- a/src/mightypork/rogue/screens/gamescreen/world/MapView.java +++ b/src/mightypork/rogue/screens/gamescreen/world/MapView.java @@ -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 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 * diff --git a/src/mightypork/rogue/screens/gamescreen/world/Minimap.java b/src/mightypork/rogue/screens/gamescreen/world/Minimap.java index 0211c07..daf27de 100644 --- a/src/mightypork/rogue/screens/gamescreen/world/Minimap.java +++ b/src/mightypork/rogue/screens/gamescreen/world/Minimap.java @@ -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); } } diff --git a/src/mightypork/rogue/world/PlayerControl.java b/src/mightypork/rogue/world/PlayerControl.java index ba329f0..49f597e 100644 --- a/src/mightypork/rogue/world/PlayerControl.java +++ b/src/mightypork/rogue/world/PlayerControl.java @@ -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 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(); } } diff --git a/src/mightypork/rogue/world/World.java b/src/mightypork/rogue/world/World.java index 14781e1..871aa33 100644 --- a/src/mightypork/rogue/world/World.java +++ b/src/mightypork/rogue/world/World.java @@ -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; diff --git a/src/mightypork/rogue/world/WorldProvider.java b/src/mightypork/rogue/world/WorldProvider.java new file mode 100644 index 0000000..e75a4f3 --- /dev/null +++ b/src/mightypork/rogue/world/WorldProvider.java @@ -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() + { + } + +} diff --git a/src/mightypork/rogue/world/WorldRenderer.java b/src/mightypork/rogue/world/WorldRenderer.java index 016ddb3..662e080 100644 --- a/src/mightypork/rogue/world/WorldRenderer.java +++ b/src/mightypork/rogue/world/WorldRenderer.java @@ -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(); - } - } diff --git a/src/mightypork/rogue/world/entity/Entities.java b/src/mightypork/rogue/world/entity/Entities.java index b14cbcd..ca8a09a 100644 --- a/src/mightypork/rogue/world/entity/Entities.java +++ b/src/mightypork/rogue/world/entity/Entities.java @@ -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 entities) throws IOException + { + entities.clear(); + while (in.hasNextEntry()) { + entities.add(loadEntity(in)); + } + } + + + public static void saveEntities(IonOutput out, Collection 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); + } } diff --git a/src/mightypork/rogue/world/entity/Entity.java b/src/mightypork/rogue/world/entity/Entity.java index fa31ab8..ea98c54 100644 --- a/src/mightypork/rogue/world/entity/Entity.java +++ b/src/mightypork/rogue/world/entity/Entity.java @@ -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 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 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 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 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 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 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 entry : modules.entrySet()) { + entry.getValue().update(delta); + } } - public EntityModel getModel() + @DefaultImpl + public void onKilled() { - return model; } } diff --git a/src/mightypork/rogue/world/entity/EntityData.java b/src/mightypork/rogue/world/entity/EntityData.java deleted file mode 100644 index e3f0f85..0000000 --- a/src/mightypork/rogue/world/entity/EntityData.java +++ /dev/null @@ -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 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); - } - -} diff --git a/src/mightypork/rogue/world/entity/EntityModel.java b/src/mightypork/rogue/world/entity/EntityModel.java new file mode 100644 index 0000000..a8e93e9 --- /dev/null +++ b/src/mightypork/rogue/world/entity/EntityModel.java @@ -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 tileClass; + + + public EntityModel(int id, Class 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); + } +} diff --git a/src/mightypork/rogue/world/entity/EntityPathfindingContext.java b/src/mightypork/rogue/world/entity/EntityPathfindingContext.java new file mode 100644 index 0000000..54ff57f --- /dev/null +++ b/src/mightypork/rogue/world/entity/EntityPathfindingContext.java @@ -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; + } + +} diff --git a/src/mightypork/rogue/world/entity/EntityRenderData.java b/src/mightypork/rogue/world/entity/EntityRenderData.java deleted file mode 100644 index 31838b6..0000000 --- a/src/mightypork/rogue/world/entity/EntityRenderData.java +++ /dev/null @@ -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; - -} diff --git a/src/mightypork/rogue/world/entity/SimpleEntityPathFindingContext.java b/src/mightypork/rogue/world/entity/SimpleEntityPathFindingContext.java new file mode 100644 index 0000000..d5fba1e --- /dev/null +++ b/src/mightypork/rogue/world/entity/SimpleEntityPathFindingContext.java @@ -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; + } + + +} diff --git a/src/mightypork/rogue/world/entity/entities/PlayerEntity.java b/src/mightypork/rogue/world/entity/entities/PlayerEntity.java new file mode 100644 index 0000000..597c6f1 --- /dev/null +++ b/src/mightypork/rogue/world/entity/entities/PlayerEntity.java @@ -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); + } +} diff --git a/src/mightypork/rogue/world/entity/models/EntityModel.java b/src/mightypork/rogue/world/entity/models/EntityModel.java deleted file mode 100644 index 641f573..0000000 --- a/src/mightypork/rogue/world/entity/models/EntityModel.java +++ /dev/null @@ -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) - { - } - -} diff --git a/src/mightypork/rogue/world/entity/models/PlayerModel.java b/src/mightypork/rogue/world/entity/models/PlayerModel.java deleted file mode 100644 index 5f2b641..0000000 --- a/src/mightypork/rogue/world/entity/models/PlayerModel.java +++ /dev/null @@ -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); - } -} diff --git a/src/mightypork/rogue/world/entity/modules/EntityHealthModule.java b/src/mightypork/rogue/world/entity/modules/EntityHealthModule.java new file mode 100644 index 0000000..ee79f16 --- /dev/null +++ b/src/mightypork/rogue/world/entity/modules/EntityHealthModule.java @@ -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; + } + +} diff --git a/src/mightypork/rogue/world/entity/modules/EntityModule.java b/src/mightypork/rogue/world/entity/modules/EntityModule.java new file mode 100644 index 0000000..469e499 --- /dev/null +++ b/src/mightypork/rogue/world/entity/modules/EntityModule.java @@ -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 { + + +} diff --git a/src/mightypork/rogue/world/entity/models/EntityMoveListener.java b/src/mightypork/rogue/world/entity/modules/EntityMoveListener.java similarity index 88% rename from src/mightypork/rogue/world/entity/models/EntityMoveListener.java rename to src/mightypork/rogue/world/entity/modules/EntityMoveListener.java index 7bae342..88527cf 100644 --- a/src/mightypork/rogue/world/entity/models/EntityMoveListener.java +++ b/src/mightypork/rogue/world/entity/modules/EntityMoveListener.java @@ -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 { /** diff --git a/src/mightypork/rogue/world/entity/EntityPos.java b/src/mightypork/rogue/world/entity/modules/EntityPos.java similarity index 91% rename from src/mightypork/rogue/world/entity/EntityPos.java rename to src/mightypork/rogue/world/entity/modules/EntityPos.java index 0203369..b87a954 100644 --- a/src/mightypork/rogue/world/entity/EntityPos.java +++ b/src/mightypork/rogue/world/entity/modules/EntityPos.java @@ -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); + } } diff --git a/src/mightypork/rogue/world/entity/modules/EntityPosModule.java b/src/mightypork/rogue/world/entity/modules/EntityPosModule.java new file mode 100644 index 0000000..4ade947 --- /dev/null +++ b/src/mightypork/rogue/world/entity/modules/EntityPosModule.java @@ -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 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 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 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 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(); + } + +} diff --git a/src/mightypork/rogue/world/entity/renderers/EntityRenderer.java b/src/mightypork/rogue/world/entity/renderers/EntityRenderer.java index ff5f3db..8965b6d 100644 --- a/src/mightypork/rogue/world/entity/renderers/EntityRenderer.java +++ b/src/mightypork/rogue/world/entity/renderers/EntityRenderer.java @@ -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); } diff --git a/src/mightypork/rogue/world/entity/renderers/NullEntityRenderer.java b/src/mightypork/rogue/world/entity/renderers/NullEntityRenderer.java deleted file mode 100644 index 0fe0d97..0000000 --- a/src/mightypork/rogue/world/entity/renderers/NullEntityRenderer.java +++ /dev/null @@ -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 - } - -} diff --git a/src/mightypork/rogue/world/entity/renderers/PlayerRenderer.java b/src/mightypork/rogue/world/entity/renderers/SimpleLeftRightMobRenderer.java similarity index 53% rename from src/mightypork/rogue/world/entity/renderers/PlayerRenderer.java rename to src/mightypork/rogue/world/entity/renderers/SimpleLeftRightMobRenderer.java index 8d3f0a6..755bca2 100644 --- a/src/mightypork/rogue/world/entity/renderers/PlayerRenderer.java +++ b/src/mightypork/rogue/world/entity/renderers/SimpleLeftRightMobRenderer.java @@ -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); diff --git a/src/mightypork/rogue/world/level/Level.java b/src/mightypork/rogue/world/level/Level.java index 7a95fb4..74b43d1 100644 --- a/src/mightypork/rogue/world/level/Level.java +++ b/src/mightypork/rogue/world/level/Level.java @@ -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; } }; diff --git a/src/mightypork/rogue/world/tile/TileModel.java b/src/mightypork/rogue/world/tile/TileModel.java index f87543f..28e0167 100644 --- a/src/mightypork/rogue/world/tile/TileModel.java +++ b/src/mightypork/rogue/world/tile/TileModel.java @@ -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); } diff --git a/src/mightypork/util/error/IllegalValueException.java b/src/mightypork/util/error/IllegalValueException.java new file mode 100644 index 0000000..3c0c3a6 --- /dev/null +++ b/src/mightypork/util/error/IllegalValueException.java @@ -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); + } + +} diff --git a/src/mightypork/util/error/YouFuckedUpException.java b/src/mightypork/util/error/YouFuckedUpException.java deleted file mode 100644 index e5a8b83..0000000 --- a/src/mightypork/util/error/YouFuckedUpException.java +++ /dev/null @@ -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); - } - -} diff --git a/src/mightypork/util/files/ion/IonOutput.java b/src/mightypork/util/files/ion/IonOutput.java index ec1891a..9123731 100644 --- a/src/mightypork/util/files/ion/IonOutput.java +++ b/src/mightypork/util/files/ion/IonOutput.java @@ -209,10 +209,10 @@ public class IonOutput { public void writeSequence(Collection 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