package me.hammerle.supersnuvi.gamelogic; import java.io.File; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.TreeSet; import java.util.stream.Collectors; import me.hammerle.supersnuvi.entity.Entity; import me.hammerle.supersnuvi.entity.EntityBuilder; import me.hammerle.supersnuvi.tiles.BottledSoulTile; import me.hammerle.supersnuvi.tiles.Location; import me.hammerle.supersnuvi.tiles.StartTile; import me.hammerle.supersnuvi.tiles.Tile; import me.hammerle.supersnuvi.util.CollisionBox; import me.hammerle.supersnuvi.rendering.IRenderer; public final class Level { private final StateRenderer state; private final IRenderer renderer; private final boolean worldLoaded; private final LevelData data; private final String name; private final HashMap entities; private Entity hero; private int entityCounter; private boolean shouldReset; private boolean done; private int souls; private int maxSouls; private double time; private TreeSet spawns; public Level(StateRenderer state, File f) { this.state = state; this.renderer = state.getRenderer(); this.data = new LevelData(f); this.worldLoaded = data.load(); this.entities = new HashMap<>(); this.entityCounter = 0; this.shouldReset = false; this.done = false; this.time = 0.0; this.spawns = new TreeSet<>(); if(worldLoaded) { this.name = data.getString("name", "error"); maxSouls = 0; data.forEachInteractTile((x, y, tile) -> { if(tile == BottledSoulTile.ID) { maxSouls++; } else if(tile == StartTile.ID) { spawns.add(new Point(x, y)); } }); } else { this.name = "error"; maxSouls = 0; } // there must be at least one spawn if(spawns.isEmpty()) { spawns.add(new Point(5, 5)); } /*if(name.equals("00-Tech Demo")) { data.addLayer(); int index = data.getLayers() - 1; data.clearLayer(index); int width = data.getWidth(); int height = data.getHeight(); for(int x = 10; x < width; x++) { for(int y = 0; y < height; y++) { data.setTile(index, x, y, 224 + Math.min(15, x - 10)); } } }*/ resetLevel(); //Entity test = EntityBuilder.buildTest(this, 100, 100); //entities.put(entityCounter++, test); } // ------------------------------------------------------------------------- // basic stuff // ------------------------------------------------------------------------- public String getName() { return name; } public void finishLevel() { shouldReset = true; done = true; } public boolean shouldFinish() { return done; } public boolean shouldReset() { return shouldReset; } public void scheduleReset() { shouldReset = true; } public void resetLevel() { state.resetTiles(); souls = 0; shouldReset = false; done = false; Entity h = spawnHero(); hero = h; entities.clear(); entities.put(entityCounter++, h); } public Entity spawnHero() { Entity h = hero; Point p; if(h == null || hero.getX() < 0) { // 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(renderer.toBlock(hero.getX()), renderer.toBlock(hero.getY()))); if(p == null) { p = spawns.first(); } } return EntityBuilder.buildHero(this, renderer.toCoord(p.getX()), renderer.toCoord(p.getY())); } public void increaseSouls() { souls++; } public LevelData getData() { return data; } // ------------------------------------------------------------------------- // tick // ------------------------------------------------------------------------- public void update() { if(worldLoaded) { if(hero.getY() < 0) { resetLevel(); return; } time += 0.0125; state.tickTiles(); // doing entity logic first entities.values().forEach(entity -> { entity.tick(); }); } } public void render() { if(worldLoaded) { // setting the right view center double rWidth = renderer.getWidth(); double rHeight = renderer.getHeight(); double centerX = Math.min(Math.max(0, hero.getX() - rWidth / 2), data.getWidth() * 32 - rWidth); double centerY = Math.min(Math.max(0, hero.getY() - rHeight / 2), data.getHeight() * 32 - rHeight); renderer.setViewCenter(centerX, centerY); int startX = renderer.getFirstVisibleBlockX(); int startY = renderer.getFirstVisibleBlockY(); int endX = Math.min(renderer.getLastVisibleBlockX() + 1, data.getWidth()); int endY = Math.min(renderer.getLastVisibleBlockY() + 1, data.getHeight()); data.forEachBackground((x, y, tile) -> { if(tile != -1) { Tile t = state.getTile(tile); t.preRender(renderer, x, y); renderer.drawBlockImage(t.getImage(x, y), x, y, t.getRenderOffsetX(), t.getRenderOffsetY()); t.postRender(renderer, x, y); } }, startX, endX, startY, endY); entities.values().forEach(en -> en.getAnimator().render(renderer)); data.forEachForeground((x, y, tile) -> { if(tile != -1) { Tile t = state.getTile(tile); t.preRender(renderer, x, y); renderer.drawBlockImage(t.getImage(x, y), x, y, t.getRenderOffsetX(), t.getRenderOffsetY()); t.postRender(renderer, x, y); } }, startX, endX, startY, endY); // menu rendering // |-------------------------------------| // | B00/00 | ENERGYENERGY | HPHP | TIME | // |-------------------------------------| renderer.prepareTextDrawing(255, 255, 255, 1.0, 40); { double baseLine = renderer.getTextHeight(1); double line = baseLine * 1.5; double y = baseLine * 0.25; renderer.save(); { renderer.setFillColor(0, 0, 0, 0.5); renderer.fillRectangle(0, 0, renderer.getWidth(), line); } renderer.restore(); // soul rendering char[] c = new char[5]; if(souls <= 9) { c[0] = '0'; c[1] = (char) (souls + '0'); } else if(souls > 99) { c[0] = 'X'; c[1] = 'X'; } else { c[0] = (char) ((souls / 10) + '0'); c[1] = (char) ((souls % 10) + '0'); } c[2] = '/'; if(maxSouls <= 9) { c[3] = '0'; c[4] = (char) (maxSouls + '0'); } else if(maxSouls > 99) { c[3] = 'X'; c[4] = 'X'; } else { c[3] = (char) ((maxSouls / 10) + '0'); c[4] = (char) ((maxSouls % 10) + '0'); } double x = 5; renderer.drawFixedImage(BottledSoulTile.IMAGE, x, 0, line, line); x += line; renderer.drawText(x, y, c); x += renderer.getTextWidth(5) + 10; // energy rendering double leftX = (renderer.getWidth() - 40.0 - renderer.getTextWidth(10) - line) * 0.5; //double energyWidth = renderer.getWidth() * 0.2; //double ex = (renderer.getWidth() / 2) - energyWidth; //energyWidth *= 2; renderer.save(); { renderer.setFillColor(0, 0, 0, 1.0); renderer.fillRectangle(x, y, leftX, baseLine); renderer.setFillColor(0, 0, 150, 1.0); renderer.fillRectangle(x + 3, y + 3, (leftX * hero.getEnergy().getEnergyPercent()) - 6, baseLine - 6); } renderer.restore(); x += leftX + 10; // hp rendering renderer.save(); { renderer.setFillColor(0, 0, 0, 1.0); renderer.fillRectangle(x, y, leftX, baseLine); renderer.setFillColor(150, 0, 0, 1.0); renderer.fillRectangle(x + 3, y + 3, (leftX * hero.getHealth().getHealthPercent()) - 6, baseLine - 6); } renderer.restore(); x += leftX + 10; // time rendering if(time >= 999.9) { c[0] = '9'; c[1] = '9'; c[2] = '9'; c[3] = '.'; c[4] = '9'; renderer.drawText(x, y, c); } else { renderer.drawText(x, y, String.format("%05.1f", time).toCharArray()); } } renderer.stopTextDrawing(); } } // ------------------------------------------------------------------------- // collision box, interaction layer // ------------------------------------------------------------------------- public Tile getInteractionTile(int x, int y) { int i = data.getInteractionTile(x, y); if(i == -1) { return StateRenderer.FALLBACK_TILE; } return state.getInteractionTile(i); } public CollisionBox getTileMovementBox(int x, int y) { int i = data.getInteractionTile(x, y); if(i == -1) { return CollisionBox.NULL_BOX; } Tile tile = state.getInteractionTile(i); return tile.getMovementBox(x, y).reset().offset(renderer.toCoord(x), renderer.toCoord(y)); } public LinkedList getTilesInMovementOf(CollisionBox cb) { LinkedList boxes = new LinkedList<>(); int startX = renderer.toBlock(cb.getMinX()); int endX = renderer.toBlock(cb.getMaxX()); int startY = renderer.toBlock(cb.getMinY()); int endY = renderer.toBlock(cb.getMaxY()); for(int x = startX; x <= endX; x++) { for(int y = startY; y <= endY; y++) { if(getTileMovementBox(x, y).intersects(cb)) { boxes.add(new Location(getInteractionTile(x, y), x, y)); } } } return boxes; } public CollisionBox getTileCollisionBox(int x, int y) { int i = data.getInteractionTile(x, y); if(i == -1) { return CollisionBox.NULL_BOX; } Tile tile = state.getInteractionTile(i); return tile.getCollisionBox(x, y).reset().offset(renderer.toCoord(x), renderer.toCoord(y)); } public LinkedList getTilesCollidingWith(CollisionBox cb) { LinkedList boxes = new LinkedList<>(); int startX = renderer.toBlock(cb.getMinX()); int endX = renderer.toBlock(cb.getMaxX()); int startY = renderer.toBlock(cb.getMinY()); int endY = renderer.toBlock(cb.getMaxY()); for(int x = startX; x <= endX; x++) { for(int y = startY; y <= endY; y++) { if(getTileCollisionBox(x, y).intersects(cb)) { boxes.add(new Location(getInteractionTile(x, y), x, y)); } } } return boxes; } public List getEntitiesCollidingWith(Entity not, CollisionBox cb) { return entities.values().stream().filter(ent -> ent != not && ent.getBox().intersects(cb)).collect(Collectors.toList()); } public LinkedList getMovementBoxesAt(Entity not, CollisionBox cb) { LinkedList boxes = new LinkedList<>(/*entities.values().stream() .filter(ent -> ent != not && ent.getBox().intersects(cb)) .map(ent -> ent.getBox()) .collect(Collectors.toList())*/); int startX = renderer.toBlock(cb.getMinX()); int endX = renderer.toBlock(cb.getMaxX()); int startY = renderer.toBlock(cb.getMinY()); int endY = renderer.toBlock(cb.getMaxY()); CollisionBox box; for(int x = startX; x <= endX; x++) { for(int y = startY; y <= endY; y++) { box = getTileMovementBox(x, y); if(box.intersects(cb)) { boxes.add(box.copy()); } } } return boxes; } public Entity getHero() { return hero; } }