mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-04-03 06:16:19 +00:00
f
This commit is contained in:
@@ -342,11 +342,16 @@ public class IrisCreator {
|
||||
int chunkX = rawLocation.getBlockX() >> 4;
|
||||
int chunkZ = rawLocation.getBlockZ() >> 4;
|
||||
try {
|
||||
CompletableFuture<Chunk> chunkFuture = PaperLib.getChunkAtAsync(world, chunkX, chunkZ, true);
|
||||
CompletableFuture<Chunk> chunkFuture = PaperLib.getChunkAtAsync(world, chunkX, chunkZ, false);
|
||||
if (chunkFuture != null) {
|
||||
chunkFuture.get(15, TimeUnit.SECONDS);
|
||||
chunkFuture.get(10, TimeUnit.SECONDS);
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
return rawLocation;
|
||||
}
|
||||
|
||||
if (!world.isChunkLoaded(chunkX, chunkZ)) {
|
||||
return rawLocation;
|
||||
}
|
||||
|
||||
CompletableFuture<Location> regionFuture = new CompletableFuture<>();
|
||||
@@ -376,19 +381,19 @@ public class IrisCreator {
|
||||
int z = source.getBlockZ();
|
||||
int minY = world.getMinHeight() + 1;
|
||||
int maxY = world.getMaxHeight() - 2;
|
||||
int topY = world.getHighestBlockYAt(x, z, HeightMap.MOTION_BLOCKING_NO_LEAVES);
|
||||
int startY = Math.max(minY, Math.min(maxY, topY + 1));
|
||||
int sourceY = source.getBlockY();
|
||||
int startY = Math.max(minY, Math.min(maxY, sourceY));
|
||||
float yaw = source.getYaw();
|
||||
float pitch = source.getPitch();
|
||||
|
||||
int upperBound = Math.min(maxY, startY + 16);
|
||||
int upperBound = Math.min(maxY, startY + 32);
|
||||
for (int y = startY; y <= upperBound; y++) {
|
||||
if (isSafeStandingLocation(world, x, y, z)) {
|
||||
return new Location(world, x + 0.5D, y, z + 0.5D, yaw, pitch);
|
||||
}
|
||||
}
|
||||
|
||||
int lowerBound = Math.max(minY, startY - 24);
|
||||
int lowerBound = Math.max(minY, startY - 64);
|
||||
for (int y = startY - 1; y >= lowerBound; y--) {
|
||||
if (isSafeStandingLocation(world, x, y, z)) {
|
||||
return new Location(world, x + 0.5D, y, z + 0.5D, yaw, pitch);
|
||||
|
||||
@@ -433,6 +433,10 @@ public class IrisComplex implements DataProvider {
|
||||
|
||||
for (IrisGenerator gen : generators) {
|
||||
String key = gen.getLoadKey();
|
||||
if (key == null || key.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
max += biome.getGenLinkMax(key, engine);
|
||||
min += biome.getGenLinkMin(key, engine);
|
||||
}
|
||||
|
||||
@@ -107,6 +107,7 @@ public class IrisEngine implements Engine {
|
||||
private double maxBiomeLayerDensity;
|
||||
private double maxBiomeDecoratorDensity;
|
||||
private IrisComplex complex;
|
||||
private final AtomicBoolean modeFallbackLogged;
|
||||
|
||||
public IrisEngine(EngineTarget target, boolean studio) {
|
||||
this.studio = studio;
|
||||
@@ -129,6 +130,7 @@ public class IrisEngine implements Engine {
|
||||
context = new IrisContext(this);
|
||||
cleaning = new AtomicBoolean(false);
|
||||
noisemapPrebakeRunning = new AtomicBoolean(false);
|
||||
modeFallbackLogged = new AtomicBoolean(false);
|
||||
execution = getData().getEnvironment().with(this);
|
||||
if (studio) {
|
||||
getData().dump();
|
||||
@@ -165,10 +167,29 @@ public class IrisEngine implements Engine {
|
||||
}
|
||||
|
||||
private void prehotload() {
|
||||
worldManager.close();
|
||||
complex.close();
|
||||
effects.close();
|
||||
mode.close();
|
||||
EngineWorldManager currentWorldManager = worldManager;
|
||||
worldManager = null;
|
||||
if (currentWorldManager != null) {
|
||||
currentWorldManager.close();
|
||||
}
|
||||
|
||||
IrisComplex currentComplex = complex;
|
||||
complex = null;
|
||||
if (currentComplex != null) {
|
||||
currentComplex.close();
|
||||
}
|
||||
|
||||
EngineEffects currentEffects = effects;
|
||||
effects = null;
|
||||
if (currentEffects != null) {
|
||||
currentEffects.close();
|
||||
}
|
||||
|
||||
EngineMode currentMode = mode;
|
||||
mode = null;
|
||||
if (currentMode != null) {
|
||||
currentMode.close();
|
||||
}
|
||||
execution = getData().getEnvironment().with(this);
|
||||
|
||||
J.a(() -> new IrisProject(getData().getDataFolder()).updateWorkspace());
|
||||
@@ -178,12 +199,14 @@ public class IrisEngine implements Engine {
|
||||
try {
|
||||
Iris.debug("Setup Engine " + getCacheID());
|
||||
cacheId = RNG.r.nextInt();
|
||||
worldManager = new IrisWorldManager(this);
|
||||
complex = new IrisComplex(this);
|
||||
complex = ensureComplex();
|
||||
effects = new IrisEngineEffects(this);
|
||||
hash32 = new CompletableFuture<>();
|
||||
mantle.hotload();
|
||||
setupMode();
|
||||
IrisWorldManager manager = new IrisWorldManager(this);
|
||||
worldManager = manager;
|
||||
manager.startManager();
|
||||
getDimension().getEngineScripts().forEach(execution::execute);
|
||||
J.a(this::computeBiomeMaxes);
|
||||
J.a(() -> {
|
||||
@@ -207,11 +230,76 @@ public class IrisEngine implements Engine {
|
||||
}
|
||||
|
||||
private void setupMode() {
|
||||
if (mode != null) {
|
||||
mode.close();
|
||||
EngineMode currentMode = mode;
|
||||
if (currentMode != null) {
|
||||
currentMode.close();
|
||||
}
|
||||
|
||||
mode = getDimension().getMode().create(this);
|
||||
mode = null;
|
||||
mode = ensureMode();
|
||||
}
|
||||
|
||||
private EngineMode ensureMode() {
|
||||
EngineMode currentMode = mode;
|
||||
if (currentMode != null) {
|
||||
return currentMode;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
currentMode = mode;
|
||||
if (currentMode != null) {
|
||||
return currentMode;
|
||||
}
|
||||
|
||||
try {
|
||||
IrisComplex readyComplex = ensureComplex();
|
||||
if (readyComplex == null) {
|
||||
throw new IllegalStateException("Iris complex is unavailable");
|
||||
}
|
||||
|
||||
IrisDimensionMode configuredMode = getDimension().getMode();
|
||||
if (configuredMode == null) {
|
||||
configuredMode = new IrisDimensionMode();
|
||||
getDimension().setMode(configuredMode);
|
||||
}
|
||||
|
||||
currentMode = configuredMode.create(this);
|
||||
if (currentMode == null) {
|
||||
throw new IllegalStateException("Dimension mode factory returned null");
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
if (modeFallbackLogged.compareAndSet(false, true)) {
|
||||
Iris.warn("Failed to initialize configured dimension mode for " + getDimension().getLoadKey() + ", falling back to OVERWORLD mode.");
|
||||
}
|
||||
currentMode = IrisDimensionModeType.OVERWORLD.create(this);
|
||||
}
|
||||
|
||||
mode = currentMode;
|
||||
return currentMode;
|
||||
}
|
||||
}
|
||||
|
||||
private IrisComplex ensureComplex() {
|
||||
IrisComplex currentComplex = complex;
|
||||
if (currentComplex != null) {
|
||||
return currentComplex;
|
||||
}
|
||||
|
||||
if (closed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
currentComplex = complex;
|
||||
if (currentComplex != null) {
|
||||
return currentComplex;
|
||||
}
|
||||
|
||||
currentComplex = new IrisComplex(this);
|
||||
complex = currentComplex;
|
||||
return currentComplex;
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleStartupNoisemapPrebake() {
|
||||
@@ -447,17 +535,39 @@ public class IrisEngine implements Engine {
|
||||
PregeneratorJob.shutdownInstance();
|
||||
closed = true;
|
||||
J.car(art);
|
||||
getWorldManager().close();
|
||||
EngineWorldManager currentWorldManager = getWorldManager();
|
||||
if (currentWorldManager != null) {
|
||||
currentWorldManager.close();
|
||||
}
|
||||
getTarget().close();
|
||||
saveEngineData();
|
||||
getMantle().close();
|
||||
getComplex().close();
|
||||
mode.close();
|
||||
IrisComplex currentComplex = complex;
|
||||
if (currentComplex != null) {
|
||||
currentComplex.close();
|
||||
}
|
||||
complex = null;
|
||||
EngineMode currentMode = mode;
|
||||
if (currentMode != null) {
|
||||
currentMode.close();
|
||||
}
|
||||
mode = null;
|
||||
effects = null;
|
||||
worldManager = null;
|
||||
getData().dump();
|
||||
getData().clearLists();
|
||||
Iris.service(PreservationSVC.class).dereference();
|
||||
Iris.debug("Engine Fully Shutdown!");
|
||||
complex = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IrisComplex getComplex() {
|
||||
return ensureComplex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EngineMode getMode() {
|
||||
return ensureMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -510,7 +620,8 @@ public class IrisEngine implements Engine {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mode.generate(x, z, blocks, vbiomes, multicore);
|
||||
EngineMode activeMode = ensureMode();
|
||||
activeMode.generate(x, z, blocks, vbiomes, multicore);
|
||||
}
|
||||
|
||||
World realWorld = getWorld().realWorld();
|
||||
|
||||
@@ -195,7 +195,12 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
};
|
||||
looper.setPriority(Thread.MIN_PRIORITY);
|
||||
looper.setName("Iris World Manager " + getTarget().getWorld().name());
|
||||
looper.start();
|
||||
}
|
||||
|
||||
public void startManager() {
|
||||
if (!looper.isAlive()) {
|
||||
looper.start();
|
||||
}
|
||||
}
|
||||
|
||||
private void discoverChunks() {
|
||||
@@ -528,6 +533,11 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
return;
|
||||
}
|
||||
|
||||
IrisComplex complex = getEngine().getComplex();
|
||||
if (complex == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (initial) {
|
||||
energy += 1.2;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package art.arcane.iris.engine.mantle.components;
|
||||
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.mantle.MantleWriter;
|
||||
import art.arcane.iris.engine.object.IrisCaveFieldModule;
|
||||
import art.arcane.iris.engine.object.IrisCaveProfile;
|
||||
import art.arcane.iris.engine.object.IrisRange;
|
||||
import art.arcane.iris.util.project.noise.CNG;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import art.arcane.volmlib.util.matter.MatterCavern;
|
||||
|
||||
public class IrisCaveCarver3D {
|
||||
private static final byte LIQUID_AIR = 0;
|
||||
private static final byte LIQUID_WATER = 1;
|
||||
private static final byte LIQUID_LAVA = 2;
|
||||
private static final byte LIQUID_FORCED_AIR = 3;
|
||||
|
||||
private final Engine engine;
|
||||
private final IrisData data;
|
||||
private final IrisCaveProfile profile;
|
||||
private final CNG baseDensity;
|
||||
private final CNG detailDensity;
|
||||
private final CNG warpDensity;
|
||||
private final CNG surfaceBreakDensity;
|
||||
private final RNG thresholdRng;
|
||||
private final KList<ModuleState> modules;
|
||||
private final double normalization;
|
||||
private final MatterCavern carveAir;
|
||||
private final MatterCavern carveWater;
|
||||
private final MatterCavern carveLava;
|
||||
private final MatterCavern carveForcedAir;
|
||||
|
||||
public IrisCaveCarver3D(Engine engine, IrisCaveProfile profile) {
|
||||
this.engine = engine;
|
||||
this.data = engine.getData();
|
||||
this.profile = profile;
|
||||
this.carveAir = new MatterCavern(true, "", LIQUID_AIR);
|
||||
this.carveWater = new MatterCavern(true, "", LIQUID_WATER);
|
||||
this.carveLava = new MatterCavern(true, "", LIQUID_LAVA);
|
||||
this.carveForcedAir = new MatterCavern(true, "", LIQUID_FORCED_AIR);
|
||||
this.modules = new KList<>();
|
||||
|
||||
RNG baseRng = new RNG(engine.getSeedManager().getCarve());
|
||||
this.baseDensity = profile.getBaseDensityStyle().create(baseRng.nextParallelRNG(934_447), data);
|
||||
this.detailDensity = profile.getDetailDensityStyle().create(baseRng.nextParallelRNG(612_991), data);
|
||||
this.warpDensity = profile.getWarpStyle().create(baseRng.nextParallelRNG(770_713), data);
|
||||
this.surfaceBreakDensity = profile.getSurfaceBreakStyle().create(baseRng.nextParallelRNG(341_219), data);
|
||||
this.thresholdRng = baseRng.nextParallelRNG(489_112);
|
||||
|
||||
double weight = Math.abs(profile.getBaseWeight()) + Math.abs(profile.getDetailWeight());
|
||||
int index = 0;
|
||||
for (IrisCaveFieldModule module : profile.getModules()) {
|
||||
CNG moduleDensity = module.getStyle().create(baseRng.nextParallelRNG(1_000_003L + (index * 65_537L)), data);
|
||||
ModuleState state = new ModuleState(module, moduleDensity);
|
||||
modules.add(state);
|
||||
weight += Math.abs(state.weight);
|
||||
index++;
|
||||
}
|
||||
|
||||
normalization = weight <= 0 ? 1 : weight;
|
||||
}
|
||||
|
||||
public int carve(MantleWriter writer, int chunkX, int chunkZ) {
|
||||
int worldHeight = writer.getMantle().getWorldHeight();
|
||||
int minY = Math.max(0, (int) Math.floor(profile.getVerticalRange().getMin()));
|
||||
int maxY = Math.min(worldHeight - 1, (int) Math.ceil(profile.getVerticalRange().getMax()));
|
||||
int sampleStep = Math.max(1, profile.getSampleStep());
|
||||
int surfaceClearance = Math.max(0, profile.getSurfaceClearance());
|
||||
int surfaceBreakDepth = Math.max(0, profile.getSurfaceBreakDepth());
|
||||
double surfaceBreakNoiseThreshold = profile.getSurfaceBreakNoiseThreshold();
|
||||
double surfaceBreakThresholdBoost = Math.max(0, profile.getSurfaceBreakThresholdBoost());
|
||||
int waterMinDepthBelowSurface = Math.max(0, profile.getWaterMinDepthBelowSurface());
|
||||
boolean waterRequiresFloor = profile.isWaterRequiresFloor();
|
||||
boolean allowSurfaceBreak = profile.isAllowSurfaceBreak();
|
||||
if (maxY < minY) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int x0 = chunkX << 4;
|
||||
int z0 = chunkZ << 4;
|
||||
int carved = 0;
|
||||
|
||||
for (int x = x0; x < x0 + 16; x++) {
|
||||
for (int z = z0; z < z0 + 16; z++) {
|
||||
double threshold = profile.getDensityThreshold().get(thresholdRng, x, z, data) - profile.getThresholdBias();
|
||||
int columnSurface = engine.getHeight(x, z);
|
||||
int clearanceTopY = Math.min(maxY, Math.max(minY, columnSurface - surfaceClearance));
|
||||
boolean surfaceBreakColumn = allowSurfaceBreak
|
||||
&& signed(surfaceBreakDensity.noise(x, z)) >= surfaceBreakNoiseThreshold;
|
||||
int columnMaxY = surfaceBreakColumn
|
||||
? Math.min(maxY, Math.max(minY, columnSurface))
|
||||
: clearanceTopY;
|
||||
int surfaceBreakFloorY = Math.max(minY, columnSurface - surfaceBreakDepth);
|
||||
if (columnMaxY < minY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int y = minY; y <= columnMaxY; y += sampleStep) {
|
||||
double localThreshold = threshold;
|
||||
if (surfaceBreakColumn && y >= surfaceBreakFloorY) {
|
||||
localThreshold += surfaceBreakThresholdBoost;
|
||||
}
|
||||
|
||||
if (sampleDensity(x, y, z) <= localThreshold) {
|
||||
int carveMaxY = Math.min(columnMaxY, y + sampleStep - 1);
|
||||
for (int yy = y; yy <= carveMaxY; yy++) {
|
||||
writer.setData(x, yy, z, resolveMatter(x, yy, z, localThreshold, waterMinDepthBelowSurface, waterRequiresFloor));
|
||||
carved++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return carved;
|
||||
}
|
||||
|
||||
private double sampleDensity(int x, int y, int z) {
|
||||
double warpedX = x;
|
||||
double warpedY = y;
|
||||
double warpedZ = z;
|
||||
double warpStrength = profile.getWarpStrength();
|
||||
|
||||
if (warpStrength > 0) {
|
||||
double offsetX = signed(warpDensity.noise(x, y, z)) * warpStrength;
|
||||
double offsetY = signed(warpDensity.noise(y, z, x)) * warpStrength;
|
||||
double offsetZ = signed(warpDensity.noise(z, x, y)) * warpStrength;
|
||||
warpedX += offsetX;
|
||||
warpedY += offsetY;
|
||||
warpedZ += offsetZ;
|
||||
}
|
||||
|
||||
double density = signed(baseDensity.noise(warpedX, warpedY, warpedZ)) * profile.getBaseWeight();
|
||||
density += signed(detailDensity.noise(warpedX, warpedY, warpedZ)) * profile.getDetailWeight();
|
||||
|
||||
for (ModuleState module : modules) {
|
||||
if (y < module.minY || y > module.maxY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double moduleDensity = signed(module.density.noise(warpedX, warpedY, warpedZ)) - module.threshold;
|
||||
if (module.invert) {
|
||||
moduleDensity = -moduleDensity;
|
||||
}
|
||||
|
||||
density += moduleDensity * module.weight;
|
||||
}
|
||||
|
||||
return density / normalization;
|
||||
}
|
||||
|
||||
private MatterCavern resolveMatter(int x, int y, int z, double localThreshold, int waterMinDepthBelowSurface, boolean waterRequiresFloor) {
|
||||
int lavaHeight = engine.getDimension().getCaveLavaHeight();
|
||||
int fluidHeight = engine.getDimension().getFluidHeight();
|
||||
|
||||
if (profile.isAllowLava() && y <= lavaHeight) {
|
||||
return carveLava;
|
||||
}
|
||||
|
||||
if (profile.isAllowWater() && y <= fluidHeight) {
|
||||
int surfaceY = engine.getHeight(x, z);
|
||||
if (surfaceY - y < waterMinDepthBelowSurface) {
|
||||
return carveAir;
|
||||
}
|
||||
|
||||
double depthFactor = Math.max(0, Math.min(1.5, (fluidHeight - y) / 48D));
|
||||
double cutoff = 0.35 + (depthFactor * 0.2);
|
||||
double aquifer = signed(detailDensity.noise(x, y * 0.5D, z));
|
||||
if (aquifer <= cutoff) {
|
||||
return carveAir;
|
||||
}
|
||||
|
||||
if (waterRequiresFloor && !hasAquiferCupSupport(x, y, z, localThreshold)) {
|
||||
return carveAir;
|
||||
}
|
||||
|
||||
return carveWater;
|
||||
}
|
||||
|
||||
if (!profile.isAllowLava() && y <= lavaHeight) {
|
||||
return carveForcedAir;
|
||||
}
|
||||
|
||||
return carveAir;
|
||||
}
|
||||
|
||||
private boolean hasAquiferCupSupport(int x, int y, int z, double threshold) {
|
||||
int floorY = Math.max(0, y - 1);
|
||||
int deepFloorY = Math.max(0, y - 2);
|
||||
int aboveY = Math.min(engine.getHeight() - 1, y + 1);
|
||||
if (!isDensitySolid(x, floorY, z, threshold)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isDensitySolid(x, deepFloorY, z, threshold - 0.05D)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int support = 0;
|
||||
if (isDensitySolid(x + 1, y, z, threshold)) {
|
||||
support++;
|
||||
}
|
||||
if (isDensitySolid(x - 1, y, z, threshold)) {
|
||||
support++;
|
||||
}
|
||||
if (isDensitySolid(x, y, z + 1, threshold)) {
|
||||
support++;
|
||||
}
|
||||
if (isDensitySolid(x, y, z - 1, threshold)) {
|
||||
support++;
|
||||
}
|
||||
if (isDensitySolid(x, aboveY, z, threshold)) {
|
||||
support++;
|
||||
}
|
||||
|
||||
return support >= 4;
|
||||
}
|
||||
|
||||
private boolean isDensitySolid(int x, int y, int z, double threshold) {
|
||||
return sampleDensity(x, y, z) > threshold;
|
||||
}
|
||||
|
||||
private double signed(double value) {
|
||||
return (value * 2D) - 1D;
|
||||
}
|
||||
|
||||
private static final class ModuleState {
|
||||
private final CNG density;
|
||||
private final int minY;
|
||||
private final int maxY;
|
||||
private final double weight;
|
||||
private final double threshold;
|
||||
private final boolean invert;
|
||||
|
||||
private ModuleState(IrisCaveFieldModule module, CNG density) {
|
||||
IrisRange range = module.getVerticalRange();
|
||||
this.density = density;
|
||||
this.minY = (int) Math.floor(range.getMin());
|
||||
this.maxY = (int) Math.ceil(range.getMax());
|
||||
this.weight = module.getWeight();
|
||||
this.threshold = module.getThreshold();
|
||||
this.invert = module.isInvert();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,14 +25,21 @@ import art.arcane.iris.engine.mantle.IrisMantleComponent;
|
||||
import art.arcane.iris.engine.mantle.MantleWriter;
|
||||
import art.arcane.iris.engine.object.IrisBiome;
|
||||
import art.arcane.iris.engine.object.IrisCarving;
|
||||
import art.arcane.iris.engine.object.IrisCaveProfile;
|
||||
import art.arcane.iris.engine.object.IrisDimension;
|
||||
import art.arcane.iris.engine.object.IrisRegion;
|
||||
import art.arcane.iris.util.project.context.ChunkContext;
|
||||
import art.arcane.volmlib.util.documentation.ChunkCoordinates;
|
||||
import art.arcane.volmlib.util.mantle.flag.ReservedFlag;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ComponentFlag(ReservedFlag.CARVED)
|
||||
public class MantleCarvingComponent extends IrisMantleComponent {
|
||||
private final Map<IrisCaveProfile, IrisCaveCarver3D> profileCarvers = new IdentityHashMap<>();
|
||||
|
||||
public MantleCarvingComponent(EngineMantle engineMantle) {
|
||||
super(engineMantle, ReservedFlag.CARVED, 0);
|
||||
}
|
||||
@@ -49,9 +56,18 @@ public class MantleCarvingComponent extends IrisMantleComponent {
|
||||
|
||||
@ChunkCoordinates
|
||||
private void carve(MantleWriter writer, RNG rng, int cx, int cz, IrisRegion region, IrisBiome biome) {
|
||||
carve(getDimension().getCarving(), writer, new RNG((rng.nextLong() * cx) + 490495 + cz), cx, cz);
|
||||
carve(biome.getCarving(), writer, new RNG((rng.nextLong() * cx) + 490495 + cz), cx, cz);
|
||||
carve(region.getCarving(), writer, new RNG((rng.nextLong() * cx) + 490495 + cz), cx, cz);
|
||||
IrisCaveProfile dimensionProfile = getDimension().getCaveProfile();
|
||||
IrisCaveProfile biomeProfile = biome.getCaveProfile();
|
||||
IrisCaveProfile regionProfile = region.getCaveProfile();
|
||||
IrisCaveProfile activeProfile = resolveActiveProfile(dimensionProfile, regionProfile, biomeProfile);
|
||||
if (isProfileEnabled(activeProfile)) {
|
||||
carveProfile(activeProfile, writer, cx, cz);
|
||||
return;
|
||||
}
|
||||
|
||||
carve(getDimension().getCarving(), writer, nextCarveRng(rng, cx, cz), cx, cz);
|
||||
carve(biome.getCarving(), writer, nextCarveRng(rng, cx, cz), cx, cz);
|
||||
carve(region.getCarving(), writer, nextCarveRng(rng, cx, cz), cx, cz);
|
||||
}
|
||||
|
||||
@ChunkCoordinates
|
||||
@@ -59,17 +75,60 @@ public class MantleCarvingComponent extends IrisMantleComponent {
|
||||
carving.doCarving(writer, rng, getEngineMantle().getEngine(), cx << 4, -1, cz << 4, 0);
|
||||
}
|
||||
|
||||
private RNG nextCarveRng(RNG rng, int cx, int cz) {
|
||||
return new RNG((rng.nextLong() * cx) + 490495L + cz);
|
||||
}
|
||||
|
||||
@ChunkCoordinates
|
||||
private void carveProfile(IrisCaveProfile profile, MantleWriter writer, int cx, int cz) {
|
||||
if (!isProfileEnabled(profile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IrisCaveCarver3D carver = getCarver(profile);
|
||||
carver.carve(writer, cx, cz);
|
||||
}
|
||||
|
||||
private IrisCaveCarver3D getCarver(IrisCaveProfile profile) {
|
||||
synchronized (profileCarvers) {
|
||||
IrisCaveCarver3D carver = profileCarvers.get(profile);
|
||||
if (carver != null) {
|
||||
return carver;
|
||||
}
|
||||
|
||||
IrisCaveCarver3D createdCarver = new IrisCaveCarver3D(getEngineMantle().getEngine(), profile);
|
||||
profileCarvers.put(profile, createdCarver);
|
||||
return createdCarver;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isProfileEnabled(IrisCaveProfile profile) {
|
||||
return profile != null && profile.isEnabled();
|
||||
}
|
||||
|
||||
private IrisCaveProfile resolveActiveProfile(IrisCaveProfile dimensionProfile, IrisCaveProfile regionProfile, IrisCaveProfile biomeProfile) {
|
||||
if (isProfileEnabled(biomeProfile)) {
|
||||
return biomeProfile;
|
||||
}
|
||||
|
||||
if (isProfileEnabled(regionProfile)) {
|
||||
return regionProfile;
|
||||
}
|
||||
|
||||
return dimensionProfile;
|
||||
}
|
||||
|
||||
protected int computeRadius() {
|
||||
var dimension = getDimension();
|
||||
IrisDimension dimension = getDimension();
|
||||
int max = 0;
|
||||
|
||||
max = Math.max(max, dimension.getCarving().getMaxRange(getData(), 0));
|
||||
|
||||
for (var i : dimension.getAllRegions(this::getData)) {
|
||||
for (IrisRegion i : dimension.getAllRegions(this::getData)) {
|
||||
max = Math.max(max, i.getCarving().getMaxRange(getData(), 0));
|
||||
}
|
||||
|
||||
for (var i : dimension.getAllBiomes(this::getData)) {
|
||||
for (IrisBiome i : dimension.getAllBiomes(this::getData)) {
|
||||
max = Math.max(max, i.getCarving().getMaxRange(getData(), 0));
|
||||
}
|
||||
|
||||
|
||||
@@ -63,21 +63,29 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
int xxx = 8 + (x << 4);
|
||||
int zzz = 8 + (z << 4);
|
||||
IrisRegion region = getComplex().getRegionStream().get(xxx, zzz);
|
||||
IrisBiome biome = getComplex().getTrueBiomeStream().get(xxx, zzz);
|
||||
IrisBiome surfaceBiome = getComplex().getTrueBiomeStream().get(xxx, zzz);
|
||||
IrisBiome caveBiome = getComplex().getCaveBiomeStream().get(xxx, zzz);
|
||||
if (traceRegen) {
|
||||
Iris.info("Regen object layer start: chunk=" + x + "," + z
|
||||
+ " biome=" + biome.getLoadKey()
|
||||
+ " surfaceBiome=" + surfaceBiome.getLoadKey()
|
||||
+ " caveBiome=" + caveBiome.getLoadKey()
|
||||
+ " region=" + region.getLoadKey()
|
||||
+ " biomePlacers=" + biome.getSurfaceObjects().size()
|
||||
+ " regionPlacers=" + region.getSurfaceObjects().size());
|
||||
+ " biomeSurfacePlacers=" + surfaceBiome.getSurfaceObjects().size()
|
||||
+ " biomeCavePlacers=" + caveBiome.getCarvingObjects().size()
|
||||
+ " regionSurfacePlacers=" + region.getSurfaceObjects().size()
|
||||
+ " regionCavePlacers=" + region.getCarvingObjects().size());
|
||||
}
|
||||
ObjectPlacementSummary summary = placeObjects(writer, rng, x, z, biome, region, traceRegen);
|
||||
ObjectPlacementSummary summary = placeObjects(writer, rng, x, z, surfaceBiome, caveBiome, region, traceRegen);
|
||||
if (traceRegen) {
|
||||
Iris.info("Regen object layer done: chunk=" + x + "," + z
|
||||
+ " biomePlacersChecked=" + summary.biomePlacersChecked()
|
||||
+ " biomePlacersTriggered=" + summary.biomePlacersTriggered()
|
||||
+ " regionPlacersChecked=" + summary.regionPlacersChecked()
|
||||
+ " regionPlacersTriggered=" + summary.regionPlacersTriggered()
|
||||
+ " biomeSurfacePlacersChecked=" + summary.biomeSurfacePlacersChecked()
|
||||
+ " biomeSurfacePlacersTriggered=" + summary.biomeSurfacePlacersTriggered()
|
||||
+ " biomeCavePlacersChecked=" + summary.biomeCavePlacersChecked()
|
||||
+ " biomeCavePlacersTriggered=" + summary.biomeCavePlacersTriggered()
|
||||
+ " regionSurfacePlacersChecked=" + summary.regionSurfacePlacersChecked()
|
||||
+ " regionSurfacePlacersTriggered=" + summary.regionSurfacePlacersTriggered()
|
||||
+ " regionCavePlacersChecked=" + summary.regionCavePlacersChecked()
|
||||
+ " regionCavePlacersTriggered=" + summary.regionCavePlacersTriggered()
|
||||
+ " objectAttempts=" + summary.objectAttempts()
|
||||
+ " objectPlaced=" + summary.objectPlaced()
|
||||
+ " objectRejected=" + summary.objectRejected()
|
||||
@@ -92,32 +100,40 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
}
|
||||
|
||||
@ChunkCoordinates
|
||||
private ObjectPlacementSummary placeObjects(MantleWriter writer, RNG rng, int x, int z, IrisBiome biome, IrisRegion region, boolean traceRegen) {
|
||||
int biomeChecked = 0;
|
||||
int biomeTriggered = 0;
|
||||
int regionChecked = 0;
|
||||
int regionTriggered = 0;
|
||||
private ObjectPlacementSummary placeObjects(MantleWriter writer, RNG rng, int x, int z, IrisBiome surfaceBiome, IrisBiome caveBiome, IrisRegion region, boolean traceRegen) {
|
||||
int biomeSurfaceChecked = 0;
|
||||
int biomeSurfaceTriggered = 0;
|
||||
int biomeCaveChecked = 0;
|
||||
int biomeCaveTriggered = 0;
|
||||
int regionSurfaceChecked = 0;
|
||||
int regionSurfaceTriggered = 0;
|
||||
int regionCaveChecked = 0;
|
||||
int regionCaveTriggered = 0;
|
||||
int attempts = 0;
|
||||
int placed = 0;
|
||||
int rejected = 0;
|
||||
int nullObjects = 0;
|
||||
int errors = 0;
|
||||
IrisCaveProfile biomeCaveProfile = resolveCaveProfile(caveBiome.getCaveProfile(), region.getCaveProfile());
|
||||
IrisCaveProfile regionCaveProfile = resolveCaveProfile(region.getCaveProfile(), caveBiome.getCaveProfile());
|
||||
int biomeSurfaceExclusionDepth = resolveSurfaceObjectExclusionDepth(biomeCaveProfile);
|
||||
int regionSurfaceExclusionDepth = resolveSurfaceObjectExclusionDepth(regionCaveProfile);
|
||||
|
||||
for (IrisObjectPlacement i : biome.getSurfaceObjects()) {
|
||||
biomeChecked++;
|
||||
for (IrisObjectPlacement i : surfaceBiome.getSurfaceObjects()) {
|
||||
biomeSurfaceChecked++;
|
||||
boolean chance = rng.chance(i.getChance() + rng.d(-0.005, 0.005));
|
||||
if (traceRegen) {
|
||||
Iris.info("Regen object placer chance: chunk=" + x + "," + z
|
||||
+ " scope=biome"
|
||||
+ " scope=biome-surface"
|
||||
+ " chanceResult=" + chance
|
||||
+ " chanceBase=" + i.getChance()
|
||||
+ " densityMid=" + i.getDensity()
|
||||
+ " objects=" + i.getPlace().size());
|
||||
}
|
||||
if (chance) {
|
||||
biomeTriggered++;
|
||||
biomeSurfaceTriggered++;
|
||||
try {
|
||||
ObjectPlacementResult result = placeObject(writer, rng, x << 4, z << 4, i, traceRegen, x, z, "biome");
|
||||
ObjectPlacementResult result = placeObject(writer, rng, x << 4, z << 4, i, biomeSurfaceExclusionDepth, traceRegen, x, z, "biome-surface");
|
||||
attempts += result.attempts();
|
||||
placed += result.placed();
|
||||
rejected += result.rejected();
|
||||
@@ -126,7 +142,41 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
} catch (Throwable e) {
|
||||
errors++;
|
||||
Iris.reportError(e);
|
||||
Iris.error("Failed to place objects in the following biome: " + biome.getName());
|
||||
Iris.error("Failed to place objects in the following biome: " + surfaceBiome.getName());
|
||||
Iris.error("Object(s) " + i.getPlace().toString(", ") + " (" + e.getClass().getSimpleName() + ").");
|
||||
Iris.error("Are these objects missing?");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (IrisObjectPlacement i : caveBiome.getCarvingObjects()) {
|
||||
if (!i.getCarvingSupport().equals(CarvingMode.CARVING_ONLY)) {
|
||||
continue;
|
||||
}
|
||||
biomeCaveChecked++;
|
||||
boolean chance = rng.chance(i.getChance() + rng.d(-0.005, 0.005));
|
||||
if (traceRegen) {
|
||||
Iris.info("Regen object placer chance: chunk=" + x + "," + z
|
||||
+ " scope=biome-cave"
|
||||
+ " chanceResult=" + chance
|
||||
+ " chanceBase=" + i.getChance()
|
||||
+ " densityMid=" + i.getDensity()
|
||||
+ " objects=" + i.getPlace().size());
|
||||
}
|
||||
if (chance) {
|
||||
biomeCaveTriggered++;
|
||||
try {
|
||||
ObjectPlacementResult result = placeCaveObject(writer, rng, x, z, i, biomeCaveProfile, traceRegen, x, z, "biome-cave");
|
||||
attempts += result.attempts();
|
||||
placed += result.placed();
|
||||
rejected += result.rejected();
|
||||
nullObjects += result.nullObjects();
|
||||
errors += result.errors();
|
||||
} catch (Throwable e) {
|
||||
errors++;
|
||||
Iris.reportError(e);
|
||||
Iris.error("Failed to place cave objects in the following biome: " + caveBiome.getName());
|
||||
Iris.error("Object(s) " + i.getPlace().toString(", ") + " (" + e.getClass().getSimpleName() + ").");
|
||||
Iris.error("Are these objects missing?");
|
||||
e.printStackTrace();
|
||||
@@ -135,20 +185,20 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
}
|
||||
|
||||
for (IrisObjectPlacement i : region.getSurfaceObjects()) {
|
||||
regionChecked++;
|
||||
regionSurfaceChecked++;
|
||||
boolean chance = rng.chance(i.getChance() + rng.d(-0.005, 0.005));
|
||||
if (traceRegen) {
|
||||
Iris.info("Regen object placer chance: chunk=" + x + "," + z
|
||||
+ " scope=region"
|
||||
+ " scope=region-surface"
|
||||
+ " chanceResult=" + chance
|
||||
+ " chanceBase=" + i.getChance()
|
||||
+ " densityMid=" + i.getDensity()
|
||||
+ " objects=" + i.getPlace().size());
|
||||
}
|
||||
if (chance) {
|
||||
regionTriggered++;
|
||||
regionSurfaceTriggered++;
|
||||
try {
|
||||
ObjectPlacementResult result = placeObject(writer, rng, x << 4, z << 4, i, traceRegen, x, z, "region");
|
||||
ObjectPlacementResult result = placeObject(writer, rng, x << 4, z << 4, i, regionSurfaceExclusionDepth, traceRegen, x, z, "region-surface");
|
||||
attempts += result.attempts();
|
||||
placed += result.placed();
|
||||
rejected += result.rejected();
|
||||
@@ -165,11 +215,49 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
}
|
||||
}
|
||||
|
||||
for (IrisObjectPlacement i : region.getCarvingObjects()) {
|
||||
if (!i.getCarvingSupport().equals(CarvingMode.CARVING_ONLY)) {
|
||||
continue;
|
||||
}
|
||||
regionCaveChecked++;
|
||||
boolean chance = rng.chance(i.getChance() + rng.d(-0.005, 0.005));
|
||||
if (traceRegen) {
|
||||
Iris.info("Regen object placer chance: chunk=" + x + "," + z
|
||||
+ " scope=region-cave"
|
||||
+ " chanceResult=" + chance
|
||||
+ " chanceBase=" + i.getChance()
|
||||
+ " densityMid=" + i.getDensity()
|
||||
+ " objects=" + i.getPlace().size());
|
||||
}
|
||||
if (chance) {
|
||||
regionCaveTriggered++;
|
||||
try {
|
||||
ObjectPlacementResult result = placeCaveObject(writer, rng, x, z, i, regionCaveProfile, traceRegen, x, z, "region-cave");
|
||||
attempts += result.attempts();
|
||||
placed += result.placed();
|
||||
rejected += result.rejected();
|
||||
nullObjects += result.nullObjects();
|
||||
errors += result.errors();
|
||||
} catch (Throwable e) {
|
||||
errors++;
|
||||
Iris.reportError(e);
|
||||
Iris.error("Failed to place cave objects in the following region: " + region.getName());
|
||||
Iris.error("Object(s) " + i.getPlace().toString(", ") + " (" + e.getClass().getSimpleName() + ").");
|
||||
Iris.error("Are these objects missing?");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ObjectPlacementSummary(
|
||||
biomeChecked,
|
||||
biomeTriggered,
|
||||
regionChecked,
|
||||
regionTriggered,
|
||||
biomeSurfaceChecked,
|
||||
biomeSurfaceTriggered,
|
||||
biomeCaveChecked,
|
||||
biomeCaveTriggered,
|
||||
regionSurfaceChecked,
|
||||
regionSurfaceTriggered,
|
||||
regionCaveChecked,
|
||||
regionCaveTriggered,
|
||||
attempts,
|
||||
placed,
|
||||
rejected,
|
||||
@@ -185,6 +273,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
int x,
|
||||
int z,
|
||||
IrisObjectPlacement objectPlacement,
|
||||
int surfaceObjectExclusionDepth,
|
||||
boolean traceRegen,
|
||||
int chunkX,
|
||||
int chunkZ,
|
||||
@@ -213,6 +302,11 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
}
|
||||
int xx = rng.i(x, x + 15);
|
||||
int zz = rng.i(z, z + 15);
|
||||
int surfaceObjectExclusionRadius = resolveSurfaceObjectExclusionRadius(v);
|
||||
if (surfaceObjectExclusionDepth > 0 && hasSurfaceCarveExposure(writer, xx, zz, surfaceObjectExclusionDepth, surfaceObjectExclusionRadius)) {
|
||||
rejected++;
|
||||
continue;
|
||||
}
|
||||
int id = rng.i(0, Integer.MAX_VALUE);
|
||||
try {
|
||||
int result = v.place(xx, -1, zz, writer, objectPlacement, rng, (b, data) -> {
|
||||
@@ -253,16 +347,270 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
||||
return new ObjectPlacementResult(attempts, placed, rejected, nullObjects, errors);
|
||||
}
|
||||
|
||||
@ChunkCoordinates
|
||||
private ObjectPlacementResult placeCaveObject(
|
||||
MantleWriter writer,
|
||||
RNG rng,
|
||||
int chunkX,
|
||||
int chunkZ,
|
||||
IrisObjectPlacement objectPlacement,
|
||||
IrisCaveProfile caveProfile,
|
||||
boolean traceRegen,
|
||||
int metricChunkX,
|
||||
int metricChunkZ,
|
||||
String scope
|
||||
) {
|
||||
int attempts = 0;
|
||||
int placed = 0;
|
||||
int rejected = 0;
|
||||
int nullObjects = 0;
|
||||
int errors = 0;
|
||||
int minX = chunkX << 4;
|
||||
int minZ = chunkZ << 4;
|
||||
int density = objectPlacement.getDensity(rng, minX, minZ, getData());
|
||||
KMap<Long, KList<Integer>> anchorCache = new KMap<>();
|
||||
IrisCaveAnchorMode anchorMode = resolveAnchorMode(objectPlacement, caveProfile);
|
||||
int anchorScanStep = resolveAnchorScanStep(caveProfile);
|
||||
int objectMinDepthBelowSurface = resolveObjectMinDepthBelowSurface(caveProfile);
|
||||
int anchorSearchAttempts = resolveAnchorSearchAttempts(caveProfile);
|
||||
|
||||
for (int i = 0; i < density; i++) {
|
||||
attempts++;
|
||||
IrisObject object = objectPlacement.getScale().get(rng, objectPlacement.getObject(getComplex(), rng));
|
||||
if (object == null) {
|
||||
nullObjects++;
|
||||
if (traceRegen) {
|
||||
Iris.warn("Regen cave object placement null object: chunk=" + metricChunkX + "," + metricChunkZ
|
||||
+ " scope=" + scope
|
||||
+ " densityIndex=" + i
|
||||
+ " density=" + density
|
||||
+ " placementKeys=" + objectPlacement.getPlace().toString(","));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
int x = 0;
|
||||
int z = 0;
|
||||
int y = -1;
|
||||
for (int search = 0; search < anchorSearchAttempts; search++) {
|
||||
int candidateX = rng.i(minX, minX + 15);
|
||||
int candidateZ = rng.i(minZ, minZ + 15);
|
||||
int candidateY = findCaveAnchorY(writer, rng, candidateX, candidateZ, anchorMode, anchorScanStep, objectMinDepthBelowSurface, anchorCache);
|
||||
if (candidateY < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
x = candidateX;
|
||||
z = candidateZ;
|
||||
y = candidateY;
|
||||
break;
|
||||
}
|
||||
|
||||
if (y < 0) {
|
||||
rejected++;
|
||||
continue;
|
||||
}
|
||||
|
||||
int id = rng.i(0, Integer.MAX_VALUE);
|
||||
|
||||
try {
|
||||
int result = object.place(x, y, z, writer, objectPlacement, rng, (b, data) -> {
|
||||
writer.setData(b.getX(), b.getY(), b.getZ(), object.getLoadKey() + "@" + id);
|
||||
if (objectPlacement.isDolphinTarget() && objectPlacement.isUnderwater() && B.isStorageChest(data)) {
|
||||
writer.setData(b.getX(), b.getY(), b.getZ(), MatterStructurePOI.BURIED_TREASURE);
|
||||
}
|
||||
}, null, getData());
|
||||
|
||||
if (result >= 0) {
|
||||
placed++;
|
||||
} else {
|
||||
rejected++;
|
||||
}
|
||||
|
||||
if (traceRegen) {
|
||||
Iris.info("Regen cave object placement result: chunk=" + metricChunkX + "," + metricChunkZ
|
||||
+ " scope=" + scope
|
||||
+ " object=" + object.getLoadKey()
|
||||
+ " resultY=" + result
|
||||
+ " anchorY=" + y
|
||||
+ " px=" + x
|
||||
+ " pz=" + z
|
||||
+ " densityIndex=" + i
|
||||
+ " density=" + density);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
errors++;
|
||||
Iris.reportError(e);
|
||||
Iris.error("Regen cave object placement exception: chunk=" + metricChunkX + "," + metricChunkZ
|
||||
+ " scope=" + scope
|
||||
+ " object=" + object.getLoadKey()
|
||||
+ " densityIndex=" + i
|
||||
+ " density=" + density
|
||||
+ " error=" + e.getClass().getSimpleName() + ":" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return new ObjectPlacementResult(attempts, placed, rejected, nullObjects, errors);
|
||||
}
|
||||
|
||||
private int findCaveAnchorY(MantleWriter writer, RNG rng, int x, int z, IrisCaveAnchorMode anchorMode, int anchorScanStep, int objectMinDepthBelowSurface, KMap<Long, KList<Integer>> anchorCache) {
|
||||
long key = Cache.key(x, z);
|
||||
KList<Integer> anchors = anchorCache.computeIfAbsent(key, (k) -> scanCaveAnchorColumn(writer, anchorMode, anchorScanStep, objectMinDepthBelowSurface, x, z));
|
||||
if (anchors.isEmpty()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (anchors.size() == 1) {
|
||||
return anchors.get(0);
|
||||
}
|
||||
|
||||
return anchors.get(rng.i(0, anchors.size() - 1));
|
||||
}
|
||||
|
||||
private KList<Integer> scanCaveAnchorColumn(MantleWriter writer, IrisCaveAnchorMode anchorMode, int anchorScanStep, int objectMinDepthBelowSurface, int x, int z) {
|
||||
KList<Integer> anchors = new KList<>();
|
||||
int height = getEngineMantle().getEngine().getHeight();
|
||||
int step = Math.max(1, anchorScanStep);
|
||||
int surfaceY = getEngineMantle().getEngine().getHeight(x, z);
|
||||
int maxAnchorY = Math.min(height - 1, surfaceY - Math.max(0, objectMinDepthBelowSurface));
|
||||
if (maxAnchorY <= 1) {
|
||||
return anchors;
|
||||
}
|
||||
|
||||
for (int y = 1; y < maxAnchorY; y += step) {
|
||||
if (!writer.isCarved(x, y, z)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean solidBelow = y <= 0 || !writer.isCarved(x, y - 1, z);
|
||||
boolean solidAbove = y >= (height - 1) || !writer.isCarved(x, y + 1, z);
|
||||
if (matchesCaveAnchor(anchorMode, solidBelow, solidAbove)) {
|
||||
anchors.add(y);
|
||||
}
|
||||
}
|
||||
|
||||
return anchors;
|
||||
}
|
||||
|
||||
private boolean matchesCaveAnchor(IrisCaveAnchorMode anchorMode, boolean solidBelow, boolean solidAbove) {
|
||||
return switch (anchorMode) {
|
||||
case PROFILE_DEFAULT, FLOOR -> solidBelow;
|
||||
case CEILING -> solidAbove;
|
||||
case CENTER -> !solidBelow && !solidAbove;
|
||||
case ANY -> true;
|
||||
};
|
||||
}
|
||||
|
||||
private IrisCaveProfile resolveCaveProfile(IrisCaveProfile preferred, IrisCaveProfile secondary) {
|
||||
IrisCaveProfile dimensionProfile = getDimension().getCaveProfile();
|
||||
if (preferred != null && preferred.isEnabled()) {
|
||||
return preferred;
|
||||
}
|
||||
|
||||
if (secondary != null && secondary.isEnabled()) {
|
||||
return secondary;
|
||||
}
|
||||
|
||||
if (dimensionProfile != null) {
|
||||
return dimensionProfile;
|
||||
}
|
||||
|
||||
return new IrisCaveProfile();
|
||||
}
|
||||
|
||||
private IrisCaveAnchorMode resolveAnchorMode(IrisObjectPlacement objectPlacement, IrisCaveProfile caveProfile) {
|
||||
IrisCaveAnchorMode placementMode = objectPlacement.getCaveAnchorMode();
|
||||
if (placementMode != null && !placementMode.equals(IrisCaveAnchorMode.PROFILE_DEFAULT)) {
|
||||
return placementMode;
|
||||
}
|
||||
|
||||
if (caveProfile == null) {
|
||||
return IrisCaveAnchorMode.FLOOR;
|
||||
}
|
||||
|
||||
IrisCaveAnchorMode profileMode = caveProfile.getDefaultObjectAnchor();
|
||||
if (profileMode == null || profileMode.equals(IrisCaveAnchorMode.PROFILE_DEFAULT)) {
|
||||
return IrisCaveAnchorMode.FLOOR;
|
||||
}
|
||||
|
||||
return profileMode;
|
||||
}
|
||||
|
||||
private int resolveAnchorScanStep(IrisCaveProfile caveProfile) {
|
||||
if (caveProfile == null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return Math.max(1, caveProfile.getAnchorScanStep());
|
||||
}
|
||||
|
||||
private int resolveObjectMinDepthBelowSurface(IrisCaveProfile caveProfile) {
|
||||
if (caveProfile == null) {
|
||||
return 6;
|
||||
}
|
||||
|
||||
return Math.max(0, caveProfile.getObjectMinDepthBelowSurface());
|
||||
}
|
||||
|
||||
private int resolveSurfaceObjectExclusionDepth(IrisCaveProfile caveProfile) {
|
||||
if (caveProfile == null) {
|
||||
return 5;
|
||||
}
|
||||
|
||||
return Math.max(0, caveProfile.getSurfaceObjectExclusionDepth());
|
||||
}
|
||||
|
||||
private int resolveSurfaceObjectExclusionRadius(IrisObject object) {
|
||||
if (object == null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int maxDimension = Math.max(object.getW(), object.getD());
|
||||
return Math.max(1, Math.min(8, Math.floorDiv(Math.max(1, maxDimension), 2)));
|
||||
}
|
||||
|
||||
private int resolveAnchorSearchAttempts(IrisCaveProfile caveProfile) {
|
||||
if (caveProfile == null) {
|
||||
return 6;
|
||||
}
|
||||
|
||||
return Math.max(1, caveProfile.getAnchorSearchAttempts());
|
||||
}
|
||||
|
||||
private boolean hasSurfaceCarveExposure(MantleWriter writer, int x, int z, int depth, int radius) {
|
||||
int horizontalRadius = Math.max(0, radius);
|
||||
for (int dx = -horizontalRadius; dx <= horizontalRadius; dx++) {
|
||||
for (int dz = -horizontalRadius; dz <= horizontalRadius; dz++) {
|
||||
int columnX = x + dx;
|
||||
int columnZ = z + dz;
|
||||
int surfaceY = getEngineMantle().getEngine().getHeight(columnX, columnZ, true);
|
||||
int fromY = Math.max(1, surfaceY - Math.max(0, depth));
|
||||
int toY = Math.min(getEngineMantle().getEngine().getHeight() - 1, surfaceY + 1);
|
||||
for (int y = fromY; y <= toY; y++) {
|
||||
if (writer.isCarved(columnX, y, columnZ)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isRegenTraceThread() {
|
||||
return Thread.currentThread().getName().startsWith("Iris-Regen-")
|
||||
&& IrisSettings.get().getGeneral().isDebug();
|
||||
}
|
||||
|
||||
private record ObjectPlacementSummary(
|
||||
int biomePlacersChecked,
|
||||
int biomePlacersTriggered,
|
||||
int regionPlacersChecked,
|
||||
int regionPlacersTriggered,
|
||||
int biomeSurfacePlacersChecked,
|
||||
int biomeSurfacePlacersTriggered,
|
||||
int biomeCavePlacersChecked,
|
||||
int biomeCavePlacersTriggered,
|
||||
int regionSurfacePlacersChecked,
|
||||
int regionSurfacePlacersTriggered,
|
||||
int regionCavePlacersChecked,
|
||||
int regionCavePlacersTriggered,
|
||||
int objectAttempts,
|
||||
int objectPlaced,
|
||||
int objectRejected,
|
||||
|
||||
@@ -108,6 +108,8 @@ public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
|
||||
output.set(rx, yy, rz, context.getFluid().get(rx, rz));
|
||||
} else if (c.isLava()) {
|
||||
output.set(rx, yy, rz, LAVA);
|
||||
} else if (c.getLiquid() == 3) {
|
||||
output.set(rx, yy, rz, AIR);
|
||||
} else {
|
||||
if (getEngine().getDimension().getCaveLavaHeight() > yy) {
|
||||
output.set(rx, yy, rz, LAVA);
|
||||
|
||||
@@ -102,6 +102,8 @@ public class IrisBiome extends IrisRegistrant implements IRare {
|
||||
private int lockLayersMax = 7;
|
||||
@Desc("Carving configuration for the dimension")
|
||||
private IrisCarving carving = new IrisCarving();
|
||||
@Desc("Profile-driven 3D cave configuration")
|
||||
private IrisCaveProfile caveProfile = new IrisCaveProfile();
|
||||
@Desc("Configuration of fluid bodies such as rivers & lakes")
|
||||
private IrisFluidBodies fluidBodies = new IrisFluidBodies();
|
||||
@MinNumber(1)
|
||||
@@ -194,12 +196,21 @@ public class IrisBiome extends IrisRegistrant implements IRare {
|
||||
}
|
||||
|
||||
public double getGenLinkMax(String loadKey, Engine engine) {
|
||||
if (loadKey == null || loadKey.isBlank()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Integer v = genCacheMax.aquire(() ->
|
||||
{
|
||||
KMap<String, Integer> l = new KMap<>();
|
||||
|
||||
for (IrisBiomeGeneratorLink i : getGenerators()) {
|
||||
l.put(i.getGenerator(), i.getMax());
|
||||
String generatorKey = i.getGenerator();
|
||||
if (generatorKey == null || generatorKey.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
l.put(generatorKey, i.getMax());
|
||||
|
||||
}
|
||||
|
||||
@@ -210,12 +221,21 @@ public class IrisBiome extends IrisRegistrant implements IRare {
|
||||
}
|
||||
|
||||
public double getGenLinkMin(String loadKey, Engine engine) {
|
||||
if (loadKey == null || loadKey.isBlank()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Integer v = genCacheMin.aquire(() ->
|
||||
{
|
||||
KMap<String, Integer> l = new KMap<>();
|
||||
|
||||
for (IrisBiomeGeneratorLink i : getGenerators()) {
|
||||
l.put(i.getGenerator(), i.getMin());
|
||||
String generatorKey = i.getGenerator();
|
||||
if (generatorKey == null || generatorKey.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
l.put(generatorKey, i.getMin());
|
||||
}
|
||||
|
||||
return l;
|
||||
@@ -225,12 +245,21 @@ public class IrisBiome extends IrisRegistrant implements IRare {
|
||||
}
|
||||
|
||||
public IrisBiomeGeneratorLink getGenLink(String loadKey) {
|
||||
if (loadKey == null || loadKey.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return genCache.aquire(() ->
|
||||
{
|
||||
KMap<String, IrisBiomeGeneratorLink> l = new KMap<>();
|
||||
|
||||
for (IrisBiomeGeneratorLink i : getGenerators()) {
|
||||
l.put(i.getGenerator(), i);
|
||||
String generatorKey = i.getGenerator();
|
||||
if (generatorKey == null || generatorKey.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
l.put(generatorKey, i);
|
||||
}
|
||||
|
||||
return l;
|
||||
|
||||
@@ -52,12 +52,21 @@ public class IrisBiomeGeneratorLink {
|
||||
|
||||
public IrisGenerator getCachedGenerator(DataProvider g) {
|
||||
return gen.aquire(() -> {
|
||||
IrisGenerator gen = g.getData().getGeneratorLoader().load(getGenerator());
|
||||
String generatorKey = getGenerator();
|
||||
if (generatorKey == null || generatorKey.isBlank()) {
|
||||
generatorKey = "default";
|
||||
}
|
||||
|
||||
IrisGenerator gen = g.getData().getGeneratorLoader().load(generatorKey);
|
||||
|
||||
if (gen == null) {
|
||||
gen = new IrisGenerator();
|
||||
}
|
||||
|
||||
if (gen.getLoadKey() == null || gen.getLoadKey().isBlank()) {
|
||||
gen.setLoadKey(generatorKey);
|
||||
}
|
||||
|
||||
return gen;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package art.arcane.iris.engine.object;
|
||||
|
||||
import art.arcane.iris.engine.object.annotations.Desc;
|
||||
|
||||
@Desc("Defines which carved-space anchor to target for cave object placement.")
|
||||
public enum IrisCaveAnchorMode {
|
||||
@Desc("Use the active cave profile default anchor mode.")
|
||||
PROFILE_DEFAULT,
|
||||
|
||||
@Desc("Target cave floor anchors where carved space has solid support below.")
|
||||
FLOOR,
|
||||
|
||||
@Desc("Target cave ceiling anchors where carved space has solid support above.")
|
||||
CEILING,
|
||||
|
||||
@Desc("Target carved positions with no immediate solid support above or below.")
|
||||
CENTER,
|
||||
|
||||
@Desc("Target any carved-space anchor.")
|
||||
ANY
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package art.arcane.iris.engine.object;
|
||||
|
||||
import art.arcane.iris.engine.object.annotations.Desc;
|
||||
import art.arcane.iris.engine.object.annotations.MaxNumber;
|
||||
import art.arcane.iris.engine.object.annotations.MinNumber;
|
||||
import art.arcane.iris.engine.object.annotations.Snippet;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@Snippet("cave-field-module")
|
||||
@Accessors(chain = true)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Desc("Represents a modular cave-density layer.")
|
||||
@Data
|
||||
public class IrisCaveFieldModule {
|
||||
@Desc("Density style used by this module.")
|
||||
private IrisGeneratorStyle style = NoiseStyle.CELLULAR_IRIS_DOUBLE.style();
|
||||
|
||||
@MinNumber(0)
|
||||
@Desc("Layer contribution multiplier.")
|
||||
private double weight = 1;
|
||||
|
||||
@MinNumber(-1)
|
||||
@MaxNumber(1)
|
||||
@Desc("Threshold offset applied to this layer before blending.")
|
||||
private double threshold = 0;
|
||||
|
||||
@Desc("Vertical bounds where this module can contribute.")
|
||||
private IrisRange verticalRange = new IrisRange(0, 384);
|
||||
|
||||
@Desc("Invert this module before weighting.")
|
||||
private boolean invert = false;
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package art.arcane.iris.engine.object;
|
||||
|
||||
import art.arcane.iris.engine.object.annotations.ArrayType;
|
||||
import art.arcane.iris.engine.object.annotations.Desc;
|
||||
import art.arcane.iris.engine.object.annotations.MaxNumber;
|
||||
import art.arcane.iris.engine.object.annotations.MinNumber;
|
||||
import art.arcane.iris.engine.object.annotations.Snippet;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@Snippet("cave-profile")
|
||||
@Accessors(chain = true)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Desc("Represents a configurable 3D cave profile.")
|
||||
@Data
|
||||
public class IrisCaveProfile {
|
||||
@Desc("Enable profile-driven cave carving.")
|
||||
private boolean enabled = false;
|
||||
|
||||
@Desc("Global vertical bounds for profile cave carving.")
|
||||
private IrisRange verticalRange = new IrisRange(0, 384);
|
||||
|
||||
@Desc("Base density style for cave field generation.")
|
||||
private IrisGeneratorStyle baseDensityStyle = NoiseStyle.CELLULAR_IRIS_DOUBLE.style();
|
||||
|
||||
@Desc("Detail density style blended into base caves.")
|
||||
private IrisGeneratorStyle detailDensityStyle = new IrisGeneratorStyle(NoiseStyle.SIMPLEX);
|
||||
|
||||
@Desc("Warp style used to distort cave coordinates.")
|
||||
private IrisGeneratorStyle warpStyle = new IrisGeneratorStyle(NoiseStyle.FLAT);
|
||||
|
||||
@MinNumber(0)
|
||||
@Desc("Base cave field multiplier.")
|
||||
private double baseWeight = 1;
|
||||
|
||||
@MinNumber(0)
|
||||
@Desc("Detail cave field multiplier.")
|
||||
private double detailWeight = 0.35;
|
||||
|
||||
@MinNumber(0)
|
||||
@Desc("Coordinate warp strength for cave fields.")
|
||||
private double warpStrength = 0;
|
||||
|
||||
@Desc("Threshold range used for carve cutoff decisions.")
|
||||
private IrisStyledRange densityThreshold = new IrisStyledRange(-0.2, 0.2, NoiseStyle.CELLULAR_IRIS_DOUBLE.style());
|
||||
|
||||
@MinNumber(0)
|
||||
@MaxNumber(1)
|
||||
@Desc("Extra threshold bias subtracted from sampled threshold before carve tests.")
|
||||
private double thresholdBias = 0.16;
|
||||
|
||||
@MinNumber(1)
|
||||
@MaxNumber(8)
|
||||
@Desc("Vertical sample step used while evaluating cave density.")
|
||||
private int sampleStep = 2;
|
||||
|
||||
@MinNumber(0)
|
||||
@MaxNumber(64)
|
||||
@Desc("Minimum solid clearance below terrain surface where carving may occur.")
|
||||
private int surfaceClearance = 4;
|
||||
|
||||
@Desc("Allow profile-driven cave carving to break through terrain surface in selected columns.")
|
||||
private boolean allowSurfaceBreak = true;
|
||||
|
||||
@Desc("Noise style used to decide where surface-breaking cave columns are allowed.")
|
||||
private IrisGeneratorStyle surfaceBreakStyle = new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.08);
|
||||
|
||||
@MinNumber(-1)
|
||||
@MaxNumber(1)
|
||||
@Desc("Minimum signed surface-break noise value required before near-surface carving is allowed.")
|
||||
private double surfaceBreakNoiseThreshold = 0.62;
|
||||
|
||||
@MinNumber(0)
|
||||
@MaxNumber(64)
|
||||
@Desc("Near-surface depth window used for surface-break carve logic.")
|
||||
private int surfaceBreakDepth = 18;
|
||||
|
||||
@MinNumber(0)
|
||||
@MaxNumber(1)
|
||||
@Desc("Additional threshold boost applied while carving in the surface-break depth window.")
|
||||
private double surfaceBreakThresholdBoost = 0.2;
|
||||
|
||||
@MinNumber(0)
|
||||
@MaxNumber(64)
|
||||
@Desc("Minimum depth below terrain surface required for cave-only object anchor placement.")
|
||||
private int objectMinDepthBelowSurface = 6;
|
||||
|
||||
@MinNumber(0)
|
||||
@MaxNumber(32)
|
||||
@Desc("Skip surface-object placement when carved cells exist this many blocks below terrain surface.")
|
||||
private int surfaceObjectExclusionDepth = 5;
|
||||
|
||||
@ArrayType(type = IrisCaveFieldModule.class, min = 1)
|
||||
@Desc("Additional layered cave-density modules.")
|
||||
private KList<IrisCaveFieldModule> modules = new KList<>();
|
||||
|
||||
@Desc("Default cave anchor mode for cave-only object placement.")
|
||||
private IrisCaveAnchorMode defaultObjectAnchor = IrisCaveAnchorMode.FLOOR;
|
||||
|
||||
@MinNumber(1)
|
||||
@MaxNumber(8)
|
||||
@Desc("Vertical scan step used while searching cave anchors.")
|
||||
private int anchorScanStep = 1;
|
||||
|
||||
@MinNumber(1)
|
||||
@MaxNumber(64)
|
||||
@Desc("Maximum random column retries while searching a valid cave object anchor in the chunk.")
|
||||
private int anchorSearchAttempts = 6;
|
||||
|
||||
@Desc("Allow cave water placement below fluid level.")
|
||||
private boolean allowWater = true;
|
||||
|
||||
@MinNumber(0)
|
||||
@MaxNumber(64)
|
||||
@Desc("Minimum depth below terrain surface required before cave water may be placed.")
|
||||
private int waterMinDepthBelowSurface = 12;
|
||||
|
||||
@Desc("Require solid floor support below cave water to reduce cascading cave waterfalls.")
|
||||
private boolean waterRequiresFloor = true;
|
||||
|
||||
@Desc("Allow cave lava placement based on lava height.")
|
||||
private boolean allowLava = true;
|
||||
}
|
||||
@@ -142,10 +142,12 @@ public class IrisDimension extends IrisRegistrant {
|
||||
private boolean postProcessingSlabs = true;
|
||||
@Desc("Add painted walls in post processing")
|
||||
private boolean postProcessingWalls = true;
|
||||
@Desc("Carving configuration for the dimension")
|
||||
private IrisCarving carving = new IrisCarving();
|
||||
@Desc("Configuration of fluid bodies such as rivers & lakes")
|
||||
private IrisFluidBodies fluidBodies = new IrisFluidBodies();
|
||||
@Desc("Carving configuration for the dimension")
|
||||
private IrisCarving carving = new IrisCarving();
|
||||
@Desc("Profile-driven 3D cave configuration")
|
||||
private IrisCaveProfile caveProfile = new IrisCaveProfile();
|
||||
@Desc("Configuration of fluid bodies such as rivers & lakes")
|
||||
private IrisFluidBodies fluidBodies = new IrisFluidBodies();
|
||||
@Desc("forceConvertTo320Height")
|
||||
private Boolean forceConvertTo320Height = false;
|
||||
@Desc("The world environment")
|
||||
|
||||
@@ -689,10 +689,7 @@ public class IrisObject extends IrisRegistrant {
|
||||
// todo Convert this to a dedicated mode.
|
||||
y = (getH() + 1) + rty;
|
||||
if (!config.isForcePlace()) {
|
||||
if (placer.isCarved(x, y, z) ||
|
||||
placer.isCarved(x, y - 1, z) ||
|
||||
placer.isCarved(x, y - 2, z) ||
|
||||
placer.isCarved(x, y - 3, z)) {
|
||||
if (shouldBailForCarvingAnchor(placer, config, x, y, z)) {
|
||||
bail = true;
|
||||
}
|
||||
}
|
||||
@@ -700,7 +697,7 @@ public class IrisObject extends IrisRegistrant {
|
||||
if (config.getMode().equals(ObjectPlaceMode.CENTER_HEIGHT) || config.getMode() == ObjectPlaceMode.CENTER_STILT) {
|
||||
y = (c != null ? c.getSurface() : placer.getHighest(x, z, getLoader(), config.isUnderwater())) + rty;
|
||||
if (!config.isForcePlace()) {
|
||||
if (placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z)) {
|
||||
if (shouldBailForCarvingAnchor(placer, config, x, y, z)) {
|
||||
bail = true;
|
||||
}
|
||||
}
|
||||
@@ -717,7 +714,7 @@ public class IrisObject extends IrisRegistrant {
|
||||
for (int ii = minZ; ii <= maxZ; ii++) {
|
||||
int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty;
|
||||
if (!config.isForcePlace()) {
|
||||
if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) {
|
||||
if (shouldBailForCarvingAnchor(placer, config, i, h, ii)) {
|
||||
bail = true;
|
||||
break;
|
||||
}
|
||||
@@ -743,7 +740,7 @@ public class IrisObject extends IrisRegistrant {
|
||||
for (int ii = minZ; ii <= maxZ; ii += Math.abs(zRadius) + 1) {
|
||||
int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty;
|
||||
if (!config.isForcePlace()) {
|
||||
if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) {
|
||||
if (shouldBailForCarvingAnchor(placer, config, i, h, ii)) {
|
||||
bail = true;
|
||||
break;
|
||||
}
|
||||
@@ -767,7 +764,7 @@ public class IrisObject extends IrisRegistrant {
|
||||
for (int ii = minZ; ii <= maxZ; ii++) {
|
||||
int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty;
|
||||
if (!config.isForcePlace()) {
|
||||
if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) {
|
||||
if (shouldBailForCarvingAnchor(placer, config, i, h, ii)) {
|
||||
bail = true;
|
||||
break;
|
||||
}
|
||||
@@ -795,7 +792,7 @@ public class IrisObject extends IrisRegistrant {
|
||||
for (int ii = minZ; ii <= maxZ; ii += Math.abs(zRadius) + 1) {
|
||||
int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty;
|
||||
if (!config.isForcePlace()) {
|
||||
if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) {
|
||||
if (shouldBailForCarvingAnchor(placer, config, i, h, ii)) {
|
||||
bail = true;
|
||||
break;
|
||||
}
|
||||
@@ -808,7 +805,7 @@ public class IrisObject extends IrisRegistrant {
|
||||
} else if (config.getMode().equals(ObjectPlaceMode.PAINT)) {
|
||||
y = placer.getHighest(x, z, getLoader(), config.isUnderwater()) + rty;
|
||||
if (!config.isForcePlace()) {
|
||||
if (placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z)) {
|
||||
if (shouldBailForCarvingAnchor(placer, config, x, y, z)) {
|
||||
bail = true;
|
||||
}
|
||||
}
|
||||
@@ -816,7 +813,7 @@ public class IrisObject extends IrisRegistrant {
|
||||
} else {
|
||||
y = yv;
|
||||
if (!config.isForcePlace()) {
|
||||
if (placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z)) {
|
||||
if (shouldBailForCarvingAnchor(placer, config, x, y, z)) {
|
||||
bail = true;
|
||||
}
|
||||
}
|
||||
@@ -825,7 +822,7 @@ public class IrisObject extends IrisRegistrant {
|
||||
if (yv >= 0 && config.isBottom()) {
|
||||
y += Math.floorDiv(h, 2);
|
||||
if (!config.isForcePlace()) {
|
||||
bail = placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z);
|
||||
bail = shouldBailForCarvingAnchor(placer, config, x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1160,6 +1157,23 @@ public class IrisObject extends IrisRegistrant {
|
||||
return y;
|
||||
}
|
||||
|
||||
private boolean shouldBailForCarvingAnchor(IObjectPlacer placer, IrisObjectPlacement placement, int x, int y, int z) {
|
||||
boolean carved = isCarvedAnchor(placer, x, y, z);
|
||||
CarvingMode carvingMode = placement.getCarvingSupport();
|
||||
return switch (carvingMode) {
|
||||
case SURFACE_ONLY -> carved;
|
||||
case CARVING_ONLY -> !carved;
|
||||
case ANYWHERE -> false;
|
||||
};
|
||||
}
|
||||
|
||||
private boolean isCarvedAnchor(IObjectPlacer placer, int x, int y, int z) {
|
||||
return placer.isCarved(x, y, z)
|
||||
|| placer.isCarved(x, y - 1, z)
|
||||
|| placer.isCarved(x, y - 2, z)
|
||||
|| placer.isCarved(x, y - 3, z);
|
||||
}
|
||||
|
||||
public IrisObject rotateCopy(IrisObjectRotation rt) {
|
||||
IrisObject copy = copy();
|
||||
copy.rotate(rt, 0, 0, 0);
|
||||
|
||||
@@ -99,6 +99,8 @@ public class IrisObjectPlacement {
|
||||
private boolean underwater = false;
|
||||
@Desc("If set to true, objects will place in carvings (such as underground) or under an overhang.")
|
||||
private CarvingMode carvingSupport = CarvingMode.SURFACE_ONLY;
|
||||
@Desc("When carving placement is enabled, select which carved-space anchor this placement targets.")
|
||||
private IrisCaveAnchorMode caveAnchorMode = IrisCaveAnchorMode.PROFILE_DEFAULT;
|
||||
@Desc("If this is defined, this object wont place on the terrain heightmap, but instead on this virtual heightmap")
|
||||
private IrisNoiseGenerator heightmap;
|
||||
@Desc("If set to true, Iris will try to fill the insides of 'rooms' and 'pockets' where air should fit based off of raytrace checks. This prevents a village house placing in an area where a tree already exists, and instead replaces the parts of the tree where the interior of the structure is. \n\nThis operation does not affect warmed-up generation speed however it does slow down loading objects.")
|
||||
@@ -165,6 +167,7 @@ public class IrisObjectPlacement {
|
||||
p.setOnwater(onwater);
|
||||
p.setSmartBore(smartBore);
|
||||
p.setCarvingSupport(carvingSupport);
|
||||
p.setCaveAnchorMode(caveAnchorMode);
|
||||
p.setUnderwater(underwater);
|
||||
p.setBoreExtendMaxY(boreExtendMaxY);
|
||||
p.setBoreExtendMinY(boreExtendMinY);
|
||||
|
||||
@@ -114,6 +114,8 @@ public class IrisRegion extends IrisRegistrant implements IRare {
|
||||
private double caveBiomeZoom = 1;
|
||||
@Desc("Carving configuration for the dimension")
|
||||
private IrisCarving carving = new IrisCarving();
|
||||
@Desc("Profile-driven 3D cave configuration")
|
||||
private IrisCaveProfile caveProfile = new IrisCaveProfile();
|
||||
@Desc("Configuration of fluid bodies such as rivers & lakes")
|
||||
private IrisFluidBodies fluidBodies = new IrisFluidBodies();
|
||||
@RegistryListResource(IrisBiome.class)
|
||||
|
||||
@@ -203,8 +203,15 @@ public class CustomBiomeSource extends BiomeSource {
|
||||
|
||||
int blockX = x << 2;
|
||||
int blockZ = z << 2;
|
||||
int blockY = (y - engine.getMinHeight()) << 2;
|
||||
IrisBiome irisBiome = engine.getComplex().getTrueBiomeStream().get(blockX, blockZ);
|
||||
int blockY = y << 2;
|
||||
int surfaceY = engine.getComplex().getHeightStream().get(blockX, blockZ).intValue();
|
||||
boolean underground = blockY <= surfaceY - 2;
|
||||
IrisBiome irisBiome = underground
|
||||
? engine.getComplex().getCaveBiomeStream().get(blockX, blockZ)
|
||||
: engine.getComplex().getTrueBiomeStream().get(blockX, blockZ);
|
||||
if (irisBiome == null && underground) {
|
||||
irisBiome = engine.getComplex().getTrueBiomeStream().get(blockX, blockZ);
|
||||
}
|
||||
if (irisBiome == null) {
|
||||
return getFallbackBiome();
|
||||
}
|
||||
@@ -226,7 +233,9 @@ public class CustomBiomeSource extends BiomeSource {
|
||||
return getFallbackBiome();
|
||||
}
|
||||
|
||||
org.bukkit.block.Biome vanillaBiome = irisBiome.getSkyBiome(noiseRng, blockX, blockY, blockZ);
|
||||
org.bukkit.block.Biome vanillaBiome = underground
|
||||
? irisBiome.getGroundBiome(noiseRng, blockX, blockY, blockZ)
|
||||
: irisBiome.getSkyBiome(noiseRng, blockX, blockY, blockZ);
|
||||
Holder<Biome> holder = NMSBinding.biomeToBiomeBase(biomeRegistry, vanillaBiome);
|
||||
if (holder != null) {
|
||||
return holder;
|
||||
|
||||
Reference in New Issue
Block a user