Reworked biome per island generator.

This commit is contained in:
Harrison Deng 2020-04-24 15:11:42 -05:00
parent 60ab555c57
commit 8517a20f9f

View File

@ -1,122 +1,159 @@
package ca.recrown.islandsurvivalcraft.world.generation; package ca.recrown.islandsurvivalcraft.world.generation;
import java.util.Arrays;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import ca.recrown.islandsurvivalcraft.caching.Cache;
import ca.recrown.islandsurvivalcraft.caching.CacheValue;
import ca.recrown.islandsurvivalcraft.caching.CoordinateIdentifier;
import ca.recrown.islandsurvivalcraft.caching.Identifier;
import ca.recrown.islandsurvivalcraft.pathfinding.CoordinateTargetValidatable; import ca.recrown.islandsurvivalcraft.pathfinding.CoordinateTargetValidatable;
import ca.recrown.islandsurvivalcraft.pathfinding.CoordinateValidatable;
import ca.recrown.islandsurvivalcraft.pathfinding.DepthFirstSearch; import ca.recrown.islandsurvivalcraft.pathfinding.DepthFirstSearch;
import ca.recrown.islandsurvivalcraft.world.BiomeSelector; import ca.recrown.islandsurvivalcraft.world.BiomeSelector;
import ca.recrown.islandsurvivalcraft.world.IslandWorldMapper; import ca.recrown.islandsurvivalcraft.world.IslandWorldMapper;
public class BiomePerIslandGenerator implements IslandBiomeGenerator, CoordinateTargetValidatable { //Note: technically, the validators have to be run on land, and so, some condition checks may not be nessecary.
public class BiomePerIslandGenerator implements IslandBiomeGenerator, CoordinateValidatable, CoordinateTargetValidatable {
private final Cache<ChunkBiomes> chunkBiomesCache;
private final Cache<Boolean> chunkGenStatusCache;
private final TemperatureMapGenerator temperatureMapGenerator;
private boolean initialized; private boolean initialized;
private IslandWorldMapper islandLocator; private IslandWorldMapper worldIslandMap;
private BiomeSelector biomeSelector; private BiomeSelector biomeSelector;
private TemperatureMapGenerator temperatureMapGenerator;
private World world; private World world;
private DepthFirstSearch dfs; private DepthFirstSearch mapWideDFS;
private Biome mainBiome; private DepthFirstSearch propagator;
private Biome shoreBiome; private LocalIslandPropagator propagatorInfo;
private float temperature; CoordinateIdentifier chunkCoords;
ChunkBiomes localChunkBiomes;
private int chunkX, chunkZ;
private Biome[][] chunkCache; float temperature;
public BiomePerIslandGenerator() { public BiomePerIslandGenerator() {
this.temperatureMapGenerator = new TemperatureMapGenerator(); this.temperatureMapGenerator = new TemperatureMapGenerator();
chunkCache = new Biome[16][16]; chunkBiomesCache = new Cache<>(1024);
chunkGenStatusCache = new Cache<>(1024);
propagatorInfo = new LocalIslandPropagator();
} }
@Override @Override
public void initialize(World world, IslandWorldMapper mapGenerator, BiomeSelector biomeSelector) { public void initialize(final World world, final IslandWorldMapper mapGenerator, final BiomeSelector biomeSelector) {
if (initialized) throw new IllegalStateException("Biome generator already initialized."); if (initialized) throw new IllegalStateException("Biome generator already initialized.");
initialized = true; initialized = true;
this.islandLocator = mapGenerator; this.worldIslandMap = mapGenerator;
this.biomeSelector = biomeSelector; this.biomeSelector = biomeSelector;
this.world = world; this.world = world;
this.temperatureMapGenerator.setSeed(world.getSeed()); this.temperatureMapGenerator.setSeed(world.getSeed());
dfs = new DepthFirstSearch(2048, islandLocator); mapWideDFS = new DepthFirstSearch(worldIslandMap);
} mapWideDFS.setEndPosition(0, 0);
propagator = new DepthFirstSearch(256, this);
@Override
public boolean isCoordinateTarget(int x, int y) {
//Should be ran for a island until finding biome info or until it looked for it's entirety and couldn't find it. This is one island.
//First check if the chunk we're looking at is generated.
if (world.isChunkGenerated(x/16, y/16)) {
//If so, we know that the biomes have already been assigned, and can therefore use it.
Biome foundBiome = world.getBiome(x, 0, y);
if (islandLocator.isShore(x, y)) {
this.shoreBiome = foundBiome;
} else {
this.mainBiome = foundBiome;
}
if (mainBiome != null && shoreBiome != null) {
return true;
}
} else
//If the same chunk, then do the same thing, except use the chunk biome cache instead to look for it.
if (x/16 == chunkX && y/16 == chunkZ) {
Biome foundBiome = chunkCache[Math.abs(x % 16)][Math.abs(y % 16)];
if (foundBiome == null) return false;
if (islandLocator.isShore(x, y)) {
this.shoreBiome = foundBiome;
} else {
this.mainBiome = foundBiome;
}
if (mainBiome != null && shoreBiome != null) {
return true;
}
}
return false;
}
@Override
public Biome GenerateBiome(int chunkX, int chunkZ, int localX, int localZ) {
int worldX = chunkX * 16 + localX;
int worldZ = chunkZ * 16 + localZ;
//Clear chunk cache if different chunk
if (chunkX != this.chunkX || chunkZ != this.chunkZ) {
this.chunkX = chunkX;
this.chunkZ = chunkZ;
for (int i = 0; i < chunkCache.length; i++) {
Arrays.fill(chunkCache[i], null);
}
}
if (islandLocator.isIsland(worldX, worldZ)) {
if (mainBiome == null && shoreBiome == null) {
dfs.setStartPosition(worldX, worldZ);
if (!dfs.findTarget(this)) {
temperature = temperatureMapGenerator.getTemperature(worldX, worldZ);
mainBiome = biomeSelector.getLandBiome(temperature);
shoreBiome = biomeSelector.getShoreBiome(mainBiome, temperature);
}
}
} else {
mainBiome = null;
shoreBiome = null;
}
Biome designatedBiome = mainBiome;
if (mainBiome == null) {
designatedBiome = biomeSelector.getOceanBiome(temperatureMapGenerator.getTemperature(worldX, worldZ));
} else if (islandLocator.isShore(worldX, worldZ)) {
designatedBiome = shoreBiome;
}
chunkCache[localX][localZ] = designatedBiome;
return designatedBiome;
} }
@Override @Override
public IslandBiomeGenerator getInstance() { public IslandBiomeGenerator getInstance() {
return new BiomePerIslandGenerator(); return new BiomePerIslandGenerator();
} }
@Override
public Biome GenerateBiome(final int chunkX, final int chunkZ, final int localX, final int localZ) {
if (chunkCoords == null || chunkCoords.getX() != chunkX || chunkCoords.getY() != chunkZ) {
chunkCoords = new CoordinateIdentifier(chunkX, chunkZ);
chunkGenStatusCache.retrieveCache(chunkCoords).setValue(true);
localChunkBiomes = new ChunkBiomes();
chunkBiomesCache.retrieveCache(chunkCoords).setValue(localChunkBiomes);
}
int worldX = 16 * chunkX + localX;
int worldZ = 16 * chunkZ + localZ;
this.temperature = temperatureMapGenerator.getTemperature(worldX, worldZ);
if (worldIslandMap.isIsland(worldX, worldZ) && localChunkBiomes.biomes[localX][localZ] == null) {
mapWideDFS.setStartPosition(worldX, worldZ);
if (!mapWideDFS.findTarget(this)) {
if (propagatorInfo.mainBiome == null) propagatorInfo.mainBiome = biomeSelector.getLandBiome(temperature);
if (propagatorInfo.shoreBiome == null) propagatorInfo.shoreBiome = biomeSelector.getShoreBiome(propagatorInfo.mainBiome, temperature);
if (propagatorInfo.shallowBiome == null) propagatorInfo.shallowBiome = biomeSelector.getOceanBiome(temperature);
}
propagator.setStartPosition(worldX, worldZ);
propagator.findTarget(propagatorInfo);
}
if (localChunkBiomes.biomes[localX][localZ] == null) {
localChunkBiomes.biomes[localX][localZ] = biomeSelector.getOceanBiome(temperature);
return biomeSelector.getOceanBiome(temperature);
}
return localChunkBiomes.biomes[localX][localZ];
}
@Override
public boolean validate(int x, int y) {
return (x / 16) == chunkCoords.getX() && (y / 16) == chunkCoords.getY() && worldIslandMap.isIsland(x, y);
}
@Override
public boolean isCoordinateTarget(int x, int y) {
if ((propagatorInfo.shoreBiome == null || propagatorInfo.mainBiome == null) && worldIslandMap.isLand(x, y)) {
if (propagatorInfo.shoreBiome == null && worldIslandMap.isShore(x, y)) {
propagatorInfo.shoreBiome = getBiomeAt(x, y);
} else if (propagatorInfo.mainBiome != null) {
propagatorInfo.mainBiome = getBiomeAt(x, y);
}
} else if (propagatorInfo.shallowBiome == null) {
propagatorInfo.shallowBiome = getBiomeAt(x, y);
}
return (propagatorInfo.shallowBiome != null && propagatorInfo.mainBiome != null && propagatorInfo.shoreBiome != null);
}
private Biome getBiomeAt(int worldX, int worldZ) {
int chunkX = worldX / 16;
int chunkZ = worldZ / 16;
int localX = Math.abs(worldX % 16);
int localZ = Math.abs(worldZ % 16);
Identifier chunkBiomeID = new CoordinateIdentifier(chunkX, chunkZ);
Identifier chunkGenID = new CoordinateIdentifier(chunkX, chunkZ);
CacheValue<ChunkBiomes> chunkBiomeVal = chunkBiomesCache.retrieveCache(chunkBiomeID);
CacheValue<Boolean> chunkGenVal = chunkGenStatusCache.retrieveCache(chunkGenID);
if (chunkGenVal.isEmpty()) chunkGenVal.setValue(world.isChunkGenerated(worldX / 16, worldZ / 16));
if (chunkGenVal.getValue()) {
if (chunkBiomeVal.isEmpty()) {
chunkBiomeVal.setValue(new ChunkBiomes());
}
if (chunkBiomeVal.getValue().biomes[localX][localZ] == null) {
chunkBiomeVal.getValue().biomes[localX][localZ] = world.getBiome(worldX, 0, worldZ);
}
return chunkBiomeVal.getValue().biomes[localX][localZ] = world.getBiome(worldX, 0, worldZ);
}
return null;
}
private class ChunkBiomes {
public final Biome[][] biomes = new Biome[16][16];
}
private class LocalIslandPropagator implements CoordinateTargetValidatable {
public Biome mainBiome = null;
public Biome shoreBiome = null;
public Biome shallowBiome = null;
@Override
public boolean isCoordinateTarget(int x, int y) {
int localX = Math.abs(x % 16);
int localZ = Math.abs(y % 16);
if (worldIslandMap.isLand(x, y)) {
if (worldIslandMap.isShore(x, y)) {
localChunkBiomes.biomes[localX][localZ] = shoreBiome;
} else {
localChunkBiomes.biomes[localX][localZ] = mainBiome;
}
} else if (worldIslandMap.isShallowPortion(x, y)) {
localChunkBiomes.biomes[localX][localZ] = shallowBiome;
}
return false;
}
}
} }