|
@@ -1,756 +1,208 @@
|
|
|
package me.hammerle.supersnuvi.gamelogic;
|
|
|
|
|
|
-import java.io.File;
|
|
|
-import java.util.ArrayList;
|
|
|
import java.util.HashMap;
|
|
|
import java.util.LinkedList;
|
|
|
import java.util.List;
|
|
|
-import java.util.TreeSet;
|
|
|
import java.util.function.Consumer;
|
|
|
import java.util.stream.Collectors;
|
|
|
-import me.hammerle.snuviengine.api.Shader;
|
|
|
-import me.hammerle.snuviengine.api.Texture;
|
|
|
-import me.hammerle.snuviengine.api.TextureRenderer;
|
|
|
-import me.hammerle.snuviengine.util.Rectangle;
|
|
|
import me.hammerle.snuviscript.code.Script;
|
|
|
import me.hammerle.supersnuvi.Game;
|
|
|
-import me.hammerle.supersnuvi.Keys;
|
|
|
import me.hammerle.supersnuvi.entity.Entity;
|
|
|
import me.hammerle.supersnuvi.entity.EntityBuilder;
|
|
|
+import me.hammerle.supersnuvi.rendering.TileUpdater;
|
|
|
import me.hammerle.supersnuvi.tiles.Location;
|
|
|
import me.hammerle.supersnuvi.tiles.Tile;
|
|
|
import me.hammerle.supersnuvi.util.CollisionObject;
|
|
|
import me.hammerle.supersnuvi.util.Utils;
|
|
|
|
|
|
-public final class Level implements ILevel
|
|
|
+public abstract class Level
|
|
|
{
|
|
|
- public final static float ERROR = 1f / 65536f;
|
|
|
-
|
|
|
- public final static Texture TILES = new Texture("resources/tiles.png");
|
|
|
- private final static Texture GUI = new Texture("resources/gui.png");
|
|
|
- private final static TextureRenderer GUI_RENDERER = new TextureRenderer(60);
|
|
|
-
|
|
|
- private final boolean worldLoaded;
|
|
|
- private final LevelData data;
|
|
|
-
|
|
|
- private final String fileName;
|
|
|
- private final String name;
|
|
|
-
|
|
|
- private final HashMap<Integer, Entity> entities = new HashMap<>();
|
|
|
- private final LinkedList<Entity> spawnQueue = new LinkedList<>();
|
|
|
- private Entity hero;
|
|
|
- private int entityCounter = 0;
|
|
|
-
|
|
|
- private boolean shouldReset = false;
|
|
|
- private boolean done = true; // this will be reseted in resetLevel()
|
|
|
-
|
|
|
- private int souls;
|
|
|
- private int maxSouls;
|
|
|
-
|
|
|
- private float time = 0.0f;
|
|
|
-
|
|
|
- private float cameraX = 0.0f;
|
|
|
- private float cameraY = 0.0f;
|
|
|
- private float oldCameraX = 0.0f;
|
|
|
- private float oldCameraY = 0.0f;
|
|
|
-
|
|
|
- private final int meshSize = 16;
|
|
|
- private final int meshLayers;
|
|
|
- private final int meshWidth;
|
|
|
- private final int meshHeight;
|
|
|
- private final TextureRenderer[][][] meshes;
|
|
|
-
|
|
|
- //private int tries = 7;
|
|
|
+ // -------------------------------------------------------------------------
|
|
|
+ // level data
|
|
|
+ // -------------------------------------------------------------------------
|
|
|
|
|
|
- private TreeSet<Point> spawns = new TreeSet<>();
|
|
|
+ private final TileUpdater tileUpdater = new TileUpdater();
|
|
|
|
|
|
- private Script levelScript = null;
|
|
|
- private final LinkedList<String> messages = new LinkedList<>();
|
|
|
+ public abstract LevelData getData();
|
|
|
|
|
|
- public Level(File f)
|
|
|
- {
|
|
|
- this.data = new LevelData(f);
|
|
|
- this.worldLoaded = data.load();
|
|
|
-
|
|
|
- if(worldLoaded)
|
|
|
- {
|
|
|
- this.name = data.getString("name", "error");
|
|
|
-
|
|
|
- meshLayers = data.getLayers() - 1;
|
|
|
- meshWidth = (int) Math.ceil((double) data.getWidth() / meshSize);
|
|
|
- meshHeight = (int) Math.ceil((double) data.getHeight() / meshSize);
|
|
|
- meshes = new TextureRenderer[meshLayers][meshWidth][meshHeight];
|
|
|
- for(int l = 0; l < meshLayers; l++)
|
|
|
- {
|
|
|
- for(int mx = 0; mx < meshWidth; mx++)
|
|
|
- {
|
|
|
- for(int my = 0; my < meshHeight; my++)
|
|
|
- {
|
|
|
- meshes[l][mx][my] = new TextureRenderer(meshSize * meshSize * 2);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // debug stuff
|
|
|
- /*if(name.equals("01-Tech Demo"))
|
|
|
- {
|
|
|
- int l = data.getBackgroundIndex();
|
|
|
-
|
|
|
- }*/
|
|
|
- // end debug stuff
|
|
|
-
|
|
|
- maxSouls = 0;
|
|
|
- data.forEachInteractTile((x, y, tile) ->
|
|
|
- {
|
|
|
- Tile t = Game.get().getTile(tile);
|
|
|
- if(t != null)
|
|
|
- {
|
|
|
- maxSouls += t.getBottleScore();
|
|
|
- }
|
|
|
-
|
|
|
- if(tile == 24) // start tile
|
|
|
- {
|
|
|
- spawns.add(new Point(x, y - 1));
|
|
|
- }
|
|
|
- });
|
|
|
- data.forEachEntity((x, y, tile) ->
|
|
|
- {
|
|
|
- if(tile == 1)
|
|
|
- {
|
|
|
- maxSouls++;
|
|
|
- }
|
|
|
- }, 0, data.getWidth(), 0, data.getHeight());
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- this.name = f.getName();
|
|
|
- maxSouls = 0;
|
|
|
-
|
|
|
- meshLayers = 0;
|
|
|
- meshWidth = 0;
|
|
|
- meshHeight = 0;
|
|
|
- meshes = new TextureRenderer[0][0][0];
|
|
|
- }
|
|
|
- fileName = f.getName();
|
|
|
-
|
|
|
- // there must be at least one spawn
|
|
|
- if(spawns.isEmpty())
|
|
|
- {
|
|
|
- spawns.add(new Point(5, 5));
|
|
|
- }
|
|
|
-
|
|
|
- // make sure hero is spawned before any script starts
|
|
|
- resetLevel();
|
|
|
-
|
|
|
- String scriptName = f.getPath().replace(".map", "");
|
|
|
- // mark current level as active to make currentLevel calls work
|
|
|
- Level l = Game.get().getCurrentLevel();
|
|
|
- Game.get().setCurrentLevel(this);
|
|
|
- levelScript = Game.get().getParser().startScript(false, ".snuvi", scriptName);
|
|
|
- // call level reset here, because levelScript was null in resetLevel()
|
|
|
- callEvent("level_reset");
|
|
|
- // mark previous level as active
|
|
|
- Game.get().setCurrentLevel(l);
|
|
|
- }
|
|
|
-
|
|
|
- // -------------------------------------------------------------------------
|
|
|
- // basic stuff
|
|
|
- // -------------------------------------------------------------------------
|
|
|
-
|
|
|
- @Override
|
|
|
- public Entity getHero()
|
|
|
+ public String getName()
|
|
|
{
|
|
|
- return hero;
|
|
|
+ return "Unknown";
|
|
|
}
|
|
|
|
|
|
- public String getFileName()
|
|
|
+ public String getFileName()
|
|
|
{
|
|
|
- return fileName;
|
|
|
+ return "Unknown";
|
|
|
}
|
|
|
|
|
|
- public String getName()
|
|
|
+ public final TileUpdater getTileUpdater()
|
|
|
{
|
|
|
- return name;
|
|
|
+ return tileUpdater;
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public void finishLevel()
|
|
|
- {
|
|
|
- shouldReset = true;
|
|
|
- done = true;
|
|
|
- }
|
|
|
+ // -------------------------------------------------------------------------
|
|
|
+ // level stats
|
|
|
+ // -------------------------------------------------------------------------
|
|
|
|
|
|
- public boolean shouldFinish()
|
|
|
+ public int getCurrentBottles()
|
|
|
{
|
|
|
- return done;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
- public boolean shouldReset()
|
|
|
+ public int getMaxBottles()
|
|
|
{
|
|
|
- return shouldReset;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public void scheduleReset()
|
|
|
+ public void addBottles(int bottles)
|
|
|
{
|
|
|
- shouldReset = true;
|
|
|
}
|
|
|
|
|
|
- public boolean resetLevel()
|
|
|
+ public float getTime()
|
|
|
{
|
|
|
- Game.get().resetTiles(this);
|
|
|
- data.activateEntities();
|
|
|
- souls = 0;
|
|
|
- time = 0.0f;
|
|
|
- shouldReset = false;
|
|
|
- done = false;
|
|
|
- Entity h = spawnHero(true);
|
|
|
- hero = h;
|
|
|
- entities.clear();
|
|
|
- spawnEntity(hero);
|
|
|
-
|
|
|
- for(int l = 0; l < meshLayers; l++)
|
|
|
- {
|
|
|
- for(int x = 0; x < meshWidth; x++)
|
|
|
- {
|
|
|
- for(int y = 0; y < meshHeight; y++)
|
|
|
- {
|
|
|
- meshes[l][x][y].clear();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- messages.clear();
|
|
|
-
|
|
|
- callEvent("level_reset");
|
|
|
- return false;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public void spawnEntity(Entity ent)
|
|
|
+ public void addMessage(String message)
|
|
|
{
|
|
|
- spawnQueue.add(ent);
|
|
|
}
|
|
|
|
|
|
- public Entity spawnHero(boolean first)
|
|
|
+ public String getMessage()
|
|
|
{
|
|
|
- Entity h = hero;
|
|
|
- Point p;
|
|
|
- if(h == null || hero.getX() < 0 || first)
|
|
|
- {
|
|
|
- // first spawn or out of map, use first spawn
|
|
|
- p = spawns.first();
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- // hero is somewhere in the map, getting last spawn
|
|
|
- p = spawns.floor(new Point(Utils.toBlock(hero.getX()), Utils.toBlock(hero.getY())));
|
|
|
- if(p == null)
|
|
|
- {
|
|
|
- p = spawns.first();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- Entity newHero = EntityBuilder.buildHero(this, Utils.toCoord(p.getX()), Utils.toCoord(p.getY()));
|
|
|
-
|
|
|
- // reset the camera
|
|
|
- oldCameraX = -getViewX(newHero.getCenterX());
|
|
|
- oldCameraY = -getViewY(newHero.getCenterY());
|
|
|
- cameraX = oldCameraX;
|
|
|
- cameraY = oldCameraY;
|
|
|
-
|
|
|
- return newHero;
|
|
|
+ return null;
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public void increaseSouls(int score)
|
|
|
- {
|
|
|
- souls += score;
|
|
|
- }
|
|
|
+ // -------------------------------------------------------------------------
|
|
|
+ // entity related
|
|
|
+ // -------------------------------------------------------------------------
|
|
|
|
|
|
- public int getCurrentBottles()
|
|
|
- {
|
|
|
- return souls;
|
|
|
- }
|
|
|
+ private final HashMap<Integer, Entity> entities = new HashMap<>();
|
|
|
+ private final LinkedList<Entity> spawnQueue = new LinkedList<>();
|
|
|
+ private Entity hero = null;
|
|
|
+ private int entityCounter = 0;
|
|
|
|
|
|
- public LevelData getData()
|
|
|
+ public final Entity getHero()
|
|
|
{
|
|
|
- return data;
|
|
|
+ return hero;
|
|
|
}
|
|
|
|
|
|
- public float getTime()
|
|
|
+ public final void setHero(Entity hero)
|
|
|
{
|
|
|
- return time;
|
|
|
- }
|
|
|
+ this.hero = hero;
|
|
|
+ }
|
|
|
|
|
|
- @Override
|
|
|
- public int getWidth()
|
|
|
+ public final void removeEntities()
|
|
|
{
|
|
|
- return data.getWidth();
|
|
|
+ entities.clear();
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public int getHeight()
|
|
|
+ public final void spawnEntity(Entity ent)
|
|
|
{
|
|
|
- return data.getHeight();
|
|
|
+ spawnQueue.add(ent);
|
|
|
}
|
|
|
-
|
|
|
- // -------------------------------------------------------------------------
|
|
|
- // tick
|
|
|
- // -------------------------------------------------------------------------
|
|
|
|
|
|
- public void tick()
|
|
|
+ public final void tickEntities()
|
|
|
{
|
|
|
- if(worldLoaded)
|
|
|
+ entities.values().removeIf(ent ->
|
|
|
{
|
|
|
- if(!messages.isEmpty() && Keys.ENTER.getTime() == 1)
|
|
|
- {
|
|
|
- messages.removeFirst();
|
|
|
- }
|
|
|
-
|
|
|
- time += Game.SECS_PER_TICK;
|
|
|
- Game.get().tickTiles();
|
|
|
-
|
|
|
- // doing entity logic first
|
|
|
- entities.values().removeIf(entity ->
|
|
|
- {
|
|
|
- entity.tick();
|
|
|
- if(entity.getHealth().shouldDespawn())
|
|
|
- {
|
|
|
- callEvent("entity_despawn", (sc) ->
|
|
|
- {
|
|
|
- sc.setVar("entity", entity);
|
|
|
- }, null);
|
|
|
- return true;
|
|
|
- }
|
|
|
- return false;
|
|
|
- });
|
|
|
-
|
|
|
- if(!spawnQueue.isEmpty())
|
|
|
+ ent.tick(this);
|
|
|
+ if(ent.getHealth().shouldDespawn() || (ent.getY() > getData().getHeight() * Tile.SIZE))
|
|
|
{
|
|
|
- spawnQueue.forEach(ent ->
|
|
|
+ ent.getHealth().onDespawn();
|
|
|
+ callEvent("entity_despawn", (sc) ->
|
|
|
{
|
|
|
- entities.put(entityCounter++, ent);
|
|
|
- callEvent("entity_spawn", (sc) ->
|
|
|
- {
|
|
|
- sc.setVar("entity", ent);
|
|
|
- }, null);
|
|
|
- });
|
|
|
- spawnQueue.clear();
|
|
|
+ sc.setVar("entity", ent);
|
|
|
+ }, null);
|
|
|
+ return true;
|
|
|
}
|
|
|
-
|
|
|
- // calculate new camera position
|
|
|
- oldCameraX = cameraX;
|
|
|
- oldCameraY = cameraY;
|
|
|
- cameraX = -getViewX(hero.getCenterX());
|
|
|
- cameraY = -getViewY(hero.getCenterY());
|
|
|
-
|
|
|
- // entity spawn layer after camera update
|
|
|
- int startX = (int) (-cameraX / Tile.SIZE);
|
|
|
- int startY = (int) (-cameraY / Tile.SIZE);
|
|
|
- int endX = Math.min((int) Math.ceil((-cameraX + Shader.getViewWidth()) / Tile.SIZE), data.getWidth());
|
|
|
- int endY = Math.min((int) Math.ceil((-cameraY + Shader.getViewHeight()) / Tile.SIZE), data.getWidth());
|
|
|
- data.forEachEntity((x, y, tile) ->
|
|
|
+ return false;
|
|
|
+ });
|
|
|
+
|
|
|
+ if(!spawnQueue.isEmpty())
|
|
|
+ {
|
|
|
+ spawnQueue.forEach(ent ->
|
|
|
{
|
|
|
- if(tile > 0)
|
|
|
+ entities.put(entityCounter++, ent);
|
|
|
+ callEvent("entity_spawn", (sc) ->
|
|
|
{
|
|
|
- data.deactivateEntity(x, y);
|
|
|
-
|
|
|
- Entity ent = EntityBuilder.fromId(tile, this, Utils.toCoord(x), Utils.toCoord(y));
|
|
|
- if(ent != null)
|
|
|
- {
|
|
|
- entities.put(entityCounter++, ent);
|
|
|
- }
|
|
|
- }
|
|
|
- }, startX, endX, startY, endY);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public String formatBottles(int bottles)
|
|
|
- {
|
|
|
- char[] c = new char[5];
|
|
|
- if(bottles <= 9)
|
|
|
- {
|
|
|
- c[0] = '0';
|
|
|
- c[1] = (char) (bottles + '0');
|
|
|
- }
|
|
|
- else if(bottles > 99)
|
|
|
- {
|
|
|
- c[0] = 'X';
|
|
|
- c[1] = 'X';
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- c[0] = (char) ((bottles / 10) + '0');
|
|
|
- c[1] = (char) ((bottles % 10) + '0');
|
|
|
- }
|
|
|
- c[2] = '/';
|
|
|
-
|
|
|
- int currentMaxSouls = Math.max(bottles, maxSouls);
|
|
|
- if(currentMaxSouls <= 9)
|
|
|
- {
|
|
|
- c[3] = '0';
|
|
|
- c[4] = (char) (currentMaxSouls + '0');
|
|
|
- }
|
|
|
- else if(currentMaxSouls > 99)
|
|
|
- {
|
|
|
- c[3] = 'X';
|
|
|
- c[4] = 'X';
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- c[3] = (char) ((currentMaxSouls / 10) + '0');
|
|
|
- c[4] = (char) ((currentMaxSouls % 10) + '0');
|
|
|
+ sc.setVar("entity", ent);
|
|
|
+ }, null);
|
|
|
+ });
|
|
|
+ spawnQueue.clear();
|
|
|
}
|
|
|
- return new String(c);
|
|
|
}
|
|
|
|
|
|
- public String formatTime(float time)
|
|
|
- {
|
|
|
- if(time == -1.0f)
|
|
|
- {
|
|
|
- return "-----";
|
|
|
- }
|
|
|
- else if(time >= 999.9f)
|
|
|
- {
|
|
|
- return "999.9";
|
|
|
- }
|
|
|
- return String.format("%05.1f", time);
|
|
|
- }
|
|
|
-
|
|
|
- private float getViewX(float x)
|
|
|
+ public final List<Entity> getEntitiesCollidingWith(Entity not, CollisionObject cb)
|
|
|
{
|
|
|
- x -= Shader.getViewWidth() >> 1;
|
|
|
- if(x < 0)
|
|
|
- {
|
|
|
- return 0;
|
|
|
- }
|
|
|
- float max = data.getWidth() * Tile.SIZE - Shader.getViewWidth();
|
|
|
- if(x > max)
|
|
|
- {
|
|
|
- return max;
|
|
|
- }
|
|
|
- return x;
|
|
|
+ return entities.values().stream().filter(ent -> ent != not && ent.getBox().isColliding(cb)).collect(Collectors.toList());
|
|
|
}
|
|
|
|
|
|
- private float getViewY(float y)
|
|
|
+ public final void forEachEntity(Consumer<Entity> c)
|
|
|
{
|
|
|
- y -= Shader.getViewHeight() >> 1;
|
|
|
- if(y < 0)
|
|
|
- {
|
|
|
- return 0;
|
|
|
- }
|
|
|
- float max = data.getHeight() * Tile.SIZE - Shader.getViewHeight();
|
|
|
- if(y > max)
|
|
|
- {
|
|
|
- return max;
|
|
|
- }
|
|
|
- return y;
|
|
|
+ entities.values().forEach(c);
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public void updateTile(int layer, int x, int y)
|
|
|
+ // -------------------------------------------------------------------------
|
|
|
+ // controlling
|
|
|
+ // -------------------------------------------------------------------------
|
|
|
+
|
|
|
+ private boolean shouldReset = false;
|
|
|
+ private boolean done = false;
|
|
|
+
|
|
|
+ public abstract void tick();
|
|
|
+
|
|
|
+ public final void finishLevel()
|
|
|
{
|
|
|
- if(layer == data.getBackgroundIndex() + 1)
|
|
|
- {
|
|
|
- // do not update changes on entity layer
|
|
|
- return;
|
|
|
- }
|
|
|
- if(layer > data.getBackgroundIndex())
|
|
|
- {
|
|
|
- layer--;
|
|
|
- }
|
|
|
- meshes[layer][x / meshSize][y / meshSize].clear();
|
|
|
+ shouldReset = true;
|
|
|
+ done = true;
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public void updateTile(int x, int y)
|
|
|
+ public final boolean isFinished()
|
|
|
{
|
|
|
- updateTile(data.getBackgroundIndex(), x, y);
|
|
|
+ return done;
|
|
|
}
|
|
|
|
|
|
- private void drawMesh(int l, int tl, int mx, int my)
|
|
|
+ public final boolean shouldReset()
|
|
|
{
|
|
|
- TextureRenderer tr = meshes[l][mx][my];
|
|
|
- if(!tr.isBuilt())
|
|
|
- {
|
|
|
- int tsx = mx * meshSize;
|
|
|
- int tsy = my * meshSize;
|
|
|
- int tex = Math.min(tsx + meshSize, data.getWidth());
|
|
|
- int tey = Math.min(tsy + meshSize, data.getHeight());
|
|
|
- for(int x = tsx; x < tex; x++)
|
|
|
- {
|
|
|
- for(int y = tsy; y < tey; y++)
|
|
|
- {
|
|
|
- Tile t = Game.get().getTile(data.getTile(tl, x, y));
|
|
|
- if(t.shouldRender(x, y, this))
|
|
|
- {
|
|
|
- float minX = x * Tile.SIZE + t.getOffsetX();
|
|
|
- float minY = y * Tile.SIZE + t.getOffsetY();
|
|
|
- tr.addRectangle(minX, minY,
|
|
|
- minX + t.getWidth(), minY + t.getHeight(),
|
|
|
- t.getTextureMinX(x, y, this) + ERROR, t.getTextureMinY(x, y, this) + ERROR,
|
|
|
- t.getTextureMaxX(x, y, this) - ERROR, t.getTextureMaxY(x, y, this) - ERROR);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- tr.build();
|
|
|
- }
|
|
|
- meshes[l][mx][my].draw();
|
|
|
+ return shouldReset;
|
|
|
}
|
|
|
|
|
|
- private String[] split(String s)
|
|
|
+ public final void scheduleReset()
|
|
|
{
|
|
|
- ArrayList<String> list = new ArrayList<>();
|
|
|
- int old = 0;
|
|
|
- int index = 0;
|
|
|
- while(index < s.length())
|
|
|
- {
|
|
|
- switch(s.charAt(index))
|
|
|
- {
|
|
|
- case '\n':
|
|
|
- list.add(s.substring(old, index));
|
|
|
- list.add("\n");
|
|
|
- old = index + 1;
|
|
|
- break;
|
|
|
- case ' ':
|
|
|
- list.add(s.substring(old, index));
|
|
|
- old = index + 1;
|
|
|
- break;
|
|
|
- }
|
|
|
- index++;
|
|
|
- }
|
|
|
- if(old < s.length())
|
|
|
- {
|
|
|
- list.add(s.substring(old, index));
|
|
|
- }
|
|
|
- return list.toArray(new String[list.size()]);
|
|
|
+ shouldReset = true;
|
|
|
}
|
|
|
|
|
|
- public void renderTick(float lag)
|
|
|
+ public final void reset()
|
|
|
{
|
|
|
- if(worldLoaded)
|
|
|
+ onReset();
|
|
|
+ shouldReset = false;
|
|
|
+ done = false;
|
|
|
+
|
|
|
+ // spawn entities
|
|
|
+ getData().forEachEntity((x, y, tile) ->
|
|
|
{
|
|
|
- float camX = Utils.interpolate(oldCameraX, cameraX, lag);
|
|
|
- float camY = Utils.interpolate(oldCameraY, cameraY, lag);
|
|
|
-
|
|
|
- Shader.translateTo(camX, camY);
|
|
|
- Shader.updateMatrix();
|
|
|
-
|
|
|
- int startX = (int) (-camX / (meshSize * Tile.SIZE));
|
|
|
- int startY = (int) (-camY / (meshSize * Tile.SIZE));
|
|
|
- int endX = (int) Math.ceil((-camX + Shader.getViewWidth()) / (meshSize * Tile.SIZE));
|
|
|
- int endY = (int) Math.ceil((-camY + Shader.getViewHeight()) / (meshSize * Tile.SIZE));
|
|
|
-
|
|
|
- startX = Math.min(Math.max(startX, 0), meshWidth);
|
|
|
- startY = Math.min(Math.max(startY, 0), meshHeight);
|
|
|
- endX = Math.min(Math.max(endX, 0), meshWidth);
|
|
|
- endY = Math.min(Math.max(endY, 0), meshHeight);
|
|
|
-
|
|
|
- // background
|
|
|
- Shader.setColorEnabled(false);
|
|
|
- Shader.setTextureEnabled(true);
|
|
|
- Shader.setBlendingEnabled(true);
|
|
|
- TILES.bind();
|
|
|
-
|
|
|
- int fromLayer = 0;
|
|
|
- int toLayer = data.getBackgroundIndex() + 1;
|
|
|
-
|
|
|
- for(int l = fromLayer; l < toLayer; l++)
|
|
|
- {
|
|
|
- for(int mx = startX; mx < endX; mx++)
|
|
|
- {
|
|
|
- for(int my = startY; my < endY; my++)
|
|
|
- {
|
|
|
- drawMesh(l, l, mx, my);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // entities
|
|
|
-
|
|
|
- for(Entity entity : entities.values())
|
|
|
- {
|
|
|
- entity.renderTick(lag);
|
|
|
- }
|
|
|
-
|
|
|
- // foreground
|
|
|
- Shader.setColorEnabled(false);
|
|
|
- Shader.setTextureEnabled(true);
|
|
|
- Shader.setBlendingEnabled(true);
|
|
|
- TILES.bind();
|
|
|
-
|
|
|
- fromLayer = toLayer + 1;
|
|
|
- toLayer = data.getLayers();
|
|
|
-
|
|
|
- for(int l = fromLayer; l < toLayer; l++)
|
|
|
- {
|
|
|
- for(int mx = startX; mx < endX; mx++)
|
|
|
- {
|
|
|
- for(int my = startY; my < endY; my++)
|
|
|
- {
|
|
|
- drawMesh(l - 1, l, mx, my);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // menu rendering
|
|
|
- Shader.translateTo(0.0f, 0.0f);
|
|
|
- Shader.updateMatrix();
|
|
|
-
|
|
|
- // grey background of clock and bottles
|
|
|
- float lineHeight = Shader.getFontRenderer().getHeight();
|
|
|
- float lineWidth = Shader.getFontRenderer().getWidth();
|
|
|
- Shader.setColorEnabled(true);
|
|
|
- Shader.setTextureEnabled(false);
|
|
|
- Shader.getColorRenderer().drawRectangle(0.0f, 0.0f, (lineWidth * 6.0f) + 10.0f, (lineHeight * 2.0f + 10.0f), 0x77000000);
|
|
|
- Shader.setTextureEnabled(true);
|
|
|
- float y = 5.0f;
|
|
|
- y = Shader.getFontRenderer().drawString(13.0f, y, formatBottles(souls));
|
|
|
- Shader.getFontRenderer().drawString(13.0f, y, formatTime(time));
|
|
|
- Shader.setColorEnabled(false);
|
|
|
-
|
|
|
- // draw messages
|
|
|
- if(!messages.isEmpty())
|
|
|
+ if(tile > 0)
|
|
|
{
|
|
|
- String[] message = split(messages.getFirst());
|
|
|
- int index = 0;
|
|
|
- ArrayList<StringBuilder> list = new ArrayList<>();
|
|
|
- list.add(new StringBuilder());
|
|
|
- float currentWidth = 0;
|
|
|
-
|
|
|
- float w = Shader.getViewWidth() - 26;
|
|
|
- for(String s : message)
|
|
|
- {
|
|
|
- if(s.equals("\n"))
|
|
|
- {
|
|
|
- currentWidth = w;
|
|
|
- continue;
|
|
|
- }
|
|
|
- Rectangle rec = Shader.getFontRenderer().getSize(s);
|
|
|
- // + lineWidth for the space
|
|
|
- if(currentWidth + rec.getWidth() + lineWidth < w)
|
|
|
- {
|
|
|
- currentWidth += rec.getWidth();
|
|
|
-
|
|
|
- StringBuilder sb = list.get(index);
|
|
|
- if(sb.length() == 0)
|
|
|
- {
|
|
|
- sb.append(s);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- sb.append(" ");
|
|
|
- sb.append(s);
|
|
|
-
|
|
|
- currentWidth += lineWidth;
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- StringBuilder sb = new StringBuilder();
|
|
|
- list.add(sb);
|
|
|
- index++;
|
|
|
- sb.append(s);
|
|
|
- currentWidth = rec.getWidth();
|
|
|
- }
|
|
|
- }
|
|
|
- float height = list.size() * lineHeight;
|
|
|
-
|
|
|
- Shader.setColorEnabled(true);
|
|
|
- Shader.setTextureEnabled(false);
|
|
|
- float messageY = Shader.getViewHeight() - height - 26;
|
|
|
- Shader.getColorRenderer().drawRectangle(0.0f, messageY, Shader.getViewWidth(), Shader.getViewHeight(), 0x77000000);
|
|
|
- messageY += 13;
|
|
|
- Shader.setTextureEnabled(true);
|
|
|
- for(StringBuilder sb : list)
|
|
|
+ Entity ent = EntityBuilder.fromId(tile, this, Utils.toCoord(x), Utils.toCoord(y));
|
|
|
+ if(ent != null)
|
|
|
{
|
|
|
- messageY = Shader.getFontRenderer().drawString(13.0f, messageY, sb.toString());
|
|
|
+ entities.put(entityCounter++, ent);
|
|
|
}
|
|
|
- Shader.setColorEnabled(false);
|
|
|
}
|
|
|
- /*Shader.setColorEnabled(true);
|
|
|
- Shader.setTextureEnabled(false);
|
|
|
- Shader.getColorRenderer().drawRectangle(0.0f, 0.0f, (lineWidth * 6.0f) + 10.0f, (lineHeight * 2.0f + 10.0f), 0x77000000);
|
|
|
- Shader.setTextureEnabled(true);
|
|
|
- y = 5.0f;
|
|
|
- y = Shader.getFontRenderer().drawString(13.0f, y, formatBottles(souls));
|
|
|
- Shader.getFontRenderer().drawString(13.0f, y, formatTime(time));
|
|
|
- Shader.setColorEnabled(false);*/
|
|
|
-
|
|
|
-
|
|
|
- GUI.bind();
|
|
|
- GUI_RENDERER.clear();
|
|
|
-
|
|
|
- int scale = Shader.getViewScale();
|
|
|
-
|
|
|
- // bottles
|
|
|
- switch(scale)
|
|
|
- {
|
|
|
- case 1: GUI_RENDERER.addRectangle(6.0f, 4.0f, 12.0f, 14.0f, 0.0f, 0.046875f, 0.01171875f, 0.068359375f); break;
|
|
|
- case 2: GUI_RENDERER.addRectangle(6.0f, 4.0f, 12.0f, 14.0f, 0.01171875f, 0.046875f, 0.037109375f, 0.0859375f); break;
|
|
|
- default: GUI_RENDERER.addRectangle(6.0f, 4.0f, 12.0f, 14.0f, 0.037109375f, 0.046875f, 0.06640625f, 0.10546875f); break;
|
|
|
- }
|
|
|
-
|
|
|
- // clock
|
|
|
- switch(scale)
|
|
|
- {
|
|
|
- case 1: GUI_RENDERER.addRectangle(4.0f, y, 13.0f, y + 9.0f, 0.0f, 0.265625f, 0.017578125f, 0.283203125f); break;
|
|
|
- case 2: GUI_RENDERER.addRectangle(4.5f, y, 13.0f, y + 8.5f, 0.017578125f, 0.265625f, 0.05078125f, 0.298828125f); break;
|
|
|
- default: GUI_RENDERER.addRectangle(4.666666666f, y, 13.0f, y + 8.333333333f, 0.05078125f, 0.265625f, 0.099609375f, 0.314453125f); break;
|
|
|
- }
|
|
|
-
|
|
|
- float w = Shader.getViewWidth();
|
|
|
-
|
|
|
- // gui background
|
|
|
- GUI_RENDERER.addRectangle(w - 111.0f, 0.0f, w - 44.0f, 24.0f, 0.0f, 0.0f, 0.130859375f, 0.046875f);
|
|
|
- GUI_RENDERER.addRectangle(w - 44.0f, 0.0f, w - 18.0f, 16.0f, 0.130859375f, 0.0f, 0.181640625f, 0.03125f);
|
|
|
- GUI_RENDERER.addRectangle(w - 76.0f, 24.0f, w - 45.0f, 57.0f, 0.068359375f, 0.046875f, 0.12890625f, 0.111328125f);
|
|
|
-
|
|
|
- // health mirror
|
|
|
- int healthFrame = (int) (hero.getHealth().getHealthPercent() * 7);
|
|
|
- float leftMirror = (7 - healthFrame) * 0.0625f;
|
|
|
- GUI_RENDERER.addRectangle(w - 39.0f, 8.0f, w - 7.0f, 46.0f, leftMirror, 0.15625f, leftMirror + 0.0625f, 0.23046875f);
|
|
|
-
|
|
|
- // energy
|
|
|
- float energy = hero.getEnergy().getEnergyPercent();
|
|
|
- float fullEnd = w - 109.0f + 64.0f * energy;
|
|
|
- GUI_RENDERER.addRectangle(w - 109.0f, 13.0f, fullEnd, 21.0f, 0.0f, 0.140625f, 0.125f * energy, 0.15625f);
|
|
|
- GUI_RENDERER.addRectangle(fullEnd, 13.0f, w - 45.0f, 21.0f, 0.125f * energy, 0.125f, 0.125f, 0.140625f);
|
|
|
-
|
|
|
- // gui foreground
|
|
|
- GUI_RENDERER.addRectangle(w - 49.0f, 0.0f, w, 64.0f, 0.201171875f, 0.0f, 0.296875f, 0.125f);
|
|
|
- GUI_RENDERER.addRectangle(w - 109.0f, 15.0f, w - 106.0f, 18.0f, 0.15625f, 0.03125f, 0.162109375f, 0.037109375f);
|
|
|
- GUI_RENDERER.addRectangle(w - 97.0f, 15.0f, w - 92.0f, 20.0f, 0.1796875f, 0.03125f, 0.189453125f, 0.041015625f);
|
|
|
-
|
|
|
- // health number overlay
|
|
|
- GUI_RENDERER.addRectangle(w - 30.0f, 53.0f, w - 12.0f, 62.0f, leftMirror, 0.23828125f, leftMirror + 0.03515625f, 0.255859375f);
|
|
|
-
|
|
|
- GUI_RENDERER.build();
|
|
|
- GUI_RENDERER.draw();
|
|
|
-
|
|
|
- // dynamic clock hand
|
|
|
- Shader.setColorEnabled(true);
|
|
|
- Shader.setTextureEnabled(false);
|
|
|
- switch(scale)
|
|
|
- {
|
|
|
- case 1: Shader.translateTo(8.5f, y + 4.5f); break;
|
|
|
- case 2: Shader.translateTo(8.75f, y + 4.25f); break;
|
|
|
- default: Shader.translateTo(8.8333333333f, y + 4.16666667f); break;
|
|
|
- }
|
|
|
- Shader.rotate(-time * 72.0f);
|
|
|
- Shader.updateMatrix();
|
|
|
- Shader.getColorRenderer().drawRectangle(-0.5f / scale, -0.5f / scale, 0.5f / scale, 4.0f - 0.5f * scale, 0xFF000000);
|
|
|
- }
|
|
|
+ }, 0, getData().getWidth(), 0, getData().getHeight());
|
|
|
}
|
|
|
|
|
|
+ protected void onReset()
|
|
|
+ {
|
|
|
+ };
|
|
|
+
|
|
|
// -------------------------------------------------------------------------
|
|
|
- // collision box, interaction layer
|
|
|
+ // collision
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
|
|
private Tile getInteractionTile(int x, int y)
|
|
|
{
|
|
|
- int i = data.getInteractionTile(x, y);
|
|
|
+ int i = getData().getInteractionTile(x, y);
|
|
|
if(i == -1)
|
|
|
{
|
|
|
return Game.FALLBACK_TILE;
|
|
@@ -760,7 +212,7 @@ public final class Level implements ILevel
|
|
|
|
|
|
private CollisionObject getMovementBox(int x, int y)
|
|
|
{
|
|
|
- int i = data.getInteractionTile(x, y);
|
|
|
+ int i = getData().getInteractionTile(x, y);
|
|
|
if(i == -1)
|
|
|
{
|
|
|
return CollisionObject.NULL_BOX;
|
|
@@ -768,8 +220,7 @@ public final class Level implements ILevel
|
|
|
return Game.get().getTile(i).getMovementBox(x, y, this).reset().offset(Utils.toCoord(x), Utils.toCoord(y));
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public List<CollisionObject> getMovementBoxesAt(CollisionObject box, Entity not)
|
|
|
+ public final List<CollisionObject> getMovementBoxesAt(CollisionObject box, Entity not)
|
|
|
{
|
|
|
List<CollisionObject> boxes;
|
|
|
if(not != null)
|
|
@@ -801,17 +252,15 @@ public final class Level implements ILevel
|
|
|
|
|
|
private CollisionObject getCollisionBox(int x, int y)
|
|
|
{
|
|
|
- int i = data.getInteractionTile(x, y);
|
|
|
+ int i = getData().getInteractionTile(x, y);
|
|
|
if(i == -1)
|
|
|
{
|
|
|
return CollisionObject.NULL_BOX;
|
|
|
}
|
|
|
- Tile tile = Game.get().getTile(i);
|
|
|
- return tile.getCollisionBox(x, y, this).reset().offset(Utils.toCoord(x), Utils.toCoord(y));
|
|
|
+ return Game.get().getTile(i).getCollisionBox(x, y, this).reset().offset(Utils.toCoord(x), Utils.toCoord(y));
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public List<Location> getCollisionBoxesAt(CollisionObject cb)
|
|
|
+ public final List<Location> getCollisionBoxesAt(CollisionObject cb)
|
|
|
{
|
|
|
LinkedList<Location> boxes = new LinkedList<>();
|
|
|
int startX = Utils.toBlock(cb.getMinX());
|
|
@@ -832,32 +281,16 @@ public final class Level implements ILevel
|
|
|
return boxes;
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public List<Entity> getEntitiesCollidingWith(Entity not, CollisionObject cb)
|
|
|
- {
|
|
|
- return entities.values().stream().filter(ent -> ent != not && ent.getBox().isColliding(cb)).collect(Collectors.toList());
|
|
|
- }
|
|
|
+ // -------------------------------------------------------------------------
|
|
|
+ // scripting
|
|
|
+ // -------------------------------------------------------------------------
|
|
|
|
|
|
- @Override
|
|
|
public void callEvent(String name, Consumer<Script> before, Consumer<Script> after)
|
|
|
{
|
|
|
- if(levelScript != null)
|
|
|
- {
|
|
|
- //Level l = Game.get().getCurrentLevel();
|
|
|
- //Game.get().setCurrentLevel(this);
|
|
|
- Game.get().getParser().callEvent(name, levelScript, before, after);
|
|
|
- //Game.get().setCurrentLevel(l);
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public void callEvent(String name)
|
|
|
+ public final void callEvent(String name)
|
|
|
{
|
|
|
callEvent(name, null, null);
|
|
|
}
|
|
|
-
|
|
|
- public void addMessage(String message)
|
|
|
- {
|
|
|
- messages.add(message);
|
|
|
- }
|
|
|
}
|