Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Connect Settlements with Roads #44

Merged
merged 9 commits into from Jul 5, 2019
Expand Up @@ -44,10 +44,13 @@
import org.terasology.dynamicCities.decoration.DecorationRasterizer;
import org.terasology.dynamicCities.decoration.SingleBlockRasterizer;
import org.terasology.dynamicCities.parcels.DynParcel;
import org.terasology.dynamicCities.parcels.RoadParcel;
import org.terasology.dynamicCities.parcels.RoadStatus;
import org.terasology.dynamicCities.playerTracking.PlayerTracker;
import org.terasology.dynamicCities.population.CultureComponent;
import org.terasology.dynamicCities.rasterizer.AbsDynBuildingRasterizer;
import org.terasology.dynamicCities.rasterizer.BufferRasterTarget;
import org.terasology.dynamicCities.rasterizer.RoadRasterizer;
import org.terasology.dynamicCities.rasterizer.doors.DoorRasterizer;
import org.terasology.dynamicCities.rasterizer.doors.SimpleDoorRasterizer;
import org.terasology.dynamicCities.rasterizer.doors.WingDoorRasterizer;
Expand All @@ -65,6 +68,7 @@
import org.terasology.dynamicCities.rasterizer.window.RectWindowRasterizer;
import org.terasology.dynamicCities.rasterizer.window.SimpleWindowRasterizer;
import org.terasology.dynamicCities.rasterizer.window.WindowRasterizer;
import org.terasology.dynamicCities.roads.RoadSegment;
import org.terasology.economy.components.MultiInvStorageComponent;
import org.terasology.entitySystem.entity.EntityManager;
import org.terasology.entitySystem.entity.EntityRef;
Expand All @@ -79,6 +83,7 @@
import org.terasology.math.Side;
import org.terasology.math.geom.BaseVector2i;
import org.terasology.math.geom.BaseVector3i;
import org.terasology.math.geom.ImmutableVector2i;
import org.terasology.math.geom.Rect2i;
import org.terasology.math.geom.Vector2i;
import org.terasology.math.geom.Vector3f;
Expand All @@ -89,8 +94,8 @@
import org.terasology.registry.Share;
import org.terasology.structureTemplates.components.SpawnBlockRegionsComponent;
import org.terasology.structureTemplates.interfaces.StructureTemplateProvider;
import org.terasology.structureTemplates.util.BlockRegionUtilities;
import org.terasology.structureTemplates.util.BlockRegionTransform;
import org.terasology.structureTemplates.util.BlockRegionUtilities;
import org.terasology.world.BlockEntityRegistry;
import org.terasology.world.WorldProvider;
import org.terasology.world.block.Block;
Expand Down Expand Up @@ -146,13 +151,15 @@ public class Construction extends BaseComponentSystem {
private InventoryManager inventoryManager;


private BlockTheme theme;
private BlockTheme cityTheme;
private BlockTheme roadTheme;

private Block air;
private Block plant;
private Block water;
private Block defaultBlock;
private int maxMinDeviation = 40;
private RoadRasterizer roadRasterizer;
private final List<AbsDynBuildingRasterizer> stdRasterizers = new ArrayList<>();
private final List<WindowRasterizer> windowRasterizers = new ArrayList<>();
private final List<DoorRasterizer> doorRasterizers = new ArrayList<>();
Expand All @@ -161,8 +168,13 @@ public class Construction extends BaseComponentSystem {
private final List<Block> plantBlocks = new ArrayList<>();
private Logger logger = LoggerFactory.getLogger(Construction.class);

private Map<Integer, Integer> segmentCache = new HashMap<>();

/**
* Initialises the system and rasterizers with default themes
*/
public void initialise() {
mayant15 marked this conversation as resolved.
Show resolved Hide resolved
theme = BlockTheme.builder(blockManager)
cityTheme = BlockTheme.builder(blockManager)
.register(DefaultBlockType.ROAD_FILL, "core:dirt")
.register(DefaultBlockType.ROAD_SURFACE, "core:Gravel")
.register(DefaultBlockType.LOT_EMPTY, "core:dirt")
Expand Down Expand Up @@ -193,33 +205,40 @@ public void initialise() {

.build();

if (roadRasterizer == null) {
roadRasterizer = new RoadRasterizer();
roadTheme = BlockTheme.builder(blockManager)
.register(DefaultBlockType.ROAD_FILL, "core:dirt")
.register(DefaultBlockType.ROAD_SURFACE, "core:Gravel")
.build();
}

blockManager = CoreRegistry.get(BlockManager.class);
air = blockManager.getBlock("engine:air");
water = blockManager.getBlock("core:water");
plant = blockManager.getBlock("core:plant");
defaultBlock = blockManager.getBlock("core:dirt");

stdRasterizers.add(new HollowBuildingPartRasterizer(theme, worldProvider));
stdRasterizers.add(new RectPartRasterizer(theme, worldProvider));
stdRasterizers.add(new RoundPartRasterizer(theme, worldProvider));
stdRasterizers.add(new StaircaseRasterizer(theme, worldProvider));

stdRasterizers.add(new HollowBuildingPartRasterizer(cityTheme, worldProvider));
stdRasterizers.add(new RectPartRasterizer(cityTheme, worldProvider));
stdRasterizers.add(new RoundPartRasterizer(cityTheme, worldProvider));
stdRasterizers.add(new StaircaseRasterizer(cityTheme, worldProvider));

decorationRasterizers.add(new SingleBlockRasterizer(theme));
decorationRasterizers.add(new ColumnRasterizer(theme));
decorationRasterizers.add(new SingleBlockRasterizer(cityTheme));
decorationRasterizers.add(new ColumnRasterizer(cityTheme));

doorRasterizers.add(new SimpleDoorRasterizer(theme));
doorRasterizers.add(new WingDoorRasterizer(theme));
doorRasterizers.add(new SimpleDoorRasterizer(cityTheme));
doorRasterizers.add(new WingDoorRasterizer(cityTheme));

windowRasterizers.add(new RectWindowRasterizer(theme));
windowRasterizers.add(new SimpleWindowRasterizer(theme));
windowRasterizers.add(new RectWindowRasterizer(cityTheme));
windowRasterizers.add(new SimpleWindowRasterizer(cityTheme));

roofRasterizers.add(new ConicRoofRasterizer(theme));
roofRasterizers.add(new DomeRoofRasterizer(theme));
roofRasterizers.add(new FlatRoofRasterizer(theme));
roofRasterizers.add(new HipRoofRasterizer(theme));
roofRasterizers.add(new PentRoofRasterizer(theme));
roofRasterizers.add(new SaddleRoofRasterizer(theme));
roofRasterizers.add(new ConicRoofRasterizer(cityTheme));
roofRasterizers.add(new DomeRoofRasterizer(cityTheme));
roofRasterizers.add(new FlatRoofRasterizer(cityTheme));
roofRasterizers.add(new HipRoofRasterizer(cityTheme));
roofRasterizers.add(new PentRoofRasterizer(cityTheme));
roofRasterizers.add(new SaddleRoofRasterizer(cityTheme));

//Register plant blocks
plantBlocks.add(blockManager.getBlock("core:GreenLeaf"));
Expand All @@ -231,11 +250,22 @@ public void initialise() {
plantBlocks.add(blockManager.getBlock("core:Cactus"));
}

/**
* Setup external rasterizer to be used for the road and it's block theme
* @param rasterizer to be used for the road
* @param theme theme that the rasterizer will use
*/
public void setRoadRasterizer(RoadRasterizer rasterizer, BlockTheme theme) {
roadRasterizer = rasterizer;
roadTheme = theme;
}

/**
* Maybe return a structured data with (int or false) as return value
* @param area The area which should be flattened
*
* @param area The area which should be flattened
* @param defaultHeight A rough estimation of the mean height of the terrain
* @param filler The blocktype which should be used to fill up terrain under the mean height
* @param filler The blocktype which should be used to fill up terrain under the mean height
* @return The height on which it was flattened to
*/
public int flatten(Rect2i area, int defaultHeight, Block filler) {
Expand Down Expand Up @@ -281,9 +311,9 @@ public int flatten(Rect2i area, int defaultHeight, Block filler) {
public int flatten(Rect2i area, int defaultHeight) {
return flatten(area, defaultHeight, defaultBlock);
}

/**
*
* @param area The area which should be sampled
* @param area The area which should be sampled
* @param height A rough estimation of the mean height of the terrain
* @return
*/
Expand Down Expand Up @@ -339,7 +369,7 @@ public boolean buildParcel(DynParcel dynParcel, EntityRef settlement, CultureCom
//Flatten the parcel area
dynParcel.height = flatten(dynParcel.shape, dynParcel.height);

RasterTarget rasterTarget = new BufferRasterTarget(blockBufferSystem, theme, dynParcel.shape);
RasterTarget rasterTarget = new BufferRasterTarget(blockBufferSystem, cityTheme, dynParcel.shape);
Rect2i shape = dynParcel.shape;
HeightMap hm = HeightMaps.constant(dynParcel.height);

Expand Down Expand Up @@ -386,13 +416,17 @@ public boolean buildParcel(DynParcel dynParcel, EntityRef settlement, CultureCom
boolean needsRotation = buildingManager.needsRotation(dynParcel, building);
if (needsRotation) {
switch (dynParcel.getOrientation()) {
case NORTH: dynParcel.orientation = Orientation.EAST;
case NORTH:
dynParcel.orientation = Orientation.EAST;
break;
case SOUTH: dynParcel.orientation = Orientation.WEST;
case SOUTH:
dynParcel.orientation = Orientation.WEST;
break;
case WEST: dynParcel.orientation = Orientation.NORTH;
case WEST:
dynParcel.orientation = Orientation.NORTH;
break;
case EAST: dynParcel.orientation = Orientation.SOUTH;
case EAST:
dynParcel.orientation = Orientation.SOUTH;
break;
}
}
Expand Down Expand Up @@ -473,8 +507,6 @@ public boolean buildParcel(DynParcel dynParcel, EntityRef settlement, CultureCom
}




/**
* Send block-change event to refresh the minimap
*/
Expand All @@ -487,6 +519,84 @@ public boolean buildParcel(DynParcel dynParcel, EntityRef settlement, CultureCom
return true;
}

public RoadStatus buildRoadParcel(RoadParcel parcel, EntityRef settlement) {
boolean containsRelevantRegion = false;
boolean segmentFailed = false;

final int vertLimit = 255; // To check if region is relevant

final int segmentHeight = 10; // Height to be given to the flatten function
final int failHeight = -9999;

// Factor by which the rect will be expanded while flattening
final int expWidth = 1;
final int expHeight = 1;
final ImmutableVector2i rectExpansionFactor = new ImmutableVector2i(expWidth, expHeight);

for (int i = 0; i < parcel.rects.size(); i++) {
RoadSegment segment = parcel.rects.elementAt(i);

if (segmentCache.containsKey(segment.hashCode()) && segmentCache.get(segment.hashCode()) == parcel.hashCode()) {
continue;
}

// Check if the region is relevant
Region3i region = Region3i.createFromMinMax(
new Vector3i(segment.rect.minX(), vertLimit, segment.rect.minY()),
new Vector3i(segment.rect.maxX(), -1 * vertLimit, segment.rect.maxY())
);
if (!worldProvider.isRegionRelevant(region)) {
continue;
} else {
containsRelevantRegion = true;
}

// Flatten the rect
// TODO: Find a way to store the surface height at that point to the segment here.
segment.height = flatten(segment.rect.expand(rectExpansionFactor), segmentHeight);

// Create raster targets
RasterTarget rasterTarget = new BufferRasterTarget(blockBufferSystem, roadTheme, segment.rect);
HeightMap hm = HeightMaps.constant(segment.height);

if (segment.height == failHeight) {
segmentFailed = true;
continue;
}

// Check for player collision
Map<EntityRef, EntityRef> playerCityMap = playerTracker.getPlayerCityMap();

boolean shouldRaster = true;
for (EntityRef player : playerCityMap.keySet()) {
if (playerCityMap.get(player) == settlement) {
LocationComponent playerLocation = player.getComponent(LocationComponent.class);
if (playerLocation != null && segment.rect.contains(playerLocation.getLocalPosition().x(), playerLocation.getLocalPosition().z())) {
segmentFailed = true;
shouldRaster = false;
break;
}
}
}

// Rasterize the road
if (shouldRaster) {
roadRasterizer.raster(rasterTarget, segment, hm);
segmentCache.put(segment.hashCode(), parcel.hashCode());
}
}

if (!containsRelevantRegion) {
return RoadStatus.NONE;
}

if (segmentFailed) {
return RoadStatus.PARTIAL;
}

return RoadStatus.COMPLETE;
}

/**
* Catches the spawn of a structure template on a parcel and assigns potential chest entities to the parcels entity
*/
Expand Down Expand Up @@ -524,7 +634,7 @@ public void catchOnSpawnDynamicStructure(OnSpawnDynamicStructureEvent event, Ent
public void onSpawnBlockRegions(SpawnStructureBufferedEvent event, EntityRef entity,
SpawnBlockRegionsComponent spawnBlockRegionComponent) {
BlockRegionTransform transformation = event.getTransformation();
for (SpawnBlockRegionsComponent.RegionToFill regionToFill: spawnBlockRegionComponent.regionsToFill) {
for (SpawnBlockRegionsComponent.RegionToFill regionToFill : spawnBlockRegionComponent.regionsToFill) {
Block block = regionToFill.blockType;

Region3i region = regionToFill.region;
Expand Down
26 changes: 16 additions & 10 deletions src/main/java/org/terasology/dynamicCities/parcels/ParcelList.java
Expand Up @@ -16,6 +16,7 @@
package org.terasology.dynamicCities.parcels;


import org.terasology.cities.parcels.Parcel;
import org.terasology.entitySystem.Component;
import org.terasology.math.geom.Rect2i;
import org.terasology.network.Replicate;
Expand All @@ -36,7 +37,7 @@ public class ParcelList implements Component {
@Replicate
public float builtUpRadius;

public List<DynParcel> parcels;
public List<Parcel> parcels;

public ParcelList() { }
public ParcelList(int i) {
Expand All @@ -46,13 +47,16 @@ public ParcelList(int i) {
areaPerZone = new HashMap<>();
}

public void addParcel(DynParcel parcel) {
public void addParcel(Parcel parcel) {
parcels.add(parcel);
String zone = parcel.getZone();
if (areaPerZone.containsKey(zone)) {
areaPerZone.put(zone, areaPerZone.get(zone) + parcel.getShape().area());
} else {
areaPerZone.put(zone, parcel.getShape().area());
if (parcel instanceof DynParcel) {
DynParcel dynParcel = ((DynParcel) parcel);
String zone = dynParcel.getZone();
if (areaPerZone.containsKey(zone)) {
areaPerZone.put(zone, areaPerZone.get(zone) + dynParcel.getShape().area());
} else {
areaPerZone.put(zone, dynParcel.getShape().area());
}
}
}

Expand All @@ -61,9 +65,11 @@ public boolean isNotIntersecting(DynParcel parcel) {
}

public boolean isNotIntersecting(Rect2i rect) {
for (DynParcel spawnedParcels : parcels) {
if (spawnedParcels.getShape().overlaps(rect)) {
return false;
for (Parcel spawnedParcels : parcels) {
if (spawnedParcels instanceof RoadParcel) {
return ((RoadParcel) spawnedParcels).isNotIntersecting(rect);
} else if (spawnedParcels.getShape().overlaps(rect)) {
return false;
}
}
return true;
Expand Down