Fix structure spawn requirements, begin work on custom trees

This commit is contained in:
dfsek 2020-10-08 01:47:34 -07:00
parent fc53064ee1
commit e6f6a63194
8 changed files with 138 additions and 100 deletions

View File

@ -90,10 +90,6 @@ public class AsyncStructureFinder implements Runnable {
main: for(int y = target.getSearchStart().get(r2); y > 0; y--) { main: for(int y = target.getSearchStart().get(r2); y > 0; y--) {
spawn.setY(y); spawn.setY(y);
if(y > target.getBound().getMax() || y < target.getBound().getMin()) return false; 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; return true;
} }

View File

@ -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")); 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); if("true".equals(args[2])) struc.paste(sender.getLocation(), r);
else struc.paste(sender.getLocation(), sender.getLocation().getChunk(), r); else struc.paste(sender.getLocation(), sender.getLocation().getChunk(), r);
//sender.sendMessage(String.valueOf(struc.checkSpawns(sender.getLocation(), r)));
} catch(IOException e) { } catch(IOException e) {
e.printStackTrace(); e.printStackTrace();
LangUtil.send("command.structure.invalid", sender, args[0]); LangUtil.send("command.structure.invalid", sender, args[0]);

View File

@ -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;
}
}

View File

@ -34,17 +34,15 @@ public class StructurePopulator extends BlockPopulator {
Location spawn = conf.getSpawn().getNearestSpawn(cx + 8, cz + 8, world.getSeed()).toLocation(world); Location spawn = conf.getSpawn().getNearestSpawn(cx + 8, cz + 8, world.getSeed()).toLocation(world);
Random r2 = new Random(spawn.hashCode()); Random r2 = new Random(spawn.hashCode());
GaeaStructure struc = conf.getStructure(r2); 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--) { main: for(int y = conf.getSearchStart().get(r2); y > 0; y--) {
if(y > conf.getBound().getMax() || y < conf.getBound().getMin()) continue structure; if(y > conf.getBound().getMax() || y < conf.getBound().getMin()) continue structure;
spawn.setY(y); spawn.setY(y);
for(StructureSpawnRequirement s : struc.getSpawns()) { if(!struc.checkSpawns(spawn, rotation)) continue;
if(! s.isValidSpawn(spawn)) continue main;
if(!b.equals(grid.getBiome(spawn.clone().add(s.getX(), s.getY(), s.getZ()), GenerationPhase.POPULATE))) continue structure;
}
double horizontal = struc.getStructureInfo().getMaxHorizontal(); double horizontal = struc.getStructureInfo().getMaxHorizontal();
if(Math.abs((cx + 8) - spawn.getBlockX()) <= horizontal && Math.abs((cz + 8) - spawn.getBlockZ()) <= horizontal) { if(Math.abs((cx + 8) - spawn.getBlockX()) <= horizontal && Math.abs((cz + 8) - spawn.getBlockZ()) <= horizontal) {
try(ProfileFuture ignore = TerraProfiler.fromWorld(world).measure("StructurePasteTime")) { 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; break;
} }
} }

View File

@ -31,8 +31,10 @@ import java.io.ObjectOutputStream;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -41,10 +43,10 @@ import static com.dfsek.terra.util.structure.RotationUtil.*;
public class GaeaStructure implements Serializable { public class GaeaStructure implements Serializable {
public static final long serialVersionUID = -6664585217063842035L; public static final long serialVersionUID = -6664585217063842035L;
private final StructureContainedBlock[][][] structure; private final StructureContainedBlock[][][] structure;
private final ArrayList<StructureSpawnRequirement> spawns = new ArrayList<>();
private final GaeaStructureInfo structureInfo; private final GaeaStructureInfo structureInfo;
private final String id; private final String id;
private final UUID uuid; private final UUID uuid;
private final HashSet<StructureContainedBlock> spawns;
@NotNull @NotNull
public static GaeaStructure load(@NotNull File f) throws IOException { 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 { 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.id = id;
this.uuid = UUID.randomUUID(); 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!"); 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]; 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++) { for(int x = 0; x <= l2.getBlockX()-l1.getBlockX(); x++) {
@ -68,6 +71,7 @@ public class GaeaStructure implements Serializable {
BlockState state = b.getState(); BlockState state = b.getState();
BlockData d = b.getBlockData(); BlockData d = b.getBlockData();
boolean useState = true; boolean useState = true;
StructureSpawnRequirement requirement = StructureSpawnRequirement.BLANK;
if(state instanceof Sign) { if(state instanceof Sign) {
Sign s = (Sign) b.getState(); Sign s = (Sign) b.getState();
if(s.getLine(0).equals("[TERRA]")) { if(s.getLine(0).equals("[TERRA]")) {
@ -76,22 +80,14 @@ public class GaeaStructure implements Serializable {
useState = false; useState = false;
if(s.getLine(1).equals("[CENTER]")) { if(s.getLine(1).equals("[CENTER]")) {
centerX = x; centerX = x;
centerY = y;
centerZ = z; centerZ = z;
} else if(s.getLine(1).startsWith("[SPAWN=") && s.getLine(1).endsWith("]")) { } else if(s.getLine(1).startsWith("[SPAWN=") && s.getLine(1).endsWith("]")) {
String og = s.getLine(1); String og = s.getLine(1);
String spawn = og.substring(og.indexOf("=")+1, og.length()-1); String spawn = og.substring(og.indexOf("=")+1, og.length()-1);
switch(spawn.toUpperCase()) { try {
case "LAND": requirement = StructureSpawnRequirement.valueOf(spawn.toUpperCase());
spawns.add(new StructureSpawnRequirement(StructureSpawnRequirement.RequirementType.LAND, x, y, z)); } catch(IllegalArgumentException e) {
break; throw new InitializationException("Invalid spawn type: " + spawn);
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) { } 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."); 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 Vector(centerX, centerY, centerZ)); structureInfo = new GaeaStructureInfo(l2.getBlockX()-l1.getBlockX()+1, l2.getBlockY()-l1.getBlockY()+1, l2.getBlockZ()-l1.getBlockZ()+1, new Vector2(centerX, centerZ));
}
public ArrayList<StructureSpawnRequirement> getSpawns() {
return spawns;
} }
/** /**
@ -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); 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) { private void pasteBlock(StructureContainedBlock block, Location origin, Rotation r) {
BlockData data = block.getBlockData().clone(); 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.getMaterial().equals(Material.STRUCTURE_VOID)) {
if(data instanceof Rotatable) { if(data instanceof Rotatable) {
BlockFace rt = getRotatedFace(((Rotatable) data).getRotation(), r); BlockFace rt = getRotatedFace(((Rotatable) data).getRotation(), r);
@ -203,7 +205,7 @@ public class GaeaStructure implements Serializable {
c.add(new Vector2(structureInfo.getCenterX(), structureInfo.getCenterZ())); c.add(new Vector2(structureInfo.getCenterX(), structureInfo.getCenterZ()));
if(isInStructure((int) c.getX(), y, (int) c.getZ())) { if(isInStructure((int) c.getX(), y, (int) c.getZ())) {
StructureContainedBlock b = structure[(int) c.getX()][(int) c.getZ()][y]; 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()));
} }
} }
} }

View File

@ -1,5 +1,6 @@
package com.dfsek.terra.structure; package com.dfsek.terra.structure;
import com.dfsek.terra.procgen.math.Vector2;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import java.io.Serializable; import java.io.Serializable;
@ -11,14 +12,12 @@ public class GaeaStructureInfo implements Serializable {
private final int sizeZ; private final int sizeZ;
private final int centerX; private final int centerX;
private final int centerZ; private final int centerZ;
private final int centerY; public GaeaStructureInfo(int sizeX, int sizeY, int sizeZ, Vector2 center) {
public GaeaStructureInfo(int sizeX, int sizeY, int sizeZ, Vector center) {
this.sizeX = sizeX; this.sizeX = sizeX;
this.sizeY = sizeY; this.sizeY = sizeY;
this.sizeZ = sizeZ; this.sizeZ = sizeZ;
this.centerX = center.getBlockX(); this.centerX = (int) center.getX();
this.centerZ = center.getBlockZ(); this.centerZ = (int) center.getZ();
this.centerY = center.getBlockY();
} }
public int getSizeX() { public int getSizeX() {
@ -37,10 +36,6 @@ public class GaeaStructureInfo implements Serializable {
return centerX; return centerX;
} }
public int getCenterY() {
return centerY;
}
public int getCenterZ() { public int getCenterZ() {
return centerZ; return centerZ;
} }

View File

@ -18,7 +18,8 @@ public class StructureContainedBlock implements Serializable {
private final int y; private final int y;
private final int z; private final int z;
private final SerializableBlockState state; 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) { if(state instanceof Sign) {
this.state = new SerializableSign((org.bukkit.block.Sign) state); this.state = new SerializableSign((org.bukkit.block.Sign) state);
} else this.state = null; } else this.state = null;
@ -26,8 +27,9 @@ public class StructureContainedBlock implements Serializable {
this.y = y; this.y = y;
this.z = z; this.z = z;
this.bl = new SerializableBlockData(d); 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) { if(state instanceof SerializableSign) {
this.state = state; this.state = state;
} else this.state = null; } else this.state = null;
@ -35,6 +37,11 @@ public class StructureContainedBlock implements Serializable {
this.y = y; this.y = y;
this.z = z; this.z = z;
this.bl = new SerializableBlockData(d); this.bl = new SerializableBlockData(d);
this.requirement = spawn;
}
public StructureSpawnRequirement getRequirement() {
return requirement;
} }
public int getX() { public int getX() {

View File

@ -12,65 +12,38 @@ import java.io.Serializable;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
public class StructureSpawnRequirement implements Serializable { public enum StructureSpawnRequirement implements Serializable {
AIR {
@Override
public boolean matches(World w, int x, int y, int z) {
UserDefinedBiome b = (UserDefinedBiome) TerraWorld.getWorld(w).getGrid().getBiome(x, z, GenerationPhase.POPULATE);
BiomeConfig c = TerraWorld.getWorld(w).getConfig().getBiome(b);
if(y <= c.getOcean().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) TerraWorld.getWorld(w).getGrid().getBiome(x, z, GenerationPhase.POPULATE);
BiomeConfig c = TerraWorld.getWorld(w).getConfig().getBiome(b);
if(y > c.getOcean().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) TerraWorld.getWorld(w).getGrid().getBiome(x, z, GenerationPhase.POPULATE);
return b.getGenerator().getNoise(getNoise(w), w, x, y, z) > 0;
}
}, BLANK {
@Override
public boolean matches(World w, int x, int y, int z) {
return true;
}
};
private static final long serialVersionUID = -175639605885943679L; private static final long serialVersionUID = -175639605885943679L;
private static final transient Map<World, FastNoise> noiseMap = new HashMap<>(); private static final transient Map<World, FastNoise> noiseMap = new HashMap<>();
private final RequirementType type; public abstract boolean matches(World w, int x, int y, int z);
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) TerraWorld.getWorld(w).getGrid().getBiome(x, z, GenerationPhase.POPULATE);
BiomeConfig c = TerraWorld.getWorld(w).getConfig().getBiome(b);
if(y <= c.getOcean().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) TerraWorld.getWorld(w).getGrid().getBiome(x, z, GenerationPhase.POPULATE);
BiomeConfig c = TerraWorld.getWorld(w).getConfig().getBiome(b);
if(y > c.getOcean().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) 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);
}
public static void putNoise(World w, FastNoise noise) { public static void putNoise(World w, FastNoise noise) {
noiseMap.putIfAbsent(w, noise); noiseMap.putIfAbsent(w, noise);
} }