From ecbb2c86157ba519ef7b4b646365c19a53990f7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Thu, 1 May 2014 04:14:23 +0200 Subject: [PATCH] new tiles, secret room, locked door, passages --- res/img/tiles16.png | Bin 6973 -> 9904 bytes res/img/tiles16.xcf | Bin 14076 -> 20916 bytes src/mightypork/gamecore/render/Render.java | 4 +- src/mightypork/rogue/App.java | 2 +- src/mightypork/rogue/Const.java | 2 +- src/mightypork/rogue/Res.java | 5 +- .../rogue/screens/game/WorldLayer.java | 5 +- src/mightypork/rogue/world/PlayerControl.java | 40 +++++-- .../rogue/world/entity/EntityModule.java | 2 +- .../world/entity/entities/PlayerEntity.java | 1 - .../rogue/world/gen/LevelGenerator.java | 13 +-- src/mightypork/rogue/world/gen/MapTheme.java | 3 + .../rogue/world/gen/ScratchMap.java | 10 +- .../rogue/world/gen/TileProtectLevel.java | 7 ++ .../world/gen/rooms/AbstractRectRoom.java | 99 ++++++++++++++++++ .../rogue/world/gen/rooms/BasicRoom.java | 41 ++++++++ .../rogue/world/gen/rooms/Rooms.java | 12 +++ .../rogue/world/gen/rooms/SecretRoom.java | 41 ++++++++ .../rogue/world/gen/rooms/SimpleRectRoom.java | 72 ------------- .../rogue/world/gen/themes/ThemeBrick.java | 7 ++ src/mightypork/rogue/world/gui/MapView.java | 24 +++-- .../gui/interaction/MIPClickPathfWalk.java | 36 +++++-- .../world/gui/interaction/MIPKeyWalk.java | 45 +++++--- .../world/gui/interaction/MIPMouseWalk.java | 34 ++++-- .../world/gui/interaction/MIPTileClick.java | 43 ++++++++ .../gui/interaction/MapInteractionPlugin.java | 9 +- .../world/level/render/TileRenderContext.java | 2 +- src/mightypork/rogue/world/tile/Tile.java | 18 +++- .../rogue/world/tile/TileGenData.java | 5 +- .../rogue/world/tile/TileRenderer.java | 8 +- src/mightypork/rogue/world/tile/Tiles.java | 13 ++- ...oorTileRenderer.java => DoorRenderer.java} | 4 +- .../tile/renderers/LockedDoorRenderer.java | 37 +++++++ .../world/tile/tiles/SecretDoorTile.java | 35 +++++++ 34 files changed, 515 insertions(+), 164 deletions(-) create mode 100644 src/mightypork/rogue/world/gen/TileProtectLevel.java create mode 100644 src/mightypork/rogue/world/gen/rooms/AbstractRectRoom.java create mode 100644 src/mightypork/rogue/world/gen/rooms/BasicRoom.java create mode 100644 src/mightypork/rogue/world/gen/rooms/Rooms.java create mode 100644 src/mightypork/rogue/world/gen/rooms/SecretRoom.java delete mode 100644 src/mightypork/rogue/world/gen/rooms/SimpleRectRoom.java create mode 100644 src/mightypork/rogue/world/gui/interaction/MIPTileClick.java rename src/mightypork/rogue/world/tile/renderers/{DoorTileRenderer.java => DoorRenderer.java} (88%) create mode 100644 src/mightypork/rogue/world/tile/renderers/LockedDoorRenderer.java create mode 100644 src/mightypork/rogue/world/tile/tiles/SecretDoorTile.java diff --git a/res/img/tiles16.png b/res/img/tiles16.png index 0ba62186dca892e5026a4d6945605b68e54257d3..b4adbe2e8c7c7d51b823769fcfdce0997ef0f57e 100644 GIT binary patch literal 9904 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 diff --git a/res/img/tiles16.xcf b/res/img/tiles16.xcf index f7ee4b64ce0fdc4e07c0a414bce5935be8e05f0c..cac826d4743d0661a32b1df7fe6a7fe29ebcc860 100644 GIT binary patch delta 11224 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 delta 4316 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 diff --git a/src/mightypork/gamecore/render/Render.java b/src/mightypork/gamecore/render/Render.java index 2b0e94e..e3afe42 100644 --- a/src/mightypork/gamecore/render/Render.java +++ b/src/mightypork/gamecore/render/Render.java @@ -455,7 +455,9 @@ public class Render { final RectDigest q = quad.digest(); final RectDigest u = txquad.uvs.digest(); - double tL = u.left, tR = u.right, tT = u.top, tB = u.bottom; + final double offs = 0.0001;// hack to avoid white stitching + + double tL = u.left + offs, tR = u.right - offs, tT = u.top + offs, tB = u.bottom - offs; // handle flip if (txquad.isFlippedY()) { diff --git a/src/mightypork/rogue/App.java b/src/mightypork/rogue/App.java index d7c44bf..e0b776e 100644 --- a/src/mightypork/rogue/App.java +++ b/src/mightypork/rogue/App.java @@ -160,7 +160,7 @@ public final class App extends BaseApp { protected void postInit() { // TODO tmp - WorldProvider.get().createWorld(42); + WorldProvider.get().createWorld(Double.doubleToLongBits(Math.random())); getEventBus().send(new CrossfadeRequest("menu", true)); } diff --git a/src/mightypork/rogue/Const.java b/src/mightypork/rogue/Const.java index 1fc5ec9..f6806f9 100644 --- a/src/mightypork/rogue/Const.java +++ b/src/mightypork/rogue/Const.java @@ -15,7 +15,7 @@ public final class Const { public static final String TITLEBAR = APP_NAME + " v." + VERSION; // AUDIO - public static final int FPS_RENDER = 100; // max + public static final int FPS_RENDER = 120; // max // INITIAL WINDOW SIZE public static final int WINDOW_W = 800; diff --git a/src/mightypork/rogue/Res.java b/src/mightypork/rogue/Res.java index 81426d6..1318865 100644 --- a/src/mightypork/rogue/Res.java +++ b/src/mightypork/rogue/Res.java @@ -85,12 +85,13 @@ public final class Res { tiles = texture.grid(8, 8); textures.addSheet("tile.brick.floor", tiles.makeSheet(0, 1, 5, 1)); - textures.addSheet("tile.brick.wall", tiles.makeSheet(0, 0, 5, 1)); + textures.addSheet("tile.brick.wall", tiles.makeSheet(0, 0, 8, 1)); textures.addSheet("tile.brick.door.closed", tiles.makeSheet(1, 2, 1, 1)); textures.addSheet("tile.brick.door.open", tiles.makeSheet(2, 2, 1, 1)); + textures.addSheet("tile.brick.door.secret", tiles.makeSheet(0, 3, 2, 1)); - textures.addSheet("tile.brick.passage", tiles.makeSheet(3, 2, 2, 1)); + textures.addSheet("tile.brick.passage", tiles.makeSheet(3, 2, 4, 1)); textures.addQuad("tile.shadow.n", tiles.makeQuad(0, 7)); textures.addQuad("tile.shadow.s", tiles.makeQuad(0, 7).flipY()); diff --git a/src/mightypork/rogue/screens/game/WorldLayer.java b/src/mightypork/rogue/screens/game/WorldLayer.java index 3ca2ba7..5bfd500 100644 --- a/src/mightypork/rogue/screens/game/WorldLayer.java +++ b/src/mightypork/rogue/screens/game/WorldLayer.java @@ -7,6 +7,8 @@ import mightypork.gamecore.util.math.constraints.num.Num; import mightypork.rogue.world.gui.MapView; import mightypork.rogue.world.gui.interaction.MIPClickPathfWalk; import mightypork.rogue.world.gui.interaction.MIPKeyWalk; +import mightypork.rogue.world.gui.interaction.MIPMouseWalk; +import mightypork.rogue.world.gui.interaction.MIPTileClick; public class WorldLayer extends ScreenLayer { @@ -24,8 +26,9 @@ public class WorldLayer extends ScreenLayer { // map input plugins worldView.addPlugin(new MIPKeyWalk()); + worldView.addPlugin(new MIPTileClick()); worldView.addPlugin(new MIPClickPathfWalk()); - //worldView.addPlugin(new MIPMouseWalk()); + worldView.addPlugin(new MIPMouseWalk()); // size of lower navbar final Num lownav = root.width().min(root.height()).max(700).perc(7); diff --git a/src/mightypork/rogue/world/PlayerControl.java b/src/mightypork/rogue/world/PlayerControl.java index 7c53be1..e9add8c 100644 --- a/src/mightypork/rogue/world/PlayerControl.java +++ b/src/mightypork/rogue/world/PlayerControl.java @@ -48,34 +48,31 @@ public abstract class PlayerControl { public void goNorth() { - getPlayerEntity().pos.cancelPath(); - getPlayerEntity().pos.addStep(Step.NORTH); + go(Step.NORTH); } public void goSouth() { - getPlayerEntity().pos.cancelPath(); - getPlayerEntity().pos.addStep(Step.SOUTH); + go(Step.SOUTH); } public void goEast() { - getPlayerEntity().pos.cancelPath(); - getPlayerEntity().pos.addStep(Step.EAST); + go(Step.EAST); } public void goWest() { - getPlayerEntity().pos.cancelPath(); - getPlayerEntity().pos.addStep(Step.WEST); + go(Step.WEST); } public void navigateTo(Coord pos) { + if (!getLevel().getTile(pos).isExplored()) return; getPlayerEntity().pos.navigateTo(pos); } @@ -99,4 +96,31 @@ public abstract class PlayerControl { { return getPlayerEntity().pos.getCoord(); } + + + public boolean canGo(Step side) + { + return getLevel().getTile(getCoord().add(side)).isWalkable(); + } + + + public boolean clickTile(Step side) + { + return clickTile(getCoord().add(side)); + } + + + public boolean clickTile(Coord pos) + { + if (pos.dist(getCoord()) > 8) return false; // too far + + return getLevel().getTile(pos).onClick(); + } + + + public void go(Step side) + { + getPlayerEntity().pos.cancelPath(); + getPlayerEntity().pos.addStep(side); + } } diff --git a/src/mightypork/rogue/world/entity/EntityModule.java b/src/mightypork/rogue/world/entity/EntityModule.java index 8abc83a..db8c3ed 100644 --- a/src/mightypork/rogue/world/entity/EntityModule.java +++ b/src/mightypork/rogue/world/entity/EntityModule.java @@ -18,7 +18,7 @@ import mightypork.gamecore.util.ion.IonObjBundled; public abstract class EntityModule implements IonObjBundled, Updateable { protected final Entity entity; - protected final Random rand = new Random(); + protected static final Random rand = new Random(); public EntityModule(Entity entity) diff --git a/src/mightypork/rogue/world/entity/entities/PlayerEntity.java b/src/mightypork/rogue/world/entity/entities/PlayerEntity.java index 9be40e8..de8c817 100644 --- a/src/mightypork/rogue/world/entity/entities/PlayerEntity.java +++ b/src/mightypork/rogue/world/entity/entities/PlayerEntity.java @@ -110,5 +110,4 @@ public class PlayerEntity extends Entity { { return EntityType.PLAYER; } - } diff --git a/src/mightypork/rogue/world/gen/LevelGenerator.java b/src/mightypork/rogue/world/gen/LevelGenerator.java index 9e79b35..3bb7724 100644 --- a/src/mightypork/rogue/world/gen/LevelGenerator.java +++ b/src/mightypork/rogue/world/gen/LevelGenerator.java @@ -4,8 +4,7 @@ package mightypork.rogue.world.gen; import java.util.Random; import mightypork.gamecore.util.math.algo.Coord; -import mightypork.rogue.world.gen.rooms.DeadEndRoom; -import mightypork.rogue.world.gen.rooms.SimpleRectRoom; +import mightypork.rogue.world.gen.rooms.Rooms; import mightypork.rogue.world.gen.themes.ThemeBrick; import mightypork.rogue.world.level.Level; @@ -14,9 +13,6 @@ public class LevelGenerator { public static final MapTheme DUNGEON_THEME = new ThemeBrick(); - public static final RoomBuilder ROOM_SQUARE = new SimpleRectRoom(); - private static final RoomBuilder DEAD_END = new DeadEndRoom(); - public static Level build(long seed, int complexity, MapTheme theme) { @@ -27,11 +23,12 @@ public class LevelGenerator { final ScratchMap map = new ScratchMap(max_size, theme, rand); // start - map.addRoom(ROOM_SQUARE); + map.addRoom(Rooms.BASIC); for (int i = 0; i < 2 + complexity / 2 + rand.nextInt((int) (1 + complexity * 0.3)); i++) { - map.addRoom(ROOM_SQUARE); - if (rand.nextInt(6) > 0) map.addRoom(DEAD_END); + map.addRoom(Rooms.BASIC); + if (rand.nextInt(7) > 0) map.addRoom(Rooms.SECRET); + if (rand.nextInt(6) > 0) map.addRoom(Rooms.DEAD_END); } map.buildCorridors(); diff --git a/src/mightypork/rogue/world/gen/MapTheme.java b/src/mightypork/rogue/world/gen/MapTheme.java index dda3a80..e3704e1 100644 --- a/src/mightypork/rogue/world/gen/MapTheme.java +++ b/src/mightypork/rogue/world/gen/MapTheme.java @@ -21,4 +21,7 @@ public interface MapTheme { TileModel passage(); + + + TileModel secretDoor(); } diff --git a/src/mightypork/rogue/world/gen/ScratchMap.java b/src/mightypork/rogue/world/gen/ScratchMap.java index 2f94705..31a37fe 100644 --- a/src/mightypork/rogue/world/gen/ScratchMap.java +++ b/src/mightypork/rogue/world/gen/ScratchMap.java @@ -42,7 +42,9 @@ public class ScratchMap { @Override public boolean isAccessible(Coord pos) { - return isIn(pos); // suffices for now + if (!isIn(pos)) return false; + final Tile t = get(pos); + return t.isPotentiallyWalkable() || (t.genData.protection != TileProtectLevel.STRONG); } @@ -63,7 +65,7 @@ public class ScratchMap { return 20; case WALL: - if (t.genData.isProtected) return 2000; + if (t.genData.protection != TileProtectLevel.NONE) return 2000; return 100; @@ -220,14 +222,14 @@ public class ScratchMap { } - public void protect(Coord min, Coord max) + public void protect(Coord min, Coord max, TileProtectLevel prot) { if (!isIn(min) || !isIn(max)) throw new IndexOutOfBoundsException("Tile(s) not in map: " + min + " , " + max); final Coord c = Coord.make(0, 0); for (c.x = min.x; c.x <= max.x; c.x++) for (c.y = min.y; c.y <= max.y; c.y++) - get(c).genData.isProtected = true; + get(c).genData.protection = prot; } diff --git a/src/mightypork/rogue/world/gen/TileProtectLevel.java b/src/mightypork/rogue/world/gen/TileProtectLevel.java new file mode 100644 index 0000000..65d58f8 --- /dev/null +++ b/src/mightypork/rogue/world/gen/TileProtectLevel.java @@ -0,0 +1,7 @@ +package mightypork.rogue.world.gen; + + +public enum TileProtectLevel +{ + NONE, WEAK, STRONG; +} diff --git a/src/mightypork/rogue/world/gen/rooms/AbstractRectRoom.java b/src/mightypork/rogue/world/gen/rooms/AbstractRectRoom.java new file mode 100644 index 0000000..3a20fcd --- /dev/null +++ b/src/mightypork/rogue/world/gen/rooms/AbstractRectRoom.java @@ -0,0 +1,99 @@ +package mightypork.rogue.world.gen.rooms; + + +import java.util.Random; + +import mightypork.gamecore.util.annot.DefaultImpl; +import mightypork.gamecore.util.math.algo.Coord; +import mightypork.gamecore.util.math.algo.Sides; +import mightypork.rogue.world.gen.MapTheme; +import mightypork.rogue.world.gen.RoomBuilder; +import mightypork.rogue.world.gen.RoomDesc; +import mightypork.rogue.world.gen.ScratchMap; +import mightypork.rogue.world.gen.TileProtectLevel; +import mightypork.rogue.world.tile.TileModel; + + +public abstract class AbstractRectRoom implements RoomBuilder { + + @Override + public RoomDesc buildToFit(ScratchMap map, MapTheme theme, Random rand, Coord center) + { + // half width, half height actually + final Coord innerSize = getInnerSize(rand); + final int width = 2 + innerSize.x; + final int height = 2 + innerSize.y; + + final int wLow = width / 2; + final int wHigh = width - wLow; + final int hLow = height / 2; + final int hHigh = height - hLow; + + final Coord min = new Coord(center.x - wLow, center.y - hLow); + final Coord max = new Coord(center.x + wHigh, center.y + hHigh); + + if (!map.isClear(min.add(-1, -1), max)) return null; + + map.fill(min, max, theme.floor()); + map.border(min, max, theme.wall()); + map.protect(min, max, getWallProtectionLevel()); + + placeDoors(map, theme, rand, min, max); + + buildExtras(map, theme, rand, min, max); + + return new RoomDesc(min.add(-1, -1), max); + } + + + protected void placeDoors(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max) + { + final int width = max.x - min.x; + final int height = max.y - min.y; + + for (int i = 0, j = 0; i <= getDoorCount(rand) && j < 100; j++) { // j is to prevent inf loop + final Coord door = min.copy(); + switch (rand.nextInt(4)) { + case 0: + door.y = min.y; + door.x += 1 + rand.nextInt(width - 1); + break; + case 1: + door.y = max.y; + door.x += 1 + rand.nextInt(width - 1); + break; + case 2: + door.x = min.x; + door.y += 1 + rand.nextInt(height - 1); + break; + case 3: + door.x = max.x; + door.y += 1 + rand.nextInt(height - 1); + break; + } + + if ((map.findDoors(door) & Sides.CARDINAL) == 0) { + map.set(door, getDoorType(theme, rand)); + i++; // increment pointer + } + } + } + + + @DefaultImpl + protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max) + { + } + + + protected abstract Coord getInnerSize(Random rand); + + + protected abstract TileProtectLevel getWallProtectionLevel(); + + + protected abstract TileModel getDoorType(MapTheme theme, Random rand); + + + protected abstract int getDoorCount(Random rand); +} diff --git a/src/mightypork/rogue/world/gen/rooms/BasicRoom.java b/src/mightypork/rogue/world/gen/rooms/BasicRoom.java new file mode 100644 index 0000000..2a4a9e0 --- /dev/null +++ b/src/mightypork/rogue/world/gen/rooms/BasicRoom.java @@ -0,0 +1,41 @@ +package mightypork.rogue.world.gen.rooms; + + +import java.util.Random; + +import mightypork.gamecore.util.math.algo.Coord; +import mightypork.rogue.world.gen.MapTheme; +import mightypork.rogue.world.gen.TileProtectLevel; +import mightypork.rogue.world.tile.TileModel; + + +public class BasicRoom extends AbstractRectRoom { + + + @Override + protected TileModel getDoorType(MapTheme theme, Random rand) + { + return rand.nextInt(4) == 0 ? theme.passage() : theme.door(); + } + + + @Override + protected int getDoorCount(Random rand) + { + return 1 + rand.nextInt(5); + } + + + @Override + protected TileProtectLevel getWallProtectionLevel() + { + return TileProtectLevel.WEAK; + } + + + @Override + protected Coord getInnerSize(Random rand) + { + return Coord.make(3 + rand.nextInt(3), 3 + rand.nextInt(3)); + } +} diff --git a/src/mightypork/rogue/world/gen/rooms/Rooms.java b/src/mightypork/rogue/world/gen/rooms/Rooms.java new file mode 100644 index 0000000..1a3add8 --- /dev/null +++ b/src/mightypork/rogue/world/gen/rooms/Rooms.java @@ -0,0 +1,12 @@ +package mightypork.rogue.world.gen.rooms; + + +import mightypork.rogue.world.gen.RoomBuilder; + + +public class Rooms { + + public static final RoomBuilder BASIC = new BasicRoom(); + public static final RoomBuilder SECRET = new SecretRoom(); + public static final RoomBuilder DEAD_END = new DeadEndRoom(); +} diff --git a/src/mightypork/rogue/world/gen/rooms/SecretRoom.java b/src/mightypork/rogue/world/gen/rooms/SecretRoom.java new file mode 100644 index 0000000..d024ccc --- /dev/null +++ b/src/mightypork/rogue/world/gen/rooms/SecretRoom.java @@ -0,0 +1,41 @@ +package mightypork.rogue.world.gen.rooms; + + +import java.util.Random; + +import mightypork.gamecore.util.math.algo.Coord; +import mightypork.rogue.world.gen.MapTheme; +import mightypork.rogue.world.gen.TileProtectLevel; +import mightypork.rogue.world.tile.TileModel; + + +public class SecretRoom extends AbstractRectRoom { + + + @Override + protected TileModel getDoorType(MapTheme theme, Random rand) + { + return theme.secretDoor(); + } + + + @Override + protected int getDoorCount(Random rand) + { + return 1 + rand.nextInt(2); + } + + + @Override + protected TileProtectLevel getWallProtectionLevel() + { + return TileProtectLevel.STRONG; + } + + + @Override + protected Coord getInnerSize(Random rand) + { + return Coord.make(3 + rand.nextInt(2), 3 + rand.nextInt(2)); + } +} diff --git a/src/mightypork/rogue/world/gen/rooms/SimpleRectRoom.java b/src/mightypork/rogue/world/gen/rooms/SimpleRectRoom.java deleted file mode 100644 index 1ccc9a3..0000000 --- a/src/mightypork/rogue/world/gen/rooms/SimpleRectRoom.java +++ /dev/null @@ -1,72 +0,0 @@ -package mightypork.rogue.world.gen.rooms; - - -import java.util.Random; - -import mightypork.gamecore.util.math.algo.Coord; -import mightypork.gamecore.util.math.algo.Sides; -import mightypork.rogue.world.gen.MapTheme; -import mightypork.rogue.world.gen.RoomBuilder; -import mightypork.rogue.world.gen.RoomDesc; -import mightypork.rogue.world.gen.ScratchMap; -import mightypork.rogue.world.tile.TileModel; - - -public class SimpleRectRoom implements RoomBuilder { - - @Override - public RoomDesc buildToFit(ScratchMap map, MapTheme theme, Random rand, Coord center) - { - // half width, half height actually - final int width = 2 + rand.nextInt(2); - final int height = 2 + rand.nextInt(2); - - final Coord min = new Coord(center.x - width, center.y - height); - final Coord max = new Coord(center.x + width, center.y + height); - - if (!map.isClear(min, max)) return null; - - map.fill(min, max, theme.floor()); - map.border(min, max, theme.wall()); - map.protect(min, max); - - final boolean holes = rand.nextInt(4) == 0; - - for (int i = 0; i <= 2 + rand.nextInt(6); i++) { - final Coord door = min.copy(); - switch (rand.nextInt(4)) { - case 0: - door.y = min.y; - door.x += 1 + rand.nextInt((width - 1) * 2); - break; - case 1: - door.y = max.y; - door.x += 1 + rand.nextInt((width - 1) * 2); - break; - case 2: - door.x = min.x; - door.y += 1 + rand.nextInt((height - 1) * 2); - break; - case 3: - door.x = max.x; - door.y += 1 + rand.nextInt((height - 1) * 2); - break; - } - - if ((map.findDoors(door) & Sides.CARDINAL) == 0) { - TileModel placed; - switch (rand.nextInt(8)) { - case 0: - case 1: - placed = theme.passage(); - break; - default: - placed = holes ? theme.floor() : theme.door(); - } - map.set(door, placed); - } - } - - return new RoomDesc(min.add(-1, -1), max); - } -} diff --git a/src/mightypork/rogue/world/gen/themes/ThemeBrick.java b/src/mightypork/rogue/world/gen/themes/ThemeBrick.java index fa11bb8..1695c10 100644 --- a/src/mightypork/rogue/world/gen/themes/ThemeBrick.java +++ b/src/mightypork/rogue/world/gen/themes/ThemeBrick.java @@ -35,4 +35,11 @@ public class ThemeBrick implements MapTheme { { return Tiles.BRICK_PASSAGE; } + + + @Override + public TileModel secretDoor() + { + return Tiles.BRICK_HIDDEN_DOOR; + } } diff --git a/src/mightypork/rogue/world/gui/MapView.java b/src/mightypork/rogue/world/gui/MapView.java index bd8325b..f4516ad 100644 --- a/src/mightypork/rogue/world/gui/MapView.java +++ b/src/mightypork/rogue/world/gui/MapView.java @@ -1,7 +1,7 @@ package mightypork.rogue.world.gui; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; import mightypork.gamecore.eventbus.events.Updateable; @@ -33,7 +33,7 @@ public class MapView extends InputComponent implements KeyListener, MouseButtonL protected final WorldRenderer worldRenderer; private final PlayerControl pc; - private final Set plugins = new HashSet<>(); + private final Set plugins = new LinkedHashSet<>(); private final NumAnimated zoom = new NumAnimated(0, Easing.SINE_BOTH); private final Num tileSize; @@ -71,7 +71,7 @@ public class MapView extends InputComponent implements KeyListener, MouseButtonL public void onStepFinished() { for (final MapInteractionPlugin p : plugins) { - p.onStepEnd(this, pc); + if (p.onStepEnd(this, pc)) break; } } @@ -80,7 +80,7 @@ public class MapView extends InputComponent implements KeyListener, MouseButtonL public void onPathFinished() { for (final MapInteractionPlugin p : plugins) { - p.onStepEnd(this, pc); + if (p.onStepEnd(this, pc)) break; } } @@ -89,7 +89,7 @@ public class MapView extends InputComponent implements KeyListener, MouseButtonL public void onPathInterrupted() { for (final MapInteractionPlugin p : plugins) { - p.onStepEnd(this, pc); + if (p.onStepEnd(this, pc)) break; } } @@ -100,10 +100,11 @@ public class MapView extends InputComponent implements KeyListener, MouseButtonL if (!event.isOver(this)) return; for (final MapInteractionPlugin p : plugins) { - p.onClick(this, pc, event.getPos(), event.getButton(), event.isDown()); + if (p.onClick(this, pc, event.getPos(), event.getButton(), event.isDown())) { + event.consume(); + break; + } } - - event.consume(); // only our clicks. } @@ -111,7 +112,7 @@ public class MapView extends InputComponent implements KeyListener, MouseButtonL public void receive(KeyEvent event) { for (final MapInteractionPlugin p : plugins) { - p.onKey(this, pc, event.getKey(), event.isDown()); + if (p.onKey(this, pc, event.getKey(), event.isDown())) break; } if (event.getKey() == Keys.Z) { @@ -120,8 +121,6 @@ public class MapView extends InputComponent implements KeyListener, MouseButtonL } else { zoom.fadeOut(1); } - - } // don't consume key events, can be useful for others. @@ -142,6 +141,9 @@ public class MapView extends InputComponent implements KeyListener, MouseButtonL @Override public void update(double delta) { + for (final MapInteractionPlugin p : plugins) { + p.update(this, pc, delta); + } zoom.update(delta); } } diff --git a/src/mightypork/rogue/world/gui/interaction/MIPClickPathfWalk.java b/src/mightypork/rogue/world/gui/interaction/MIPClickPathfWalk.java index f142020..fb3157c 100644 --- a/src/mightypork/rogue/world/gui/interaction/MIPClickPathfWalk.java +++ b/src/mightypork/rogue/world/gui/interaction/MIPClickPathfWalk.java @@ -1,11 +1,11 @@ package mightypork.rogue.world.gui.interaction; -import mightypork.gamecore.input.InputSystem; import mightypork.gamecore.util.math.algo.Coord; import mightypork.gamecore.util.math.constraints.vect.Vect; import mightypork.rogue.world.PlayerControl; import mightypork.rogue.world.gui.MapView; +import mightypork.rogue.world.tile.Tile; public class MIPClickPathfWalk implements MapInteractionPlugin { @@ -14,29 +14,47 @@ public class MIPClickPathfWalk implements MapInteractionPlugin { @Override - public void onStepEnd(MapView view, PlayerControl player) + public void update(MapView view, PlayerControl player, double delta) { - if (InputSystem.isMouseButtonDown(BTN)) { - final Coord clicked = view.toWorldPos(InputSystem.getMousePos()); - player.navigateTo(clicked); - } +// if (InputSystem.isMouseButtonDown(BTN)) { +// +// troToNav(view, player, InputSystem.getMousePos()); +// } } @Override - public void onClick(MapView view, PlayerControl player, Vect mouse, int button, boolean down) + public boolean onClick(MapView view, PlayerControl player, Vect mouse, int button, boolean down) { - if (!down || button != BTN) return; + if (down || button != BTN) return false; + return troToNav(view, player, mouse); + } + + + private boolean troToNav(MapView view, PlayerControl player, Vect mouse) + { final Coord clicked = view.toWorldPos(mouse); + final Tile t = player.getLevel().getTile(clicked); + if (!t.isWalkable() || !t.isExplored()) return false; + player.navigateTo(clicked); + return true; + } + + + @Override + public boolean onKey(MapView view, PlayerControl player, int key, boolean down) + { + return false; } @Override - public void onKey(MapView view, PlayerControl player, int key, boolean down) + public boolean onStepEnd(MapView mapView, PlayerControl player) { + return false; } } diff --git a/src/mightypork/rogue/world/gui/interaction/MIPKeyWalk.java b/src/mightypork/rogue/world/gui/interaction/MIPKeyWalk.java index 14bb129..695fdd9 100644 --- a/src/mightypork/rogue/world/gui/interaction/MIPKeyWalk.java +++ b/src/mightypork/rogue/world/gui/interaction/MIPKeyWalk.java @@ -3,6 +3,8 @@ package mightypork.rogue.world.gui.interaction; import mightypork.gamecore.input.InputSystem; import mightypork.gamecore.input.Keys; +import mightypork.gamecore.util.math.algo.Sides; +import mightypork.gamecore.util.math.algo.Step; import mightypork.gamecore.util.math.constraints.vect.Vect; import mightypork.rogue.world.PlayerControl; import mightypork.rogue.world.gui.MapView; @@ -11,36 +13,51 @@ import mightypork.rogue.world.gui.MapView; public class MIPKeyWalk implements MapInteractionPlugin { @Override - public void onStepEnd(MapView view, PlayerControl player) + public boolean onStepEnd(MapView view, PlayerControl player) { - walkByKey(player); + return walkByKey(player); } @Override - public void onClick(MapView view, PlayerControl player, Vect mouse, int button, boolean down) + public boolean onClick(MapView view, PlayerControl player, Vect mouse, int button, boolean down) { + return false; } @Override - public void onKey(MapView view, PlayerControl player, int key, boolean down) + public boolean onKey(MapView view, PlayerControl player, int key, boolean down) { - if (down) walkByKey(player); + if (down) return walkByKey(player); + return false; } - private void walkByKey(PlayerControl player) + private boolean walkByKey(PlayerControl player) { - if (InputSystem.isKeyDown(Keys.LEFT)) { - player.goWest(); - } else if (InputSystem.isKeyDown(Keys.RIGHT)) { - player.goEast(); - } else if (InputSystem.isKeyDown(Keys.UP)) { - player.goNorth(); - } else if (InputSystem.isKeyDown(Keys.DOWN)) { - player.goSouth(); + final int[] keys = { Keys.LEFT, Keys.RIGHT, Keys.UP, Keys.DOWN }; + final Step[] sides = { Sides.W, Sides.E, Sides.N, Sides.S }; + + for (int i = 0; i < 4; i++) { + if (InputSystem.isKeyDown(keys[i])) { + + final Step side = sides[i]; + if (player.canGo(side)) { + player.go(side); + return true; + } else { + return player.clickTile(side); + } + + } } + return false; } + + @Override + public void update(MapView mapView, PlayerControl pc, double delta) + { + } } diff --git a/src/mightypork/rogue/world/gui/interaction/MIPMouseWalk.java b/src/mightypork/rogue/world/gui/interaction/MIPMouseWalk.java index 2cec069..0a291e3 100644 --- a/src/mightypork/rogue/world/gui/interaction/MIPMouseWalk.java +++ b/src/mightypork/rogue/world/gui/interaction/MIPMouseWalk.java @@ -8,12 +8,13 @@ import mightypork.gamecore.util.math.algo.Coord; import mightypork.gamecore.util.math.constraints.vect.Vect; import mightypork.rogue.world.PlayerControl; import mightypork.rogue.world.gui.MapView; +import mightypork.rogue.world.tile.Tile; public class MIPMouseWalk implements MapInteractionPlugin { @Override - public void onStepEnd(MapView world, PlayerControl player) + public void update(MapView world, PlayerControl player, double delta) { if (InputSystem.isMouseButtonDown(0)) { // walk by holding btn @@ -23,13 +24,15 @@ public class MIPMouseWalk implements MapInteractionPlugin { @Override - public void onClick(MapView world, PlayerControl player, Vect mouse, int button, boolean down) + public boolean onClick(MapView world, PlayerControl player, Vect mouse, int button, boolean down) { - if (!down) return; + if (!down || button != 0) return false; final Coord plpos = player.getCoord(); final Coord clicked = world.toWorldPos(mouse); + final Tile t = player.getLevel().getTile(clicked); + final Polar p = Polar.fromCoord(clicked.x - plpos.x, clicked.y - plpos.y); final int dir = Deg.round90(p.getAngleDeg()) / 90; @@ -37,26 +40,35 @@ public class MIPMouseWalk implements MapInteractionPlugin { switch (dir) { case 0: player.goEast(); - return; - + break; + case 1: player.goSouth(); - return; - + break; + case 2: player.goWest(); - return; - + break; + case 3: player.goNorth(); - return; + break; } + return true; + } + + + @Override + public boolean onKey(MapView world, PlayerControl player, int key, boolean down) + { + return false; } @Override - public void onKey(MapView world, PlayerControl player, int key, boolean down) + public boolean onStepEnd(MapView mapView, PlayerControl player) { + return false; } } diff --git a/src/mightypork/rogue/world/gui/interaction/MIPTileClick.java b/src/mightypork/rogue/world/gui/interaction/MIPTileClick.java new file mode 100644 index 0000000..64ff2f4 --- /dev/null +++ b/src/mightypork/rogue/world/gui/interaction/MIPTileClick.java @@ -0,0 +1,43 @@ +package mightypork.rogue.world.gui.interaction; + + +import mightypork.gamecore.util.math.algo.Coord; +import mightypork.gamecore.util.math.constraints.vect.Vect; +import mightypork.rogue.world.PlayerControl; +import mightypork.rogue.world.gui.MapView; + + +public class MIPTileClick implements MapInteractionPlugin { + + @Override + public boolean onStepEnd(MapView wv, PlayerControl player) + { + return false; + } + + + @Override + public boolean onClick(MapView wv, PlayerControl player, Vect mouse, int button, boolean down) + { + if (down && button == 1) { // right button + final Coord pos = wv.toWorldPos(mouse); + player.clickTile(pos); + return true; + } + + return false; + } + + + @Override + public boolean onKey(MapView wv, PlayerControl player, int key, boolean down) + { + return false; + } + + + @Override + public void update(MapView mapView, PlayerControl pc, double delta) + { + } +} diff --git a/src/mightypork/rogue/world/gui/interaction/MapInteractionPlugin.java b/src/mightypork/rogue/world/gui/interaction/MapInteractionPlugin.java index 588f257..041d290 100644 --- a/src/mightypork/rogue/world/gui/interaction/MapInteractionPlugin.java +++ b/src/mightypork/rogue/world/gui/interaction/MapInteractionPlugin.java @@ -8,12 +8,15 @@ import mightypork.rogue.world.gui.MapView; public interface MapInteractionPlugin { - void onStepEnd(MapView wv, PlayerControl player); + boolean onStepEnd(MapView mapView, PlayerControl player); - void onClick(MapView wv, PlayerControl player, Vect mouse, int button, boolean down); + boolean onClick(MapView mapView, PlayerControl player, Vect mouse, int button, boolean down); - void onKey(MapView wv, PlayerControl player, int key, boolean down); + boolean onKey(MapView mapView, PlayerControl player, int key, boolean down); + + + void update(MapView mapView, PlayerControl pc, double delta); } diff --git a/src/mightypork/rogue/world/level/render/TileRenderContext.java b/src/mightypork/rogue/world/level/render/TileRenderContext.java index 23c8ad1..5d2c2cf 100644 --- a/src/mightypork/rogue/world/level/render/TileRenderContext.java +++ b/src/mightypork/rogue/world/level/render/TileRenderContext.java @@ -25,7 +25,7 @@ public final class TileRenderContext extends MapRenderContext implements RectBou { super(map, drawArea); - //this.tiler.setOverlap(0.02); // avoid gaps (rounding error?) + //this.tiler.setOverlap(0.002); // avoid gaps (rounding error?) this.noise = map.getNoiseGen(); } diff --git a/src/mightypork/rogue/world/tile/Tile.java b/src/mightypork/rogue/world/tile/Tile.java index ca4cccb..b1aab25 100644 --- a/src/mightypork/rogue/world/tile/Tile.java +++ b/src/mightypork/rogue/world/tile/Tile.java @@ -2,6 +2,7 @@ package mightypork.rogue.world.tile; import java.io.IOException; +import java.util.Random; import mightypork.gamecore.util.annot.DefaultImpl; import mightypork.gamecore.util.ion.IonInput; @@ -24,6 +25,9 @@ public abstract class Tile implements IonObjBlob { public final TileRenderData renderData = new TileRenderData(); public final TileGenData genData = new TileGenData(); + /** RNG for random stuff in tiles */ + protected static final Random rand = new Random(); + protected final TileRenderer renderer; public final TileModel model; @@ -168,7 +172,7 @@ public abstract class Tile implements IonObjBlob { } - public final Color getMapColor() + public Color getMapColor() { return getType().getMapColor(); } @@ -202,4 +206,16 @@ public abstract class Tile implements IonObjBlob { */ public abstract boolean hasItem(); + + /** + * Handle player click + * + * @return true if the tile is interactive and did something. + */ + @DefaultImpl + public boolean onClick() + { + return false; + } + } diff --git a/src/mightypork/rogue/world/tile/TileGenData.java b/src/mightypork/rogue/world/tile/TileGenData.java index 1f1e4ad..1270596 100644 --- a/src/mightypork/rogue/world/tile/TileGenData.java +++ b/src/mightypork/rogue/world/tile/TileGenData.java @@ -1,6 +1,9 @@ package mightypork.rogue.world.tile; +import mightypork.rogue.world.gen.TileProtectLevel; + + /** * Data storage for world generator * @@ -8,6 +11,6 @@ package mightypork.rogue.world.tile; */ public class TileGenData { - public boolean isProtected = false; + public TileProtectLevel protection = TileProtectLevel.NONE; } diff --git a/src/mightypork/rogue/world/tile/TileRenderer.java b/src/mightypork/rogue/world/tile/TileRenderer.java index 179ceea..904daa9 100644 --- a/src/mightypork/rogue/world/tile/TileRenderer.java +++ b/src/mightypork/rogue/world/tile/TileRenderer.java @@ -72,16 +72,16 @@ public abstract class TileRenderer { if (trd.shadows == 0) return; final Rect rect = context.getRect(); - if ((trd.shadows & Sides.MASK_NW) != 0) Render.quadTextured(rect, SH_NW); + if ((trd.shadows & Sides.NW_CORNER) == Sides.MASK_NW) Render.quadTextured(rect, SH_NW); if ((trd.shadows & Sides.MASK_N) != 0) Render.quadTextured(rect, SH_N); - if ((trd.shadows & Sides.MASK_NE) != 0) Render.quadTextured(rect, SH_NE); + if ((trd.shadows & Sides.NE_CORNER) == Sides.MASK_NE) Render.quadTextured(rect, SH_NE); if ((trd.shadows & Sides.MASK_W) != 0) Render.quadTextured(rect, SH_W); if ((trd.shadows & Sides.MASK_E) != 0) Render.quadTextured(rect, SH_E); - if ((trd.shadows & Sides.MASK_SW) != 0) Render.quadTextured(rect, SH_SW); + if ((trd.shadows & Sides.SW_CORNER) == Sides.MASK_SW) Render.quadTextured(rect, SH_SW); if ((trd.shadows & Sides.MASK_S) != 0) Render.quadTextured(rect, SH_S); - if ((trd.shadows & Sides.MASK_SE) != 0) Render.quadTextured(rect, SH_SE); + if ((trd.shadows & Sides.SE_CORNER) == Sides.MASK_SE) Render.quadTextured(rect, SH_SE); } diff --git a/src/mightypork/rogue/world/tile/Tiles.java b/src/mightypork/rogue/world/tile/Tiles.java index 0d2786b..96d14a8 100644 --- a/src/mightypork/rogue/world/tile/Tiles.java +++ b/src/mightypork/rogue/world/tile/Tiles.java @@ -6,13 +6,10 @@ import java.io.IOException; import mightypork.gamecore.util.ion.IonInput; import mightypork.gamecore.util.ion.IonOutput; import mightypork.rogue.world.tile.renderers.BasicTileRenderer; -import mightypork.rogue.world.tile.renderers.DoorTileRenderer; +import mightypork.rogue.world.tile.renderers.DoorRenderer; +import mightypork.rogue.world.tile.renderers.LockedDoorRenderer; import mightypork.rogue.world.tile.renderers.NullTileRenderer; -import mightypork.rogue.world.tile.tiles.DoorTile; -import mightypork.rogue.world.tile.tiles.FloorTile; -import mightypork.rogue.world.tile.tiles.NullTile; -import mightypork.rogue.world.tile.tiles.WallPassageTile; -import mightypork.rogue.world.tile.tiles.WallTile; +import mightypork.rogue.world.tile.tiles.*; /** @@ -28,8 +25,10 @@ public final class Tiles { public static final TileModel BRICK_FLOOR = new TileModel(10, FloorTile.class, new BasicTileRenderer("tile.brick.floor")); public static final TileModel BRICK_WALL = new TileModel(11, WallTile.class, new BasicTileRenderer("tile.brick.wall")); - public static final TileModel BRICK_DOOR = new TileModel(12, DoorTile.class, new DoorTileRenderer("tile.brick.door.closed", "tile.brick.door.open")); + public static final TileModel BRICK_DOOR = new TileModel(12, DoorTile.class, new DoorRenderer("tile.brick.door.closed", "tile.brick.door.open")); public static final TileModel BRICK_PASSAGE = new TileModel(13, WallPassageTile.class, new BasicTileRenderer("tile.brick.passage")); + public static final TileModel BRICK_HIDDEN_DOOR = new TileModel(14, SecretDoorTile.class, new LockedDoorRenderer("tile.brick.door.secret", + "tile.brick.door.closed", "tile.brick.door.open")); public static void register(int id, TileModel model) diff --git a/src/mightypork/rogue/world/tile/renderers/DoorTileRenderer.java b/src/mightypork/rogue/world/tile/renderers/DoorRenderer.java similarity index 88% rename from src/mightypork/rogue/world/tile/renderers/DoorTileRenderer.java rename to src/mightypork/rogue/world/tile/renderers/DoorRenderer.java index a009582..9d3bbb1 100644 --- a/src/mightypork/rogue/world/tile/renderers/DoorTileRenderer.java +++ b/src/mightypork/rogue/world/tile/renderers/DoorRenderer.java @@ -10,13 +10,13 @@ import mightypork.rogue.world.tile.Tile; import mightypork.rogue.world.tile.TileRenderer; -public class DoorTileRenderer extends TileRenderer { +public class DoorRenderer extends TileRenderer { private final TxSheet closed; private final TxSheet open; - public DoorTileRenderer(String quadClosed, String quadOpen) + public DoorRenderer(String quadClosed, String quadOpen) { this.closed = Res.getTxSheet(quadClosed); this.open = Res.getTxSheet(quadOpen); diff --git a/src/mightypork/rogue/world/tile/renderers/LockedDoorRenderer.java b/src/mightypork/rogue/world/tile/renderers/LockedDoorRenderer.java new file mode 100644 index 0000000..7846a55 --- /dev/null +++ b/src/mightypork/rogue/world/tile/renderers/LockedDoorRenderer.java @@ -0,0 +1,37 @@ +package mightypork.rogue.world.tile.renderers; + + +import mightypork.gamecore.render.Render; +import mightypork.gamecore.resources.textures.TxSheet; +import mightypork.gamecore.util.math.constraints.rect.Rect; +import mightypork.rogue.Res; +import mightypork.rogue.world.level.render.TileRenderContext; +import mightypork.rogue.world.tile.Tile; + + +public class LockedDoorRenderer extends DoorRenderer { + + private final TxSheet locked; + + + public LockedDoorRenderer(String sheetLocked, String sheetClosed, String sheetOpen) + { + super(sheetClosed, sheetOpen); + this.locked = Res.getTxSheet(sheetLocked); + } + + + @Override + public void renderTile(TileRenderContext context) + { + final Tile t = context.getTile(); + + if (!t.isWalkable()) { + final Rect rect = context.getRect(); + Render.quadTextured(rect, locked.getRandomQuad(context.getTileNoise())); + } else { + super.renderTile(context); + } + } + +} diff --git a/src/mightypork/rogue/world/tile/tiles/SecretDoorTile.java b/src/mightypork/rogue/world/tile/tiles/SecretDoorTile.java new file mode 100644 index 0000000..ee28f47 --- /dev/null +++ b/src/mightypork/rogue/world/tile/tiles/SecretDoorTile.java @@ -0,0 +1,35 @@ +package mightypork.rogue.world.tile.tiles; + + +import mightypork.gamecore.util.math.color.Color; +import mightypork.rogue.world.tile.TileModel; +import mightypork.rogue.world.tile.TileRenderer; +import mightypork.rogue.world.tile.TileType; + + +public class SecretDoorTile extends LockedDoorTile { + + public SecretDoorTile(TileModel model, TileRenderer renderer) + { + super(model, renderer); + } + + + @Override + public boolean onClick() + { + if (!locked) return false; + + locked = false; + + return true; + } + + + @Override + public Color getMapColor() + { + if (locked) return TileType.WALL.getMapColor(); + return super.getMapColor(); + } +}