package com.gtnewhorizon.structurelib.structure;
import com.gtnewhorizon.structurelib.StructureLibAPI;
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
import com.gtnewhorizon.structurelib.structure.adders.IBlockAdder;
import com.gtnewhorizon.structurelib.structure.adders.ITileAdder;
import com.gtnewhorizon.structurelib.util.Vec3Impl;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.IIcon;
import net.minecraft.world.World;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import static java.lang.Integer.MIN_VALUE;
/**
* Fluent API for structure checking!
*
* (Just import static this class to have a nice fluent syntax while defining structure definitions)
*/
public class StructureUtility {
private static final String NICE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz=|!@#$%&()[]{};:<>/?_,.*^'`";
@SuppressWarnings("rawtypes")
private static final Map STEP = new HashMap<>();
@SuppressWarnings("rawtypes")
private static final IStructureElement AIR = new IStructureElement() {
@Override
public boolean check(Object t, World world, int x, int y, int z) {
return world.isAirBlock(x, y, z);
}
@Override
public boolean spawnHint(Object o, World world, int x, int y, int z, ItemStack trigger) {
StructureLibAPI.hintParticle(world, x, y, z, StructureLibAPI.getBlockHint(), 13);
return true;
}
@Override
public boolean placeBlock(Object o, World world, int x, int y, int z, ItemStack trigger) {
world.setBlock(x, y, z, Blocks.air, 0, 2);
return false;
}
};
@SuppressWarnings("rawtypes")
private static final IStructureElement NOT_AIR = new IStructureElement() {
@Override
public boolean check(Object t, World world, int x, int y, int z) {
return !world.isAirBlock(x, y, z);
}
@Override
public boolean spawnHint(Object o, World world, int x, int y, int z, ItemStack trigger) {
StructureLibAPI.hintParticle(world, x, y, z, StructureLibAPI.getBlockHint(), 14);
return true;
}
@Override
public boolean placeBlock(Object o, World world, int x, int y, int z, ItemStack trigger) {
world.setBlock(x, y, z, StructureLibAPI.getBlockHint(), 14, 2);
return true;
}
};
@SuppressWarnings("rawtypes")
private static final IStructureElement ERROR = new IStructureElement() {
@Override
public boolean check(Object t, World world, int x, int y, int z) {
return false;
}
@Override
public boolean spawnHint(Object o, World world, int x, int y, int z, ItemStack trigger) {
StructureLibAPI.hintParticle(world, x, y, z, StructureLibAPI.getBlockHint(), 15);
return true;
}
@Override
public boolean placeBlock(Object o, World world, int x, int y, int z, ItemStack trigger) {
return true;
}
};
private StructureUtility() {
}
@SuppressWarnings("unchecked")
public static IStructureElement isAir() {
return AIR;
}
@SuppressWarnings("unchecked")
public static IStructureElement notAir() {
return NOT_AIR;
}
/**
* Check returns false.
* Placement is always handled by this and does nothing.
* Makes little to no use it in fallback chain.
*
*/
@SuppressWarnings("unchecked")
public static IStructureElement error() {
return ERROR;
}
//region hint only
/**
* Check always returns: true.
*
*/
public static IStructureElementNoPlacement ofHint(int dots) {
int meta = dots - 1;
return new IStructureElementNoPlacement() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
return true;
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
StructureLibAPI.hintParticle(world, x, y, z, StructureLibAPI.getBlockHint(), meta);
return false;
}
};
}
/**
* Check always returns: true.
*
*/
public static IStructureElementNoPlacement ofHintDeferred(Supplier icons) {
return new IStructureElementNoPlacement() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
return true;
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
StructureLibAPI.hintParticle(world, x, y, z, icons.get());
return false;
}
};
}
/**
* Check always returns: true.
*
*/
public static IStructureElementNoPlacement ofHintDeferred(Supplier icons, short[] RGBa) {
return new IStructureElementNoPlacement() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
return true;
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
StructureLibAPI.hintParticleTinted(world, x, y, z, icons.get(), RGBa);
return false;
}
};
}
//endregion
//region block
/**
* Does not allow Block duplicates (with different meta)
*/
public static IStructureElementNoPlacement ofBlocksFlatHint(Map blocsMap, Block hintBlock, int hintMeta) {
if (blocsMap == null || blocsMap.isEmpty() || hintBlock == null) {
throw new IllegalArgumentException();
}
return new IStructureElementNoPlacement() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
Block worldBlock = world.getBlock(x, y, z);
return blocsMap.getOrDefault(worldBlock, MIN_VALUE) == worldBlock.getDamageValue(world, x, y, z);
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
StructureLibAPI.hintParticle(world, x, y, z, hintBlock, hintMeta);
return true;
}
};
}
/**
* Allows block duplicates (with different meta)
*/
public static IStructureElementNoPlacement ofBlocksMapHint(Map> blocsMap, Block hintBlock, int hintMeta) {
if (blocsMap == null || blocsMap.isEmpty() || hintBlock == null) {
throw new IllegalArgumentException();
}
for (Collection value : blocsMap.values()) {
if (value.isEmpty()) {
throw new IllegalArgumentException();
}
}
return new IStructureElementNoPlacement() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
Block worldBlock = world.getBlock(x, y, z);
return blocsMap.getOrDefault(worldBlock, Collections.emptySet()).contains(worldBlock.getDamageValue(world, x, y, z));
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
StructureLibAPI.hintParticle(world, x, y, z, hintBlock, hintMeta);
return true;
}
};
}
public static IStructureElementNoPlacement ofBlockHint(Block block, int meta, Block hintBlock, int hintMeta) {
if (block == null || hintBlock == null) {
throw new IllegalArgumentException();
}
return new IStructureElementNoPlacement() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
Block worldBlock = world.getBlock(x, y, z);
return block == worldBlock && meta == worldBlock.getDamageValue(world, x, y, z);
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
StructureLibAPI.hintParticle(world, x, y, z, hintBlock, hintMeta);
return true;
}
};
}
public static IStructureElementNoPlacement ofBlockHint(Block block, int meta) {
return ofBlockHint(block, meta, block, meta);
}
public static IStructureElementNoPlacement ofBlockAdderHint(IBlockAdder iBlockAdder, Block hintBlock, int hintMeta) {
if (iBlockAdder == null || hintBlock == null) {
throw new IllegalArgumentException();
}
return new IStructureElementNoPlacement() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
Block worldBlock = world.getBlock(x, y, z);
return iBlockAdder.apply(t, worldBlock, worldBlock.getDamageValue(world, x, y, z));
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
StructureLibAPI.hintParticle(world, x, y, z, hintBlock, hintMeta);
return true;
}
};
}
/**
* Does not allow Block duplicates (with different meta)
*/
public static IStructureElement ofBlocksFlat(Map blocsMap, Block defaultBlock, int defaultMeta) {
if (blocsMap == null || blocsMap.isEmpty() || defaultBlock == null) {
throw new IllegalArgumentException();
}
if (defaultBlock instanceof ICustomBlockSetting) {
return new IStructureElement() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
Block worldBlock = world.getBlock(x, y, z);
return blocsMap.getOrDefault(worldBlock, MIN_VALUE) == worldBlock.getDamageValue(world, x, y, z);
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
((ICustomBlockSetting) defaultBlock).setBlock(world, x, y, z, defaultMeta);
return true;
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
StructureLibAPI.hintParticle(world, x, y, z, defaultBlock, defaultMeta);
return true;
}
};
} else {
return new IStructureElement() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
Block worldBlock = world.getBlock(x, y, z);
return blocsMap.getOrDefault(worldBlock, MIN_VALUE) == worldBlock.getDamageValue(world, x, y, z);
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
world.setBlock(x, y, z, defaultBlock, defaultMeta, 2);
return true;
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
StructureLibAPI.hintParticle(world, x, y, z, defaultBlock, defaultMeta);
return true;
}
};
}
}
/**
* Allows block duplicates (with different meta)
*/
public static IStructureElement ofBlocksMap(Map> blocsMap, Block defaultBlock, int defaultMeta) {
if (blocsMap == null || blocsMap.isEmpty() || defaultBlock == null) {
throw new IllegalArgumentException();
}
for (Collection value : blocsMap.values()) {
if (value.isEmpty()) {
throw new IllegalArgumentException();
}
}
if (defaultBlock instanceof ICustomBlockSetting) {
return new IStructureElement() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
Block worldBlock = world.getBlock(x, y, z);
return blocsMap.getOrDefault(worldBlock, Collections.emptySet()).contains(worldBlock.getDamageValue(world, x, y, z));
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
((ICustomBlockSetting) defaultBlock).setBlock(world, x, y, z, defaultMeta);
return true;
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
StructureLibAPI.hintParticle(world, x, y, z, defaultBlock, defaultMeta);
return true;
}
};
} else {
return new IStructureElement() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
Block worldBlock = world.getBlock(x, y, z);
return blocsMap.getOrDefault(worldBlock, Collections.emptySet()).contains(worldBlock.getDamageValue(world, x, y, z));
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
world.setBlock(x, y, z, defaultBlock, defaultMeta, 2);
return true;
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
StructureLibAPI.hintParticle(world, x, y, z, defaultBlock, defaultMeta);
return true;
}
};
}
}
public static IStructureElement ofBlock(Block block, int meta, Block defaultBlock, int defaultMeta) {
if (block == null || defaultBlock == null) {
throw new IllegalArgumentException();
}
if (block instanceof ICustomBlockSetting) {
return new IStructureElement() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
Block worldBlock = world.getBlock(x, y, z);
return block == worldBlock && meta == worldBlock.getDamageValue(world, x, y, z);
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
((ICustomBlockSetting) defaultBlock).setBlock(world, x, y, z, defaultMeta);
return true;
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
StructureLibAPI.hintParticle(world, x, y, z, defaultBlock, defaultMeta);
return true;
}
};
} else {
return new IStructureElement() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
Block worldBlock = world.getBlock(x, y, z);
return block == worldBlock && meta == worldBlock.getDamageValue(world, x, y, z);
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
world.setBlock(x, y, z, defaultBlock, defaultMeta, 2);
return true;
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
StructureLibAPI.hintParticle(world, x, y, z, defaultBlock, defaultMeta);
return true;
}
};
}
}
/**
* Same as above but ignores target meta id
*/
public static IStructureElement ofBlockAnyMeta(Block block, Block defaultBlock, int defaultMeta) {
if (block == null || defaultBlock == null) {
throw new IllegalArgumentException();
}
if (block instanceof ICustomBlockSetting) {
return new IStructureElement() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
return block == world.getBlock(x, y, z);
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
((ICustomBlockSetting) defaultBlock).setBlock(world, x, y, z, defaultMeta);
return true;
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
StructureLibAPI.hintParticle(world, x, y, z, defaultBlock, defaultMeta);
return true;
}
};
} else {
return new IStructureElement() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
return block == world.getBlock(x, y, z);
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
world.setBlock(x, y, z, defaultBlock, defaultMeta, 2);
return true;
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
StructureLibAPI.hintParticle(world, x, y, z, defaultBlock, defaultMeta);
return true;
}
};
}
}
public static IStructureElement ofBlock(Block block, int meta) {
return ofBlock(block, meta, block, meta);
}
/**
* Same as above but ignores target meta id
*/
public static IStructureElement ofBlockAnyMeta(Block block) {
return ofBlockAnyMeta(block, block, 0);
}
/**
* Same as above but allows to set hint particle render
*/
public static IStructureElement ofBlockAnyMeta(Block block, int defaultMeta) {
return ofBlockAnyMeta(block, block, defaultMeta);
}
//endregion
//region adders
public static IStructureElement ofBlockAdder(IBlockAdder iBlockAdder, Block defaultBlock, int defaultMeta) {
if (iBlockAdder == null || defaultBlock == null) {
throw new IllegalArgumentException();
}
if (defaultBlock instanceof ICustomBlockSetting) {
return new IStructureElement() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
Block worldBlock = world.getBlock(x, y, z);
return iBlockAdder.apply(t, worldBlock, worldBlock.getDamageValue(world, x, y, z));
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
((ICustomBlockSetting) defaultBlock).setBlock(world, x, y, z, defaultMeta);
return true;
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
StructureLibAPI.hintParticle(world, x, y, z, defaultBlock, defaultMeta);
return true;
}
};
} else {
return new IStructureElement() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
Block worldBlock = world.getBlock(x, y, z);
return iBlockAdder.apply(t, worldBlock, worldBlock.getDamageValue(world, x, y, z));
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
world.setBlock(x, y, z, defaultBlock, defaultMeta, 2);
return true;
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
StructureLibAPI.hintParticle(world, x, y, z, defaultBlock, defaultMeta);
return true;
}
};
}
}
public static IStructureElement ofBlockAdder(IBlockAdder iBlockAdder, int dots) {
return ofBlockAdder(iBlockAdder, StructureLibAPI.getBlockHint(), dots - 1);
}
public static IStructureElementNoPlacement ofTileAdder(ITileAdder iTileAdder, Block hintBlock, int hintMeta) {
if (iTileAdder == null || hintBlock == null) {
throw new IllegalArgumentException();
}
return new IStructureElementNoPlacement() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
TileEntity tileEntity = world.getTileEntity(x, y, z);
// This used to check if it's a GT tile. Since this is now an standalone mod we no longer do this
return iTileAdder.apply(t, tileEntity);
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
StructureLibAPI.hintParticle(world, x, y, z, hintBlock, hintMeta);
return true;
}
};
}
// No more hatch adder. Implement it via tile adder. We could of course add a wrapper around it in gregtech, but not any more in this standalone mod.
//endregion
//region side effects
public static , T> IStructureElement onElementPass(Consumer onCheckPass, B element) {
return new IStructureElement() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
boolean check = element.check(t, world, x, y, z);
if (check) {
onCheckPass.accept(t);
}
return check;
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
return element.placeBlock(t, world, x, y, z, trigger);
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
return element.spawnHint(t, world, x, y, z, trigger);
}
};
}
public static , T> IStructureElement onElementFail(Consumer onFail, B element) {
return new IStructureElement() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
boolean check = element.check(t, world, x, y, z);
if (!check) {
onFail.accept(t);
}
return check;
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
return element.placeBlock(t, world, x, y, z, trigger);
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
return element.spawnHint(t, world, x, y, z, trigger);
}
};
}
//endregion
/**
* Take care while chaining, as it will try to call every structure element until it returns true.
* If none does it will finally return false.
*
*/
@SafeVarargs
public static IStructureElementChain ofChain(IStructureElement... elementChain) {
if (elementChain == null || elementChain.length == 0) {
throw new IllegalArgumentException();
}
for (IStructureElement iStructureElement : elementChain) {
if (iStructureElement == null) {
throw new IllegalArgumentException();
}
}
return () -> elementChain;
}
/**
* Take care while chaining, as it will try to call every structure element until it returns true.
* If none does it will finally return false.
*
*/
@SuppressWarnings("unchecked")
public static IStructureElementChain ofChain(List> elementChain) {
return ofChain(elementChain.toArray(new IStructureElement[0]));
}
//region defer
public static IStructureElementDeferred defer(Supplier> to) {
if (to == null) {
throw new IllegalArgumentException();
}
return new IStructureElementDeferred() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
return to.get().check(t, world, x, y, z);
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
return to.get().placeBlock(t, world, x, y, z, trigger);
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
return to.get().spawnHint(t, world, x, y, z, trigger);
}
};
}
public static IStructureElementDeferred defer(Function> to) {
if (to == null) {
throw new IllegalArgumentException();
}
return new IStructureElementDeferred() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
return to.apply(t).check(t, world, x, y, z);
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
return to.apply(t).placeBlock(t, world, x, y, z, trigger);
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
return to.apply(t).spawnHint(t, world, x, y, z, trigger);
}
};
}
public static IStructureElementDeferred defer(Function keyExtractor, Map> map) {
if (keyExtractor == null || map == null) {
throw new IllegalArgumentException();
}
return new IStructureElementDeferred() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
return map.get(keyExtractor.apply(t)).check(t, world, x, y, z);
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
return map.get(keyExtractor.apply(t)).placeBlock(t, world, x, y, z, trigger);
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
return map.get(keyExtractor.apply(t)).spawnHint(t, world, x, y, z, trigger);
}
};
}
public static IStructureElementDeferred defer(Function keyExtractor, Map> map, IStructureElement defaultElem) {
if (keyExtractor == null || map == null) {
throw new IllegalArgumentException();
}
return new IStructureElementDeferred() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
return map.getOrDefault(keyExtractor.apply(t), defaultElem).check(t, world, x, y, z);
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
return map.getOrDefault(keyExtractor.apply(t), defaultElem).placeBlock(t, world, x, y, z, trigger);
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
return map.getOrDefault(keyExtractor.apply(t), defaultElem).spawnHint(t, world, x, y, z, trigger);
}
};
}
@SafeVarargs
public static IStructureElementDeferred defer(Function keyExtractor, IStructureElement... array) {
if (keyExtractor == null || array == null) {
throw new IllegalArgumentException();
}
return new IStructureElementDeferred() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
return array[keyExtractor.apply(t)].check(t, world, x, y, z);
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
return array[keyExtractor.apply(t)].placeBlock(t, world, x, y, z, trigger);
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
return array[keyExtractor.apply(t)].spawnHint(t, world, x, y, z, trigger);
}
};
}
@SuppressWarnings("unchecked")
public static IStructureElementDeferred defer(Function keyExtractor, List> array) {
return defer(keyExtractor, array.toArray(new IStructureElement[0]));
}
public static IStructureElementDeferred defer(BiFunction> to) {
if (to == null) {
throw new IllegalArgumentException();
}
return new IStructureElementDeferred() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
return to.apply(t, null).check(t, world, x, y, z);
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
return to.apply(t, trigger).placeBlock(t, world, x, y, z, trigger);
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
return to.apply(t, trigger).spawnHint(t, world, x, y, z, trigger);
}
};
}
public static IStructureElementDeferred defer(BiFunction keyExtractor, Map> map) {
if (keyExtractor == null || map == null) {
throw new IllegalArgumentException();
}
return new IStructureElementDeferred() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
return map.get(keyExtractor.apply(t, null)).check(t, world, x, y, z);
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
return map.get(keyExtractor.apply(t, trigger)).placeBlock(t, world, x, y, z, trigger);
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
return map.get(keyExtractor.apply(t, trigger)).spawnHint(t, world, x, y, z, trigger);
}
};
}
public static IStructureElementDeferred defer(BiFunction keyExtractor, Map> map, IStructureElement defaultElem) {
if (keyExtractor == null || map == null) {
throw new IllegalArgumentException();
}
return new IStructureElementDeferred() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
return map.getOrDefault(keyExtractor.apply(t, null), defaultElem).check(t, world, x, y, z);
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
return map.getOrDefault(keyExtractor.apply(t, trigger), defaultElem).placeBlock(t, world, x, y, z, trigger);
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
return map.getOrDefault(keyExtractor.apply(t, trigger), defaultElem).spawnHint(t, world, x, y, z, trigger);
}
};
}
@SafeVarargs
public static IStructureElementDeferred defer(BiFunction keyExtractor, IStructureElement... array) {
if (keyExtractor == null || array == null) {
throw new IllegalArgumentException();
}
return new IStructureElementDeferred() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
return array[keyExtractor.apply(t, null)].check(t, world, x, y, z);
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
return array[keyExtractor.apply(t, trigger)].placeBlock(t, world, x, y, z, trigger);
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
return array[keyExtractor.apply(t, trigger)].spawnHint(t, world, x, y, z, trigger);
}
};
}
@SuppressWarnings("unchecked")
public static IStructureElementDeferred defer(BiFunction keyExtractor, List> array) {
return defer(keyExtractor, array.toArray(new IStructureElement[0]));
}
public static IStructureElementDeferred defer(Function> toCheck, BiFunction> to) {
if (to == null) {
throw new IllegalArgumentException();
}
return new IStructureElementDeferred() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
return toCheck.apply(t).check(t, world, x, y, z);
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
return to.apply(t, trigger).placeBlock(t, world, x, y, z, trigger);
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
return to.apply(t, trigger).spawnHint(t, world, x, y, z, trigger);
}
};
}
public static IStructureElementDeferred defer(Function keyExtractorCheck, BiFunction keyExtractor, Map> map) {
if (keyExtractor == null || map == null) {
throw new IllegalArgumentException();
}
return new IStructureElementDeferred() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
return map.get(keyExtractorCheck.apply(t)).check(t, world, x, y, z);
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
return map.get(keyExtractor.apply(t, trigger)).placeBlock(t, world, x, y, z, trigger);
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
return map.get(keyExtractor.apply(t, trigger)).spawnHint(t, world, x, y, z, trigger);
}
};
}
public static IStructureElementDeferred defer(Function keyExtractorCheck, BiFunction keyExtractor, Map> map, IStructureElement defaultElem) {
if (keyExtractor == null || map == null) {
throw new IllegalArgumentException();
}
return new IStructureElementDeferred() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
return map.getOrDefault(keyExtractorCheck.apply(t), defaultElem).check(t, world, x, y, z);
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
return map.getOrDefault(keyExtractor.apply(t, trigger), defaultElem).placeBlock(t, world, x, y, z, trigger);
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
return map.getOrDefault(keyExtractor.apply(t, trigger), defaultElem).spawnHint(t, world, x, y, z, trigger);
}
};
}
@SafeVarargs
public static IStructureElementDeferred defer(Function keyExtractorCheck, BiFunction keyExtractor, IStructureElement... array) {
if (keyExtractor == null || array == null) {
throw new IllegalArgumentException();
}
return new IStructureElementDeferred() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
return array[keyExtractorCheck.apply(t)].check(t, world, x, y, z);
}
@Override
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
return array[keyExtractor.apply(t, trigger)].placeBlock(t, world, x, y, z, trigger);
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
return array[keyExtractor.apply(t, trigger)].spawnHint(t, world, x, y, z, trigger);
}
};
}
@SuppressWarnings("unchecked")
public static IStructureElementDeferred defer(Function keyExtractorCheck, BiFunction keyExtractor, List> array) {
return defer(keyExtractorCheck, keyExtractor, array.toArray(new IStructureElement[0]));
}
//endregion
/**
* Used internally, to generate skips for structure definitions
*
*/
public static IStructureNavigate step(int a, int b, int c) {
return step(new Vec3Impl(a, b, c));
}
/**
* Used internally, to generate skips for structure definitions
*
*/
@SuppressWarnings("unchecked")
public static IStructureNavigate step(Vec3Impl step) {
if (step == null || step.get0() < 0 || step.get1() < 0 || step.get2() < 0) {
throw new IllegalArgumentException();
}
return STEP.computeIfAbsent(step, vec3 -> {
if (vec3.get2() > 0) {
return stepC(vec3.get0(), vec3.get1(), vec3.get2());
} else if (vec3.get1() > 0) {
return stepB(vec3.get0(), vec3.get1(), vec3.get2());
} else {
return stepA(vec3.get0(), vec3.get1(), vec3.get2());
}
});
}
private static IStructureNavigate stepA(int a, int b, int c) {
return new IStructureNavigate() {
@Override
public int getStepA() {
return a;
}
@Override
public int getStepB() {
return b;
}
@Override
public int getStepC() {
return c;
}
};
}
private static IStructureNavigate stepB(int a, int b, int c) {
return new IStructureNavigate() {
@Override
public int getStepA() {
return a;
}
@Override
public int getStepB() {
return b;
}
@Override
public int getStepC() {
return c;
}
@Override
public boolean resetA() {
return true;
}
};
}
private static IStructureNavigate stepC(int a, int b, int c) {
return new IStructureNavigate() {
@Override
public int getStepA() {
return a;
}
@Override
public int getStepB() {
return b;
}
@Override
public int getStepC() {
return c;
}
@Override
public boolean resetA() {
return true;
}
@Override
public boolean resetB() {
return true;
}
};
}
/**
* Used only to get pseudo code in structure writer...
*
* NOTE: GT specific code got removed. TODO add a mean
* @param tileEntityClassifier return a string that denote the type of a tile entity, or null if it's nothing special. useful if the tile entity cannot be simply distinguished via getClass.
*/
public static String getPseudoJavaCode(World world, ExtendedFacing extendedFacing,
int basePositionX, int basePositionY, int basePositionZ,
int basePositionA, int basePositionB, int basePositionC,
Function super TileEntity, String> tileEntityClassifier,
int sizeA, int sizeB, int sizeC, boolean transpose) {
Map> blocks = new TreeMap<>(Comparator.comparing(Block::getUnlocalizedName));
Set> tiles = new HashSet<>();
Set specialTiles = new HashSet<>();
iterate(world, extendedFacing, basePositionX, basePositionY, basePositionZ,
basePositionA, basePositionB, basePositionC,
sizeA, sizeB, sizeC, ((w, x, y, z) -> {
TileEntity tileEntity = w.getTileEntity(x, y, z);
if (tileEntity == null) {
Block block = w.getBlock(x, y, z);
if (block != null && block != Blocks.air) {
blocks.compute(block, (b, set) -> {
if (set == null) {
set = new TreeSet<>();
}
set.add(block.getDamageValue(world, x, y, z));
return set;
});
}
} else {
String classification = tileEntityClassifier.apply(tileEntity);
if (classification == null) {
tiles.add(tileEntity.getClass());
} else
specialTiles.add(classification);
}
}));
Map map = new HashMap<>();
StringBuilder builder = new StringBuilder();
{
int i = 0;
char c;
builder.append("\n\nStructure:\n")
.append("\nBlocks:\n");
for (Map.Entry> entry : blocks.entrySet()) {
Block block = entry.getKey();
Set set = entry.getValue();
for (Integer meta : set) {
c = NICE_CHARS.charAt(i++);
if (i > NICE_CHARS.length()) {
return "Too complicated for nice chars";
}
map.put(block.getUnlocalizedName() + '\0' + meta, c);
builder.append(c).append(" -> ofBlock...(")
.append(block.getUnlocalizedName()).append(", ").append(meta).append(", ...);\n");
}
}
builder.append("\nTiles:\n");
for (Class extends TileEntity> tile : tiles) {
c = NICE_CHARS.charAt(i++);
if (i > NICE_CHARS.length()) {
return "Too complicated for nice chars";
}
map.put(tile.getCanonicalName(), c);
builder.append(c).append(" -> ofTileAdder(")
.append(tile).append(", ...);\n");
}
builder.append("\nSpecial Tiles:\n");
for (String tile : specialTiles) {
c = NICE_CHARS.charAt(i++);
if (i > NICE_CHARS.length()) {
return "Too complicated for nice chars";
}
map.put(tile, c);
builder.append(c).append(" -> ofSpecialTileAdder(")
.append(tile).append(", ...); // You will probably want to change it to something else\n");
}
}
builder.append("\nOffsets:\n")
.append(basePositionA).append(' ').append(basePositionB).append(' ').append(basePositionC).append('\n');
if (transpose) {
builder.append("\nTransposed Scan:\n")
.append("new String[][]{\n")
.append(" {\"");
iterate(world, extendedFacing, basePositionX, basePositionY, basePositionZ,
basePositionA, basePositionB, basePositionC, true,
sizeA, sizeB, sizeC, ((w, x, y, z) -> {
TileEntity tileEntity = w.getTileEntity(x, y, z);
if (tileEntity == null) {
Block block = w.getBlock(x, y, z);
if (block != null && block != Blocks.air) {
builder.append(map.get(block.getUnlocalizedName() + '\0' + block.getDamageValue(world, x, y, z)));
} else {
builder.append(' ');
}
} else {
String classification = tileEntityClassifier.apply(tileEntity);
if (classification == null) {
classification = tileEntity.getClass().getCanonicalName();
}
builder.append(map.get(classification));
}
}),
() -> builder.append("\",\""),
() -> {
builder.setLength(builder.length() - 2);
builder.append("},\n {\"");
});
builder.setLength(builder.length() - 8);
builder.append("\n}\n\n");
} else {
builder.append("\nNormal Scan:\n")
.append("new String[][]{{\n")
.append(" \"");
iterate(world, extendedFacing, basePositionX, basePositionY, basePositionZ,
basePositionA, basePositionB, basePositionC, false,
sizeA, sizeB, sizeC, ((w, x, y, z) -> {
TileEntity tileEntity = w.getTileEntity(x, y, z);
if (tileEntity == null) {
Block block = w.getBlock(x, y, z);
if (block != null && block != Blocks.air) {
builder.append(map.get(block.getUnlocalizedName() + '\0' + block.getDamageValue(world, x, y, z)));
} else {
builder.append(' ');
}
} else {
String classification = tileEntityClassifier.apply(tileEntity);
if (classification == null) {
classification = tileEntity.getClass().getCanonicalName();
}
builder.append(map.get(classification));
}
}),
() -> builder.append("\",\n").append(" \""),
() -> {
builder.setLength(builder.length() - 7);
builder.append("\n").append("},{\n").append(" \"");
});
builder.setLength(builder.length() - 8);
builder.append("}\n\n");
}
return (builder.toString().replaceAll("\"\"", "E"));
}
public static void iterate(World world, ExtendedFacing extendedFacing,
int basePositionX, int basePositionY, int basePositionZ,
int basePositionA, int basePositionB, int basePositionC,
int sizeA, int sizeB, int sizeC,
IBlockPosConsumer iBlockPosConsumer) {
sizeA -= basePositionA;
sizeB -= basePositionB;
sizeC -= basePositionC;
int[] abc = new int[3];
int[] xyz = new int[3];
for (abc[2] = -basePositionC; abc[2] < sizeC; abc[2]++) {
for (abc[1] = -basePositionB; abc[1] < sizeB; abc[1]++) {
for (abc[0] = -basePositionA; abc[0] < sizeA; abc[0]++) {
extendedFacing.getWorldOffset(abc, xyz);
iBlockPosConsumer.consume(world, xyz[0] + basePositionX, xyz[1] + basePositionY, xyz[2] + basePositionZ);
}
}
}
}
public static void iterate(World world, ExtendedFacing extendedFacing,
int basePositionX, int basePositionY, int basePositionZ,
int basePositionA, int basePositionB, int basePositionC,
boolean transpose, int sizeA, int sizeB, int sizeC,
IBlockPosConsumer iBlockPosConsumer,
Runnable nextB,
Runnable nextC) {
sizeA -= basePositionA;
sizeB -= basePositionB;
sizeC -= basePositionC;
int[] abc = new int[3];
int[] xyz = new int[3];
if (transpose) {
for (abc[1] = -basePositionB; abc[1] < sizeB; abc[1]++) {
for (abc[2] = -basePositionC; abc[2] < sizeC; abc[2]++) {
for (abc[0] = -basePositionA; abc[0] < sizeA; abc[0]++) {
extendedFacing.getWorldOffset(abc, xyz);
iBlockPosConsumer.consume(world, xyz[0] + basePositionX, xyz[1] + basePositionY, xyz[2] + basePositionZ);
}
nextB.run();
}
nextC.run();
}
} else {
for (abc[2] = -basePositionC; abc[2] < sizeC; abc[2]++) {
for (abc[1] = -basePositionB; abc[1] < sizeB; abc[1]++) {
for (abc[0] = -basePositionA; abc[0] < sizeA; abc[0]++) {
extendedFacing.getWorldOffset(abc, xyz);
iBlockPosConsumer.consume(world, xyz[0] + basePositionX, xyz[1] + basePositionY, xyz[2] + basePositionZ);
}
nextB.run();
}
nextC.run();
}
}
}
/**
* Transposes shape (swaps B and C axis, can be used to un-transpose transposed shape)
* WARNING! Do not use on old api...
*
* @param structurePiece shape (transposed shape)
* @return transposed shape (untransposed shape)
*/
public static String[][] transpose(String[][] structurePiece) {
String[][] shape = new String[structurePiece[0].length][structurePiece.length];
for (int i = 0; i < structurePiece.length; i++) {
for (int j = 0; j < structurePiece[i].length; j++) {
shape[j][i] = structurePiece[i][j];
}
}
return shape;
}
}