mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-05-19 16:10:42 +00:00
RedoDeco
This commit is contained in:
Vendored
+1
-1
@@ -1 +1 @@
|
||||
699705819
|
||||
-1935789196
|
||||
@@ -27,7 +27,9 @@ import art.arcane.volmlib.util.math.Spiraler;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
@Builder
|
||||
@Data
|
||||
@@ -117,10 +119,50 @@ public class PregenTask {
|
||||
}));
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface InterleavedChunkConsumer {
|
||||
boolean on(int regionX, int regionZ, int chunkX, int chunkZ, boolean firstChunkInRegion, boolean lastChunkInRegion);
|
||||
}
|
||||
|
||||
public void iterateAllChunks(Spiraled s) {
|
||||
iterateRegions(((rX, rZ) -> iterateChunks(rX, rZ, s)));
|
||||
}
|
||||
|
||||
public void iterateAllChunksInterleaved(InterleavedChunkConsumer consumer) {
|
||||
List<int[]> regions = new ArrayList<>();
|
||||
iterateRegions((rX, rZ) -> regions.add(new int[]{rX, rZ}));
|
||||
|
||||
List<List<int[]>> regionChunks = new ArrayList<>();
|
||||
for (int[] region : regions) {
|
||||
List<int[]> chunks = new ArrayList<>();
|
||||
iterateChunks(region[0], region[1], (cx, cz) -> chunks.add(new int[]{region[0], region[1], cx, cz}));
|
||||
if (!chunks.isEmpty()) {
|
||||
regionChunks.add(chunks);
|
||||
}
|
||||
}
|
||||
|
||||
int[] indices = new int[regionChunks.size()];
|
||||
boolean anyRemaining = true;
|
||||
while (anyRemaining) {
|
||||
anyRemaining = false;
|
||||
for (int r = 0; r < regionChunks.size(); r++) {
|
||||
List<int[]> chunks = regionChunks.get(r);
|
||||
int idx = indices[r];
|
||||
if (idx >= chunks.size()) {
|
||||
continue;
|
||||
}
|
||||
anyRemaining = true;
|
||||
int[] entry = chunks.get(idx);
|
||||
boolean first = idx == 0;
|
||||
boolean last = idx == chunks.size() - 1;
|
||||
indices[r]++;
|
||||
if (!consumer.on(entry[0], entry[1], entry[2], entry[3], first, last)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Bounds {
|
||||
private Bound chunk = null;
|
||||
private Bound region = null;
|
||||
|
||||
@@ -355,13 +355,13 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
||||
|
||||
static int computePaperLikeRecommendedCap(int workerThreads) {
|
||||
int normalizedWorkers = Math.max(1, workerThreads);
|
||||
int recommendedCap = normalizedWorkers * 4;
|
||||
int recommendedCap = normalizedWorkers * 2;
|
||||
if (recommendedCap < 8) {
|
||||
return 8;
|
||||
}
|
||||
|
||||
if (recommendedCap > 128) {
|
||||
return 128;
|
||||
if (recommendedCap > 96) {
|
||||
return 96;
|
||||
}
|
||||
|
||||
return recommendedCap;
|
||||
|
||||
@@ -328,7 +328,14 @@ public final class WorldRuntimeControlService {
|
||||
int[] scanOrder = new int[maxY - minY + 1];
|
||||
int index = 0;
|
||||
|
||||
for (int y = maxY; y >= minY; y--) {
|
||||
int runtimeSurface = world.getHighestBlockYAt((int) source.getX(), (int) source.getZ());
|
||||
int startY = Math.min(maxY, runtimeSurface + 1);
|
||||
|
||||
for (int y = startY; y >= minY; y--) {
|
||||
scanOrder[index++] = y;
|
||||
}
|
||||
|
||||
for (int y = startY + 1; y <= maxY; y++) {
|
||||
scanOrder[index++] = y;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,430 @@
|
||||
/*
|
||||
* 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.decorator;
|
||||
|
||||
import art.arcane.iris.Iris;
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import art.arcane.iris.engine.mantle.EngineMantle;
|
||||
import art.arcane.iris.engine.object.IrisBiome;
|
||||
import art.arcane.iris.engine.object.IrisDecorationPart;
|
||||
import art.arcane.iris.engine.object.IrisDecorator;
|
||||
import art.arcane.iris.util.common.data.B;
|
||||
import art.arcane.iris.util.project.hunk.Hunk;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.BlockSupport;
|
||||
import org.bukkit.block.data.Bisected;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.MultipleFacing;
|
||||
import org.bukkit.block.data.type.PointedDripstone;
|
||||
|
||||
final class DecoratorCore {
|
||||
|
||||
private static final long SEED_OFFSET = 29356788L;
|
||||
private static final long PART_FACTOR = 10439677L;
|
||||
|
||||
static final ThreadLocal<PlaceOpts> SCRATCH_OPTS = ThreadLocal.withInitial(PlaceOpts::new);
|
||||
|
||||
static final class PlaceOpts {
|
||||
boolean caveSkipFluid;
|
||||
boolean underwater;
|
||||
int fluidHeight;
|
||||
|
||||
void reset() {
|
||||
caveSkipFluid = false;
|
||||
underwater = false;
|
||||
fluidHeight = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static long partSeed(long baseSeed, int partOrdinal) {
|
||||
return baseSeed + SEED_OFFSET - (partOrdinal * PART_FACTOR);
|
||||
}
|
||||
|
||||
static long partSeed(long baseSeed, IrisDecorationPart part) {
|
||||
return partSeed(baseSeed, part.ordinal());
|
||||
}
|
||||
|
||||
static IrisDecorator pickDecorator(IrisBiome biome, IrisDecorationPart part, RNG gRNG,
|
||||
RNG colRng, IrisData data, double realX, double realZ) {
|
||||
IrisDecorator[] bucket = biome.getDecoratorBucket(part);
|
||||
if (bucket.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
IrisDecorator picked = null;
|
||||
int count = 0;
|
||||
|
||||
for (IrisDecorator d : bucket) {
|
||||
try {
|
||||
if (d.passesChanceGate(gRNG, realX, realZ, data)) {
|
||||
count++;
|
||||
if (count == 1 || colRng.nextInt(count) == 0) {
|
||||
picked = d;
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
return picked;
|
||||
}
|
||||
|
||||
static void placeSingleUp(IrisDecorator decorator, int x, int z,
|
||||
int realX, int height, int realZ, Hunk<BlockData> data,
|
||||
RNG rng, IrisData irisData, boolean caveSkipFluid, EngineMantle mantle) {
|
||||
BlockData bd = decorator.pickBlockData(rng, irisData, realX, realZ);
|
||||
if (bd == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bd instanceof Bisected) {
|
||||
BlockData top = bd.clone();
|
||||
((Bisected) top).setHalf(Bisected.Half.TOP);
|
||||
try {
|
||||
if (!caveSkipFluid || !B.isFluid(data.get(x, height + 2, z))) {
|
||||
data.set(x, height + 2, z, top);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
}
|
||||
bd = bd.clone();
|
||||
((Bisected) bd).setHalf(Bisected.Half.BOTTOM);
|
||||
}
|
||||
|
||||
if (B.isAir(data.get(x, height + 1, z))) {
|
||||
data.set(x, height + 1, z, fixFacesForHunk(bd, data, x, z, realX, height + 1, realZ, mantle));
|
||||
}
|
||||
}
|
||||
|
||||
static void placeSurfaceSingle(IrisDecorator decorator,
|
||||
int x, int z, int realX, int height, int realZ,
|
||||
Hunk<BlockData> data, RNG rng, IrisData irisData,
|
||||
boolean underwater, boolean caveSkipFluid, EngineMantle mantle) {
|
||||
BlockData bdx = data.get(x, height, z);
|
||||
BlockData bd = decorator.pickBlockData(rng, irisData, realX, realZ);
|
||||
|
||||
if (!underwater && !canGoOn(bd, bdx)
|
||||
&& !decorator.isForcePlace() && decorator.getForceBlock() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (decorator.getForceBlock() != null) {
|
||||
if (caveSkipFluid && B.isFluid(bdx)) {
|
||||
return;
|
||||
}
|
||||
data.set(x, height, z, fixFacesForHunk(
|
||||
decorator.getForceBlock().getBlockData(irisData), data, x, z, realX, height, realZ, mantle));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!decorator.isForcePlace()) {
|
||||
if (decorator.getWhitelist() != null
|
||||
&& decorator.getWhitelist().stream().noneMatch(d -> d.getBlockData(irisData).equals(bdx))) {
|
||||
return;
|
||||
}
|
||||
if (decorator.getBlacklist() != null
|
||||
&& decorator.getBlacklist().stream().anyMatch(d -> d.getBlockData(irisData).equals(bdx))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (bd instanceof Bisected) {
|
||||
BlockData top = bd.clone();
|
||||
((Bisected) top).setHalf(Bisected.Half.TOP);
|
||||
try {
|
||||
if (!caveSkipFluid || !B.isFluid(data.get(x, height + 2, z))) {
|
||||
data.set(x, height + 2, z, top);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
}
|
||||
bd = bd.clone();
|
||||
((Bisected) bd).setHalf(Bisected.Half.BOTTOM);
|
||||
}
|
||||
|
||||
if (B.isAir(data.get(x, height + 1, z))) {
|
||||
data.set(x, height + 1, z, fixFacesForHunk(bd, data, x, z, realX, height + 1, realZ, mantle));
|
||||
}
|
||||
}
|
||||
|
||||
static void placeSingleAt(IrisDecorator decorator, int x, int z,
|
||||
int realX, int height, int realZ, Hunk<BlockData> data,
|
||||
RNG rng, IrisData irisData, boolean applyFixFaces, EngineMantle mantle) {
|
||||
BlockData bd = decorator.pickBlockData(rng, irisData, realX, realZ);
|
||||
if (bd == null) {
|
||||
return;
|
||||
}
|
||||
if (applyFixFaces) {
|
||||
bd = fixFacesForHunk(bd, data, x, z, realX, height, realZ, mantle);
|
||||
}
|
||||
data.set(x, height, z, bd);
|
||||
}
|
||||
|
||||
static void placeStackUp(IrisDecorator decorator, int x, int z, int realX, int realZ,
|
||||
int height, int max, Hunk<BlockData> data,
|
||||
RNG rng, IrisData irisData, PlaceOpts opts) {
|
||||
int effectiveMax = max;
|
||||
if (opts.underwater && height < opts.fluidHeight) {
|
||||
effectiveMax = opts.fluidHeight;
|
||||
}
|
||||
|
||||
int stack = computeStack(decorator, rng, realX, realZ, irisData, effectiveMax);
|
||||
|
||||
if (stack == 1) {
|
||||
if (opts.caveSkipFluid && B.isFluid(data.get(x, height, z))) {
|
||||
return;
|
||||
}
|
||||
data.set(x, height, z, decorator.pickBlockDataTop(rng, irisData, realX, realZ));
|
||||
return;
|
||||
}
|
||||
|
||||
BlockData bdx = data.get(x, height, z);
|
||||
|
||||
for (int i = 0; i < stack; i++) {
|
||||
int h = height + i;
|
||||
double threshold = ((double) i) / (stack - 1);
|
||||
BlockData bd = threshold >= decorator.getTopThreshold()
|
||||
? decorator.pickBlockDataTop(rng, irisData, realX, realZ)
|
||||
: decorator.pickBlockData(rng, irisData, realX, realZ);
|
||||
|
||||
if (bd == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == 0 && !opts.underwater && !canGoOn(bd, bdx)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (opts.underwater && height + 1 + i > opts.fluidHeight) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (opts.caveSkipFluid && B.isFluid(data.get(x, height + 1 + i, z))) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (bd instanceof PointedDripstone) {
|
||||
bd = dripstoneBlock(stack, i, BlockFace.UP);
|
||||
}
|
||||
|
||||
data.set(x, height + 1 + i, z, bd);
|
||||
}
|
||||
}
|
||||
|
||||
static void placeStackDown(IrisDecorator decorator, int x, int z, int realX, int realZ,
|
||||
int height, int minHeight, Hunk<BlockData> data,
|
||||
RNG rng, IrisData irisData, int max, PlaceOpts opts, EngineMantle mantle) {
|
||||
int stack = computeStack(decorator, rng, realX, realZ, irisData, max);
|
||||
|
||||
if (stack == 1) {
|
||||
if (opts.caveSkipFluid && B.isFluid(data.get(x, height, z))) {
|
||||
return;
|
||||
}
|
||||
data.set(x, height, z, fixFacesForHunk(
|
||||
decorator.pickBlockDataTop(rng, irisData, realX, realZ),
|
||||
data, x, z, realX, height, realZ, mantle));
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < stack; i++) {
|
||||
int h = height - i;
|
||||
if (h < minHeight) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double threshold = ((double) i) / (double) (stack - 1);
|
||||
BlockData bd = threshold >= decorator.getTopThreshold()
|
||||
? decorator.pickBlockDataTop(rng, irisData, realX, realZ)
|
||||
: decorator.pickBlockData(rng, irisData, realX, realZ);
|
||||
|
||||
if (bd instanceof PointedDripstone) {
|
||||
bd = dripstoneBlock(stack, i, BlockFace.DOWN);
|
||||
}
|
||||
|
||||
if (opts.caveSkipFluid && B.isFluid(data.get(x, h, z))) {
|
||||
break;
|
||||
}
|
||||
data.set(x, h, z, fixFacesForHunk(bd, data, x, z, realX, h, realZ, mantle));
|
||||
}
|
||||
}
|
||||
|
||||
static void placeFloatingSimple(IrisDecorator decorator,
|
||||
int xf, int zf, int realX, int realZ,
|
||||
int height, int max, Hunk<BlockData> data,
|
||||
RNG rng, IrisData irisData) {
|
||||
BlockData bd = decorator.pickBlockData(rng, irisData, realX, realZ);
|
||||
if (bd == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bd instanceof Bisected) {
|
||||
BlockData top = bd.clone();
|
||||
((Bisected) top).setHalf(Bisected.Half.TOP);
|
||||
try {
|
||||
if (max > 2) {
|
||||
data.set(xf, height + 2, zf, top);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
}
|
||||
bd = bd.clone();
|
||||
((Bisected) bd).setHalf(Bisected.Half.BOTTOM);
|
||||
}
|
||||
|
||||
if (max > 1) {
|
||||
data.set(xf, height + 1, zf, bd);
|
||||
}
|
||||
}
|
||||
|
||||
static int placeFloatingStacked(IrisDecorator decorator,
|
||||
int xf, int zf, int realX, int realZ,
|
||||
int height, int max, Hunk<BlockData> data,
|
||||
RNG rng, IrisData irisData) {
|
||||
int stack = decorator.getHeight(rng, realX, realZ, irisData);
|
||||
if (decorator.isScaleStack()) {
|
||||
stack = Math.min((int) Math.ceil((double) max * ((double) stack / 100)), decorator.getAbsoluteMaxStack());
|
||||
} else {
|
||||
stack = Math.min(max, stack);
|
||||
}
|
||||
|
||||
int placed = 0;
|
||||
for (int i = 0; i < stack; i++) {
|
||||
int h = height + 1 + i;
|
||||
if (h >= height + max) {
|
||||
break;
|
||||
}
|
||||
double threshold = stack == 1 ? 0.0 : ((double) i) / (stack - 1);
|
||||
BlockData bd = threshold >= decorator.getTopThreshold()
|
||||
? decorator.pickBlockDataTop(rng, irisData, realX, realZ)
|
||||
: decorator.pickBlockData(rng, irisData, realX, realZ);
|
||||
if (bd == null) {
|
||||
break;
|
||||
}
|
||||
data.set(xf, h, zf, bd);
|
||||
placed++;
|
||||
}
|
||||
return placed;
|
||||
}
|
||||
|
||||
static BlockData fixFacesForHunk(BlockData b, Hunk<BlockData> hunk, int rX, int rZ,
|
||||
int x, int y, int z, EngineMantle mantle) {
|
||||
if (!B.isVineBlock(b)) {
|
||||
return b;
|
||||
}
|
||||
MultipleFacing data = (MultipleFacing) b.clone();
|
||||
data.getFaces().forEach(f -> data.setFace(f, false));
|
||||
|
||||
boolean found = false;
|
||||
for (BlockFace f : BlockFace.values()) {
|
||||
if (!f.isCartesian()) {
|
||||
continue;
|
||||
}
|
||||
int yy = y + f.getModY();
|
||||
|
||||
BlockData r = null;
|
||||
if (mantle != null) {
|
||||
r = mantle.getMantle().get(x + f.getModX(), yy, z + f.getModZ(), BlockData.class);
|
||||
}
|
||||
if (r == null) {
|
||||
r = EngineMantle.AIR;
|
||||
}
|
||||
if (r.isFaceSturdy(f.getOppositeFace(), BlockSupport.FULL)) {
|
||||
found = true;
|
||||
data.setFace(f, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
int xx = rX + f.getModX();
|
||||
int zz = rZ + f.getModZ();
|
||||
if (xx < 0 || xx > 15 || zz < 0 || zz > 15 || yy < 0 || yy > hunk.getHeight()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
r = hunk.get(xx, yy, zz);
|
||||
if (r.isFaceSturdy(f.getOppositeFace(), BlockSupport.FULL)) {
|
||||
found = true;
|
||||
data.setFace(f, true);
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
data.setFace(BlockFace.DOWN, true);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
static boolean canGoOn(BlockData decorator, BlockData surface) {
|
||||
return surface.isFaceSturdy(BlockFace.UP, BlockSupport.FULL);
|
||||
}
|
||||
|
||||
private static int computeStack(IrisDecorator decorator, RNG rng, double realX, double realZ,
|
||||
IrisData irisData, int max) {
|
||||
int stack = decorator.getHeight(rng, realX, realZ, irisData);
|
||||
if (decorator.isScaleStack()) {
|
||||
stack = Math.min((int) Math.ceil((double) max * ((double) stack / 100)), decorator.getAbsoluteMaxStack());
|
||||
} else {
|
||||
stack = Math.min(max, stack);
|
||||
}
|
||||
return stack;
|
||||
}
|
||||
|
||||
// Lazily populated on first dripstone decoration — avoids Bukkit API at class-load time.
|
||||
// Index: 0=TIP, 1=FRUSTUM, 2=BASE. Race on init is benign (only allocation cost, not correctness).
|
||||
private static volatile BlockData[] dripstoneUp;
|
||||
private static volatile BlockData[] dripstoneDown;
|
||||
|
||||
private static BlockData[] buildDripstoneArr(BlockFace direction) {
|
||||
PointedDripstone.Thickness[] order = {
|
||||
PointedDripstone.Thickness.TIP,
|
||||
PointedDripstone.Thickness.FRUSTUM,
|
||||
PointedDripstone.Thickness.BASE
|
||||
};
|
||||
BlockData[] arr = new BlockData[3];
|
||||
for (int k = 0; k < 3; k++) {
|
||||
BlockData bd = Material.POINTED_DRIPSTONE.createBlockData();
|
||||
((PointedDripstone) bd).setThickness(order[k]);
|
||||
((PointedDripstone) bd).setVerticalDirection(direction);
|
||||
arr[k] = bd;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
private static BlockData dripstoneBlock(int stack, int i, BlockFace direction) {
|
||||
int thIdx;
|
||||
if (i == stack - 1) {
|
||||
thIdx = 0;
|
||||
} else if (i == stack - 2) {
|
||||
thIdx = 1;
|
||||
} else {
|
||||
thIdx = 2;
|
||||
}
|
||||
if (direction == BlockFace.UP) {
|
||||
if (dripstoneUp == null) {
|
||||
dripstoneUp = buildDripstoneArr(BlockFace.UP);
|
||||
}
|
||||
return dripstoneUp[thIdx];
|
||||
}
|
||||
if (dripstoneDown == null) {
|
||||
dripstoneDown = buildDripstoneArr(BlockFace.DOWN);
|
||||
}
|
||||
return dripstoneDown[thIdx];
|
||||
}
|
||||
}
|
||||
@@ -18,117 +18,38 @@
|
||||
|
||||
package art.arcane.iris.engine.decorator;
|
||||
|
||||
import art.arcane.iris.Iris;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.object.IrisBiome;
|
||||
import art.arcane.iris.engine.object.IrisDecorationPart;
|
||||
import art.arcane.iris.engine.object.IrisDecorator;
|
||||
import art.arcane.iris.util.project.hunk.Hunk;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import org.bukkit.block.data.Bisected;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/*
|
||||
* Floating island decoration path. Bypasses all canGoOn, slope, whitelist, and blacklist
|
||||
* gating from IrisSurfaceDecorator — the island top IS the biome's designated surface by
|
||||
* construction, so those material-compatibility checks are never meaningful here.
|
||||
*/
|
||||
public class FloatingDecorator {
|
||||
public static final AtomicLong decCandidatesNull = new AtomicLong();
|
||||
|
||||
private static final long SEED_SALT = 29356788L;
|
||||
private static final long PART_SALT = 10439677L;
|
||||
|
||||
public static int decorateColumn(Engine engine, IrisBiome target, IrisDecorationPart part,
|
||||
int xf, int zf, int realX, int realZ,
|
||||
int height, int max, Hunk<BlockData> data, RNG rng) {
|
||||
long gSeed = engine.getSeedManager().getDecorator() + SEED_SALT - (part.ordinal() * PART_SALT);
|
||||
RNG gRNG = new RNG(gSeed);
|
||||
KList<IrisDecorator> candidates = new KList<>();
|
||||
int height, int max, Hunk<BlockData> data, RNG rng,
|
||||
Runnable candidatesNullCallback) {
|
||||
RNG gRNG = new RNG(DecoratorCore.partSeed(engine.getSeedManager().getDecorator(), part));
|
||||
IrisDecorator decorator = DecoratorCore.pickDecorator(target, part, gRNG, rng, engine.getData(), realX, realZ);
|
||||
|
||||
for (IrisDecorator d : target.getDecorators()) {
|
||||
try {
|
||||
if (d.getPartOf().equals(part) && d.getBlockData(target, gRNG, realX, realZ, engine.getData()) != null) {
|
||||
candidates.add(d);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (candidates.isEmpty()) {
|
||||
decCandidatesNull.incrementAndGet();
|
||||
if (decorator == null) {
|
||||
candidatesNullCallback.run();
|
||||
return 0;
|
||||
}
|
||||
|
||||
IrisDecorator decorator = candidates.get(rng.nextInt(candidates.size()));
|
||||
|
||||
if (!decorator.isStacking()) {
|
||||
return placeSimple(decorator, target, xf, zf, realX, realZ, height, max, data, rng, engine);
|
||||
} else {
|
||||
return placeStacked(decorator, target, xf, zf, realX, realZ, height, max, data, rng, engine);
|
||||
}
|
||||
}
|
||||
|
||||
private static int placeSimple(IrisDecorator decorator, IrisBiome target,
|
||||
int xf, int zf, int realX, int realZ,
|
||||
int height, int max, Hunk<BlockData> data, RNG rng, Engine engine) {
|
||||
BlockData bd = decorator.getBlockData100(target, rng, realX, height, realZ, engine.getData());
|
||||
if (bd == null) {
|
||||
return 0;
|
||||
DecoratorCore.placeFloatingSimple(decorator, xf, zf, realX, realZ, height, max, data, rng, engine.getData());
|
||||
return max > 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
if (bd instanceof Bisected) {
|
||||
BlockData top = bd.clone();
|
||||
((Bisected) top).setHalf(Bisected.Half.TOP);
|
||||
try {
|
||||
if (max > 2) {
|
||||
data.set(xf, height + 2, zf, top);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
}
|
||||
bd = bd.clone();
|
||||
((Bisected) bd).setHalf(Bisected.Half.BOTTOM);
|
||||
}
|
||||
|
||||
if (max > 1) {
|
||||
data.set(xf, height + 1, zf, bd);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int placeStacked(IrisDecorator decorator, IrisBiome target,
|
||||
int xf, int zf, int realX, int realZ,
|
||||
int height, int max, Hunk<BlockData> data, RNG rng, Engine engine) {
|
||||
int stack = decorator.getHeight(rng, realX, realZ, engine.getData());
|
||||
|
||||
if (decorator.isScaleStack()) {
|
||||
stack = Math.min((int) Math.ceil((double) max * ((double) stack / 100)), decorator.getAbsoluteMaxStack());
|
||||
} else {
|
||||
stack = Math.min(max, stack);
|
||||
}
|
||||
|
||||
int placed = 0;
|
||||
for (int i = 0; i < stack; i++) {
|
||||
int h = height + 1 + i;
|
||||
if (h >= height + max) {
|
||||
break;
|
||||
}
|
||||
double threshold = stack == 1 ? 0.0 : ((double) i) / (stack - 1);
|
||||
BlockData bd = threshold >= decorator.getTopThreshold()
|
||||
? decorator.getBlockDataForTop(target, rng, realX, height + i, realZ, engine.getData())
|
||||
: decorator.getBlockData100(target, rng, realX, height + i, realZ, engine.getData());
|
||||
if (bd == null) {
|
||||
break;
|
||||
}
|
||||
data.set(xf, h, zf, bd);
|
||||
placed++;
|
||||
}
|
||||
return placed;
|
||||
return DecoratorCore.placeFloatingStacked(decorator, xf, zf, realX, realZ, height, max, data, rng, engine.getData());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,89 +24,42 @@ import art.arcane.iris.engine.object.IrisBiome;
|
||||
import art.arcane.iris.engine.object.IrisDecorationPart;
|
||||
import art.arcane.iris.engine.object.IrisDecorator;
|
||||
import art.arcane.iris.util.common.data.B;
|
||||
import art.arcane.volmlib.util.documentation.BlockCoordinates;
|
||||
import art.arcane.iris.util.project.hunk.Hunk;
|
||||
import art.arcane.volmlib.util.documentation.BlockCoordinates;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.type.PointedDripstone;
|
||||
|
||||
public class IrisCeilingDecorator extends IrisEngineDecorator {
|
||||
private final RNG partRNG;
|
||||
|
||||
public IrisCeilingDecorator(Engine engine) {
|
||||
super(engine, "Ceiling", IrisDecorationPart.CEILING);
|
||||
this.partRNG = new RNG(DecoratorCore.partSeed(getSeed(), IrisDecorationPart.CEILING));
|
||||
}
|
||||
|
||||
@BlockCoordinates
|
||||
@Override
|
||||
public void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1, Hunk<BlockData> data, IrisBiome biome, int height, int max) {
|
||||
RNG rng = getRNG(realX, realZ);
|
||||
IrisDecorator decorator = getDecorator(rng, biome, realX, realZ);
|
||||
public void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1,
|
||||
Hunk<BlockData> data, IrisBiome biome, int height, int max) {
|
||||
boolean caveSkipFluid = biome.getInferredType() == InferredType.CAVE;
|
||||
RNG rng = getRNG(realX, realZ);
|
||||
IrisDecorator decorator = DecoratorCore.pickDecorator(biome, getPart(), partRNG, rng, getData(), realX, realZ);
|
||||
|
||||
if (decorator != null) {
|
||||
if (!decorator.isStacking()) {
|
||||
if (caveSkipFluid && B.isFluid(data.get(x, height, z))) {
|
||||
return;
|
||||
}
|
||||
data.set(x, height, z, fixFaces(decorator.getBlockData100(biome, rng, realX, height, realZ, getData()), data, x, z, realX, height, realZ));
|
||||
} else {
|
||||
int stack = decorator.getHeight(rng, realX, realZ, getData());
|
||||
if (decorator.isScaleStack()) {
|
||||
stack = Math.min((int) Math.ceil((double) max * ((double) stack / 100)), decorator.getAbsoluteMaxStack());
|
||||
} else {
|
||||
stack = Math.min(max, stack);
|
||||
}
|
||||
|
||||
if (stack == 1) {
|
||||
if (caveSkipFluid && B.isFluid(data.get(x, height, z))) {
|
||||
return;
|
||||
}
|
||||
data.set(x, height, z, decorator.getBlockDataForTop(biome, rng, realX, height, realZ, getData()));
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < stack; i++) {
|
||||
int h = height - i;
|
||||
if (h < getEngine().getMinHeight()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double threshold = (((double) i) / (double) (stack - 1));
|
||||
|
||||
BlockData bd = threshold >= decorator.getTopThreshold() ?
|
||||
decorator.getBlockDataForTop(biome, rng, realX, h, realZ, getData()) :
|
||||
decorator.getBlockData100(biome, rng, realX, h, realZ, getData());
|
||||
|
||||
if (bd instanceof PointedDripstone) {
|
||||
PointedDripstone.Thickness th = PointedDripstone.Thickness.BASE;
|
||||
|
||||
if (stack == 2) {
|
||||
th = PointedDripstone.Thickness.FRUSTUM;
|
||||
|
||||
if (i == stack - 1) {
|
||||
th = PointedDripstone.Thickness.TIP;
|
||||
}
|
||||
} else {
|
||||
if (i == stack - 1) {
|
||||
th = PointedDripstone.Thickness.TIP;
|
||||
} else if (i == stack - 2) {
|
||||
th = PointedDripstone.Thickness.FRUSTUM;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bd = Material.POINTED_DRIPSTONE.createBlockData();
|
||||
((PointedDripstone) bd).setThickness(th);
|
||||
((PointedDripstone) bd).setVerticalDirection(BlockFace.DOWN);
|
||||
}
|
||||
|
||||
if (caveSkipFluid && B.isFluid(data.get(x, h, z))) {
|
||||
break;
|
||||
}
|
||||
data.set(x, h, z, bd);
|
||||
}
|
||||
}
|
||||
if (decorator == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!decorator.isStacking()) {
|
||||
if (caveSkipFluid && B.isFluid(data.get(x, height, z))) {
|
||||
return;
|
||||
}
|
||||
DecoratorCore.placeSingleAt(decorator, x, z, realX, height, realZ, data, rng, getData(), true, getEngine().getMantle());
|
||||
return;
|
||||
}
|
||||
|
||||
DecoratorCore.PlaceOpts opts = DecoratorCore.SCRATCH_OPTS.get();
|
||||
opts.reset();
|
||||
opts.caveSkipFluid = caveSkipFluid;
|
||||
DecoratorCore.placeStackDown(decorator, x, z, realX, realZ, height, getEngine().getMinHeight(), data, rng, getData(), max, opts, getEngine().getMantle());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,102 +18,28 @@
|
||||
|
||||
package art.arcane.iris.engine.decorator;
|
||||
|
||||
import art.arcane.iris.Iris;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.framework.EngineAssignedComponent;
|
||||
import art.arcane.iris.engine.framework.EngineDecorator;
|
||||
import art.arcane.iris.engine.mantle.EngineMantle;
|
||||
import art.arcane.iris.engine.object.IrisBiome;
|
||||
import art.arcane.iris.engine.object.IrisDecorationPart;
|
||||
import art.arcane.iris.engine.object.IrisDecorator;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.iris.util.common.data.B;
|
||||
import art.arcane.iris.util.project.hunk.Hunk;
|
||||
import art.arcane.volmlib.util.documentation.BlockCoordinates;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.BlockSupport;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.MultipleFacing;
|
||||
|
||||
public abstract class IrisEngineDecorator extends EngineAssignedComponent implements EngineDecorator {
|
||||
@Getter
|
||||
private final IrisDecorationPart part;
|
||||
private final long seed;
|
||||
private final long modX, modZ;
|
||||
|
||||
public IrisEngineDecorator(Engine engine, String name, IrisDecorationPart part) {
|
||||
super(engine, name + " Decorator");
|
||||
this.part = part;
|
||||
this.seed = getSeed() + 29356788 - (part.ordinal() * 10439677L);
|
||||
this.modX = 29356788 ^ (part.ordinal() + 6);
|
||||
this.modZ = 10439677 ^ (part.ordinal() + 1);
|
||||
}
|
||||
|
||||
@BlockCoordinates
|
||||
protected RNG getRNG(int x, int z) {
|
||||
long seed = DecoratorCore.partSeed(getSeed(), part);
|
||||
long modX = 29356788L ^ (part.ordinal() + 6);
|
||||
long modZ = 10439677L ^ (part.ordinal() + 1);
|
||||
return new RNG(x * modX + z * modZ + seed);
|
||||
}
|
||||
|
||||
protected IrisDecorator getDecorator(RNG rng, IrisBiome biome, double realX, double realZ) {
|
||||
KList<IrisDecorator> v = new KList<>();
|
||||
|
||||
RNG gRNG = new RNG(seed);
|
||||
for (IrisDecorator i : biome.getDecorators()) {
|
||||
try {
|
||||
if (i.getPartOf().equals(part) && i.getBlockData(biome, gRNG, realX, realZ, getData()) != null) {
|
||||
v.add(i);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
Iris.error("PART OF: " + biome.getLoadFile().getAbsolutePath() + " HAS AN INVALID DECORATOR near 'partOf'!!!");
|
||||
}
|
||||
}
|
||||
|
||||
if (v.isNotEmpty()) {
|
||||
return v.get(rng.nextInt(v.size()));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected BlockData fixFaces(BlockData b, Hunk<BlockData> hunk, int rX, int rZ, int x, int y, int z) {
|
||||
if (B.isVineBlock(b)) {
|
||||
MultipleFacing data = (MultipleFacing) b.clone();
|
||||
data.getFaces().forEach(f -> data.setFace(f, false));
|
||||
|
||||
boolean found = false;
|
||||
for (BlockFace f : BlockFace.values()) {
|
||||
if (!f.isCartesian())
|
||||
continue;
|
||||
int yy = y + f.getModY();
|
||||
|
||||
BlockData r = getEngine().getMantle().getMantle().get(x + f.getModX(), yy, z + f.getModZ(), BlockData.class);
|
||||
if (r == null) {
|
||||
r = EngineMantle.AIR;
|
||||
}
|
||||
if (r.isFaceSturdy(f.getOppositeFace(), BlockSupport.FULL)) {
|
||||
found = true;
|
||||
data.setFace(f, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
int xx = rX + f.getModX();
|
||||
int zz = rZ + f.getModZ();
|
||||
if (xx < 0 || xx > 15 || zz < 0 || zz > 15 || yy < 0 || yy > hunk.getHeight())
|
||||
continue;
|
||||
|
||||
r = hunk.get(xx, yy, zz);
|
||||
if (r.isFaceSturdy(f.getOppositeFace(), BlockSupport.FULL)) {
|
||||
found = true;
|
||||
data.setFace(f, true);
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
data.setFace(BlockFace.DOWN, true);
|
||||
return data;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,56 +22,63 @@ import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.object.IrisBiome;
|
||||
import art.arcane.iris.engine.object.IrisDecorationPart;
|
||||
import art.arcane.iris.engine.object.IrisDecorator;
|
||||
import art.arcane.volmlib.util.documentation.BlockCoordinates;
|
||||
import art.arcane.iris.util.project.hunk.Hunk;
|
||||
import art.arcane.volmlib.util.documentation.BlockCoordinates;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
public class IrisSeaFloorDecorator extends IrisEngineDecorator {
|
||||
private final RNG partRNG;
|
||||
|
||||
public IrisSeaFloorDecorator(Engine engine) {
|
||||
super(engine, "Sea Floor", IrisDecorationPart.SEA_FLOOR);
|
||||
this.partRNG = new RNG(DecoratorCore.partSeed(getSeed(), IrisDecorationPart.SEA_FLOOR));
|
||||
}
|
||||
|
||||
@BlockCoordinates
|
||||
@Override
|
||||
public void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1, Hunk<BlockData> data, IrisBiome biome, int height, int max) {
|
||||
public void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1,
|
||||
Hunk<BlockData> data, IrisBiome biome, int height, int max) {
|
||||
RNG rng = getRNG(realX, realZ);
|
||||
IrisDecorator decorator = getDecorator(rng, biome, realX, realZ);
|
||||
IrisDecorator decorator = DecoratorCore.pickDecorator(biome, getPart(), partRNG, rng, getData(), realX, realZ);
|
||||
|
||||
if (decorator != null) {
|
||||
if (!decorator.isStacking()) {
|
||||
if (!decorator.isForcePlace() && !decorator.getSlopeCondition().isDefault()
|
||||
&& !decorator.getSlopeCondition().isValid(getComplex().getSlopeStream().get(realX, realZ))) {
|
||||
return;
|
||||
}
|
||||
if (height >= 0 || height < getEngine().getHeight()) {
|
||||
data.set(x, height, z, decorator.getBlockData100(biome, rng, realX, height, realZ, getData()));
|
||||
}
|
||||
} else {
|
||||
int stack = decorator.getHeight(rng, realX, realZ, getData());
|
||||
if (decorator.isScaleStack()) {
|
||||
int maxStack = max - height;
|
||||
stack = (int) Math.ceil((double) maxStack * ((double) stack / 100));
|
||||
} else stack = Math.min(stack, max - height);
|
||||
|
||||
if (stack == 1) {
|
||||
data.set(x, height, z, decorator.getBlockDataForTop(biome, rng, realX, height, realZ, getData()));
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < stack; i++) {
|
||||
int h = height + i;
|
||||
if (h > max || h > getEngine().getHeight()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double threshold = ((double) i) / (stack - 1);
|
||||
data.set(x, h, z, threshold >= decorator.getTopThreshold() ?
|
||||
decorator.getBlockDataForTop(biome, rng, realX, h, realZ, getData()) :
|
||||
decorator.getBlockData100(biome, rng, realX, h, realZ, getData()));
|
||||
}
|
||||
}
|
||||
if (decorator == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!decorator.isStacking()) {
|
||||
if (!decorator.isForcePlace() && !decorator.getSlopeCondition().isDefault()
|
||||
&& !decorator.getSlopeCondition().isValid(getComplex().getSlopeStream().get(realX, realZ))) {
|
||||
return;
|
||||
}
|
||||
if (height >= 0 || height < getEngine().getHeight()) {
|
||||
data.set(x, height, z, decorator.getBlockData100(biome, rng, realX, height, realZ, getData()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int stack = decorator.getHeight(rng, realX, realZ, getData());
|
||||
if (decorator.isScaleStack()) {
|
||||
stack = (int) Math.ceil((double) (max - height) * ((double) stack / 100));
|
||||
} else {
|
||||
stack = Math.min(stack, max - height);
|
||||
}
|
||||
|
||||
if (stack == 1) {
|
||||
data.set(x, height, z, decorator.getBlockDataForTop(biome, rng, realX, height, realZ, getData()));
|
||||
return;
|
||||
}
|
||||
|
||||
int engineHeight = getEngine().getHeight();
|
||||
for (int i = 0; i < stack; i++) {
|
||||
int h = height + i;
|
||||
if (h > max || h > engineHeight) {
|
||||
continue;
|
||||
}
|
||||
double threshold = ((double) i) / (stack - 1);
|
||||
data.set(x, h, z, threshold >= decorator.getTopThreshold()
|
||||
? decorator.getBlockDataForTop(biome, rng, realX, h, realZ, getData())
|
||||
: decorator.getBlockData100(biome, rng, realX, h, realZ, getData()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,51 +22,57 @@ import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.object.IrisBiome;
|
||||
import art.arcane.iris.engine.object.IrisDecorationPart;
|
||||
import art.arcane.iris.engine.object.IrisDecorator;
|
||||
import art.arcane.volmlib.util.documentation.BlockCoordinates;
|
||||
import art.arcane.iris.util.project.hunk.Hunk;
|
||||
import art.arcane.volmlib.util.documentation.BlockCoordinates;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
public class IrisSeaSurfaceDecorator extends IrisEngineDecorator {
|
||||
private final RNG partRNG;
|
||||
|
||||
public IrisSeaSurfaceDecorator(Engine engine) {
|
||||
super(engine, "Sea Surface", IrisDecorationPart.SEA_SURFACE);
|
||||
this.partRNG = new RNG(DecoratorCore.partSeed(getSeed(), IrisDecorationPart.SEA_SURFACE));
|
||||
}
|
||||
|
||||
@BlockCoordinates
|
||||
@Override
|
||||
public void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1, Hunk<BlockData> data, IrisBiome biome, int height, int max) {
|
||||
public void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1,
|
||||
Hunk<BlockData> data, IrisBiome biome, int height, int max) {
|
||||
RNG rng = getRNG(realX, realZ);
|
||||
IrisDecorator decorator = getDecorator(rng, biome, realX, realZ);
|
||||
IrisDecorator decorator = DecoratorCore.pickDecorator(biome, getPart(), partRNG, rng, getData(), realX, realZ);
|
||||
|
||||
if (decorator != null) {
|
||||
if (!decorator.isStacking()) {
|
||||
if (height >= 0 || height < getEngine().getHeight()) {
|
||||
data.set(x, height + 1, z, decorator.getBlockData100(biome, rng, realX, height, realZ, getData()));
|
||||
}
|
||||
} else {
|
||||
int stack = decorator.getHeight(rng, realX, realZ, getData());
|
||||
if (decorator.isScaleStack()) {
|
||||
int maxStack = max - height;
|
||||
stack = (int) Math.ceil((double) maxStack * ((double) stack / 100));
|
||||
}
|
||||
if (decorator == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (stack == 1) {
|
||||
data.set(x, height, z, decorator.getBlockDataForTop(biome, rng, realX, height, realZ, getData()));
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < stack; i++) {
|
||||
int h = height + i;
|
||||
if (h >= max || h >= getEngine().getHeight()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double threshold = ((double) i) / (stack - 1);
|
||||
data.set(x, h + 1, z, threshold >= decorator.getTopThreshold() ?
|
||||
decorator.getBlockDataForTop(biome, rng, realX, h, realZ, getData()) :
|
||||
decorator.getBlockData100(biome, rng, realX, h, realZ, getData()));
|
||||
}
|
||||
if (!decorator.isStacking()) {
|
||||
if (height >= 0 || height < getEngine().getHeight()) {
|
||||
data.set(x, height + 1, z, decorator.getBlockData100(biome, rng, realX, height, realZ, getData()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int stack = decorator.getHeight(rng, realX, realZ, getData());
|
||||
if (decorator.isScaleStack()) {
|
||||
stack = (int) Math.ceil((double) (max - height) * ((double) stack / 100));
|
||||
}
|
||||
|
||||
if (stack == 1) {
|
||||
data.set(x, height, z, decorator.getBlockDataForTop(biome, rng, realX, height, realZ, getData()));
|
||||
return;
|
||||
}
|
||||
|
||||
int engineHeight = getEngine().getHeight();
|
||||
for (int i = 0; i < stack; i++) {
|
||||
int h = height + i;
|
||||
if (h >= max || h >= engineHeight) {
|
||||
continue;
|
||||
}
|
||||
double threshold = ((double) i) / (stack - 1);
|
||||
data.set(x, h + 1, z, threshold >= decorator.getTopThreshold()
|
||||
? decorator.getBlockDataForTop(biome, rng, realX, h, realZ, getData())
|
||||
: decorator.getBlockData100(biome, rng, realX, h, realZ, getData()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,59 +22,72 @@ import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.object.IrisBiome;
|
||||
import art.arcane.iris.engine.object.IrisDecorationPart;
|
||||
import art.arcane.iris.engine.object.IrisDecorator;
|
||||
import art.arcane.volmlib.util.documentation.BlockCoordinates;
|
||||
import art.arcane.iris.util.project.hunk.Hunk;
|
||||
import art.arcane.iris.util.project.stream.ProceduralStream;
|
||||
import art.arcane.volmlib.util.documentation.BlockCoordinates;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
public class IrisShoreLineDecorator extends IrisEngineDecorator {
|
||||
private final RNG partRNG;
|
||||
|
||||
public IrisShoreLineDecorator(Engine engine) {
|
||||
super(engine, "Shore Line", IrisDecorationPart.SHORE_LINE);
|
||||
this.partRNG = new RNG(DecoratorCore.partSeed(getSeed(), IrisDecorationPart.SHORE_LINE));
|
||||
}
|
||||
|
||||
@BlockCoordinates
|
||||
@Override
|
||||
public void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1, Hunk<BlockData> data, IrisBiome biome, int height, int max) {
|
||||
public void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1,
|
||||
Hunk<BlockData> data, IrisBiome biome, int height, int max) {
|
||||
if (height != getDimension().getFluidHeight()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (height == getDimension().getFluidHeight()) {
|
||||
if (Math.round(getComplex().getHeightStream().get(realX1, realZ)) < getComplex().getFluidHeight() ||
|
||||
Math.round(getComplex().getHeightStream().get(realX_1, realZ)) < getComplex().getFluidHeight() ||
|
||||
Math.round(getComplex().getHeightStream().get(realX, realZ1)) < getComplex().getFluidHeight() ||
|
||||
Math.round(getComplex().getHeightStream().get(realX, realZ_1)) < getComplex().getFluidHeight()
|
||||
) {
|
||||
RNG rng = getRNG(realX, realZ);
|
||||
IrisDecorator decorator = getDecorator(rng, biome, realX, realZ);
|
||||
double complexFluidHeight = getComplex().getFluidHeight();
|
||||
ProceduralStream<Double> heightStream = getComplex().getHeightStream();
|
||||
if (Math.round(heightStream.get(realX1, realZ)) >= complexFluidHeight
|
||||
&& Math.round(heightStream.get(realX_1, realZ)) >= complexFluidHeight
|
||||
&& Math.round(heightStream.get(realX, realZ1)) >= complexFluidHeight
|
||||
&& Math.round(heightStream.get(realX, realZ_1)) >= complexFluidHeight) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (decorator != null) {
|
||||
if (!decorator.isForcePlace() && !decorator.getSlopeCondition().isDefault()
|
||||
&& !decorator.getSlopeCondition().isValid(getComplex().getSlopeStream().get(realX, realZ))) {
|
||||
return;
|
||||
}
|
||||
RNG rng = getRNG(realX, realZ);
|
||||
IrisDecorator decorator = DecoratorCore.pickDecorator(biome, getPart(), partRNG, rng, getData(), realX, realZ);
|
||||
|
||||
if (!decorator.isStacking()) {
|
||||
data.set(x, height + 1, z, decorator.getBlockData100(biome, rng, realX, height, realZ, getData()));
|
||||
} else {
|
||||
int stack = decorator.getHeight(rng, realX, realZ, getData());
|
||||
if (decorator.isScaleStack()) {
|
||||
int maxStack = max - height;
|
||||
stack = (int) Math.ceil((double) maxStack * ((double) stack / 100));
|
||||
} else stack = Math.min(max - height, stack);
|
||||
if (decorator == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (stack == 1) {
|
||||
data.set(x, height, z, decorator.getBlockDataForTop(biome, rng, realX, height, realZ, getData()));
|
||||
return;
|
||||
}
|
||||
if (!decorator.isForcePlace() && !decorator.getSlopeCondition().isDefault()
|
||||
&& !decorator.getSlopeCondition().isValid(getComplex().getSlopeStream().get(realX, realZ))) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < stack; i++) {
|
||||
int h = height + i;
|
||||
double threshold = ((double) i) / (stack - 1);
|
||||
data.set(x, h + 1, z, threshold >= decorator.getTopThreshold() ?
|
||||
decorator.getBlockDataForTop(biome, rng, realX, h, realZ, getData()) :
|
||||
decorator.getBlockData100(biome, rng, realX, h, realZ, getData()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!decorator.isStacking()) {
|
||||
data.set(x, height + 1, z, decorator.getBlockData100(biome, rng, realX, height, realZ, getData()));
|
||||
return;
|
||||
}
|
||||
|
||||
int stack = decorator.getHeight(rng, realX, realZ, getData());
|
||||
if (decorator.isScaleStack()) {
|
||||
stack = (int) Math.ceil((double) (max - height) * ((double) stack / 100));
|
||||
} else {
|
||||
stack = Math.min(max - height, stack);
|
||||
}
|
||||
|
||||
if (stack == 1) {
|
||||
data.set(x, height, z, decorator.getBlockDataForTop(biome, rng, realX, height, realZ, getData()));
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < stack; i++) {
|
||||
int h = height + i;
|
||||
double threshold = ((double) i) / (stack - 1);
|
||||
data.set(x, h + 1, z, threshold >= decorator.getTopThreshold()
|
||||
? decorator.getBlockDataForTop(biome, rng, realX, h, realZ, getData())
|
||||
: decorator.getBlockData100(biome, rng, realX, h, realZ, getData()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,29 +18,27 @@
|
||||
|
||||
package art.arcane.iris.engine.decorator;
|
||||
|
||||
import art.arcane.iris.Iris;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.object.InferredType;
|
||||
import art.arcane.iris.engine.object.IrisBiome;
|
||||
import art.arcane.iris.engine.object.IrisDecorationPart;
|
||||
import art.arcane.iris.engine.object.IrisDecorator;
|
||||
import art.arcane.iris.util.common.data.B;
|
||||
import art.arcane.volmlib.util.documentation.BlockCoordinates;
|
||||
import art.arcane.iris.util.project.hunk.Hunk;
|
||||
import art.arcane.volmlib.util.documentation.BlockCoordinates;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.data.Bisected;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.type.PointedDripstone;
|
||||
|
||||
public class IrisSurfaceDecorator extends IrisEngineDecorator {
|
||||
private final RNG partRNG;
|
||||
|
||||
public IrisSurfaceDecorator(Engine engine) {
|
||||
super(engine, "Surface", IrisDecorationPart.NONE);
|
||||
this.partRNG = new RNG(DecoratorCore.partSeed(getSeed(), IrisDecorationPart.NONE));
|
||||
}
|
||||
|
||||
protected IrisSurfaceDecorator(Engine engine, String name) {
|
||||
super(engine, name, IrisDecorationPart.NONE);
|
||||
this.partRNG = new RNG(DecoratorCore.partSeed(getSeed(), IrisDecorationPart.NONE));
|
||||
}
|
||||
|
||||
protected boolean isSlopeValid(IrisDecorator decorator, int realX, int realZ) {
|
||||
@@ -52,133 +50,33 @@ public class IrisSurfaceDecorator extends IrisEngineDecorator {
|
||||
|
||||
@BlockCoordinates
|
||||
@Override
|
||||
public void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1, Hunk<BlockData> data, IrisBiome biome, int height, int max) {
|
||||
if (biome.getInferredType().equals(InferredType.SHORE) && height < getDimension().getFluidHeight()) {
|
||||
public void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1,
|
||||
Hunk<BlockData> data, IrisBiome biome, int height, int max) {
|
||||
int fluidHeight = getDimension().getFluidHeight();
|
||||
if (biome.getInferredType().equals(InferredType.SHORE) && height < fluidHeight) {
|
||||
return;
|
||||
}
|
||||
|
||||
BlockData bd, bdx;
|
||||
RNG rng = getRNG(realX, realZ);
|
||||
IrisDecorator decorator = getDecorator(rng, biome, realX, realZ);
|
||||
bdx = data.get(x, height, z);
|
||||
boolean underwater = height < getDimension().getFluidHeight() && biome.getInferredType() != InferredType.CAVE;
|
||||
boolean underwater = height < fluidHeight && biome.getInferredType() != InferredType.CAVE;
|
||||
boolean caveSkipFluid = biome.getInferredType() == InferredType.CAVE;
|
||||
RNG rng = getRNG(realX, realZ);
|
||||
IrisDecorator decorator = DecoratorCore.pickDecorator(biome, getPart(), partRNG, rng, getData(), realX, realZ);
|
||||
|
||||
if (decorator != null) {
|
||||
if (!isSlopeValid(decorator, realX, realZ)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!decorator.isStacking()) {
|
||||
bd = decorator.getBlockData100(biome, rng, realX, height, realZ, getData());
|
||||
|
||||
if (!underwater) {
|
||||
if (!canGoOn(bd, bdx) && (!decorator.isForcePlace() && decorator.getForceBlock() == null)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (decorator.getForceBlock() != null) {
|
||||
if (caveSkipFluid && B.isFluid(data.get(x, height, z))) {
|
||||
return;
|
||||
}
|
||||
data.set(x, height, z, fixFaces(decorator.getForceBlock().getBlockData(getData()), data, x, z, realX, height, realZ));
|
||||
} else if (!decorator.isForcePlace()) {
|
||||
if (decorator.getWhitelist() != null && decorator.getWhitelist().stream().noneMatch(d -> d.getBlockData(getData()).equals(bdx))) {
|
||||
return;
|
||||
}
|
||||
if (decorator.getBlacklist() != null && decorator.getBlacklist().stream().anyMatch(d -> d.getBlockData(getData()).equals(bdx))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (bd instanceof Bisected) {
|
||||
bd = bd.clone();
|
||||
((Bisected) bd).setHalf(Bisected.Half.TOP);
|
||||
try {
|
||||
if (!caveSkipFluid || !B.isFluid(data.get(x, height + 2, z))) {
|
||||
data.set(x, height + 2, z, bd);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
}
|
||||
bd = bd.clone();
|
||||
((Bisected) bd).setHalf(Bisected.Half.BOTTOM);
|
||||
}
|
||||
|
||||
if (B.isAir(data.get(x, height + 1, z))) {
|
||||
data.set(x, height + 1, z, fixFaces(bd, data, x, z, realX, height + 1, realZ));
|
||||
}
|
||||
} else {
|
||||
if (height < getDimension().getFluidHeight()) {
|
||||
max = getDimension().getFluidHeight();
|
||||
}
|
||||
|
||||
int stack = decorator.getHeight(rng, realX, realZ, getData());
|
||||
|
||||
if (decorator.isScaleStack()) {
|
||||
stack = Math.min((int) Math.ceil((double) max * ((double) stack / 100)), decorator.getAbsoluteMaxStack());
|
||||
} else {
|
||||
stack = Math.min(max, stack);
|
||||
}
|
||||
|
||||
if (stack == 1) {
|
||||
if (caveSkipFluid && B.isFluid(data.get(x, height, z))) {
|
||||
return;
|
||||
}
|
||||
data.set(x, height, z, decorator.getBlockDataForTop(biome, rng, realX, height, realZ, getData()));
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < stack; i++) {
|
||||
int h = height + i;
|
||||
double threshold = ((double) i) / (stack - 1);
|
||||
bd = threshold >= decorator.getTopThreshold() ?
|
||||
decorator.getBlockDataForTop(biome, rng, realX, h, realZ, getData()) :
|
||||
decorator.getBlockData100(biome, rng, realX, h, realZ, getData());
|
||||
|
||||
if (bd == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == 0 && !underwater && !canGoOn(bd, bdx)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (underwater && height + 1 + i > getDimension().getFluidHeight()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (caveSkipFluid && B.isFluid(data.get(x, height + 1 + i, z))) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (bd instanceof PointedDripstone) {
|
||||
PointedDripstone.Thickness th = PointedDripstone.Thickness.BASE;
|
||||
|
||||
if (stack == 2) {
|
||||
th = PointedDripstone.Thickness.FRUSTUM;
|
||||
|
||||
if (i == stack - 1) {
|
||||
th = PointedDripstone.Thickness.TIP;
|
||||
}
|
||||
} else {
|
||||
if (i == stack - 1) {
|
||||
th = PointedDripstone.Thickness.TIP;
|
||||
} else if (i == stack - 2) {
|
||||
th = PointedDripstone.Thickness.FRUSTUM;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bd = Material.POINTED_DRIPSTONE.createBlockData();
|
||||
((PointedDripstone) bd).setThickness(th);
|
||||
((PointedDripstone) bd).setVerticalDirection(BlockFace.UP);
|
||||
}
|
||||
|
||||
data.set(x, height + 1 + i, z, bd);
|
||||
}
|
||||
}
|
||||
if (decorator == null || !isSlopeValid(decorator, realX, realZ)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (decorator.isStacking()) {
|
||||
DecoratorCore.PlaceOpts opts = DecoratorCore.SCRATCH_OPTS.get();
|
||||
opts.reset();
|
||||
opts.underwater = underwater;
|
||||
opts.fluidHeight = fluidHeight;
|
||||
opts.caveSkipFluid = caveSkipFluid;
|
||||
DecoratorCore.placeStackUp(decorator, x, z, realX, realZ, height, max, data, rng, getData(), opts);
|
||||
return;
|
||||
}
|
||||
|
||||
DecoratorCore.placeSurfaceSingle(decorator, x, z, realX, height, realZ,
|
||||
data, rng, getData(), underwater, caveSkipFluid, getEngine().getMantle());
|
||||
}
|
||||
}
|
||||
|
||||
+5
-3
@@ -59,8 +59,10 @@ public class IrisFloatingChildBiomeModifier extends EngineAssignedModifier<Block
|
||||
private static final AtomicLong decorateNoChange = new AtomicLong();
|
||||
private static final AtomicLong decorateFloorNull = new AtomicLong();
|
||||
private static final java.util.concurrent.ConcurrentHashMap<String, AtomicLong> floorMatHisto = new java.util.concurrent.ConcurrentHashMap<>();
|
||||
private static final AtomicLong decCandidatesNull = new AtomicLong();
|
||||
private static final AtomicLong lastReportMs = new AtomicLong(0L);
|
||||
private static final AtomicLong reportCycle = new AtomicLong(0L);
|
||||
private static final Runnable INC_DEC_CANDIDATES_NULL = () -> decCandidatesNull.incrementAndGet();
|
||||
private final RNG rng;
|
||||
private final EngineDecorator seaSurfaceDecorator;
|
||||
|
||||
@@ -84,7 +86,7 @@ public class IrisFloatingChildBiomeModifier extends EngineAssignedModifier<Block
|
||||
+ " decPlaced=" + decoratePlaced.get()
|
||||
+ " decNoChange=" + decorateNoChange.get()
|
||||
+ " decFloorNull=" + decorateFloorNull.get()
|
||||
+ " decCandidatesNull=" + FloatingDecorator.decCandidatesNull.get()
|
||||
+ " decCandidatesNull=" + decCandidatesNull.get()
|
||||
+ " decSkipNonAir=" + decorateSkippedNotAir.get()
|
||||
+ " decSkipNoInherit=" + decorateSkippedNoInherit.get()
|
||||
+ " decPhaseCols=" + decoratePhaseColumns.get()
|
||||
@@ -126,7 +128,7 @@ public class IrisFloatingChildBiomeModifier extends EngineAssignedModifier<Block
|
||||
decorateNoChange.set(0);
|
||||
decorateFloorNull.set(0);
|
||||
floorMatHisto.clear();
|
||||
FloatingDecorator.decCandidatesNull.set(0);
|
||||
decCandidatesNull.set(0);
|
||||
MantleFloatingObjectComponent.resetObjectCounters();
|
||||
}
|
||||
|
||||
@@ -285,7 +287,7 @@ public class IrisFloatingChildBiomeModifier extends EngineAssignedModifier<Block
|
||||
}
|
||||
try {
|
||||
RNG colRng = rng.nextParallelRNG((int) FloatingIslandSample.columnSeed(baseSeed, wx, wz));
|
||||
int placed = FloatingDecorator.decorateColumn(getEngine(), target, IrisDecorationPart.NONE, xf, zf, wx, wz, topY, max, output, colRng);
|
||||
int placed = FloatingDecorator.decorateColumn(getEngine(), target, IrisDecorationPart.NONE, xf, zf, wx, wz, topY, max, output, colRng, INC_DEC_CANDIDATES_NULL);
|
||||
if (placed > 0) {
|
||||
decoratePlaced.addAndGet(placed);
|
||||
} else {
|
||||
|
||||
@@ -48,6 +48,7 @@ import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.EnumMap;
|
||||
|
||||
@Accessors(chain = true)
|
||||
@NoArgsConstructor
|
||||
@@ -77,6 +78,8 @@ public class IrisBiome extends IrisRegistrant implements IRare {
|
||||
private final transient AtomicCache<KList<CNG>> layerSeaHeightGenerators = new AtomicCache<>();
|
||||
private final transient AtomicCache<KList<IrisOreGenerator>> surfaceOreCache = new AtomicCache<>();
|
||||
private final transient AtomicCache<KList<IrisOreGenerator>> undergroundOreCache = new AtomicCache<>();
|
||||
private final transient AtomicCache<EnumMap<IrisDecorationPart, IrisDecorator[]>> decoratorBuckets = new AtomicCache<>();
|
||||
private static final IrisDecorator[] EMPTY_BUCKET = new IrisDecorator[0];
|
||||
@MinNumber(2)
|
||||
@Required
|
||||
@Desc("This is the human readable name for this biome. This can and should be different than the file name. This is not used for loading biomes in other objects.")
|
||||
@@ -791,7 +794,23 @@ public class IrisBiome extends IrisRegistrant implements IRare {
|
||||
return "biomes";
|
||||
}
|
||||
|
||||
@Override
|
||||
public IrisDecorator[] getDecoratorBucket(IrisDecorationPart part) {
|
||||
return decoratorBuckets.aquire(this::buildDecoratorBuckets).getOrDefault(part, EMPTY_BUCKET);
|
||||
}
|
||||
|
||||
private EnumMap<IrisDecorationPart, IrisDecorator[]> buildDecoratorBuckets() {
|
||||
EnumMap<IrisDecorationPart, KList<IrisDecorator>> staging = new EnumMap<>(IrisDecorationPart.class);
|
||||
for (IrisDecorator d : decorators) {
|
||||
staging.computeIfAbsent(d.getPartOf(), k -> new KList<>()).add(d);
|
||||
}
|
||||
EnumMap<IrisDecorationPart, IrisDecorator[]> result = new EnumMap<>(IrisDecorationPart.class);
|
||||
for (IrisDecorationPart part : IrisDecorationPart.values()) {
|
||||
KList<IrisDecorator> list = staging.get(part);
|
||||
result.put(part, list == null ? EMPTY_BUCKET : list.toArray(EMPTY_BUCKET));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getTypeName() {
|
||||
return "Biome";
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@ public class IrisDecorator {
|
||||
private final transient AtomicCache<CNG> heightGenerator = new AtomicCache<>();
|
||||
private final transient AtomicCache<KList<BlockData>> blockData = new AtomicCache<>();
|
||||
private final transient AtomicCache<KList<BlockData>> blockDataTops = new AtomicCache<>();
|
||||
private final transient AtomicCache<BlockData[]> blockDataArray = new AtomicCache<>();
|
||||
private final transient AtomicCache<BlockData[]> blockDataTopsArray = new AtomicCache<>();
|
||||
@Desc("The varience dispersion is used when multiple blocks are put in the palette. Scatter scrambles them, Wispy shows streak-looking varience")
|
||||
private IrisGeneratorStyle variance = NoiseStyle.STATIC.style();
|
||||
@Desc("Forcefully place this decorant anywhere it is supposed to go even if it should not go on a specific surface block. For example, you could force tallgrass to place on top of stone by using this.")
|
||||
@@ -134,24 +136,23 @@ public class IrisDecorator {
|
||||
return palette;
|
||||
}
|
||||
|
||||
public BlockData getBlockData(IrisBiome b, RNG rng, double x, double z, IrisData data) {
|
||||
public boolean passesChanceGate(RNG rng, double x, double z, IrisData data) {
|
||||
if (getBlockData(data).isEmpty()) {
|
||||
Iris.warn("Empty Block Data for " + b.getName());
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
double xx = x / style.getZoom();
|
||||
double zz = z / style.getZoom();
|
||||
return getGenerator(rng, data).fitDouble(0D, 1D, xx, zz) <= chance;
|
||||
}
|
||||
|
||||
if (getGenerator(rng, data).fitDouble(0D, 1D, xx, zz) <= chance) {
|
||||
if (getBlockData(data).size() == 1) {
|
||||
return getBlockData(data).get(0);
|
||||
}
|
||||
|
||||
return getVarianceGenerator(rng, data).fit(getBlockData(data), z, x); //X and Z must be switched
|
||||
public BlockData getBlockData(IrisBiome b, RNG rng, double x, double z, IrisData data) {
|
||||
if (!passesChanceGate(rng, x, z, data)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
if (getBlockData(data).size() == 1) {
|
||||
return getBlockData(data).get(0);
|
||||
}
|
||||
return getVarianceGenerator(rng, data).fit(getBlockData(data), z, x);
|
||||
}
|
||||
|
||||
public BlockData getBlockData100(IrisBiome b, RNG rng, double x, double y, double z, IrisData data) {
|
||||
@@ -230,6 +231,42 @@ public class IrisDecorator {
|
||||
});
|
||||
}
|
||||
|
||||
public BlockData[] getBlockDataArray(IrisData data) {
|
||||
return blockDataArray.aquire(() -> {
|
||||
KList<BlockData> list = getBlockData(data);
|
||||
return list.toArray(new BlockData[0]);
|
||||
});
|
||||
}
|
||||
|
||||
public BlockData[] getBlockDataTopsArray(IrisData data) {
|
||||
return blockDataTopsArray.aquire(() -> {
|
||||
KList<BlockData> list = getBlockDataTops(data);
|
||||
return list.toArray(new BlockData[0]);
|
||||
});
|
||||
}
|
||||
|
||||
public BlockData pickBlockData(RNG rng, IrisData data, double x, double z) {
|
||||
BlockData[] arr = getBlockDataArray(data);
|
||||
if (arr.length == 0) {
|
||||
return null;
|
||||
}
|
||||
if (arr.length == 1) {
|
||||
return arr[0];
|
||||
}
|
||||
return arr[Math.abs((int) getVarianceGenerator(rng, data).fit(0, arr.length - 1, z, x))];
|
||||
}
|
||||
|
||||
public BlockData pickBlockDataTop(RNG rng, IrisData data, double x, double z) {
|
||||
BlockData[] arr = getBlockDataTopsArray(data);
|
||||
if (arr.length == 0) {
|
||||
return pickBlockData(rng, data, x, z);
|
||||
}
|
||||
if (arr.length == 1) {
|
||||
return arr[0];
|
||||
}
|
||||
return arr[Math.abs((int) getVarianceGenerator(rng, data).fit(0, arr.length - 1, z, x))];
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
public boolean isStacking() {
|
||||
return getStackMax() > 1;
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
package art.arcane.iris.engine.decorator;
|
||||
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import art.arcane.iris.engine.object.IrisBiome;
|
||||
import art.arcane.iris.engine.object.IrisDecorationPart;
|
||||
import art.arcane.iris.engine.object.IrisDecorator;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
public class DecoratorCoreTest {
|
||||
|
||||
@Test
|
||||
public void partSeed_differsByPartOrdinal() {
|
||||
long base = 123456789L;
|
||||
long s0 = DecoratorCore.partSeed(base, 0);
|
||||
long s1 = DecoratorCore.partSeed(base, 1);
|
||||
long s2 = DecoratorCore.partSeed(base, 2);
|
||||
assertNotEquals(s0, s1);
|
||||
assertNotEquals(s1, s2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void partSeed_isDeterministic() {
|
||||
long base = 987654321L;
|
||||
assertEquals(DecoratorCore.partSeed(base, 0), DecoratorCore.partSeed(base, 0));
|
||||
assertEquals(DecoratorCore.partSeed(base, 3), DecoratorCore.partSeed(base, 3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void placeOpts_resetClearsAllFields() {
|
||||
DecoratorCore.PlaceOpts opts = DecoratorCore.SCRATCH_OPTS.get();
|
||||
opts.caveSkipFluid = true;
|
||||
opts.underwater = true;
|
||||
opts.fluidHeight = 99;
|
||||
opts.reset();
|
||||
assertFalse(opts.caveSkipFluid);
|
||||
assertFalse(opts.underwater);
|
||||
assertEquals(0, opts.fluidHeight);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scratchOpts_sameInstanceReturnedWithinThread() {
|
||||
DecoratorCore.PlaceOpts a = DecoratorCore.SCRATCH_OPTS.get();
|
||||
DecoratorCore.PlaceOpts b = DecoratorCore.SCRATCH_OPTS.get();
|
||||
assertSame(a, b);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pickDecorator_emptyBucket_returnsNull() {
|
||||
IrisBiome biome = mock(IrisBiome.class);
|
||||
IrisData data = mock(IrisData.class);
|
||||
when(biome.getDecoratorBucket(IrisDecorationPart.NONE)).thenReturn(new IrisDecorator[0]);
|
||||
|
||||
IrisDecorator result = DecoratorCore.pickDecorator(
|
||||
biome, IrisDecorationPart.NONE, new RNG(1L), new RNG(2L), data, 0.0, 0.0);
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pickDecorator_nonePassChanceGate_returnsNull() {
|
||||
IrisBiome biome = mock(IrisBiome.class);
|
||||
IrisData data = mock(IrisData.class);
|
||||
IrisDecorator d = mock(IrisDecorator.class);
|
||||
when(d.passesChanceGate(any(RNG.class), anyDouble(), anyDouble(), any(IrisData.class))).thenReturn(false);
|
||||
when(biome.getDecoratorBucket(IrisDecorationPart.NONE)).thenReturn(new IrisDecorator[]{d});
|
||||
|
||||
IrisDecorator result = DecoratorCore.pickDecorator(
|
||||
biome, IrisDecorationPart.NONE, new RNG(1L), new RNG(2L), data, 0.0, 0.0);
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pickDecorator_singleCandidate_alwaysReturnsThat() {
|
||||
IrisBiome biome = mock(IrisBiome.class);
|
||||
IrisData data = mock(IrisData.class);
|
||||
IrisDecorator d = mock(IrisDecorator.class);
|
||||
when(d.passesChanceGate(any(RNG.class), anyDouble(), anyDouble(), any(IrisData.class))).thenReturn(true);
|
||||
when(biome.getDecoratorBucket(IrisDecorationPart.NONE)).thenReturn(new IrisDecorator[]{d});
|
||||
|
||||
RNG gRNG = new RNG(42L);
|
||||
for (int t = 0; t < 50; t++) {
|
||||
IrisDecorator result = DecoratorCore.pickDecorator(
|
||||
biome, IrisDecorationPart.NONE, gRNG, new RNG(t * 13L + 7), data, 0.0, 0.0);
|
||||
assertSame(d, result);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pickDecorator_multiplePassingCandidates_selectsUniformly() {
|
||||
IrisBiome biome = mock(IrisBiome.class);
|
||||
IrisData data = mock(IrisData.class);
|
||||
|
||||
int n = 4;
|
||||
IrisDecorator[] decorators = new IrisDecorator[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
IrisDecorator d = mock(IrisDecorator.class);
|
||||
when(d.passesChanceGate(any(RNG.class), anyDouble(), anyDouble(), any(IrisData.class))).thenReturn(true);
|
||||
decorators[i] = d;
|
||||
}
|
||||
when(biome.getDecoratorBucket(IrisDecorationPart.NONE)).thenReturn(decorators);
|
||||
|
||||
RNG gRNG = new RNG(99L);
|
||||
int[] counts = new int[n];
|
||||
int trials = 2000;
|
||||
|
||||
for (int t = 0; t < trials; t++) {
|
||||
IrisDecorator picked = DecoratorCore.pickDecorator(
|
||||
biome, IrisDecorationPart.NONE, gRNG, new RNG(t * 31L + 3), data, 0.0, 0.0);
|
||||
assertNotNull(picked);
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (picked == decorators[i]) {
|
||||
counts[i]++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double expected = trials / (double) n;
|
||||
for (int i = 0; i < n; i++) {
|
||||
double deviation = Math.abs(counts[i] - expected) / expected;
|
||||
assertTrue("Decorator " + i + " selected " + counts[i] + " times; expected ~" + (int) expected
|
||||
+ " (deviation " + String.format("%.0f%%", deviation * 100) + ")", deviation < 0.20);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user