mirror of
https://github.com/PolyhedralDev/Terra.git
synced 2025-07-18 10:32:30 +00:00
Fix structure spawn requirements, begin work on custom trees
This commit is contained in:
parent
fc53064ee1
commit
e6f6a63194
@ -90,10 +90,6 @@ public class AsyncStructureFinder implements Runnable {
|
||||
main: for(int y = target.getSearchStart().get(r2); y > 0; y--) {
|
||||
spawn.setY(y);
|
||||
if(y > target.getBound().getMax() || y < target.getBound().getMin()) return false;
|
||||
for(StructureSpawnRequirement s : struc.getSpawns()) {
|
||||
if(! s.isValidSpawn(spawn)) continue main; // Probably(tm) async safe
|
||||
if(!b.equals(grid.getBiome(spawn.clone().add(s.getX(), s.getY(), s.getZ()), GenerationPhase.POPULATE))) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ public class LoadCommand extends PlayerCommand {
|
||||
GaeaStructure struc = GaeaStructure.load(new File(Terra.getInstance().getDataFolder() + File.separator + "export" + File.separator + "structures", args[0] + ".tstructure"));
|
||||
if("true".equals(args[2])) struc.paste(sender.getLocation(), r);
|
||||
else struc.paste(sender.getLocation(), sender.getLocation().getChunk(), r);
|
||||
//sender.sendMessage(String.valueOf(struc.checkSpawns(sender.getLocation(), r)));
|
||||
} catch(IOException e) {
|
||||
e.printStackTrace();
|
||||
LangUtil.send("command.structure.invalid", sender, args[0]);
|
||||
|
@ -0,0 +1,66 @@
|
||||
package com.dfsek.terra.config.genconfig;
|
||||
|
||||
import com.dfsek.terra.Debug;
|
||||
import com.dfsek.terra.config.TerraConfig;
|
||||
import com.dfsek.terra.config.base.ConfigPack;
|
||||
import com.dfsek.terra.config.base.ConfigUtil;
|
||||
import com.dfsek.terra.config.exception.ConfigException;
|
||||
import com.dfsek.terra.config.exception.NotFoundException;
|
||||
import com.dfsek.terra.structure.GaeaStructure;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.configuration.InvalidConfigurationException;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.polydev.gaea.math.ProbabilityCollection;
|
||||
import org.polydev.gaea.tree.Tree;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
public class TreeConfig extends TerraConfig implements Tree {
|
||||
private final Set<Material> spawnable;
|
||||
private final String id;
|
||||
private final ProbabilityCollection<GaeaStructure> structure = new ProbabilityCollection<>();
|
||||
public TreeConfig(File file, ConfigPack config) throws IOException, InvalidConfigurationException {
|
||||
super(file, config);
|
||||
spawnable = ConfigUtil.toBlockData(getStringList("spawnable"), "spawnable", getID());
|
||||
if(!contains("id")) throw new ConfigException("No ID specified!", "null");
|
||||
id = getString("id");
|
||||
if(!contains("files")) throw new ConfigException("No files specified!", getID());
|
||||
try {
|
||||
for(Map.Entry<String, Object> e : Objects.requireNonNull(getConfigurationSection("files")).getValues(false).entrySet()) {
|
||||
try {
|
||||
File structureFile = new File(config.getDataFolder() + File.separator + "trees" + File.separator + "data", e.getKey() + ".tstructure");
|
||||
structure.add(GaeaStructure.load(structureFile), (Integer) e.getValue());
|
||||
} catch(FileNotFoundException ex) {
|
||||
Debug.stack(ex);
|
||||
throw new NotFoundException("Tree Structure File", e.getKey(), getID());
|
||||
} catch(ClassCastException ex) {
|
||||
Debug.stack(ex);
|
||||
throw new ConfigException("Unable to parse Tree configuration! Check YAML syntax.", getID());
|
||||
}
|
||||
}
|
||||
} catch(IOException | NullPointerException e) {
|
||||
if(ConfigUtil.debug) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
throw new NotFoundException("Tree Structure", getString("file"), getID());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean plant(Location location, Random random, boolean b, JavaPlugin javaPlugin) {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -34,17 +34,15 @@ public class StructurePopulator extends BlockPopulator {
|
||||
Location spawn = conf.getSpawn().getNearestSpawn(cx + 8, cz + 8, world.getSeed()).toLocation(world);
|
||||
Random r2 = new Random(spawn.hashCode());
|
||||
GaeaStructure struc = conf.getStructure(r2);
|
||||
GaeaStructure.Rotation rotation = GaeaStructure.Rotation.fromDegrees(r2.nextInt(4) * 90);
|
||||
main: for(int y = conf.getSearchStart().get(r2); y > 0; y--) {
|
||||
if(y > conf.getBound().getMax() || y < conf.getBound().getMin()) continue structure;
|
||||
spawn.setY(y);
|
||||
for(StructureSpawnRequirement s : struc.getSpawns()) {
|
||||
if(! s.isValidSpawn(spawn)) continue main;
|
||||
if(!b.equals(grid.getBiome(spawn.clone().add(s.getX(), s.getY(), s.getZ()), GenerationPhase.POPULATE))) continue structure;
|
||||
}
|
||||
if(!struc.checkSpawns(spawn, rotation)) continue;
|
||||
double horizontal = struc.getStructureInfo().getMaxHorizontal();
|
||||
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(r2.nextInt(4) * 90));
|
||||
struc.paste(spawn, chunk, rotation);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -31,8 +31,10 @@ import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@ -41,10 +43,10 @@ import static com.dfsek.terra.util.structure.RotationUtil.*;
|
||||
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;
|
||||
private final HashSet<StructureContainedBlock> spawns;
|
||||
|
||||
@NotNull
|
||||
public static GaeaStructure load(@NotNull File f) throws IOException {
|
||||
@ -56,9 +58,10 @@ public class GaeaStructure implements Serializable {
|
||||
}
|
||||
|
||||
public GaeaStructure(@NotNull Location l1, @NotNull Location l2, @NotNull String id) throws InitializationException {
|
||||
int centerX = -1, centerY = -1, centerZ = -1;
|
||||
int centerX = -1, centerZ = -1;
|
||||
this.id = id;
|
||||
this.uuid = UUID.randomUUID();
|
||||
this.spawns = new HashSet<>();
|
||||
if(l1.getX() > l2.getX() || l1.getY() > l2.getY() || l1.getZ() > l2.getZ()) throw new IllegalArgumentException("Invalid locations provided!");
|
||||
structure = new StructureContainedBlock[l2.getBlockX()-l1.getBlockX()+1][l2.getBlockZ()-l1.getBlockZ()+1][l2.getBlockY()-l1.getBlockY()+1];
|
||||
for(int x = 0; x <= l2.getBlockX()-l1.getBlockX(); x++) {
|
||||
@ -68,6 +71,7 @@ public class GaeaStructure implements Serializable {
|
||||
BlockState state = b.getState();
|
||||
BlockData d = b.getBlockData();
|
||||
boolean useState = true;
|
||||
StructureSpawnRequirement requirement = StructureSpawnRequirement.BLANK;
|
||||
if(state instanceof Sign) {
|
||||
Sign s = (Sign) b.getState();
|
||||
if(s.getLine(0).equals("[TERRA]")) {
|
||||
@ -76,22 +80,14 @@ public class GaeaStructure implements Serializable {
|
||||
useState = false;
|
||||
if(s.getLine(1).equals("[CENTER]")) {
|
||||
centerX = x;
|
||||
centerY = y;
|
||||
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);
|
||||
try {
|
||||
requirement = StructureSpawnRequirement.valueOf(spawn.toUpperCase());
|
||||
} catch(IllegalArgumentException e) {
|
||||
throw new InitializationException("Invalid spawn type: " + spawn);
|
||||
}
|
||||
}
|
||||
} catch(IllegalArgumentException e) {
|
||||
@ -99,16 +95,14 @@ public class GaeaStructure implements Serializable {
|
||||
}
|
||||
}
|
||||
}
|
||||
structure[x][z][y] = new StructureContainedBlock(x, y, z, useState ? state : null, d);
|
||||
StructureContainedBlock block = new StructureContainedBlock(x, y, z, useState ? state : null, d, requirement);
|
||||
if(!requirement.equals(StructureSpawnRequirement.BLANK)) spawns.add(block);
|
||||
structure[x][z][y] = block;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(centerX < 0 || centerY < 0 || centerZ < 0) throw new InitializationException("No structure center specified.");
|
||||
structureInfo = new GaeaStructureInfo(l2.getBlockX()-l1.getBlockX()+1, l2.getBlockY()-l1.getBlockY()+1, l2.getBlockZ()-l1.getBlockZ()+1, new Vector(centerX, centerY, centerZ));
|
||||
}
|
||||
|
||||
public ArrayList<StructureSpawnRequirement> getSpawns() {
|
||||
return spawns;
|
||||
if(centerX < 0 || centerZ < 0) throw new InitializationException("No structure center specified.");
|
||||
structureInfo = new GaeaStructureInfo(l2.getBlockX()-l1.getBlockX()+1, l2.getBlockY()-l1.getBlockY()+1, l2.getBlockZ()-l1.getBlockZ()+1, new Vector2(centerX, centerZ));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -129,6 +123,13 @@ public class GaeaStructure implements Serializable {
|
||||
this.executeForBlocksInRange(getRange(Axis.X), getRange(Axis.Y), getRange(Axis.Z), block -> pasteBlock(block, origin, r), r);
|
||||
}
|
||||
|
||||
public boolean checkSpawns(Location origin, Rotation r) {
|
||||
for(StructureContainedBlock b : spawns) {
|
||||
Vector2 rot = getRotatedCoords(new Vector2(b.getX()-structureInfo.getCenterX(), b.getZ()-structureInfo.getCenterZ()), r);
|
||||
if(!b.getRequirement().matches(origin.getWorld(), (int) rot.getX()+origin.getBlockX(), origin.getBlockY(), (int) rot.getZ()+origin.getBlockZ())) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -158,7 +159,8 @@ public class GaeaStructure implements Serializable {
|
||||
*/
|
||||
private void pasteBlock(StructureContainedBlock block, Location origin, Rotation r) {
|
||||
BlockData data = block.getBlockData().clone();
|
||||
Block worldBlock = origin.clone().add(block.getX(), block.getY(), block.getZ()).getBlock();
|
||||
Location loc = origin.clone().add(block.getX(), block.getY(), block.getZ());
|
||||
Block worldBlock = loc.getBlock();
|
||||
if(!data.getMaterial().equals(Material.STRUCTURE_VOID)) {
|
||||
if(data instanceof Rotatable) {
|
||||
BlockFace rt = getRotatedFace(((Rotatable) data).getRotation(), r);
|
||||
@ -203,7 +205,7 @@ public class GaeaStructure implements Serializable {
|
||||
c.add(new Vector2(structureInfo.getCenterX(), structureInfo.getCenterZ()));
|
||||
if(isInStructure((int) c.getX(), y, (int) c.getZ())) {
|
||||
StructureContainedBlock b = structure[(int) c.getX()][(int) c.getZ()][y];
|
||||
exec.accept(new StructureContainedBlock(x - getStructureInfo().getCenterX(), y, z - getStructureInfo().getCenterZ(), b.getState(), b.getBlockData()));
|
||||
exec.accept(new StructureContainedBlock(x - getStructureInfo().getCenterX(), y, z - getStructureInfo().getCenterZ(), b.getState(), b.getBlockData(), b.getRequirement()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.dfsek.terra.structure;
|
||||
|
||||
import com.dfsek.terra.procgen.math.Vector2;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.io.Serializable;
|
||||
@ -11,14 +12,12 @@ public class GaeaStructureInfo implements Serializable {
|
||||
private final int sizeZ;
|
||||
private final int centerX;
|
||||
private final int centerZ;
|
||||
private final int centerY;
|
||||
public GaeaStructureInfo(int sizeX, int sizeY, int sizeZ, Vector center) {
|
||||
public GaeaStructureInfo(int sizeX, int sizeY, int sizeZ, Vector2 center) {
|
||||
this.sizeX = sizeX;
|
||||
this.sizeY = sizeY;
|
||||
this.sizeZ = sizeZ;
|
||||
this.centerX = center.getBlockX();
|
||||
this.centerZ = center.getBlockZ();
|
||||
this.centerY = center.getBlockY();
|
||||
this.centerX = (int) center.getX();
|
||||
this.centerZ = (int) center.getZ();
|
||||
}
|
||||
|
||||
public int getSizeX() {
|
||||
@ -37,10 +36,6 @@ public class GaeaStructureInfo implements Serializable {
|
||||
return centerX;
|
||||
}
|
||||
|
||||
public int getCenterY() {
|
||||
return centerY;
|
||||
}
|
||||
|
||||
public int getCenterZ() {
|
||||
return centerZ;
|
||||
}
|
||||
|
@ -18,7 +18,8 @@ public class StructureContainedBlock implements Serializable {
|
||||
private final int y;
|
||||
private final int z;
|
||||
private final SerializableBlockState state;
|
||||
public StructureContainedBlock(int x, int y, int z, BlockState state, BlockData d) {
|
||||
private final StructureSpawnRequirement requirement;
|
||||
public StructureContainedBlock(int x, int y, int z, BlockState state, BlockData d, StructureSpawnRequirement spawn) {
|
||||
if(state instanceof Sign) {
|
||||
this.state = new SerializableSign((org.bukkit.block.Sign) state);
|
||||
} else this.state = null;
|
||||
@ -26,8 +27,9 @@ public class StructureContainedBlock implements Serializable {
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.bl = new SerializableBlockData(d);
|
||||
this.requirement = spawn;
|
||||
}
|
||||
public StructureContainedBlock(int x, int y, int z, SerializableBlockState state, BlockData d) {
|
||||
public StructureContainedBlock(int x, int y, int z, SerializableBlockState state, BlockData d, StructureSpawnRequirement spawn) {
|
||||
if(state instanceof SerializableSign) {
|
||||
this.state = state;
|
||||
} else this.state = null;
|
||||
@ -35,6 +37,11 @@ public class StructureContainedBlock implements Serializable {
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.bl = new SerializableBlockData(d);
|
||||
this.requirement = spawn;
|
||||
}
|
||||
|
||||
public StructureSpawnRequirement getRequirement() {
|
||||
return requirement;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
|
@ -12,40 +12,7 @@ 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 {
|
||||
public enum StructureSpawnRequirement implements Serializable {
|
||||
AIR {
|
||||
@Override
|
||||
public boolean matches(World w, int x, int y, int z) {
|
||||
@ -68,9 +35,15 @@ public class StructureSpawnRequirement implements Serializable {
|
||||
UserDefinedBiome b = (UserDefinedBiome) TerraWorld.getWorld(w).getGrid().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);
|
||||
}, BLANK {
|
||||
@Override
|
||||
public boolean matches(World w, int x, int y, int z) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
private static final long serialVersionUID = -175639605885943679L;
|
||||
private static final transient Map<World, FastNoise> noiseMap = new HashMap<>();
|
||||
public abstract boolean matches(World w, int x, int y, int z);
|
||||
public static void putNoise(World w, FastNoise noise) {
|
||||
noiseMap.putIfAbsent(w, noise);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user