/*
* Copyright (c) 2003-onwards Shaven Puppy Ltd
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'Shaven Puppy' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package worm;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import net.puppygames.applet.Game;
import net.puppygames.applet.Screen;
import net.puppygames.applet.TickableObject;
import net.puppygames.applet.effects.Emitter;
import net.puppygames.applet.effects.EmitterFeature;
import org.lwjgl.util.Color;
import org.lwjgl.util.Point;
import org.lwjgl.util.ReadableColor;
import org.lwjgl.util.ReadablePoint;
import worm.animation.SimpleThingWithLayers;
import worm.features.DecalFeature;
import worm.features.LayersFeature;
import worm.screens.GameScreen;
import com.shavenpuppy.jglib.Resources;
import com.shavenpuppy.jglib.interpolators.ColorInterpolator;
import com.shavenpuppy.jglib.interpolators.LinearInterpolator;
import com.shavenpuppy.jglib.opengl.GLRenderable;
import com.shavenpuppy.jglib.opengl.GLTexture;
import com.shavenpuppy.jglib.resources.MappedColor;
import com.shavenpuppy.jglib.sprites.Sprite;
import com.shavenpuppy.jglib.util.FPMath;
import static org.lwjgl.opengl.GL11.*;
/**
* A renderable chunk of map.
* @author Cas
*/
public class MapRenderer implements MapListener {
/** The size of a tile */
public static final int TILE_SIZE = 16;
public static final int OPAQUE_SIZE = 6;
public static final int FADE_SIZE = 12;
private static final short[] FADE_INDICES = {0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15};
private static final short[] OPAQUE_INDICES = {16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23, 24, 25, 26, 24, 26, 27, 28, 29, 30, 28, 30, 31};
/** Layers */
private static final int FLOOR_LAYER = 0;
private static final int FLOOR_FADE = 4;
/** Temporary color */
private static final Color TEMP = new Color();
/** Dimensions of the renderable screen, in tiles */
private int tileWidth, tileHeight;
/** Origin on screen */
private int originX, originY;
/** A bunch of TileInfos */
private TileInfo[] tileInfo;
/** Fog o' war */
private TickableObject fogOfWar;
/** Map we're rendering */
private GameMap map;
/** Screen */
private final Screen screen;
/** A Map of visible tiles. This maps map coordinates to RenderedTiles. */
private final Map<Point, RenderedTile> visibleMap = new HashMap<Point, RenderedTile>();
/** Visibility */
private boolean visible = true;
/** Current location, in pixels */
private int x, y;
/** Debug */
private boolean debug;
/** A RenderedTile contains all the information needed to render a tile. */
private class RenderedTile implements TileInfo.TileDisplay {
private Sprite[] tileSprite;
private Sprite[][] decalSprite;
private SimpleThingWithLayers[] layersSprite;
private Emitter[][] layersEmitter;
private ReadablePoint[][] decalOffset;
private Emitter emitter;
private int emitterX, emitterY;
private Tile[] displayed;
private final int mapx, mapy;
private int screenx, screeny;
private final float ratio00, ratio10, ratio11, ratio01;
private boolean updated;
/**
* C'tor
* @param src
*/
RenderedTile(int mapx, int mapy) {
this.mapx = mapx;
this.mapy = mapy;
ratio00 = ColorAttenuationConstants.dist(mapx, mapy, map.getWidth(), map.getHeight()) / ColorAttenuationConstants.getMaxDist();
ratio10 = ColorAttenuationConstants.dist(mapx + 1, mapy, map.getWidth(), map.getHeight()) / ColorAttenuationConstants.getMaxDist();
ratio11 = ColorAttenuationConstants.dist(mapx + 1, mapy + 1, map.getWidth(), map.getHeight()) / ColorAttenuationConstants.getMaxDist();
ratio01 = ColorAttenuationConstants.dist(mapx, mapy + 1, map.getWidth(), map.getHeight()) / ColorAttenuationConstants.getMaxDist();
}
void setUpdated() {
updated = true;
}
boolean isUpdated() {
return updated;
}
void updateColors() {
for (int i = 0; i < displayed.length; i++) {
// Fade layer 0 tiles
if (displayed[i] != null) {
MappedColor color = getColor(displayed[i]);
Sprite ts = tileSprite[i];
if (i == 0 || displayed[i] != null && displayed[i].isAttenuated()) {
if (color.getColorName() != null && color.getColorName().intern() == LayersFeature.SHADOW_COLOR_NAME) {
ts.setColor(0, new AttenuatedColor(color, map.getFFade(mapx, mapy), FLOOR_FADE, ratio00, 0, true));
ts.setColor(1, new AttenuatedColor(color, map.getFFade(mapx + 1, mapy), FLOOR_FADE, ratio10, 0, true));
ts.setColor(2, new AttenuatedColor(color, map.getFFade(mapx + 1, mapy + 1), FLOOR_FADE, ratio11, 0, true));
ts.setColor(3, new AttenuatedColor(color, map.getFFade(mapx, mapy + 1), FLOOR_FADE, ratio01, 0, true));
} else {
ts.setColor(0, new AttenuatedColor(color, map.getFFade(mapx, mapy), FLOOR_FADE, ratio00, 0, false));
ts.setColor(1, new AttenuatedColor(color, map.getFFade(mapx + 1, mapy), FLOOR_FADE, ratio10, 0, false));
ts.setColor(2, new AttenuatedColor(color, map.getFFade(mapx + 1, mapy + 1), FLOOR_FADE, ratio11, 0, false));
ts.setColor(3, new AttenuatedColor(color, map.getFFade(mapx, mapy + 1), FLOOR_FADE, ratio01, 0, false));
}
} else {
ts.setColors(color);
}
List<DecalFeature> decals = displayed[i].getDecals();
if (decals != null) {
for (int j = 0; j < decalSprite[i].length; j ++) {
DecalFeature df = decals.get(j);
if (df != null) {
MappedColor decalColor = getColor(df);
Sprite ds = decalSprite[i][j];
if (df.isAttenuated()) {
if (decalColor.getColorName() != null && decalColor.getColorName() == LayersFeature.SHADOW_COLOR_NAME) {
ds.setColor(0, new AttenuatedColor(decalColor, map.getFFade(mapx, mapy), FLOOR_FADE, ratio00, 0, true));
ds.setColor(1, new AttenuatedColor(decalColor, map.getFFade(mapx + 1, mapy), FLOOR_FADE, ratio10, 0, true));
ds.setColor(2, new AttenuatedColor(decalColor, map.getFFade(mapx + 1, mapy + 1), FLOOR_FADE, ratio11, 0, true));
ds.setColor(3, new AttenuatedColor(decalColor, map.getFFade(mapx, mapy + 1), FLOOR_FADE, ratio01, 0, true));
} else {
ds.setColor(0, new AttenuatedColor(decalColor, map.getFFade(mapx, mapy), FLOOR_FADE, ratio00, 0, false));
ds.setColor(1, new AttenuatedColor(decalColor, map.getFFade(mapx + 1, mapy), FLOOR_FADE, ratio10, 0, false));
ds.setColor(2, new AttenuatedColor(decalColor, map.getFFade(mapx + 1, mapy + 1), FLOOR_FADE, ratio11, 0, false));
ds.setColor(3, new AttenuatedColor(decalColor, map.getFFade(mapx, mapy + 1), FLOOR_FADE, ratio01, 0, false));
}
} else {
ds.setColors(decalColor);
}
}
}
}
LayersFeature layers = displayed[i].getLayers();
if (layers != null) {
layers.updateColors(layersSprite[i].getSprites(), ratio00, ratio10, ratio11, ratio01, map.getFFade(mapx, mapy), map.getFFade(mapx, mapy), map.getTFade(mapx, mapy), map.getTFade(mapx, mapy));
}
}
}
}
@Override
public void setTiles(Tile[] tile) {
updated = false;
if (tile != null) {
if (tileSprite == null) {
tileSprite = new Sprite[tile.length];
for (int i = 0; i < tileSprite.length; i ++) {
tileSprite[i] = screen.allocateSprite(screen);
tileSprite[i].setLayer(FLOOR_LAYER + i);
}
}
if (decalSprite == null) {
decalSprite = new Sprite[tile.length][];
decalOffset = new ReadablePoint[tile.length][];
}
if (layersSprite == null) {
layersSprite = new SimpleThingWithLayers[tile.length];
layersEmitter = new Emitter[tile.length][];
}
for (int i = 0; i < tileSprite.length; i ++) {
if (tile[i] == null) {
tileSprite[i].setVisible(false);
} else {
tile[i].toSprite(tileSprite[i]);
if (!visible) {
tileSprite[i].setVisible(false);
}
}
}
// Maybe there's an emitter?
EmitterFeature ef = null;
int emitterLayer = 0;
for (int i = tile.length; --i >= 0; ) {
if (tile[i] != null) {
ef = tile[i].getEmitter();
emitterLayer = i;
if (ef != null) {
break;
}
}
}
if (emitter == null && ef != null) {
emitter = ef.spawn(screen);
Point p = tile[emitterLayer].getEmitterPos();
if (p != null) {
emitterX = p.getX();
emitterY = p.getY();
}
emitter.setLocation(mapx * TILE_SIZE + emitterX, mapy * TILE_SIZE + emitterY);
} else if (emitter != null && ef == null) {
emitter.remove();
emitter = null;
}
if (emitter != null) {
emitter.setVisible(visible);
}
if (displayed == null) {
// All the tiles are new
displayed = new Tile[tile.length];
for (int i = 0; i < displayed.length; i ++) {
displayed[i] = tile[i];
if (displayed[i] != null) {
// Maybe create decals
List<DecalFeature> decals = tile[i].getDecals();
if (decals != null) {
decalSprite[i] = new Sprite[decals.size()];
decalOffset[i] = new ReadablePoint[decals.size()];
for (int j = 0; j < decalSprite[i].length; j ++) {
DecalFeature df = decals.get(j);
if (df != null) {
decalSprite[i][j] = screen.allocateSprite(screen);
decalSprite[i][j].setScale(FPMath.fpValue(df.getScale()));
decalSprite[i][j].setLayer(df.getLayer());
decalSprite[i][j].setSubLayer(df.getSubLayer());
decalSprite[i][j].setYSortOffset(df.getYSortOffset());
decalOffset[i][j] = df.getOffset();
df.getAppearance().toSprite(decalSprite[i][j]);
}
}
}
if (tile[i].getLayers() != null) {
layersSprite[i] = new SimpleThingWithLayers(GameScreen.getInstance());
tile[i].getLayers().createSprites(GameScreen.getInstance(), layersSprite[i]);
Point p = tile[i].getEmitterPos();
if (p != null) {
emitterX = p.getX();
emitterY = p.getY();
}
layersEmitter[i] = tile[i].getLayers().createEmitters(GameScreen.getInstance(), mapx * TILE_SIZE + emitterX, mapy * TILE_SIZE + emitterY);
}
}
}
updateColors();
} else {
// Some tiles are new
boolean doColorsUpdate = GameScreen.isDiddlerOpen();
for (int i = 0; i < displayed.length; i++) {
if (displayed[i] != tile[i]) {
doColorsUpdate = true;
// Maybe remove decals
if (decalSprite[i] != null) {
for (int j = 0; j < decalSprite[i].length; j ++) {
if (decalSprite[i][j] != null) {
decalSprite[i][j].deallocate();
}
}
decalSprite[i] = null;
decalOffset[i] = null;
}
// Maybe remove layers
if (layersSprite[i] != null) {
layersSprite[i].remove();
layersSprite[i] = null;
}
// Maybe emitters too
if (layersEmitter[i] != null) {
for (int j = 0; j < layersEmitter[i].length; j ++) {
if (layersEmitter[i][j] != null) {
layersEmitter[i][j].remove();
}
}
layersEmitter[i] = null;
}
// Maybe create decals
List<DecalFeature> decals = tile[i].getDecals();
if (decals != null) {
decalSprite[i] = new Sprite[decals.size()];
decalOffset[i] = new ReadablePoint[decals.size()];
for (int j = 0; j < decalSprite[i].length; j ++) {
DecalFeature df = decals.get(j);
if (df != null) {
decalSprite[i][j] = screen.allocateSprite(screen);
decalSprite[i][j].setLayer(df.getLayer());
decalSprite[i][j].setSubLayer(df.getSubLayer());
decalSprite[i][j].setYSortOffset(df.getYSortOffset());
decalSprite[i][j].setScale(FPMath.fpValue(df.getScale()));
decalOffset[i][j] = df.getOffset();
df.getAppearance().toSprite(decalSprite[i][j]);
}
}
}
// Maybe create layers
if (tile[i].getLayers() != null) {
layersSprite[i] = new SimpleThingWithLayers(GameScreen.getInstance());
tile[i].getLayers().createSprites(GameScreen.getInstance(), layersSprite[i]);
Point p = tile[i].getEmitterPos();
if (p != null) {
emitterX = p.getX();
emitterY = p.getY();
}
layersEmitter[i] = tile[i].getLayers().createEmitters(GameScreen.getInstance(), mapx * TILE_SIZE + emitterX, mapy * TILE_SIZE + emitterY);
}
displayed[i] = tile[i];
}
}
if (doColorsUpdate) {
updateColors();
}
}
} else {
remove();
}
}
/**
* Removes all sprites and emitter associated with this tile
*/
void remove() {
if (tileSprite != null) {
for (Sprite element : tileSprite) {
if (element != null) {
element.deallocate();
}
}
tileSprite = null;
}
if (decalSprite != null) {
for (Sprite[] element : decalSprite) {
if (element != null) {
for (int j = 0; j < element.length; j ++) {
element[j].deallocate();
}
}
}
decalSprite = null;
decalOffset = null;
}
if (emitter != null) {
emitter.remove();
emitter = null;
}
for (int i = 0; i < layersSprite.length; i ++) {
if (layersSprite[i] != null) {
layersSprite[i].remove();
layersSprite[i] = null;
}
}
for (int i = 0; i < layersEmitter.length; i ++) {
if (layersEmitter[i] != null) {
for (int j = 0; j < layersEmitter[i].length; j ++) {
if (layersEmitter[i][j] != null) {
layersEmitter[i][j].remove();
}
}
layersEmitter[i] = null;
}
}
layersSprite = null;
layersEmitter = null;
displayed = null;
}
/**
* Sets the location onscreen
* @param sx
* @param sy
*/
void setLocation(int sx, int sy) {
screenx = sx;
screeny = sy;
if (tileSprite != null) {
for (Sprite element : tileSprite) {
if (element != null) {
element.setLocation(sx, sy);
}
}
}
if (decalSprite != null) {
for (int i = 0; i < decalSprite.length; i ++) {
if (decalSprite[i] != null) {
for (int j = 0; j < decalSprite[i].length; j ++) {
if (decalSprite[i][j] != null) {
decalSprite[i][j].setLocation(sx + decalOffset[i][j].getX(), sy + decalOffset[i][j].getY());
}
}
}
}
}
if (layersSprite != null) {
for (int i = 0; i < layersSprite.length; i ++) {
if (layersSprite[i] != null) {
displayed[i].getLayers().updateLocation(layersSprite[i].getSprites(), sx, sy);
}
}
}
}
/**
* Debugging
*/
void postRender() {
if (debug) {
if (map == null) {
return;
}
Tile t0 = map.getTile(mapx, mapy, 0);
Tile t1 = map.getTile(mapx, mapy, 1);
Tile t2 = map.getTile(mapx, mapy, 2);
if (t0 != null && t0.isImpassable() || t1 != null && t1.isImpassable() || t2 != null && t2.isImpassable()) {
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glLineWidth(1.0f);
// We need to work out the screen coordinates of the tile
glColor3f(1,0,0);
glBegin(GL_LINE_LOOP);
glVertex2i(screenx, screeny);
glVertex2i(screenx + TILE_SIZE, screeny);
glVertex2i(screenx + TILE_SIZE, screeny + TILE_SIZE);
glVertex2i(screenx, screeny + TILE_SIZE);
glEnd();
}
if (t0 != null && !t0.isBulletThrough() || t1 != null && !t1.isBulletThrough() || t2 != null && !t2.isBulletThrough()) {
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glLineWidth(1.0f);
// We need to work out the screen coordinates of the tile
glColor3f(1,0,0);
glBegin(GL_LINES);
glVertex2i(screenx, screeny);
glVertex2i(screenx + TILE_SIZE, screeny + TILE_SIZE);
glEnd();
}
if (map.isAttacking(mapx, mapy)) {
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glLineWidth(1.0f);
// We need to work out the screen coordinates of the tile
glColor3f(0,1,0);
glBegin(GL_LINE_LOOP);
glVertex2i(screenx + 1, screeny + 1);
glVertex2i(screenx - 1 + TILE_SIZE, screeny + 1);
glVertex2i(screenx - 1+ TILE_SIZE, screeny + TILE_SIZE - 1);
glVertex2i(screenx + 1, screeny + TILE_SIZE - 1);
glEnd();
}
if (map.isOccupied(mapx, mapy)) {
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glLineWidth(1.0f);
// We need to work out the screen coordinates of the tile
glColor3f(0,0,1);
glBegin(GL_LINE_LOOP);
glVertex2i(screenx + 2, screeny + 2);
glVertex2i(screenx - 2 + TILE_SIZE, screeny + 2);
glVertex2i(screenx - 2 + TILE_SIZE, screeny + TILE_SIZE - 2);
glVertex2i(screenx + 2, screeny + TILE_SIZE - 2);
glEnd();
}
int danger = map.getDanger(mapx, mapy);
if (danger > 0) {
glDisable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
float alpha = 0.5f;
float ratio = danger / 64.0f;
Color c = ColorInterpolator.interpolate(ReadableColor.BLACK, ReadableColor.WHITE, ratio, LinearInterpolator.instance, new Color());
glColor4ub(c.getRedByte(), c.getGreenByte(), c.getBlueByte(), (byte) (alpha * 255));
//glColor4f(danger < 25 ? danger / 25.0f : danger == 0 ? 0.0f : 1.0f, danger >= 25 && danger < 50 ? (danger - 25.0f) / 25.0f : danger >= 50 ? 1.0f : 0.0f, danger >= 50 && danger < 100 ? (danger - 50.0f) / 50.0f : danger >= 100 ? 1.0f : 0.0f, alpha);
glBegin(GL_QUADS);
glVertex2i(screenx, screeny);
glVertex2i(screenx + TILE_SIZE, screeny);
glVertex2i(screenx + TILE_SIZE, screeny + TILE_SIZE);
glVertex2i(screenx, screeny + TILE_SIZE);
glEnd();
}
}
}
}
/** Scratch Point used for visibleMap reading */
private static final Point temp = new Point();
/**
* C'tor
* @param screen The screen we're being drawn on
*/
public MapRenderer(Screen screen) {
this.screen = screen;
fogOfWar = new TickableObject() {
final GLTexture fadeTexture = Resources.get("edgefade.texture");
final GLRenderable init = new GLRenderable() {
@Override
public void render() {
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
fadeTexture.render();
}
};
final GLRenderable init2 = new GLRenderable() {
@Override
public void render() {
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
}
};
@Override
protected void render() {
glRender(init);
// Determine the offset in pixels
int ox = x - originX;
int oy = y - originY;
// Bottom strip
glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
glTexCoord2f(0.0f, 0.5f);
glVertex2f(0 - ox, 0 - oy);
glTexCoord2f(0.0f, 0.5f);
glVertex2f(map.getWidth() * TILE_SIZE - ox, 0 - oy);
glTexCoord2f(0.5f, 0.5f);
glVertex2f(map.getWidth() * TILE_SIZE - ox, TILE_SIZE * FADE_SIZE - oy);
glTexCoord2f(0.5f, 0.5f);
glVertex2f(0 - ox, TILE_SIZE * FADE_SIZE - oy);
// Left strip
glTexCoord2f(0.0f, 0.5f);
glVertex2f(0 - ox, 0 - oy);
glTexCoord2f(0.5f, 0.5f);
glVertex2f(TILE_SIZE * FADE_SIZE - ox, 0 - oy);
glTexCoord2f(0.5f, 0.5f);
glVertex2f(TILE_SIZE * FADE_SIZE - ox, map.getHeight() * TILE_SIZE - oy);
glTexCoord2f(0.0f, 0.5f);
glVertex2f(0 - ox, map.getHeight() * TILE_SIZE - oy);
// Top strip
glTexCoord2f(0.5f, 0.5f);
glVertex2f(0 - ox, (map.getHeight() - FADE_SIZE - 1) * TILE_SIZE - oy);
glTexCoord2f(0.5f, 0.5f);
glVertex2f(map.getWidth() * TILE_SIZE - ox, (map.getHeight() - FADE_SIZE - 1) * TILE_SIZE - oy);
glTexCoord2f(0.0f, 0.5f);
glVertex2f(map.getWidth() * TILE_SIZE - ox, (map.getHeight()) * TILE_SIZE - oy);
glTexCoord2f(0.0f, 0.5f);
glVertex2f(0 - ox, (map.getHeight()) * TILE_SIZE - oy);
// Right strip
glTexCoord2f(0.5f, 0.5f);
glVertex2f((map.getWidth() - FADE_SIZE - 1) * TILE_SIZE - ox, 0 - oy);
glTexCoord2f(0.0f, 0.5f);
glVertex2f((map.getWidth()) * TILE_SIZE - ox, 0 - oy);
glTexCoord2f(0.0f, 0.5f);
glVertex2f((map.getWidth()) * TILE_SIZE - ox, map.getHeight() * TILE_SIZE - oy);
glTexCoord2f(0.5f, 0.5f);
glVertex2f((map.getWidth() - FADE_SIZE - 1) * TILE_SIZE - ox, map.getHeight() * TILE_SIZE - oy);
glRender(GL_TRIANGLES, FADE_INDICES);
glRender(init2);
// Bottom opaque strip
glVertex2f(0 - ox, 0 - oy - TILE_SIZE * OPAQUE_SIZE);
glVertex2f((map.getWidth()) * TILE_SIZE - ox, 0 - oy - TILE_SIZE * OPAQUE_SIZE);
glVertex2f((map.getWidth()) * TILE_SIZE - ox, 0 - oy);
glVertex2f(0 - ox, 0 - oy);
// Left opaque strip
glVertex2f(0 - ox - TILE_SIZE * OPAQUE_SIZE, 0 - oy - TILE_SIZE * OPAQUE_SIZE);
glVertex2f(0 - ox, 0 - oy - TILE_SIZE * OPAQUE_SIZE);
glVertex2f(0 - ox, (map.getHeight() + OPAQUE_SIZE) * TILE_SIZE - oy);
glVertex2f(0 - ox - TILE_SIZE * OPAQUE_SIZE, (map.getHeight() + OPAQUE_SIZE) * TILE_SIZE - oy);
// Top opaque strip
glVertex2f(0 - ox, (map.getHeight()) * TILE_SIZE - oy);
glVertex2f((map.getWidth()) * TILE_SIZE - ox, (map.getHeight()) * TILE_SIZE - oy);
glVertex2f((map.getWidth()) * TILE_SIZE - ox, (map.getHeight() + OPAQUE_SIZE) * TILE_SIZE - oy);
glVertex2f(0 - ox, (map.getHeight() + 1 + OPAQUE_SIZE) * TILE_SIZE - oy);
// Right opaque strip
glVertex2f((map.getWidth()) * TILE_SIZE - ox, 0 - oy - TILE_SIZE * OPAQUE_SIZE);
glVertex2f((map.getWidth() + OPAQUE_SIZE) * TILE_SIZE - ox, 0 - oy - TILE_SIZE * OPAQUE_SIZE);
glVertex2f((map.getWidth() + OPAQUE_SIZE) * TILE_SIZE - ox, (map.getHeight() + OPAQUE_SIZE) * TILE_SIZE - oy);
glVertex2f((map.getWidth()) * TILE_SIZE - ox, (map.getHeight() + OPAQUE_SIZE) * TILE_SIZE - oy);
glRender(GL_TRIANGLES, OPAQUE_INDICES);
}
};
fogOfWar.setLayer(Layers.FOG);
fogOfWar.spawn(screen);
}
/**
* Called when the UI is resized
*/
public void onResized() {
int adjustWidth = Game.getWidth() % TILE_SIZE > 0 ? 7 : 6;
int adjustHeight = Game.getHeight() % TILE_SIZE > 0 ? 7 : 6;
tileWidth = Game.getWidth() / TILE_SIZE + adjustWidth;
tileHeight = Game.getHeight() / TILE_SIZE + adjustHeight;
tileInfo = new TileInfo[tileWidth * tileHeight];
for (int i = 0; i < tileInfo.length; i ++) {
tileInfo[i] = new TileInfo();
}
}
/**
* Sets the pixel origin of the map
*/
public void setOrigin(int originX, int originY) {
this.originX = originX;
this.originY = originY;
}
/**
* Cleanup
*/
public void cleanup() {
setMap(null);
if (fogOfWar != null) {
fogOfWar.remove();
fogOfWar = null;
}
}
/**
* Sets the map that we are going to render
* @param map The map to set.
*/
public void setMap(GameMap map) {
this.map = map;
if (map != null) {
float dx = map.getWidth() / 2.0f;
float dy = map.getHeight() / 2.0f;
ColorAttenuationConstants.setMaxDist((float) Math.sqrt(dx * dx + dy * dy));
map.setListener(this);
}
// Reset the display
reset();
}
/**
* Reset the display - clears away all the sprites etc.
*/
private void reset() {
for (Entry<Point, RenderedTile> entry : visibleMap.entrySet()) {
entry.getValue().remove();
}
visibleMap.clear();
}
/**
* @return Returns the map.
*/
public GameMap getMap() {
return map;
}
/**
* Render the map at the specified pixel coordinates.
* @param x
* @param y
*/
public void render(int newx, int newy) {
this.x = newx;
this.y = newy;
// No map? Draw nothing!
if (map == null) {
return;
}
// Determine the map coordinates
int mx = x / MapRenderer.TILE_SIZE;
int my = y / MapRenderer.TILE_SIZE;
// Determine the offset in pixels
int ox = x % MapRenderer.TILE_SIZE - originX;
int oy = y % MapRenderer.TILE_SIZE - originY;
// Read in the block of map tiles
int count = 0;
for (int yy = my; yy < my + tileHeight; yy ++) {
for (int xx = mx; xx < mx + tileWidth; xx ++) {
map.toTileInfo(xx, yy, tileInfo[count ++]);
}
}
// Remove all those tiles that are no longer visible from the visible set,
// which deallocates the sprites and removes any emitters.
for (Iterator<Map.Entry<Point, RenderedTile>> i = visibleMap.entrySet().iterator(); i.hasNext(); ) {
Map.Entry<Point, RenderedTile> entry = i.next();
Point p = entry.getKey();
if (p.getX() < mx || p.getX() >= mx + tileWidth || p.getY() < my || p.getY() >= my + tileHeight) {
RenderedTile rt = entry.getValue();
rt.remove();
i.remove();
}
}
// Now add the new tiles
count = 0;
for (int yy = my; yy < my + tileHeight; yy ++) {
for (int xx = mx; xx < mx + tileWidth; xx ++) {
// See if this tile is already visible.
temp.setLocation(xx, yy);
RenderedTile rt = visibleMap.get(temp);
if (rt == null) {
// It's not visible on the screen, so let's create one
rt = new RenderedTile(xx, yy);
// Plonk it in the visible map
Point p = new Point(xx, yy);
visibleMap.put(p, rt);
// Update with current tileinfo
tileInfo[count].toDisplay(rt);
} else if (rt.isUpdated() || GameScreen.isDiddlerOpen()) {
// Make sure colours update
tileInfo[count].toDisplay(rt);
}
// Set the location on screen
rt.setLocation((xx - mx) * TILE_SIZE - ox, (yy - my) * TILE_SIZE - oy);
count ++;
}
}
}
/**
* Called after all sprites are rendered etc.
*/
public void postRender() {
if (!visible) {
return;
}
// Determine the map coordinates
int mx = x / MapRenderer.TILE_SIZE;
int my = y / MapRenderer.TILE_SIZE;
for (int yy = my; yy < my + tileHeight; yy ++) {
for (int xx = mx; xx < mx + tileWidth; xx ++) {
// See if this tile is already visible.
temp.setLocation(xx, yy);
RenderedTile rt = visibleMap.get(temp);
if (rt != null) {
rt.postRender();
}
}
}
}
/**
* Sets the visibility of the map
* @param visible
*/
public void setVisible(boolean visible) {
this.visible = visible;
if (!visible) {
reset();
}
}
/**
* @return return true if the renderer is visible
*/
public boolean isVisible() {
return visible;
}
/**
* @return the width, in tiles, plus 1
*/
public int getWidth() {
return tileWidth;
}
/**
* @return the height, in tiles, plus 1
*/
public int getHeight() {
return tileHeight;
}
/**
* @return Returns the originX.
*/
public int getOriginX() {
return originX;
}
/**
* @return Returns the originY.
*/
public int getOriginY() {
return originY;
}
private MappedColor getColor(Tile t) {
MappedColor ret;
if (t == null) {
return new MappedColor(ReadableColor.WHITE);
} else {
ret = t.getColor();
}
if (ret == null) {
return new MappedColor(ReadableColor.WHITE);
}
return ret;
}
private MappedColor getColor(DecalFeature df) {
MappedColor ret;
if (df == null) {
return new MappedColor(ReadableColor.WHITE);
} else {
ret = df.getColor();
}
if (ret == null) {
return new MappedColor(ReadableColor.WHITE);
}
return ret;
}
/**
* @param debug the debug to set
*/
public void setDebug(boolean debug) {
this.debug = debug;
}
@Override
public void onChanged(int x, int y) {
RenderedTile rt = visibleMap.get(temp);
if (rt != null) {
rt.setUpdated();
}
}
}