package me.hammerle.supersnuvi.gamelogic; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.function.Consumer; import me.hammerle.snuviscript.code.Script; import me.hammerle.supersnuvi.Game; import me.hammerle.supersnuvi.entity.Entity; import me.hammerle.supersnuvi.entity.EntityBuilder; import me.hammerle.supersnuvi.rendering.Light; import me.hammerle.supersnuvi.rendering.TileUpdater; import me.hammerle.supersnuvi.tiles.Tile; import me.hammerle.supersnuvi.util.Utils; public abstract class Level { // level data private final TileUpdater tileUpdater = new TileUpdater(); // entity related private final HashMap entities = new HashMap<>(); private final LinkedList spawnQueue = new LinkedList<>(); private Entity hero = null; private int entityCounter = 0; // controlling private boolean shouldReset = false; private boolean done = false; // lighting private float red = 1.0f; private float green = 1.0f; private float blue = 1.0f; private final Light[] lights = new Light[32]; public Level() { for(int i = 0; i < lights.length; i++) { lights[i] = new Light(); } } // ------------------------------------------------------------------------- // level data // ------------------------------------------------------------------------- public abstract LevelData getData(); public String getName() { return "Unknown"; } public String getFileName() { return "Unknown"; } public final TileUpdater getTileUpdater() { return tileUpdater; } // ------------------------------------------------------------------------- // level stats // ------------------------------------------------------------------------- public int getCurrentBottles() { return 0; } public int getMaxBottles() { return 0; } public void addBottles(int bottles) { } public float getTime() { return 0; } public void addMessage(String message) { } public String getMessage() { return null; } // ------------------------------------------------------------------------- // entity related // ------------------------------------------------------------------------- public final Entity getHero() { return hero; } public final void setHero(Entity hero) { this.hero = hero; } public final void removeEntities() { entities.clear(); } public final void spawnEntity(Entity ent) { spawnQueue.add(ent); } public final void tickEntities() { entities.values().removeIf(ent -> { ent.tick(this); if((ent.getHealth().isDead() && !ent.isAnimated()) || (ent.getY() > getData().getHeight() * Tile.SIZE)) { ent.onDespawn(); callEvent("entity_despawn", (sc) -> { sc.setVar("entity", ent); }, null); return true; } return false; }); entities.values().forEach(ent -> ent.tickCollision(this)); int loops = 20; int changes = 1; while(changes > 0) { loops--; if(loops == 0) { System.out.println(System.nanoTime() + " Loop end reached ..."); break; } changes = 0; for(Entity ent : entities.values()) { changes += ent.move(this); } } if(!spawnQueue.isEmpty()) { spawnQueue.forEach(ent -> { entities.put(entityCounter++, ent); callEvent("entity_spawn", (sc) -> { sc.setVar("entity", ent); }, null); }); spawnQueue.clear(); } } public final void forEachCollidingEntity(Entity ent, Consumer c) { float minX = ent.getX() - Entity.STEP; float minY = ent.getY() - Entity.STEP; float maxX = ent.getX() + ent.getWidth() + Entity.STEP; float maxY = ent.getY() + ent.getHeight() + Entity.STEP; entities.values().stream().filter(other -> ent != other && other.isColliding(minX, minY, maxX, maxY)).forEach(c); } public final void forEachEntity(Consumer c) { entities.values().forEach(c); } public final Collection getEntities() { return entities.values(); } public final Collection getEntitiesInQueue() { return spawnQueue; } // ------------------------------------------------------------------------- // controlling // ------------------------------------------------------------------------- public abstract void tick(); public final void finishLevel() { shouldReset = true; done = true; } public final boolean isFinished() { return done; } public final boolean shouldReset() { return shouldReset; } public final void scheduleReset() { shouldReset = true; } public final void reset() { // reset lights for(Light l : lights) { l.reset(); } onReset(); shouldReset = false; done = false; // spawn entities getData().forEachEntity((x, y, tile) -> { if(tile > 0) { Entity ent = EntityBuilder.fromId(tile, this, Utils.toCoord(x), Utils.toCoord(y)); if(ent != null) { entities.put(entityCounter++, ent); } } }, 0, getData().getWidth(), 0, getData().getHeight()); } protected void onReset() { }; // ------------------------------------------------------------------------- // collision // ------------------------------------------------------------------------- public Tile getInteractionTile(int x, int y) { int i = getData().getInteractionTile(x, y); if(i == -1) { return Game.FALLBACK_TILE; } return Game.get().getTile(i); } // ------------------------------------------------------------------------- // scripting // ------------------------------------------------------------------------- public void callEvent(String name, Consumer