/*******************************************************************************
* Copyright (c) 2016
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*******************************************************************************/
package jsettlers.graphics.map.minimap;
import jsettlers.common.Color;
import jsettlers.common.CommonConstants;
import jsettlers.common.buildings.IBuilding;
import jsettlers.common.map.IGraphicsGrid;
import jsettlers.common.mapobject.EMapObjectType;
import jsettlers.common.mapobject.IMapObject;
import jsettlers.common.movable.IMovable;
import jsettlers.graphics.map.MapDrawContext;
import jsettlers.graphics.map.minimap.MinimapMode.OccupiedAreaMode;
import jsettlers.graphics.map.minimap.MinimapMode.SettlersMode;
/**
* This class does the minimap line loading without knowing how to store the data.
*
* @author Michael Zangl
*/
public abstract class AbstractLineLoader implements Runnable {
protected static final short BLACK = Color.BLACK.toShortColor(1);
protected static final short TRANSPARENT = 0;
private static final int Y_STEP_HEIGHT = 5;
private static final int X_STEP_WIDTH = 5;
private static final int LINES_PER_RUN = 30;
private int currentline = 0;
private boolean stopped;
private int workingMinimapWidth = -1;
private int workingMinimapHeight = -1;
private int currYOffset = 0;
private int currXOffset = 0;
private final MinimapMode modeSettings;
protected final IMinimapData minimapData;
/**
* The explored landscape.
*/
private short[][] landscape = new short[1][1];
public AbstractLineLoader(IMinimapData minimapData, MinimapMode modeSettings) {
this.minimapData = minimapData;
this.modeSettings = modeSettings;
landscape[0][0] = TRANSPARENT;
}
@Override
public void run() {
while (!stopped) {
try {
updateLine();
} catch (Throwable e) {
e.printStackTrace();
}
}
}
/**
* Updates a line by putting it to the update buffer. Next time the gl context is available, it is updated.
*/
private void updateLine() {
minimapData.blockUntilUpdateAllowedOrStopped();
for (int i = 0; i < LINES_PER_RUN; i++) {
int width = minimapData.getWidth();
int height = minimapData.getHeight();
if (workingMinimapWidth != width || workingMinimapHeight != height) {
workingMinimapWidth = width;
workingMinimapHeight = height;
resizeBuffer(width, height);
resizeBackground(width, height);
currentline = 0;
currXOffset = 0;
currYOffset = 0;
}
calculateLineData(currentline);
markLineUpdate(currentline);
currentline += Y_STEP_HEIGHT;
if (currentline >= workingMinimapHeight) {
currYOffset++;
if (currYOffset >= Y_STEP_HEIGHT) {
currYOffset = 0;
currXOffset += 3;
currXOffset %= X_STEP_WIDTH;
}
currentline = currYOffset;
}
}
}
private void resizeBackground(int width, int height) {
short[][] oldLandscape = landscape;
landscape = new short[height][width];
int oldHeight = oldLandscape.length;
int oldWidth = oldLandscape[0].length;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int oldY = Math.round((float) y * oldHeight / height);
int oldX = Math.round((float) x * oldWidth / width);
landscape[y][x] = oldLandscape[Math.min(oldY, oldHeight - 1)][Math.min(oldX, oldWidth - 1)];
}
}
}
protected abstract void markLineUpdate(int line);
protected abstract void resizeBuffer(int width, int height);
private void calculateLineData(final int currentline) {
// may change!
final int safeWidth = workingMinimapWidth;
final int safeHeight = workingMinimapHeight;
final MapDrawContext context = minimapData.getContext();
final IGraphicsGrid map = context.getMap();
// for height shades
final short mapWidth = map.getWidth();
final short mapHeight = map.getHeight();
int mapLineHeight = mapHeight / safeHeight + 1;
// first map tile in line
int mapMaxY = (int) ((1 - (float) currentline / safeHeight) * mapHeight);
// first map line not in line
int mapMinY = (int) ((1 - (float) (currentline + 1) / safeHeight) * mapHeight);
if (mapMinY == mapMaxY) {
if (mapMaxY == mapHeight) {
mapMinY = mapHeight - 1;
} else {
mapMaxY = mapMinY - 1;
}
}
int myXOffset = (currXOffset + currentline * 3) % X_STEP_WIDTH;
for (int x = myXOffset; x < safeWidth; x += X_STEP_WIDTH) {
int mapMinX = (int) ((float) x / safeWidth * mapWidth);
int mapMaxX = (int) ((float) (x + 1) / safeWidth * mapWidth);
if (mapMinX != 0 && mapMaxX == mapMinX) {
mapMinX = mapMaxX - 1;
}
int centerX = (mapMaxX + mapMinX) / 2;
int centerY = (mapMaxY + mapMinY) / 2;
short color = TRANSPARENT;
byte visibleStatus = map.getVisibleStatus(centerX, centerY);
if (visibleStatus > CommonConstants.FOG_OF_WAR_EXPLORED) {
color = getSettlerForArea(map, context, mapMinX, mapMinY, mapMaxX, mapMaxY);
}
if (visibleStatus > CommonConstants.FOG_OF_WAR_EXPLORED || landscape[currentline][x] == TRANSPARENT) {
float basecolor = ((float) visibleStatus) / CommonConstants.FOG_OF_WAR_VISIBLE;
int dheight = map.getHeightAt(centerX, mapMinY) - map.getHeightAt(centerX, Math.min(mapMinY + mapLineHeight, mapHeight - 1));
basecolor *= 1 + .15f * dheight;
short landscapeColor;
if (basecolor >= 0) {
landscapeColor = getColorForArea(map, mapMinX, mapMinY, mapMaxX, mapMaxY).toShortColor(basecolor);
} else {
landscapeColor = BLACK;
}
if (color == TRANSPARENT) {
color = landscapeColor;
}
landscape[currentline][x] = landscapeColor;
}
if (color == TRANSPARENT) {
color = landscape[currentline][x];
}
setBuffer(currentline, x, color);
}
}
protected abstract void setBuffer(int currentline, int x, short color);
private Color getColorForArea(IGraphicsGrid map, int mapminX, int mapminY, int mapmaxX, int mapmaxY) {
int centerx = (mapmaxX + mapminX) / 2;
int centery = (mapmaxY + mapminY) / 2;
return map.getLandscapeTypeAt(centerx, centery).color;
}
private short getSettlerForArea(IGraphicsGrid map, MapDrawContext context, int mapminX, int mapminY, int mapmaxX, int mapmaxY) {
SettlersMode displaySettlers = this.modeSettings.getDisplaySettlers();
OccupiedAreaMode displayOccupied = this.modeSettings.getDisplayOccupied();
boolean displayBuildings = this.modeSettings.getDisplayBuildings();
short occupiedColor = TRANSPARENT;
short settlerColor = TRANSPARENT;
short buildingColor = TRANSPARENT;
for (int y = mapminY; y < mapmaxY && (displayOccupied != OccupiedAreaMode.NONE || displayBuildings || displaySettlers != SettlersMode.NONE); y++) {
for (int x = mapminX; x < mapmaxX
&& (displayOccupied != OccupiedAreaMode.NONE || displayBuildings || displaySettlers != SettlersMode.NONE); x++) {
boolean visible = map.getVisibleStatus(x, y) > CommonConstants.FOG_OF_WAR_EXPLORED;
if (visible && displaySettlers != SettlersMode.NONE) {
IMovable settler = map.getMovableAt(x, y);
if (settler != null && (displaySettlers == SettlersMode.ALL || settler.getMovableType().isPlayerControllable())) {
settlerColor = context.getPlayerColor(settler.getPlayerId()).toShortColor(1);
// don't search any more.
displaySettlers = SettlersMode.NONE;
} else if (displaySettlers != SettlersMode.NONE) {
IMapObject object = map.getMapObjectsAt(x, y);
IBuilding building = (object != null) ? (IBuilding) object.getMapObject(EMapObjectType.BUILDING) : null;
if (building instanceof IBuilding.IOccupied) {
IBuilding.IOccupied occupyed = (IBuilding.IOccupied) building;
if (occupyed.isOccupied()) {
settlerColor = context.getPlayerColor(occupyed.getPlayerId()).toShortColor(1);
}
}
}
}
if (visible && displayOccupied == OccupiedAreaMode.BORDERS) {
if (map.isBorder(x, y)) {
byte player = map.getPlayerIdAt(x, y);
Color playerColor = context.getPlayerColor(player);
occupiedColor = playerColor.toShortColor(1);
displayOccupied = OccupiedAreaMode.NONE;
}
} else if (visible && displayOccupied == OccupiedAreaMode.AREA) {
byte player = map.getPlayerIdAt(x, y);
if (player >= 0 && !map.getLandscapeTypeAt(x, y).isBlocking) {
Color playerColor = context.getPlayerColor(player);
// Now add a landscape below that....
Color landscape = getColorForArea(map, mapminX, mapminY, mapmaxX, mapmaxY);
playerColor = landscape.toGreyScale().multiply(playerColor);
occupiedColor = playerColor.toShortColor(1);
displayOccupied = OccupiedAreaMode.NONE;
}
}
if (displayBuildings) {
if (map.isBuilding(x, y)) {
buildingColor = BLACK;
}
}
}
}
return settlerColor != TRANSPARENT ? settlerColor : buildingColor != TRANSPARENT ? buildingColor : occupiedColor;
}
/**
* Stops the execution of this line loader.
*/
public void stop() {
stopped = true;
}
}