package com.thecherno.ld29.level; import static org.lwjgl.opengl.GL11.*; import java.awt.image.BufferedImage; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Random; import javax.imageio.ImageIO; import org.lwjgl.util.vector.Vector2f; import com.thecherno.ld29.Main; import com.thecherno.ld29.State; import com.thecherno.ld29.entity.Entity; import com.thecherno.ld29.entity.mob.Player; import com.thecherno.ld29.graphics.Camera; import com.thecherno.ld29.graphics.Font; import com.thecherno.ld29.graphics.Light; import com.thecherno.ld29.graphics.Shader; import com.thecherno.ld29.level.tile.DirtTile; import com.thecherno.ld29.level.tile.ForeTile; import com.thecherno.ld29.level.tile.GrassTile; import com.thecherno.ld29.level.tile.Tile; import com.thecherno.ld29.level.tile.WallTile; import com.thecherno.ld29.level.tile.WaterTile; public class Level { protected int width, height; private Shader lightShader; private int xOffset, yOffset; public final static int BACKGROUND = 0x0; public final static int FOREGROUND = 0x1; private Font font; private int[] backgroundTiles, foregroundTiles; private Random random = new Random(); private Vector2f[][] foregroundVertices; private List<Light> lights = new ArrayList<Light>(); private List<Entity> entities = new ArrayList<Entity>(); private float[] waterLevels, waterIncreaseRate; private float waterLevel = 0.0f; private int time = 0; private Tile[] tileIDs = new Tile[5]; private String path, lightPath; public Level(int width, int height) { this.width = width; this.height = height; backgroundTiles = new int[width * height]; foregroundTiles = new int[width * height]; foregroundVertices = new Vector2f[width * height][4]; init(); genRandom(); } public Level(String path, String lightPath) { this.path = path; this.lightPath = lightPath; initOnce(); createLevel(); } private void createLevel() { load(path); loadLights(lightPath); init(); } private void load(String path) { int[] pixels = null; try { BufferedImage image = ImageIO.read(new FileInputStream(path)); this.width = image.getWidth(); this.height = image.getHeight(); pixels = new int[width * height]; image.getRGB(0, 0, width, height, pixels, 0, width); } catch (IOException e) { e.printStackTrace(); } backgroundTiles = new int[width * height]; for (int i = 0; i < backgroundTiles.length; i++) { backgroundTiles[i] = -1; } foregroundTiles = new int[width * height]; for (int i = 0; i < foregroundTiles.length; i++) { foregroundTiles[i] = -1; } foregroundVertices = new Vector2f[width * height][4]; for (int i = 0; i < width * height; i++) { if (pixels[i] == 0xff000000) createForegroundTile(i % width, i / width, ForeTile.WALL); else if (pixels[i] == 0xffffffff) createBackgroundTile(i % width, i / width, Tile.GROUND); else if (pixels[i] == 0xff00ff00) createBackgroundTile(i % width, i / width, ForeTile.GRASS); else if (pixels[i] == 0xffffff00) createBackgroundTile(i % width, i / width, ForeTile.DIRT); else if (pixels[i] == 0xff0000ff) createForegroundTile(i % width, i / width, ForeTile.WATER); } } private void loadLights(String lightPath) { int[] pixels = null; try { BufferedImage image = ImageIO.read(new FileInputStream(lightPath)); this.width = image.getWidth(); this.height = image.getHeight(); pixels = new int[width * height]; image.getRGB(0, 0, width, height, pixels, 0, width); } catch (IOException e) { e.printStackTrace(); } for (int i = 0; i < width * height; i++) { if (pixels[i] != 0xff000000) { float intensity = (pixels[i] & 0xff000000) >> 24; if (intensity < 0) intensity += 0x7f; lights.add(new Light(((i % width) << 6) + (64 >> 1), ((i / width) << 6) + (64 >> 1), pixels[i], intensity / 12)); } } lights.add(new Light((106 << 6) + 32, (71 << 6) + 32, 0xffffff, 50.0f)); } private void initOnce() { tileIDs[Tile.GROUND] = new Tile(); tileIDs[ForeTile.WALL] = new WallTile(); tileIDs[Tile.DIRT] = new DirtTile(); tileIDs[Tile.GRASS] = new GrassTile(); tileIDs[ForeTile.WATER] = new WaterTile(); lightShader = Shader.LIGHT; waterLevels = new float[width * height]; waterIncreaseRate = new float[width * height]; font = new Font(); } private void init() { add(new Player(11, 5)); for (int i = 0; i < waterIncreaseRate.length; i++) { waterIncreaseRate[i] = random.nextFloat(); } } private void add(Entity e) { entities.add(e); e.init(this); } public List<Light> getLights(int x, int y, int dira) { List<Light> lights = new ArrayList<Light>(); int dir = 0; for (int i = 0; i < this.lights.size(); i++) { Light light = this.lights.get(i); float e0 = light.y - 20; float e1 = light.x + 20; float e2 = light.y + 20; float e3 = light.x - 20; if (e3 < x + 64) { if (e1 > x) { if (e0 < y + 64) { if (e2 > y) { lights.add(light); } } } } Vector2f lightPos = new Vector2f(light.x, light.y); float distance = 20.0f; // Edge 0 if (distance(lightPos, new Vector2f(x + 32.0f, y + 64.0f)) < distance) { distance = distance(lightPos, new Vector2f(x + 32.0f, y + 64.0f)); dir = 2; } // Edge 2 if (distance(lightPos, new Vector2f(x + 32.0f, y)) < distance) { distance = distance(lightPos, new Vector2f(x + 32.0f, y)); dir = 0; } // Edge 1 if (distance(lightPos, new Vector2f(x, y + 32.0f)) < distance) { distance = distance(lightPos, new Vector2f(x, y + 32.0f)); dir = 3; } // Edge 3 if (distance(lightPos, new Vector2f(x + 64.0f, y + 32.0f)) < distance) { dir = 1; } light.dir = dir; } return lights; } private float distance(Vector2f a, Vector2f b) { float x = a.x - b.x; float y = a.y - b.y; return (float) Math.sqrt(x * x + y * y); } private void genRandom() { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int tile = random.nextInt(5); if (tile < 4) backgroundTiles[x + y * width] = 1; else createForegroundTile(x, y, ForeTile.WALL); } } } private void createBackgroundTile(int x, int y, int type) { backgroundTiles[x + y * width] = type; } private void createForegroundTile(int x, int y, int type) { foregroundTiles[x + y * width] = type; Vector2f[] vertices = new Vector2f[4]; vertices[0] = new Vector2f(x * Tile.SIZE, y * Tile.SIZE); vertices[1] = new Vector2f(x * Tile.SIZE + Tile.SIZE, y * Tile.SIZE); vertices[2] = new Vector2f(x * Tile.SIZE + Tile.SIZE, y * Tile.SIZE + Tile.SIZE); vertices[3] = new Vector2f(x * Tile.SIZE, y * Tile.SIZE + Tile.SIZE); foregroundVertices[x + y * width] = vertices; } public void update() { for (int i = 0; i < entities.size(); i++) { entities.get(i).update(); checkWin(entities.get(i)); } tileIDs[ForeTile.WATER].update(); time++; for (int i = 0; i < tileIDs.length; i++) { tileIDs[i].update(); } if (waterLevel / 100 > 100) { reset(); Main.setMenu(Main.gameover); State.setState(State.MENU); } increaseWaterLevels(); } private void checkWin(Entity entity) { if (entity instanceof Player) { if (entity.getX() + 128 < 106 * 64 + 192 && entity.getX() - 64 > 106 * 64 - 128) { if (entity.getY() + 128 < 71 * 64 + 192 && entity.getY() - 64 > 71 * 64 - 128) { reset(); Main.setMenu(Main.win); State.setState(State.MENU); } } } } private void increaseWaterLevels() { float speed = 3.0f; if (waterLevel / 100 > 40 && waterLevel / 100 < 60) speed = 0.2f; waterLevel += random.nextFloat() * speed; } public void render() { Camera.move(-xOffset, -yOffset); Camera.render(); glDepthMask(false); for (int i = 0; i < lights.size(); i++) { Light light = lights.get(i); light.setOffset(xOffset, yOffset); light.shadows(foregroundVertices, entities, width, height); light.render(lightShader.getID()); renderBackground(light); // Could be optimized... } renderForeground(lights); glDepthMask(true); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); for (int j = 0; j < lights.size(); j++) { for (int i = 0; i < entities.size(); i++) { entities.get(i).bindUniforms(lights.get(j)); entities.get(i).render(); } } glDisable(GL_BLEND); font.drawString((int) waterLevel / 100 + "%", 20, 20, 6, -5); } public void renderBackground(Light light) { glEnable(GL_BLEND); int x0 = xOffset >> 6; int x1 = (xOffset >> 6) + 16; int y0 = yOffset >> 6; int y1 = (yOffset >> 6) + 10; for (int y = y0; y < y1; y++) { for (int x = x0; x < x1; x++) { Tile tile = getTile(x, y, BACKGROUND); if (tile != null) { tile.bindUniforms(light); tile.render(x << 6, y << 6, waterLevel); } } } glDisable(GL_BLEND); } public void renderForeground(List<Light> lights) { int x0 = xOffset >> 6; int x1 = (xOffset >> 6) + 16; int y0 = yOffset >> 6; int y1 = (yOffset >> 6) + 10; for (int y = y0; y < y1; y++) { for (int x = x0; x < x1; x++) { Tile tile = getTile(x, y, FOREGROUND); if (tile != null) { tile.bindUniforms(lights); tile.render(x << 6, y << 6, 0.0f); } } } } public void setOffset(int xOffset, int yOffset) { this.xOffset = xOffset; this.yOffset = yOffset; } public int getXOffset() { return xOffset; } public int getYOffset() { return yOffset; } public Tile getTile(int x, int y, int level) { if (x < 0 || x >= width || y < 0 || y >= height) return null; int id = 0; if (level == BACKGROUND) { id = backgroundTiles[x + y * width]; if (id == -1) return null; return tileIDs[id]; } id = foregroundTiles[x + y * width]; if (id == -1) return null; return tileIDs[id]; } public void reset() { waterLevel = 0.0f; lights.clear(); entities.clear(); createLevel(); } /*public int getTile(int x, int y, int level) { if (x < 0 || x >= width || y < 0 || y >= height) return 0; return level == BACKGROUND ? backgroundTiles[x + y * width] : foregroundTiles[x + y * width]; }*/ }