- better handling of wetland terrain

- fix missing logs from tree
- tweaks to biomemap
This commit is contained in:
dags- 2020-03-06 17:22:23 +00:00
parent 5e43e4fc34
commit d3ce349d61
27 changed files with 377 additions and 188 deletions

View File

@ -28,19 +28,32 @@ TerraForged is an ambitious new terrain generator mod for Minecraft (Java Editio
![TerraForged Gallery](https://terraforged.com/curse/gallery.jpg)
#### FAQ:
1. "Is this compatible with mod xyz?"
1) "Is this compatible with mod xyz?"
_Probably! (to some degree) - TerraForged is designed to work with many of the same world-gen systems
that the majority of block & biome providing mods use. Certain biomes' terrain may not always look
exactly as their author designed but should otherwise be compatible. Feel free to report any other
compatibility issues on the issue tracker._
2. "How can I use this on my server?"
2) "How can I use this on my server?"
_When Forge supports it, you can simply set level-type=terraforged in your server.properties file. In
the meantime, you will need to create the world in single player and then copy that to your server
directory. (In both cases, TerraForged must be installed on the client and the server)._
3. "Will I need a super-computer to run this?!"
3) "Will I need a super-computer to run this?!"
_No, not really - while this world generator will be a bit slower than vanilla's (on account of it
doing more work to make things look nice), it would only be apparent when first generating a chunk -
they might load in slower when moving to new parts of the world, but game performance should
doing more work to make things look nice), it would only be apparent when first generating a chunk
- they might load in slower when moving to new parts of the world, but game performance should
otherwise be normal. A 4-core CPU should be able to handle this just fine._
4) "Can this be ported Fabric/Bukkit/Spigot/Sponge?"
_If someone would like to take this task on, yes - a large part of the TerraForged codebase is already
platform independent. There are certain client-side features in the forge-mod that would not translate
onto server-only APIs, but the core experience could certainly be ported. I don't intend to work on
this directly but others are very welcome._
5) "Will this be back-ported to older Forge versions?"
_Not by myself, no - My aim is to keep current with Forge. I'm simply not prolific enough a modder to
write and maintain for multiple versions (hats off to those who do!). Again though, others are welcome
to back-port it, if inclined to do so._
[View questions on github](https://github.com/TerraForged/TerraForged/issues?q=label:question)

View File

@ -34,52 +34,73 @@ import me.dags.noise.Module;
import me.dags.noise.Source;
import me.dags.noise.util.NoiseUtil;
public class SwampPools implements Decorator {
public class Wetlands implements Decorator {
private final Module module;
private final Levels levels;
private final Terrains terrains;
private final float minY;
private final float maxY;
private final float blendY;
private final float blendRange;
private final float poolBase;
private final float bankHeight;
public SwampPools(Seed seed, Terrains terrains, Levels levels) {
this.levels = levels;
this.terrains = terrains;
this.minY = levels.water(-3);
this.maxY = levels.water(1);
this.blendY = levels.water(4);
this.blendRange = blendY - maxY;
this.module = Source.perlin(seed.next(), 14, 1).clamp(0.45, 0.8).map(0, 1);
private final Terrain wetlands;
public Wetlands(Seed seed, Terrains terrain, Levels levels) {
this.wetlands = terrain.wetlands;
this.poolBase = levels.water(-3);
this.bankHeight = levels.water(2);
this.module = Source.perlin(seed.next(), 12, 1).clamp(0.35, 0.65).map(0, 1);
}
@Override
public boolean apply(Cell<Terrain> cell, float x, float y) {
if (cell.tag == terrains.ocean) {
if (cell.value < poolBase) {
return false;
}
if (cell.moisture < 0.7 || cell.temperature < 0.3) {
float tempAlpha = getAlpha(cell.temperature, 0.3F, 0.7F);
if (tempAlpha == 0) {
return false;
}
if (cell.value <= minY) {
float moistAlpha = getAlpha(cell.moisture, 0.7F, 1F);
if (moistAlpha == 0) {
return false;
}
if (cell.value > blendY) {
float riverAlpha = getAlpha(1 - cell.riverMask, 0.85F, 0.95F);
if (riverAlpha == 0) {
return false;
}
float alpha = module.getValue(x, y);
if (cell.value > maxY) {
float delta = blendY - cell.value;
float alpha2 = delta / blendRange;
alpha *= alpha2;
float alpha = tempAlpha * moistAlpha * riverAlpha;
float value1 = NoiseUtil.lerp(cell.value, bankHeight, alpha);
cell.value = Math.min(cell.value, value1);
float poolAlpha = getAlpha(alpha, 0.35F, 0.55F);
float shape = module.getValue(x, y);
float value2 = NoiseUtil.lerp(cell.value, poolBase, shape * poolAlpha);
cell.value = Math.min(cell.value, value2);
if (poolAlpha > 0.5) {
cell.tag = wetlands;
}
cell.value = NoiseUtil.lerp(cell.value, minY, alpha);
return true;
}
private static float getAlpha(float value, float min, float max) {
return getAlpha(value, min, max, false);
}
private static float getAlpha(float value, float min, float max, boolean inverse) {
if (value < min) {
return 0F;
}
if (value >= max) {
return 1F;
}
float alpha = (value - min) / (max - min);
if (inverse) {
return 1F - alpha;
}
return alpha;
}
}

View File

@ -27,7 +27,7 @@ package com.terraforged.core.world;
import com.terraforged.core.decorator.Decorator;
import com.terraforged.core.decorator.DesertStacks;
import com.terraforged.core.decorator.SwampPools;
import com.terraforged.core.decorator.Wetlands;
import java.util.ArrayList;
import java.util.Collections;
@ -41,7 +41,7 @@ public class WorldDecorators {
context = context.copy();
List<Decorator> list = new ArrayList<>();
list.add(new DesertStacks(context.seed, context.levels));
list.add(new SwampPools(context.seed, context.terrain, context.levels));
list.add(new Wetlands(context.seed, context.terrain, context.levels));
decorators = Collections.unmodifiableList(list);
}

View File

@ -61,7 +61,7 @@ public class ClimateModule {
int warpScale = settings.biome.biomeWarpScale;
this.seed = seed.next();
this.edgeClamp = 0.85F;
this.edgeClamp = 1F;
this.edgeScale = 1 / edgeClamp;
this.biomeFreq = 1F / biomeSize;
this.warpStrength = settings.biome.biomeWarpStrength;

View File

@ -134,43 +134,47 @@ public class Terrain implements Tag {
return new Terrain("river_banks", 4);
}
public static Terrain wetlands(Settings settings) {
return new Terrain("wetlands", 5);
}
public static Terrain steppe(Settings settings) {
return new Terrain("steppe", 5, settings.terrain.steppe.weight);
return new Terrain("steppe", 6, settings.terrain.steppe.weight);
}
public static Terrain plains(Settings settings) {
return new Terrain("plains", 5, settings.terrain.plains.weight);
return new Terrain("plains", 7, settings.terrain.plains.weight);
}
public static Terrain plateau(Settings settings) {
return new Terrain("plateau", 6, settings.terrain.plateau.weight);
return new Terrain("plateau", 8, settings.terrain.plateau.weight);
}
public static Terrain badlands(Settings settings) {
return new Terrain("badlands", 7, settings.terrain.badlands.weight);
return new Terrain("badlands", 9, settings.terrain.badlands.weight);
}
public static Terrain hills(Settings settings) {
return new Terrain("hills", 8, settings.terrain.hills.weight);
return new Terrain("hills", 10, settings.terrain.hills.weight);
}
public static Terrain dales(Settings settings) {
return new Terrain("dales", 9, settings.terrain.dales.weight);
return new Terrain("dales", 11, settings.terrain.dales.weight);
}
public static Terrain torridonian(Settings settings) {
return new Terrain("torridonian_fells", 10, settings.terrain.torridonian.weight);
return new Terrain("torridonian_fells", 12, settings.terrain.torridonian.weight);
}
public static Terrain mountains(Settings settings) {
return new Terrain("mountains", 11, settings.terrain.mountains.weight);
return new Terrain("mountains", 13, settings.terrain.mountains.weight);
}
public static Terrain volcano(Settings settings) {
return new Terrain("volcano", 12, settings.terrain.volcano.weight);
return new Terrain("volcano", 14, settings.terrain.volcano.weight);
}
public static Terrain volcanoPipe(Settings settings) {
return new Terrain("volcano_pipe", 13, settings.terrain.volcano.weight);
return new Terrain("volcano_pipe", 15, settings.terrain.volcano.weight);
}
}

View File

@ -40,6 +40,7 @@ public class Terrains {
public final Terrain lake;
public final Terrain river;
public final Terrain riverBanks;
public final Terrain wetlands;
public final Terrain badlands;
public final Terrain steppe;
public final Terrain plains;
@ -64,6 +65,7 @@ public class Terrains {
river = mutable.river,
torridonian = mutable.torridonian,
riverBanks = mutable.riverbanks,
wetlands = mutable.wetlands,
badlands = mutable.badlands,
plateau = mutable.plateau,
steppe = mutable.steppe,
@ -102,6 +104,7 @@ public class Terrains {
terrain.lake = Terrain.lake(settings);
terrain.river = Terrain.river(settings);
terrain.riverbanks = Terrain.riverBank(settings);
terrain.wetlands = Terrain.wetlands(settings);
terrain.badlands = Terrain.badlands(settings);
terrain.plateau = Terrain.plateau(settings);
terrain.steppe = Terrain.steppe(settings);
@ -123,6 +126,7 @@ public class Terrains {
public Terrain lake = Terrain.NONE;
public Terrain river = Terrain.NONE;
public Terrain riverbanks = Terrain.NONE;
public Terrain wetlands = Terrain.NONE;
public Terrain badlands = Terrain.NONE;
public Terrain plateau = Terrain.NONE;
public Terrain steppe = Terrain.NONE;

View File

@ -0,0 +1,89 @@
/*
*
* MIT License
*
* Copyright (c) 2020 TerraForged
*
* 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 com.terraforged.mod.biome;
import com.terraforged.api.biome.BiomeVariant;
import net.minecraft.entity.EntityClassification;
import net.minecraft.entity.EntityType;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.Biomes;
import net.minecraft.world.biome.DefaultBiomeFeatures;
import net.minecraft.world.biome.SwampBiome;
import net.minecraft.world.gen.surfacebuilders.SurfaceBuilder;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public class Marshland extends BiomeVariant {
public Marshland() {
super((new Builder()).surfaceBuilder(SurfaceBuilder.SWAMP, SurfaceBuilder.GRASS_DIRT_GRAVEL_CONFIG).precipitation(RainType.RAIN).category(Category.SWAMP).depth(0.2F).scale(0.2F).temperature(0.8F).downfall(0.4F).waterColor(6388580).waterFogColor(2302743).parent((String) null));
this.setRegistryName("terraforged", "marshland");
DefaultBiomeFeatures.addCarvers(this);
DefaultBiomeFeatures.addStructures(this);
DefaultBiomeFeatures.addMonsterRooms(this);
DefaultBiomeFeatures.addStoneVariants(this);
DefaultBiomeFeatures.addOres(this);
DefaultBiomeFeatures.addSedimentDisks(this);
DefaultBiomeFeatures.addDefaultFlowers(this);
DefaultBiomeFeatures.addTallGrass(this);
DefaultBiomeFeatures.addTallGrass(this);
DefaultBiomeFeatures.addTallGrass(this);
DefaultBiomeFeatures.addTallGrass(this);
DefaultBiomeFeatures.addGrass(this);
DefaultBiomeFeatures.addVeryDenseGrass(this);
DefaultBiomeFeatures.addReedsAndPumpkins(this);
DefaultBiomeFeatures.addFreezeTopLayer(this);
this.addSpawn(EntityClassification.CREATURE, new SpawnListEntry(EntityType.SHEEP, 12, 4, 4));
this.addSpawn(EntityClassification.CREATURE, new SpawnListEntry(EntityType.PIG, 10, 4, 4));
this.addSpawn(EntityClassification.CREATURE, new SpawnListEntry(EntityType.CHICKEN, 10, 4, 4));
this.addSpawn(EntityClassification.CREATURE, new SpawnListEntry(EntityType.COW, 8, 4, 4));
this.addSpawn(EntityClassification.CREATURE, new SpawnListEntry(EntityType.WOLF, 8, 4, 4));
this.addSpawn(EntityClassification.CREATURE, new SpawnListEntry(EntityType.RABBIT, 4, 2, 3));
this.addSpawn(EntityClassification.CREATURE, new SpawnListEntry(EntityType.FOX, 8, 2, 4));
this.addSpawn(EntityClassification.AMBIENT, new SpawnListEntry(EntityType.BAT, 10, 8, 8));
this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.SPIDER, 100, 4, 4));
this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.ZOMBIE, 95, 4, 4));
this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.ZOMBIE_VILLAGER, 5, 1, 1));
this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.SKELETON, 100, 4, 4));
this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.CREEPER, 100, 4, 4));
this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.SLIME, 100, 4, 4));
this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.ENDERMAN, 10, 1, 4));
this.addSpawn(EntityClassification.MONSTER, new SpawnListEntry(EntityType.WITCH, 5, 1, 1));
}
public int getGrassColor(double p_225528_1_, double p_225528_3_) {
double d0 = INFO_NOISE.noiseAt(p_225528_1_ * 0.0225D, p_225528_3_ * 0.0225D, false);
return d0 < -0.1D ? 5011004 : 6975545;
}
public int getFoliageColor() {
return 6975545;
}
@Override
public Biome getBase() {
return Biomes.SWAMP;
}
}

View File

@ -46,6 +46,7 @@ public class ModBiomes {
public static final Biome STEPPE = register(new Steppe());
public static final Biome TAIGA_SCRUB = register(new TaigaScrub());
public static final Biome WARM_BEACH = register(new WarmBeach());
public static final Biome MARSHLAND = register(new Marshland());
private static Biome register(BiomeVariant biome) {
biomes.add(biome);

View File

@ -38,39 +38,54 @@ import net.minecraft.world.biome.Biomes;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Function;
public abstract class AbstractBiomeMap implements BiomeMap {
private final Biome[][] beach;
private final Biome[][] river;
private final Biome[][] wetland;
private final Biome[][] ocean;
private final Biome[][] deepOcean;
protected final DefaultBiome defaultLand = this::defaultBiome;
protected final DefaultBiome defaultBeach = this::defaultBeach;
protected final DefaultBiome defaultRiver = this::defaultRiver;
protected final DefaultBiome defaultWetland = this::defaultWetland;
protected final DefaultBiome defaultOcean = this::defaultOcean;
protected final DefaultBiome defaultDeepOcean = this::defaultDeepOcean;
protected AbstractBiomeMap(BiomeMapBuilder builder) {
river = builder.rivers();
beach = builder.beaches();
ocean = builder.oceans();
wetland = builder.wetlands();
deepOcean = builder.deepOceans();
}
@Override
public Biome getBeach(float temperature, float moisture, float shape) {
return get(beach, getCategory(temperature), shape, defaultBeach(temperature));
return get(beach, getCategory(temperature), shape, temperature, defaultBeach);
}
@Override
public Biome getRiver(float temperature, float moisture, float shape) {
return get(river, getCategory(temperature), shape, defaultRiver(temperature));
return get(river, getCategory(temperature), shape, temperature, defaultRiver);
}
@Override
public Biome getWetland(float temperature, float moisture, float shape) {
return get(wetland, getCategory(temperature), shape, temperature, defaultWetland);
}
@Override
public Biome getOcean(float temperature, float moisture, float shape) {
return get(ocean, getCategory(temperature), shape, defaultOcean(temperature));
return get(ocean, getCategory(temperature), shape, temperature, defaultOcean);
}
@Override
public Biome getDeepOcean(float temperature, float moisture, float shape) {
return get(deepOcean, getCategory(temperature), shape, defaultDeepOcean(temperature));
return get(deepOcean, getCategory(temperature), shape, temperature, defaultDeepOcean);
}
@Override
@ -92,7 +107,8 @@ public abstract class AbstractBiomeMap implements BiomeMap {
public JsonObject toJson() {
JsonObject root = new JsonObject();
root.add("rivers", collect(river));
root.add("beaches", collect(river));
root.add("wetland", collect(wetland));
root.add("beaches", collect(beach));
root.add("oceans", collect(ocean));
root.add("deepOceans", collect(deepOcean));
return root;
@ -144,6 +160,13 @@ public abstract class AbstractBiomeMap implements BiomeMap {
return Biomes.RIVER;
}
protected Biome defaultWetland(float temperature) {
if (temperature < 0.15) {
return ModBiomes.TAIGA_SCRUB;
}
return ModBiomes.MARSHLAND;
}
protected Biome defaultOcean(float temperature) {
if (temperature < 0.3) {
return Biomes.FROZEN_OCEAN;
@ -164,7 +187,7 @@ public abstract class AbstractBiomeMap implements BiomeMap {
return Biomes.DEEP_OCEAN;
}
protected Biome defaultBiome(float temperature, float moisture) {
protected Biome defaultBiome(float temperature) {
if (temperature < 0.3) {
return ModBiomes.TAIGA_SCRUB;
}
@ -174,22 +197,22 @@ public abstract class AbstractBiomeMap implements BiomeMap {
return Biomes.PLAINS;
}
protected Biome get(Biome[][] group, Biome.TempCategory category, float shape, Biome def) {
return get(group, category.ordinal() - 1, shape, def);
protected Biome get(Biome[][] group, Biome.TempCategory category, float shape, float temp, DefaultBiome def) {
return get(group, category.ordinal() - 1, shape, temp, def);
}
protected Biome get(Biome[][] group, BiomeType type, float shape, Biome def) {
return get(group, type.ordinal(), shape, def);
protected Biome get(Biome[][] group, BiomeType type, float shape, float temp, DefaultBiome def) {
return get(group, type.ordinal(), shape, temp, def);
}
protected Biome get(Biome[][] group, int ordinal, float shape, Biome def) {
protected Biome get(Biome[][] group, int ordinal, float shape, float temp, DefaultBiome def) {
if (ordinal >= group.length) {
return def;
return def.getDefaultBiome(temp);
}
Biome[] biomes = group[ordinal];
if (biomes == null || biomes.length == 0) {
return def;
return def.getDefaultBiome(temp);
}
int index = NoiseUtil.round((biomes.length - 1) * shape);

View File

@ -64,7 +64,7 @@ public class BasicBiomeMap extends AbstractBiomeMap {
@Override
public Biome getBiome(BiomeType type, float temperature, float moisture, float shape) {
return get(biomeTypes, type, shape, defaultBiome(temperature, moisture));
return get(biomeTypes, type, shape, temperature, defaultLand);
}
@Override

View File

@ -26,7 +26,9 @@
package com.terraforged.mod.biome.map;
import com.google.gson.JsonElement;
import com.terraforged.core.cell.Cell;
import com.terraforged.core.world.biome.BiomeType;
import com.terraforged.core.world.terrain.Terrain;
import net.minecraft.world.biome.Biome;
import java.util.List;
@ -38,12 +40,18 @@ public interface BiomeMap {
Biome getRiver(float temperature, float moisture, float shape);
Biome getWetland(float temperature, float moisture, float shape);
Biome getOcean(float temperature, float moisture, float shape);
Biome getDeepOcean(float temperature, float moisture, float shape);
Biome getBiome(BiomeType type, float temperature, float moisture, float shape);
default Biome getBiome(Cell<Terrain> cell) {
return getBiome(cell.biomeType, cell.temperature, cell.moisture, cell.biome);
}
List<Biome> getAllBiomes(BiomeType type);
Set<Biome> getBiomes(BiomeType type);
@ -56,12 +64,18 @@ public interface BiomeMap {
JsonElement toJson();
static Biome getBiome(Biome biome) {
return biome.delegate.get();
}
interface Builder {
Builder addBeach(Biome biome, int count);
Builder addRiver(Biome biome, int count);
Builder addWetland(Biome biome, int count);
Builder addOcean(Biome biome, int count);
Builder addBiome(BiomeType type, Biome biome, int count);

View File

@ -44,6 +44,7 @@ import java.util.function.Function;
public class BiomeMapBuilder implements BiomeMap.Builder {
private final Map<Biome.TempCategory, List<Biome>> rivers = new HashMap<>();
private final Map<Biome.TempCategory, List<Biome>> wetlands = new HashMap<>();
private final Map<Biome.TempCategory, List<Biome>> beaches = new HashMap<>();
private final Map<Biome.TempCategory, List<Biome>> oceans = new HashMap<>();
private final Map<Biome.TempCategory, List<Biome>> deepOceans = new HashMap<>();
@ -87,6 +88,13 @@ public class BiomeMapBuilder implements BiomeMap.Builder {
return this;
}
@Override
public BiomeMapBuilder addWetland(Biome biome, int count) {
Biome.TempCategory category = BiomeHelper.getTempCategory(biome);
add(wetlands.computeIfAbsent(category, c -> new ArrayList<>()), biome, count);
return this;
}
@Override
public BiomeMapBuilder addBiome(BiomeType type, Biome biome, int count) {
add(map.computeIfAbsent(type, t -> new ArrayList<>()), biome, count);
@ -102,6 +110,10 @@ public class BiomeMapBuilder implements BiomeMap.Builder {
return collectTemps(rivers);
}
Biome[][] wetlands() {
return collectTemps(wetlands);
}
Biome[][] beaches() {
return collectTemps(beaches);
}

View File

@ -0,0 +1,12 @@
package com.terraforged.mod.biome.map;
import net.minecraft.world.biome.Biome;
public interface DefaultBiome {
Biome getBiome(float temperature);
default Biome getDefaultBiome(float temperature) {
return getBiome(temperature).delegate.get();
}
}

View File

@ -54,11 +54,6 @@ public class BiomeModifierManager implements BiomeModifier, ModifierManager {
context.factory.getClimate(),
context.levels
));
modifiers.add(new SwampModifier(
context.seed,
context.factory.getClimate(),
context.levels
));
Collections.sort(modifiers);
this.biomeModifiers = modifiers;
}

View File

@ -1,57 +0,0 @@
/*
*
* MIT License
*
* Copyright (c) 2020 TerraForged
*
* 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 com.terraforged.mod.biome.modifier;
import com.terraforged.core.cell.Cell;
import com.terraforged.core.util.Seed;
import com.terraforged.core.world.climate.Climate;
import com.terraforged.core.world.heightmap.Levels;
import com.terraforged.core.world.terrain.Terrain;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.Biomes;
// prevents swamps forming at high levels
public class SwampModifier extends AbstractMaxHeightModifier {
public SwampModifier(Seed seed, Climate climate, Levels levels) {
super(seed, climate, 60, 1, levels.scale(10), levels.ground(4), levels.ground(12));
}
@Override
protected Biome getModifiedBiome(Biome in, Cell<Terrain> cell, int x, int z, float ox, float oz) {
return Biomes.PLAINS;
}
@Override
public int priority() {
return 0;
}
@Override
public boolean test(Biome biome) {
return biome.getCategory() == Biome.Category.SWAMP;
}
}

View File

@ -60,7 +60,7 @@ public class BiomeHelper {
put(BiomeType.DESERT, BiomePredicate.DESERT.or(BiomePredicate.MESA).not(BiomePredicate.COAST).not(BiomePredicate.MOUNTAIN).not(BiomePredicate.WETLAND));
put(BiomeType.TEMPERATE_RAINFOREST, BiomePredicate.TEMPERATE_RAINFOREST.not(BiomePredicate.COAST).not(BiomePredicate.MOUNTAIN));
put(BiomeType.TEMPERATE_FOREST, BiomePredicate.TEMPERATE_FOREST.not(BiomePredicate.COAST).not(BiomePredicate.MOUNTAIN).not(BiomePredicate.WETLAND));
put(BiomeType.GRASSLAND, BiomePredicate.GRASSLAND.or(BiomePredicate.WETLAND).not(BiomePredicate.COAST).not(BiomePredicate.MOUNTAIN));
put(BiomeType.GRASSLAND, BiomePredicate.GRASSLAND.not(BiomePredicate.WETLAND).not(BiomePredicate.COAST).not(BiomePredicate.MOUNTAIN));
put(BiomeType.COLD_STEPPE, BiomePredicate.COLD_STEPPE.not(BiomePredicate.COAST).not(BiomePredicate.MOUNTAIN));
put(BiomeType.STEPPE, BiomePredicate.STEPPE.not(BiomePredicate.COAST).not(BiomePredicate.MOUNTAIN));
put(BiomeType.TAIGA, BiomePredicate.TAIGA.not(BiomePredicate.TUNDRA).not(BiomePredicate.COLD_STEPPE).not(BiomePredicate.COAST).not(BiomePredicate.MOUNTAIN));
@ -104,6 +104,8 @@ public class BiomeHelper {
builder.addRiver(biome, weight);
} else if (biome.getCategory() == Biome.Category.BEACH || biome == Biomes.STONE_SHORE) {
builder.addBeach(biome, weight);
} else if (biome.getCategory() == Biome.Category.SWAMP) {
builder.addWetland(biome, weight);
} else {
Collection<BiomeType> types = getTypes(data, biome);
for (BiomeType type : types) {

View File

@ -27,6 +27,7 @@ package com.terraforged.mod.biome.provider;
import com.google.common.collect.Sets;
import com.terraforged.core.cell.Cell;
import com.terraforged.core.decorator.Decorator;
import com.terraforged.core.region.chunk.ChunkReader;
import com.terraforged.core.util.concurrent.ObjectPool;
import com.terraforged.core.world.terrain.Terrain;
@ -40,8 +41,11 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.feature.structure.Structure;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
@ -50,6 +54,7 @@ public class BiomeProvider extends AbstractBiomeProvider {
private final BiomeMap biomeMap;
private final TerraContext context;
private final BiomeModifierManager modifierManager;
private final Map<Biome, List<Decorator>> decorators = new HashMap<>();
public BiomeProvider(TerraContext context) {
this.context = context;
@ -57,19 +62,6 @@ public class BiomeProvider extends AbstractBiomeProvider {
this.modifierManager = SetupHooks.setup(new BiomeModifierManager(context, biomeMap), context.copy());
}
public BiomeModifierManager getModifierManager() {
return modifierManager;
}
public TerraContainer createBiomeContainer(ChunkReader chunkReader) {
TerraContainer.Builder builder = TerraContainer.builder();
chunkReader.iterate((cell, dx, dz) -> {
Biome biome = getBiome(cell, chunkReader.getBlockX() + dx, chunkReader.getBlockZ() + dz);
builder.fill(dx, dz, biome);
});
return builder.build(chunkReader);
}
@Override
public Biome getBiome(int x, int y, int z) {
try (ObjectPool.Item<Cell<Terrain>> item = Cell.pooled()) {
@ -135,29 +127,51 @@ public class BiomeProvider extends AbstractBiomeProvider {
return this.topBlocksCache;
}
public BiomeModifierManager getModifierManager() {
return modifierManager;
}
public List<Decorator> getDecorators(Biome biome) {
return decorators.getOrDefault(biome, Collections.emptyList());
}
public TerraContainer createBiomeContainer(ChunkReader chunkReader) {
TerraContainer.Builder builder = TerraContainer.builder();
chunkReader.iterate((cell, dx, dz) -> {
Biome biome = getBiome(cell, chunkReader.getBlockX() + dx, chunkReader.getBlockZ() + dz);
builder.fill(dx, dz, biome);
});
return builder.build(chunkReader);
}
public Biome getBiome(Cell<Terrain> cell, int x, int z) {
if (cell.value <= context.levels.water) {
if (cell.tag == context.terrain.river || cell.tag == context.terrain.riverBanks) {
Biome biome = getBiome(cell);
if (biome.getCategory() == Biome.Category.SWAMP) {
return biome;
}
return biomeMap.getRiver(cell.temperature, cell.moisture, cell.biome);
} else if (cell.tag == context.terrain.ocean) {
return biomeMap.getOcean(cell.temperature, cell.moisture, cell.biome);
} else if (cell.tag == context.terrain.deepOcean) {
return biomeMap.getDeepOcean(cell.temperature, cell.moisture, cell.biome);
}
if (cell.tag == context.terrain.wetlands) {
return biomeMap.getWetland(cell.temperature, cell.moisture, cell.biome);
}
return modifyBiome(getBiome(cell), cell, x, z);
if (cell.value > context.levels.water) {
return getModifierManager().modify(biomeMap.getBiome(cell), cell, x, z);
}
if (cell.tag == context.terrain.river || cell.tag == context.terrain.riverBanks) {
Biome biome = biomeMap.getBiome(cell);
if (overridesRiver(biome)) {
return biome;
}
return biomeMap.getRiver(cell.temperature, cell.moisture, cell.biome);
}
if (cell.tag == context.terrain.ocean) {
return biomeMap.getOcean(cell.temperature, cell.moisture, cell.biome);
}
return biomeMap.getDeepOcean(cell.temperature, cell.moisture, cell.biome);
}
public Biome getBiome(Cell<Terrain> cell) {
return biomeMap.getBiome(cell.biomeType, cell.temperature, cell.moisture, cell.biome);
}
public Biome modifyBiome(Biome biome, Cell<Terrain> cell, int x, int z) {
return modifierManager.modify(biome, cell, x, z);
private static boolean overridesRiver(Biome biome) {
return biome.getCategory() == Biome.Category.SWAMP
|| biome.getCategory() == Biome.Category.JUNGLE;
}
private static class SearchContext {

View File

@ -0,0 +1,12 @@
package com.terraforged.mod.chunk;
import com.terraforged.api.chunk.column.DecoratorContext;
import com.terraforged.core.region.chunk.ChunkReader;
import net.minecraft.util.math.ChunkPos;
public interface ChunkProcessor {
void preProcess(ChunkPos pos, ChunkReader chunk, TerraContainer container);
void postProcess(ChunkReader chunk, TerraContainer container, DecoratorContext context);
}

View File

@ -32,6 +32,7 @@ import com.terraforged.api.chunk.surface.SurfaceContext;
import com.terraforged.api.chunk.surface.SurfaceManager;
import com.terraforged.api.material.layer.LayerManager;
import com.terraforged.core.cell.Cell;
import com.terraforged.core.decorator.Decorator;
import com.terraforged.core.region.RegionCache;
import com.terraforged.core.region.RegionGenerator;
import com.terraforged.core.region.Size;
@ -69,7 +70,6 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.IWorld;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeContainer;
import net.minecraft.world.biome.BiomeManager;
import net.minecraft.world.biome.Biomes;
import net.minecraft.world.chunk.ChunkPrimer;
@ -86,7 +86,7 @@ import net.minecraft.world.gen.feature.template.TemplateManager;
import java.util.ArrayList;
import java.util.List;
public class TerraChunkGenerator extends ObfHelperChunkGenerator<GenerationSettings> {
public class TerraChunkGenerator extends ObfHelperChunkGenerator<GenerationSettings> implements ChunkProcessor {
private final TerraContext context;
private final BiomeProvider biomeProvider;
@ -125,9 +125,23 @@ public class TerraChunkGenerator extends ObfHelperChunkGenerator<GenerationSetti
@Override
public final void generateBiomes(IChunk chunk) {
ChunkPos pos = chunk.getPos();
ChunkReader view = getChunkReader(pos.x, pos.z);
BiomeContainer container = getBiomeProvider().createBiomeContainer(view);
ChunkReader reader = getChunkReader(pos.x, pos.z);
TerraContainer container = getBiomeProvider().createBiomeContainer(reader);
((ChunkPrimer) chunk).func_225548_a_(container);
// apply chunk-local heightmap modifications
preProcess(pos, reader, container);
}
@Override
public final void preProcess(ChunkPos pos, ChunkReader chunk, TerraContainer container) {
chunk.iterate((cell, dx, dz) -> {
Biome biome = container.getBiome(dx, dz);
for (Decorator decorator : getBiomeProvider().getDecorators(biome)) {
if (decorator.apply(cell, pos.getXStart() + dx, pos.getZStart() + dz)) {
return;
}
}
});
}
@Override
@ -142,7 +156,6 @@ public class TerraChunkGenerator extends ObfHelperChunkGenerator<GenerationSetti
context.biome = container.getBiome(dx, dz);
ChunkPopulator.INSTANCE.decorate(chunk, context, px, py, pz);
});
terrainHelper.flatten(world, chunk, context.blockX, context.blockZ);
}
@ -193,19 +206,25 @@ public class TerraChunkGenerator extends ObfHelperChunkGenerator<GenerationSetti
// place biome features
featureManager.decorate(this, regionFix, chunk, biome, pos);
// run post processors
container.getChunkReader().iterate((cell, dx, dz) -> {
// run post processes on chunk
postProcess(container.getChunkReader(), container, context);
// bake biome array & discard gen data
((ChunkPrimer) chunk).func_225548_a_(container.bakeBiomes());
}
@Override
public final void postProcess(ChunkReader chunk, TerraContainer container, DecoratorContext context) {
chunk.iterate((cell, dx, dz) -> {
int px = context.blockX + dx;
int pz = context.blockZ + dz;
int py = chunk.getTopBlockY(Heightmap.Type.WORLD_SURFACE_WG, dx, dz);
int py = context.chunk.getTopBlockY(Heightmap.Type.WORLD_SURFACE_WG, dx, dz);
context.cell = cell;
context.biome = container.getBiome(dx, dz);
for (ColumnDecorator decorator : getPostProcessors()) {
decorator.decorate(chunk, context, px, py, pz);
decorator.decorate(context.chunk, context, px, py, pz);
}
});
((ChunkPrimer) chunk).func_225548_a_(container.bakeBiomes());
}
@Override

View File

@ -37,10 +37,10 @@ import net.minecraft.world.biome.BiomeContainer;
public class TerraContainer extends BiomeContainer {
private static final int ZOOM_HORIZ = (int) Math.round(Math.log(16.0D) / Math.log(2.0D)) - 2;
private static final int BITS_WIDTH = (int) Math.round(Math.log(16.0D) / Math.log(2.0D)) - 2;
private static final int ZOOM_VERT = (int) Math.round(Math.log(256.0D) / Math.log(2.0D)) - 2;
public static final int SIZE = 1 << ZOOM_HORIZ + ZOOM_HORIZ + ZOOM_VERT;
public static final int MASK_HORIZ = (1 << ZOOM_HORIZ) - 1;
public static final int SIZE = 1 << BITS_WIDTH + BITS_WIDTH + ZOOM_VERT;
public static final int MASK_HORIZ = (1 << BITS_WIDTH) - 1;
public static final int MASK_VERT = (1 << ZOOM_VERT) - 1;
private final Biome[] biomes;
@ -97,10 +97,10 @@ public class TerraContainer extends BiomeContainer {
}
private static int indexOf(int x, int y, int z) {
int bx = x & MASK_HORIZ;
int by = MathHelper.clamp(y, 0, MASK_VERT);
int bz = z & MASK_HORIZ;
return by << ZOOM_HORIZ + ZOOM_HORIZ | bz << ZOOM_HORIZ | bx;
x &= MASK_HORIZ;
y = MathHelper.clamp(y, 0, MASK_VERT);
z &= MASK_HORIZ;
return y << BITS_WIDTH + BITS_WIDTH | z << BITS_WIDTH | x;
}
public static Builder builder() {
@ -119,7 +119,7 @@ public class TerraContainer extends BiomeContainer {
}
public void fill(int x, int z, Biome biome) {
for (int y = 0; y < 64; y++) {
for (int y = 0; y < 256; y += 4) {
set(x, y, z, biome);
}
}

View File

@ -35,10 +35,10 @@ public class Test {
public static boolean fixedBiome = true;
public static Terrain getTerrainType(Terrains terrains) {
return terrains.steppe;
return terrains.dales;
}
public static Biome getBiome() {
return Biomes.DESERT;
return Biomes.PLAINS;
}
}

View File

@ -27,7 +27,6 @@ package com.terraforged.mod.chunk.test;
import com.terraforged.core.cell.Cell;
import com.terraforged.core.world.terrain.Terrain;
import com.terraforged.mod.biome.ModBiomes;
import com.terraforged.mod.biome.provider.BiomeProvider;
import com.terraforged.mod.chunk.TerraContext;
import net.minecraft.world.biome.Biome;
@ -40,9 +39,6 @@ public class TestBiomeProvider extends BiomeProvider {
@Override
public Biome getBiome(Cell<Terrain> cell, int x, int z) {
if (cell.biome < 0.5F) {
return ModBiomes.TAIGA_SCRUB;
}
return Test.getBiome();
}
}

View File

@ -75,7 +75,6 @@ public class TestChunkGenerator extends TerraChunkGenerator {
@Override
public void apply(Cell<Terrain> cell, float x, float y) {
super.apply(cell, x, y);
populator.apply(cell, x, y);
}

View File

@ -26,6 +26,7 @@
package com.terraforged.mod.command.task;
import com.terraforged.core.cell.Cell;
import com.terraforged.core.decorator.Decorator;
import com.terraforged.core.world.WorldGenerator;
import com.terraforged.core.world.terrain.Terrain;
import com.terraforged.mod.biome.provider.BiomeProvider;
@ -58,9 +59,14 @@ public class FindBiomeTask extends FindTask {
@Override
protected boolean search(int x, int z) {
generator.getHeightmap().apply(cell, x, z);
for (Decorator decorator : generator.getDecorators().getDecorators()) {
if (decorator.apply(cell, x, z)) {
break;
}
}
if (biome.getCategory() != Biome.Category.BEACH && biome.getCategory() != Biome.Category.OCEAN) {
if (cell.continentEdge > 0.4 && cell.continentEdge < 0.5) {
if (cell.continentEdge > 0.4 && cell.continentEdge < 0.45) {
return false;
}
}

View File

@ -25,6 +25,7 @@
package com.terraforged.mod.data;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.terraforged.core.world.biome.BiomeType;
@ -33,7 +34,11 @@ import com.terraforged.mod.biome.provider.BiomeHelper;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.biome.Biome;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Set;
public class WorldGenBiomes extends DataGen {
@ -43,6 +48,12 @@ public class WorldGenBiomes extends DataGen {
for (BiomeType type : BiomeType.values()) {
genBiomes(dataDir, type, map);
}
try (Writer writer = new BufferedWriter(new FileWriter(new File("biome_map.json")))) {
new GsonBuilder().setPrettyPrinting().create().toJson(map.toJson(), writer);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void genBiomes(File dir, BiomeType type, BiomeMap map) {

View File

@ -2,7 +2,6 @@
"replaceable": false,
"values": [
"minecraft:plains",
"minecraft:sunflower_plains",
"minecraft:swamp"
"minecraft:sunflower_plains"
]
}