mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-07-19 02:36:59 +00:00
289 lines
9.0 KiB
Java
289 lines
9.0 KiB
Java
package com.volmit.iris.object;
|
|
|
|
import com.volmit.iris.Iris;
|
|
import com.volmit.iris.generator.noise.CNG;
|
|
import com.volmit.iris.manager.IrisDataManager;
|
|
import com.volmit.iris.scaffold.cache.AtomicCache;
|
|
import com.volmit.iris.scaffold.data.DataProvider;
|
|
import com.volmit.iris.util.*;
|
|
import lombok.AllArgsConstructor;
|
|
import lombok.Data;
|
|
import lombok.EqualsAndHashCode;
|
|
import lombok.NoArgsConstructor;
|
|
import lombok.experimental.Accessors;
|
|
import org.bukkit.Material;
|
|
import org.bukkit.block.data.BlockData;
|
|
|
|
@EqualsAndHashCode()
|
|
@Accessors(chain = true)
|
|
@NoArgsConstructor
|
|
@AllArgsConstructor
|
|
@Desc("Represents an iris object placer. It places objects.")
|
|
@Data
|
|
public class IrisObjectPlacement
|
|
{
|
|
@RegistryListObject
|
|
@Required
|
|
@ArrayType(min = 1, type = String.class)
|
|
@DontObfuscate
|
|
@Desc("List of objects to place")
|
|
private KList<String> place = new KList<>();
|
|
|
|
@Desc("Rotate this objects placement")
|
|
private IrisObjectRotation rotation = new IrisObjectRotation();
|
|
|
|
@DontObfuscate
|
|
@Desc("Limit the max height or min height of placement.")
|
|
private IrisObjectLimit clamp = new IrisObjectLimit();
|
|
|
|
@MinNumber(0)
|
|
@MaxNumber(1)
|
|
@DontObfuscate
|
|
@Desc("The maximum layer level of a snow filter overtop of this placement. Set to 0 to disable. Max of 1.")
|
|
private double snow = 0;
|
|
|
|
@Required
|
|
@MinNumber(0)
|
|
@MaxNumber(1)
|
|
@DontObfuscate
|
|
@Desc("The chance for this to place in a chunk. If you need multiple per chunk, set this to 1 and use density.")
|
|
private double chance = 1;
|
|
|
|
@MinNumber(1)
|
|
@DontObfuscate
|
|
@Desc("If the chance check passes, place this many in a single chunk")
|
|
private int density = 1;
|
|
|
|
@MaxNumber(64)
|
|
@MinNumber(0)
|
|
@DontObfuscate
|
|
@Desc("If the place mode is set to stilt, you can over-stilt it even further into the ground. Especially useful when using fast stilt due to inaccuracies.")
|
|
private int overStilt = 0;
|
|
|
|
@MaxNumber(64)
|
|
@MinNumber(0)
|
|
@DontObfuscate
|
|
@Desc("When bore is enabled, expand max-y of the cuboid it removes")
|
|
private int boreExtendMaxY = 0;
|
|
|
|
@MaxNumber(64)
|
|
@MinNumber(-1)
|
|
@DontObfuscate
|
|
@Desc("When bore is enabled, lower min-y of the cuboid it removes")
|
|
private int boreExtendMinY = 0;
|
|
|
|
@DontObfuscate
|
|
@Desc("If set to true, objects will place on the terrain height, ignoring the water surface.")
|
|
private boolean underwater = false;
|
|
|
|
@DontObfuscate
|
|
@Desc("If set to true, objects will place in carvings (such as underground) or under an overhang.")
|
|
private CarvingMode carvingSupport = CarvingMode.SURFACE_ONLY;
|
|
|
|
@DontObfuscate
|
|
@Desc("If this is defined, this object wont place on the terrain heightmap, but instead on this virtual heightmap")
|
|
private IrisNoiseGenerator heightmap;
|
|
|
|
@DontObfuscate
|
|
@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.")
|
|
private boolean smartBore = false;
|
|
|
|
@DontObfuscate
|
|
@Desc("If set to true, Blocks placed underwater that could be waterlogged are waterlogged.")
|
|
private boolean waterloggable = false;
|
|
|
|
@DontObfuscate
|
|
@Desc("If set to true, objects will place on the fluid height level Such as boats.")
|
|
private boolean onwater = false;
|
|
|
|
@DontObfuscate
|
|
@Desc("If set to true, this object will only place parts of itself where blocks already exist. Warning: Melding is very performance intensive!")
|
|
private boolean meld = false;
|
|
|
|
@DontObfuscate
|
|
@Desc("If set to true, this object will place from the ground up instead of height checks when not y locked to the surface. This is not compatable with X and Z axis rotations (it may look off)")
|
|
private boolean bottom = false;
|
|
|
|
@DontObfuscate
|
|
@Desc("If set to true, air will be placed before the schematic places.")
|
|
private boolean bore = false;
|
|
|
|
@DontObfuscate
|
|
@Desc("Use a generator to warp the field of coordinates. Using simplex for example would make a square placement warp like a flag")
|
|
private IrisGeneratorStyle warp = new IrisGeneratorStyle(NoiseStyle.FLAT);
|
|
|
|
@DontObfuscate
|
|
@Desc("If the place mode is set to CENTER_HEIGHT_RIGID and you have an X/Z translation, Turning on translate center will also translate the center height check.")
|
|
private boolean translateCenter = false;
|
|
|
|
@DontObfuscate
|
|
@Desc("The placement mode")
|
|
private ObjectPlaceMode mode = ObjectPlaceMode.CENTER_HEIGHT;
|
|
|
|
@ArrayType(min = 1, type = IrisObjectReplace.class)
|
|
@DontObfuscate
|
|
@Desc("Find and replace blocks")
|
|
private KList<IrisObjectReplace> edit = new KList<>();
|
|
|
|
@DontObfuscate
|
|
@Desc("Translate this object's placement")
|
|
private IrisObjectTranslate translate = new IrisObjectTranslate();
|
|
|
|
@DontObfuscate
|
|
@Desc("Scale Objects")
|
|
private IrisObjectScale scale = new IrisObjectScale();
|
|
|
|
@ArrayType(min = 1, type = IrisObjectLoot.class)
|
|
@DontObfuscate
|
|
@Desc("The loot tables to apply to these objects")
|
|
private KList<IrisObjectLoot> loot = new KList<>();
|
|
|
|
public IrisObjectPlacement toPlacement(String... place)
|
|
{
|
|
IrisObjectPlacement p = new IrisObjectPlacement();
|
|
p.setPlace(new KList<>(place));
|
|
p.setTranslateCenter(translateCenter);
|
|
p.setMode(mode);
|
|
p.setEdit(edit);
|
|
p.setTranslate(translate);
|
|
p.setWarp(warp);
|
|
p.setBore(bore);
|
|
p.setMeld(meld);
|
|
p.setWaterloggable(waterloggable);
|
|
p.setOnwater(onwater);
|
|
p.setSmartBore(smartBore);
|
|
p.setCarvingSupport(carvingSupport);
|
|
p.setUnderwater(underwater);
|
|
p.setBoreExtendMaxY(boreExtendMaxY);
|
|
p.setBoreExtendMinY(boreExtendMinY);
|
|
p.setOverStilt(overStilt);
|
|
p.setDensity(density);
|
|
p.setChance(chance);
|
|
p.setSnow(snow);
|
|
p.setClamp(clamp);
|
|
p.setRotation(rotation);
|
|
p.setLoot(loot);
|
|
return p;
|
|
}
|
|
|
|
private final transient AtomicCache<CNG> surfaceWarp = new AtomicCache<>();
|
|
|
|
public CNG getSurfaceWarp(RNG rng)
|
|
{
|
|
return surfaceWarp.aquire(() ->
|
|
{
|
|
return getWarp().create(rng);
|
|
});
|
|
}
|
|
|
|
public double warp(RNG rng, double x, double y, double z)
|
|
{
|
|
return getSurfaceWarp(rng).fitDouble(-(getWarp().getMultiplier() / 2D), (getWarp().getMultiplier() / 2D), x, y, z);
|
|
}
|
|
|
|
public int getTriesForChunk(RNG random)
|
|
{
|
|
if(chance <= 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if(chance >= 1 || random.nextDouble() < chance)
|
|
{
|
|
return density;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
public IrisObject getObject(DataProvider g, RNG random)
|
|
{
|
|
if(place.isEmpty())
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return g.getData().getObjectLoader().load(place.get(random.nextInt(place.size())));
|
|
}
|
|
|
|
public boolean isVacuum() {
|
|
return getMode().equals(ObjectPlaceMode.VACUUM);
|
|
}
|
|
|
|
private transient AtomicCache<TableCache> cache = new AtomicCache<>();
|
|
|
|
private class TableCache {
|
|
transient WeightedRandom<IrisLootTable> global = new WeightedRandom<>();
|
|
transient KMap<Material, WeightedRandom<IrisLootTable>> basic = new KMap<>();
|
|
transient KMap<Material, KMap<BlockData, WeightedRandom<IrisLootTable>>> exact = new KMap<>();
|
|
}
|
|
|
|
private TableCache getCache(IrisDataManager manager) {
|
|
return cache.aquire(() -> {
|
|
TableCache tc = new TableCache();
|
|
|
|
for (IrisObjectLoot loot : getLoot()) {
|
|
IrisLootTable table = manager.getLootLoader().load(loot.getName());
|
|
if (table == null) {
|
|
Iris.warn("Couldn't find loot table " + loot.getName());
|
|
continue;
|
|
}
|
|
|
|
if (loot.getFilter().isEmpty()) //Table applies to all containers
|
|
{
|
|
tc.global.put(table, loot.getWeight());
|
|
} else if (!loot.isExact()) //Table is meant to be by type
|
|
{
|
|
for (BlockData filterData : loot.getFilter(manager)) {
|
|
if (!tc.basic.containsKey(filterData.getMaterial())) {
|
|
tc.basic.put(filterData.getMaterial(), new WeightedRandom<>());
|
|
}
|
|
|
|
tc.basic.get(filterData.getMaterial()).put(table, loot.getWeight());
|
|
}
|
|
} else //Filter is exact
|
|
{
|
|
for (BlockData filterData : loot.getFilter(manager)) {
|
|
if (!tc.exact.containsKey(filterData.getMaterial())) {
|
|
tc.exact.put(filterData.getMaterial(), new KMap<>());
|
|
}
|
|
|
|
if (!tc.exact.get(filterData.getMaterial()).containsKey(filterData)) {
|
|
tc.exact.get(filterData.getMaterial()).put(filterData, new WeightedRandom<>());
|
|
}
|
|
|
|
tc.exact.get(filterData.getMaterial()).get(filterData).put(table, loot.getWeight());
|
|
}
|
|
}
|
|
}
|
|
return tc;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Gets the loot table that should be used for the block
|
|
* @param data The block data of the block
|
|
* @param dataManager Iris Data Manager
|
|
* @return The loot table it should use.
|
|
*/
|
|
public IrisLootTable getTable(BlockData data, IrisDataManager dataManager) {
|
|
TableCache cache = getCache(dataManager);
|
|
|
|
if(B.isStorageChest(data))
|
|
{
|
|
IrisLootTable picked = null;
|
|
if (cache.exact.containsKey(data.getMaterial()) && cache.exact.containsKey(data)) {
|
|
picked = cache.exact.get(data.getMaterial()).get(data).pullRandom();
|
|
} else if (cache.basic.containsKey(data.getMaterial())) {
|
|
picked = cache.basic.get(data.getMaterial()).pullRandom();
|
|
} else if (cache.global.getSize() > 0){
|
|
picked = cache.global.pullRandom();
|
|
}
|
|
|
|
return picked;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|