mirror of
https://github.com/PolyhedralDev/Terra.git
synced 2026-04-09 17:26:07 +00:00
refactor flora stuff
This commit is contained in:
@@ -44,8 +44,8 @@ import com.dfsek.terra.config.loaders.palette.CarverPaletteLoader;
|
||||
import com.dfsek.terra.config.loaders.palette.PaletteHolderLoader;
|
||||
import com.dfsek.terra.config.loaders.palette.PaletteLayerLoader;
|
||||
import com.dfsek.terra.config.loaders.palette.slant.SlantHolderLoader;
|
||||
import com.dfsek.terra.world.population.items.flora.FloraLayer;
|
||||
import com.dfsek.terra.world.population.items.flora.TerraFlora;
|
||||
import com.dfsek.terra.addons.flora.flora.FloraLayer;
|
||||
import com.dfsek.terra.addons.flora.flora.TerraFlora;
|
||||
import com.dfsek.terra.world.population.items.ores.OreConfig;
|
||||
import com.dfsek.terra.world.population.items.ores.OreHolder;
|
||||
import com.dfsek.terra.world.population.items.tree.TreeLayer;
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.dfsek.terra.config.factories;
|
||||
|
||||
import com.dfsek.terra.api.TerraPlugin;
|
||||
import com.dfsek.terra.api.config.ConfigFactory;
|
||||
import com.dfsek.terra.api.world.Flora;
|
||||
import com.dfsek.terra.api.world.palette.NoisePalette;
|
||||
import com.dfsek.terra.api.world.palette.PaletteImpl;
|
||||
import com.dfsek.terra.api.world.palette.holder.PaletteLayerHolder;
|
||||
import com.dfsek.terra.config.templates.FloraTemplate;
|
||||
import com.dfsek.terra.noise.samplers.noise.random.WhiteNoiseSampler;
|
||||
import com.dfsek.terra.world.population.items.flora.TerraFlora;
|
||||
|
||||
public class FloraFactory implements ConfigFactory<FloraTemplate, Flora> {
|
||||
@Override
|
||||
public TerraFlora build(FloraTemplate config, TerraPlugin main) {
|
||||
PaletteImpl palette = new NoisePalette(new WhiteNoiseSampler(2403), false);
|
||||
for(PaletteLayerHolder layer : config.getFloraPalette()) {
|
||||
palette.add(layer.getLayer(), layer.getSize(), layer.getSampler());
|
||||
}
|
||||
return new TerraFlora(palette, config.doPhysics(), config.isCeiling(), config.getIrrigable(), config.getSpawnable(), config.getReplaceable(), config.getRotatable(), config.getMaxPlacements(), config.getSearch(), config.isSpawnBlacklist(), config.getIrrigableOffset(), main);
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package com.dfsek.terra.config.loaders.config;
|
||||
|
||||
import com.dfsek.tectonic.exception.ConfigException;
|
||||
import com.dfsek.tectonic.exception.LoadException;
|
||||
import com.dfsek.tectonic.loading.ConfigLoader;
|
||||
import com.dfsek.tectonic.loading.TypeLoader;
|
||||
import com.dfsek.terra.api.util.ProbabilityCollection;
|
||||
import com.dfsek.terra.api.util.Range;
|
||||
import com.dfsek.terra.api.util.seeded.NoiseSeeded;
|
||||
import com.dfsek.terra.api.world.Flora;
|
||||
import com.dfsek.terra.config.loaders.Types;
|
||||
import com.dfsek.terra.noise.samplers.noise.random.WhiteNoiseSampler;
|
||||
import com.dfsek.terra.world.population.items.flora.FloraLayer;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class FloraLayerLoader implements TypeLoader<FloraLayer> {
|
||||
@Override
|
||||
public FloraLayer load(Type type, Object o, ConfigLoader configLoader) throws LoadException {
|
||||
Map<String, Object> map = (Map<String, Object>) o;
|
||||
double density = ((Number) map.get("density")).doubleValue();
|
||||
Range range = configLoader.loadClass(Range.class, map.get("y"));
|
||||
if(range == null) throw new LoadException("Flora range unspecified");
|
||||
ProbabilityCollection<Flora> items = (ProbabilityCollection<Flora>) configLoader.loadType(Types.FLORA_PROBABILITY_COLLECTION_TYPE, map.get("items"));
|
||||
|
||||
NoiseSeeded sampler;
|
||||
if(map.containsKey("distribution")) {
|
||||
try {
|
||||
sampler = configLoader.loadClass(NoiseSeeded.class, map.get("distribution"));
|
||||
} catch(ConfigException e) {
|
||||
throw new LoadException("Unable to load noise", e);
|
||||
}
|
||||
return new FloraLayer(density, range, items, sampler.apply(2403L));
|
||||
}
|
||||
|
||||
return new FloraLayer(density, range, items, new WhiteNoiseSampler(2403));
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
package com.dfsek.terra.config.templates;
|
||||
|
||||
import com.dfsek.tectonic.annotations.Abstractable;
|
||||
import com.dfsek.tectonic.annotations.Default;
|
||||
import com.dfsek.tectonic.annotations.Value;
|
||||
import com.dfsek.terra.api.config.AbstractableTemplate;
|
||||
import com.dfsek.terra.api.util.collections.MaterialSet;
|
||||
import com.dfsek.terra.api.world.palette.holder.PaletteLayerHolder;
|
||||
import com.dfsek.terra.world.population.items.flora.TerraFlora;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings({"FieldMayBeFinal", "unused"})
|
||||
public class FloraTemplate implements AbstractableTemplate {
|
||||
@Value("id")
|
||||
private String id;
|
||||
|
||||
@Value("spawnable")
|
||||
@Abstractable
|
||||
private MaterialSet spawnable;
|
||||
|
||||
@Value("spawn-blacklist")
|
||||
@Abstractable
|
||||
@Default
|
||||
private boolean spawnBlacklist = false;
|
||||
|
||||
|
||||
@Value("replaceable")
|
||||
@Abstractable
|
||||
@Default
|
||||
private MaterialSet replaceable = new MaterialSet();
|
||||
|
||||
@Value("irrigable")
|
||||
@Abstractable
|
||||
@Default
|
||||
private MaterialSet irrigable = null;
|
||||
|
||||
@Value("rotatable")
|
||||
@Abstractable
|
||||
@Default
|
||||
private MaterialSet rotatable = new MaterialSet();
|
||||
|
||||
@Value("layers")
|
||||
@Abstractable
|
||||
private List<PaletteLayerHolder> palette;
|
||||
|
||||
@Value("physics")
|
||||
@Abstractable
|
||||
@Default
|
||||
private boolean doPhysics = false;
|
||||
|
||||
@Value("ceiling")
|
||||
@Abstractable
|
||||
@Default
|
||||
private boolean ceiling = false;
|
||||
|
||||
@Value("search")
|
||||
@Default
|
||||
@Abstractable
|
||||
private TerraFlora.Search search = TerraFlora.Search.UP;
|
||||
|
||||
@Value("max-placements")
|
||||
@Default
|
||||
@Abstractable
|
||||
private int maxPlacements = -1;
|
||||
|
||||
@Value("irrigable-offset")
|
||||
@Abstractable
|
||||
@Default
|
||||
private int irrigableOffset;
|
||||
|
||||
public int getIrrigableOffset() {
|
||||
return irrigableOffset;
|
||||
}
|
||||
|
||||
public TerraFlora.Search getSearch() {
|
||||
return search;
|
||||
}
|
||||
|
||||
public int getMaxPlacements() {
|
||||
return maxPlacements;
|
||||
}
|
||||
|
||||
public MaterialSet getReplaceable() {
|
||||
return replaceable;
|
||||
}
|
||||
|
||||
public MaterialSet getSpawnable() {
|
||||
return spawnable;
|
||||
}
|
||||
|
||||
public MaterialSet getIrrigable() {
|
||||
return irrigable;
|
||||
}
|
||||
|
||||
public String getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public List<PaletteLayerHolder> getFloraPalette() {
|
||||
return palette;
|
||||
}
|
||||
|
||||
public boolean doPhysics() {
|
||||
return doPhysics;
|
||||
}
|
||||
|
||||
public boolean isCeiling() {
|
||||
return ceiling;
|
||||
}
|
||||
|
||||
public boolean isSpawnBlacklist() {
|
||||
return spawnBlacklist;
|
||||
}
|
||||
|
||||
public MaterialSet getRotatable() {
|
||||
return rotatable;
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
package com.dfsek.terra.world.population;
|
||||
|
||||
import com.dfsek.terra.api.TerraPlugin;
|
||||
import com.dfsek.terra.api.profiler.ProfileFrame;
|
||||
import com.dfsek.terra.api.util.PopulationUtil;
|
||||
import com.dfsek.terra.api.vector.Vector2;
|
||||
import com.dfsek.terra.api.world.Chunk;
|
||||
import com.dfsek.terra.api.world.TerraWorld;
|
||||
import com.dfsek.terra.api.world.World;
|
||||
import com.dfsek.terra.api.world.biome.UserDefinedBiome;
|
||||
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
|
||||
import com.dfsek.terra.api.world.generator.TerraBlockPopulator;
|
||||
import com.dfsek.terra.world.population.items.flora.FloraLayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Populates Flora
|
||||
*/
|
||||
public class FloraPopulator implements TerraBlockPopulator {
|
||||
private final TerraPlugin main;
|
||||
|
||||
public FloraPopulator(TerraPlugin main) {
|
||||
this.main = main;
|
||||
}
|
||||
|
||||
@SuppressWarnings("try")
|
||||
@Override
|
||||
public void populate(@NotNull World world, @NotNull Chunk chunk) {
|
||||
TerraWorld tw = main.getWorld(world);
|
||||
try(ProfileFrame ignore = main.getProfiler().profile("flora")) {
|
||||
if(tw.getConfig().disableFlora()) return;
|
||||
|
||||
if(!tw.isSafe()) return;
|
||||
BiomeProvider provider = tw.getBiomeProvider();
|
||||
Map<Vector2, List<FloraLayer>> layers = new HashMap<>();
|
||||
for(int x = 0; x < 16; x++) {
|
||||
for(int z = 0; z < 16; z++) {
|
||||
UserDefinedBiome biome = (UserDefinedBiome) provider.getBiome((chunk.getX() << 4) + x, (chunk.getZ() << 4) + z);
|
||||
Vector2 l = new Vector2(x, z);
|
||||
layers.put(l, biome.getConfig().getFlora());
|
||||
}
|
||||
}
|
||||
|
||||
Random random = PopulationUtil.getRandom(chunk);
|
||||
|
||||
int iter = 0;
|
||||
boolean finished = false;
|
||||
while(!finished) {
|
||||
finished = true;
|
||||
for(Map.Entry<Vector2, List<FloraLayer>> entry : layers.entrySet()) {
|
||||
if(entry.getValue().size() <= iter) continue;
|
||||
finished = false;
|
||||
FloraLayer layer = entry.getValue().get(iter);
|
||||
if(layer.getDensity() >= random.nextDouble() * 100D) layer.place(chunk, entry.getKey());
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package com.dfsek.terra.world.population.items.flora;
|
||||
|
||||
import com.dfsek.terra.api.noise.NoiseSampler;
|
||||
import com.dfsek.terra.api.util.ProbabilityCollection;
|
||||
import com.dfsek.terra.api.util.Range;
|
||||
import com.dfsek.terra.api.vector.Vector2;
|
||||
import com.dfsek.terra.api.world.Chunk;
|
||||
import com.dfsek.terra.api.world.Flora;
|
||||
import com.dfsek.terra.world.population.items.PlaceableLayer;
|
||||
|
||||
public class FloraLayer extends PlaceableLayer<Flora> {
|
||||
|
||||
public FloraLayer(double density, Range level, ProbabilityCollection<Flora> layer, NoiseSampler noise) {
|
||||
super(density, level, layer, noise);
|
||||
}
|
||||
|
||||
public double getDensity() {
|
||||
return density;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void place(Chunk chunk, Vector2 coords) {
|
||||
int cx = chunk.getX() << 4;
|
||||
int cz = chunk.getZ() << 4;
|
||||
Flora item = layer.get(noise, coords.getX() + cx, coords.getZ() + cz);
|
||||
item.getValidSpawnsAt(chunk, (int) coords.getX(), (int) coords.getZ(), level).forEach(block -> item.plant(block.add(cx, 0, cz), chunk.getWorld()));
|
||||
}
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
package com.dfsek.terra.world.population.items.flora;
|
||||
|
||||
import com.dfsek.terra.api.TerraPlugin;
|
||||
import com.dfsek.terra.api.block.state.BlockState;
|
||||
import com.dfsek.terra.api.block.state.properties.base.Properties;
|
||||
import com.dfsek.terra.api.block.state.properties.enums.Direction;
|
||||
import com.dfsek.terra.api.util.FastRandom;
|
||||
import com.dfsek.terra.api.util.Range;
|
||||
import com.dfsek.terra.api.util.collections.MaterialSet;
|
||||
import com.dfsek.terra.api.vector.Vector3;
|
||||
import com.dfsek.terra.api.world.Chunk;
|
||||
import com.dfsek.terra.api.world.Flora;
|
||||
import com.dfsek.terra.api.world.World;
|
||||
import com.dfsek.terra.api.world.generator.Palette;
|
||||
import net.jafama.FastMath;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
public class TerraFlora implements Flora {
|
||||
private final Palette floraPalette;
|
||||
private final boolean physics;
|
||||
private final boolean ceiling;
|
||||
|
||||
private final MaterialSet irrigable;
|
||||
|
||||
private final MaterialSet spawnable;
|
||||
private final MaterialSet replaceable;
|
||||
|
||||
private final MaterialSet testRotation;
|
||||
|
||||
private final int maxPlacements;
|
||||
|
||||
private final Search search;
|
||||
|
||||
private final boolean spawnBlacklist;
|
||||
|
||||
private final int irrigableOffset;
|
||||
|
||||
private final TerraPlugin main;
|
||||
|
||||
public TerraFlora(Palette floraPalette, boolean physics, boolean ceiling, MaterialSet irrigable, MaterialSet spawnable, MaterialSet replaceable, MaterialSet testRotation, int maxPlacements, Search search, boolean spawnBlacklist, int irrigableOffset, TerraPlugin main) {
|
||||
this.floraPalette = floraPalette;
|
||||
this.physics = physics;
|
||||
this.testRotation = testRotation;
|
||||
this.spawnBlacklist = spawnBlacklist;
|
||||
this.ceiling = ceiling;
|
||||
this.irrigable = irrigable;
|
||||
this.spawnable = spawnable;
|
||||
this.replaceable = replaceable;
|
||||
this.maxPlacements = maxPlacements;
|
||||
this.search = search;
|
||||
this.irrigableOffset = irrigableOffset;
|
||||
this.main = main;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Vector3> getValidSpawnsAt(Chunk chunk, int x, int z, Range range) {
|
||||
int size = floraPalette.getSize();
|
||||
Vector3 current = new Vector3(x, search.equals(Search.UP) ? range.getMin() : range.getMax(), z);
|
||||
List<Vector3> blocks = new ArrayList<>();
|
||||
int cx = chunk.getX() << 4;
|
||||
int cz = chunk.getZ() << 4;
|
||||
for(int y : range) {
|
||||
if(y > 255 || y < 0) continue;
|
||||
current = current.add(0, search.equals(Search.UP) ? 1 : -1, 0);
|
||||
if((spawnBlacklist != spawnable.contains(chunk.getBlock(current.getBlockX(), current.getBlockY(), current.getBlockZ()).getBlockType())) && isIrrigated(current.clone().add(cx, irrigableOffset, cz), chunk.getWorld()) && valid(size, current.clone().add(cx, 0, cz), chunk.getWorld())) {
|
||||
blocks.add(current.clone());
|
||||
if(maxPlacements > 0 && blocks.size() >= maxPlacements) break;
|
||||
}
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
|
||||
private boolean valid(int size, Vector3 block, World world) {
|
||||
for(int i = 0; i < size; i++) { // Down if ceiling, up if floor
|
||||
if(block.getY() + 1 > 255 || block.getY() < 0) return false;
|
||||
block.add(0, ceiling ? -1 : 1, 0);
|
||||
if(!replaceable.contains(world.getBlockData(block).getBlockType())) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isIrrigated(Vector3 b, World world) {
|
||||
if(irrigable == null) return true;
|
||||
return irrigable.contains(world.getBlockData(b.getBlockX() + 1, b.getBlockY(), b.getBlockZ()).getBlockType())
|
||||
|| irrigable.contains(world.getBlockData(b.getBlockX() - 1, b.getBlockY(), b.getBlockZ()).getBlockType())
|
||||
|| irrigable.contains(world.getBlockData(b.getBlockX(), b.getBlockY(), b.getBlockZ() + 1).getBlockType())
|
||||
|| irrigable.contains(world.getBlockData(b.getBlockX(), b.getBlockY(), b.getBlockZ() - 1).getBlockType());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean plant(Vector3 location, World world) {
|
||||
boolean doRotation = testRotation.size() > 0;
|
||||
int size = floraPalette.getSize();
|
||||
int c = ceiling ? -1 : 1;
|
||||
|
||||
EnumSet<Direction> faces = doRotation ? getFaces(location.clone().add(0, c, 0), world) : EnumSet.noneOf(Direction.class);
|
||||
if(doRotation && faces.size() == 0) return false; // Don't plant if no faces are valid.
|
||||
|
||||
for(int i = 0; FastMath.abs(i) < size; i += c) { // Down if ceiling, up if floor
|
||||
int lvl = (FastMath.abs(i));
|
||||
BlockState data = floraPalette.get((ceiling ? lvl : size - lvl - 1), location.getX(), location.getY(), location.getZ()).clone();
|
||||
if(doRotation) {
|
||||
Direction oneFace = new ArrayList<>(faces).get(new FastRandom(location.getBlockX() ^ location.getBlockZ()).nextInt(faces.size())); // Get random face.
|
||||
|
||||
data.setIfPresent(Properties.DIRECTION, oneFace.opposite())
|
||||
.setIfPresent(Properties.NORTH, faces.contains(Direction.NORTH))
|
||||
.setIfPresent(Properties.SOUTH, faces.contains(Direction.SOUTH))
|
||||
.setIfPresent(Properties.EAST, faces.contains(Direction.EAST))
|
||||
.setIfPresent(Properties.WEST, faces.contains(Direction.WEST));
|
||||
}
|
||||
world.setBlockData(location.clone().add(0, i + c, 0), data, physics);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private EnumSet<Direction> getFaces(Vector3 b, World world) {
|
||||
EnumSet<Direction> faces = EnumSet.noneOf(Direction.class);
|
||||
test(faces, Direction.NORTH, b, world);
|
||||
test(faces, Direction.SOUTH, b, world);
|
||||
test(faces, Direction.EAST, b, world);
|
||||
test(faces, Direction.WEST, b, world);
|
||||
return faces;
|
||||
}
|
||||
|
||||
private void test(EnumSet<Direction> faces, Direction f, Vector3 b, World world) {
|
||||
if(testRotation.contains(world.getBlockData(b.getBlockX() + f.getModX(), b.getBlockY() + f.getModY(), b.getBlockZ() + f.getModZ()).getBlockType()))
|
||||
faces.add(f);
|
||||
}
|
||||
|
||||
public enum Search {
|
||||
UP,
|
||||
DOWN
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user