diff --git a/res/img/map_tiles.png b/res/img/tiles.png similarity index 100% rename from res/img/map_tiles.png rename to res/img/tiles.png diff --git a/res/img/map_tiles.xcf b/res/img/tiles.xcf similarity index 100% rename from res/img/map_tiles.xcf rename to res/img/tiles.xcf diff --git a/src/mightypork/gamecore/render/Render.java b/src/mightypork/gamecore/render/Render.java index 73757b6..1ab374f 100644 --- a/src/mightypork/gamecore/render/Render.java +++ b/src/mightypork/gamecore/render/Render.java @@ -5,9 +5,11 @@ import static org.lwjgl.opengl.GL11.*; import java.io.IOException; +import mightypork.gamecore.audio.players.EffectPlayer; import mightypork.gamecore.render.textures.FilterMode; import mightypork.gamecore.render.textures.GLTexture; import mightypork.gamecore.render.textures.TxQuad; +import mightypork.rogue.Res; import mightypork.util.constraints.rect.Rect; import mightypork.util.constraints.rect.caching.RectDigest; import mightypork.util.constraints.vect.Vect; @@ -230,6 +232,8 @@ public class Render { } private static int pushed = 0; + /** Can be used to avoid texture binding and glBegin/glEnd in textured quads */ + public static boolean batchTexturedQuadMode; /** @@ -432,14 +436,14 @@ public class Render { */ public static void quadTextured(Rect quad, Rect uvs, GLTexture texture, Color tint) { - glEnable(GL_TEXTURE_2D); - - texture.bind(); + if (!batchTexturedQuadMode) { + glEnable(GL_TEXTURE_2D); + texture.bind(); + glBegin(GL_QUADS); + } setColor(tint); - glBegin(GL_QUADS); - final RectDigest q = quad.digest(); final RectDigest u = uvs.digest(); @@ -458,7 +462,8 @@ public class Render { glTexCoord2d(u.left * w, u.top * h); glVertex2d(q.left, q.top); - glEnd(); + + if (!batchTexturedQuadMode) glEnd(); } @@ -544,4 +549,18 @@ public class Render { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } + + public static void enterBatchTexturedQuadMode(GLTexture texture) + { + texture.bind(); + GL11.glBegin(GL11.GL_QUADS); + batchTexturedQuadMode = true; + } + + + public static void leaveBatchTexturedQuadMode() + { + GL11.glEnd(); + batchTexturedQuadMode = false; + } } diff --git a/src/mightypork/gamecore/render/fonts/impl/CachedFont.java b/src/mightypork/gamecore/render/fonts/impl/CachedFont.java index 6368899..11a4d4c 100644 --- a/src/mightypork/gamecore/render/fonts/impl/CachedFont.java +++ b/src/mightypork/gamecore/render/fonts/impl/CachedFont.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Map; import mightypork.gamecore.render.fonts.GLFont; +import mightypork.gamecore.render.textures.DeferredTexture; import mightypork.gamecore.render.textures.FilterMode; import mightypork.util.constraints.vect.Vect; import mightypork.util.constraints.vect.VectConst; @@ -374,6 +375,8 @@ public class CachedFont implements GLFont { { GLUtils.checkGLContext(); + DeferredTexture.lastBind = null; // needs rebind. + // PUSH glPushAttrib(GL_ENABLE_BIT); diff --git a/src/mightypork/gamecore/render/textures/DeferredTexture.java b/src/mightypork/gamecore/render/textures/DeferredTexture.java index 102962c..062453e 100644 --- a/src/mightypork/gamecore/render/textures/DeferredTexture.java +++ b/src/mightypork/gamecore/render/textures/DeferredTexture.java @@ -19,6 +19,8 @@ import org.lwjgl.opengl.GL11; @MustLoadInMainThread public class DeferredTexture extends DeferredResource implements GLTexture { + public static DeferredTexture lastBind = null; + private org.newdawn.slick.opengl.Texture backingTexture; private FilterMode filter = FilterMode.NEAREST; private WrapMode wrap = WrapMode.CLAMP; @@ -70,15 +72,19 @@ public class DeferredTexture extends DeferredResource implements GLTexture { GL11.glEnable(GL11.GL_TEXTURE_2D); - GL11.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE); - - GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, wrap.num); - GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, wrap.num); - - GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, filter.num); - GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, filter.num); - - GL11.glBindTexture(GL11.GL_TEXTURE_2D, getTextureID()); + if (lastBind != this) { + lastBind = this; + + GL11.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE); + + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, wrap.num); + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, wrap.num); + + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, filter.num); + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, filter.num); + + GL11.glBindTexture(GL11.GL_TEXTURE_2D, getTextureID()); + } } diff --git a/src/mightypork/rogue/App.java b/src/mightypork/rogue/App.java index 67b0300..c8ef4d8 100644 --- a/src/mightypork/rogue/App.java +++ b/src/mightypork/rogue/App.java @@ -22,9 +22,9 @@ 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.PlayerEntity; +import mightypork.rogue.world.entity.Entity; import mightypork.rogue.world.item.Item; -import mightypork.rogue.world.map.Level; +import mightypork.rogue.world.level.Level; import mightypork.rogue.world.tile.Tile; import mightypork.util.control.eventbus.EventBus; import mightypork.util.control.eventbus.events.Event; @@ -110,7 +110,7 @@ public final class App extends BaseApp { Ion.registerBinary(Tile.ION_MARK, Tile.class); Ion.registerBinary(Item.ION_MARK, Item.class); Ion.registerBinary(Level.ION_MARK, Level.class); - Ion.registerBinary(Level.ION_MARK, PlayerEntity.class); + Ion.registerBinary(Entity.ION_MARK, Entity.class); } diff --git a/src/mightypork/rogue/Res.java b/src/mightypork/rogue/Res.java index 0078db3..475d674 100644 --- a/src/mightypork/rogue/Res.java +++ b/src/mightypork/rogue/Res.java @@ -76,7 +76,7 @@ public final class Res { textures.addQuad("xp_off", gui.makeQuad(.5, 1.5, .5, .5)); textures.addQuad("panel", gui.makeQuad(0, 3.75, 4, .25)); - texture = textures.loadTexture("tiles", "/res/img/map_tiles.png", FilterMode.NEAREST, WrapMode.CLAMP); + texture = textures.loadTexture("tiles", "/res/img/tiles.png", FilterMode.NEAREST, WrapMode.CLAMP); final QuadGrid tiles = texture.grid(32, 32); textures.addSheet("tile.wall.mossy_bricks", tiles.makeSheet(4, 0, 7, 1)); diff --git a/src/mightypork/rogue/screens/ingame/WorldLayer.java b/src/mightypork/rogue/screens/ingame/WorldLayer.java index 58dfb48..6d0be6d 100644 --- a/src/mightypork/rogue/screens/ingame/WorldLayer.java +++ b/src/mightypork/rogue/screens/ingame/WorldLayer.java @@ -7,9 +7,6 @@ import java.util.Random; import mightypork.gamecore.gui.screens.Screen; import mightypork.gamecore.gui.screens.ScreenLayer; -import mightypork.gamecore.input.InputSystem; -import mightypork.gamecore.input.KeyStroke; -import mightypork.gamecore.input.Keys; import mightypork.rogue.Paths; import mightypork.rogue.world.MapGenerator; import mightypork.rogue.world.World; @@ -28,7 +25,7 @@ public class WorldLayer extends ScreenLayer { final World w = MapGenerator.createWorld(rand.nextLong()); try { - Ion.toFile(new File(Paths.WORKDIR,"test-world.ion"), w); + Ion.toFile(new File(Paths.WORKDIR, "test-world.ion"), w); } catch (final IOException e) { e.printStackTrace(); System.exit(1); @@ -49,63 +46,63 @@ public class WorldLayer extends ScreenLayer { wr.setRect(root); root.add(wr); - bindKey(new KeyStroke(true, Keys.LEFT), new Runnable() { - - @Override - public void run() - { - w.getPlayer().walk(-1, 0); - } - }); - bindKey(new KeyStroke(true, Keys.RIGHT), new Runnable() { - - @Override - public void run() - { - w.getPlayer().walk(1, 0); - } - }); - bindKey(new KeyStroke(true, Keys.UP), new Runnable() { - - @Override - public void run() - { - w.getPlayer().walk(0, -1); - } - }); - bindKey(new KeyStroke(true, Keys.DOWN), new Runnable() { - - @Override - public void run() - { - w.getPlayer().walk(0, 1); - } - }); - bindKey(new KeyStroke(true, Keys.SPACE), new Runnable() { - - @Override - public void run() - { - w.getPlayer().walk(5, 5); - } - }); - - w.getPlayer().setMoveListener(new Runnable() { - - @Override - public void run() - { - if (InputSystem.isKeyDown(Keys.LEFT)) { - w.getPlayer().walk(-1, 0); - } else if (InputSystem.isKeyDown(Keys.RIGHT)) { - w.getPlayer().walk(1, 0); - } else if (InputSystem.isKeyDown(Keys.UP)) { - w.getPlayer().walk(0, -1); - } else if (InputSystem.isKeyDown(Keys.DOWN)) { - w.getPlayer().walk(0, 1); - } - } - }); +// bindKey(new KeyStroke(true, Keys.LEFT), new Runnable() { +// +// @Override +// public void run() +// { +// w.getPlayer().walk(-1, 0); +// } +// }); +// bindKey(new KeyStroke(true, Keys.RIGHT), new Runnable() { +// +// @Override +// public void run() +// { +// w.getPlayer().walk(1, 0); +// } +// }); +// bindKey(new KeyStroke(true, Keys.UP), new Runnable() { +// +// @Override +// public void run() +// { +// w.getPlayer().walk(0, -1); +// } +// }); +// bindKey(new KeyStroke(true, Keys.DOWN), new Runnable() { +// +// @Override +// public void run() +// { +// w.getPlayer().walk(0, 1); +// } +// }); +// bindKey(new KeyStroke(true, Keys.SPACE), new Runnable() { +// +// @Override +// public void run() +// { +// w.getPlayer().walk(5, 5); +// } +// }); +// +// w.getPlayer().setMoveListener(new Runnable() { +// +// @Override +// public void run() +// { +// if (InputSystem.isKeyDown(Keys.LEFT)) { +// w.getPlayer().walk(-1, 0); +// } else if (InputSystem.isKeyDown(Keys.RIGHT)) { +// w.getPlayer().walk(1, 0); +// } else if (InputSystem.isKeyDown(Keys.UP)) { +// w.getPlayer().walk(0, -1); +// } else if (InputSystem.isKeyDown(Keys.DOWN)) { +// w.getPlayer().walk(0, 1); +// } +// } +// }); } diff --git a/src/mightypork/rogue/screens/test_cat_sound/LayerFlyingCat.java b/src/mightypork/rogue/screens/test_cat_sound/LayerFlyingCat.java index 2cb7de8..b609113 100644 --- a/src/mightypork/rogue/screens/test_cat_sound/LayerFlyingCat.java +++ b/src/mightypork/rogue/screens/test_cat_sound/LayerFlyingCat.java @@ -90,7 +90,7 @@ public class LayerFlyingCat extends ScreenLayer implements MouseButtonEvent.List { if (!event.isDown()) return; - cat_position.setTo(event.getPos()); + cat_position.animate(event.getPos()); final double newSize = root.height().perc(10 + rand.nextInt(40)).value(); diff --git a/src/mightypork/rogue/world/ClientWorld.java b/src/mightypork/rogue/world/ClientWorld.java deleted file mode 100644 index 8dc015f..0000000 --- a/src/mightypork/rogue/world/ClientWorld.java +++ /dev/null @@ -1,94 +0,0 @@ -package mightypork.rogue.world; - - -import mightypork.rogue.world.map.Level; -import mightypork.rogue.world.map.TileRenderContext; -import mightypork.util.constraints.rect.Rect; -import mightypork.util.constraints.rect.RectConst; -import mightypork.util.constraints.rect.proxy.RectBound; -import mightypork.util.constraints.vect.VectConst; -import mightypork.util.control.timing.Updateable; - - -public class ClientWorld implements Updateable, WorldAccess { - - private Level level = null; - - private final PlayerEntity player = null; - - - @Override - public void update(double delta) - { - player.update(delta); - - level.updateVisual(player, delta); - } - - - /** - * 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) - { - final Rect r = viewport.getRect(); - final double vpH = r.height().value(); - final double vpW = r.width().value(); - - // adjust tile size to fit desired amount of tiles - - final double allowedSizeW = vpW / xTiles; - final double allowedSizeH = vpH / yTiles; - int tileSize = (int) Math.round(Math.max(Math.min(allowedSizeH, allowedSizeW), minSize)); - - tileSize -= tileSize % 16; - - final VectConst vpCenter = r.center().sub(tileSize * 0.5, tileSize).freeze(); // 0.5 to center, 1 to move up (down is teh navbar) - - final double playerX = player.getPosition().getVisualX(); - final double playerY = player.getPosition().getVisualY(); - - // total map area - //@formatter:off - final RectConst mapRect = vpCenter.startRect().grow( - playerX*tileSize, - (level.getWidth() - playerX) * tileSize, - playerY*tileSize, - (level.getHeight() - playerY) * tileSize - ).freeze(); - //@formatter:on - - // tiles to render - final int x1 = (int) Math.floor(playerX - (vpW / tileSize)); - final int y1 = (int) Math.floor(playerY - (vpH / tileSize)); - final int x2 = (int) Math.ceil(playerX + (vpW / tileSize)); - final int y2 = (int) Math.ceil(playerY + (vpH / tileSize)); - - final TileRenderContext trc = new TileRenderContext(level, 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 PlayerEntity getPlayer() - { - return player; - } - - - @Override - public boolean isServer() - { - // TODO Auto-generated method stub - return false; - } - -} diff --git a/src/mightypork/rogue/world/ClientWorldAccess.java b/src/mightypork/rogue/world/ClientWorldAccess.java deleted file mode 100644 index 384141f..0000000 --- a/src/mightypork/rogue/world/ClientWorldAccess.java +++ /dev/null @@ -1,16 +0,0 @@ -package mightypork.rogue.world; - -import mightypork.rogue.world.map.Level; -import mightypork.util.control.timing.Updateable; - - -/** - * Abstraction of client-server connection from the client's view - * - * @author MightyPork - */ -public abstract class ClientWorldAccess implements Updateable { - - public abstract Level getLevel(); - -} diff --git a/src/mightypork/rogue/world/LevelRenderer.java b/src/mightypork/rogue/world/LevelRenderer.java deleted file mode 100644 index 0162fc5..0000000 --- a/src/mightypork/rogue/world/LevelRenderer.java +++ /dev/null @@ -1,64 +0,0 @@ -package mightypork.rogue.world; - - -import mightypork.rogue.world.map.Level; -import mightypork.rogue.world.map.TileRenderContext; -import mightypork.util.constraints.rect.Rect; -import mightypork.util.constraints.rect.RectConst; -import mightypork.util.constraints.rect.proxy.RectBound; -import mightypork.util.constraints.vect.VectConst; - - -public class LevelRenderer { - - /** - * 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 static void render(Level level, PlayerEntity player, RectBound viewport, final int yTiles, final int xTiles, final int minSize) - { - final Rect r = viewport.getRect(); - final double vpH = r.height().value(); - final double vpW = r.width().value(); - - // adjust tile size to fit desired amount of tiles - - final double allowedSizeW = vpW / xTiles; - final double allowedSizeH = vpH / yTiles; - int tileSize = (int) Math.round(Math.max(Math.min(allowedSizeH, allowedSizeW), minSize)); - - tileSize -= tileSize % 16; - - final VectConst vpCenter = r.center().sub(tileSize * 0.5, tileSize).freeze(); // 0.5 to center, 1 to move up (down is teh navbar) - - final double playerX = player.getPosition().getVisualX(); - final double playerY = player.getPosition().getVisualY(); - - // total map area - //@formatter:off - final RectConst mapRect = vpCenter.startRect().grow( - playerX*tileSize, - (level.getWidth() - playerX) * tileSize, - playerY*tileSize, - (level.getHeight() - playerY) * tileSize - ).freeze(); - //@formatter:on - - // tiles to render - final int x1 = (int) Math.floor(playerX - (vpW / tileSize)); - final int y1 = (int) Math.floor(playerY - (vpH / tileSize)); - final int x2 = (int) Math.ceil(playerX + (vpW / tileSize)); - final int y2 = (int) Math.ceil(playerY + (vpH / tileSize)); - - final TileRenderContext trc = new TileRenderContext(level, mapRect); //-tileSize*0.5 - for (trc.y = y1; trc.y <= y2; trc.y++) { - for (trc.x = x1; trc.x <= x2; trc.x++) { - trc.render(); - } - } - } -} diff --git a/src/mightypork/rogue/world/MapGenerator.java b/src/mightypork/rogue/world/MapGenerator.java index bd69cdd..65797bd 100644 --- a/src/mightypork/rogue/world/MapGenerator.java +++ b/src/mightypork/rogue/world/MapGenerator.java @@ -3,9 +3,9 @@ package mightypork.rogue.world; import java.util.Random; -import mightypork.rogue.world.map.Level; -import mightypork.rogue.world.tile.TileModel; +import mightypork.rogue.world.level.Level; import mightypork.rogue.world.tile.Tiles; +import mightypork.rogue.world.tile.models.TileModel; public class MapGenerator { @@ -13,19 +13,19 @@ public class MapGenerator { public static final Random rand = new Random(); - public static ServerWorld createWorld(long seed) + public static World createWorld(long seed) { synchronized (rand) { rand.setSeed(seed); - final ServerWorld w = new ServerWorld(); + final World w = new World(); w.setSeed(seed); w.addLevel(createLevel(rand.nextLong(), Tiles.CRYSTAL_FLOOR, Tiles.CRYSTAL_WALL)); w.addLevel(createLevel(rand.nextLong(), Tiles.BRCOBBLE_FLOOR, Tiles.BRCOBBLE_WALL)); // TODO place on start position - w.addPlayer("local", new PlayerEntity(10, 10, 0)); + w.createPlayer(10, 10, 0); return w; } } diff --git a/src/mightypork/rogue/world/PathStep.java b/src/mightypork/rogue/world/PathStep.java index ae57b61..ada46a2 100644 --- a/src/mightypork/rogue/world/PathStep.java +++ b/src/mightypork/rogue/world/PathStep.java @@ -16,7 +16,8 @@ public class PathStep implements IonBinary { public int y; - public PathStep(int x, int y) { + public PathStep(int x, int y) + { this.x = x < 1 ? -1 : x > 0 ? 1 : 0; this.y = y < 1 ? -1 : y > 0 ? 1 : 0; diff --git a/src/mightypork/rogue/world/PlayerEntity.java b/src/mightypork/rogue/world/PlayerEntity.java deleted file mode 100644 index f34f348..0000000 --- a/src/mightypork/rogue/world/PlayerEntity.java +++ /dev/null @@ -1,83 +0,0 @@ -package mightypork.rogue.world; - - -import java.io.IOException; - -import mightypork.rogue.world.map.MapObserver; -import mightypork.util.ion.IonBundle; - - -/** - * Player info - * - * @author MightyPork - */ -public class PlayerEntity extends WorldEntity implements MapObserver { - - public static final double PLAYER_STEP_TIME = 0.3; - - public boolean connected = false; - - - public PlayerEntity() { - super(); - } - - - public PlayerEntity(ServerWorld world, int x, int y, int floor) { - super(world, new WorldPos(x, y, floor)); - } - - - @Override - public void load(IonBundle bundle) throws IOException - { - super.load(bundle); - } - - - @Override - public void save(IonBundle bundle) throws IOException - { - super.save(bundle); - } - - - @Override - public int getViewRange() - { - return 15; - } - - - @Override - protected double getStepTime() - { - return PLAYER_STEP_TIME; - } - - - public boolean isConnected() - { - return connected; - } - - - public void setConnected(boolean connected) - { - this.connected = connected; - } - - - @Override - public WorldPos getViewPosition() - { - return getPosition(); - } - - @Override - public boolean isPhysical() - { - return isConnected(); - } -} diff --git a/src/mightypork/rogue/world/PlayerInfo.java b/src/mightypork/rogue/world/PlayerInfo.java new file mode 100644 index 0000000..1e85cf3 --- /dev/null +++ b/src/mightypork/rogue/world/PlayerInfo.java @@ -0,0 +1,62 @@ +package mightypork.rogue.world; + + +import java.io.IOException; + +import mightypork.util.ion.IonBundle; +import mightypork.util.ion.IonBundled; + + +public class PlayerInfo implements IonBundled { + + private int eid = -1; // marks not initialized + private int level; + + + @Override + public void load(IonBundle bundle) throws IOException + { + eid = bundle.get("attached_eid", 0); + level = bundle.get("current_level", 0); + } + + + @Override + public void save(IonBundle bundle) throws IOException + { + bundle.put("attached_eid", eid); + bundle.put("current_level", level); + } + + + public void setEID(int eid) + { + if (isInitialized()) throw new RuntimeException("Cannot change player EID."); + this.eid = eid; + } + + + public void setLevel(int level) + { + this.level = level; + } + + + public int getEID() + { + return eid; + } + + + public int getLevel() + { + return level; + } + + + public boolean isInitialized() + { + return eid != -1; + } + +} diff --git a/src/mightypork/rogue/world/ServerWorld.java b/src/mightypork/rogue/world/ServerWorld.java deleted file mode 100644 index 1cb7ed6..0000000 --- a/src/mightypork/rogue/world/ServerWorld.java +++ /dev/null @@ -1,107 +0,0 @@ -package mightypork.rogue.world; - - -import java.io.IOException; -import java.util.*; - -import mightypork.rogue.world.map.Level; -import mightypork.util.control.timing.Updateable; -import mightypork.util.ion.IonBundle; -import mightypork.util.ion.IonBundled; - - -/** - * World on a server. To a server, all players and levels are equal. - * - * @author MightyPork - */ -public class ServerWorld implements IonBundled, Updateable, WorldAccess { - - private final ArrayList levels = new ArrayList<>(); - - private final Map players = new HashMap<>(); - - /** This seed can be used to re-create identical world. */ - private long seed; - - /** Next spawned entity ID */ - private long eid; - - - @Override - public void load(IonBundle in) throws IOException - { - seed = in.get("seed", 0L); - eid = in.get("eid", 0L); - in.loadSequence("levels", levels); - in.loadMap("players", players); - } - - - @Override - public void save(IonBundle out) throws IOException - { - out.put("seed", seed); - out.put("eid", eid); - out.putSequence("levels", levels); - out.putMap("players", players); - } - - - public void addLevel(Level level) - { - levels.add(level); - } - - public void addPlayer(String name, PlayerEntity player) - { - players.put(name, player); - } - - public void removePlayer(String name) - { - players.remove(name); - } - - @Override - public void update(double delta) - { - Set occupiedLevels = new HashSet<>(); - - // food meters and such - for (final PlayerEntity pl : players.values()) { - if(pl.isConnected()) { - pl.updateLogic(this, delta); - occupiedLevels.add(pl.getPosition().floor); - } - } - - for(int i : occupiedLevels) { - levels.get(i).updateLogic(this, delta); - } - } - - - public void setSeed(long seed) - { - this.seed = seed; - } - - - public long getSeed() - { - return seed; - } - - - public long generateEntityId() - { - return eid++; - } - - @Override - public boolean isServer() - { - return true; - } -} diff --git a/src/mightypork/rogue/world/World.java b/src/mightypork/rogue/world/World.java new file mode 100644 index 0000000..25553aa --- /dev/null +++ b/src/mightypork/rogue/world/World.java @@ -0,0 +1,121 @@ +package mightypork.rogue.world; + + +import java.io.IOException; +import java.util.ArrayList; + +import mightypork.rogue.world.entity.Entities; +import mightypork.rogue.world.entity.Entity; +import mightypork.rogue.world.level.Level; +import mightypork.util.constraints.rect.proxy.RectBound; +import mightypork.util.control.timing.Updateable; +import mightypork.util.ion.IonBundle; +import mightypork.util.ion.IonBundled; + + +/** + * World on a server. To a server, all players and levels are equal. + * + * @author MightyPork + */ +public class World implements IonBundled, Updateable { + + private final ArrayList levels = new ArrayList<>(); + + private final PlayerInfo player = new PlayerInfo(); + + private long seed; // world seed + private int eid; // next entity ID + + + @Override + public void load(IonBundle in) throws IOException + { + seed = in.get("seed", 0L); + eid = in.get("next_eid", 0); + in.loadSequence("levels", levels); + in.loadBundled("player", player); + } + + + @Override + public void save(IonBundle out) throws IOException + { + out.put("seed", seed); + out.put("next_eid", eid); + out.putSequence("levels", levels); + out.putBundled("player", player); + } + + + public void addLevel(Level level) + { + levels.add(level); + } + + + @Override + public void update(double delta) + { + getCurrentLevel().update(delta); + } + + + public void setSeed(long seed) + { + this.seed = seed; + } + + + public long getSeed() + { + return seed; + } + + + /** + * @return new entity ID + */ + public int getNewEID() + { + return eid++; + } + + + public void createPlayer(int x, int y, int level) + { + if (player.isInitialized()) { + throw new RuntimeException("Player already created."); + } + + // make entity + int playerEid = getNewEID(); + + final Entity entity = Entities.PLAYER.createEntity(playerEid, new WorldPos(x, y)); + + player.setLevel(level); + player.setEID(playerEid); + + levels.get(level).addEntity(entity); + } + + + /** + * 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(RectBound viewport, final int yTiles, final int xTiles, final int minSize) + { + getCurrentLevel().render(player, viewport, yTiles, xTiles, minSize); + } + + + public Level getCurrentLevel() + { + return levels.get(player.getLevel()); + } +} diff --git a/src/mightypork/rogue/world/WorldAccess.java b/src/mightypork/rogue/world/WorldAccess.java deleted file mode 100644 index 81d1192..0000000 --- a/src/mightypork/rogue/world/WorldAccess.java +++ /dev/null @@ -1,8 +0,0 @@ -package mightypork.rogue.world; - - -public interface WorldAccess { - - public boolean isServer(); - -} diff --git a/src/mightypork/rogue/world/WorldEntity.java b/src/mightypork/rogue/world/WorldEntity.java deleted file mode 100644 index 673ebc8..0000000 --- a/src/mightypork/rogue/world/WorldEntity.java +++ /dev/null @@ -1,143 +0,0 @@ -package mightypork.rogue.world; - - -import java.io.IOException; -import java.util.LinkedList; -import java.util.Queue; - -import mightypork.util.ion.IonBundle; -import mightypork.util.ion.IonBundled; - - -public abstract class WorldEntity implements IonBundled { - - private final WorldPos position = new WorldPos(); - private Runnable moveEndListener, pathEndListener; - - private long serial_id = 0L; - - private final Queue path = new LinkedList<>(); - - - public WorldEntity(ServerWorld world, WorldPos pos) { - this.serial_id = world.generateEntityId(); - this.position.setTo(pos); - } - - - public WorldEntity() { - // for ion - } - - - @Override - public void save(IonBundle bundle) throws IOException - { - bundle.putBundled("pos", position); - bundle.putSequence("steps", path); - bundle.put("eid", serial_id); - } - - - @Override - public void load(IonBundle bundle) throws IOException - { - bundle.loadBundled("pos", position); - bundle.loadSequence("path", path); - serial_id = bundle.get("eid", 0L); - } - - - public void setMoveEndListener(Runnable moveEndListener) - { - this.moveEndListener = moveEndListener; - } - - - public void setPathEndListener(Runnable pathEndListener) - { - this.pathEndListener = pathEndListener; - } - - - public WorldPos getPosition() - { - return position; - } - - - public void setPosition(WorldPos pos) - { - position.setTo(pos); - } - - - public void setPosition(int x, int y, int floor) - { - position.setTo(x, y, floor); - cancelPath(); // discard remaining steps - } - - - /** - * @param world the world - * @param delta delta time - */ - public void updateLogic(WorldAccess world, double delta) - { - if(!isPhysical()) return; - - if (!position.isFinished()) { - position.update(delta); - } - - if (position.isFinished()) { - - if (moveEndListener != null) moveEndListener.run(); - - if (!path.isEmpty()) { - // get next step to walk - PathStep step = path.poll(); - position.walk(step.x, step.y, getStepTime()); - } else { - // notify AI or whatever - if (pathEndListener != null) pathEndListener.run(); - } - } - } - - - /** - * @param world the world - * @param delta delta time - */ - public void updateVisual(WorldAccess world, double delta) - { - } - - - public boolean isPathFinished() - { - return position.isFinished() && path.isEmpty(); - } - - - public void addStep(PathStep step) - { - path.add(step); - } - - - public void cancelPath() - { - path.clear(); - } - - - protected abstract double getStepTime(); - - public boolean isPhysical() { - return true; - } - -} diff --git a/src/mightypork/rogue/world/WorldPos.java b/src/mightypork/rogue/world/WorldPos.java index 7a78fb2..bdecdda 100644 --- a/src/mightypork/rogue/world/WorldPos.java +++ b/src/mightypork/rogue/world/WorldPos.java @@ -18,19 +18,20 @@ import mightypork.util.math.Easing; */ public class WorldPos implements IonBundled, Updateable { - public int x, y, floor; + public int x, y; private final VectAnimated walkOffset = new VectAnimated(Vect.ZERO, Easing.LINEAR); - public WorldPos(int x, int y, int floor) { + public WorldPos(int x, int y) + { super(); this.x = x; this.y = y; - this.floor = floor; } - public WorldPos() { + public WorldPos() + { } @@ -45,7 +46,6 @@ public class WorldPos implements IonBundled, Updateable { { x = in.get("x", 0); y = in.get("y", 0); - floor = in.get("z", 0); walkOffset.reset(); } @@ -55,7 +55,6 @@ public class WorldPos implements IonBundled, Updateable { { out.put("x", x); out.put("y", y); - out.put("z", floor); } @@ -71,12 +70,6 @@ public class WorldPos implements IonBundled, Updateable { } - public double getFloor() - { - return floor; - } - - public double getVisualX() { return x + walkOffset.x(); @@ -97,18 +90,9 @@ public class WorldPos implements IonBundled, Updateable { } - public void setTo(int x, int y, int floor) - { - this.x = x; - this.y = y; - this.floor = floor; - walkOffset.reset(); - } - - public void setTo(WorldPos other) { - setTo(other.x, other.y, other.floor); + setTo(other.x, other.y); } @@ -117,7 +101,6 @@ public class WorldPos implements IonBundled, Updateable { { final int prime = 31; int result = 1; - result = prime * result + floor; result = prime * result + x; result = prime * result + y; return result; @@ -131,7 +114,6 @@ public class WorldPos implements IonBundled, Updateable { if (obj == null) return false; if (!(obj instanceof WorldPos)) return false; final WorldPos other = (WorldPos) obj; - if (floor != other.floor) return false; if (x != other.x) return false; if (y != other.y) return false; return true; diff --git a/src/mightypork/rogue/world/entity/Entities.java b/src/mightypork/rogue/world/entity/Entities.java new file mode 100644 index 0000000..371e8b2 --- /dev/null +++ b/src/mightypork/rogue/world/entity/Entities.java @@ -0,0 +1,44 @@ +package mightypork.rogue.world.entity; + + +import mightypork.rogue.world.entity.models.EntityModel; +import mightypork.rogue.world.entity.models.PlayerModel; + + +/** + * Tile registry + * + * @author MightyPork + */ +public final class Entities { + + private static final EntityModel[] entities = new EntityModel[256]; + + public static final EntityModel PLAYER = new PlayerModel(0); + + + public static void register(int id, EntityModel model) + { + if (id < 0 || id >= entities.length) { + throw new IllegalArgumentException("Entity model ID " + id + " is out of range."); + } + + if (entities[id] != null) { + throw new IllegalArgumentException("Entity model ID " + id + " already in use."); + } + + entities[id] = model; + } + + + public static EntityModel get(int id) + { + final EntityModel e = entities[id]; + + if (e == null) { + throw new IllegalArgumentException("No entity model with ID " + id + "."); + } + + return e; + } +} diff --git a/src/mightypork/rogue/world/entity/Entity.java b/src/mightypork/rogue/world/entity/Entity.java new file mode 100644 index 0000000..82ca2b1 --- /dev/null +++ b/src/mightypork/rogue/world/entity/Entity.java @@ -0,0 +1,192 @@ +package mightypork.rogue.world.entity; + + +import java.io.IOException; +import java.util.LinkedList; +import java.util.Queue; + +import mightypork.rogue.world.PathStep; +import mightypork.rogue.world.World; +import mightypork.rogue.world.WorldPos; +import mightypork.rogue.world.entity.models.EntityModel; +import mightypork.rogue.world.level.Level; +import mightypork.util.ion.IonBinary; +import mightypork.util.ion.IonBundle; +import mightypork.util.ion.IonBundled; +import mightypork.util.ion.IonInput; +import mightypork.util.ion.IonOutput; + + +/** + * World entity (mob or player) + * + * @author MightyPork + */ +public final class Entity implements IonBinary, IonBundled { + +// binary & bundled - binary stores via a bundle + + public static final int ION_MARK = 52; + + private final WorldPos position = new WorldPos(); + + /** Entity ID */ + private int eid = 0; + + /** Model ID */ + private int id; + + private final Queue path = new LinkedList<>(); + private EntityModel model; + private final IonBundle metadata = new IonBundle(); + + + public Entity(int eid, WorldPos pos, EntityModel entityModel) + { + this.eid = eid; + this.position.setTo(pos); + + setModel(entityModel); + } + + + private void setModel(EntityModel entityModel) + { + this.id = entityModel.id; + this.model = entityModel; + } + + + public Entity() + { + // for ion + } + + + @Override + public short getIonMark() + { + return ION_MARK; + } + + + @Override + public void save(IonOutput out) throws IOException + { + final IonBundle ib = new IonBundle(); + save(ib); + out.writeBundle(ib); + } + + + @Override + public void load(IonInput in) throws IOException + { + load(in.readBundle()); + } + + + @Override + public void save(IonBundle bundle) throws IOException + { + bundle.put("id", id); + bundle.putBundled("pos", position); + bundle.putSequence("steps", path); + bundle.put("eid", eid); + bundle.put("metadata", metadata); + } + + + @Override + public void load(IonBundle bundle) throws IOException + { + id = bundle.get("id", 0); + + if (model == null || id != model.id) { + setModel(Entities.get(id)); + } + + bundle.loadBundled("pos", position); + bundle.loadSequence("path", path); + eid = bundle.get("eid", eid); + + metadata.clear(); + bundle.loadBundle("metadata", metadata); + } + + + public int getEID() + { + return eid; + } + + + public WorldPos getPosition() + { + return position; + } + + + public void setPosition(WorldPos pos) + { + position.setTo(pos); + } + + + public void setPosition(int x, int y) + { + position.setTo(x, y); + cancelPath(); // discard remaining steps + } + + + /** + * @param world the world + * @param delta delta time + */ + public void update(World world, Level level, double delta) + { + if (!position.isFinished()) { + position.update(delta); + } + + if (position.isFinished()) { + + model.onStepFinished(this, world, level); + + if (!path.isEmpty()) { + // get next step to walk + final PathStep step = path.poll(); + position.walk(step.x, step.y, getStepTime()); + } else { + // notify AI or whatever + model.onPathFinished(this, world, level); + } + } + } + + + public boolean isPathFinished() + { + return position.isFinished() && path.isEmpty(); + } + + + public void addStep(PathStep step) + { + path.add(step); + } + + + public void cancelPath() + { + path.clear(); + } + + + protected double getStepTime() + { + return model.getStepTime(this); + } + +} diff --git a/src/mightypork/rogue/world/entity/models/EntityModel.java b/src/mightypork/rogue/world/entity/models/EntityModel.java new file mode 100644 index 0000000..ea0de63 --- /dev/null +++ b/src/mightypork/rogue/world/entity/models/EntityModel.java @@ -0,0 +1,71 @@ +package mightypork.rogue.world.entity.models; + + +import mightypork.rogue.world.World; +import mightypork.rogue.world.WorldPos; +import mightypork.rogue.world.entity.Entities; +import mightypork.rogue.world.entity.Entity; +import mightypork.rogue.world.entity.renderers.EntityRenderer; +import mightypork.rogue.world.level.Level; + + +/** + * Entity model + * + * @author MightyPork + */ +public abstract class EntityModel { + + /** Model ID */ + public final int id; + public EntityRenderer renderer = EntityRenderer.NONE; + + + public EntityModel(int id) + { + Entities.register(id, this); + this.id = id; + } + + + public EntityModel setRenderer(EntityRenderer renderer) + { + this.renderer = renderer; + return this; + } + + + /** + * @return new tile of this type; if 100% invariant, can return cached one. + */ + public Entity createEntity(int eid, WorldPos pos) + { + return new Entity(eid, pos, this); + } + + + /** + * Update entity + */ + public abstract void update(Entity entity, Level level, double delta); + + + /** + * @return true if this entity type has metadata worth saving + */ + public abstract boolean hasMetadata(); + + + /** + * @param entity the value is valid for + * @return step time (seconds) + */ + public abstract double getStepTime(Entity entity); + + + public abstract void onStepFinished(Entity entity, World world, Level level); + + + public abstract void onPathFinished(Entity entity, World world, Level level); + +} diff --git a/src/mightypork/rogue/world/entity/models/PlayerModel.java b/src/mightypork/rogue/world/entity/models/PlayerModel.java new file mode 100644 index 0000000..1cf2ca2 --- /dev/null +++ b/src/mightypork/rogue/world/entity/models/PlayerModel.java @@ -0,0 +1,67 @@ +package mightypork.rogue.world.entity.models; + + +import mightypork.rogue.world.World; +import mightypork.rogue.world.WorldPos; +import mightypork.rogue.world.entity.Entity; +import mightypork.rogue.world.level.Level; + + +/** + * Player info + * + * @author MightyPork + */ +public class PlayerModel extends EntityModel { + + private static final double STEP_TIME = 0.3; + + + public PlayerModel(int id) + { + super(id); + } + + + @Override + public Entity createEntity(int eid, WorldPos pos) + { + final Entity e = super.createEntity(eid, pos); + + // set metadata + + return e; + } + + + @Override + public void update(Entity entity, Level level, double delta) + { + } + + + @Override + public boolean hasMetadata() + { + return true; + } + + + @Override + public double getStepTime(Entity entity) + { + return STEP_TIME; + } + + + @Override + public void onStepFinished(Entity entity, World world, Level level) + { + } + + + @Override + public void onPathFinished(Entity entity, World world, Level level) + { + } +} diff --git a/src/mightypork/rogue/world/entity/renderers/EntityRenderer.java b/src/mightypork/rogue/world/entity/renderers/EntityRenderer.java new file mode 100644 index 0000000..7858a73 --- /dev/null +++ b/src/mightypork/rogue/world/entity/renderers/EntityRenderer.java @@ -0,0 +1,8 @@ +package mightypork.rogue.world.entity.renderers; + + +public class EntityRenderer { + + public static final EntityRenderer NONE = new NullEntityRenderer(); + +} diff --git a/src/mightypork/rogue/world/entity/renderers/NullEntityRenderer.java b/src/mightypork/rogue/world/entity/renderers/NullEntityRenderer.java new file mode 100644 index 0000000..37e9bd2 --- /dev/null +++ b/src/mightypork/rogue/world/entity/renderers/NullEntityRenderer.java @@ -0,0 +1,6 @@ +package mightypork.rogue.world.entity.renderers; + + +public class NullEntityRenderer extends EntityRenderer { + +} diff --git a/src/mightypork/rogue/world/item/Item.java b/src/mightypork/rogue/world/item/Item.java index 0712e7c..952ca4b 100644 --- a/src/mightypork/rogue/world/item/Item.java +++ b/src/mightypork/rogue/world/item/Item.java @@ -13,7 +13,7 @@ public class Item implements IonBinary { public static final short ION_MARK = 51; - private transient ItemModel model; + private ItemModel model; public int id; diff --git a/src/mightypork/rogue/world/item/Items.java b/src/mightypork/rogue/world/item/Items.java index cb18aaf..2038e8d 100644 --- a/src/mightypork/rogue/world/item/Items.java +++ b/src/mightypork/rogue/world/item/Items.java @@ -11,7 +11,7 @@ public final class Items { private static final ItemModel[] items = new ItemModel[256]; - static void register(int id, ItemModel model) + public static void register(int id, ItemModel model) { if (id < 0 || id >= items.length) if (items[id] != null) throw new IllegalArgumentException("Item ID " + id + " already in use."); diff --git a/src/mightypork/rogue/world/level/Level.java b/src/mightypork/rogue/world/level/Level.java new file mode 100644 index 0000000..0c050d7 --- /dev/null +++ b/src/mightypork/rogue/world/level/Level.java @@ -0,0 +1,330 @@ +package mightypork.rogue.world.level; + + +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.lwjgl.opengl.GL11; + +import mightypork.gamecore.render.Render; +import mightypork.rogue.Res; +import mightypork.rogue.world.PlayerInfo; +import mightypork.rogue.world.WorldPos; +import mightypork.rogue.world.entity.Entity; +import mightypork.rogue.world.level.render.TileRenderContext; +import mightypork.rogue.world.tile.Tile; +import mightypork.rogue.world.tile.Tiles; +import mightypork.rogue.world.tile.models.TileModel; +import mightypork.util.constraints.rect.Rect; +import mightypork.util.constraints.rect.RectConst; +import mightypork.util.constraints.rect.proxy.RectBound; +import mightypork.util.constraints.vect.VectConst; +import mightypork.util.ion.IonBinary; +import mightypork.util.ion.IonBundle; +import mightypork.util.ion.IonInput; +import mightypork.util.ion.IonOutput; +import mightypork.util.math.noise.NoiseGen; + + +/** + * One level of the dungeon + * + * @author MightyPork + */ +public class Level implements MapAccess, IonBinary { + + public static final int ION_MARK = 53; + + private static final boolean USE_BATCH_RENDERING = true; + + private int width, height; + + /** Array of tiles [y][x] */ + private Tile[][] tiles; + + private final Map entity_map = new HashMap<>(); + private final Set entity_set = new HashSet<>(); + + /** Level seed (used for generation and tile variation) */ + public long seed; + + private transient NoiseGen noiseGen; + + + public Level() + { + } + + + public Level(int width, int height) + { + this.width = width; + this.height = height; + buildArray(); + } + + + private void buildArray() + { + this.tiles = new Tile[height][width]; + } + + + public void fill(short 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] = model.createTile(); + } + } + } + + + @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); + } + + + 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; + } + + + @Override + public final int getWidth() + { + return width; + } + + + @Override + public final int getHeight() + { + return height; + } + + + public void setSeed(long seed) + { + this.seed = seed; + } + + + @Override + public long getSeed() + { + return seed; + } + + + @Override + public void load(IonInput in) throws IOException + { + // metadata + final IonBundle ib = in.readBundle(); + seed = ib.get("seed", 0L); + width = ib.get("w", 0); + height = ib.get("h", 0); + + ib.loadSequence("entities", entity_set); + for (final Entity ent : entity_set) { + entity_map.put(ent.getEID(), ent); + } + + // init array of size + buildArray(); + + // load tiles + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + // no mark + tiles[y][x] = new Tile(); + tiles[y][x].load(in); + } + } + } + + + @Override + public void save(IonOutput out) throws IOException + { + // metadata + final IonBundle ib = new IonBundle(); + ib.put("seed", seed); + ib.put("w", width); + ib.put("h", height); + ib.putSequence("entities", entity_set); + out.writeBundle(ib); + + // tiles (writing this way to save space) + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + // no mark to save space + tiles[y][x].save(out); + } + } + } + + + @Override + public short getIonMark() + { + return ION_MARK; + } + + + public void update(double delta) + { + // just update them all + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + getTile(x, y).update(this, delta); + } + } + } + + + @Override + public NoiseGen getNoiseGen() + { + if (noiseGen == null) { + noiseGen = new NoiseGen(0.2, 0, 0.5, 1, seed); + } + + return noiseGen; + } + + + /** + * Draw on screen + * + * @param playerInfo layer + * @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(PlayerInfo playerInfo, RectBound viewport, final int yTiles, final int xTiles, final int minSize) + { + final WorldPos pos; + + try { + pos = getEntity(playerInfo.getEID()).getPosition(); + } catch (NullPointerException e) { + throw new RuntimeException("Player entity not found in level.", e); + } + + final Rect r = viewport.getRect(); + final double vpH = r.height().value(); + final double vpW = r.width().value(); + + // adjust tile size to fit desired amount of tiles + + final double allowedSizeW = vpW / xTiles; + final double allowedSizeH = vpH / yTiles; + int tileSize = (int) Math.round(Math.max(Math.min(allowedSizeH, allowedSizeW), minSize)); + + tileSize -= tileSize % 16; + + final VectConst vpCenter = r.center().sub(tileSize * 0.5, tileSize).freeze(); // 0.5 to center, 1 to move up (down is teh navbar) + + final double playerX = pos.getVisualX(); + final double playerY = pos.getVisualY(); + + // total map area + //@formatter:off + final RectConst mapRect = vpCenter.startRect().grow( + playerX*tileSize, + (getWidth() - playerX) * tileSize, + playerY*tileSize, + (getHeight() - playerY) * tileSize + ).freeze(); + //@formatter:on + + // tiles to render + final int x1 = (int) Math.floor(playerX - (vpW / tileSize / 2)); + + final int y1 = (int) Math.floor(playerY - (vpH / tileSize / 2)); + final int x2 = (int) Math.ceil(playerX + (vpW / tileSize / 2)); + final int y2 = (int) Math.ceil(playerY + (vpH / tileSize / 2)); + + final TileRenderContext trc = new TileRenderContext(this, mapRect); //-tileSize*0.5 + + // batch rendering of the tiles + if (USE_BATCH_RENDERING) { + Render.enterBatchTexturedQuadMode(Res.getTexture("tiles")); + } + + for (trc.y = y1; trc.y <= y2; trc.y++) { + for (trc.x = x1; trc.x <= x2; trc.x++) { + trc.renderTile(); + } + } + + if (USE_BATCH_RENDERING) { + Render.leaveBatchTexturedQuadMode(); + } + + // render extras + for (trc.y = y1; trc.y <= y2; trc.y++) { + for (trc.x = x1; trc.x <= x2; trc.x++) { + trc.renderItems(); + } + } + } + + + public Entity getEntity(int eid) + { + return entity_map.get(eid); + } + + + public void addEntity(Entity entity) + { + entity_map.put(entity.getEID(), entity); + entity_set.add(entity); + } + + + public void removeEntity(Entity entity) + { + entity_map.remove(entity.getEID()); + entity_set.remove(entity); + } + + + public void removeEntity(int eid) + { + final Entity removed = entity_map.remove(eid); + entity_set.remove(removed); + } +} diff --git a/src/mightypork/rogue/world/map/MapAccess.java b/src/mightypork/rogue/world/level/MapAccess.java similarity index 93% rename from src/mightypork/rogue/world/map/MapAccess.java rename to src/mightypork/rogue/world/level/MapAccess.java index 6e11a04..4e28886 100644 --- a/src/mightypork/rogue/world/map/MapAccess.java +++ b/src/mightypork/rogue/world/level/MapAccess.java @@ -1,4 +1,4 @@ -package mightypork.rogue.world.map; +package mightypork.rogue.world.level; import mightypork.rogue.world.tile.Tile; diff --git a/src/mightypork/rogue/world/map/EntityRenderContext.java b/src/mightypork/rogue/world/level/render/EntityRenderContext.java similarity index 68% rename from src/mightypork/rogue/world/map/EntityRenderContext.java rename to src/mightypork/rogue/world/level/render/EntityRenderContext.java index b388181..bdc48e8 100644 --- a/src/mightypork/rogue/world/map/EntityRenderContext.java +++ b/src/mightypork/rogue/world/level/render/EntityRenderContext.java @@ -1,6 +1,7 @@ -package mightypork.rogue.world.map; +package mightypork.rogue.world.level.render; +import mightypork.rogue.world.level.MapAccess; import mightypork.util.constraints.rect.Rect; diff --git a/src/mightypork/rogue/world/map/MapRenderContext.java b/src/mightypork/rogue/world/level/render/MapRenderContext.java similarity index 85% rename from src/mightypork/rogue/world/map/MapRenderContext.java rename to src/mightypork/rogue/world/level/render/MapRenderContext.java index 5cbdf49..573eaa6 100644 --- a/src/mightypork/rogue/world/map/MapRenderContext.java +++ b/src/mightypork/rogue/world/level/render/MapRenderContext.java @@ -1,6 +1,7 @@ -package mightypork.rogue.world.map; +package mightypork.rogue.world.level.render; +import mightypork.rogue.world.level.MapAccess; import mightypork.util.constraints.rect.Rect; import mightypork.util.constraints.rect.builders.TiledRect; diff --git a/src/mightypork/rogue/world/map/TileRenderContext.java b/src/mightypork/rogue/world/level/render/TileRenderContext.java similarity index 84% rename from src/mightypork/rogue/world/map/TileRenderContext.java rename to src/mightypork/rogue/world/level/render/TileRenderContext.java index d929b77..f5e79b8 100644 --- a/src/mightypork/rogue/world/map/TileRenderContext.java +++ b/src/mightypork/rogue/world/level/render/TileRenderContext.java @@ -1,6 +1,7 @@ -package mightypork.rogue.world.map; +package mightypork.rogue.world.level.render; +import mightypork.rogue.world.level.MapAccess; import mightypork.rogue.world.tile.Tile; import mightypork.util.constraints.rect.Rect; import mightypork.util.constraints.rect.proxy.RectBound; @@ -60,11 +61,15 @@ public final class TileRenderContext extends MapRenderContext implements RectBou } - public void render() + public void renderTile() { - map.getTile(x, y).render(this); + map.getTile(x, y).renderTile(this); + } + + public void renderItems() + { + map.getTile(x, y).renderItems(this); } - /** * Rect of the current tile to draw diff --git a/src/mightypork/rogue/world/map/Level.java b/src/mightypork/rogue/world/map/Level.java deleted file mode 100644 index 4da281c..0000000 --- a/src/mightypork/rogue/world/map/Level.java +++ /dev/null @@ -1,248 +0,0 @@ -package mightypork.rogue.world.map; - - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import mightypork.rogue.world.PlayerEntity; -import mightypork.rogue.world.WorldAccess; -import mightypork.rogue.world.WorldEntity; -import mightypork.rogue.world.WorldPos; -import mightypork.rogue.world.tile.Tile; -import mightypork.rogue.world.tile.TileModel; -import mightypork.rogue.world.tile.Tiles; -import mightypork.util.ion.IonBinary; -import mightypork.util.ion.IonBundle; -import mightypork.util.ion.IonInput; -import mightypork.util.ion.IonOutput; -import mightypork.util.math.noise.NoiseGen; - - -/** - * One level of the dungeon - * - * @author MightyPork - */ -public class Level implements MapAccess, IonBinary { - - public static final int ION_MARK = 53; - - private int width, height; - - /** Array of tiles [y][x] */ - private Tile[][] tiles; - - private final List entities = new ArrayList<>(); - - /** Level seed (used for generation and tile variation) */ - public long seed; - - private transient NoiseGen noiseGen; - - - public Level() - { - } - - - public Level(int width, int height) - { - this.width = width; - this.height = height; - buildArray(); - } - - - private void buildArray() - { - this.tiles = new Tile[height][width]; - } - - - public void fill(short 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] = model.createTile(); - } - } - } - - - @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); - } - - - 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; - } - - - @Override - public final int getWidth() - { - return width; - } - - - @Override - public final int getHeight() - { - return height; - } - - - public void setSeed(long seed) - { - this.seed = seed; - } - - - @Override - public long getSeed() - { - return seed; - } - - - @Override - public void load(IonInput in) throws IOException - { - // metadata - final IonBundle ib = in.readBundle(); - seed = ib.get("seed", 0L); - width = ib.get("w", 0); - height = ib.get("h", 0); - - ib.loadSequence("entities", entities); - - // init array of size - buildArray(); - - // load tiles - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - // no mark - tiles[y][x] = new Tile(); - tiles[y][x].load(in); - } - } - } - - - @Override - public void save(IonOutput out) throws IOException - { - // metadata - final IonBundle ib = new IonBundle(); - ib.put("seed", seed); - ib.put("w", width); - ib.put("h", height); - ib.putSequence("entities", entities); - out.writeBundle(ib); - - // tiles (writing this way to save space) - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - // no mark to save space - tiles[y][x].save(out); - } - } - } - - - @Override - public short getIonMark() - { - return ION_MARK; - } - - - public void updateLogic(WorldAccess world, double delta) - { - if (!world.isServer()) { - throw new RuntimeException("Not allowed for client."); - } - - // just update them all - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - getTile(x, y).updateLogic(world, this, delta); - } - } - } - - - /** - * Update visuals for player (particle effects etc) - * - * @param world - * @param player - * @param delta - */ - public void updateVisual(WorldAccess world, PlayerEntity player, double delta) - { - if (world.isServer()) { - throw new RuntimeException("Not allowed for server."); - } - - final int viewRange = player.getViewRange(); - final WorldPos eyepos = player.getViewPosition(); - - int x1 = eyepos.x - viewRange; - int y1 = eyepos.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).updateVisual(world, this, delta); - } - } - } - - - @Override - public NoiseGen getNoiseGen() - { - if (noiseGen == null) { - noiseGen = new NoiseGen(0.2, 0, 0.5, 1, seed); - } - - return noiseGen; - } - -} diff --git a/src/mightypork/rogue/world/map/MapObserver.java b/src/mightypork/rogue/world/map/MapObserver.java deleted file mode 100644 index 8c41375..0000000 --- a/src/mightypork/rogue/world/map/MapObserver.java +++ /dev/null @@ -1,13 +0,0 @@ -package mightypork.rogue.world.map; - - -import mightypork.rogue.world.WorldPos; - - -public interface MapObserver { - - int getViewRange(); - - - WorldPos getViewPosition(); -} diff --git a/src/mightypork/rogue/world/tile/DroppedItemRenderer.java b/src/mightypork/rogue/world/tile/DroppedItemRenderer.java index d56db66..95a2e36 100644 --- a/src/mightypork/rogue/world/tile/DroppedItemRenderer.java +++ b/src/mightypork/rogue/world/tile/DroppedItemRenderer.java @@ -2,12 +2,11 @@ package mightypork.rogue.world.tile; import mightypork.rogue.world.item.Item; -import mightypork.rogue.world.map.TileRenderContext; +import mightypork.rogue.world.level.render.TileRenderContext; import mightypork.util.constraints.rect.Rect; import mightypork.util.constraints.rect.proxy.RectBoundAdapter; import mightypork.util.control.timing.Animator; import mightypork.util.control.timing.AnimatorBounce; -import mightypork.util.control.timing.Updateable; import mightypork.util.math.Easing; diff --git a/src/mightypork/rogue/world/tile/Tile.java b/src/mightypork/rogue/world/tile/Tile.java index 82efa0b..42b962b 100644 --- a/src/mightypork/rogue/world/tile/Tile.java +++ b/src/mightypork/rogue/world/tile/Tile.java @@ -4,11 +4,11 @@ package mightypork.rogue.world.tile; import java.io.IOException; import java.util.Stack; -import mightypork.rogue.world.WorldAccess; import mightypork.rogue.world.item.Item; -import mightypork.rogue.world.map.Level; -import mightypork.rogue.world.map.TileRenderContext; -import mightypork.util.control.timing.Animator; +import mightypork.rogue.world.level.Level; +import mightypork.rogue.world.level.render.TileRenderContext; +import mightypork.rogue.world.tile.models.TileModel; +import mightypork.rogue.world.tile.renderers.TileRenderer; import mightypork.util.ion.IonBinary; import mightypork.util.ion.IonBundle; import mightypork.util.ion.IonInput; @@ -27,16 +27,13 @@ public final class Tile implements IonBinary { private TileModel model; private TileRenderer renderer; - public int id; + private int id; private final Stack items = new Stack<>(); /** persistent field for model, reflected by renderer */ public final IonBundle metadata = new IonBundle(); - /** non-persistent data field for model */ - public Object tmpdata; - public Tile(int id) { @@ -63,7 +60,10 @@ public final class Tile implements IonBinary { } - public void render(TileRenderContext context) + /** + * Render the tile alone (must not use other than the main map texture) + */ + public void renderTile(TileRenderContext context) { renderer.render(context); @@ -73,6 +73,19 @@ public final class Tile implements IonBinary { } + /** + * Render items + * @param context + */ + public void renderItems(TileRenderContext context) + { + if (hasItems()) { + renderer.renderItemOnTile(items.peek(), context); + } + } + + + @Override public void save(IonOutput out) throws IOException { @@ -82,7 +95,7 @@ public final class Tile implements IonBinary { out.writeSequence(items); } - if (model.hasPersistentMetadata()) { + if (model.hasMetadata()) { out.writeBundle(metadata); } } @@ -102,7 +115,7 @@ public final class Tile implements IonBinary { in.readSequence(items); } - if (model.hasPersistentMetadata()) { + if (model.hasMetadata()) { in.readBundle(metadata); } } @@ -111,18 +124,12 @@ public final class Tile implements IonBinary { /** * Update tile logic state (on server) * - * @param world the world + * @param level the level * @param delta delta time */ - public void updateLogic(WorldAccess world, Level level, double delta) - { - model.updateLogic(this, world, level, delta); - } - - - public void updateVisual(WorldAccess world, Level level, double delta) + public void update(Level level, double delta) { - model.updateVisual(this, world, level, delta); + model.update(this, level, delta); } diff --git a/src/mightypork/rogue/world/tile/Tiles.java b/src/mightypork/rogue/world/tile/Tiles.java index 36692ad..65597af 100644 --- a/src/mightypork/rogue/world/tile/Tiles.java +++ b/src/mightypork/rogue/world/tile/Tiles.java @@ -4,6 +4,7 @@ package mightypork.rogue.world.tile; import mightypork.rogue.world.tile.models.Floor; import mightypork.rogue.world.tile.models.NullFloor; import mightypork.rogue.world.tile.models.NullWall; +import mightypork.rogue.world.tile.models.TileModel; import mightypork.rogue.world.tile.models.Wall; @@ -35,7 +36,7 @@ public final class Tiles { public static final TileModel CRYSTAL_WALL = new Wall(11).setTexture("tile.wall.crystal"); - static void register(int id, TileModel model) + public static void register(int id, TileModel model) { if (id < 0 || id >= tiles.length) { throw new IllegalArgumentException("Tile ID " + id + " is out of range."); diff --git a/src/mightypork/rogue/world/tile/models/AbstractNullTile.java b/src/mightypork/rogue/world/tile/models/AbstractNullTile.java index 4fbbee2..0e26c70 100644 --- a/src/mightypork/rogue/world/tile/models/AbstractNullTile.java +++ b/src/mightypork/rogue/world/tile/models/AbstractNullTile.java @@ -39,7 +39,7 @@ public abstract class AbstractNullTile extends SimpleTile { @Override - public boolean hasPersistentMetadata() + public boolean hasMetadata() { return false; } diff --git a/src/mightypork/rogue/world/tile/models/SimpleTile.java b/src/mightypork/rogue/world/tile/models/SimpleTile.java index d1270fd..3c961fb 100644 --- a/src/mightypork/rogue/world/tile/models/SimpleTile.java +++ b/src/mightypork/rogue/world/tile/models/SimpleTile.java @@ -1,10 +1,8 @@ package mightypork.rogue.world.tile.models; -import mightypork.rogue.world.WorldAccess; -import mightypork.rogue.world.map.Level; +import mightypork.rogue.world.level.Level; import mightypork.rogue.world.tile.Tile; -import mightypork.rogue.world.tile.TileModel; import mightypork.util.annotations.DefaultImpl; @@ -33,7 +31,7 @@ public abstract class SimpleTile extends TileModel { @Override - public boolean hasPersistentMetadata() + public boolean hasMetadata() { return false; } @@ -41,13 +39,7 @@ public abstract class SimpleTile extends TileModel { @Override @DefaultImpl - public void updateLogic(Tile tile, WorldAccess world, Level level, double delta) - { - } - - @Override - @DefaultImpl - public void updateVisual(Tile tile, WorldAccess world, Level level, double delta) + public void update(Tile tile, Level level, double delta) { } } diff --git a/src/mightypork/rogue/world/tile/TileModel.java b/src/mightypork/rogue/world/tile/models/TileModel.java similarity index 71% rename from src/mightypork/rogue/world/tile/TileModel.java rename to src/mightypork/rogue/world/tile/models/TileModel.java index 9748671..9d3147b 100644 --- a/src/mightypork/rogue/world/tile/TileModel.java +++ b/src/mightypork/rogue/world/tile/models/TileModel.java @@ -1,9 +1,11 @@ -package mightypork.rogue.world.tile; +package mightypork.rogue.world.tile.models; -import mightypork.rogue.world.WorldAccess; -import mightypork.rogue.world.map.Level; +import mightypork.rogue.world.level.Level; +import mightypork.rogue.world.tile.Tile; +import mightypork.rogue.world.tile.Tiles; import mightypork.rogue.world.tile.renderers.BasicTileRenderer; +import mightypork.rogue.world.tile.renderers.TileRenderer; /** @@ -68,21 +70,15 @@ public abstract class TileModel { /** - * Update tile in world, called by server + * Update tile in world */ - public abstract void updateLogic(Tile tile, WorldAccess world, Level level, double delta); - - - /** - * Update tile visuals (particles / update for renderer) - */ - public abstract void updateVisual(Tile tile, WorldAccess world, Level level, double delta); + public abstract void update(Tile tile, Level level, double delta); /** * @return true if this item type has metadata worth saving */ - public abstract boolean hasPersistentMetadata(); + public abstract boolean hasMetadata(); /** diff --git a/src/mightypork/rogue/world/tile/renderers/BasicTileRenderer.java b/src/mightypork/rogue/world/tile/renderers/BasicTileRenderer.java index 1a185ee..7ca2e71 100644 --- a/src/mightypork/rogue/world/tile/renderers/BasicTileRenderer.java +++ b/src/mightypork/rogue/world/tile/renderers/BasicTileRenderer.java @@ -4,13 +4,12 @@ package mightypork.rogue.world.tile.renderers; 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.TileRenderer; +import mightypork.rogue.world.level.render.TileRenderContext; public class BasicTileRenderer extends TileRenderer { - private TxSheet sheet; + private final TxSheet sheet; public BasicTileRenderer(String sheetKey) diff --git a/src/mightypork/rogue/world/tile/renderers/NullTileRenderer.java b/src/mightypork/rogue/world/tile/renderers/NullTileRenderer.java index aaa612f..4280f53 100644 --- a/src/mightypork/rogue/world/tile/renderers/NullTileRenderer.java +++ b/src/mightypork/rogue/world/tile/renderers/NullTileRenderer.java @@ -1,7 +1,7 @@ package mightypork.rogue.world.tile.renderers; -import mightypork.rogue.world.map.TileRenderContext; -import mightypork.rogue.world.tile.TileRenderer; + +import mightypork.rogue.world.level.render.TileRenderContext; public class NullTileRenderer extends TileRenderer { diff --git a/src/mightypork/rogue/world/tile/TileRenderer.java b/src/mightypork/rogue/world/tile/renderers/TileRenderer.java similarity index 82% rename from src/mightypork/rogue/world/tile/TileRenderer.java rename to src/mightypork/rogue/world/tile/renderers/TileRenderer.java index c83af77..1960353 100644 --- a/src/mightypork/rogue/world/tile/TileRenderer.java +++ b/src/mightypork/rogue/world/tile/renderers/TileRenderer.java @@ -1,9 +1,9 @@ -package mightypork.rogue.world.tile; +package mightypork.rogue.world.tile.renderers; import mightypork.rogue.world.item.Item; -import mightypork.rogue.world.map.TileRenderContext; -import mightypork.rogue.world.tile.renderers.NullTileRenderer; +import mightypork.rogue.world.level.render.TileRenderContext; +import mightypork.rogue.world.tile.DroppedItemRenderer; /** diff --git a/src/mightypork/util/ion/IonBundle.java b/src/mightypork/util/ion/IonBundle.java index 53aefd7..a421644 100644 --- a/src/mightypork/util/ion/IonBundle.java +++ b/src/mightypork/util/ion/IonBundle.java @@ -42,6 +42,29 @@ public class IonBundle implements IonBinary { } + public void loadBundle(String key, IonBundle bundle) + { + if (!containsKey(key)) return; + + final IonBundle ib = get(key, null); + + bundle.clear(); + bundle.putAll(ib); + } + + + public boolean containsKey(Object key) + { + return backingMap.containsKey(key); + } + + + public boolean containsValue(Object value) + { + return backingMap.containsValue(value); + } + + public Map getMap(String key) { final Map m = new HashMap<>(); @@ -50,12 +73,12 @@ public class IonBundle implements IonBinary { } - public void loadMap(String key, Map map) + public void loadMap(String key, Map filled) { final IonMapWrapper imw = get(key, null); if (imw == null) return; - map.clear(); - imw.fill(map); + filled.clear(); + imw.fill(filled); } @@ -67,12 +90,12 @@ public class IonBundle implements IonBinary { } - public void loadSequence(String key, Collection sequence) + public void loadSequence(String key, Collection filled) { final IonSequenceWrapper isw = get(key, null); if (isw == null) return; - sequence.clear(); - isw.fill(sequence); + filled.clear(); + isw.fill(filled); } @@ -262,6 +285,12 @@ public class IonBundle implements IonBinary { } + public void putAll(IonBundle otherBundle) + { + backingMap.putAll(otherBundle.backingMap); + } + + @Override public String toString() { diff --git a/src/mightypork/util/ion/IonInput.java b/src/mightypork/util/ion/IonInput.java index 1841a75..083e3bb 100644 --- a/src/mightypork/util/ion/IonInput.java +++ b/src/mightypork/util/ion/IonInput.java @@ -224,7 +224,7 @@ public class IonInput { */ public IonBundle readBundle() throws IOException { - IonBundle ib = new IonBundle(); + final IonBundle ib = new IonBundle(); ib.load(this); return ib; } diff --git a/src/mightypork/util/ion/IonOutput.java b/src/mightypork/util/ion/IonOutput.java index 2be5533..f443b78 100644 --- a/src/mightypork/util/ion/IonOutput.java +++ b/src/mightypork/util/ion/IonOutput.java @@ -270,7 +270,7 @@ public class IonOutput { } if (obj instanceof IonBundled) { - throw new IOException("Bundled objects cannot be written to ION stream directly."); + throw new IOException("Bundled objects cannot be written to ION stream directly at " + obj); } if (obj instanceof Boolean) {