v5stable
Ondřej Hruška 10 years ago
parent 880bcfb553
commit 9e4a237192
  1. 2
      src/mightypork/rogue/world/ClientWorld.java
  2. 16
      src/mightypork/rogue/world/ClientWorldAccess.java
  3. 64
      src/mightypork/rogue/world/LevelRenderer.java
  4. 6
      src/mightypork/rogue/world/LocalWorldAccess.java
  5. 25
      src/mightypork/rogue/world/ServerWorld.java
  6. 2
      src/mightypork/rogue/world/WorldEntity.java
  7. 48
      src/mightypork/rogue/world/map/Level.java
  8. 2
      src/mightypork/rogue/world/tile/DroppedItemRenderer.java
  9. 80
      src/mightypork/rogue/world/tile/Tile.java
  10. 98
      src/mightypork/rogue/world/tile/TileModel.java
  11. 50
      src/mightypork/rogue/world/tile/TileRenderer.java
  12. 25
      src/mightypork/rogue/world/tile/Tiles.java
  13. 40
      src/mightypork/rogue/world/tile/models/AbstractNullTile.java
  14. 6
      src/mightypork/rogue/world/tile/models/Floor.java
  15. 2
      src/mightypork/rogue/world/tile/models/NullFloor.java
  16. 2
      src/mightypork/rogue/world/tile/models/NullWall.java
  17. 46
      src/mightypork/rogue/world/tile/models/SimpleTile.java
  18. 6
      src/mightypork/rogue/world/tile/models/Wall.java
  19. 27
      src/mightypork/rogue/world/tile/renderers/BasicTileRenderer.java
  20. 15
      src/mightypork/rogue/world/tile/renderers/NullTileRenderer.java
  21. 17
      src/mightypork/util/ion/IonInput.java
  22. 5
      src/mightypork/util/ion/IonOutput.java

@ -20,7 +20,7 @@ public class ClientWorld implements Updateable, WorldAccess {
@Override
public void update(double delta)
{
player.updateVisual(delta);
player.update(delta);
level.updateVisual(player, delta);
}

@ -0,0 +1,16 @@
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();
}

@ -0,0 +1,64 @@
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();
}
}
}
}

@ -1,6 +0,0 @@
package mightypork.rogue.world;
public class LocalWorldAccess implements WorldAccess {
}

@ -2,9 +2,8 @@ package mightypork.rogue.world;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import mightypork.rogue.world.map.Level;
import mightypork.util.control.timing.Updateable;
import mightypork.util.ion.IonBundle;
@ -67,20 +66,18 @@ public class ServerWorld implements IonBundled, Updateable, WorldAccess {
@Override
public void update(double delta)
{
Set<Integer> occupiedLevels = new HashSet<>();
// food meters and such
for (final PlayerEntity pl : players.values()) {
if(pl.isConnected()) pl.updateLogic(this, delta);
if(pl.isConnected()) {
pl.updateLogic(this, delta);
occupiedLevels.add(pl.getPosition().floor);
}
}
for (int level = 0; level < levels.size(); level++) {
// more than 1 player can be on floor, update for all of them
for (final PlayerEntity pl : players.values()) {
if (pl.getPosition().floor == level) {
levels.get(level).updateLogic(this, pl, delta);
}
}
for(int i : occupiedLevels) {
levels.get(i).updateLogic(this, delta);
}
}
@ -97,7 +94,7 @@ public class ServerWorld implements IonBundled, Updateable, WorldAccess {
}
public long genEid()
public long generateEntityId()
{
return eid++;
}

@ -20,7 +20,7 @@ public abstract class WorldEntity implements IonBundled {
public WorldEntity(ServerWorld world, WorldPos pos) {
this.serial_id = world.genEid();
this.serial_id = world.generateEntityId();
this.position.setTo(pos);
}

@ -41,11 +41,13 @@ public class Level implements MapAccess, IonBinary {
private transient NoiseGen noiseGen;
public Level() {
public Level()
{
}
public Level(int width, int height) {
public Level(int width, int height)
{
this.width = width;
this.height = height;
buildArray();
@ -148,9 +150,8 @@ public class Level implements MapAccess, IonBinary {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// no mark
final Tile t = new Tile();
t.load(in);
tiles[y][x] = t;
tiles[y][x] = new Tile();
tiles[y][x].load(in);
}
}
}
@ -184,22 +185,36 @@ public class Level implements MapAccess, IonBinary {
}
public void updateLogic(WorldAccess world, MapObserver observer, double delta)
public void updateLogic(WorldAccess world, double delta)
{
updateForObserver(world, observer, delta, true, false);
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)
{
updateForObserver(world, player, delta, false, true);
}
private void updateForObserver(WorldAccess world, MapObserver observer, double delta, boolean logic, boolean visual)
{
final int viewRange = observer.getViewRange();
final WorldPos eyepos = observer.getViewPosition();
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;
@ -214,8 +229,7 @@ public class Level implements MapAccess, IonBinary {
for (int y = y1; y <= y2; y++) {
for (int x = x1; x <= x2; x++) {
if (logic) getTile(x, y).updateLogic(world, delta);
if (visual) getTile(x, y).updateVisual(world, delta);
getTile(x, y).updateVisual(world, this, delta);
}
}
}

@ -37,7 +37,7 @@ public class DroppedItemRenderer {
}
public void updateVisual(double delta)
public void update(double delta)
{
itemAnim.update(delta);
}

@ -6,6 +6,7 @@ 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.util.ion.IonBinary;
@ -14,24 +15,27 @@ import mightypork.util.ion.IonInput;
import mightypork.util.ion.IonOutput;
/**
* Tile data bundle. Client only renders.
*
* @author MightyPork
*/
public final class Tile implements IonBinary {
public static final short ION_MARK = 50;
private transient TileModel model;
private TileModel model;
private TileRenderer renderer;
/**
* Temporary storage for the model (unlocked door state, lever switched etc)
*/
public transient Object modelData;
public int id;
/** Animator field for the model to use, if needed */
public transient Animator anim;
private final Stack<Item> items = new Stack<>();
private transient DroppedItemRenderer itemRenderer; // lazy
/** persistent field for model, reflected by renderer */
public final IonBundle metadata = new IonBundle();
public int id;
private final Stack<Item> items = new Stack<>();
/** non-persistent data field for model */
public Object tmpdata;
public Tile(int id)
@ -42,8 +46,7 @@ public final class Tile implements IonBinary {
public Tile(TileModel model)
{
this.model = model;
this.id = model.id;
setModel(model);
}
@ -52,12 +55,20 @@ public final class Tile implements IonBinary {
}
private void setModel(TileModel model)
{
this.model = model;
this.id = model.id;
this.renderer = model.renderer;
}
public void render(TileRenderContext context)
{
model.render(context);
renderer.render(context);
if (hasItems()) {
getItemRenderer().render(items.peek(), context);
renderer.renderItemOnTile(items.peek(), context);
}
}
@ -71,10 +82,8 @@ public final class Tile implements IonBinary {
out.writeSequence(items);
}
if (model.hasMetadata()) {
final IonBundle ib = new IonBundle();
model.saveMetadata(this, ib);
out.writeBundle(ib);
if (model.hasPersistentMetadata()) {
out.writeBundle(metadata);
}
}
@ -84,41 +93,42 @@ public final class Tile implements IonBinary {
{
id = in.readIntByte();
// check model
// if model changed
if (model == null || id != model.id) {
model = Tiles.get(id);
setModel(Tiles.get(id));
}
if (model.hasDroppedItems()) {
in.readSequence(items);
}
if (model.hasMetadata()) {
model.loadMetadata(this, in.readBundle());
if (model.hasPersistentMetadata()) {
in.readBundle(metadata);
}
}
public void updateLogic(WorldAccess world, double delta)
/**
* Update tile logic state (on server)
*
* @param world the world
* @param delta delta time
*/
public void updateLogic(WorldAccess world, Level level, double delta)
{
model.updateLogic(this, world, delta);
model.updateLogic(this, world, level, delta);
}
public void updateVisual(WorldAccess world, double delta)
public void updateVisual(WorldAccess world, Level level, double delta)
{
model.updateVisual(this, world, delta);
if (hasItems()) {
getItemRenderer().updateVisual(delta);
}
model.updateVisual(this, world, level, delta);
}
private DroppedItemRenderer getItemRenderer()
public boolean isWalkable()
{
if (itemRenderer == null) {
itemRenderer = new DroppedItemRenderer();
}
return itemRenderer;
return model.isWalkable(this);
}

@ -2,13 +2,12 @@ package mightypork.rogue.world.tile;
import mightypork.rogue.world.WorldAccess;
import mightypork.rogue.world.map.TileRenderContext;
import mightypork.util.annotations.DefaultImpl;
import mightypork.util.ion.IonBundle;
import mightypork.rogue.world.map.Level;
import mightypork.rogue.world.tile.renderers.BasicTileRenderer;
/**
* Singleton-like tile implementation
* Tile model (logic of a tile)
*
* @author MightyPork
*/
@ -16,51 +15,50 @@ public abstract class TileModel {
/** Model ID */
public final int id;
public TileRenderer renderer = TileRenderer.NONE;
public TileModel(int id) {
public TileModel(int id)
{
Tiles.register(id, this);
this.id = id;
}
/**
* Create a tile. In case of null tiles / tiles with absolutely no
* variability, the same instance can be returned over and over (created ie.
* using lazy load)
*
* @return new tile with this model
*/
@DefaultImpl
public Tile createTile()
public TileModel setRenderer(TileRenderer renderer)
{
return new Tile(this);
this.renderer = renderer;
return this;
}
public TileModel setTexture(String sheetKey)
{
this.renderer = new BasicTileRenderer(sheetKey);
return this;
}
/**
* Render the tile.
*
* @param context
* @return new tile of this type; if 100% invariant, can return cached one.
*/
public abstract void render(TileRenderContext context);
public Tile createTile()
{
return new Tile(this);
}
/**
* @param tile
* @return is walkable at the current conditions
* @return true if walkable right now
*/
public abstract boolean isWalkable(Tile tile);
/**
* Check if the tile is walkable at some conditions. Used for world
* generation to distinguish between doors etc and regular walls.<br>
* Null tile should return true, if it can be replaced by a regular floor.
*
* @return if it's potentially walkable
* @return true if walkable at some conditions
*/
public abstract boolean isPotentiallyWalkable();
public abstract boolean isWalkable();
public boolean isNullTile()
@ -70,58 +68,26 @@ public abstract class TileModel {
/**
* Update tile state etc
*
* @param tile tile
* @param world
* @param delta delta time
*/
public abstract void updateLogic(Tile tile, WorldAccess world, double delta);
/**
* Update tile effects
*
* @param tile tile
* @param world
* @param delta delta time
* Update tile in world, called by server
*/
public abstract void updateVisual(Tile tile, WorldAccess world, double delta);
public abstract void updateLogic(Tile tile, WorldAccess world, Level level, double delta);
/**
* Store tile metadata (door lock state etc)
*
* @param tile stored tile
* @param ib written data bundle
* Update tile visuals (particles / update for renderer)
*/
public abstract void saveMetadata(Tile tile, IonBundle ib);
public abstract void updateVisual(Tile tile, WorldAccess world, Level level, double delta);
/**
* Load from an IonBundle. The bundle is guaranteed to not be null, but
* could be empty.
*
* @param tile loaded tile
* @param ib item data bundle
* @return true if this item type has metadata worth saving
*/
public abstract void loadMetadata(Tile tile, IonBundle ib);
public abstract boolean hasPersistentMetadata();
/**
* True if this tile's data should be saved/loaded.<br>
* Must be a constant value.
*
* @return has data
*/
public abstract boolean hasMetadata();
/**
* Check if the tile can hold dropped items. Walls and such can return false
* to save disk space (no need to write empty list).
*
* @return true
* @return true if this item can have dropped items
*/
public abstract boolean hasDroppedItems();
}

@ -0,0 +1,50 @@
package mightypork.rogue.world.tile;
import mightypork.rogue.world.item.Item;
import mightypork.rogue.world.map.TileRenderContext;
import mightypork.rogue.world.tile.renderers.NullTileRenderer;
/**
* Renderer for a tile model, in client
*
* @author MightyPork
*/
public abstract class TileRenderer {
public static final TileRenderer NONE = new NullTileRenderer();
private DroppedItemRenderer itemRenderer;
/**
* Update tile renderer
*
* @param delta delta time
*/
public void update(double delta)
{
if (itemRenderer != null) {
itemRenderer.update(delta);
}
}
/**
* Render the tile.
*
* @param context
*/
public abstract void render(TileRenderContext context);
public void renderItemOnTile(Item item, TileRenderContext context)
{
if (itemRenderer == null) {
itemRenderer = new DroppedItemRenderer();
}
itemRenderer.render(item, context);
}
}

@ -18,16 +18,21 @@ public final class Tiles {
public static final TileModel NULL_SOLID = new NullWall(0);
public static final TileModel NULL_EMPTY = new NullFloor(1);
public static final TileModel BRICK_FLOOR_VINES = new Floor(2, "tile.floor.mossy_bricks");
public static final TileModel BRICK_WALL_VINES = new Wall(3, "tile.wall.mossy_bricks");
public static final TileModel BRICK_FLOOR_RECT = new Floor(4, "tile.floor.rect_bricks");
public static final TileModel BRICK_WALL_SMALL = new Wall(5, "tile.wall.small_bricks");
public static final TileModel SANDSTONE_FLOOR = new Floor(6, "tile.floor.sandstone");
public static final TileModel SANDSTONE_WALL = new Wall(7, "tile.wall.sandstone");
public static final TileModel BRCOBBLE_FLOOR = new Floor(8, "tile.floor.brown_cobble");
public static final TileModel BRCOBBLE_WALL = new Wall(9, "tile.wall.brown_cobble");
public static final TileModel CRYSTAL_FLOOR = new Floor(10, "tile.floor.crystal");
public static final TileModel CRYSTAL_WALL = new Wall(11, "tile.wall.crystal");
public static final TileModel BRICK_FLOOR_VINES = new Floor(2).setTexture("tile.floor.mossy_bricks");
public static final TileModel BRICK_WALL_VINES = new Wall(3).setTexture("tile.wall.mossy_bricks");
public static final TileModel BRICK_FLOOR_RECT = new Floor(4).setTexture("tile.floor.rect_bricks");
public static final TileModel BRICK_WALL_SMALL = new Wall(5).setTexture("tile.wall.small_bricks");
public static final TileModel SANDSTONE_FLOOR = new Floor(6).setTexture("tile.floor.sandstone");
public static final TileModel SANDSTONE_WALL = new Wall(7).setTexture("tile.wall.sandstone");
public static final TileModel BRCOBBLE_FLOOR = new Floor(8).setTexture("tile.floor.brown_cobble");
public static final TileModel BRCOBBLE_WALL = new Wall(9).setTexture("tile.wall.brown_cobble");
public static final TileModel CRYSTAL_FLOOR = new Floor(10).setTexture("tile.floor.crystal");
public static final TileModel CRYSTAL_WALL = new Wall(11).setTexture("tile.wall.crystal");
static void register(int id, TileModel model)

@ -1,10 +1,7 @@
package mightypork.rogue.world.tile.models;
import mightypork.rogue.world.map.TileRenderContext;
import mightypork.rogue.world.tile.Tile;
import mightypork.rogue.world.tile.TileModel;
import mightypork.util.ion.IonBundle;
/**
@ -12,7 +9,7 @@ import mightypork.util.ion.IonBundle;
*
* @author MightyPork
*/
public abstract class AbstractNullTile extends TileModel {
public abstract class AbstractNullTile extends SimpleTile {
private Tile inst;
@ -23,25 +20,6 @@ public abstract class AbstractNullTile extends TileModel {
}
@Override
public void render(TileRenderContext context)
{
}
@Override
public void updateLogic(Tile tile, double delta)
{
}
@Override
public boolean isWalkable(Tile tile)
{
return isPotentiallyWalkable();
}
@Override
public boolean isNullTile()
{
@ -61,28 +39,20 @@ public abstract class AbstractNullTile extends TileModel {
@Override
public boolean hasMetadata()
public boolean hasPersistentMetadata()
{
return false;
}
@Override
public void loadMetadata(Tile tile, IonBundle ib)
{
}
@Override
public void saveMetadata(Tile tile, IonBundle ib)
public boolean hasDroppedItems()
{
return false;
}
@Override
public boolean hasDroppedItems()
{
return false;
}
public abstract boolean isWalkable();
}

@ -8,14 +8,14 @@ package mightypork.rogue.world.tile.models;
*/
public class Floor extends SimpleTile {
public Floor(int id, String sheetKey)
public Floor(int id)
{
super(id, sheetKey);
super(id);
}
@Override
public boolean isPotentiallyWalkable()
public boolean isWalkable()
{
return true;
}

@ -10,7 +10,7 @@ public class NullFloor extends AbstractNullTile {
@Override
public boolean isPotentiallyWalkable()
public boolean isWalkable()
{
return true;
}

@ -10,7 +10,7 @@ public class NullWall extends AbstractNullTile {
@Override
public boolean isPotentiallyWalkable()
public boolean isWalkable()
{
return false;
}

@ -1,79 +1,53 @@
package mightypork.rogue.world.tile.models;
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.WorldAccess;
import mightypork.rogue.world.map.Level;
import mightypork.rogue.world.tile.Tile;
import mightypork.rogue.world.tile.TileModel;
import mightypork.util.annotations.DefaultImpl;
import mightypork.util.ion.IonBundle;
/**
* Basic implementation of a tile with coord-random texture and no animation.
* Basic implementation of a tile.
*
* @author MightyPork
*/
public abstract class SimpleTile extends TileModel {
protected final TxSheet sheet;
public SimpleTile(int id, String sheetKey)
public SimpleTile(int id)
{
super(id);
this.sheet = Res.getTxSheet(sheetKey);
}
@Override
public void render(TileRenderContext context)
{
Render.quadTextured(context.getRect(), sheet.getRandomQuad(context.getTileNoise()));
}
@Override
@DefaultImpl
public void updateLogic(Tile tile, double delta)
{
}
/*
* Items can override this if their walkability changes based on something
*/
@Override
public boolean isWalkable(Tile tile)
{
return isPotentiallyWalkable();
return isWalkable();
}
@Override
public abstract boolean isPotentiallyWalkable();
public abstract boolean isWalkable();
@Override
@DefaultImpl
public boolean hasMetadata()
public boolean hasPersistentMetadata()
{
return false; // it's a SIMPLE tile
return false;
}
@Override
@DefaultImpl
public void loadMetadata(Tile tile, IonBundle ib)
public void updateLogic(Tile tile, WorldAccess world, Level level, double delta)
{
}
@Override
@DefaultImpl
public void saveMetadata(Tile tile, IonBundle ib)
public void updateVisual(Tile tile, WorldAccess world, Level level, double delta)
{
}
}

@ -8,14 +8,14 @@ package mightypork.rogue.world.tile.models;
*/
public class Wall extends SimpleTile {
public Wall(int id, String sheetKey)
public Wall(int id)
{
super(id, sheetKey);
super(id);
}
@Override
public boolean isPotentiallyWalkable()
public boolean isWalkable()
{
return false;
}

@ -0,0 +1,27 @@
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;
public class BasicTileRenderer extends TileRenderer {
private TxSheet sheet;
public BasicTileRenderer(String sheetKey)
{
this.sheet = Res.getTxSheet(sheetKey);
}
@Override
public void render(TileRenderContext context)
{
Render.quadTextured(context.getRect(), sheet.getRandomQuad(context.getTileNoise()));
}
}

@ -0,0 +1,15 @@
package mightypork.rogue.world.tile.renderers;
import mightypork.rogue.world.map.TileRenderContext;
import mightypork.rogue.world.tile.TileRenderer;
public class NullTileRenderer extends TileRenderer {
@Override
public void render(TileRenderContext context)
{
// nope
}
}

@ -219,9 +219,24 @@ public class IonInput {
}
/**
* Read bundle without a mark
*/
public IonBundle readBundle() throws IOException
{
return (IonBundle) readObject();
IonBundle ib = new IonBundle();
ib.load(this);
return ib;
}
/**
* Read bundle without a mark, load into a provided one
*/
public void readBundle(IonBundle filled) throws IOException
{
filled.clear();
filled.load(this);
}

@ -181,9 +181,12 @@ public class IonOutput {
}
/**
* Write a bundle without a mark
*/
public void writeBundle(IonBundle bundle) throws IOException
{
writeObject(bundle);
bundle.save(this);
}

Loading…
Cancel
Save