package pathgame.rendering; import me.hammerle.snuviengine.api.FontRenderer; import me.hammerle.snuviengine.api.Renderer; import me.hammerle.snuviengine.api.Texture; import me.hammerle.snuviengine.api.TextureRenderer; import pathgame.gameplay.Keys; import pathgame.gameplay.Player; import pathgame.tilemap.Tile; import pathgame.tilemap.TileMap; import pathgame.tilemap.TileRenderType; /** * A renderer for tile maps. * * @author kajetan */ public class TileMapRenderer { // prevents rendering artifacts especially on different zoom levels private final static float ERROR = 1.0f / 512.0F; private final static float T_ERROR = 1.0f / 4096.0F; private final Texture tileTexture = new Texture("resources/tiles.png"); private final TextureRenderer textureRenderer = new TextureRenderer(20 * 20 * 2); // default to 20x20 map private final TextureRenderer swampWaterOverlayRenderer = new TextureRenderer(20 * 20 * 2); // default to 20x20 map private final TextureRenderer grassOverlayRenderer = new TextureRenderer(20 * 20 * 2); // default to 20x20 map private float scale = 1.0f; private final static String[] OVERLAY = new String[] { "&a1", "&a2", "&a3", "&e4", "&e5", "&66", "&67", "&68", "&c9" }; /** * Creates a new tile map renderer. * */ public TileMapRenderer() { } /** * Sets the scale of the map. * * @param scale the scale of the map */ public void setScale(float scale) { this.scale = scale; } /** * Returns the scale of the map. * * @return the scale of the map */ public float getScale() { return scale; } /** * Returns the scaled render width of a map. * * @param map a map * @return the scaled render width of a map */ public float getWidth(TileMap map) { return map.getWidth() * TileRenderer.TILE_SIZE * scale; } /** * Returns the scaled render height of a map. * * @param map a map * @return the scaled render height of a map */ public float getHeight(TileMap map) { return map.getHeight() * TileRenderer.TILE_SIZE * scale; } private float toTexture(int x) { return x * TileRenderer.TILE_SIZE / TileTexture.TEXTURE_SIZE; } private void addTile(TextureRenderer tr, int x, int y, float tMinX, float tMinY, float tMaxX, float tMaxY) { tr.addRectangle( x * TileRenderer.TILE_SIZE - ERROR, y * TileRenderer.TILE_SIZE - ERROR, (x + 1) * TileRenderer.TILE_SIZE + ERROR, (y + 1) * TileRenderer.TILE_SIZE + ERROR, tMinX + T_ERROR, tMinY + T_ERROR, tMaxX - T_ERROR, tMaxY - T_ERROR); } private void addTileOverlay(TextureRenderer tr, int x, int y, int ox, int oy) { addTile(tr, x, y, toTexture(ox), toTexture(oy), toTexture(ox + 1), toTexture(oy + 1)); } private boolean isInRange(TileMap map, int x, int y) { return x >= 0 && y >= 0 && x < map.getWidth() && y < map.getHeight(); } private boolean isSwamp(TileMap map, int x, int y) { return isInRange(map, x, y) && map.getTile(x, y).getRenderType() == TileRenderType.SWAMP; } private boolean isSwampOrWaterOrBorder(TileMap map, int x, int y) { if(!isInRange(map, x, y)) { return true; } TileRenderType type = map.getTile(x, y).getRenderType(); return type == TileRenderType.SWAMP || type == TileRenderType.WATER; } private void addWaterSwampOverlay(TileMap map, int x, int y) { boolean n = isSwamp(map, x, y - 1); boolean e = isSwamp(map, x + 1, y); boolean s = isSwamp(map, x, y + 1); boolean w = isSwamp(map, x - 1, y); if(!n && !w && isSwamp(map, x - 1, y - 1)) // upper, left corner { addTileOverlay(swampWaterOverlayRenderer, x, y, 7, 13); } if(!n && !e && isSwamp(map, x + 1, y - 1)) // upper, right corner { addTileOverlay(swampWaterOverlayRenderer, x, y, 6, 13); } if(!s && !w && isSwamp(map, x - 1, y + 1)) // lower, left corner { addTileOverlay(swampWaterOverlayRenderer, x, y, 5, 13); } if(!s && !e && isSwamp(map, x + 1, y + 1)) // lower, right corner { addTileOverlay(swampWaterOverlayRenderer, x, y, 4, 13); } int index = 0; index += n ? 0 : 8; index += e ? 0 : 4; index += s ? 0 : 2; index += w ? 0 : 1; if(index == 15) { return; } addTileOverlay(swampWaterOverlayRenderer, x, y, index, 15); } private void addGrassOverlay(TileMap map, int x, int y) { boolean n = isSwampOrWaterOrBorder(map, x, y - 1); boolean e = isSwampOrWaterOrBorder(map, x + 1, y); boolean s = isSwampOrWaterOrBorder(map, x, y + 1); boolean w = isSwampOrWaterOrBorder(map, x - 1, y); if(n && w && !isSwampOrWaterOrBorder(map, x - 1, y - 1)) // upper, left corner { addTileOverlay(grassOverlayRenderer, x, y, 3, 13); } if(n && e && !isSwampOrWaterOrBorder(map, x + 1, y - 1)) // upper, right corner { addTileOverlay(grassOverlayRenderer, x, y, 2, 13); } if(s && w && !isSwampOrWaterOrBorder(map, x - 1, y + 1)) // lower, left corner { addTileOverlay(grassOverlayRenderer, x, y, 1, 13); } if(s && e && !isSwampOrWaterOrBorder(map, x + 1, y + 1)) // lower, right corner { addTileOverlay(grassOverlayRenderer, x, y, 0, 13); } int index = 0; index += !n ? 0 : 8; index += !e ? 0 : 4; index += !s ? 0 : 2; index += !w ? 0 : 1; if(index == 15) { return; } addTileOverlay(grassOverlayRenderer, x, y, index, 14); } private void updateData(TileMap map) { textureRenderer.clear(); swampWaterOverlayRenderer.clear(); grassOverlayRenderer.clear(); for(int x = 0; x < map.getWidth(); x++) { for(int y = 0; y < map.getHeight(); y++) { Tile t = map.getTile(x, y); TileTexture tt = TileRenderer.getTileTexture(map, t, x, y); if(tt == null) { continue; } if(t.getRenderType() == TileRenderType.WATER) { addWaterSwampOverlay(map, x, y); addGrassOverlay(map, x, y); } else if(t.getRenderType() == TileRenderType.SWAMP) { addGrassOverlay(map, x, y); } addTile(textureRenderer, x, y, tt.getMinX(), tt.getMinY(), tt.getMaxX(), tt.getMaxY()); } } textureRenderer.build(); swampWaterOverlayRenderer.build(); grassOverlayRenderer.build(); } /** * Ticks the renderer. Used for animated tiles. * */ public void tick() { // tick tile animations here } /** * Draws the given map at the given offset. * * @param map a map * @param r the renderer given by the engine * @param forceUpdate whether an update of the render data should be forced * @param offX the x coordinate of the offset * @param offY the y coordinate of the offset */ public void renderTick(TileMap map, Renderer r, Player p, boolean forceUpdate, float offX, float offY) { r.setTextureEnabled(true); r.setColorEnabled(false); r.setMixColorEnabled(false); tileTexture.bind(); if(forceUpdate || map.isDirty()) { updateData(map); map.clean(); } float viewScale = r.getViewScale(); offX = (int) (offX * viewScale) / viewScale; offY = (int) (offY * viewScale) / viewScale; r.translateTo(offX, offY); r.scale(scale, scale); r.updateMatrix(); textureRenderer.draw(); swampWaterOverlayRenderer.draw(); grassOverlayRenderer.draw(); if(Keys.OVERLAY_KEY.isDown()) { r.translateTo(0.0f, 0.0f); r.updateMatrix(); r.setTextureEnabled(false); r.getColorRenderer().drawRectangle(0, 0, r.getViewWidth(), r.getViewHeight(), 0x44000000); r.setTextureEnabled(true); r.setColorEnabled(true); r.translateTo(offX, offY); r.scale(scale * 2.0f, scale * 2.0f); r.updateMatrix(); FontRenderer fr = r.getFontRenderer(); float midX = (TileRenderer.TILE_SIZE - fr.getWidth() * 2) * 0.5f * 0.5f; float midY = (TileRenderer.TILE_SIZE - (fr.getHeight() - 1) * 2) * 0.5f * 0.5f; for(int x = 0; x < map.getWidth(); x++) { for(int y = 0; y < map.getHeight(); y++) { Tile t = map.getTile(x, y); float tx = midX + TileRenderer.TILE_SIZE * x * 0.5f; float ty = midY + TileRenderer.TILE_SIZE * y * 0.5f; if(t.isBlockingMovement(p)) { fr.drawString(tx, ty, true, "&4-"); } else { fr.drawString(tx, ty, true, OVERLAY[map.getTile(x, y).getEnergyCost(p) - 1]); } } } } } }