mirror of
https://github.com/PolyhedralDev/Terra.git
synced 2025-07-18 10:32:30 +00:00
Implement basic structure config
This commit is contained in:
parent
89224723e6
commit
eb208ed9fe
2
pom.xml
2
pom.xml
@ -95,7 +95,7 @@
|
||||
<dependency>
|
||||
<groupId>org.polydev</groupId>
|
||||
<artifactId>gaea</artifactId>
|
||||
<version>1.10.46</version>
|
||||
<version>1.10.47</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>me.lucko</groupId>
|
||||
|
@ -9,6 +9,7 @@ import com.dfsek.terra.config.genconfig.CarverConfig;
|
||||
import com.dfsek.terra.config.genconfig.FloraConfig;
|
||||
import com.dfsek.terra.config.genconfig.OreConfig;
|
||||
import com.dfsek.terra.config.genconfig.PaletteConfig;
|
||||
import com.dfsek.terra.config.genconfig.StructureConfig;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
@ -21,7 +22,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
public class ConfigUtil {
|
||||
public static boolean debug;
|
||||
public static long dataSave;
|
||||
public static long dataSave; // Period of population data saving, in ticks.
|
||||
public static void loadConfig(JavaPlugin main) {
|
||||
main.saveDefaultConfig();
|
||||
FileConfiguration config = main.getConfig();
|
||||
@ -42,6 +43,8 @@ public class ConfigUtil {
|
||||
|
||||
new ConfigLoader("abstract" + File.separator + "biomes").load(main, AbstractBiomeConfig.class);
|
||||
|
||||
new ConfigLoader("structure" + File.separator + "single").load(main, StructureConfig.class);
|
||||
|
||||
TerraBiomeGrid.invalidate();
|
||||
BiomeZone.invalidate(); // Invalidate BiomeZone and BiomeGrid caches to prevent old instances from being accessed.
|
||||
new ConfigLoader("biomes").load(main, BiomeConfig.class);
|
||||
|
@ -15,7 +15,7 @@ import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
public class BiomeConfigUtil {
|
||||
public static Map<Material, Palette<BlockData>> getSlabPalettes(List<Map<?, ?>> paletteConfigSection, TerraConfigObject config) throws InvalidConfigurationException {
|
||||
protected static Map<Material, Palette<BlockData>> getSlabPalettes(List<Map<?, ?>> paletteConfigSection, TerraConfigObject config) throws InvalidConfigurationException {
|
||||
Map<Material, Palette<BlockData>> paletteMap = new HashMap<>();
|
||||
|
||||
for(Map<?, ?> e : paletteConfigSection) {
|
||||
|
@ -1,23 +1,66 @@
|
||||
package com.dfsek.terra.config.genconfig;
|
||||
|
||||
import com.dfsek.terra.Range;
|
||||
import com.dfsek.terra.Terra;
|
||||
import com.dfsek.terra.config.TerraConfigObject;
|
||||
import com.dfsek.terra.population.StructurePopulator;
|
||||
import com.dfsek.terra.structure.GaeaStructure;
|
||||
import com.dfsek.terra.structure.StructureSpawn;
|
||||
import com.dfsek.terra.structure.StructureSpawnRequirement;
|
||||
import org.bukkit.configuration.InvalidConfigurationException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
public class StructureConfig extends TerraConfigObject {
|
||||
private GaeaStructure structure;
|
||||
private StructureSpawn spawn;
|
||||
private String id;
|
||||
private Range searchStart;
|
||||
private Range bound;
|
||||
StructurePopulator.SearchType type;
|
||||
public StructureConfig(File file) throws IOException, InvalidConfigurationException {
|
||||
super(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() throws InvalidConfigurationException {
|
||||
|
||||
try {
|
||||
structure = GaeaStructure.load(new File(Terra.getInstance().getDataFolder() + File.separator + "config" + File.separator + "structures" + File.separator + "data", Objects.requireNonNull(getString("file"))));
|
||||
} catch(IOException | NullPointerException e) {
|
||||
throw new InvalidConfigurationException("Unable to locate structure: " + getString("file"));
|
||||
}
|
||||
if(!contains("id")) throw new InvalidConfigurationException("No ID specified!");
|
||||
id = getString("id");
|
||||
spawn = new StructureSpawn(getInt("spawn.width", 500), getInt("spawn.padding", 100));
|
||||
searchStart = new Range(getInt("spawn.start.min", 72), getInt("spawn.start.max", 72));
|
||||
bound = new Range(getInt("spawn.bound.min", 48), getInt("spawn.bound.max", 72));
|
||||
try {
|
||||
type = StructurePopulator.SearchType.valueOf(getString("spawn,search", "DOWN"));
|
||||
} catch(IllegalArgumentException e) {
|
||||
throw new InvalidConfigurationException("Invalid search type, " + getString("spawn,search"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getID() {
|
||||
return null;
|
||||
return id;
|
||||
}
|
||||
|
||||
public GaeaStructure getStructure() {
|
||||
return structure;
|
||||
}
|
||||
|
||||
public StructureSpawn getSpawn() {
|
||||
return spawn;
|
||||
}
|
||||
|
||||
public Range getBound() {
|
||||
return bound;
|
||||
}
|
||||
|
||||
public Range getSearchStart() {
|
||||
return searchStart;
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ public class SlabGenerator extends GenerationPopulator {
|
||||
BlockData slab = slabs.getOrDefault(down.getMaterial(), AIRPALETTE).get(0, block.getBlockX(), block.getBlockZ());
|
||||
if(slab instanceof Waterlogged) {
|
||||
((Waterlogged) slab).setWaterlogged(orig.matches(WATER));
|
||||
}
|
||||
} else if(orig.matches(WATER)) return;
|
||||
chunk.setBlock(block.getBlockX(), block.getBlockY(), block.getBlockZ(), slab);
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import com.dfsek.terra.population.FloraPopulator;
|
||||
import com.dfsek.terra.population.OrePopulator;
|
||||
import com.dfsek.terra.population.StructurePopulator;
|
||||
import com.dfsek.terra.population.TreePopulator;
|
||||
import com.dfsek.terra.structure.StructureSpawnRequirement;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
@ -51,6 +52,7 @@ public class TerraChunkGenerator extends GaeaChunkGenerator {
|
||||
@Override
|
||||
public ChunkData generateBase(@NotNull World world, @NotNull Random random, int chunkX, int chunkZ, FastNoise fastNoise) {
|
||||
if(needsLoad) load(world);
|
||||
StructureSpawnRequirement.putNoise(world, fastNoise); // Assign noise to world to be used for structures.
|
||||
ChunkData chunk = createChunkData(world);
|
||||
int xOrig = (chunkX << 4);
|
||||
int zOrig = (chunkZ << 4);
|
||||
@ -130,4 +132,6 @@ public class TerraChunkGenerator extends GaeaChunkGenerator {
|
||||
public boolean shouldGenerateStructures() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
29
src/main/java/com/dfsek/terra/math/BaseHeightFunction.java
Normal file
29
src/main/java/com/dfsek/terra/math/BaseHeightFunction.java
Normal file
@ -0,0 +1,29 @@
|
||||
package com.dfsek.terra.math;
|
||||
|
||||
import org.polydev.gaea.math.FastNoise;
|
||||
import org.polydev.gaea.math.parsii.eval.Expression;
|
||||
import org.polydev.gaea.math.parsii.eval.Function;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class BaseHeightFunction implements Function {
|
||||
private FastNoise gen;
|
||||
@Override
|
||||
public int getNumberOfArguments() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double eval(List<Expression> list) {
|
||||
return gen.getSimplexFractal((float) list.get(0).evaluate(), (float) list.get(1).evaluate(), (float) list.get(2).evaluate());
|
||||
}
|
||||
|
||||
public void setNoise(FastNoise gen) {
|
||||
this.gen = gen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNaturalFunction() {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ import com.dfsek.terra.Terra;
|
||||
import com.dfsek.terra.TerraProfiler;
|
||||
import com.dfsek.terra.structure.GaeaStructure;
|
||||
import com.dfsek.terra.structure.StructureSpawn;
|
||||
import com.dfsek.terra.structure.StructureSpawnRequirement;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
@ -35,12 +36,23 @@ public class StructurePopulator extends BlockPopulator {
|
||||
int cx = (chunk.getX() << 4);
|
||||
int cz = (chunk.getZ() << 4);
|
||||
Location spawn = spawnTest.getNearestSpawn(cx+ 8, cz + 8, world.getSeed()).toLocation(world);
|
||||
spawn.setY(72);
|
||||
main: for(int y = 72; y > 0; y--) {
|
||||
spawn.setY(y);
|
||||
for(StructureSpawnRequirement s : struc.getSpawns()) {
|
||||
if(!s.isValidSpawn(spawn)) continue main;
|
||||
}
|
||||
Bukkit.getLogger().info("Valid spawn at " + spawn);
|
||||
if(Math.abs((cx+8)-spawn.getBlockX()) <= horizontal && Math.abs((cz+8)-spawn.getBlockZ()) <= horizontal) {
|
||||
try(ProfileFuture ignore = TerraProfiler.fromWorld(world).measure("StructurePasteTime")) {
|
||||
struc.paste(spawn, chunk, GaeaStructure.Rotation.fromDegrees(random.nextInt(4)*90), Collections.emptyList());
|
||||
struc.paste(spawn, chunk, GaeaStructure.Rotation.fromDegrees(new Random(spawn.hashCode()).nextInt(4)*90), Collections.emptyList());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public enum SearchType {
|
||||
UP, DOWN
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import java.io.InputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@ -33,6 +34,7 @@ import java.util.function.Consumer;
|
||||
public class GaeaStructure implements Serializable {
|
||||
public static final long serialVersionUID = -6664585217063842035L;
|
||||
private final StructureContainedBlock[][][] structure;
|
||||
private final ArrayList<StructureSpawnRequirement> spawns = new ArrayList<>();
|
||||
private final GaeaStructureInfo structureInfo;
|
||||
private final String id;
|
||||
private final UUID uuid;
|
||||
@ -68,6 +70,21 @@ public class GaeaStructure implements Serializable {
|
||||
if(s.getLine(1).equals("[CENTER]")) {
|
||||
centerX = x;
|
||||
centerZ = z;
|
||||
} else if(s.getLine(1).startsWith("[SPAWN=") && s.getLine(1).endsWith("]")) {
|
||||
String og = s.getLine(1);
|
||||
String spawn = og.substring(og.indexOf("=")+1, og.length()-1);
|
||||
switch(spawn.toUpperCase()) {
|
||||
case "LAND":
|
||||
spawns.add(new StructureSpawnRequirement(StructureSpawnRequirement.RequirementType.LAND, x, y, z));
|
||||
break;
|
||||
case "OCEAN":
|
||||
spawns.add(new StructureSpawnRequirement(StructureSpawnRequirement.RequirementType.OCEAN, x, y, z));
|
||||
break;
|
||||
case "AIR":
|
||||
spawns.add(new StructureSpawnRequirement(StructureSpawnRequirement.RequirementType.AIR, x, y, z));
|
||||
break;
|
||||
default: throw new InitializationException("Invalid spawn type, " + spawn);
|
||||
}
|
||||
}
|
||||
} catch(IllegalArgumentException e) {
|
||||
throw new InitializationException("Invalid Block Data on sign: \"" + s.getLine(2) + s.getLine(3) + "\"");
|
||||
@ -82,15 +99,36 @@ public class GaeaStructure implements Serializable {
|
||||
structureInfo = new GaeaStructureInfo(l2.getBlockX()-l1.getBlockX()+1, l2.getBlockY()-l1.getBlockY()+1, l2.getBlockZ()-l1.getBlockZ()+1, centerX, centerZ);
|
||||
}
|
||||
|
||||
public ArrayList<StructureSpawnRequirement> getSpawns() {
|
||||
return spawns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get GaeaStructureInfo object
|
||||
* @return Structure Info
|
||||
*/
|
||||
@NotNull
|
||||
public GaeaStructureInfo getStructureInfo() {
|
||||
return structureInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Paste the structure at a Location, ignoring chunk boundaries.
|
||||
* @param origin Origin location
|
||||
* @param r Rotation
|
||||
* @param m Mirror
|
||||
*/
|
||||
public void paste(@NotNull Location origin, Rotation r, List<Mirror> m) {
|
||||
this.executeForBlocksInRange(getRange(Axis.X), getRange(Axis.Y), getRange(Axis.Z), block -> pasteBlock(block, origin, r, m), r, m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate and mirror a coordinate pair.
|
||||
* @param arr Array containing X and Z coordinates.
|
||||
* @param r Rotation
|
||||
* @param m Mirror
|
||||
* @return Rotated coordinate pair
|
||||
*/
|
||||
private int[] getRotatedCoords(int[] arr, Rotation r, List<Mirror> m) {
|
||||
int[] cp = Arrays.copyOf(arr, 2);
|
||||
switch(r) {
|
||||
@ -112,34 +150,6 @@ public class GaeaStructure implements Serializable {
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inverse of {@link GaeaStructure#getRotatedCoords(int[], Rotation, List)}, gets the coordinates <i>before</i> rotation
|
||||
* @param arr 2-integer array holding X and Z coordinates
|
||||
* @param r Rotation
|
||||
* @param m Mirror
|
||||
* @return New coordinates
|
||||
*/
|
||||
private int[] getOriginalCoords(int[] arr, Rotation r, List<Mirror> m) {
|
||||
int[] cp = Arrays.copyOf(arr, 2);
|
||||
switch(r) {
|
||||
case CW_90:
|
||||
arr[1] = cp[0];
|
||||
arr[0] = - cp[1];
|
||||
break;
|
||||
case CCW_90:
|
||||
arr[1] = - cp[0];
|
||||
arr[0] = cp[1];
|
||||
break;
|
||||
case CW_180:
|
||||
arr[1] = - cp[1];
|
||||
arr[0] = - cp[0];
|
||||
break;
|
||||
}
|
||||
if(m.contains(Mirror.X)) arr[0] = - arr[0];
|
||||
if(m.contains(Mirror.Z)) arr[1] = - arr[1];
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the BlockFace with rotation and mirrors applied to it
|
||||
* @param f BlockFace to apply rotation to
|
||||
@ -246,11 +256,9 @@ public class GaeaStructure implements Serializable {
|
||||
if(!data.getMaterial().equals(Material.STRUCTURE_VOID)) {
|
||||
if(data instanceof Rotatable) {
|
||||
BlockFace rt = getRotatedFace(((Rotatable) data).getRotation(), r, m);
|
||||
Bukkit.getLogger().info("Previous: " + ((Rotatable) data).getRotation() + ", New: " + rt);
|
||||
((Rotatable) data).setRotation(rt);
|
||||
} else if(data instanceof Directional) {
|
||||
BlockFace rt = getRotatedFace(((Directional) data).getFacing(), r, m);
|
||||
Bukkit.getLogger().info("Previous: " + ((Directional) data).getFacing() + ", New: " + rt);
|
||||
((Directional) data).setFacing(rt);
|
||||
}
|
||||
worldBlock.setBlockData(data, false);
|
||||
@ -276,7 +284,6 @@ public class GaeaStructure implements Serializable {
|
||||
int[] c = getRotatedCoords(new int[] {x-structureInfo.getCenterX(), z-structureInfo.getCenterZ()}, r, m);
|
||||
c[0] = c[0] + structureInfo.getCenterX();
|
||||
c[1] = c[1] + structureInfo.getCenterZ();
|
||||
Bukkit.getLogger().info("Before: " + x + ", " + z + " After: " + c[0] + ", " + c[1]);
|
||||
if(isInStructure(c[0], y, c[1])) {
|
||||
StructureContainedBlock b = structure[c[0]][c[1]][y];
|
||||
exec.accept(new StructureContainedBlock(x - getStructureInfo().getCenterX(), y, z - getStructureInfo().getCenterZ(), b.getState(), b.getBlockData()));
|
||||
@ -297,6 +304,15 @@ public class GaeaStructure implements Serializable {
|
||||
return x < structureInfo.getSizeX() && y < structureInfo.getSizeY() && z < structureInfo.getSizeZ() && x >= 0 && y >= 0 && z >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* From an origin location (First bound) fetch the second bound.
|
||||
* @param origin Origin location
|
||||
* @return Other bound location
|
||||
*/
|
||||
public Location getOtherBound(Location origin) {
|
||||
return origin.clone().add(structureInfo.getSizeX(), structureInfo.getSizeY(), structureInfo.getSizeZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the structure to a file
|
||||
* @param f File to save to
|
||||
|
@ -0,0 +1,79 @@
|
||||
package com.dfsek.terra.structure;
|
||||
|
||||
import com.dfsek.terra.biome.TerraBiomeGrid;
|
||||
import com.dfsek.terra.biome.UserDefinedBiome;
|
||||
import com.dfsek.terra.config.genconfig.BiomeConfig;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.polydev.gaea.biome.Generator;
|
||||
import org.polydev.gaea.generation.GenerationPhase;
|
||||
import org.polydev.gaea.math.FastNoise;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class StructureSpawnRequirement implements Serializable {
|
||||
private static final long serialVersionUID = -175639605885943679L;
|
||||
private static final transient Map<World, FastNoise> noiseMap = new HashMap<>();
|
||||
private final RequirementType type;
|
||||
private final int x, y, z;
|
||||
|
||||
public StructureSpawnRequirement(RequirementType type, int x, int y, int z) {
|
||||
this.type = type;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public RequirementType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public int getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
public boolean isValidSpawn(Location origin) {
|
||||
return type.matches(origin.getWorld(), origin.getBlockX()+x, origin.getBlockY()+y, origin.getBlockZ()+z);
|
||||
}
|
||||
|
||||
public enum RequirementType {
|
||||
AIR {
|
||||
@Override
|
||||
public boolean matches(World w, int x, int y, int z) {
|
||||
UserDefinedBiome b = (UserDefinedBiome) TerraBiomeGrid.fromWorld(w).getBiome(x, z, GenerationPhase.POPULATE);
|
||||
if(y <= BiomeConfig.fromBiome(b).getSeaLevel()) return false;
|
||||
return b.getGenerator().getNoise(getNoise(w), w, x, y, z) <= 0;
|
||||
}
|
||||
}, OCEAN {
|
||||
@Override
|
||||
public boolean matches(World w, int x, int y, int z) {
|
||||
UserDefinedBiome b = (UserDefinedBiome) TerraBiomeGrid.fromWorld(w).getBiome(x, z, GenerationPhase.POPULATE);
|
||||
if(y > BiomeConfig.fromBiome(b).getSeaLevel()) return false;
|
||||
return b.getGenerator().getNoise(getNoise(w), w, x, y, z) <= 0;
|
||||
}
|
||||
}, LAND {
|
||||
@Override
|
||||
public boolean matches(World w, int x, int y, int z) {
|
||||
UserDefinedBiome b = (UserDefinedBiome) TerraBiomeGrid.fromWorld(w).getBiome(x, z, GenerationPhase.POPULATE);
|
||||
return b.getGenerator().getNoise(getNoise(w), w, x, y, z) > 0;
|
||||
}
|
||||
};
|
||||
public abstract boolean matches(World w, int x, int y, int z);
|
||||
}
|
||||
public static void putNoise(World w, FastNoise noise) {
|
||||
noiseMap.putIfAbsent(w, noise);
|
||||
}
|
||||
private static FastNoise getNoise(World w) {
|
||||
return noiseMap.get(w);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user