New 16px textures, player sprite, entity system.

v5stable
Ondřej Hruška 11 years ago
parent 3e69505787
commit 6761b69b63
  1. BIN
      res/img/dudes.png
  2. BIN
      res/img/dudes.xcf
  3. BIN
      res/img/tiles16.png
  4. BIN
      res/img/tiles16.xcf
  5. 100
      src/mightypork/gamecore/render/Render.java
  6. 1
      src/mightypork/gamecore/render/Screenshot.java
  7. 39
      src/mightypork/gamecore/render/textures/TxQuad.java
  8. 19
      src/mightypork/gamecore/render/textures/TxSheet.java
  9. 51
      src/mightypork/rogue/Res.java
  10. 139
      src/mightypork/rogue/screens/ingame/WorldLayer.java
  11. 2
      src/mightypork/rogue/screens/ingame/WorldRenderer.java
  12. 4
      src/mightypork/rogue/world/MapGenerator.java
  13. 37
      src/mightypork/rogue/world/PathStep.java
  14. 46
      src/mightypork/rogue/world/PlayerControl.java
  15. 23
      src/mightypork/rogue/world/World.java
  16. 12
      src/mightypork/rogue/world/WorldPos.java
  17. 121
      src/mightypork/rogue/world/entity/Entity.java
  18. 13
      src/mightypork/rogue/world/entity/models/EntityModel.java
  19. 28
      src/mightypork/rogue/world/entity/models/EntityMoveListener.java
  20. 8
      src/mightypork/rogue/world/entity/models/PlayerModel.java
  21. 9
      src/mightypork/rogue/world/entity/renderers/EntityRenderer.java
  22. 10
      src/mightypork/rogue/world/entity/renderers/NullEntityRenderer.java
  23. 43
      src/mightypork/rogue/world/entity/renderers/PlayerRenderer.java
  24. 64
      src/mightypork/rogue/world/level/Level.java
  25. 2
      src/mightypork/rogue/world/level/render/TileRenderContext.java
  26. 21
      src/mightypork/rogue/world/tile/Tile.java
  27. 33
      src/mightypork/rogue/world/tile/Tiles.java
  28. 6
      src/mightypork/rogue/world/tile/models/AbstractNullTile.java
  29. 6
      src/mightypork/rogue/world/tile/models/Floor.java
  30. 3
      src/mightypork/rogue/world/tile/models/TileModel.java
  31. 7
      src/mightypork/rogue/world/tile/models/Wall.java
  32. 62
      src/mightypork/rogue/world/tile/renderers/BasicTileRenderer.java

Binary file not shown.

After

Width:  |  Height:  |  Size: 835 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

@ -5,11 +5,9 @@ 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;
@ -430,93 +428,69 @@ public class Render {
* Render textured rect
*
* @param quad rectangle (px)
* @param uvs texture coords rectangle (0-1)
* @param texture texture instance
* @param txquad texture quad
*/
public static void quadTextured(Rect quad, TxQuad txquad)
{
quadTextured(quad, txquad, Color.WHITE);
}
/**
* Render textured rect
*
* @param quad rectangle (px)
* @param txquad texture instance
* @param tint color tint
*/
public static void quadTextured(Rect quad, Rect uvs, GLTexture texture, Color tint)
public static void quadTextured(Rect quad, TxQuad txquad, Color tint)
{
if (!batchTexturedQuadMode) {
glEnable(GL_TEXTURE_2D);
texture.bind();
txquad.tx.bind();
glBegin(GL_QUADS);
}
setColor(tint);
final RectDigest q = quad.digest();
final RectDigest u = uvs.digest();
final RectDigest u = txquad.uvs.digest();
final double w = texture.getWidth01();
final double h = texture.getHeight01();
double tL = u.left, tR = u.right, tT = u.top, tB = u.bottom;
// handle flip
if (txquad.isFlippedY()) {
final double swap = tT;
tT = tB;
tB = swap;
}
if (txquad.isFlippedX()) {
final double swap = tL;
tL = tR;
tR = swap;
}
final double w = txquad.tx.getWidth01();
final double h = txquad.tx.getHeight01();
// quad with texture
glTexCoord2d(u.x * w, u.bottom * h);
glTexCoord2d(tL * w, tB * h);
glVertex2d(q.left, q.bottom);
glTexCoord2d(u.right * w, u.bottom * h);
glTexCoord2d(tR * w, tB * h);
glVertex2d(q.right, q.bottom);
glTexCoord2d(u.right * w, u.top * h);
glTexCoord2d(tR * w, tT * h);
glVertex2d(q.right, q.top);
glTexCoord2d(u.left * w, u.top * h);
glTexCoord2d(tL * w, tT * h);
glVertex2d(q.left, q.top);
if (!batchTexturedQuadMode) glEnd();
}
/**
* Render textured rect
*
* @param quad rectangle (px)
* @param uvs texture coords rectangle (px)
* @param texture texture instance
*/
public static void quadTextured(Rect quad, Rect uvs, GLTexture texture)
{
quadTextured(quad, uvs, texture, Color.WHITE);
}
/**
* Render textured rect
*
* @param quad rectangle (px)
* @param texture texture instance
*/
public static void quadTextured(Rect quad, GLTexture texture)
{
quadTextured(quad, Rect.ONE, texture, Color.WHITE);
}
/**
* Render textured rect
*
* @param quad rectangle (px)
* @param txquad texture quad
*/
public static void quadTextured(Rect quad, TxQuad txquad)
{
quadTextured(quad, txquad, Color.WHITE);
}
/**
* Render textured rect
*
* @param quad rectangle (px)
* @param txquad texture instance
* @param tint color tint
*/
public static void quadTextured(Rect quad, TxQuad txquad, Color tint)
{
quadTextured(quad, txquad.uvs, txquad.tx, tint);
}
/**
* Setup Ortho projection for 2D graphics
*

@ -75,6 +75,7 @@ public class Screenshot {
*/
public void save(File file) throws IOException
{
file.getParentFile().mkdirs();
ImageIO.write(getImage(), "PNG", file);
}
}

@ -17,6 +17,9 @@ public class TxQuad {
/** Coords in texture (0-1) */
public final RectConst uvs;
private boolean flipX;
private boolean flipY;
/**
* TxQuad from origin and size in pixels
@ -88,6 +91,8 @@ public class TxQuad {
{
this.tx = txQuad.tx;
this.uvs = txQuad.uvs;
this.flipX = txQuad.flipX;
this.flipY = txQuad.flipY;
}
@ -113,4 +118,38 @@ public class TxQuad {
{
return new TxSheet(this, width, height);
}
/**
* @return copy flipped X
*/
public TxQuad flipX()
{
final TxQuad copy = new TxQuad(this);
copy.flipX ^= true;
return copy;
}
/**
* @return copy flipped Y
*/
public TxQuad flipY()
{
final TxQuad copy = new TxQuad(this);
copy.flipY ^= true;
return copy;
}
public boolean isFlippedY()
{
return flipY;
}
public boolean isFlippedX()
{
return flipX;
}
}

@ -3,6 +3,8 @@ package mightypork.gamecore.render.textures;
import java.util.Random;
import mightypork.util.logging.Log;
/**
* Basic sprite sheet
@ -39,6 +41,18 @@ public class TxSheet {
}
/**
* Get a quad based on ratio 0-1 (0: first, 1: last)
*
* @param ratio ratio
* @return quad
*/
public TxQuad getQuad(double ratio)
{
return getQuad((int) Math.round((count - 1) * ratio));
}
/**
* Get quad of index
*
@ -47,8 +61,9 @@ public class TxSheet {
*/
public TxQuad getQuad(int index)
{
if (index < 0 || index > count) {
throw new IndexOutOfBoundsException("Index out of bounds: " + index + ", allowed: 0.." + count);
if (index < 0 || index >= count) {
Log.w("Index out of bounds: " + index + ", allowed: 0.." + count);
index = index % count;
}
// lazy - init only when needed

@ -61,10 +61,13 @@ public final class Res {
private static void loadTextures()
{
GLTexture texture;
QuadGrid tiles;
// tests
texture = textures.loadTexture("test.kitten", "/res/img/kitten.png", FilterMode.LINEAR, WrapMode.CLAMP);
texture = textures.loadTexture("test.kitten2", "/res/img/kitten_npot.png", FilterMode.LINEAR, WrapMode.CLAMP);
// gui
texture = textures.loadTexture("gui1", "/res/img/gui1.png", FilterMode.NEAREST, WrapMode.CLAMP);
final QuadGrid gui = texture.grid(4, 4);
textures.addQuad("item_frame", gui.makeQuad(0, 0));
@ -76,19 +79,41 @@ 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/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));
textures.addSheet("tile.wall.small_bricks", tiles.makeSheet(0, 0, 4, 1));
textures.addSheet("tile.floor.mossy_bricks", tiles.makeSheet(16, 5, 7, 1));
textures.addSheet("tile.floor.rect_bricks", tiles.makeSheet(23, 5, 4, 1));
textures.addSheet("tile.wall.sandstone", tiles.makeSheet(0, 3, 10, 1));
textures.addSheet("tile.floor.sandstone", tiles.makeSheet(0, 6, 10, 1));
textures.addSheet("tile.wall.brown_cobble", tiles.makeSheet(0, 8, 8, 1));
textures.addSheet("tile.floor.brown_cobble", tiles.makeSheet(0, 11, 9, 1));
textures.addSheet("tile.floor.crystal", tiles.makeSheet(4, 5, 6, 1));
textures.addSheet("tile.wall.crystal", tiles.makeSheet(12, 2, 14, 1));
// huge sheet
// texture = textures.loadTexture("tiles", "/res/img/tiles.png", FilterMode.NEAREST, WrapMode.CLAMP);
// tiles = texture.grid(32, 32);
// textures.addSheet("tile.wall.mossy_bricks", tiles.makeSheet(4, 0, 7, 1));
// textures.addSheet("tile.wall.small_bricks", tiles.makeSheet(0, 0, 4, 1));
// textures.addSheet("tile.floor.mossy_bricks", tiles.makeSheet(16, 5, 7, 1));
// textures.addSheet("tile.floor.rect_bricks", tiles.makeSheet(23, 5, 4, 1));
// textures.addSheet("tile.wall.sandstone", tiles.makeSheet(0, 3, 10, 1));
// textures.addSheet("tile.floor.sandstone", tiles.makeSheet(0, 6, 10, 1));
// textures.addSheet("tile.wall.brown_cobble", tiles.makeSheet(0, 8, 8, 1));
// textures.addSheet("tile.floor.brown_cobble", tiles.makeSheet(0, 11, 9, 1));
// textures.addSheet("tile.floor.crystal", tiles.makeSheet(4, 5, 6, 1));
// textures.addSheet("tile.wall.crystal", tiles.makeSheet(12, 2, 14, 1));
// sprites
texture = textures.loadTexture("mob", "/res/img/dudes.png", FilterMode.NEAREST, WrapMode.CLAMP);
tiles = texture.grid(8, 8);
textures.addSheet("player", tiles.makeSheet(0, 0, 4, 1));
// small sheet
texture = textures.loadTexture("tiles16", "/res/img/tiles16.png", FilterMode.NEAREST, WrapMode.CLAMP);
tiles = texture.grid(8, 8);
textures.addSheet("tile16.floor.dark", tiles.makeSheet(0, 1, 5, 1));
textures.addSheet("tile16.wall.brick", tiles.makeSheet(0, 0, 5, 1));
textures.addQuad("tile16.shadow.n", tiles.makeQuad(0, 7));
textures.addQuad("tile16.shadow.s", tiles.makeQuad(0, 7).flipY());
textures.addQuad("tile16.shadow.w", tiles.makeQuad(2, 7));
textures.addQuad("tile16.shadow.e", tiles.makeQuad(2, 7).flipX());
textures.addQuad("tile16.shadow.nw", tiles.makeQuad(1, 7));
textures.addQuad("tile16.shadow.ne", tiles.makeQuad(1, 7).flipX());
textures.addQuad("tile16.shadow.sw", tiles.makeQuad(1, 7).flipY());
textures.addQuad("tile16.shadow.se", tiles.makeQuad(1, 7).flipY().flipX());
}

@ -7,9 +7,16 @@ 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.PlayerControl;
import mightypork.rogue.world.World;
import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.entity.models.EntityMoveListener;
import mightypork.rogue.world.level.Level;
import mightypork.util.ion.Ion;
@ -46,63 +53,81 @@ 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);
// }
// }
// });
final PlayerControl c = w.getPlayerControl();
bindKey(new KeyStroke(true, Keys.LEFT), new Runnable() {
@Override
public void run()
{
c.walkWest();
}
});
bindKey(new KeyStroke(true, Keys.RIGHT), new Runnable() {
@Override
public void run()
{
c.walkEast();
}
});
bindKey(new KeyStroke(true, Keys.UP), new Runnable() {
@Override
public void run()
{
c.walkNorth();
}
});
bindKey(new KeyStroke(true, Keys.DOWN), new Runnable() {
@Override
public void run()
{
c.walkSouth();
}
});
c.addMoveListener(new EntityMoveListener() {
private void tryGo(Entity entity)
{
if (InputSystem.isKeyDown(Keys.LEFT)) {
c.walkWest();
} else if (InputSystem.isKeyDown(Keys.RIGHT)) {
c.walkEast();
} else if (InputSystem.isKeyDown(Keys.UP)) {
c.walkNorth();
} else if (InputSystem.isKeyDown(Keys.DOWN)) {
c.walkSouth();
}
}
@Override
public void onStepFinished(Entity entity, World world, Level level)
{
entity.cancelPath(); // halt
tryGo(entity);
}
@Override
public void onPathFinished(Entity entity, World world, Level level)
{
entity.cancelPath(); // halt
tryGo(entity);
}
@Override
public void onPathAborted(Entity entity, World world, Level level)
{
}
});
}

@ -48,7 +48,7 @@ public class WorldRenderer extends InputComponent implements Updateable {
@Override
protected void renderComponent()
{
world.render(this, 8, 6, 64);
world.render(this, 8, 6, 110);
Render.quadGradH(leftShadow, RGB.BLACK, RGB.NONE);
Render.quadGradH(rightShadow, RGB.NONE, RGB.BLACK);

@ -21,8 +21,8 @@ public class MapGenerator {
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));
w.addLevel(createLevel(rand.nextLong(), Tiles.FLOOR_DARK, Tiles.WALL_BRICK));
//w.addLevel(createLevel(rand.nextLong(), Tiles.BRCOBBLE_FLOOR, Tiles.BRCOBBLE_WALL));
// TODO place on start position
w.createPlayer(10, 10, 0);

@ -10,6 +10,37 @@ import mightypork.util.ion.IonOutput;
public class PathStep implements IonBinary {
public static final PathStep NORTH = new PathStep(0, -1);
public static final PathStep SOUTH = new PathStep(0, 1);
public static final PathStep EAST = new PathStep(1, 0);
public static final PathStep WEST = new PathStep(-1, 0);
public static final PathStep NORTH_EAST = new PathStep(1, -1);
public static final PathStep NORTH_WEST = new PathStep(-1, -1);
public static final PathStep SOUTH_EAST = new PathStep(1, 1);
public static final PathStep SOUTH_WEST = new PathStep(-1, 1);
public static final PathStep NONE = new PathStep(0, 0);
public static PathStep make(int x, int y)
{
x = x < 0 ? -1 : x > 0 ? 1 : 0;
y = y < 0 ? -1 : y > 0 ? 1 : 0;
if (x == 0 && y == -1) return NORTH;
if (x == 0 && y == 1) return SOUTH;
if (x == -1 && y == 0) return WEST;
if (x == 1 && y == 0) return EAST;
if (x == -1 && y == -1) return NORTH_WEST;
if (x == 1 && y == -1) return NORTH_EAST;
if (x == -1 && y == 1) return SOUTH_WEST;
if (x == 1 && y == 1) return SOUTH_EAST;
if (x == 0 && y == 0) return NONE;
return new PathStep(x, y);
}
public static final int ION_MARK = 0;
public int x;
@ -18,10 +49,8 @@ public class PathStep implements IonBinary {
public PathStep(int x, int y)
{
this.x = x < 1 ? -1 : x > 0 ? 1 : 0;
this.y = y < 1 ? -1 : y > 0 ? 1 : 0;
y = (int) Math.signum(x);
this.x = x < 0 ? -1 : x > 0 ? 1 : 0;
this.y = y < 0 ? -1 : y > 0 ? 1 : 0;
}

@ -0,0 +1,46 @@
package mightypork.rogue.world;
import mightypork.rogue.world.entity.models.EntityMoveListener;
public class PlayerControl {
private final World world;
public PlayerControl(World w)
{
this.world = w;
}
public void walkNorth()
{
world.playerEntity.addStep(PathStep.NORTH);
}
public void walkSouth()
{
world.playerEntity.addStep(PathStep.SOUTH);
}
public void walkEast()
{
world.playerEntity.addStep(PathStep.EAST);
}
public void walkWest()
{
world.playerEntity.addStep(PathStep.WEST);
}
public void addMoveListener(EntityMoveListener eml)
{
world.playerEntity.addMoveListener(eml);
}
}

@ -22,7 +22,10 @@ public class World implements IonBundled, Updateable {
private final ArrayList<Level> levels = new ArrayList<>();
private final PlayerInfo player = new PlayerInfo();
final PlayerInfo player = new PlayerInfo();
Entity playerEntity;
private final PlayerControl control = new PlayerControl(this);
private long seed; // world seed
private int eid; // next entity ID
@ -57,7 +60,7 @@ public class World implements IonBundled, Updateable {
@Override
public void update(double delta)
{
getCurrentLevel().update(delta);
getCurrentLevel().update(this, delta);
}
@ -89,14 +92,14 @@ public class World implements IonBundled, Updateable {
}
// make entity
int playerEid = getNewEID();
final int playerEid = getNewEID();
final Entity entity = Entities.PLAYER.createEntity(playerEid, new WorldPos(x, y));
playerEntity = Entities.PLAYER.createEntity(playerEid, new WorldPos(x, y));
player.setLevel(level);
player.setEID(playerEid);
levels.get(level).addEntity(entity);
levels.get(level).addEntity(playerEntity);
}
@ -108,9 +111,9 @@ public class World implements IonBundled, Updateable {
* @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)
public void render(RectBound viewport, final int xTiles, final int yTiles, final int minSize)
{
getCurrentLevel().render(player, viewport, yTiles, xTiles, minSize);
getCurrentLevel().render(playerEntity.getPosition(), viewport, xTiles, yTiles, minSize);
}
@ -118,4 +121,10 @@ public class World implements IonBundled, Updateable {
{
return levels.get(player.getLevel());
}
public PlayerControl getPlayerControl()
{
return control;
}
}

@ -82,6 +82,18 @@ public class WorldPos implements IonBundled, Updateable {
}
public double getVisualXOffset()
{
return walkOffset.x();
}
public double getVisualYOffset()
{
return walkOffset.y();
}
public void setTo(int x, int y)
{
this.x = x;

@ -2,14 +2,18 @@ package mightypork.rogue.world.entity;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
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.entity.models.EntityMoveListener;
import mightypork.rogue.world.level.Level;
import mightypork.rogue.world.level.render.EntityRenderContext;
import mightypork.util.ion.IonBinary;
import mightypork.util.ion.IonBundle;
import mightypork.util.ion.IonBundled;
@ -22,23 +26,32 @@ import mightypork.util.ion.IonOutput;
*
* @author MightyPork
*/
public final class Entity implements IonBinary, IonBundled {
public final class Entity implements IonBinary, IonBundled, EntityMoveListener {
// binary & bundled - binary stores via a bundle
public static final int ION_MARK = 52;
private final WorldPos position = new WorldPos();
private final WorldPos position = new WorldPos(); // saved
/** Entity ID */
private int eid = 0;
private int eid = 0; // saved
/** Model ID */
private int id;
private int id; // saved
private final Queue<PathStep> path = new LinkedList<>();
private final Queue<PathStep> path = new LinkedList<>(); // saved
private EntityModel model;
private final IonBundle metadata = new IonBundle();
public final IonBundle metadata = new IonBundle(); // saved
public final IonBundle tmpdata = new IonBundle(); // NOT saved
// used for rendering "facing" sprite
public int lastXMove = 1;
public int lastYMove = 1;
private final List<EntityMoveListener> moveListeners = new ArrayList<>();
private boolean walking = false;
public Entity(int eid, WorldPos pos, EntityModel entityModel)
@ -52,6 +65,10 @@ public final class Entity implements IonBinary, IonBundled {
private void setModel(EntityModel entityModel)
{
// replace listener
if (model != null) moveListeners.remove(model);
moveListeners.add(entityModel);
this.id = entityModel.id;
this.model = entityModel;
}
@ -93,7 +110,10 @@ public final class Entity implements IonBinary, IonBundled {
bundle.putBundled("pos", position);
bundle.putSequence("steps", path);
bundle.put("eid", eid);
bundle.put("metadata", metadata);
if (model.hasMetadata()) {
bundle.put("metadata", metadata);
}
}
@ -110,11 +130,16 @@ public final class Entity implements IonBinary, IonBundled {
bundle.loadSequence("path", path);
eid = bundle.get("eid", eid);
metadata.clear();
bundle.loadBundle("metadata", metadata);
if (model.hasMetadata()) {
metadata.clear();
bundle.loadBundle("metadata", metadata);
}
}
/**
* @return unique entity id
*/
public int getEID()
{
return eid;
@ -150,19 +175,49 @@ public final class Entity implements IonBinary, IonBundled {
position.update(delta);
}
if (position.isFinished()) {
if (walking && position.isFinished()) {
walking = false;
model.onStepFinished(this, world, level);
onStepFinished(this, world, level);
if (!path.isEmpty()) {
// get next step to walk
final PathStep step = path.poll();
position.walk(step.x, step.y, getStepTime());
if (path.isEmpty()) {
onPathFinished(this, world, level);
}
}
if (!walking && !path.isEmpty()) {
walking = true;
final PathStep step = path.poll();
final int projX = position.x + step.x, projY = position.y + step.y;
if (!level.canWalkInto(projX, projY)) {
cancelPath();
onPathAborted(this, world, level);
walking = false;
} else {
// notify AI or whatever
model.onPathFinished(this, world, level);
// tmp for renderer
if (step.x != 0) lastXMove = step.x;
if (step.y != 0) lastYMove = step.y;
position.walk(step.x, step.y, getStepTime());
level.occupyTile(projX, projY);
level.freeTile(position.x, position.y);
}
}
if (!walking) {
model.update(this, level, delta);
}
}
public void render(EntityRenderContext context)
{
model.renderer.render(this, context);
}
@ -189,4 +244,36 @@ public final class Entity implements IonBinary, IonBundled {
return model.getStepTime(this);
}
@Override
public void onStepFinished(Entity entity, World world, Level level)
{
for (final EntityMoveListener l : moveListeners) {
l.onStepFinished(entity, world, level);
}
}
@Override
public void onPathFinished(Entity entity, World world, Level level)
{
for (final EntityMoveListener l : moveListeners) {
l.onStepFinished(entity, world, level);
}
}
@Override
public void onPathAborted(Entity entity, World world, Level level)
{
for (final EntityMoveListener l : moveListeners) {
l.onPathAborted(entity, world, level);
}
}
public void addMoveListener(EntityMoveListener listener)
{
moveListeners.add(listener);
}
}

@ -14,7 +14,7 @@ import mightypork.rogue.world.level.Level;
*
* @author MightyPork
*/
public abstract class EntityModel {
public abstract class EntityModel implements EntityMoveListener {
/** Model ID */
public final int id;
@ -45,7 +45,7 @@ public abstract class EntityModel {
/**
* Update entity
* Entity is idle, waiting for action.
*/
public abstract void update(Entity entity, Level level, double delta);
@ -57,15 +57,20 @@ public abstract class EntityModel {
/**
* @param entity the value is valid for
* @return step time (seconds)
* Get one path step duration (in seconds)
*/
public abstract double getStepTime(Entity entity);
@Override
public abstract void onStepFinished(Entity entity, World world, Level level);
@Override
public abstract void onPathFinished(Entity entity, World world, Level level);
@Override
public abstract void onPathAborted(Entity entity, World world, Level level);
}

@ -0,0 +1,28 @@
package mightypork.rogue.world.entity.models;
import mightypork.rogue.world.World;
import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.level.Level;
public interface EntityMoveListener {
/**
* One step of a path finished
*/
void onStepFinished(Entity entity, World world, Level level);
/**
* Scheduled path finished
*/
void onPathFinished(Entity entity, World world, Level level);
/**
* Path was aborted (bumped into a wall or entity)
*/
void onPathAborted(Entity entity, World world, Level level);
}

@ -4,6 +4,7 @@ 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.entity.renderers.PlayerRenderer;
import mightypork.rogue.world.level.Level;
@ -20,6 +21,7 @@ public class PlayerModel extends EntityModel {
public PlayerModel(int id)
{
super(id);
setRenderer(new PlayerRenderer("player"));
}
@ -64,4 +66,10 @@ public class PlayerModel extends EntityModel {
public void onPathFinished(Entity entity, World world, Level level)
{
}
@Override
public void onPathAborted(Entity entity, World world, Level level)
{
}
}

@ -1,8 +1,15 @@
package mightypork.rogue.world.entity.renderers;
public class EntityRenderer {
import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.level.render.EntityRenderContext;
public abstract class EntityRenderer {
public static final EntityRenderer NONE = new NullEntityRenderer();
public abstract void render(Entity entity, EntityRenderContext context);
}

@ -1,6 +1,16 @@
package mightypork.rogue.world.entity.renderers;
import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.level.render.EntityRenderContext;
public class NullEntityRenderer extends EntityRenderer {
@Override
public void render(Entity entity, EntityRenderContext context)
{
// hell no
}
}

@ -0,0 +1,43 @@
package mightypork.rogue.world.entity.renderers;
import mightypork.gamecore.render.Render;
import mightypork.gamecore.render.textures.TxQuad;
import mightypork.gamecore.render.textures.TxSheet;
import mightypork.rogue.Res;
import mightypork.rogue.world.WorldPos;
import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.level.render.EntityRenderContext;
import mightypork.util.constraints.rect.Rect;
import mightypork.util.math.Calc;
public class PlayerRenderer extends EntityRenderer {
TxSheet sheet;
public PlayerRenderer(String sheetKey)
{
this.sheet = Res.getTxSheet(sheetKey); // expects 1x4
}
@Override
public void render(Entity entity, EntityRenderContext context)
{
TxQuad q = sheet.getQuad(Calc.frag(entity.getPosition().getProgress()));
if (entity.lastXMove == -1) q = q.flipX();
final WorldPos pos = entity.getPosition();
final Rect tileRect = context.getRectForTile(pos.x, pos.y);
final double w = tileRect.width().value();
Rect spriteRect = tileRect.move(pos.getVisualXOffset() * w, pos.getVisualYOffset() * w + w * 0.1);
spriteRect = spriteRect.shrink(w * 0.1);
Render.quadTextured(spriteRect, q);
}
}

@ -7,13 +7,12 @@ 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.World;
import mightypork.rogue.world.WorldPos;
import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.level.render.EntityRenderContext;
import mightypork.rogue.world.level.render.TileRenderContext;
import mightypork.rogue.world.tile.Tile;
import mightypork.rogue.world.tile.Tiles;
@ -170,6 +169,11 @@ public class Level implements MapAccess, IonBinary {
tiles[y][x].load(in);
}
}
// mark tiles as occupied
for (final Entity e : entity_set) {
occupyTile(e.getPosition().x, e.getPosition().y);
}
}
@ -201,7 +205,7 @@ public class Level implements MapAccess, IonBinary {
}
public void update(double delta)
public void update(World w, double delta)
{
// just update them all
for (int y = 0; y < height; y++) {
@ -209,6 +213,10 @@ public class Level implements MapAccess, IonBinary {
getTile(x, y).update(this, delta);
}
}
for (final Entity e : entity_set) {
e.update(w, this, delta);
}
}
@ -232,16 +240,8 @@ public class Level implements MapAccess, IonBinary {
* @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)
public void render(WorldPos pos, RectBound viewport, final int xTiles, final int yTiles, 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();
@ -250,9 +250,9 @@ public class Level implements MapAccess, IonBinary {
final double allowedSizeW = vpW / xTiles;
final double allowedSizeH = vpH / yTiles;
int tileSize = (int) Math.round(Math.max(Math.min(allowedSizeH, allowedSizeW), minSize));
final int tileSize = (int) Math.round(Math.max(Math.min(allowedSizeH, allowedSizeW), minSize));
tileSize -= tileSize % 16;
//tileSize -= tileSize % 8;
final VectConst vpCenter = r.center().sub(tileSize * 0.5, tileSize).freeze(); // 0.5 to center, 1 to move up (down is teh navbar)
@ -280,7 +280,7 @@ public class Level implements MapAccess, IonBinary {
// batch rendering of the tiles
if (USE_BATCH_RENDERING) {
Render.enterBatchTexturedQuadMode(Res.getTexture("tiles"));
Render.enterBatchTexturedQuadMode(Res.getTexture("tiles16"));
}
for (trc.y = y1; trc.y <= y2; trc.y++) {
@ -299,6 +299,12 @@ public class Level implements MapAccess, IonBinary {
trc.renderItems();
}
}
// render entities
final EntityRenderContext erc = new EntityRenderContext(this, mapRect);
for (final Entity e : entity_set) {
e.render(erc);
}
}
@ -327,4 +333,30 @@ public class Level implements MapAccess, IonBinary {
final Entity removed = entity_map.remove(eid);
entity_set.remove(removed);
}
public boolean canWalkInto(int x, int y)
{
final Tile t = getTile(x, y);
return t.isWalkable() && !t.isOccupied();
}
/**
* Mark tile as occupied by an entity
*/
public void occupyTile(int x, int y)
{
getTile(x, y).setOccupied(true);
}
/**
* Mark tile as free (no longet occupied)
*/
public void freeTile(int x, int y)
{
getTile(x, y).setOccupied(false);
}
}

@ -66,11 +66,13 @@ public final class TileRenderContext extends MapRenderContext implements RectBou
map.getTile(x, y).renderTile(this);
}
public void renderItems()
{
map.getTile(x, y).renderItems(this);
}
/**
* Rect of the current tile to draw
*/

@ -34,6 +34,13 @@ public final class Tile implements IonBinary {
/** persistent field for model, reflected by renderer */
public final IonBundle metadata = new IonBundle();
// temporary flag for map.
private boolean occupied;
// for renderer of AO shadows
public byte shadows;
public boolean shadowsComputed;
public Tile(int id)
{
@ -75,6 +82,7 @@ public final class Tile implements IonBinary {
/**
* Render items
*
* @param context
*/
public void renderItems(TileRenderContext context)
@ -85,7 +93,6 @@ public final class Tile implements IonBinary {
}
@Override
public void save(IonOutput out) throws IOException
{
@ -156,4 +163,16 @@ public final class Tile implements IonBinary {
{
return ION_MARK;
}
public boolean isOccupied()
{
return occupied;
}
public void setOccupied(boolean occupied)
{
this.occupied = occupied;
}
}

@ -20,21 +20,24 @@ 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).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");
public static final TileModel FLOOR_DARK = new Floor(2).setTexture("tile16.floor.dark");
public static final TileModel WALL_BRICK = new Wall(3).setTexture("tile16.wall.brick");
// 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");
public static void register(int id, TileModel model)
{

@ -55,4 +55,10 @@ public abstract class AbstractNullTile extends SimpleTile {
@Override
public abstract boolean isWalkable();
@Override
public boolean doesCastShadow()
{
return false;
}
}

@ -27,4 +27,10 @@ public class Floor extends SimpleTile {
return true;
}
@Override
public boolean doesCastShadow()
{
return false;
}
}

@ -63,6 +63,9 @@ public abstract class TileModel {
public abstract boolean isWalkable();
public abstract boolean doesCastShadow();
public boolean isNullTile()
{
return false;

@ -27,4 +27,11 @@ public class Wall extends SimpleTile {
return false;
}
@Override
public boolean doesCastShadow()
{
return true;
}
}

@ -2,25 +2,85 @@ package mightypork.rogue.world.tile.renderers;
import mightypork.gamecore.render.Render;
import mightypork.gamecore.render.textures.TxQuad;
import mightypork.gamecore.render.textures.TxSheet;
import mightypork.rogue.Res;
import mightypork.rogue.world.level.render.TileRenderContext;
import mightypork.rogue.world.tile.Tile;
import mightypork.util.constraints.rect.Rect;
public class BasicTileRenderer extends TileRenderer {
private final TxSheet sheet;
private static boolean inited;
private static TxQuad SH_N, SH_S, SH_E, SH_W, SH_NW, SH_NE, SH_SW, SH_SE;
public BasicTileRenderer(String sheetKey)
{
this.sheet = Res.getTxSheet(sheetKey);
if (!inited) {
SH_N = Res.getTxQuad("tile16.shadow.n");
SH_S = Res.getTxQuad("tile16.shadow.s");
SH_E = Res.getTxQuad("tile16.shadow.e");
SH_W = Res.getTxQuad("tile16.shadow.w");
SH_NW = Res.getTxQuad("tile16.shadow.nw");
SH_NE = Res.getTxQuad("tile16.shadow.ne");
SH_SW = Res.getTxQuad("tile16.shadow.sw");
SH_SE = Res.getTxQuad("tile16.shadow.se");
}
}
@Override
public void render(TileRenderContext context)
{
Render.quadTextured(context.getRect(), sheet.getRandomQuad(context.getTileNoise()));
final Rect rect = context.getRect();
Render.quadTextured(rect, sheet.getRandomQuad(context.getTileNoise()));
final Tile t = context.getTile();
if (t.getModel().doesCastShadow()) return; // no shadows for wall
Tile t2;
if (!t.shadowsComputed) {
// no shadows computed yet
t.shadows = 0; // reset the mask
int move = 0;
for (int y = -1; y <= 1; y++) {
for (int x = -1; x <= 1; x++) {
if (x == 0 && y == 0) continue;
t2 = context.getAdjacentTile(x, y);
if (t2.getModel().doesCastShadow()) {
t.shadows |= 1 << move;
}
move++;
}
}
t.shadowsComputed = true;
}
if (t.shadows == 0) return;
if ((t.shadows & (1 << 0)) != 0) Render.quadTextured(rect, SH_NW);
if ((t.shadows & (1 << 1)) != 0) Render.quadTextured(rect, SH_N);
if ((t.shadows & (1 << 2)) != 0) Render.quadTextured(rect, SH_NE);
if ((t.shadows & (1 << 3)) != 0) Render.quadTextured(rect, SH_W);
if ((t.shadows & (1 << 4)) != 0) Render.quadTextured(rect, SH_E);
if ((t.shadows & (1 << 5)) != 0) Render.quadTextured(rect, SH_SW);
if ((t.shadows & (1 << 6)) != 0) Render.quadTextured(rect, SH_S);
if ((t.shadows & (1 << 7)) != 0) Render.quadTextured(rect, SH_SE);
}
}

Loading…
Cancel
Save