mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-01-04 15:41:30 +00:00
@@ -21,6 +21,7 @@ package com.volmit.iris.core.commands;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.edit.JigsawEditor;
|
||||
import com.volmit.iris.core.loader.IrisData;
|
||||
import com.volmit.iris.engine.framework.placer.WorldObjectPlacer;
|
||||
import com.volmit.iris.engine.jigsaw.PlannedStructure;
|
||||
import com.volmit.iris.engine.object.IrisJigsawPiece;
|
||||
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
||||
@@ -56,10 +57,16 @@ public class CommandJigsaw implements DecreeExecutor {
|
||||
IrisJigsawStructure structure
|
||||
) {
|
||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||
PlannedStructure ps = new PlannedStructure(structure, new IrisPosition(player().getLocation()), new RNG());
|
||||
VolmitSender sender = sender();
|
||||
sender.sendMessage(C.GREEN + "Generated " + ps.getPieces().size() + " pieces in " + Form.duration(p.getMilliseconds(), 2));
|
||||
ps.place(world(), failed -> sender.sendMessage(failed == 0 ? C.GREEN + "Placed the structure!" : C.RED + "Failed to place " + failed + " pieces!"));
|
||||
try {
|
||||
var world = world();
|
||||
WorldObjectPlacer placer = new WorldObjectPlacer(world);
|
||||
PlannedStructure ps = new PlannedStructure(structure, new IrisPosition(player().getLocation().add(0, world.getMinHeight(), 0)), new RNG());
|
||||
VolmitSender sender = sender();
|
||||
sender.sendMessage(C.GREEN + "Generated " + ps.getPieces().size() + " pieces in " + Form.duration(p.getMilliseconds(), 2));
|
||||
ps.place(placer, failed -> sender.sendMessage(failed ? C.GREEN + "Placed the structure!" : C.RED + "Failed to place the structure!"));
|
||||
} catch (IllegalArgumentException e) {
|
||||
sender().sendMessage(C.RED + "Failed to place the structure: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Decree(description = "Create a jigsaw piece")
|
||||
|
||||
@@ -197,6 +197,20 @@ public class CommandObject implements DecreeExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
@Decree(description = "Shrink an object to its minimum size")
|
||||
public void shrink(@Param(description = "The object to shrink", customHandler = ObjectHandler.class) String object) {
|
||||
IrisObject o = IrisData.loadAnyObject(object);
|
||||
sender().sendMessage("Current Object Size: " + o.getW() + " * " + o.getH() + " * " + o.getD());
|
||||
o.shrinkwrap();
|
||||
sender().sendMessage("New Object Size: " + o.getW() + " * " + o.getH() + " * " + o.getD());
|
||||
try {
|
||||
o.write(o.getLoadFile());
|
||||
} catch (IOException e) {
|
||||
sender().sendMessage("Failed to save object " + o.getLoadFile() + ": " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Decree(description = "Get a powder that reveals objects", studio = true, aliases = "d")
|
||||
public void dust() {
|
||||
player().getInventory().addItem(WandSVC.createDust());
|
||||
|
||||
@@ -79,6 +79,7 @@ import java.nio.file.Files;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
@@ -346,6 +347,63 @@ public class CommandStudio implements DecreeExecutor {
|
||||
player().openInventory(inv);
|
||||
}
|
||||
|
||||
|
||||
@Decree(description = "Get all structures in a radius of chunks", aliases = "dist", origin = DecreeOrigin.PLAYER)
|
||||
public void distances(@Param(description = "The radius") int radius) {
|
||||
var engine = engine();
|
||||
if (engine == null) {
|
||||
sender().sendMessage(C.RED + "Only works in an Iris world!");
|
||||
return;
|
||||
}
|
||||
var sender = sender();
|
||||
int d = radius*2;
|
||||
KMap<String, KList<Position2>> data = new KMap<>();
|
||||
var multiBurst = new MultiBurst("Distance Sampler", Thread.MIN_PRIORITY);
|
||||
var executor = multiBurst.burst(radius * radius);
|
||||
|
||||
sender.sendMessage(C.GRAY + "Generating data...");
|
||||
var loc = player().getLocation();
|
||||
new Spiraler(d, d, (x, z) -> executor.queue(() -> {
|
||||
var struct = engine.getStructureAt(x, z);
|
||||
if (struct != null) {
|
||||
data.computeIfAbsent(struct.getLoadKey(), (k) -> new KList<>()).add(new Position2(x, z));
|
||||
}
|
||||
})).setOffset(loc.getBlockX(), loc.getBlockZ()).drain();
|
||||
|
||||
executor.complete();
|
||||
multiBurst.close();
|
||||
for (var key : data.keySet()) {
|
||||
var list = data.get(key);
|
||||
KList<Long> distances = new KList<>(list.size() - 1);
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
var pos = list.get(i);
|
||||
double dist = Integer.MAX_VALUE;
|
||||
for (var p : list) {
|
||||
if (p.equals(pos)) continue;
|
||||
dist = Math.min(dist, Math.sqrt(Math.pow(pos.getX() - p.getX(), 2) + Math.pow(pos.getZ() - p.getZ(), 2)));
|
||||
}
|
||||
if (dist == Integer.MAX_VALUE) continue;
|
||||
distances.add(Math.round(dist * 16));
|
||||
}
|
||||
long[] array = new long[distances.size()];
|
||||
for (int i = 0; i < distances.size(); i++) {
|
||||
array[i] = distances.get(i);
|
||||
}
|
||||
Arrays.sort(array);
|
||||
long min = array.length > 0 ? array[0] : 0;
|
||||
long max = array.length > 0 ? array[array.length - 1] : 0;
|
||||
long sum = Arrays.stream(array).sum();
|
||||
long avg = array.length > 0 ? Math.round(sum / (double) array.length) : 0;
|
||||
String msg = "%s: %s => min: %s/max: %s -> avg: %s".formatted(key, list.size(), min, max, avg);
|
||||
sender.sendMessage(msg);
|
||||
}
|
||||
if (data.isEmpty()) {
|
||||
sender.sendMessage(C.RED + "No data found!");
|
||||
} else {
|
||||
sender.sendMessage(C.GREEN + "Done!");
|
||||
}
|
||||
}
|
||||
|
||||
@Decree(description = "Render a world map (External GUI)", aliases = "render")
|
||||
public void map() {
|
||||
if (noGUI()) return;
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.volmit.iris.engine.framework.placer;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.loader.IrisData;
|
||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||
import com.volmit.iris.engine.data.cache.Cache;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.framework.IrisLootEvent;
|
||||
import com.volmit.iris.engine.mantle.EngineMantle;
|
||||
import com.volmit.iris.engine.object.IObjectPlacer;
|
||||
import com.volmit.iris.engine.object.InventorySlotType;
|
||||
import com.volmit.iris.engine.object.IrisLootTable;
|
||||
import com.volmit.iris.engine.object.TileData;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.data.B;
|
||||
import com.volmit.iris.util.math.RNG;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.TileState;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
|
||||
@Getter
|
||||
@EqualsAndHashCode(exclude = {"engine", "mantle"})
|
||||
public class WorldObjectPlacer implements IObjectPlacer {
|
||||
private final World world;
|
||||
private final Engine engine;
|
||||
private final EngineMantle mantle;
|
||||
|
||||
public WorldObjectPlacer(World world) {
|
||||
var a = IrisToolbelt.access(world);
|
||||
if (a == null || a.getEngine() == null) throw new IllegalStateException(world.getName() + " is not an Iris World!");
|
||||
this.world = world;
|
||||
this.engine = a.getEngine();
|
||||
this.mantle = engine.getMantle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHighest(int x, int z, IrisData data) {
|
||||
return mantle.getHighest(x, z, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHighest(int x, int z, IrisData data, boolean ignoreFluid) {
|
||||
return mantle.getHighest(x, z, data, ignoreFluid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(int x, int y, int z, BlockData d) {
|
||||
Block block = world.getBlockAt(x, y + world.getMinHeight(), z);
|
||||
|
||||
if (y <= world.getMinHeight() || block.getType() == Material.BEDROCK) return;
|
||||
InventorySlotType slot = null;
|
||||
if (B.isStorageChest(d)) {
|
||||
slot = InventorySlotType.STORAGE;
|
||||
}
|
||||
|
||||
if (slot != null) {
|
||||
RNG rx = new RNG(Cache.key(x, z));
|
||||
KList<IrisLootTable> tables = engine.getLootTables(rx, block);
|
||||
|
||||
try {
|
||||
Bukkit.getPluginManager().callEvent(new IrisLootEvent(engine, block, slot, tables));
|
||||
|
||||
if (!tables.isEmpty()){
|
||||
Iris.debug("IrisLootEvent has been accessed");
|
||||
}
|
||||
|
||||
if (tables.isEmpty())
|
||||
return;
|
||||
InventoryHolder m = (InventoryHolder) block.getState();
|
||||
engine.addItems(false, m.getInventory(), rx, tables, slot, x, y, z, 15);
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
block.setBlockData(d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockData get(int x, int y, int z) {
|
||||
return world.getBlockAt(x, y + world.getMinHeight(), z).getBlockData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPreventingDecay() {
|
||||
return mantle.isPreventingDecay();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCarved(int x, int y, int z) {
|
||||
return mantle.isCarved(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSolid(int x, int y, int z) {
|
||||
return world.getBlockAt(x, y + world.getMinHeight(), z).getType().isSolid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnderwater(int x, int z) {
|
||||
return mantle.isUnderwater(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFluidHeight() {
|
||||
return mantle.getFluidHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebugSmartBore() {
|
||||
return mantle.isDebugSmartBore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTile(int xx, int yy, int zz, TileData<? extends TileState> tile) {
|
||||
BlockState state = world.getBlockAt(xx, yy + world.getMinHeight(), zz).getState();
|
||||
tile.toBukkitTry(state);
|
||||
state.update();
|
||||
}
|
||||
}
|
||||
@@ -20,24 +20,11 @@ package com.volmit.iris.engine.jigsaw;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.loader.IrisData;
|
||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.object.*;
|
||||
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.context.IrisContext;
|
||||
import com.volmit.iris.util.math.AxisAlignedBB;
|
||||
import com.volmit.iris.util.math.BlockPosition;
|
||||
import com.volmit.iris.util.math.RNG;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.TileState;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.util.BlockVector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -97,7 +84,15 @@ public class PlannedPiece {
|
||||
}
|
||||
|
||||
BlockVector v = getObject().getCenter();
|
||||
box = object.getAABB().shifted(position.add(new IrisPosition(object.getCenter())));
|
||||
IrisPosition pos = new IrisPosition();
|
||||
IrisObjectPlacement options = piece.getPlacementOptions();
|
||||
if (options != null && options.getTranslate() != null) {
|
||||
IrisObjectTranslate translate = options.getTranslate();
|
||||
pos.setX(translate.getX());
|
||||
pos.setY(translate.getY());
|
||||
pos.setZ(translate.getZ());
|
||||
}
|
||||
box = object.getAABB().shifted(position.add(new IrisPosition(object.getCenter())).add(pos));
|
||||
return box;
|
||||
}
|
||||
|
||||
@@ -165,105 +160,6 @@ public class PlannedPiece {
|
||||
}
|
||||
|
||||
public boolean isFull() {
|
||||
return connected.size() >= piece.getConnectors().size() || isDead();
|
||||
}
|
||||
|
||||
public boolean place(World world) {
|
||||
PlatformChunkGenerator a = IrisToolbelt.access(world);
|
||||
|
||||
int minY = 0;
|
||||
if (a != null) {
|
||||
minY = a.getEngine().getMinHeight();
|
||||
|
||||
if (!a.getEngine().getDimension().isBedrock())
|
||||
minY--; //If the dimension has no bedrock, allow it to go a block lower
|
||||
}
|
||||
Engine engine = a != null ? a.getEngine() : IrisContext.get().getEngine();
|
||||
|
||||
getPiece().getPlacementOptions().setTranslate(new IrisObjectTranslate());
|
||||
getPiece().getPlacementOptions().getRotation().setEnabled(false);
|
||||
getPiece().getPlacementOptions().setRotateTowardsSlope(false);
|
||||
int finalMinY = minY;
|
||||
RNG rng = getStructure().getRng().nextParallelRNG(37555);
|
||||
|
||||
// TODO: REAL CLASSES!!!!!!!
|
||||
return getObject().place(position.getX() + getObject().getCenter().getBlockX(), position.getY() + getObject().getCenter().getBlockY(), position.getZ() + getObject().getCenter().getBlockZ(), new IObjectPlacer() {
|
||||
@Override
|
||||
public int getHighest(int x, int z, IrisData data) {
|
||||
return position.getY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHighest(int x, int z, IrisData data, boolean ignoreFluid) {
|
||||
return position.getY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(int x, int y, int z, BlockData d) {
|
||||
Block block = world.getBlockAt(x, y, z);
|
||||
|
||||
//Prevent blocks being set in or bellow bedrock
|
||||
if (y <= finalMinY || block.getType() == Material.BEDROCK) return;
|
||||
|
||||
block.setBlockData(d);
|
||||
|
||||
if (a != null && getPiece().getPlacementOptions().getLoot().isNotEmpty() &&
|
||||
block.getState() instanceof InventoryHolder) {
|
||||
|
||||
IrisLootTable table = getPiece().getPlacementOptions().getTable(block.getBlockData(), getData());
|
||||
if (table == null) return;
|
||||
engine.addItems(false, ((InventoryHolder) block.getState()).getInventory(),
|
||||
rng.nextParallelRNG(BlockPosition.toLong(x, y, z)),
|
||||
new KList<>(table), InventorySlotType.STORAGE, x, y, z, 15);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockData get(int x, int y, int z) {
|
||||
return world.getBlockAt(x, y, z).getBlockData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPreventingDecay() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCarved(int x, int y, int z) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSolid(int x, int y, int z) {
|
||||
return world.getBlockAt(x, y, z).getType().isSolid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnderwater(int x, int z) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFluidHeight() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebugSmartBore() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTile(int xx, int yy, int zz, TileData<? extends TileState> tile) {
|
||||
BlockState state = world.getBlockAt(xx, yy, zz).getState();
|
||||
tile.toBukkitTry(state);
|
||||
state.update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Engine getEngine() {
|
||||
return engine;
|
||||
}
|
||||
}, piece.getPlacementOptions(), rng, getData().getEngine() == null ? engine.getData() : getData()) != -1;
|
||||
return connected.size() >= piece.getConnectors().size();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ package com.volmit.iris.engine.jigsaw;
|
||||
import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.loader.IrisData;
|
||||
import com.volmit.iris.engine.data.cache.Cache;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.framework.placer.WorldObjectPlacer;
|
||||
import com.volmit.iris.engine.object.*;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.mantle.Mantle;
|
||||
@@ -30,13 +30,11 @@ import com.volmit.iris.util.math.Position2;
|
||||
import com.volmit.iris.util.math.RNG;
|
||||
import com.volmit.iris.util.matter.slices.container.JigsawPieceContainer;
|
||||
import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer;
|
||||
import com.volmit.iris.util.scheduling.J;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.bukkit.Axis;
|
||||
import org.bukkit.World;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.IntConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Data
|
||||
public class PlannedStructure {
|
||||
@@ -114,7 +112,6 @@ public class PlannedStructure {
|
||||
int sz = (v.getD() / 2);
|
||||
int xx = i.getPosition().getX() + sx;
|
||||
int zz = i.getPosition().getZ() + sz;
|
||||
RNG rngf = new RNG(Cache.key(xx, zz));
|
||||
int offset = i.getPosition().getY() - startHeight;
|
||||
int height;
|
||||
|
||||
@@ -139,20 +136,11 @@ public class PlannedStructure {
|
||||
return v.place(xx, height, zz, placer, options, rng, (b, data) -> {
|
||||
e.set(b.getX(), b.getY(), b.getZ(), v.getLoadKey() + "@" + id);
|
||||
e.set(b.getX(), b.getY(), b.getZ(), container);
|
||||
}, null, getData()) != -1;
|
||||
}, null, getData().getEngine() != null ? getData() : eng.getData()) != -1;
|
||||
}
|
||||
|
||||
public void place(World world, IntConsumer consumer) {
|
||||
AtomicInteger processed = new AtomicInteger();
|
||||
AtomicInteger failures = new AtomicInteger();
|
||||
for (PlannedPiece i : pieces) {
|
||||
Iris.sq(() -> {
|
||||
if (!i.place(world)) failures.incrementAndGet();
|
||||
if (processed.incrementAndGet() == pieces.size()) {
|
||||
consumer.accept(failures.get());
|
||||
}
|
||||
});
|
||||
}
|
||||
public void place(WorldObjectPlacer placer, Consumer<Boolean> consumer) {
|
||||
J.s(() -> consumer.accept(place(placer, placer.getMantle().getMantle(), placer.getEngine())));
|
||||
}
|
||||
|
||||
private void generateOutwards() {
|
||||
@@ -269,7 +257,8 @@ public class PlannedStructure {
|
||||
private KList<IrisJigsawPiece> getShuffledPiecesFor(IrisJigsawPieceConnector c) {
|
||||
KList<IrisJigsawPiece> p = new KList<>();
|
||||
|
||||
for (String i : c.getPools().shuffleCopy(rng)) {
|
||||
KList<String> pools = terminating && getStructure().getTerminatePool() != null ? new KList<>(getStructure().getTerminatePool()) : c.getPools().shuffleCopy(rng);
|
||||
for (String i : pools) {
|
||||
for (String j : getData().getJigsawPoolLoader().load(i).getPieces().shuffleCopy(rng)) {
|
||||
IrisJigsawPiece pi = getData().getJigsawPieceLoader().load(j);
|
||||
|
||||
@@ -295,7 +284,9 @@ public class PlannedStructure {
|
||||
}
|
||||
|
||||
public KList<PlannedPiece> getPiecesWithAvailableConnectors() {
|
||||
return pieces.copy().removeWhere(PlannedPiece::isFull);
|
||||
KList<PlannedPiece> available = pieces.copy().removeWhere(PlannedPiece::isFull);
|
||||
if (!terminating) available.removeIf(PlannedPiece::isDead);
|
||||
return available;
|
||||
}
|
||||
|
||||
public int getVolume() {
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
package com.volmit.iris.engine.mantle.components;
|
||||
|
||||
import com.volmit.iris.engine.data.cache.Cache;
|
||||
import com.volmit.iris.engine.jigsaw.PlannedStructure;
|
||||
import com.volmit.iris.engine.mantle.EngineMantle;
|
||||
import com.volmit.iris.engine.mantle.IrisMantleComponent;
|
||||
@@ -35,34 +34,31 @@ import com.volmit.iris.util.math.Position2;
|
||||
import com.volmit.iris.util.math.RNG;
|
||||
import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer;
|
||||
import com.volmit.iris.util.noise.CNG;
|
||||
import com.volmit.iris.util.noise.NoiseType;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MantleJigsawComponent extends IrisMantleComponent {
|
||||
private final CNG cng;
|
||||
|
||||
public MantleJigsawComponent(EngineMantle engineMantle) {
|
||||
super(engineMantle, MantleFlag.JIGSAW);
|
||||
}
|
||||
|
||||
private RNG applyNoise(int x, int z) {
|
||||
long seed = Cache.key(x, z) + getEngineMantle().getEngine().getSeedManager().getJigsaw();
|
||||
CNG cng = CNG.signatureFast(new RNG(seed), NoiseType.WHITE, NoiseType.GLOB);
|
||||
return new RNG((long) (seed * cng.noise(x, z)));
|
||||
cng = NoiseStyle.STATIC.create(new RNG(jigsaw()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateLayer(MantleWriter writer, int x, int z, ChunkContext context) {
|
||||
RNG rng = applyNoise(x, z);
|
||||
int xxx = 8 + (x << 4);
|
||||
int zzz = 8 + (z << 4);
|
||||
IrisRegion region = getComplex().getRegionStream().get(xxx, zzz);
|
||||
IrisBiome biome = getComplex().getTrueBiomeStream().get(xxx, zzz);
|
||||
generateJigsaw(writer, rng, x, z, biome, region);
|
||||
generateJigsaw(writer, x, z, biome, region);
|
||||
}
|
||||
|
||||
@ChunkCoordinates
|
||||
private void generateJigsaw(MantleWriter writer, RNG rng, int x, int z, IrisBiome biome, IrisRegion region) {
|
||||
private void generateJigsaw(MantleWriter writer, int x, int z, IrisBiome biome, IrisRegion region) {
|
||||
long seed = cng.fit(Integer.MIN_VALUE, Integer.MIN_VALUE, x, z);
|
||||
|
||||
if (getDimension().getStronghold() != null) {
|
||||
List<Position2> poss = getDimension().getStrongholds(seed());
|
||||
|
||||
@@ -70,7 +66,7 @@ public class MantleJigsawComponent extends IrisMantleComponent {
|
||||
for (Position2 pos : poss) {
|
||||
if (x == pos.getX() >> 4 && z == pos.getZ() >> 4) {
|
||||
IrisJigsawStructure structure = getData().getJigsawStructureLoader().load(getDimension().getStronghold());
|
||||
place(writer, pos.toIris(), structure, rng);
|
||||
place(writer, pos.toIris(), structure, new RNG(seed));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -80,27 +76,23 @@ public class MantleJigsawComponent extends IrisMantleComponent {
|
||||
KSet<Position2> cachedRegions = new KSet<>();
|
||||
KMap<String, KSet<Position2>> cache = new KMap<>();
|
||||
KMap<Position2, Double> distanceCache = new KMap<>();
|
||||
boolean placed = placeStructures(writer, rng, x, z, biome.getJigsawStructures(), cachedRegions, cache, distanceCache);
|
||||
boolean placed = placeStructures(writer, seed, x, z, biome.getJigsawStructures(), cachedRegions, cache, distanceCache);
|
||||
if (!placed)
|
||||
placed = placeStructures(writer, rng, x, z, region.getJigsawStructures(), cachedRegions, cache, distanceCache);
|
||||
placed = placeStructures(writer, seed, x, z, region.getJigsawStructures(), cachedRegions, cache, distanceCache);
|
||||
if (!placed)
|
||||
placeStructures(writer, rng, x, z, getDimension().getJigsawStructures(), cachedRegions, cache, distanceCache);
|
||||
placeStructures(writer, seed, x, z, getDimension().getJigsawStructures(), cachedRegions, cache, distanceCache);
|
||||
}
|
||||
|
||||
@ChunkCoordinates
|
||||
private boolean placeStructures(MantleWriter writer, RNG rng, int x, int z, KList<IrisJigsawStructurePlacement> structures,
|
||||
private boolean placeStructures(MantleWriter writer, long seed, int x, int z, KList<IrisJigsawStructurePlacement> structures,
|
||||
KSet<Position2> cachedRegions, KMap<String, KSet<Position2>> cache, KMap<Position2, Double> distanceCache) {
|
||||
for (IrisJigsawStructurePlacement i : structures) {
|
||||
if (rng.nextInt(i.getRarity()) == 0) {
|
||||
if (checkMinDistances(i.collectMinDistances(), x, z, cachedRegions, cache, distanceCache))
|
||||
continue;
|
||||
IrisPosition position = new IrisPosition((x << 4) + rng.nextInt(15), 0, (z << 4) + rng.nextInt(15));
|
||||
IrisJigsawStructure structure = getData().getJigsawStructureLoader().load(i.getStructure());
|
||||
if (place(writer, position, structure, rng))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
IrisJigsawStructurePlacement i = pick(structures, seed, x, z);
|
||||
if (i == null || checkMinDistances(i.collectMinDistances(), x, z, cachedRegions, cache, distanceCache))
|
||||
return false;
|
||||
RNG rng = new RNG(seed);
|
||||
IrisPosition position = new IrisPosition((x << 4) + rng.nextInt(15), 0, (z << 4) + rng.nextInt(15));
|
||||
IrisJigsawStructure structure = getData().getJigsawStructureLoader().load(i.getStructure());
|
||||
return place(writer, position, structure, rng);
|
||||
}
|
||||
|
||||
@ChunkCoordinates
|
||||
@@ -138,7 +130,7 @@ public class MantleJigsawComponent extends IrisMantleComponent {
|
||||
public IrisJigsawStructure guess(int x, int z) {
|
||||
// todo The guess doesnt bring into account that the placer may return -1
|
||||
// todo doesnt bring skipped placements into account
|
||||
RNG rng = applyNoise(x, z);
|
||||
long seed = cng.fit(Integer.MIN_VALUE, Integer.MIN_VALUE, x, z);
|
||||
IrisBiome biome = getEngineMantle().getEngine().getSurfaceBiome((x << 4) + 8, (z << 4) + 8);
|
||||
IrisRegion region = getEngineMantle().getEngine().getRegion((x << 4) + 8, (z << 4) + 8);
|
||||
|
||||
@@ -154,29 +146,26 @@ public class MantleJigsawComponent extends IrisMantleComponent {
|
||||
}
|
||||
}
|
||||
|
||||
for (IrisJigsawStructurePlacement i : biome.getJigsawStructures()) {
|
||||
if (rng.nextInt(i.getRarity()) == 0) {
|
||||
return getData().getJigsawStructureLoader().load(i.getStructure());
|
||||
}
|
||||
}
|
||||
IrisJigsawStructurePlacement i = pick(biome.getJigsawStructures(), seed, x, z);
|
||||
if (i == null) i = pick(region.getJigsawStructures(), seed, x, z);
|
||||
if (i == null) i = pick(getDimension().getJigsawStructures(), seed, x, z);
|
||||
return i != null ? getData().getJigsawStructureLoader().load(i.getStructure()) : null;
|
||||
}
|
||||
|
||||
for (IrisJigsawStructurePlacement i : region.getJigsawStructures()) {
|
||||
if (rng.nextInt(i.getRarity()) == 0) {
|
||||
return getData().getJigsawStructureLoader().load(i.getStructure());
|
||||
}
|
||||
}
|
||||
|
||||
for (IrisJigsawStructurePlacement i : getDimension().getJigsawStructures()) {
|
||||
if (rng.nextInt(i.getRarity()) == 0) {
|
||||
return getData().getJigsawStructureLoader().load(i.getStructure());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
@Nullable
|
||||
@ChunkCoordinates
|
||||
private IrisJigsawStructurePlacement pick(List<IrisJigsawStructurePlacement> structures, long seed, int x, int z) {
|
||||
return IRare.pick(structures.stream()
|
||||
.filter(p -> p.shouldPlace(jigsaw(), x, z))
|
||||
.toList(), new RNG(seed).nextDouble());
|
||||
}
|
||||
|
||||
@BlockCoordinates
|
||||
private boolean place(MantleWriter writer, IrisPosition position, IrisJigsawStructure structure, RNG rng) {
|
||||
return new PlannedStructure(structure, position, rng).place(writer, getMantle(), writer.getEngine());
|
||||
}
|
||||
|
||||
private long jigsaw() {
|
||||
return getEngineMantle().getEngine().getSeedManager().getJigsaw();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,10 @@ public class IrisJigsawStructure extends IrisRegistrant {
|
||||
@Desc("If set to true, iris will look for any pieces with only one connector in valid pools for edge connectors and attach them to 'terminate' the paths/piece connectors. Essentially it caps off ends. For example in a village, Iris would add houses to the ends of roads where possible. For terminators to be selected, they can only have one connector or they wont be chosen.")
|
||||
private boolean terminate = true;
|
||||
|
||||
@RegistryListResource(IrisJigsawPool.class)
|
||||
@Desc("The pool to use when terminating pieces")
|
||||
private String terminatePool = null;
|
||||
|
||||
@Desc("Override the y range instead of placing on the height map")
|
||||
private IrisStyledRange overrideYRange = null;
|
||||
|
||||
|
||||
@@ -18,20 +18,23 @@
|
||||
|
||||
package com.volmit.iris.engine.object;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.engine.object.annotations.ArrayType;
|
||||
import com.volmit.iris.engine.object.annotations.Desc;
|
||||
import com.volmit.iris.engine.object.annotations.MinNumber;
|
||||
import com.volmit.iris.engine.object.annotations.RegistryListResource;
|
||||
import com.volmit.iris.engine.object.annotations.Required;
|
||||
import com.volmit.iris.engine.object.annotations.Snippet;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
||||
import com.volmit.iris.util.math.RNG;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
|
||||
@Snippet("jigsaw-structure-placement")
|
||||
@Accessors(chain = true)
|
||||
@NoArgsConstructor
|
||||
@@ -39,16 +42,33 @@ import lombok.experimental.Accessors;
|
||||
@Desc("Represents a jigsaw structure placer")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class IrisJigsawStructurePlacement {
|
||||
public class IrisJigsawStructurePlacement implements IRare {
|
||||
@RegistryListResource(IrisJigsawStructure.class)
|
||||
@Required
|
||||
@Desc("The structure to place")
|
||||
private String structure;
|
||||
|
||||
@Required
|
||||
@Desc("The 1 in X chance rarity")
|
||||
@Desc("The 1 in X chance rarity applies when generating multiple structures at once")
|
||||
private int rarity = 100;
|
||||
|
||||
@Required
|
||||
@Desc("The salt to use when generating the structure (to differentiate structures)")
|
||||
private int salt = 76134;
|
||||
|
||||
@Required
|
||||
@MinNumber(0)
|
||||
@Desc("Average distance in chunks between two neighboring generation attempts")
|
||||
private int spacing = 32;
|
||||
|
||||
@Required
|
||||
@MinNumber(0)
|
||||
@Desc("Minimum distance in chunks between two neighboring generation attempts\nThe maximum distance of two neighboring generation attempts is 2*spacing - separation")
|
||||
private int separation = 16;
|
||||
|
||||
@Desc("The method used to spread the structure")
|
||||
private SpreadType spreadType = SpreadType.TRIANGULAR;
|
||||
|
||||
@ArrayType(type = IrisJigsawMinDistance.class)
|
||||
@Desc("List of minimum distances to check for")
|
||||
private KList<IrisJigsawMinDistance> minDistances = new KList<>();
|
||||
@@ -64,4 +84,42 @@ public class IrisJigsawStructurePlacement {
|
||||
private int toChunks(int blocks) {
|
||||
return (int) Math.ceil(blocks / 16d);
|
||||
}
|
||||
|
||||
@ChunkCoordinates
|
||||
public boolean shouldPlace(long seed, int x, int z) {
|
||||
if (separation > spacing) {
|
||||
separation = spacing;
|
||||
Iris.warn("JigsawStructurePlacement: separation must be less than or equal to spacing");
|
||||
}
|
||||
|
||||
int i = Math.floorDiv(x, spacing);
|
||||
int j = Math.floorDiv(z, spacing);
|
||||
RNG rng = new RNG(i * 341873128712L + j * 132897987541L + seed + salt);
|
||||
|
||||
int k = spacing - separation;
|
||||
int l = spreadType.apply(rng, k);
|
||||
int m = spreadType.apply(rng, k);
|
||||
return i * spacing + l == x && j * spacing + m == z;
|
||||
}
|
||||
|
||||
@Desc("Spread type")
|
||||
public enum SpreadType {
|
||||
@Desc("Linear spread")
|
||||
LINEAR(RNG::i),
|
||||
@Desc("Triangular spread")
|
||||
TRIANGULAR((rng, bound) -> (rng.i(bound) + rng.i(bound)) / 2);
|
||||
private final SpreadMethod method;
|
||||
|
||||
SpreadType(SpreadMethod method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public int apply(RNG rng, int bound) {
|
||||
return method.apply(rng, bound);
|
||||
}
|
||||
}
|
||||
|
||||
private interface SpreadMethod {
|
||||
int apply(RNG rng, int bound);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -136,6 +136,8 @@ public class IrisObjectPlacement {
|
||||
@ArrayType(type = String.class)
|
||||
@Desc("List of objects to this object is forbidden to collied with")
|
||||
private KList<String> forbiddenCollisions = new KList<>();
|
||||
@Desc("Ignore any placement restrictions for this object")
|
||||
private boolean forcePlace = false;
|
||||
private transient AtomicCache<TableCache> cache = new AtomicCache<>();
|
||||
|
||||
public IrisObjectPlacement toPlacement(String... place) {
|
||||
|
||||
Reference in New Issue
Block a user