package rtg.world.biome; import java.util.ArrayList; import java.util.List; import java.util.Random; import javax.annotation.Nonnull; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraft.world.WorldType; import net.minecraft.world.biome.Biome; import net.minecraft.world.biome.BiomeProvider; import net.minecraft.world.gen.ChunkProviderSettings; import net.minecraft.world.gen.layer.GenLayer; import net.minecraft.world.gen.layer.IntCache; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.terraingen.WorldTypeEvent; import rtg.api.RTGAPI; import rtg.api.util.noise.*; import rtg.api.world.RTGWorld; import rtg.world.biome.realistic.RealisticBiomeBase; import rtg.world.biome.realistic.RealisticBiomePatcher; @SuppressWarnings({"WeakerAccess", "unused"}) public class BiomeProviderRTG extends BiomeProvider implements IBiomeProviderRTG { private static int[] incidences = new int[100]; private static int references = 0; /** A GenLayer containing the indices into Biome.biomeList[] */ private GenLayer genBiomes; private GenLayer biomeIndexLayer; private List<Biome> biomesToSpawnIn; private RTGWorld rtgWorld; private OpenSimplexNoise simplex; private CellNoise cell; //private SimplexCellularNoise simplexCell; private VoronoiCellNoise river; private float[] borderNoise; private RealisticBiomePatcher biomePatcher; private double riverValleyLevel = 60.0 / 450.0;//60.0/450.0; private float riverSeparation = 1875; private float largeBendSize = 140; private float smallBendSize = 30; public BiomeProviderRTG(World world, WorldType worldType) { super(world.getWorldInfo()); this.rtgWorld = new RTGWorld(world); this.biomesToSpawnIn = new ArrayList<>(); this.borderNoise = new float[256]; this.biomePatcher = new RealisticBiomePatcher(); this.riverSeparation /= RTGAPI.config().RIVER_FREQUENCY_MULTIPLIER.get(); this.riverValleyLevel *= RTGAPI.config().riverSizeMultiplier(); this.largeBendSize *= RTGAPI.config().RIVER_BENDINESS_MULTIPLIER.get(); this.smallBendSize *= RTGAPI.config().RIVER_BENDINESS_MULTIPLIER.get(); long seed = world.getSeed(); if (world.provider.getDimension() != 0) throw new RuntimeException(); simplex = new OpenSimplexNoise(seed); cell = new SimplexCellularNoise(seed); //simplexCell = new SimplexCellularNoise(seed); river = new VoronoiCellNoise(seed); GenLayer[] agenlayer = GenLayer.initializeAllBiomeGenerators( seed, worldType, ChunkProviderSettings.Factory.jsonToFactory("").build() ); agenlayer = getModdedBiomeGenerators(worldType, seed, agenlayer); this.genBiomes = agenlayer[0]; //maybe this will be needed this.biomeIndexLayer = agenlayer[1]; testCellBorder(); } private static void testCellBorder() { double[] result = new double[2]; result[0] = 0.5; result[1] = 1; if (cellBorder(result, 0.5, 1) < 0) throw new RuntimeException(); } private static double cellBorder(double[] results, double width, double depth) { double c = (results[1] - results[0]) / results[1]; if (c < 0) throw new RuntimeException(); /* int slot = (int)Math.floor(c*100.0); incidences[slot] += 1; references ++; if (references>40000) { String result = ""; for (int i = 0; i< 100; i ++) { result += " " + incidences[i]; } throw new RuntimeException(result); } */ if (c < width) return ((c / width) - 1f) * depth; else return 0; } @Override public int[] getBiomesGens(int x, int z, int par3, int par4) { int[] d = new int[par3 * par4]; for (int i = 0; i < par3; i++) { for (int j = 0; j < par4; j++) { d[i * par3 + j] = Biome.getIdForBiome(getBiomeGenAt(x + i, z + j)); } } return d; } @Override public float getRiverStrength(int x, int z) { //New river curve function. No longer creates worldwide curve correlations along cardinal axes. SimplexOctave.Disk jitter = new SimplexOctave.Disk(); simplex.riverJitter().evaluateNoise((float) x / 240.0, (float) z / 240.0, jitter); double pX = x + jitter.deltax() * largeBendSize; double pZ = z + jitter.deltay() * largeBendSize; simplex.octave(2).evaluateNoise((float) x / 80.0, (float) z / 80.0, jitter); pX += jitter.deltax() * smallBendSize; pZ += jitter.deltay() * smallBendSize; double xRiver = pX / riverSeparation; double zRiver = pZ / riverSeparation; //New cellular noise. //TODO move the initialization of the results in a way that's more efficient but still thread safe. //double[] results = cell.river().eval(xRiver,zRiver ); //return (float) cellBorder(results, riverValleyLevel, 1.0); return river.octave(0).border2(xRiver, zRiver, riverValleyLevel, 1f); } /** * @see IBiomeProviderRTG */ @Override public Biome getBiomeGenAt(int x, int z) { return this.getBiome(new BlockPos(x, 0, z)); } /** * @see IBiomeProviderRTG */ @Override public RealisticBiomeBase getBiomeDataAt(int par1, int par2) { /*long coords = ChunkCoordIntPair.chunkXZ2Int(par1, par2); if (biomeDataMap.containsKey(coords)) { return biomeDataMap.get(coords); }*/ RealisticBiomeBase output; output = RealisticBiomeBase.getBiome(Biome.getIdForBiome(this.getBiomeGenAt(par1, par2))); if (output == null) output = biomePatcher.getPatchedRealisticBiome("No biome " + par1 + " " + par2); /*if (biomeDataMap.size() > 4096) { biomeDataMap.clear(); } biomeDataMap.put(coords, output);*/ return output; } /** * @see IBiomeProviderRTG */ @Override public boolean isBorderlessAt(int x, int z) { int bx, bz; for (bx = -2; bx <= 2; bx++) { for (bz = -2; bz <= 2; bz++) { borderNoise[Biome.getIdForBiome(getBiomeDataAt(x + bx * 16, z + bz * 16).baseBiome)] += 0.04f; } } bz = 0; for (bx = 0; bx < 256; bx++) { if (borderNoise[bx] > 0.98f) bz = 1; borderNoise[bx] = 0; } return bz == 1; } public boolean diff(float sample1, float sample2, float base) { return (sample1 < base && sample2 > base) || (sample1 > base && sample2 < base); } public float[] getRainfall(float[] par1ArrayOfFloat, int par2, int par3, int par4, int par5) { IntCache.resetIntCache(); if (par1ArrayOfFloat == null || par1ArrayOfFloat.length < par4 * par5) { par1ArrayOfFloat = new float[par4 * par5]; } int[] aint = this.biomeIndexLayer.getInts(par2, par3, par4, par5); for (int i1 = 0; i1 < par4 * par5; ++i1) { float f = 0; int biome = aint[i1]; try { if (biome > 255) throw new RuntimeException(biomeIndexLayer.toString()); f = RealisticBiomeBase.getBiome(biome).baseBiome.getRainfall() / 65536.0F; } catch (Exception e) { if (biome > 255) throw new RuntimeException(biomeIndexLayer.toString()); if (RealisticBiomeBase.getBiome(biome) == null) { f = biomePatcher.getPatchedRealisticBiome("Problem with biome " + biome + " from " + e.getMessage()).baseBiome.getRainfall() / 65536.0F; } } if (f > 1.0F) f = 1.0F; par1ArrayOfFloat[i1] = f; } return par1ArrayOfFloat; } @Override @Nonnull public List<Biome> getBiomesToSpawnIn() { return this.biomesToSpawnIn; } @Override public float getTemperatureAtHeight(float p_76939_1_, int p_76939_2_) { return p_76939_1_; } @Override @Nonnull public Biome[] getBiomesForGeneration(@Nonnull Biome[] biomes, int x, int z, int width, int height) { IntCache.resetIntCache(); if (biomes.length < width * height) { biomes = new Biome[width * height]; } int[] aint = this.genBiomes.getInts(x, z, width, height); for (int i1 = 0; i1 < width * height; ++i1) { biomes[i1] = Biome.getBiomeForId(aint[i1]); if (biomes[i1] == null) { biomes[i1] = biomePatcher.getPatchedBaseBiome( "BPRTG.getBiomesForGeneration() could not find biome " + aint[i1]); } } return biomes; } @Override public boolean areBiomesViable(int x, int z, int radius, @Nonnull List<Biome> allowed) { float centerNoise = getNoiseAt(x, z); if (centerNoise < 62) return false; float lowestNoise = centerNoise; float highestNoise = centerNoise; for (int i = -2; i <= 2; i++) { for (int j = -2; j <= 2; j++) { if (i != 0 && j != 0) { float n = getNoiseAt(x + i * 16, z + j * 16); if (n < lowestNoise) lowestNoise = n; if (n > highestNoise) highestNoise = n; } } } return highestNoise - lowestNoise < 22; } public float getNoiseAt(int x, int y) { float river = getRiverStrength(x, y) + 1f; if (river < 0.5f) return 59f; return getBiomeDataAt(x, y).rNoise(this.rtgWorld, x, y, 1f, river); } @Override public BlockPos findBiomePosition(int x, int z, int range, @Nonnull List<Biome> biomes, @Nonnull Random random) { IntCache.resetIntCache(); int i = x - range >> 2; int j = z - range >> 2; int k = x + range >> 2; int l = z + range >> 2; int i1 = k - i + 1; int j1 = l - j + 1; int[] aint = this.genBiomes.getInts(i, j, i1, j1); BlockPos blockpos = null; int k1 = 0; for (int l1 = 0; l1 < i1 * j1; ++l1) { int i2 = i + l1 % i1 << 2; int j2 = j + l1 / i1 << 2; Biome biome = Biome.getBiome(aint[l1]); if (biomes.contains(biome) && (blockpos == null || random.nextInt(k1 + 1) == 0)) { blockpos = new BlockPos(i2, 0, j2); ++k1; } } return blockpos; } @Override @Nonnull public GenLayer[] getModdedBiomeGenerators(@Nonnull WorldType worldType, long seed, @Nonnull GenLayer[] original) { WorldTypeEvent.InitBiomeGens event = new WorldTypeEvent.InitBiomeGens(worldType, seed, original); MinecraftForge.TERRAIN_GEN_BUS.post(event); return event.getNewBiomeGens(); } }