package gregtech.api.util; import static com.gtnewhorizon.structurelib.structure.IStructureElement.PlaceResult.ACCEPT; import static com.gtnewhorizon.structurelib.structure.IStructureElement.PlaceResult.ACCEPT_STOP; import static com.gtnewhorizon.structurelib.structure.IStructureElement.PlaceResult.REJECT; import static com.gtnewhorizon.structurelib.structure.IStructureElement.PlaceResult.SKIP; import static com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode.EXACT; import java.util.Arrays; import java.util.List; import java.util.function.BiConsumer; import java.util.function.BiPredicate; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.ToIntFunction; import javax.annotation.Nonnull; import net.minecraft.block.Block; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.init.Items; import net.minecraft.item.Item; import net.minecraft.item.ItemBlock; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.IChatComponent; import net.minecraft.util.IIcon; import net.minecraft.world.World; import com.gtnewhorizon.structurelib.StructureLibAPI; import com.gtnewhorizon.structurelib.structure.AutoPlaceEnvironment; import com.gtnewhorizon.structurelib.structure.IItemSource; import com.gtnewhorizon.structurelib.structure.IStructureElement; import com.gtnewhorizon.structurelib.structure.IStructureElementNoPlacement; import com.gtnewhorizon.structurelib.structure.StructureUtility; import com.gtnewhorizon.structurelib.util.ItemStackPredicate; import gregtech.api.GregTech_API; import gregtech.api.enums.HeatingCoilLevel; import gregtech.api.enums.Materials; import gregtech.api.enums.OrePrefixes; import gregtech.api.interfaces.IHeatingCoil; import gregtech.api.interfaces.metatileentity.IMetaTileEntity; import gregtech.api.interfaces.tileentity.IGregTechTileEntity; import gregtech.api.metatileentity.BaseMetaPipeEntity; import gregtech.api.metatileentity.implementations.GT_MetaPipeEntity_Frame; import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_TieredMachineBlock; import gregtech.common.blocks.GT_Block_Casings5; import gregtech.common.blocks.GT_Item_Machines; public class GT_StructureUtility { // private static final Map, String> customNames = new HashMap<>(); private GT_StructureUtility() { throw new AssertionError("Not instantiable"); } public static boolean hasMTE(IGregTechTileEntity aTile, Class clazz) { return aTile != null && clazz.isInstance(aTile.getMetaTileEntity()); } public static IStructureElementNoPlacement ofHatchAdder(IGT_HatchAdder aHatchAdder, int aTextureIndex, int aDots) { return ofHatchAdder(aHatchAdder, aTextureIndex, StructureLibAPI.getBlockHint(), aDots - 1); } public static IStructureElement ofFrame(Materials aFrameMaterial) { if (aFrameMaterial == null) throw new IllegalArgumentException(); return new IStructureElement<>() { private IIcon[] mIcons; @Override public boolean check(T t, World world, int x, int y, int z) { TileEntity tBase = world.getTileEntity(x, y, z); if (tBase instanceof BaseMetaPipeEntity tPipeBase) { if (tPipeBase.isInvalidTileEntity()) return false; if (tPipeBase.getMetaTileEntity() instanceof GT_MetaPipeEntity_Frame) return aFrameMaterial == ((GT_MetaPipeEntity_Frame) tPipeBase.getMetaTileEntity()).mMaterial; } return false; } @Override public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { if (mIcons == null) { mIcons = new IIcon[6]; Arrays.fill(mIcons, aFrameMaterial.mIconSet.mTextures[OrePrefixes.frameGt.mTextureIndex].getIcon()); } StructureLibAPI.hintParticleTinted(world, x, y, z, mIcons, aFrameMaterial.mRGBa); return true; } @Override public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { ItemStack tFrameStack = getFrameStack(); if (!GT_Utility.isStackValid(tFrameStack) || !(tFrameStack.getItem() instanceof ItemBlock tFrameStackItem)) return false; return tFrameStackItem .placeBlockAt(tFrameStack, null, world, x, y, z, 6, 0, 0, 0, Items.feather.getDamage(tFrameStack)); } private ItemStack getFrameStack() { return GT_OreDictUnificator.get(OrePrefixes.frameGt, aFrameMaterial, 1); } @Override public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) { ItemStack tFrameStack = getFrameStack(); if (!GT_Utility.isStackValid(tFrameStack) || !(tFrameStack.getItem() instanceof ItemBlock)) return BlocksToPlace.errored; return BlocksToPlace.create(tFrameStack); } @Override public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s, EntityPlayerMP actor, Consumer chatter) { return survivalPlaceBlock( t, world, x, y, z, trigger, AutoPlaceEnvironment.fromLegacy(s, actor, chatter)); } @Override public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) { if (check(t, world, x, y, z)) return SKIP; ItemStack tFrameStack = getFrameStack(); if (!GT_Utility.isStackValid(tFrameStack) || !(tFrameStack.getItem() instanceof ItemBlock)) return REJECT; // honestly, this is more like a programming error or pack issue return StructureUtility.survivalPlaceBlock( tFrameStack, ItemStackPredicate.NBTMode.IGNORE_KNOWN_INSIGNIFICANT_TAGS, null, false, world, x, y, z, env.getSource(), env.getActor(), env.getChatter()); } }; } public static GT_HatchElementBuilder buildHatchAdder() { return GT_HatchElementBuilder.builder(); } /** * Completely equivalent to {@link #buildHatchAdder()}, except it plays nicer with type inference when statically * imported */ public static GT_HatchElementBuilder buildHatchAdder(Class typeToken) { return GT_HatchElementBuilder.builder(); } public static IStructureElementNoPlacement ofHatchAdder(IGT_HatchAdder aHatchAdder, int aTextureIndex, Block aHintBlock, int aHintMeta) { if (aHatchAdder == null || aHintBlock == 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); return tileEntity instanceof IGregTechTileEntity && aHatchAdder.apply(t, (IGregTechTileEntity) tileEntity, (short) aTextureIndex); } @Override public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { StructureLibAPI.hintParticle(world, x, y, z, aHintBlock, aHintMeta); return true; } }; } public static IStructureElement ofHatchAdder(IGT_HatchAdder aHatchAdder, int aTextureIndex, Block aHintBlock, int aHintMeta, BiPredicate shouldSkip, Function> aMetaId, final IStructureElement.PlaceResult acceptType) { if (aHatchAdder == null) { throw new IllegalArgumentException(); } return new IStructureElement<>() { @Override public boolean check(T t, World world, int x, int y, int z) { TileEntity tileEntity = world.getTileEntity(x, y, z); return tileEntity instanceof IGregTechTileEntity && aHatchAdder.apply(t, (IGregTechTileEntity) tileEntity, (short) aTextureIndex); } @Override public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { StructureLibAPI.hintParticle(world, x, y, z, aHintBlock, aHintMeta); return true; } @Override public boolean placeBlock(T t, World world, int i, int i1, int i2, ItemStack itemStack) { // TODO return false; } @Override public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) { Class clazz = aMetaId.apply(t); if (clazz == null) return BlocksToPlace.createEmpty(); return BlocksToPlace.create(is -> clazz.isInstance(GT_Item_Machines.getMetaTileEntity(is))); } @Override public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s, EntityPlayerMP actor, Consumer chatter) { return survivalPlaceBlock( t, world, x, y, z, trigger, AutoPlaceEnvironment.fromLegacy(s, actor, chatter)); } @Override public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) { if (shouldSkip != null) { TileEntity tileEntity = world.getTileEntity(x, y, z); if (tileEntity instanceof IGregTechTileEntity && shouldSkip.test(t, (IGregTechTileEntity) tileEntity)) return SKIP; } if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, env.getActor())) return REJECT; Class clazz = aMetaId.apply(t); if (clazz == null) return REJECT; ItemStack taken = env.getSource() .takeOne(is -> clazz.isInstance(GT_Item_Machines.getMetaTileEntity(is)), true); if (GT_Utility.isStackInvalid(taken)) { env.getChatter() .accept( new ChatComponentTranslation( "GT5U.autoplace.error.no_mte.class_name", clazz.getSimpleName())); return REJECT; } if (StructureUtility .survivalPlaceBlock(taken, EXACT, null, true, world, x, y, z, env.getSource(), env.getActor()) == ACCEPT) return acceptType; return REJECT; } }; } public static IStructureElement ofHatchAdder(IGT_HatchAdder aHatchAdder, int aTextureIndex, Block aHintBlock, int aHintMeta, BiPredicate shouldSkip, ToIntFunction aMetaId) { if (aHatchAdder == null) { throw new IllegalArgumentException(); } return new IStructureElement<>() { @Override public boolean check(T t, World world, int x, int y, int z) { TileEntity tileEntity = world.getTileEntity(x, y, z); return tileEntity instanceof IGregTechTileEntity && aHatchAdder.apply(t, (IGregTechTileEntity) tileEntity, (short) aTextureIndex); } @Override public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { StructureLibAPI.hintParticle(world, x, y, z, aHintBlock, aHintMeta); return true; } @Override public boolean placeBlock(T t, World world, int i, int i1, int i2, ItemStack itemStack) { // TODO return false; } @Override public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) { GT_Item_Machines item = (GT_Item_Machines) Item.getItemFromBlock(GregTech_API.sBlockMachines); int meta = aMetaId.applyAsInt(t); if (meta < 0) return BlocksToPlace.createEmpty(); return BlocksToPlace.create( ItemStackPredicate.from(item) .setMeta(meta)); } @Override public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s, EntityPlayerMP actor, Consumer chatter) { return survivalPlaceBlock( t, world, x, y, z, trigger, AutoPlaceEnvironment.fromLegacy(s, actor, chatter)); } @Override public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) { if (shouldSkip != null) { TileEntity tileEntity = world.getTileEntity(x, y, z); if (tileEntity instanceof IGregTechTileEntity && shouldSkip.test(t, (IGregTechTileEntity) tileEntity)) return SKIP; } if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, env.getActor())) return REJECT; GT_Item_Machines item = (GT_Item_Machines) Item.getItemFromBlock(GregTech_API.sBlockMachines); int meta = aMetaId.applyAsInt(t); if (meta < 0) return REJECT; ItemStack taken = env.getSource() .takeOne( ItemStackPredicate.from(item) .setMeta(meta), true); if (GT_Utility.isStackInvalid(taken)) { env.getChatter() .accept(new ChatComponentTranslation("GT5U.autoplace.error.no_mte.id", meta)); return REJECT; } return StructureUtility .survivalPlaceBlock(taken, EXACT, null, true, world, x, y, z, env.getSource(), env.getActor()) == ACCEPT ? ACCEPT_STOP : REJECT; } }; } public static IStructureElement ofHatchAdderOptional(IGT_HatchAdder aHatchAdder, int textureIndex, int dots, Block placeCasing, int placeCasingMeta) { return ofHatchAdderOptional( aHatchAdder, textureIndex, StructureLibAPI.getBlockHint(), dots - 1, placeCasing, placeCasingMeta); } public static IStructureElement ofHatchAdderOptional(IGT_HatchAdder aHatchAdder, int aTextureIndex, Block aHintBlock, int hintMeta, Block placeCasing, int placeCasingMeta) { if (aHatchAdder == null || aHintBlock == null) { throw new IllegalArgumentException(); } return new IStructureElement<>() { @Override public boolean check(T t, World world, int x, int y, int z) { TileEntity tileEntity = world.getTileEntity(x, y, z); Block worldBlock = world.getBlock(x, y, z); return (tileEntity instanceof IGregTechTileEntity && aHatchAdder.apply(t, (IGregTechTileEntity) tileEntity, (short) aTextureIndex)) || (worldBlock == placeCasing && worldBlock.getDamageValue(world, x, y, z) == placeCasingMeta); } @Override public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { StructureLibAPI.hintParticle(world, x, y, z, aHintBlock, hintMeta); return true; } @Override public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { world.setBlock(x, y, z, placeCasing, placeCasingMeta, 2); return true; } @Override public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s, EntityPlayerMP actor, Consumer chatter) { if (check(t, world, x, y, z)) return SKIP; return StructureUtility .survivalPlaceBlock(placeCasing, placeCasingMeta, world, x, y, z, s, actor, chatter); } }; } /** * Assume all coils accepted. * * @see #ofCoil(BiPredicate, Function) */ public static IStructureElement ofCoil(BiConsumer aHeatingCoilSetter, Function aHeatingCoilGetter) { return ofCoil((t, l) -> { aHeatingCoilSetter.accept(t, l); return true; }, aHeatingCoilGetter); } /** * Heating coil structure element. * * @param aHeatingCoilSetter Notify the controller of this new coil. Got called exactly once per coil. Might be * called less times if structure test fails. If the setter returns false then it assumes * the coil is rejected. * @param aHeatingCoilGetter Get the current heating level. Null means no coil recorded yet. */ public static IStructureElement ofCoil(BiPredicate aHeatingCoilSetter, Function aHeatingCoilGetter) { if (aHeatingCoilSetter == null || aHeatingCoilGetter == null) { throw new IllegalArgumentException(); } return new IStructureElement<>() { @Override public boolean check(T t, World world, int x, int y, int z) { Block block = world.getBlock(x, y, z); if (!(block instanceof IHeatingCoil)) return false; HeatingCoilLevel existingLevel = aHeatingCoilGetter.apply(t), newLevel = ((IHeatingCoil) block).getCoilHeat(world.getBlockMetadata(x, y, z)); if (existingLevel == null || existingLevel == HeatingCoilLevel.None) { return aHeatingCoilSetter.test(t, newLevel); } else { return newLevel == existingLevel; } } @Override public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { StructureLibAPI.hintParticle(world, x, y, z, GregTech_API.sBlockCasings5, getMetaFromHint(trigger)); return true; } private int getMetaFromHint(ItemStack trigger) { return GT_Block_Casings5.getMetaFromCoilHeat(getHeatFromHint(trigger)); } private HeatingCoilLevel getHeatFromHint(ItemStack trigger) { return HeatingCoilLevel .getFromTier((byte) Math.min(HeatingCoilLevel.getMaxTier(), Math.max(0, trigger.stackSize - 1))); } @Override public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { return world.setBlock(x, y, z, GregTech_API.sBlockCasings5, getMetaFromHint(trigger), 3); } @Override public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) { return BlocksToPlace.create(GregTech_API.sBlockCasings5, getMetaFromHint(trigger)); } @Override public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s, EntityPlayerMP actor, Consumer chatter) { return survivalPlaceBlock( t, world, x, y, z, trigger, AutoPlaceEnvironment.fromLegacy(s, actor, chatter)); } @Override public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) { Block block = world.getBlock(x, y, z); boolean isCoil = block instanceof IHeatingCoil && ((IHeatingCoil) block).getCoilHeat(world.getBlockMetadata(x, y, z)) == getHeatFromHint(trigger); if (isCoil) return SKIP; return StructureUtility.survivalPlaceBlock( GregTech_API.sBlockCasings5, getMetaFromHint(trigger), world, x, y, z, env.getSource(), env.getActor(), env.getChatter()); } }; } @Nonnull public static Predicate filterByMTEClass(List> list) { return is -> { IMetaTileEntity tile = GT_Item_Machines.getMetaTileEntity(is); return tile != null && list.stream() .anyMatch(c -> c.isInstance(tile)); }; } @Nonnull public static Predicate filterByMTETier(int aMinTier, int aMaxTier) { return is -> { IMetaTileEntity tile = GT_Item_Machines.getMetaTileEntity(is); return tile instanceof GT_MetaTileEntity_TieredMachineBlock && ((GT_MetaTileEntity_TieredMachineBlock) tile).mTier <= aMaxTier && ((GT_MetaTileEntity_TieredMachineBlock) tile).mTier >= aMinTier; }; } }