From 8872fd3f5fdccbaa591aa16ffea3aa472ab16bcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 20 Apr 2014 03:45:31 +0200 Subject: [PATCH] Initial world system with tile rendering & ionization support. --- fuck.ion | Bin 0 -> 140 bytes .../gui/components/LayoutComponent.java | 2 + .../gui/components/VisualComponent.java | 1 + src/mightypork/gamecore/render/Render.java | 6 + src/mightypork/rogue/App.java | 14 +- src/mightypork/rogue/Res.java | 12 +- .../rogue/screens/ingame/GameGui.java | 17 +- .../rogue/screens/ingame/ScreenGame.java | 3 +- .../rogue/screens/ingame/WorldLayer.java | 32 +++ .../rogue/screens/ingame/WorldRenderer.java | 68 +++++++ src/mightypork/rogue/world/LocalPlayer.java | 83 ++++++++ src/mightypork/rogue/world/MapGenerator.java | 54 +++++ src/mightypork/rogue/world/MapObserver.java | 22 +++ src/mightypork/rogue/world/World.java | 185 ++++++++++++++++++ src/mightypork/rogue/world/WorldPos.java | 71 +++++++ src/mightypork/rogue/world/item/Item.java | 8 +- .../rogue/world/item/ItemModel.java | 2 +- .../{WorldMap.java => map/LevelMap.java} | 82 +++++++- src/mightypork/rogue/world/map/MapAccess.java | 40 ++++ .../{tile => map}/TileRenderContext.java | 41 ++-- .../rogue/world/structs/ItemStack.java | 19 ++ .../rogue/world/structs/LevelList.java | 18 ++ src/mightypork/rogue/world/tile/Tile.java | 19 +- src/mightypork/rogue/world/tile/TileGrid.java | 13 -- .../rogue/world/tile/TileModel.java | 25 ++- src/mightypork/rogue/world/tile/Tiles.java | 25 ++- .../{NullTile.java => AbstractNullTile.java} | 22 ++- .../rogue/world/tile/models/NullFloor.java | 18 ++ .../rogue/world/tile/models/NullWall.java | 18 ++ .../rogue/world/tile/models/SimpleTile.java | 13 +- src/mightypork/test/testworldtofile.java | 6 + .../util/constraints/rect/Rect.java | 64 ++++++ .../util/constraints/rect/RectConst.java | 16 ++ .../util/error/CorruptedDataException.java | 36 ++++ src/mightypork/util/files/ion/Ion.java | 56 ++++-- src/mightypork/util/files/ion/IonBundle.java | 30 +-- .../util/files/ion/IonConstructor.java | 11 +- .../ion/templates/IonizableArrayList.java | 28 +++ .../files/ion/templates/IonizableHashMap.java | 28 +++ .../files/ion/templates/IonizableHashSet.java | 28 +++ .../ion/templates/IonizableLinkedHashMap.java | 28 +++ .../ion/templates/IonizableLinkedList.java | 28 +++ .../files/ion/templates/IonizableStack.java} | 15 +- .../files/ion/templates/IonizableTreeSet.java | 28 +++ src/mightypork/util/math/color/RGB.java | 2 + 45 files changed, 1205 insertions(+), 132 deletions(-) create mode 100644 fuck.ion create mode 100644 src/mightypork/rogue/screens/ingame/WorldLayer.java create mode 100644 src/mightypork/rogue/screens/ingame/WorldRenderer.java create mode 100644 src/mightypork/rogue/world/LocalPlayer.java create mode 100644 src/mightypork/rogue/world/MapGenerator.java create mode 100644 src/mightypork/rogue/world/MapObserver.java create mode 100644 src/mightypork/rogue/world/World.java create mode 100644 src/mightypork/rogue/world/WorldPos.java rename src/mightypork/rogue/world/{WorldMap.java => map/LevelMap.java} (55%) create mode 100644 src/mightypork/rogue/world/map/MapAccess.java rename src/mightypork/rogue/world/{tile => map}/TileRenderContext.java (50%) create mode 100644 src/mightypork/rogue/world/structs/ItemStack.java create mode 100644 src/mightypork/rogue/world/structs/LevelList.java delete mode 100644 src/mightypork/rogue/world/tile/TileGrid.java rename src/mightypork/rogue/world/tile/models/{NullTile.java => AbstractNullTile.java} (56%) create mode 100644 src/mightypork/rogue/world/tile/models/NullFloor.java create mode 100644 src/mightypork/rogue/world/tile/models/NullWall.java create mode 100644 src/mightypork/test/testworldtofile.java create mode 100644 src/mightypork/util/error/CorruptedDataException.java create mode 100644 src/mightypork/util/files/ion/templates/IonizableArrayList.java create mode 100644 src/mightypork/util/files/ion/templates/IonizableHashMap.java create mode 100644 src/mightypork/util/files/ion/templates/IonizableHashSet.java create mode 100644 src/mightypork/util/files/ion/templates/IonizableLinkedHashMap.java create mode 100644 src/mightypork/util/files/ion/templates/IonizableLinkedList.java rename src/mightypork/{rogue/world/tile/TileItems.java => util/files/ion/templates/IonizableStack.java} (61%) create mode 100644 src/mightypork/util/files/ion/templates/IonizableTreeSet.java diff --git a/fuck.ion b/fuck.ion new file mode 100644 index 0000000000000000000000000000000000000000..44b42b221823e172fc3a7f2bf8e2baef8d7ef705 GIT binary patch literal 140 zcmW-YK?;Ik7zM|)G~Bgn`MdH8Zp8!_qM=53p*lv;Ngd@e@Oa;RKn_H5l2{Ts%SBSj yl!8yzjZ83{aiNo#w%*DTB~Prdu$P2NhkpgkO3V;lVr+LNhSDfVm!i4EHT literal 0 HcmV?d00001 diff --git a/src/mightypork/gamecore/gui/components/LayoutComponent.java b/src/mightypork/gamecore/gui/components/LayoutComponent.java index 31e687e..3c18664 100644 --- a/src/mightypork/gamecore/gui/components/LayoutComponent.java +++ b/src/mightypork/gamecore/gui/components/LayoutComponent.java @@ -13,6 +13,7 @@ import mightypork.util.constraints.rect.proxy.RectBound; import mightypork.util.control.Enableable; import mightypork.util.control.eventbus.EventBus; import mightypork.util.control.eventbus.clients.ClientHub; +import mightypork.util.logging.Log; public abstract class LayoutComponent extends VisualComponent implements Enableable, ClientHub, AppAccess { @@ -131,6 +132,7 @@ public abstract class LayoutComponent extends VisualComponent implements Enablea public final void attach(Component component) { if (component == null) return; + if (component == this) throw new IllegalArgumentException("Uruboros. (infinite recursion evaded)"); components.add(component); addChildClient(component); diff --git a/src/mightypork/gamecore/gui/components/VisualComponent.java b/src/mightypork/gamecore/gui/components/VisualComponent.java index 2956012..4f81b12 100644 --- a/src/mightypork/gamecore/gui/components/VisualComponent.java +++ b/src/mightypork/gamecore/gui/components/VisualComponent.java @@ -77,6 +77,7 @@ public abstract class VisualComponent extends AbstractRectCache implements Compo @Override public final void onLayoutChanged() { + if (source == null) throw new NullPointerException("Component is missing a bounding rect."); poll(); } diff --git a/src/mightypork/gamecore/render/Render.java b/src/mightypork/gamecore/render/Render.java index efd37c3..7b8f9c0 100644 --- a/src/mightypork/gamecore/render/Render.java +++ b/src/mightypork/gamecore/render/Render.java @@ -434,6 +434,12 @@ public class Render { } + public static void quadColor(Rect quad, Color color) + { + quadColor(quad, color, color, color, color); + } + + /** * Draw quad with coloured vertices. * diff --git a/src/mightypork/rogue/App.java b/src/mightypork/rogue/App.java index 7d992ff..8a9c040 100644 --- a/src/mightypork/rogue/App.java +++ b/src/mightypork/rogue/App.java @@ -22,10 +22,12 @@ 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.WorldMap; +import mightypork.rogue.world.LocalPlayer; +import mightypork.rogue.world.World; +import mightypork.rogue.world.WorldPos; import mightypork.rogue.world.item.Item; +import mightypork.rogue.world.map.LevelMap; import mightypork.rogue.world.tile.Tile; -import mightypork.rogue.world.tile.TileItems; import mightypork.util.control.eventbus.EventBus; import mightypork.util.control.eventbus.events.Event; import mightypork.util.files.ion.Ion; @@ -107,10 +109,12 @@ public final class App extends BaseApp { @Override protected void preInit() { - Ion.registerIonizable(Tile.ION_MARK, Tile.class); Ion.registerIonizable(Item.ION_MARK, Item.class); - Ion.registerIonizable(WorldMap.ION_MARK, WorldMap.class); - Ion.registerIonizable(TileItems.ION_MARK, TileItems.class); // used by tile to store contained items + Ion.registerIonizable(LevelMap.ION_MARK, LevelMap.class); + Ion.registerIonizable(LocalPlayer.ION_MARK, LocalPlayer.class); + Ion.registerIonizable(Tile.ION_MARK, Tile.class); + Ion.registerIonizable(World.ION_MARK, World.class); + Ion.registerIonizable(WorldPos.ION_MARK, WorldPos.class); } diff --git a/src/mightypork/rogue/Res.java b/src/mightypork/rogue/Res.java index ad42951..8bc19c5 100644 --- a/src/mightypork/rogue/Res.java +++ b/src/mightypork/rogue/Res.java @@ -80,8 +80,16 @@ public final class Res { texture = textures.loadTexture("tiles", "/res/img/map_tiles.png", FilterMode.NEAREST, WrapMode.CLAMP); final QuadGrid tiles = texture.grid(32, 32); - textures.addSheet("tile.mossy_bricks.wall", tiles.makeSheet(4, 0, 7, 1)); - textures.addSheet("tile.mossy_bricks.floor", tiles.makeSheet(16, 5, 7, 1)); + textures.addSheet("tile.wall.mossy_bricks", tiles.makeSheet(4, 0, 7, 1)); + textures.addSheet("tile.wall.small_bricks", tiles.makeSheet(0, 0, 4, 1)); + textures.addSheet("tile.floor.mossy_bricks", tiles.makeSheet(16, 5, 7, 1)); + textures.addSheet("tile.floor.rect_bricks", tiles.makeSheet(23, 5, 4, 1)); + textures.addSheet("tile.wall.sandstone", tiles.makeSheet(0, 3, 10, 1)); + textures.addSheet("tile.floor.sandstone", tiles.makeSheet(0, 6, 10, 1)); + textures.addSheet("tile.wall.brown_cobble", tiles.makeSheet(0, 8, 8, 1)); + textures.addSheet("tile.floor.brown_cobble", tiles.makeSheet(0, 11, 9, 1)); + textures.addSheet("tile.floor.crystal", tiles.makeSheet(4, 5, 6, 1)); + textures.addSheet("tile.wall.crystal", tiles.makeSheet(12, 2, 14, 1)); } diff --git a/src/mightypork/rogue/screens/ingame/GameGui.java b/src/mightypork/rogue/screens/ingame/GameGui.java index 54e2074..4c8ffd0 100644 --- a/src/mightypork/rogue/screens/ingame/GameGui.java +++ b/src/mightypork/rogue/screens/ingame/GameGui.java @@ -8,14 +8,17 @@ import mightypork.gamecore.gui.components.painters.ImagePainter; import mightypork.gamecore.gui.components.painters.QuadPainter; import mightypork.gamecore.gui.screens.Screen; import mightypork.gamecore.gui.screens.ScreenLayer; +import mightypork.gamecore.render.Render; import mightypork.rogue.Res; import mightypork.util.constraints.num.Num; import mightypork.util.constraints.rect.Rect; import mightypork.util.math.color.PAL16; +import mightypork.util.math.color.RGB; public class GameGui extends ScreenLayer { + public GameGui(Screen screen) { super(screen); @@ -23,11 +26,7 @@ public class GameGui extends ScreenLayer { final Num h = root.height(); final Num w = root.width(); final Num minWH = w.min(h).max(700); // avoid too small shrinking - - final Component qp = new QuadPainter(PAL16.VOID); - qp.setRect(root); - root.add(qp); - + final ImagePainter nav = new ImagePainter(Res.getTxQuad("panel")); nav.setRect(root.bottomEdge().growUp(minWH.perc(7))); root.add(nav); @@ -60,4 +59,12 @@ public class GameGui extends ScreenLayer { return 100; } + @Override + public void render() + { + + + super.render(); + } + } diff --git a/src/mightypork/rogue/screens/ingame/ScreenGame.java b/src/mightypork/rogue/screens/ingame/ScreenGame.java index 372d495..157e670 100644 --- a/src/mightypork/rogue/screens/ingame/ScreenGame.java +++ b/src/mightypork/rogue/screens/ingame/ScreenGame.java @@ -10,7 +10,8 @@ public class ScreenGame extends LayeredScreen { public ScreenGame(AppAccess app) { super(app); - + + addLayer(new WorldLayer(this)); //TODO with provided world addLayer(new GameGui(this)); } diff --git a/src/mightypork/rogue/screens/ingame/WorldLayer.java b/src/mightypork/rogue/screens/ingame/WorldLayer.java new file mode 100644 index 0000000..c0c9422 --- /dev/null +++ b/src/mightypork/rogue/screens/ingame/WorldLayer.java @@ -0,0 +1,32 @@ +package mightypork.rogue.screens.ingame; + + +import java.util.Random; + +import mightypork.gamecore.gui.screens.Screen; +import mightypork.gamecore.gui.screens.ScreenLayer; +import mightypork.rogue.world.MapGenerator; +import mightypork.rogue.world.World; + + +public class WorldLayer extends ScreenLayer { + + public WorldLayer(Screen screen) + { + super(screen); + + Random rand = new Random(); + World w = MapGenerator.createWorld(rand.nextLong()); + + WorldRenderer wr = new WorldRenderer(w); + wr.setRect(root); + root.add(wr); + } + + @Override + public int getPriority() + { + return -1; + } + +} diff --git a/src/mightypork/rogue/screens/ingame/WorldRenderer.java b/src/mightypork/rogue/screens/ingame/WorldRenderer.java new file mode 100644 index 0000000..b196b9b --- /dev/null +++ b/src/mightypork/rogue/screens/ingame/WorldRenderer.java @@ -0,0 +1,68 @@ +package mightypork.rogue.screens.ingame; + + +import mightypork.gamecore.control.events.MouseButtonEvent; +import mightypork.gamecore.gui.components.InputComponent; +import mightypork.gamecore.render.Render; +import mightypork.rogue.world.World; +import mightypork.util.constraints.num.Num; +import mightypork.util.constraints.rect.Rect; +import mightypork.util.control.timing.Updateable; +import mightypork.util.math.color.PAL16; +import mightypork.util.math.color.RGB; + + +public class WorldRenderer extends InputComponent implements Updateable { + + private final World world; + private final Rect rightShadow; + private final Rect leftShadow; + private final Rect topShadow; + private final Rect bottomShadow; + + + public WorldRenderer(World world) + { + this.world = world; + + final Num h = height(); + final Num w = width(); + final Num minWH = w.min(h).max(700); + + Num grX = w.perc(30); + Num grY = h.perc(20); + + leftShadow = leftEdge().growRight(grX); + rightShadow = rightEdge().growLeft(grX); + topShadow = topEdge().growDown(grY); + bottomShadow = bottomEdge().growUp(grY).moveY(minWH.perc(-6)); + } + + + @Override + public void receive(MouseButtonEvent event) + { + System.out.println("world clciked, yo"); + } + + + @Override + protected void renderComponent() + { + world.render(this, 8, 6, 64); + + Render.quadGradH(leftShadow, RGB.BLACK, RGB.NONE); + Render.quadGradH(rightShadow, RGB.NONE, RGB.BLACK); + + Render.quadGradV(topShadow, RGB.BLACK, RGB.NONE); + Render.quadGradV(bottomShadow, RGB.NONE, RGB.BLACK); + } + + + @Override + public void update(double delta) + { + world.update(delta); + } + +} diff --git a/src/mightypork/rogue/world/LocalPlayer.java b/src/mightypork/rogue/world/LocalPlayer.java new file mode 100644 index 0000000..447424d --- /dev/null +++ b/src/mightypork/rogue/world/LocalPlayer.java @@ -0,0 +1,83 @@ +package mightypork.rogue.world; + + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import mightypork.util.files.ion.Ion; +import mightypork.util.files.ion.IonBundle; +import mightypork.util.files.ion.IonConstructor; +import mightypork.util.files.ion.Ionizable; + + +/** + * Player info + * + * @author MightyPork + */ +public class LocalPlayer implements Ionizable, MapObserver { + + public static final short ION_MARK = 708; + + public WorldPos position = new WorldPos(); + + + @IonConstructor + public LocalPlayer() + { + } + + + public LocalPlayer(int x, int y, int floor) + { + this.position.setTo(x, y, floor); + } + + + public LocalPlayer(WorldPos pos) + { + this.position = pos; + } + + + @Override + public void load(InputStream in) throws IOException + { + IonBundle ib = (IonBundle) Ion.readObject(in); + + position = ib.get("pos", position); + } + + + @Override + public void save(OutputStream out) throws IOException + { + IonBundle ib = new IonBundle(); + + ib.put("pos", position); + + Ion.writeObject(out, ib); + } + + + @Override + public short getIonMark() + { + return ION_MARK; + } + + + @Override + public WorldPos getPosition() + { + return position; + } + + + @Override + public int getViewRange() + { + return 15; + } +} diff --git a/src/mightypork/rogue/world/MapGenerator.java b/src/mightypork/rogue/world/MapGenerator.java new file mode 100644 index 0000000..8ab3423 --- /dev/null +++ b/src/mightypork/rogue/world/MapGenerator.java @@ -0,0 +1,54 @@ +package mightypork.rogue.world; + + +import java.util.Random; + +import mightypork.rogue.world.map.LevelMap; +import mightypork.rogue.world.tile.Tiles; + + +public class MapGenerator { + + public static final Random rand = new Random(); + + + public static World createWorld(long seed) + { + synchronized (rand) { + rand.setSeed(seed); + + World w = new World(); + w.setSeed(seed); + + int levels = 4 + rand.nextInt(6); + + for (int i = 0; i < levels; i++) { + w.addLevel(createLevel(rand.nextLong())); + } + + // TODO place on start position + w.setPlayer(new LocalPlayer(10, 10, 0)); + return w; + } + } + + + private static LevelMap createLevel(long seed) + { + // TODO + + LevelMap lm = new LevelMap(20, 20); + + lm.fill(Tiles.CRYSTAL_FLOOR); + + Random rand = new Random(); + rand.setSeed(seed); + + for (int i = 0; i < 150; i++) { + lm.setTile(Tiles.CRYSTAL_WALL, rand.nextInt(20), rand.nextInt(20)); + } + + return lm; + } + +} diff --git a/src/mightypork/rogue/world/MapObserver.java b/src/mightypork/rogue/world/MapObserver.java new file mode 100644 index 0000000..c9b2cf4 --- /dev/null +++ b/src/mightypork/rogue/world/MapObserver.java @@ -0,0 +1,22 @@ +package mightypork.rogue.world; + + +/** + * Player observing a map represented by an observer. + * + * @author MightyPork + */ +public interface MapObserver { + + /** + * @return observer's position + */ + public WorldPos getPosition(); + + + /** + * @return observed range (in tiles) + */ + public int getViewRange(); + +} diff --git a/src/mightypork/rogue/world/World.java b/src/mightypork/rogue/world/World.java new file mode 100644 index 0000000..0a538f8 --- /dev/null +++ b/src/mightypork/rogue/world/World.java @@ -0,0 +1,185 @@ +package mightypork.rogue.world; + + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashSet; +import java.util.Set; + +import mightypork.gamecore.render.Render; +import mightypork.rogue.world.map.LevelMap; +import mightypork.rogue.world.map.TileRenderContext; +import mightypork.rogue.world.structs.LevelList; +import mightypork.util.constraints.rect.Rect; +import mightypork.util.constraints.rect.RectConst; +import mightypork.util.constraints.rect.proxy.RectBound; +import mightypork.util.constraints.vect.Vect; +import mightypork.util.constraints.vect.VectConst; +import mightypork.util.control.timing.Updateable; +import mightypork.util.error.CorruptedDataException; +import mightypork.util.files.ion.Ion; +import mightypork.util.files.ion.IonBundle; +import mightypork.util.files.ion.Ionizable; +import mightypork.util.math.color.RGB; + + +public class World implements Ionizable, Updateable { + + public static final short ION_MARK = 706; + + private LevelList levels = new LevelList(); + + private LocalPlayer player = new LocalPlayer(); + + private transient final Set observers = new HashSet<>(); + + private long seed; + + + @Override + public void load(InputStream in) throws IOException + { + // world data + IonBundle ib = (IonBundle) Ion.readObject(in); + player = ib.get("player", player); + levels = ib.get("levels", levels); + seed = ib.get("seed", seed); + + // levels + Ion.readSequence(in, levels); + + if (player == null) throw new CorruptedDataException("Null player in world."); + } + + + @Override + public void save(OutputStream out) throws IOException + { + IonBundle ib = new IonBundle(); + ib.put("player", player); + ib.put("levels", levels); + ib.put("seed", seed); + } + + + public void setPlayer(LocalPlayer player) + { + removeObserver(this.player); + + this.player = player; + + addObserver(player); + } + + + public void removeObserver(MapObserver observer) + { + observers.remove(observer); + } + + + public void addObserver(MapObserver observer) + { + observers.add(observer); + } + + + public void addLevel(LevelMap level) + { + levels.add(level); + } + + + @Override + public short getIonMark() + { + return ION_MARK; + } + + + @Override + public void update(double delta) + { + for (int level = 0; level < levels.size(); level++) { + for (MapObserver observer : observers) { + if (observer.getPosition().floor == level) { + levels.get(level).update(observer, delta); + } + } + } + } + + + public LevelMap getLevelForObserver(MapObserver observer) + { + return levels.get(observer.getPosition().floor); + } + + + /** + * Draw on screen + * + * @param viewport rendering area on screen + * @param xTiles Desired nr of tiles horizontally + * @param yTiles Desired nr of tiles vertically + * @param minSize minimum tile size + */ + public void render(final RectBound viewport, final int yTiles, final int xTiles, final int minSize) + { + LevelMap floor = getLevelForObserver(player); // TODO fractional movement + + Rect r = viewport.getRect(); + double vpH = r.height().value(); + double vpW = r.width().value(); + + // adjust tile size to fit desired amount of tiles + + double allowedSizeW = vpW / xTiles; + double allowedSizeH = vpH / yTiles; + int tileSize = (int) Math.round(Math.max(Math.min(allowedSizeH, allowedSizeW), minSize)); + + tileSize -= tileSize % 16; + + VectConst vpCenter = r.center().sub(tileSize * 0.5, tileSize).freeze(); // 0.5 to center, 1 to move up (down is teh navbar) + + int playerX = player.getPosition().x; + int playerY = player.getPosition().y; + + // total map area + //@formatter:off + RectConst mapRect = vpCenter.startRect().grow( + playerX*tileSize, + playerY*tileSize,// + (floor.getWidth() - playerX) * tileSize, + (floor.getHeight() - playerY) * tileSize + ).freeze(); + //@formatter:on + + // tiles to render + int x1 = (int) Math.floor(playerX - (vpW / tileSize)); + int y1 = (int) Math.floor(playerY - (vpH / tileSize)); + int x2 = (int) Math.ceil(playerX + (vpW / tileSize)); + int y2 = (int) Math.ceil(playerY + (vpH / tileSize)); + + TileRenderContext trc = new TileRenderContext(floor, mapRect); //-tileSize*0.5 + for (trc.y = y1; trc.y <= y2; trc.y++) { + for (trc.x = x1; trc.x <= x2; trc.x++) { + trc.render(); + } + } + } + + + public void setSeed(long seed) + { + this.seed = seed; + } + + + public long getSeed() + { + return seed; + } + +} diff --git a/src/mightypork/rogue/world/WorldPos.java b/src/mightypork/rogue/world/WorldPos.java new file mode 100644 index 0000000..6cc6314 --- /dev/null +++ b/src/mightypork/rogue/world/WorldPos.java @@ -0,0 +1,71 @@ +package mightypork.rogue.world; + + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import mightypork.util.files.ion.Ion; +import mightypork.util.files.ion.IonConstructor; +import mightypork.util.files.ion.Ionizable; + + +/** + * A simple dimension data object + * + * @author MightyPork + */ +public class WorldPos implements Ionizable { + + public static final short ION_MARK = 707; + + public int x, y, floor; + + + public WorldPos(int x, int y, int z) + { + super(); + this.x = x; + this.y = y; + this.floor = z; + } + + + @IonConstructor + public WorldPos() + { + } + + + @Override + public void load(InputStream in) throws IOException + { + x = Ion.readInt(in); + y = Ion.readInt(in); + floor = Ion.readInt(in); + } + + + @Override + public void save(OutputStream out) throws IOException + { + Ion.writeInt(out, x); + Ion.writeInt(out, y); + Ion.writeInt(out, floor); + } + + + @Override + public short getIonMark() + { + return ION_MARK; + } + + + public void setTo(int x, int y, int z) + { + this.x = x; + this.y = y; + this.floor = z; + } +} diff --git a/src/mightypork/rogue/world/item/Item.java b/src/mightypork/rogue/world/item/Item.java index c7abb17..2d19e11 100644 --- a/src/mightypork/rogue/world/item/Item.java +++ b/src/mightypork/rogue/world/item/Item.java @@ -5,7 +5,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import mightypork.rogue.world.tile.TileRenderContext; +import mightypork.rogue.world.map.TileRenderContext; import mightypork.util.constraints.rect.proxy.RectBound; import mightypork.util.control.timing.Animator; import mightypork.util.control.timing.AnimatorBounce; @@ -75,9 +75,9 @@ public class Item implements Updateable, Ionizable { { final IonBundle ib = (IonBundle) Ion.readObject(in); - id = ib.get("id", id); - flags = ib.get("flags", flags); - numbers = ib.get("numbers", numbers); + id = ib.get("id", 0); + flags = ib.get("flags", null); + numbers = ib.get("numbers", null); if (id != model.id) { model = Items.get(id); diff --git a/src/mightypork/rogue/world/item/ItemModel.java b/src/mightypork/rogue/world/item/ItemModel.java index c7acac7..922bd75 100644 --- a/src/mightypork/rogue/world/item/ItemModel.java +++ b/src/mightypork/rogue/world/item/ItemModel.java @@ -1,7 +1,7 @@ package mightypork.rogue.world.item; -import mightypork.rogue.world.tile.TileRenderContext; +import mightypork.rogue.world.map.TileRenderContext; import mightypork.util.annotations.DefaultImpl; import mightypork.util.constraints.num.proxy.NumBoundAdapter; import mightypork.util.constraints.rect.Rect; diff --git a/src/mightypork/rogue/world/WorldMap.java b/src/mightypork/rogue/world/map/LevelMap.java similarity index 55% rename from src/mightypork/rogue/world/WorldMap.java rename to src/mightypork/rogue/world/map/LevelMap.java index 99468c4..a0be413 100644 --- a/src/mightypork/rogue/world/WorldMap.java +++ b/src/mightypork/rogue/world/map/LevelMap.java @@ -1,19 +1,27 @@ -package mightypork.rogue.world; +package mightypork.rogue.world.map; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import mightypork.rogue.world.MapObserver; +import mightypork.rogue.world.WorldPos; import mightypork.rogue.world.tile.Tile; -import mightypork.rogue.world.tile.TileGrid; +import mightypork.rogue.world.tile.TileModel; import mightypork.rogue.world.tile.Tiles; import mightypork.util.files.ion.Ion; import mightypork.util.files.ion.IonConstructor; import mightypork.util.files.ion.Ionizable; +import mightypork.util.logging.Log; -public class WorldMap implements TileGrid, Ionizable { +/** + * One level of the dungeon + * + * @author MightyPork + */ +public class LevelMap implements MapAccess, Ionizable { public static final int ION_MARK = 702; @@ -22,14 +30,17 @@ public class WorldMap implements TileGrid, Ionizable { /** Array of tiles [y][x] */ private Tile[][] tiles; + /** Level seed (used for generation and tile variation) */ + public long seed; + @IonConstructor - public WorldMap() + public LevelMap() { } - public WorldMap(int width, int height) + public LevelMap(int width, int height) { this.width = width; this.height = height; @@ -41,9 +52,21 @@ public class WorldMap implements TileGrid, Ionizable { { this.tiles = new Tile[height][width]; + fill(Tiles.NULL_EMPTY); + } + + + public void fill(int id) + { + fill(Tiles.get(id)); + } + + + public void fill(TileModel model) + { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - tiles[y][x] = Tiles.NONE.create(); + tiles[y][x] = model.createTile(); } } } @@ -52,10 +75,18 @@ public class WorldMap implements TileGrid, Ionizable { @Override public final Tile getTile(int x, int y) { + if (x < 0 || x >= width || y < 0 || y >= height) return Tiles.NULL_SOLID.createTile(); // out of range + return tiles[y][x]; } + public final void setTile(TileModel model, int x, int y) + { + setTile(model.createTile(), x, y); + } + + public final void setTile(int tileId, int x, int y) { setTile(new Tile(tileId), x, y); @@ -64,6 +95,8 @@ public class WorldMap implements TileGrid, Ionizable { public final void setTile(Tile tile, int x, int y) { + if (x < 0 || x > width || y < 0 || y >= height) return; // out of range + tiles[y][x] = tile; } @@ -82,6 +115,19 @@ public class WorldMap implements TileGrid, Ionizable { } + public void setSeed(long seed) + { + this.seed = seed; + } + + + @Override + public long getSeed() + { + return seed; + } + + @Override public void load(InputStream in) throws IOException { @@ -132,4 +178,28 @@ public class WorldMap implements TileGrid, Ionizable { return ION_MARK; } + + public void update(MapObserver observer, double delta) + { + int viewRange = observer.getViewRange(); + WorldPos position = observer.getPosition(); + + int x1 = position.x - viewRange; + int y1 = position.y - viewRange; + + int x2 = x1 + viewRange * 2; + int y2 = y1 + viewRange * 2; + + x1 = Math.min(Math.max(0, x1), width); + y1 = Math.min(Math.max(0, y1), height); + x2 = Math.min(x2, width); + y2 = Math.max(y2, height); + + for (int y = y1; y <= y2; y++) { + for (int x = x1; x <= x2; x++) { + getTile(x, y).update(delta); + } + } + } + } diff --git a/src/mightypork/rogue/world/map/MapAccess.java b/src/mightypork/rogue/world/map/MapAccess.java new file mode 100644 index 0000000..4d09f4f --- /dev/null +++ b/src/mightypork/rogue/world/map/MapAccess.java @@ -0,0 +1,40 @@ +package mightypork.rogue.world.map; + + +import mightypork.rogue.world.tile.Tile; + + +/** + * Access interface for a level map. + * + * @author MightyPork + */ +public interface MapAccess { + + /** + * Ge tile at X,Y + * + * @param x + * @param y + * @return tile + */ + Tile getTile(int x, int y); + + + /** + * @return map width in tiles + */ + int getWidth(); + + + /** + * @return map height in tiles + */ + int getHeight(); + + + /** + * @return map seed + */ + long getSeed(); +} diff --git a/src/mightypork/rogue/world/tile/TileRenderContext.java b/src/mightypork/rogue/world/map/TileRenderContext.java similarity index 50% rename from src/mightypork/rogue/world/tile/TileRenderContext.java rename to src/mightypork/rogue/world/map/TileRenderContext.java index 6eb6eee..31ff34a 100644 --- a/src/mightypork/rogue/world/tile/TileRenderContext.java +++ b/src/mightypork/rogue/world/map/TileRenderContext.java @@ -1,45 +1,65 @@ -package mightypork.rogue.world.tile; +package mightypork.rogue.world.map; +import mightypork.rogue.world.tile.Tile; import mightypork.util.constraints.rect.Rect; import mightypork.util.constraints.rect.builders.TiledRect; import mightypork.util.constraints.rect.proxy.RectBound; import mightypork.util.math.noise.NoiseGen; +/** + * Context for tile rendering. Provides tile rect, surrounding tiles, rendered + * tile and random noise values for position-static tile variation. + * + * @author MightyPork + */ public final class TileRenderContext implements RectBound { - private final TileGrid map; + private final MapAccess map; private final TiledRect tiler; private final NoiseGen noise; public int x, y; - public TileRenderContext(TileGrid map, Rect drawArea, long renderNoiseSeed) + public TileRenderContext(MapAccess map, Rect drawArea) { this.map = map; this.tiler = drawArea.tiles(map.getWidth(), map.getHeight()); - this.noise = new NoiseGen(0.2, 0, 0.5, 1, renderNoiseSeed); + this.noise = new NoiseGen(0.2, 0, 0.5, 1, map.getSeed()); } + /** + * @return the rendered tile. + */ public Tile getTile() { return map.getTile(x, y); } + /** + * Get a neighbor tile + * + * @param offsetX x offset (left-right) + * @param offsetY y offset (up-down) + * @return the tile at that position + */ public Tile getAdjacentTile(int offsetX, int offsetY) { return map.getTile(x + offsetX, y + offsetY); } + /** + * Rect of the current tile to draw + */ @Override public Rect getRect() { - return tiler.tile(x, y); + return tiler.tile(x, y).grow(0.01); // important to avoid gaps b/w tiles when scaled. } @@ -52,15 +72,8 @@ public final class TileRenderContext implements RectBound { } - public void setCoord(int x, int y) - { - this.x = x; - this.y = y; - } - - - public void renderTile(Tile t) + public void render() { - t.render(this); + map.getTile(x, y).render(this); } } diff --git a/src/mightypork/rogue/world/structs/ItemStack.java b/src/mightypork/rogue/world/structs/ItemStack.java new file mode 100644 index 0000000..bde85cf --- /dev/null +++ b/src/mightypork/rogue/world/structs/ItemStack.java @@ -0,0 +1,19 @@ +package mightypork.rogue.world.structs; + + +import mightypork.rogue.world.item.Item; +import mightypork.util.files.ion.templates.IonizableStack; + + +public class ItemStack extends IonizableStack { + + private static final short ION_MARK = 710; + + + @Override + public short getIonMark() + { + return ION_MARK; + } + +} diff --git a/src/mightypork/rogue/world/structs/LevelList.java b/src/mightypork/rogue/world/structs/LevelList.java new file mode 100644 index 0000000..aee42a3 --- /dev/null +++ b/src/mightypork/rogue/world/structs/LevelList.java @@ -0,0 +1,18 @@ +package mightypork.rogue.world.structs; + + +import mightypork.rogue.world.map.LevelMap; +import mightypork.util.files.ion.templates.IonizableArrayList; + + +public class LevelList extends IonizableArrayList { + + public static final short ION_MARK = 709; + + @Override + public short getIonMark() + { + return ION_MARK; + } + +} diff --git a/src/mightypork/rogue/world/tile/Tile.java b/src/mightypork/rogue/world/tile/Tile.java index 8757e63..14e0238 100644 --- a/src/mightypork/rogue/world/tile/Tile.java +++ b/src/mightypork/rogue/world/tile/Tile.java @@ -4,7 +4,10 @@ package mightypork.rogue.world.tile; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Stack; +import mightypork.rogue.world.item.Item; +import mightypork.rogue.world.map.TileRenderContext; import mightypork.util.control.timing.Animator; import mightypork.util.control.timing.Updateable; import mightypork.util.files.ion.Ion; @@ -23,7 +26,7 @@ public final class Tile implements Ionizable, Updateable { public int id; - public TileItems items; + public Stack items = new Stack<>(); public boolean[] flags; public int[] numbers; @@ -45,13 +48,12 @@ public final class Tile implements Ionizable, Updateable { { this.model = model; this.id = model.id; - this.items = new TileItems(); } public void render(TileRenderContext context) { - model.render(this, context); + model.render(context); if (!items.isEmpty()) { items.peek().renderOnTile(context); @@ -69,7 +71,8 @@ public final class Tile implements Ionizable, Updateable { ib.put("id", id); ib.put("flags", flags); ib.put("numbers", numbers); - ib.put("items", items); + + Ion.writeSequence(out, items); Ion.writeObject(out, ib); } @@ -78,12 +81,15 @@ public final class Tile implements Ionizable, Updateable { @Override public void load(InputStream in) throws IOException { + // bundle of data final IonBundle ib = (IonBundle) Ion.readObject(in); - id = ib.get("id", id); flags = ib.get("flags", flags); numbers = ib.get("numbers", numbers); - items = ib.get("items", items); + + // stored items + items.clear(); + Ion.readSequence(in, items); // renew model if (model == null || id != model.id) { @@ -102,6 +108,7 @@ public final class Tile implements Ionizable, Updateable { @Override public void update(double delta) { + model.update(this, delta); if (!items.isEmpty()) { items.peek().update(delta); } diff --git a/src/mightypork/rogue/world/tile/TileGrid.java b/src/mightypork/rogue/world/tile/TileGrid.java deleted file mode 100644 index 960094c..0000000 --- a/src/mightypork/rogue/world/tile/TileGrid.java +++ /dev/null @@ -1,13 +0,0 @@ -package mightypork.rogue.world.tile; - - -public interface TileGrid { - - Tile getTile(int x, int y); - - - int getWidth(); - - - int getHeight(); -} diff --git a/src/mightypork/rogue/world/tile/TileModel.java b/src/mightypork/rogue/world/tile/TileModel.java index 5ecfbf4..2377d1d 100644 --- a/src/mightypork/rogue/world/tile/TileModel.java +++ b/src/mightypork/rogue/world/tile/TileModel.java @@ -1,6 +1,7 @@ package mightypork.rogue.world.tile; +import mightypork.rogue.world.map.TileRenderContext; import mightypork.util.annotations.DefaultImpl; @@ -23,10 +24,14 @@ public abstract class TileModel { /** + * Create a tile. In case of null tiles / tiles with absolutely no + * variability, the same instance can be returned over and over (created ie. + * using lazy load) + * * @return new tile with this model */ @DefaultImpl - public Tile create() + public Tile createTile() { return new Tile(this); } @@ -35,10 +40,9 @@ public abstract class TileModel { /** * Render the tile. * - * @param tile * @param context */ - public abstract void render(Tile tile, TileRenderContext context); + public abstract void render(TileRenderContext context); /** @@ -49,7 +53,11 @@ public abstract class TileModel { /** - * @return true if the tile can be walkable at some conditions + * Check if the tile is walkable at some conditions. Used for world + * generation to distinguish between doors etc and regular walls.
+ * Null tile should return true, if it can be replaced by a regular floor. + * + * @return if it's potentially walkable */ public abstract boolean isPotentiallyWalkable(); @@ -59,4 +67,13 @@ public abstract class TileModel { return false; } + + /** + * Update a tile + * + * @param tile tile + * @param delta delta time + */ + public abstract void update(Tile tile, double delta); + } diff --git a/src/mightypork/rogue/world/tile/Tiles.java b/src/mightypork/rogue/world/tile/Tiles.java index 0a8ecc7..e959fe6 100644 --- a/src/mightypork/rogue/world/tile/Tiles.java +++ b/src/mightypork/rogue/world/tile/Tiles.java @@ -2,7 +2,9 @@ package mightypork.rogue.world.tile; import mightypork.rogue.world.tile.models.Floor; -import mightypork.rogue.world.tile.models.NullTile; +import mightypork.rogue.world.tile.models.NullFloor; +import mightypork.rogue.world.tile.models.NullWall; +import mightypork.rogue.world.tile.models.Wall; /** @@ -14,14 +16,27 @@ public final class Tiles { private static final TileModel[] tiles = new TileModel[256]; - public static final TileModel NONE = new NullTile(0); - public static final TileModel FLOOR_MOSSY = new Floor(1, "tile.mossy_bricks.floor"); - public static final TileModel WALL_MOSSY = new Floor(2, "tile.mossy_bricks.wall"); + public static final TileModel NULL_SOLID = new NullWall(0); + public static final TileModel NULL_EMPTY = new NullFloor(1); + public static final TileModel BRICK_FLOOR_VINES = new Floor(2, "tile.floor.mossy_bricks"); + public static final TileModel BRICK_WALL_VINES = new Wall(3, "tile.wall.mossy_bricks"); + public static final TileModel BRICK_FLOOR_RECT = new Floor(4, "tile.floor.rect_bricks"); + public static final TileModel BRICK_WALL_SMALL = new Wall(5, "tile.wall.small_bricks"); + public static final TileModel SANDSTONE_FLOOR = new Floor(6, "tile.floor.sandstone"); + public static final TileModel SANDSTONE_WALL = new Wall(7, "tile.wall.sandstone"); + public static final TileModel BRCOBBLE_FLOOR = new Floor(8, "tile.floor.brown_cobble"); + public static final TileModel BRCOBBLE_WALL = new Wall(9, "tile.wall.brown_cobble"); + public static final TileModel CRYSTAL_FLOOR = new Floor(10, "tile.floor.crystal"); + public static final TileModel CRYSTAL_WALL = new Wall(11, "tile.wall.crystal"); static void register(int id, TileModel model) { - if (id < 0 || id >= tiles.length) if (tiles[id] != null) { + if (id < 0 || id >= tiles.length) { + throw new IllegalArgumentException("Tile ID " + id + " is out of range."); + } + + if (tiles[id] != null) { throw new IllegalArgumentException("Tile ID " + id + " already in use."); } diff --git a/src/mightypork/rogue/world/tile/models/NullTile.java b/src/mightypork/rogue/world/tile/models/AbstractNullTile.java similarity index 56% rename from src/mightypork/rogue/world/tile/models/NullTile.java rename to src/mightypork/rogue/world/tile/models/AbstractNullTile.java index dd27f2b..4e4e5a3 100644 --- a/src/mightypork/rogue/world/tile/models/NullTile.java +++ b/src/mightypork/rogue/world/tile/models/AbstractNullTile.java @@ -1,39 +1,43 @@ package mightypork.rogue.world.tile.models; +import mightypork.rogue.world.map.TileRenderContext; import mightypork.rogue.world.tile.Tile; import mightypork.rogue.world.tile.TileModel; -import mightypork.rogue.world.tile.TileRenderContext; -public class NullTile extends TileModel { +/** + * Null tile + * + * @author MightyPork + */ +public abstract class AbstractNullTile extends TileModel { private Tile inst; - public NullTile(int id) + public AbstractNullTile(int id) { super(id); } @Override - public void render(Tile tile, TileRenderContext context) + public void render(TileRenderContext context) { } @Override - public boolean isWalkable(Tile tile) + public void update(Tile tile, double delta) { - return false; } @Override - public boolean isPotentiallyWalkable() + public boolean isWalkable(Tile tile) { - return true; + return isPotentiallyWalkable(); } @@ -45,7 +49,7 @@ public class NullTile extends TileModel { @Override - public Tile create() + public Tile createTile() { if (inst == null) { inst = new Tile(this); diff --git a/src/mightypork/rogue/world/tile/models/NullFloor.java b/src/mightypork/rogue/world/tile/models/NullFloor.java new file mode 100644 index 0000000..0a672c2 --- /dev/null +++ b/src/mightypork/rogue/world/tile/models/NullFloor.java @@ -0,0 +1,18 @@ +package mightypork.rogue.world.tile.models; + + +public class NullFloor extends AbstractNullTile { + + public NullFloor(int id) + { + super(id); + } + + + @Override + public boolean isPotentiallyWalkable() + { + return true; + } + +} diff --git a/src/mightypork/rogue/world/tile/models/NullWall.java b/src/mightypork/rogue/world/tile/models/NullWall.java new file mode 100644 index 0000000..2350a30 --- /dev/null +++ b/src/mightypork/rogue/world/tile/models/NullWall.java @@ -0,0 +1,18 @@ +package mightypork.rogue.world.tile.models; + + +public class NullWall extends AbstractNullTile { + + public NullWall(int id) + { + super(id); + } + + + @Override + public boolean isPotentiallyWalkable() + { + return false; + } + +} diff --git a/src/mightypork/rogue/world/tile/models/SimpleTile.java b/src/mightypork/rogue/world/tile/models/SimpleTile.java index 1a47aa2..d18a27e 100644 --- a/src/mightypork/rogue/world/tile/models/SimpleTile.java +++ b/src/mightypork/rogue/world/tile/models/SimpleTile.java @@ -1,13 +1,16 @@ package mightypork.rogue.world.tile.models; +import mightypork.gamecore.render.DisplaySystem; import mightypork.gamecore.render.Render; import mightypork.gamecore.render.textures.TxSheet; import mightypork.rogue.Res; +import mightypork.rogue.world.map.TileRenderContext; import mightypork.rogue.world.tile.Tile; import mightypork.rogue.world.tile.TileModel; -import mightypork.rogue.world.tile.TileRenderContext; import mightypork.util.annotations.DefaultImpl; +import mightypork.util.constraints.vect.Vect; +import mightypork.util.math.color.RGB; public abstract class SimpleTile extends TileModel { @@ -23,11 +26,17 @@ public abstract class SimpleTile extends TileModel { @Override - public void render(Tile tile, TileRenderContext context) + public void render(TileRenderContext context) { Render.quadTextured(context.getRect(), sheet.getRandomQuad(context.getTileNoise())); } + @Override + @DefaultImpl + public void update(Tile tile, double delta) + { + } + @Override @DefaultImpl diff --git a/src/mightypork/test/testworldtofile.java b/src/mightypork/test/testworldtofile.java new file mode 100644 index 0000000..d6188cf --- /dev/null +++ b/src/mightypork/test/testworldtofile.java @@ -0,0 +1,6 @@ +package mightypork.test; + + +public class testworldtofile { + +} diff --git a/src/mightypork/util/constraints/rect/Rect.java b/src/mightypork/util/constraints/rect/Rect.java index 42a4214..b9088d8 100644 --- a/src/mightypork/util/constraints/rect/Rect.java +++ b/src/mightypork/util/constraints/rect/Rect.java @@ -725,6 +725,69 @@ public abstract class Rect implements RectBound, Digestable { }; } + + + + /** + * Round coords down + * + * @return result + */ + public Rect floor() + { + + return new Rect() { + + private final Rect t = Rect.this; + + + @Override + public Vect size() + { + return t.size().floor(); + } + + + @Override + public Vect origin() + { + return t.origin().floor(); + } + + }; + } + + + + /** + * Round coords up + * + * @return result + */ + public Rect ceil() + { + + return new Rect() { + + private final Rect t = Rect.this; + + + @Override + public Vect size() + { + return t.size().ceil(); + } + + + @Override + public Vect origin() + { + return t.origin().ceil(); + } + + }; + } + public Num x() @@ -985,4 +1048,5 @@ public abstract class Rect implements RectBound, Digestable { // overflow || intersect return ((rw < rx || rw > tx) && (rh < ry || rh > ty) && (tw < tx || tw > rx) && (th < ty || th > ry)); } + } diff --git a/src/mightypork/util/constraints/rect/RectConst.java b/src/mightypork/util/constraints/rect/RectConst.java index 655ec84..8664c0b 100644 --- a/src/mightypork/util/constraints/rect/RectConst.java +++ b/src/mightypork/util/constraints/rect/RectConst.java @@ -36,6 +36,8 @@ public class RectConst extends Rect { private RectConst v_edge_t; private RectConst v_edge_b; private RectDigest digest; + private RectConst v_floor; + private RectConst v_ceil; /** @@ -152,6 +154,20 @@ public class RectConst extends Rect { } + @Override + public RectConst floor() + { + return (v_floor != null) ? v_floor : (v_floor = Rect.make(pos.floor(), size.floor())); + } + + + @Override + public RectConst ceil() + { + return (v_ceil != null) ? v_ceil : (v_ceil = Rect.make(pos.ceil(), size.ceil())); + } + + @Override public NumConst x() { diff --git a/src/mightypork/util/error/CorruptedDataException.java b/src/mightypork/util/error/CorruptedDataException.java new file mode 100644 index 0000000..2a3f0e4 --- /dev/null +++ b/src/mightypork/util/error/CorruptedDataException.java @@ -0,0 +1,36 @@ +package mightypork.util.error; + +import java.io.IOException; + + +/** + * To be used when a data could not be read successfully. + * + * @author MightyPork + */ +public class CorruptedDataException extends IOException { + + public CorruptedDataException() + { + super(); + } + + + public CorruptedDataException(String message, Throwable cause) + { + super(message, cause); + } + + + public CorruptedDataException(String message) + { + super(message); + } + + + public CorruptedDataException(Throwable cause) + { + super(cause); + } + +} diff --git a/src/mightypork/util/files/ion/Ion.java b/src/mightypork/util/files/ion/Ion.java index b667b93..ea2d985 100644 --- a/src/mightypork/util/files/ion/Ion.java +++ b/src/mightypork/util/files/ion/Ion.java @@ -6,6 +6,7 @@ import java.nio.ByteBuffer; import java.util.*; import java.util.Map.Entry; +import mightypork.util.error.CorruptedDataException; import mightypork.util.logging.Log; @@ -83,7 +84,7 @@ public class Ion { static final short DATA_LIST = 81; /** Ionizables */ - private static Map> customIonizables = new HashMap<>(); + private static Map> customIonizables = new HashMap<>(); // buffers and helper arrays for storing to streams. private static ByteBuffer bi = ByteBuffer.allocate(Integer.SIZE / 8); @@ -122,7 +123,7 @@ public class Ion { * except * @param objClass class of the registered Ionizable */ - public static void registerIonizable(int mark, Class objClass) + public static void registerIonizable(int mark, Class objClass) { // negative marks are allowed. if (mark > Short.MAX_VALUE) throw new IllegalArgumentException("Mark too high (max " + Short.MAX_VALUE + ")."); @@ -147,9 +148,9 @@ public class Ion { * * @param path file path * @return the loaded object - * @throws IOException on failure + * @throws CorruptedDataException */ - public static Object fromFile(String path) throws IOException + public static Object fromFile(String path) throws CorruptedDataException { return fromFile(new File(path)); } @@ -160,9 +161,9 @@ public class Ion { * * @param file file * @return the loaded object - * @throws IOException on failure + * @throws CorruptedDataException */ - public static Object fromFile(File file) throws IOException + public static Object fromFile(File file) throws CorruptedDataException { try(InputStream in = new FileInputStream(file)) { @@ -170,7 +171,7 @@ public class Ion { return obj; } catch (final IOException e) { - throw new IOException("Error loading ION file.", e); + throw new CorruptedDataException("Error loading ION file.", e); } } @@ -257,7 +258,7 @@ public class Ion { loaded = ((Ionizable) clz.newInstance()); } catch (InstantiationException | IllegalAccessException e) { - throw new IOException("Cound not instantiate: " + Log.str(customIonizables.get(mark)), e); + throw new RuntimeException("Cound not instantiate: " + Log.str(customIonizables.get(mark)), e); } loaded.load(in); @@ -370,14 +371,14 @@ public class Ion { return Strings; default: - throw new IOException("Invalid Ion mark: " + mark); + throw new CorruptedDataException("Invalid Ion mark: " + mark); } } public static void expect(InputStream in, short mark) throws IOException { - if (readMark(in) != mark) throw new IOException("Unexpected mark in ION stream."); + if (readMark(in) != mark) throw new CorruptedDataException("Unexpected mark in ION stream."); } @@ -403,7 +404,20 @@ public class Ion { public static void writeObject(OutputStream out, Object obj) throws IOException { if (obj instanceof Ionizable) { - writeMark(out, ((Ionizable) obj).getIonMark()); + + short mark = ((Ionizable) obj).getIonMark(); + + Class clzRegistered = customIonizables.get(mark); + + if (clzRegistered == null) { + throw new IOException("Ionizable object not registered: " + Log.str(obj.getClass())); + } + + if (clzRegistered != obj.getClass()) { + throw new IOException("Registered class does not match actual one for " + Log.str(obj.getClass())); + } + + writeMark(out, mark); ((Ionizable) obj).save(out); return; } @@ -1172,12 +1186,12 @@ public class Ion { if (mark == ENTRY) return true; if (mark == END) return false; - throw new IOException("Unexpected mark encountered while reading sequence."); + throw new CorruptedDataException("Unexpected mark " + mark + " encountered in sequence, expected " + ENTRY + " or " + END); } /** - * Read a sequence of elements + * Read a sequence of elements into an ArrayList * * @param in input stream * @return the collection @@ -1185,12 +1199,12 @@ public class Ion { */ public static Collection readSequence(InputStream in) throws IOException { - return readSequence(in, new LinkedList()); + return readSequence(in, new ArrayList()); } /** - * Load entries into a collection + * Load entries into a collection. The collection is cleaned first. * * @param in input stream * @param filled collection to populate @@ -1201,12 +1215,13 @@ public class Ion { public static Collection readSequence(InputStream in, Collection filled) throws IOException { try { + filled.clear(); while (hasNextEntry(in)) { filled.add((T) readObject(in)); } return filled; } catch (final ClassCastException e) { - throw new IOException("Unexpected element type."); + throw new CorruptedDataException("Unexpected element type.", e); } } @@ -1229,7 +1244,7 @@ public class Ion { /** - * Read a map of elements + * Read element pairs into a HashMap * * @param in input stream * @return the map @@ -1237,12 +1252,12 @@ public class Ion { */ public static Map readMap(InputStream in) throws IOException { - return readMap(in, new LinkedHashMap()); + return readMap(in, new HashMap()); } /** - * Load data into a map + * Load data into a map. The map is cleaned first. * * @param in input stream * @param filled filled map @@ -1253,6 +1268,7 @@ public class Ion { public static Map readMap(InputStream in, Map filled) throws IOException { try { + filled.clear(); while (hasNextEntry(in)) { final K key = (K) readObject(in); final V value = (V) readObject(in); @@ -1261,7 +1277,7 @@ public class Ion { } return filled; } catch (final ClassCastException e) { - throw new IOException("Unexpected element type."); + throw new CorruptedDataException("Unexpected element type.", e); } } diff --git a/src/mightypork/util/files/ion/IonBundle.java b/src/mightypork/util/files/ion/IonBundle.java index 95c3ff0..31174ff 100644 --- a/src/mightypork/util/files/ion/IonBundle.java +++ b/src/mightypork/util/files/ion/IonBundle.java @@ -1,18 +1,21 @@ package mightypork.util.files.ion; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.LinkedHashMap; +import mightypork.util.files.ion.templates.IonizableHashMap; /** + *

* Data bundle. + *

+ *

+ * Storing data in a bundle guarantees that future versions will be compatible + * with the older format. Reading using default values ensures that you will get + * some value even if it was not saved in the file. + *

* * @author MightyPork */ -public class IonBundle extends LinkedHashMap implements Ionizable { +public class IonBundle extends IonizableHashMap { /** * Get an object. If not found, fallback is returned. @@ -51,19 +54,4 @@ public class IonBundle extends LinkedHashMap implements Ionizabl return Ion.DATA_BUNDLE; } - - @Override - public void load(InputStream in) throws IOException - { - clear(); - Ion.readMap(in, this); - } - - - @Override - public void save(OutputStream out) throws IOException - { - Ion.writeMap(out, this); - } - } diff --git a/src/mightypork/util/files/ion/IonConstructor.java b/src/mightypork/util/files/ion/IonConstructor.java index 39a7a73..820cdfb 100644 --- a/src/mightypork/util/files/ion/IonConstructor.java +++ b/src/mightypork/util/files/ion/IonConstructor.java @@ -9,8 +9,17 @@ import java.lang.annotation.Target; /** + *

* Implicit constructor marked like this is intended to be solely used for ION - * de-serialization. This is a description annotation and has no other function. + * de-serialization. + *

+ *

+ * Constructors marked like this should create a functional instance with + * default values. + *

+ *

+ * This is a descriptive annotation and has no other function. + *

* * @author MightyPork */ diff --git a/src/mightypork/util/files/ion/templates/IonizableArrayList.java b/src/mightypork/util/files/ion/templates/IonizableArrayList.java new file mode 100644 index 0000000..8f04952 --- /dev/null +++ b/src/mightypork/util/files/ion/templates/IonizableArrayList.java @@ -0,0 +1,28 @@ +package mightypork.util.files.ion.templates; + + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; + +import mightypork.util.files.ion.Ion; +import mightypork.util.files.ion.Ionizable; + + +public abstract class IonizableArrayList extends ArrayList implements Ionizable { + + @Override + public void load(InputStream in) throws IOException + { + Ion.readSequence(in, this); + } + + + @Override + public void save(OutputStream out) throws IOException + { + Ion.writeSequence(out, this); + } + +} diff --git a/src/mightypork/util/files/ion/templates/IonizableHashMap.java b/src/mightypork/util/files/ion/templates/IonizableHashMap.java new file mode 100644 index 0000000..5326857 --- /dev/null +++ b/src/mightypork/util/files/ion/templates/IonizableHashMap.java @@ -0,0 +1,28 @@ +package mightypork.util.files.ion.templates; + + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashMap; + +import mightypork.util.files.ion.Ion; +import mightypork.util.files.ion.Ionizable; + + +public abstract class IonizableHashMap extends HashMap implements Ionizable { + + @Override + public void load(InputStream in) throws IOException + { + Ion.readMap(in, this); + } + + + @Override + public void save(OutputStream out) throws IOException + { + Ion.writeMap(out, this); + } + +} diff --git a/src/mightypork/util/files/ion/templates/IonizableHashSet.java b/src/mightypork/util/files/ion/templates/IonizableHashSet.java new file mode 100644 index 0000000..73979cf --- /dev/null +++ b/src/mightypork/util/files/ion/templates/IonizableHashSet.java @@ -0,0 +1,28 @@ +package mightypork.util.files.ion.templates; + + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashSet; + +import mightypork.util.files.ion.Ion; +import mightypork.util.files.ion.Ionizable; + + +public abstract class IonizableHashSet extends HashSet implements Ionizable { + + @Override + public void load(InputStream in) throws IOException + { + Ion.readSequence(in, this); + } + + + @Override + public void save(OutputStream out) throws IOException + { + Ion.writeSequence(out, this); + } + +} diff --git a/src/mightypork/util/files/ion/templates/IonizableLinkedHashMap.java b/src/mightypork/util/files/ion/templates/IonizableLinkedHashMap.java new file mode 100644 index 0000000..8d10803 --- /dev/null +++ b/src/mightypork/util/files/ion/templates/IonizableLinkedHashMap.java @@ -0,0 +1,28 @@ +package mightypork.util.files.ion.templates; + + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.LinkedHashMap; + +import mightypork.util.files.ion.Ion; +import mightypork.util.files.ion.Ionizable; + + +public abstract class IonizableLinkedHashMap extends LinkedHashMap implements Ionizable { + + @Override + public void load(InputStream in) throws IOException + { + Ion.readMap(in, this); + } + + + @Override + public void save(OutputStream out) throws IOException + { + Ion.writeMap(out, this); + } + +} diff --git a/src/mightypork/util/files/ion/templates/IonizableLinkedList.java b/src/mightypork/util/files/ion/templates/IonizableLinkedList.java new file mode 100644 index 0000000..1d90e02 --- /dev/null +++ b/src/mightypork/util/files/ion/templates/IonizableLinkedList.java @@ -0,0 +1,28 @@ +package mightypork.util.files.ion.templates; + + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.LinkedList; + +import mightypork.util.files.ion.Ion; +import mightypork.util.files.ion.Ionizable; + + +public abstract class IonizableLinkedList extends LinkedList implements Ionizable { + + @Override + public void load(InputStream in) throws IOException + { + Ion.readSequence(in, this); + } + + + @Override + public void save(OutputStream out) throws IOException + { + Ion.writeSequence(out, this); + } + +} diff --git a/src/mightypork/rogue/world/tile/TileItems.java b/src/mightypork/util/files/ion/templates/IonizableStack.java similarity index 61% rename from src/mightypork/rogue/world/tile/TileItems.java rename to src/mightypork/util/files/ion/templates/IonizableStack.java index fd61f86..60c0098 100644 --- a/src/mightypork/rogue/world/tile/TileItems.java +++ b/src/mightypork/util/files/ion/templates/IonizableStack.java @@ -1,4 +1,4 @@ -package mightypork.rogue.world.tile; +package mightypork.util.files.ion.templates; import java.io.IOException; @@ -6,15 +6,11 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.Stack; -import mightypork.rogue.world.item.Item; import mightypork.util.files.ion.Ion; import mightypork.util.files.ion.Ionizable; -public class TileItems extends Stack implements Ionizable { - - public static final short ION_MARK = 703; - +public abstract class IonizableStack extends Stack implements Ionizable { @Override public void load(InputStream in) throws IOException @@ -29,11 +25,4 @@ public class TileItems extends Stack implements Ionizable { Ion.writeSequence(out, this); } - - @Override - public short getIonMark() - { - return ION_MARK; - } - } diff --git a/src/mightypork/util/files/ion/templates/IonizableTreeSet.java b/src/mightypork/util/files/ion/templates/IonizableTreeSet.java new file mode 100644 index 0000000..ccc714e --- /dev/null +++ b/src/mightypork/util/files/ion/templates/IonizableTreeSet.java @@ -0,0 +1,28 @@ +package mightypork.util.files.ion.templates; + + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.TreeSet; + +import mightypork.util.files.ion.Ion; +import mightypork.util.files.ion.Ionizable; + + +public abstract class IonizableTreeSet extends TreeSet implements Ionizable { + + @Override + public void load(InputStream in) throws IOException + { + Ion.readSequence(in, this); + } + + + @Override + public void save(OutputStream out) throws IOException + { + Ion.writeSequence(out, this); + } + +} diff --git a/src/mightypork/util/math/color/RGB.java b/src/mightypork/util/math/color/RGB.java index ec48861..572b2ae 100644 --- a/src/mightypork/util/math/color/RGB.java +++ b/src/mightypork/util/math/color/RGB.java @@ -23,4 +23,6 @@ public interface RGB { Color PINK = Color.fromHex(0xFF3FFC); Color ORANGE = Color.fromHex(0xFC4800); + + Color NONE = Color.NONE; }