/*******************************************************************************
* Copyright (c) 2015 - 2017
*
* 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.algorithms.construction;
import jsettlers.common.buildings.BuildingAreaBitSet;
import jsettlers.common.buildings.EBuildingType;
import jsettlers.common.map.shapes.IMapArea;
import jsettlers.common.map.shapes.MapRectangle;
import jsettlers.common.position.RelativePoint;
import java.util.BitSet;
/**
* Algorithm to calculate the construction marks for the user.
*
* @author Andreas Eberle
*
*/
public final class NewConstructionMarksAlgorithm {
private final AbstractConstructionMarkableMap map;
private final byte playerId;
private MapRectangle lastArea = null;
public NewConstructionMarksAlgorithm(AbstractConstructionMarkableMap map, byte player) {
this.map = map;
this.playerId = player;
}
public void calculateConstructMarks(final MapRectangle mapArea, EBuildingType buildingType) {
if (lastArea != null) {
removeConstructionMarks(lastArea, mapArea);
}
BuildingAreaBitSet buildingArea = buildingType.getBuildingAreaBitSet();
boolean binaryConstructionMarkValues = !buildingType.needsFlattenedGround();
RelativePoint[] positionsToBeFlattened = buildingType.getBuildingArea();
// declare local variables
final short[] xJumps = buildingArea.xJumps;
final short[] yJumps = buildingArea.yJumps;
final int lineLength = mapArea.getLineLength() + mapArea.getHeight() / 2;
final BitSet doneSet = new BitSet(lineLength * mapArea.getHeight());
final int xOffsetForBuilding = buildingArea.minX;
final int yOffsetForBuilding = buildingArea.minY;
final int buildingAreaWidth = buildingArea.width;
final int buildingAreaHeight = buildingArea.height;
// iterate over the positions in the mapArea with the offset from the buildingArea
for (int line = 0; line < mapArea.getHeight(); line++) {
final int y = mapArea.getLineY(line);
final int xLineOffset = mapArea.getMinX();
DX_LOOP: for (int dx = 0; dx < lineLength; dx++) {
final int x = xLineOffset + dx;
final short partitionId;
if (!mapArea.contains(x, y) || doneSet.get(dx + line * lineLength)) { // if this position has already been pruned.
continue;
}
{ // get the partition and check if the player is allowed to use this partition
int firstPosX = buildingArea.aPosition.calculateX(x);
int firstPosY = buildingArea.aPosition.calculateY(y);
if (!map.isInBounds(firstPosX, firstPosY)) {
continue;
}
partitionId = map.getPartitionIdAt(firstPosX, firstPosY);
if (!map.canPlayerConstructOnPartition(playerId, partitionId)) {
continue DX_LOOP;
}
}
// go over all positions of the building and check if they are free
for (int buildingDx = buildingAreaWidth - 1; buildingDx >= 0; buildingDx--) {
for (int buildingDy = buildingAreaHeight - 1; buildingDy >= 0; buildingDy--) {
int index = buildingDx + buildingDy * buildingAreaWidth;
// relative position regarding the building
int buildingPositionX = buildingDx + xOffsetForBuilding;
int buildingPositionY = buildingDy + yOffsetForBuilding;
// if the position must be free, but isn't
if (xJumps[index] != 0
&& !map.canUsePositionForConstruction(x + buildingPositionX, y + buildingPositionY,
buildingType.getRequiredGroundTypeAt(buildingPositionX, buildingPositionY), partitionId)) {
map.setConstructMarking(x, y, false, binaryConstructionMarkValues, null);
// prune the positions we already know that they are invalid.
for (int pruneX = 0; pruneX < xJumps[index]; pruneX++) {
int currYJumps = yJumps[(buildingDx - pruneX) + buildingDy * buildingAreaWidth];
for (int pruneY = 0; pruneY < currYJumps; pruneY++) {
if (pruneY == 0 && pruneX == 0) {
continue; // skip the original position
}
doneSet.set((dx + pruneX) + (line + pruneY) * lineLength);
map.setConstructMarking(x + pruneX, y + pruneY, false, binaryConstructionMarkValues, null);
}
}
continue DX_LOOP;
}
}
}
// no bad position found, so set the construction mark
map.setConstructMarking(x, y, true, binaryConstructionMarkValues, positionsToBeFlattened);
}
}
// set the lastArea variable for the next run
lastArea = mapArea;
}
/**
* Removes all construction marks on the screen.
*/
public void removeConstructionMarks() {
if (lastArea != null) {
lastArea.stream()
.filterBounds(map.getWidth(), map.getHeight())
.forEach((x, y) -> map.setConstructMarking(x, y, false, false, null));
lastArea = null;
}
}
/**
* Removes all construction marks in the given area.
*
* @param area
* The area to remove the marks
* @param notIn
* The area of marks that should be skipped.
*/
private void removeConstructionMarks(IMapArea area, IMapArea notIn) {
area.stream()
.filterBounds(map.getWidth(), map.getHeight())
.filter((x, y) -> !notIn.contains(x, y))
.forEach((x, y) -> map.setConstructMarking(x, y, false, false, null));
}
}