2021-05-17 03:02:24 +08:00
|
|
|
package com.gtnewhorizon.structurelib.structure;
|
|
|
|
|
|
2025-03-11 22:41:57 +08:00
|
|
|
import static com.gtnewhorizon.structurelib.StructureLib.LOGGER;
|
|
|
|
|
import static com.gtnewhorizon.structurelib.StructureLib.PANIC_MODE;
|
2022-08-23 21:33:25 +08:00
|
|
|
import static java.lang.Integer.MIN_VALUE;
|
2026-05-28 01:08:27 -05:00
|
|
|
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
|
2022-08-08 17:40:12 +08:00
|
|
|
|
2024-07-06 12:13:22 -07:00
|
|
|
import java.util.Arrays;
|
|
|
|
|
import java.util.Collection;
|
|
|
|
|
import java.util.Collections;
|
|
|
|
|
import java.util.Comparator;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.HashSet;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
2022-09-13 21:22:34 +08:00
|
|
|
import java.util.Map.Entry;
|
2024-07-06 12:13:22 -07:00
|
|
|
import java.util.Objects;
|
|
|
|
|
import java.util.Set;
|
|
|
|
|
import java.util.TreeMap;
|
|
|
|
|
import java.util.TreeSet;
|
|
|
|
|
import java.util.function.BiConsumer;
|
|
|
|
|
import java.util.function.BiFunction;
|
|
|
|
|
import java.util.function.BiPredicate;
|
|
|
|
|
import java.util.function.Consumer;
|
|
|
|
|
import java.util.function.Function;
|
|
|
|
|
import java.util.function.Predicate;
|
|
|
|
|
import java.util.function.Supplier;
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-08-23 21:33:25 +08:00
|
|
|
import javax.annotation.Nullable;
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2021-05-17 03:02:24 +08:00
|
|
|
import net.minecraft.block.Block;
|
2022-08-23 19:54:36 +08:00
|
|
|
import net.minecraft.entity.player.EntityPlayer;
|
2022-08-08 17:40:12 +08:00
|
|
|
import net.minecraft.entity.player.EntityPlayerMP;
|
2021-05-17 03:02:24 +08:00
|
|
|
import net.minecraft.init.Blocks;
|
2022-08-08 17:40:12 +08:00
|
|
|
import net.minecraft.item.Item;
|
|
|
|
|
import net.minecraft.item.ItemBlock;
|
2021-05-17 03:02:24 +08:00
|
|
|
import net.minecraft.item.ItemStack;
|
2022-08-08 17:40:12 +08:00
|
|
|
import net.minecraft.nbt.NBTTagCompound;
|
2021-05-17 03:02:24 +08:00
|
|
|
import net.minecraft.tileentity.TileEntity;
|
2026-03-26 18:23:20 +04:00
|
|
|
import net.minecraft.util.ChatComponentText;
|
2022-08-08 17:40:12 +08:00
|
|
|
import net.minecraft.util.ChatComponentTranslation;
|
2026-03-26 18:23:20 +04:00
|
|
|
import net.minecraft.util.ChatStyle;
|
|
|
|
|
import net.minecraft.util.EnumChatFormatting;
|
2022-08-08 17:40:12 +08:00
|
|
|
import net.minecraft.util.IChatComponent;
|
2021-05-17 03:02:24 +08:00
|
|
|
import net.minecraft.util.IIcon;
|
|
|
|
|
import net.minecraft.world.World;
|
2022-08-08 17:40:12 +08:00
|
|
|
import net.minecraftforge.common.util.ForgeDirection;
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2021-07-19 14:47:27 +08:00
|
|
|
import org.apache.commons.lang3.StringUtils;
|
2021-07-22 12:07:12 +08:00
|
|
|
import org.apache.commons.lang3.tuple.Pair;
|
2021-05-17 03:02:24 +08:00
|
|
|
|
2022-09-13 21:22:34 +08:00
|
|
|
import com.google.common.collect.ImmutableList;
|
2022-09-15 15:58:17 +08:00
|
|
|
import com.gtnewhorizon.structurelib.StructureEvent.StructureElementVisitedEvent;
|
2022-08-08 17:40:12 +08:00
|
|
|
import com.gtnewhorizon.structurelib.StructureLib;
|
2021-05-29 19:05:50 +08:00
|
|
|
import com.gtnewhorizon.structurelib.StructureLibAPI;
|
2022-08-23 12:29:59 +08:00
|
|
|
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
2021-05-17 03:02:24 +08:00
|
|
|
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
2022-08-08 17:40:12 +08:00
|
|
|
import com.gtnewhorizon.structurelib.structure.IStructureElement.PlaceResult;
|
2021-05-17 03:02:24 +08:00
|
|
|
import com.gtnewhorizon.structurelib.structure.adders.IBlockAdder;
|
|
|
|
|
import com.gtnewhorizon.structurelib.structure.adders.ITileAdder;
|
2022-08-08 17:40:12 +08:00
|
|
|
import com.gtnewhorizon.structurelib.util.ItemStackPredicate;
|
|
|
|
|
import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode;
|
2021-05-17 03:02:24 +08:00
|
|
|
import com.gtnewhorizon.structurelib.util.Vec3Impl;
|
2024-01-09 03:42:10 +00:00
|
|
|
|
2021-07-19 14:47:27 +08:00
|
|
|
import cpw.mods.fml.common.registry.GameRegistry;
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2021-05-17 03:02:24 +08:00
|
|
|
/**
|
2022-08-30 23:27:18 +08:00
|
|
|
* A brief index of everything contained
|
2023-02-27 17:30:57 +00:00
|
|
|
* <h2>Control blocks</h2> A lot of these might seem to make more sense as a default function on
|
2022-08-30 23:27:18 +08:00
|
|
|
* {@link IStructureElement}. However, in that case javac generic will often fail to infer the type for you, so we have
|
|
|
|
|
* to define them as static methods
|
2023-02-27 17:30:57 +00:00
|
|
|
* <h2>If block</h2> Provide if block, allowing downstream call only if user specified conditions are meet (i.e.
|
2022-08-30 23:27:18 +08:00
|
|
|
* predicate returns true)
|
|
|
|
|
* <ul>
|
|
|
|
|
* <li>{@link #onlyIf(Predicate, IStructureElement)} and its overloads</li>
|
|
|
|
|
* </ul>
|
2023-02-27 17:30:57 +00:00
|
|
|
* <h2>Switch block</h2> Provide switch block, allowing to select a downstream element by using a key or index computed
|
2022-08-30 23:27:18 +08:00
|
|
|
* from trigger item and/or context object.
|
|
|
|
|
* <ul>
|
|
|
|
|
* <li>{@link #partitionBy(Function, IStructureElement[])} and its overloads</li>
|
|
|
|
|
* </ul>
|
2023-02-27 17:30:57 +00:00
|
|
|
* <h2>Or Chain</h2> Provide a short-circuiting OR chain.
|
2022-08-30 23:27:18 +08:00
|
|
|
* <ul>
|
|
|
|
|
* <li>{@link #ofChain(IStructureElement[])} and its overloads</li>
|
|
|
|
|
* </ul>
|
2023-02-27 17:30:57 +00:00
|
|
|
* <h3>Side effect</h3> Provide callbacks on various occasion
|
2022-08-30 23:27:18 +08:00
|
|
|
* <ul>
|
|
|
|
|
* <li>{@link #onElementPass(Consumer, IStructureElement)}: Call an callback upon element check success</li>
|
|
|
|
|
* <li>{@link #onElementFail(Consumer, IStructureElement)}: Call an callback upon element check failure</li>
|
|
|
|
|
* </ul>
|
|
|
|
|
*
|
2023-02-27 17:30:57 +00:00
|
|
|
* <h2>Other Primitives</h2>
|
|
|
|
|
* <h3>Error statement</h3> Unconditionally return false.
|
2022-08-30 23:27:18 +08:00
|
|
|
* <ul>
|
|
|
|
|
* <li>{@link #error()}</li>
|
|
|
|
|
* </ul>
|
2023-02-27 17:30:57 +00:00
|
|
|
* <h3>Context Object Change</h3> Switch the context object.
|
2022-08-30 23:27:18 +08:00
|
|
|
* <ul>
|
|
|
|
|
* <li>{@link #withContext(IStructureElement)}: switch context to the extended context</li>
|
|
|
|
|
* </ul>
|
2023-02-27 17:30:57 +00:00
|
|
|
* <h3>Channel Change</h3> Change the channel. See <a href="{@docRoot}/overview-summary.html#channels">Channels section
|
2022-08-30 23:27:18 +08:00
|
|
|
* on overview</a> for more information.
|
|
|
|
|
* <ul>
|
|
|
|
|
* <li>{@link #withChannel(String, IStructureElement)}: switch channel using
|
|
|
|
|
* {@link ChannelDataAccessor#withChannel(ItemStack, String)} when a trigger item is present.</li>
|
|
|
|
|
* </ul>
|
2023-02-27 17:30:57 +00:00
|
|
|
* <h3>Spawn hint particle</h3> Just spawn a hint. Do nothing otherwise. Useful when you want to override the hint
|
2022-08-30 23:27:18 +08:00
|
|
|
* provided by another structure element, or as a fallback to a structure element that is check only.
|
|
|
|
|
* <ul>
|
|
|
|
|
* <li>{@link #ofHint(int)}, {@link #ofHintDeferred(Supplier)} and its overloads</li>
|
|
|
|
|
* </ul>
|
|
|
|
|
*
|
2023-02-27 17:30:57 +00:00
|
|
|
* <h2>Actual Element</h2> These elements are elements that can actually do structure check, spawn hint and do auto
|
2022-08-30 23:27:18 +08:00
|
|
|
* place. Elements listed in above 2 sections are not going to be helpful without elements like these. They also serve
|
|
|
|
|
* as a reference implementation for your own custom {@link IStructureElement} implementations.
|
2023-02-27 17:30:57 +00:00
|
|
|
* <h3>Simple Block Element</h3> These accept one or more different block types in one location.
|
2022-08-30 23:27:18 +08:00
|
|
|
* <ul>
|
|
|
|
|
* <li>{@link #ofBlock(Block, int, Block, int)}, {@link #ofBlocksFlat(Map, Block, int)},
|
|
|
|
|
* {@link #ofBlocksMap(Map, Block, int)} and their overloads: the most basic form</li>
|
|
|
|
|
* <li>{@link #ofBlockHint(Block, int, Block, int)}, {@link #ofBlocksFlatHint(Map, Block, int)},
|
|
|
|
|
* {@link #ofBlocksMapHint(Map, Block, int)}: these are the same as above, but will not do autoplace</li>
|
|
|
|
|
* <li>{@link #isAir()}, {@link #notAir()}: They are supplied by default under the identifier {@code '-'} and
|
|
|
|
|
* {@code '+'} respectively, but are provided here regardless in case you want to use them as a fallback.</li>
|
|
|
|
|
* </ul>
|
2023-02-27 17:30:57 +00:00
|
|
|
* <h3>Complex Block Element</h3> In case your logic on determining which block is accepted is complex, use these.
|
2022-08-30 23:27:18 +08:00
|
|
|
* <ul>
|
|
|
|
|
* <li>{@link #ofBlockAdder(IBlockAdder, Block, int)}, {@link #ofBlockAdderHint(IBlockAdder, Block, int)} and their
|
|
|
|
|
* overloads: hands off actual adder logic to an {@link IBlockAdder} you supplied. Save you the boilerplate of querying
|
|
|
|
|
* block type and block meta from {@link World}</li>
|
|
|
|
|
* <li>{@link #ofBlocksTiered(ITierConverter, Object, BiConsumer, Function)} and its overloads: accept a series of
|
|
|
|
|
* blocks that each may have different tier, but only allow one single tier for anywhere this element is used (e.g.
|
|
|
|
|
* coils).</li>
|
|
|
|
|
* <li>{@link #ofTileAdder(ITileAdder, Block, int)}, {@link #ofSpecificTileAdder(BiPredicate, Class, Block, int)}:
|
|
|
|
|
* Similar to block adder, but for tile entities.</li>
|
|
|
|
|
* </ul>
|
|
|
|
|
*
|
2023-02-27 17:30:57 +00:00
|
|
|
* <h2>Helper Methods</h2> These don't construct a {@link IStructureElement}, but is helpful to the instantiation or
|
2022-08-30 23:27:18 +08:00
|
|
|
* implementation of these.
|
|
|
|
|
* <ul>
|
2022-09-11 02:54:11 +08:00
|
|
|
* <li>{@link #survivalPlaceBlock(Block, int, World, int, int, int, IItemSource, EntityPlayer, Consumer)} and its
|
2022-08-30 23:27:18 +08:00
|
|
|
* overloads: Helper method to cut common boilerplate for implementing
|
|
|
|
|
* {@link IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, Consumer)}</li>
|
|
|
|
|
* <li>{@link #getPseudoJavaCode(World, ExtendedFacing, int, int, int, int, int, int, Function, int, int, int, boolean)}:
|
|
|
|
|
* Generate a structure code template from existing structure.</li>
|
|
|
|
|
* <li>{@link #iterate(World, ExtendedFacing, int, int, int, int, int, int, boolean, int, int, int, IBlockPosConsumer, Runnable, Runnable)}
|
|
|
|
|
* and its overloads: help iterate over a coordinate space.</li>
|
|
|
|
|
* </ul>
|
2021-05-17 03:02:24 +08:00
|
|
|
*/
|
|
|
|
|
public class StructureUtility {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
private static final String NICE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz=|!@#$%&()[]{};:<>/?_,.*^'`";
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@SuppressWarnings("rawtypes")
|
|
|
|
|
private static final Map<Vec3Impl, IStructureNavigate> STEP = new HashMap<>();
|
2022-08-08 17:40:12 +08:00
|
|
|
|
2026-05-28 01:08:27 -05:00
|
|
|
private static final IStructureElement<Object> AIR = new StructureElement_Bridge<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
|
|
|
|
public boolean check(Object t, World world, int x, int y, int z) {
|
|
|
|
|
return world.isAirBlock(x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
|
|
|
|
public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
return check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@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;
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
|
|
|
|
|
@Override
|
2022-09-11 02:54:11 +08:00
|
|
|
@Deprecated
|
2022-09-13 21:22:34 +08:00
|
|
|
public PlaceResult survivalPlaceBlock(Object o, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
2022-08-08 17:40:12 +08:00
|
|
|
if (check(o, world, x, y, z)) return PlaceResult.SKIP;
|
2022-09-13 21:22:34 +08:00
|
|
|
if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, env.getActor())) return PlaceResult.REJECT;
|
2022-08-08 17:40:12 +08:00
|
|
|
world.setBlock(x, y, z, Blocks.air, 0, 2);
|
|
|
|
|
return PlaceResult.ACCEPT;
|
|
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
2022-08-08 17:40:12 +08:00
|
|
|
|
2026-05-28 01:08:27 -05:00
|
|
|
private static final IStructureElement<Object> NOT_AIR = new StructureElement_Bridge<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
|
|
|
|
public boolean check(Object t, World world, int x, int y, int z) {
|
|
|
|
|
return !world.isAirBlock(x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
|
|
|
|
public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
return check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@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;
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
|
2022-09-13 21:22:34 +08:00
|
|
|
@Override
|
|
|
|
|
public BlocksToPlace getBlocksToPlace(Object o, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
|
|
|
|
return BlocksToPlace.create(Objects::nonNull);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
@Override
|
2022-09-13 21:22:34 +08:00
|
|
|
public PlaceResult survivalPlaceBlock(Object o, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
2022-08-08 17:40:12 +08:00
|
|
|
if (check(o, world, x, y, z)) return PlaceResult.SKIP;
|
|
|
|
|
// user should place anything here.
|
|
|
|
|
// maybe make this configurable, but for now we try to take some cobble from user
|
2022-09-13 21:22:34 +08:00
|
|
|
if (env.getSource().takeOne(new ItemStack(Blocks.cobblestone), false)) {
|
2022-08-08 17:40:12 +08:00
|
|
|
world.setBlock(x, y, z, Blocks.cobblestone, 0, 2);
|
|
|
|
|
}
|
|
|
|
|
return PlaceResult.REJECT;
|
|
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
2022-08-08 17:40:12 +08:00
|
|
|
|
2026-05-28 01:08:27 -05:00
|
|
|
private static final IStructureElement<Object> ERROR = new StructureElement_Bridge<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
|
|
|
|
public boolean check(Object t, World world, int x, int y, int z) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
|
|
|
|
public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
return check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@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;
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
|
|
|
|
|
@Override
|
2022-09-13 21:22:34 +08:00
|
|
|
public PlaceResult survivalPlaceBlock(Object o, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
2022-08-08 17:40:12 +08:00
|
|
|
return PlaceResult.REJECT;
|
|
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
|
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
private StructureUtility() {}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-08-30 23:27:18 +08:00
|
|
|
* This is a helper method for implementing
|
|
|
|
|
* {@link IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, Consumer)}
|
|
|
|
|
* <p>
|
|
|
|
|
* This method will try to look up an {@link ItemBlock} for given block and meta from {@code s} and place it at
|
2022-08-08 17:40:12 +08:00
|
|
|
* given location. This method assume block at given coord is NOT acceptable, but may or may not be trivially
|
2022-08-30 23:27:18 +08:00
|
|
|
* replaceable This method might yield error messages to the player only if there is likely a programming error or
|
|
|
|
|
* exploit.
|
|
|
|
|
*
|
|
|
|
|
* @param s drain resources from this place
|
|
|
|
|
* @param actor source of action. cannot be null.
|
2022-08-08 17:40:12 +08:00
|
|
|
*/
|
|
|
|
|
public static PlaceResult survivalPlaceBlock(Block block, int meta, World world, int x, int y, int z, IItemSource s,
|
|
|
|
|
EntityPlayerMP actor) {
|
2022-09-11 02:54:11 +08:00
|
|
|
return survivalPlaceBlock(block, meta, world, x, y, z, s, (EntityPlayer) actor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This is a helper method for implementing
|
|
|
|
|
* {@link IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, Consumer)}
|
|
|
|
|
* <p>
|
|
|
|
|
* This method will try to look up an {@link ItemBlock} for given block and meta from {@code s} and place it at
|
|
|
|
|
* given location. This method assume block at given coord is NOT acceptable, but may or may not be trivially
|
|
|
|
|
* replaceable This method might yield error messages to the player only if there is likely a programming error or
|
|
|
|
|
* exploit.
|
|
|
|
|
*
|
|
|
|
|
* @param s drain resources from this place
|
|
|
|
|
* @param actor source of action. cannot be null.
|
|
|
|
|
*/
|
|
|
|
|
public static PlaceResult survivalPlaceBlock(Block block, int meta, World world, int x, int y, int z, IItemSource s,
|
|
|
|
|
EntityPlayer actor) {
|
2022-08-08 17:40:12 +08:00
|
|
|
return survivalPlaceBlock(block, meta, world, x, y, z, s, actor, null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-08-30 23:27:18 +08:00
|
|
|
* This is a helper method for implementing
|
|
|
|
|
* {@link IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, Consumer)}
|
|
|
|
|
* <p>
|
|
|
|
|
* This method will try to look up an {@link ItemBlock} for given block and meta from {@code s} and place it at
|
2022-08-08 17:40:12 +08:00
|
|
|
* given location. This method assume block at given coord is NOT acceptable, but may or may not be trivially
|
2022-08-30 23:27:18 +08:00
|
|
|
* replaceable This method might yield error messages to the player only if there is likely a programming error or
|
|
|
|
|
* exploit. This method might yield error messages to the chatter.
|
|
|
|
|
*
|
|
|
|
|
* @param s drain resources from this place
|
|
|
|
|
* @param actor source of action. cannot be null.
|
|
|
|
|
* @param chatter normal error destination. can be null to suppress them.
|
2022-08-08 17:40:12 +08:00
|
|
|
*/
|
|
|
|
|
public static PlaceResult survivalPlaceBlock(Block block, int meta, World world, int x, int y, int z, IItemSource s,
|
|
|
|
|
EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
2022-09-11 02:54:11 +08:00
|
|
|
return survivalPlaceBlock(block, meta, world, x, y, z, s, (EntityPlayer) actor, chatter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This is a helper method for implementing
|
|
|
|
|
* {@link IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, Consumer)}
|
|
|
|
|
* <p>
|
|
|
|
|
* This method will try to look up an {@link ItemBlock} for given block and meta from {@code s} and place it at
|
|
|
|
|
* given location. This method assume block at given coord is NOT acceptable, but may or may not be trivially
|
|
|
|
|
* replaceable This method might yield error messages to the player only if there is likely a programming error or
|
|
|
|
|
* exploit. This method might yield error messages to the chatter.
|
|
|
|
|
*
|
|
|
|
|
* @param s drain resources from this place
|
|
|
|
|
* @param actor source of action. cannot be null.
|
|
|
|
|
* @param chatter normal error destination. can be null to suppress them.
|
|
|
|
|
*/
|
|
|
|
|
public static PlaceResult survivalPlaceBlock(Block block, int meta, World world, int x, int y, int z, IItemSource s,
|
|
|
|
|
EntityPlayer actor, Consumer<IChatComponent> chatter) {
|
2022-08-08 17:40:12 +08:00
|
|
|
if (block == null) throw new NullPointerException();
|
|
|
|
|
if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT;
|
|
|
|
|
Item itemBlock = Item.getItemFromBlock(block);
|
|
|
|
|
int itemMeta = itemBlock instanceof ISpecialItemBlock
|
|
|
|
|
? ((ISpecialItemBlock) itemBlock).getItemMetaFromBlockMeta(block, meta)
|
|
|
|
|
: meta;
|
2025-03-11 05:26:36 +08:00
|
|
|
ItemStack stack = new ItemStack(itemBlock, 1, itemMeta);
|
|
|
|
|
if (!s.takeOne(stack, true)) {
|
2022-08-08 17:40:12 +08:00
|
|
|
if (chatter != null) chatter.accept(
|
2026-03-26 18:23:20 +04:00
|
|
|
new ChatComponentTranslation("structurelib.autoplace.missing_block", stack.func_151000_E()));
|
2022-08-08 17:40:12 +08:00
|
|
|
return PlaceResult.REJECT;
|
|
|
|
|
}
|
2026-06-19 08:31:17 -05:00
|
|
|
if (block instanceof ICustomBlockSetting blockCustom) {
|
2025-03-11 05:26:36 +08:00
|
|
|
blockCustom.setBlock(world, x, y, z, meta);
|
|
|
|
|
} else if (!stack.copy()
|
|
|
|
|
.tryPlaceItemIntoWorld(actor, world, x, y, z, ForgeDirection.UP.ordinal(), 0.5f, 0.5f, 0.5f)) {
|
|
|
|
|
return PlaceResult.REJECT;
|
|
|
|
|
}
|
|
|
|
|
if (!s.takeOne(stack, false)) {
|
|
|
|
|
// rollback
|
|
|
|
|
world.setBlockToAir(x, y, z);
|
2022-08-08 17:40:12 +08:00
|
|
|
}
|
|
|
|
|
return PlaceResult.ACCEPT;
|
|
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
/**
|
2022-08-30 23:27:18 +08:00
|
|
|
* This is a helper method for implementing
|
|
|
|
|
* {@link IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, Consumer)}
|
|
|
|
|
* <p>
|
|
|
|
|
* This method will try to look up an {@link ItemBlock} for given block and meta from {@code s} and place it at
|
2022-08-08 17:40:12 +08:00
|
|
|
* given location. This method assume block at given coord is NOT acceptable, but may or may not be trivially
|
2022-08-30 23:27:18 +08:00
|
|
|
* replaceable This method might yield error messages to the player only if there is likely a programming error or
|
|
|
|
|
* exploit.
|
2022-08-08 17:40:12 +08:00
|
|
|
*
|
2022-08-30 23:27:18 +08:00
|
|
|
* @param stack a valid stack with stack size of exactly 1. Must be of an ItemBlock!
|
|
|
|
|
* @param s drain resources from this place
|
|
|
|
|
* @param actor source of action. cannot be null.
|
2022-08-08 17:40:12 +08:00
|
|
|
*/
|
|
|
|
|
public static PlaceResult survivalPlaceBlock(ItemStack stack, NBTMode nbtMode, NBTTagCompound tag,
|
|
|
|
|
boolean assumeStackPresent, World world, int x, int y, int z, IItemSource s, EntityPlayerMP actor) {
|
2022-09-11 02:54:11 +08:00
|
|
|
return survivalPlaceBlock(stack, nbtMode, tag, assumeStackPresent, world, x, y, z, s, (EntityPlayer) actor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This is a helper method for implementing
|
|
|
|
|
* {@link IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, Consumer)}
|
|
|
|
|
* <p>
|
|
|
|
|
* This method will try to look up an {@link ItemBlock} for given block and meta from {@code s} and place it at
|
|
|
|
|
* given location. This method assume block at given coord is NOT acceptable, but may or may not be trivially
|
|
|
|
|
* replaceable This method might yield error messages to the player only if there is likely a programming error or
|
|
|
|
|
* exploit.
|
|
|
|
|
*
|
|
|
|
|
* @param stack a valid stack with stack size of exactly 1. Must be of an ItemBlock!
|
|
|
|
|
* @param s drain resources from this place
|
|
|
|
|
* @param actor source of action. cannot be null.
|
|
|
|
|
*/
|
|
|
|
|
public static PlaceResult survivalPlaceBlock(ItemStack stack, NBTMode nbtMode, NBTTagCompound tag,
|
|
|
|
|
boolean assumeStackPresent, World world, int x, int y, int z, IItemSource s, EntityPlayer actor) {
|
2022-08-08 17:40:12 +08:00
|
|
|
return survivalPlaceBlock(stack, nbtMode, tag, assumeStackPresent, world, x, y, z, s, actor, null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-08-30 23:27:18 +08:00
|
|
|
* This is a helper method for implementing
|
|
|
|
|
* {@link IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, Consumer)}
|
|
|
|
|
* <p>
|
|
|
|
|
* This method will try to look up an {@link ItemBlock} for given block and meta from {@code s} and place it at
|
2022-08-08 17:40:12 +08:00
|
|
|
* given location. This method assume block at given coord is NOT acceptable, but may or may not be trivially
|
2022-08-30 23:27:18 +08:00
|
|
|
* replaceable This method might yield error messages to the player only if there is likely a programming error or
|
|
|
|
|
* exploit. This method might yield error messages to the chatter.
|
2022-08-08 17:40:12 +08:00
|
|
|
*
|
|
|
|
|
* @param stack a valid stack with stack size of exactly 1. Must be of an ItemBlock!
|
2022-08-30 23:27:18 +08:00
|
|
|
* @param s drain resources from this place
|
|
|
|
|
* @param actor source of action. cannot be null.
|
|
|
|
|
* @param chatter normal error destination. can be null to suppress them.
|
2022-08-08 17:40:12 +08:00
|
|
|
*/
|
|
|
|
|
public static PlaceResult survivalPlaceBlock(ItemStack stack, NBTMode nbtMode, NBTTagCompound tag,
|
|
|
|
|
boolean assumeStackPresent, World world, int x, int y, int z, IItemSource s, EntityPlayerMP actor,
|
|
|
|
|
@Nullable Consumer<IChatComponent> chatter) {
|
2022-09-11 02:54:11 +08:00
|
|
|
return survivalPlaceBlock(
|
|
|
|
|
stack,
|
|
|
|
|
nbtMode,
|
|
|
|
|
tag,
|
|
|
|
|
assumeStackPresent,
|
|
|
|
|
world,
|
|
|
|
|
x,
|
|
|
|
|
y,
|
|
|
|
|
z,
|
|
|
|
|
s,
|
|
|
|
|
(EntityPlayer) actor,
|
|
|
|
|
chatter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This is a helper method for implementing
|
|
|
|
|
* {@link IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, Consumer)}
|
|
|
|
|
* <p>
|
|
|
|
|
* This method will try to look up an {@link ItemBlock} for given block and meta from {@code s} and place it at
|
|
|
|
|
* given location. This method assume block at given coord is NOT acceptable, but may or may not be trivially
|
|
|
|
|
* replaceable This method might yield error messages to the player only if there is likely a programming error or
|
|
|
|
|
* exploit. This method might yield error messages to the chatter.
|
|
|
|
|
*
|
|
|
|
|
* @param stack a valid stack with stack size of exactly 1. Must be of an ItemBlock!
|
|
|
|
|
* @param s drain resources from this place
|
|
|
|
|
* @param actor source of action. cannot be null.
|
|
|
|
|
* @param chatter normal error destination. can be null to suppress them.
|
|
|
|
|
*/
|
|
|
|
|
public static PlaceResult survivalPlaceBlock(ItemStack stack, NBTMode nbtMode, NBTTagCompound tag,
|
|
|
|
|
boolean assumeStackPresent, World world, int x, int y, int z, IItemSource s, EntityPlayer actor,
|
|
|
|
|
@Nullable Consumer<IChatComponent> chatter) {
|
2022-08-08 17:40:12 +08:00
|
|
|
if (stack == null) throw new NullPointerException();
|
|
|
|
|
if (stack.stackSize != 1) throw new IllegalArgumentException();
|
|
|
|
|
if (!(stack.getItem() instanceof ItemBlock)) throw new IllegalArgumentException();
|
|
|
|
|
if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT;
|
|
|
|
|
if (!assumeStackPresent && !s.takeOne(stack, true)) {
|
|
|
|
|
if (chatter != null) chatter.accept(
|
2026-03-26 18:23:20 +04:00
|
|
|
new ChatComponentTranslation("structurelib.autoplace.missing_block", stack.func_151000_E()));
|
2022-08-08 17:40:12 +08:00
|
|
|
return PlaceResult.REJECT;
|
|
|
|
|
}
|
|
|
|
|
if (!stack.copy().tryPlaceItemIntoWorld(actor, world, x, y, z, ForgeDirection.UP.ordinal(), 0.5f, 0.5f, 0.5f))
|
|
|
|
|
return PlaceResult.REJECT;
|
|
|
|
|
if (!s.takeOne(stack, false))
|
|
|
|
|
// this is bad! probably an exploit somehow. Let's nullify the block we just placed instead
|
|
|
|
|
world.setBlockToAir(x, y, z);
|
|
|
|
|
return PlaceResult.ACCEPT;
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* Return a structure element that only allow air (or air equivalents, like Railcraft hidden blocks). You usually
|
|
|
|
|
* don't need to call this yourselves. Use {@code -} in shape to automatically use this. Provided nontheless in case
|
|
|
|
|
* you want this as a fallback to something else.
|
|
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static IStructureElement<Object> isAir() {
|
2022-07-08 17:14:18 +08:00
|
|
|
return AIR;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* Return a structure element that allow anything but air (or air equivalents, like Railcraft hidden blocks). You
|
|
|
|
|
* usually don't need to call this yourselves. Use {@code +} in shape to automatically use this. Provided nontheless
|
|
|
|
|
* in case you want this as a fallback to something else.
|
|
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static IStructureElement<Object> notAir() {
|
2022-07-08 17:14:18 +08:00
|
|
|
return NOT_AIR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check returns false. Placement is always handled by this and does nothing. Makes little to no use it in fallback
|
|
|
|
|
* chain.
|
|
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static IStructureElement<Object> error() {
|
2022-07-08 17:14:18 +08:00
|
|
|
return ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
// region hint only
|
2022-07-08 17:14:18 +08:00
|
|
|
|
|
|
|
|
/**
|
2022-08-30 23:27:18 +08:00
|
|
|
* Spawn a hint with given amount of dots. Check always returns: true. Only useful as a fallback, e.g.
|
|
|
|
|
* {@link #ofBlockUnlocalizedName(String, String, int, IStructureElement)}
|
2022-07-08 17:14:18 +08:00
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static IStructureElementNoPlacement<Object> ofHint(int dots) {
|
2022-07-08 17:14:18 +08:00
|
|
|
int meta = dots - 1;
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElementNoPlacement<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean check(Object t, World world, int x, int y, int z) {
|
2022-07-08 17:14:18 +08:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2024-12-14 13:13:34 -05:00
|
|
|
return check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean spawnHint(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-07-08 17:14:18 +08:00
|
|
|
StructureLibAPI.hintParticle(world, x, y, z, StructureLibAPI.getBlockHint(), meta);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-08-30 23:27:18 +08:00
|
|
|
* Spawn a hint with given textures. Check always returns: true. Only useful as a fallback, e.g.
|
|
|
|
|
* {@link #ofBlockUnlocalizedName(String, String, int, IStructureElement)}
|
2022-07-08 17:14:18 +08:00
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static IStructureElementNoPlacement<Object> ofHintDeferred(Supplier<IIcon[]> icons) {
|
|
|
|
|
return new IStructureElementNoPlacement<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean check(Object t, World world, int x, int y, int z) {
|
2022-07-08 17:14:18 +08:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2024-12-14 13:13:34 -05:00
|
|
|
return check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean spawnHint(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-07-08 17:14:18 +08:00
|
|
|
StructureLibAPI.hintParticle(world, x, y, z, icons.get());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-08-30 23:27:18 +08:00
|
|
|
* Spawn a hint with given amount of textures and tint. Check always returns: true. Only useful as a fallback, e.g.
|
|
|
|
|
* {@link #ofBlockUnlocalizedName(String, String, int, IStructureElement)}
|
2022-07-08 17:14:18 +08:00
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static IStructureElementNoPlacement<Object> ofHintDeferred(Supplier<IIcon[]> icons, short[] RGBa) {
|
|
|
|
|
return new IStructureElementNoPlacement<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean check(Object t, World world, int x, int y, int z) {
|
2022-07-08 17:14:18 +08:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2024-12-14 13:13:34 -05:00
|
|
|
return check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean spawnHint(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-07-08 17:14:18 +08:00
|
|
|
StructureLibAPI.hintParticleTinted(world, x, y, z, icons.get(), RGBa);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
// endregion
|
2022-07-08 17:14:18 +08:00
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
// region block
|
2022-07-08 17:14:18 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A more primitive variant of {@link #ofBlocksTiered(ITierConverter, List, Object, BiConsumer, Function)} Main
|
|
|
|
|
* difference is this one is check only.
|
2022-08-30 23:27:18 +08:00
|
|
|
* <p>
|
|
|
|
|
* Note that if tierExtractor returns a null, this block will be rejected immediately. This makes null a potent
|
|
|
|
|
* candidate for notSet.
|
|
|
|
|
* <p>
|
|
|
|
|
* See documentation on the other overload for more information.
|
2022-07-08 17:14:18 +08:00
|
|
|
*
|
|
|
|
|
* @see #ofBlocksTiered(ITierConverter, Object, BiConsumer, Function)
|
|
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T, TIER> IStructureElementCheckOnly<T> ofBlocksTiered(ITierConverter<? extends TIER> tierExtractor,
|
2026-05-28 23:04:03 -05:00
|
|
|
@Nullable TIER notSet, BiConsumer<? super T, ? super TIER> setter,
|
|
|
|
|
Function<? super T, ? extends TIER> getter) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (tierExtractor == null) throw new IllegalArgumentException();
|
|
|
|
|
if (setter == null) throw new IllegalArgumentException();
|
|
|
|
|
if (getter == null) throw new IllegalArgumentException();
|
|
|
|
|
|
|
|
|
|
return (t, world, x, y, z) -> {
|
|
|
|
|
Block block = world.getBlock(x, y, z);
|
|
|
|
|
int meta = world.getBlockMetadata(x, y, z);
|
|
|
|
|
TIER tier = tierExtractor.convert(block, meta);
|
2022-08-30 23:27:18 +08:00
|
|
|
if (tier == null) return false;
|
2022-07-08 17:14:18 +08:00
|
|
|
TIER current = getter.apply(t);
|
|
|
|
|
if (Objects.equals(notSet, current)) {
|
2022-08-30 23:27:18 +08:00
|
|
|
if (Objects.equals(notSet, tier)) {
|
2025-03-11 22:41:57 +08:00
|
|
|
if (PANIC_MODE) {
|
2022-08-30 23:27:18 +08:00
|
|
|
throw new AssertionError("tierExtractor should never return notSet: " + notSet);
|
|
|
|
|
} else {
|
|
|
|
|
StructureLib.LOGGER.error("#########################################");
|
|
|
|
|
StructureLib.LOGGER.error("#########################################");
|
|
|
|
|
StructureLib.LOGGER
|
|
|
|
|
.error("tierExtractor should never return notSet: {}", notSet, new Throwable());
|
|
|
|
|
StructureLib.LOGGER.error("#########################################");
|
|
|
|
|
StructureLib.LOGGER.error("#########################################");
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
setter.accept(t, tier);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return Objects.equals(current, tier);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-08-30 23:27:18 +08:00
|
|
|
* Element representing a component with different tiers. Your multi will accept any of them (as long as player does
|
2025-04-30 21:55:19 +08:00
|
|
|
* not mix tier (*)), but would like to know which is used. Player can use more blueprint to get hints denoting more
|
2022-08-30 23:27:18 +08:00
|
|
|
* advanced components.
|
|
|
|
|
* <p>
|
2025-04-30 21:55:19 +08:00
|
|
|
* Above all else, you need to decide what type of tier you are going to use. For simpler cases, Integer is a good
|
|
|
|
|
* choice. You can also use an existing Enum. It can also be a String or a
|
2022-08-30 23:27:18 +08:00
|
|
|
* {@link net.minecraft.util.ResourceLocation}, or basically anything. It doesn't even have to implement
|
|
|
|
|
* {@link Comparable}.
|
|
|
|
|
* <p>
|
2025-04-30 21:55:19 +08:00
|
|
|
* If your tier extractor function returns null on any (Block, meta) pair, then it means this block is rejected.
|
|
|
|
|
* This also implies null can never be a valid tier.
|
2022-08-30 23:27:18 +08:00
|
|
|
* <p>
|
2025-04-30 21:55:19 +08:00
|
|
|
* This structure element require you will reset the backing storage of getter in your own code, such that in every
|
|
|
|
|
* round of structure checking, the first call to getter would always return notSet. You can also make getter to
|
|
|
|
|
* return other value on that occasion to forcefully select a certain tier, but then you'd probably be better off
|
|
|
|
|
* using block adders or {@link #ofBlock(Block, int, Block, int)} and its overloads
|
2024-08-07 16:37:30 +08:00
|
|
|
* <p>
|
2022-08-30 23:27:18 +08:00
|
|
|
* There is yet no TileEntity counterpart of this utility. Feel free to submit a PR to add it.
|
|
|
|
|
* <p>
|
2025-04-30 21:55:19 +08:00
|
|
|
* (*): Implementation wise, this structure element will only accept a block if its tier is the same as existing
|
|
|
|
|
* tier or if existing tier is unset (i.e. value of third argument). If there are multiple (block, tier) tuple for a
|
|
|
|
|
* given tier, then player will be allowed to use multiple kinds of blocks, but their tier is guaranteed to be the
|
|
|
|
|
* same.
|
|
|
|
|
* <p>
|
2026-06-19 08:31:17 -05:00
|
|
|
* <h4>WARNING</h4> <b>You SHOULD NOT return notSet from your tierExtractor.</b> If you do so, we will have this
|
2025-04-30 21:55:19 +08:00
|
|
|
* chain of events:
|
2022-08-30 23:27:18 +08:00
|
|
|
* <ul>
|
|
|
|
|
* <li>at check start, tier is reset to notSet
|
2025-04-30 21:55:19 +08:00
|
|
|
* <li>the tier extractor returns notSet for first block, so since ofBlocksTiered finds your current tier is notSet,
|
|
|
|
|
* and the new block's tier is the same as that, so it's accepted
|
2022-08-30 23:27:18 +08:00
|
|
|
* <li>at 2nd, 3rd..., same thing in 2 happens
|
|
|
|
|
* <li>at last occurrence, player placed a valid block with a tier not notSet. ofBlocksTiered now sets your current
|
|
|
|
|
* tier to this tier, and accept the block
|
|
|
|
|
* <li>check got notified structure is sound, and tier is not notSet. it marks the structure as complete and start
|
|
|
|
|
* doing its business
|
|
|
|
|
* <li>Player enjoy a (probably) hilariously shaped multi and (probably) reduced build cost.
|
|
|
|
|
* </ul>
|
2026-06-19 08:31:17 -05:00
|
|
|
* <h4>Example Implementation</h4>
|
2022-08-30 23:27:18 +08:00
|
|
|
* <p>
|
2025-04-30 21:55:19 +08:00
|
|
|
* Assume you have 16 tier, each map to one particular block's 16 different meta. You will usually want something
|
|
|
|
|
* like this
|
2023-02-27 17:30:57 +00:00
|
|
|
*
|
2022-08-30 23:27:18 +08:00
|
|
|
* <pre>
|
|
|
|
|
* public class Tile extends TileEntity {
|
|
|
|
|
* private static final Block B = getTargetBlock();
|
|
|
|
|
* private static final IStructureDefinition<Tile> S = IStructureDefinition.<Tile>builder()
|
|
|
|
|
* // ... addShape, and other elements
|
|
|
|
|
* .addElement('c', ofBlocksTiered((b, m) -> b == B ? m : null,
|
|
|
|
|
* IntStream.range(0, 16).mapToObj(i -> Pair.of(B, i)).collect(Collectors.toList()),
|
|
|
|
|
* -1,
|
|
|
|
|
* (t, m) -> t.tier = m,
|
|
|
|
|
* t -> t.tier))
|
|
|
|
|
* .build();
|
|
|
|
|
* private int tier;
|
|
|
|
|
*
|
|
|
|
|
* public boolean check() {
|
|
|
|
|
* tier = -1;
|
|
|
|
|
* if (!S.check(...))
|
|
|
|
|
* return false;
|
|
|
|
|
* return isTierAcceptable(tier);
|
|
|
|
|
* }
|
|
|
|
|
* }
|
|
|
|
|
* </pre>
|
2022-07-08 17:14:18 +08:00
|
|
|
*
|
2025-04-30 21:55:19 +08:00
|
|
|
* Important bits of this example implementation:
|
|
|
|
|
* <ul>
|
|
|
|
|
* <li>The supplied ITierConverter returns null for an invalid block</li>
|
|
|
|
|
* <li>Possible tier returned by ITierConverter is Integer object 0 to 15.</li>
|
|
|
|
|
* <li>notSet is set to -1. tier got reset to this value by caller before calling into
|
|
|
|
|
* {@link IStructureDefinition#check(Object, String, World, ExtendedFacing, int, int, int, int, int, int, boolean)}</li>
|
|
|
|
|
* <li>notSet is never a tier returned by ITierConverter.</li>
|
|
|
|
|
* </ul>
|
|
|
|
|
* <p>
|
|
|
|
|
* As a friendly reminder, the above characteristics are only for this simply demo, not as a general restriction
|
|
|
|
|
* imposed by ofBlocksTiered.
|
|
|
|
|
*
|
2022-08-30 23:27:18 +08:00
|
|
|
* @param tierExtractor a function to extract tier info from a block. This function can return null and will never
|
2024-08-07 16:37:30 +08:00
|
|
|
* be passed a null block or an invalid block meta. If this function returns null, then the
|
|
|
|
|
* block would be considered invalid.
|
2022-08-30 23:27:18 +08:00
|
|
|
* @param allKnownTiers A list of all known tiers as of calling. Can be empty or null. No hint will be spawned if
|
|
|
|
|
* empty or null. Cannot have null elements. First element denotes the most primitive tier.
|
2022-07-08 17:14:18 +08:00
|
|
|
* Last element denotes the most advanced tier. If not all tiers are available at definition
|
2022-08-30 23:27:18 +08:00
|
|
|
* construction time, use {@link #lazy(Supplier)} or its overloads to delay a bit. This list is
|
2022-09-11 02:54:11 +08:00
|
|
|
* only for hint particle spawning and survival build. The hint/autoplace code will choose 1st
|
|
|
|
|
* pair to spawn/place if trigger item has master channel data of 1, 2nd pair if 2, and so on.
|
2025-04-30 21:55:19 +08:00
|
|
|
* @param notSet The value returned from {@code getter} when there were no tier info found in T yet. Cannot
|
|
|
|
|
* be null.
|
2022-08-30 23:27:18 +08:00
|
|
|
* @param getter a function to retrieve the current tier from context object
|
|
|
|
|
* @param setter a function to set the current tier into context object
|
2022-07-08 17:14:18 +08:00
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T, TIER> IStructureElement<T> ofBlocksTiered(ITierConverter<? extends TIER> tierExtractor,
|
2026-05-28 23:04:03 -05:00
|
|
|
@Nullable List<Pair<Block, Integer>> allKnownTiers, @Nullable TIER notSet,
|
|
|
|
|
BiConsumer<? super T, ? super TIER> setter, Function<? super T, ? extends TIER> getter) {
|
2026-05-23 19:42:15 +02:00
|
|
|
List<String> descriptions = null;
|
|
|
|
|
if (allKnownTiers != null) {
|
|
|
|
|
descriptions = new java.util.ArrayList<>();
|
|
|
|
|
for (Pair<Block, Integer> tier : allKnownTiers) {
|
|
|
|
|
Item item = Item.getItemFromBlock(tier.getLeft());
|
|
|
|
|
if (item != null) {
|
|
|
|
|
descriptions.add(new ItemStack(item, 1, tier.getRight()).getUnlocalizedName() + ".name");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ofBlocksTiered(tierExtractor, allKnownTiers, notSet, setter, getter, descriptions);
|
2026-05-23 09:42:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Like {@link #ofBlocksTiered(ITierConverter, List, Object, BiConsumer, Function)}, but with an explicit
|
2026-05-24 07:35:25 +02:00
|
|
|
* description that will be returned by {@link IStructureElement#getDescription(Object)}.
|
2026-05-23 09:42:20 +02:00
|
|
|
*
|
|
|
|
|
* @param description the description lang keys to attach to this element, or null for no description
|
|
|
|
|
* @see #ofBlocksTiered(ITierConverter, List, Object, BiConsumer, Function)
|
|
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T, TIER> IStructureElement<T> ofBlocksTiered(ITierConverter<? extends TIER> tierExtractor,
|
2026-05-28 23:04:03 -05:00
|
|
|
@Nullable List<Pair<Block, Integer>> allKnownTiers, @Nullable TIER notSet,
|
|
|
|
|
BiConsumer<? super T, ? super TIER> setter, Function<? super T, ? extends TIER> getter,
|
|
|
|
|
@Nullable List<String> description) {
|
2022-07-08 17:14:18 +08:00
|
|
|
List<Pair<Block, Integer>> hints = allKnownTiers == null ? Collections.emptyList() : allKnownTiers;
|
|
|
|
|
if (hints.stream().anyMatch(Objects::isNull)) throw new IllegalArgumentException();
|
|
|
|
|
IStructureElementCheckOnly<T> check = ofBlocksTiered(tierExtractor, notSet, setter, getter);
|
2026-05-28 01:08:27 -05:00
|
|
|
return new StructureElement_Bridge<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
|
|
|
|
public boolean check(T t, World world, int x, int y, int z) {
|
|
|
|
|
return check.check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Pair<Block, Integer> getHint(ItemStack trigger) {
|
2024-12-14 13:13:34 -05:00
|
|
|
if (hints.isEmpty()) return null;
|
2022-07-08 17:14:18 +08:00
|
|
|
return hints.get(Math.min(Math.max(trigger.stackSize, 1), hints.size()) - 1);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
|
|
|
|
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
Pair<Block, Integer> hint = getHint(trigger);
|
|
|
|
|
if (hint == null) return true;
|
|
|
|
|
TIER hintTier = tierExtractor.convert(hint.getKey(), hint.getValue());
|
|
|
|
|
Block block = world.getBlock(x, y, z);
|
|
|
|
|
int meta = world.getBlockMetadata(x, y, z);
|
|
|
|
|
TIER worldTier = tierExtractor.convert(block, meta);
|
|
|
|
|
|
|
|
|
|
// if the block is in the same tier as the hint block, this could be valid
|
|
|
|
|
return Objects.equals(hintTier, worldTier);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
|
|
|
|
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
Pair<Block, Integer> hint = getHint(trigger);
|
2022-08-08 17:40:12 +08:00
|
|
|
if (hint == null) return false;
|
2022-07-08 17:14:18 +08:00
|
|
|
StructureLibAPI.hintParticle(world, x, y, z, hint.getKey(), hint.getValue());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
Pair<Block, Integer> hint = getHint(trigger);
|
2022-08-08 17:40:12 +08:00
|
|
|
if (hint == null) return false;
|
2026-06-19 08:31:17 -05:00
|
|
|
if (hint.getKey() instanceof ICustomBlockSetting block) {
|
2022-07-08 17:14:18 +08:00
|
|
|
block.setBlock(world, x, y, z, hint.getValue());
|
|
|
|
|
} else {
|
|
|
|
|
world.setBlock(x, y, z, hint.getKey(), hint.getValue(), 2);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
|
2022-09-13 21:22:34 +08:00
|
|
|
@Nullable
|
|
|
|
|
@Override
|
|
|
|
|
public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
|
|
|
|
Pair<Block, Integer> hint = getHint(trigger);
|
|
|
|
|
return BlocksToPlace.create(hint.getKey(), hint.getValue());
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
@Override
|
2022-09-11 02:54:11 +08:00
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
2022-08-08 17:40:12 +08:00
|
|
|
Pair<Block, Integer> hint = getHint(trigger);
|
2026-06-06 18:10:11 +02:00
|
|
|
if (hint == null) return PlaceResult.REJECT;
|
2022-08-12 19:57:24 +08:00
|
|
|
Block block = world.getBlock(x, y, z);
|
|
|
|
|
int meta = world.getBlockMetadata(x, y, z);
|
|
|
|
|
TIER tier = tierExtractor.convert(block, meta);
|
2022-08-29 00:49:40 +08:00
|
|
|
if (Objects.equals(tier, tierExtractor.convert(hint.getKey(), hint.getValue())))
|
|
|
|
|
return PlaceResult.SKIP;
|
2022-08-08 17:40:12 +08:00
|
|
|
return StructureUtility.survivalPlaceBlock(
|
2022-09-11 02:54:11 +08:00
|
|
|
hint.getKey(),
|
|
|
|
|
hint.getValue(),
|
|
|
|
|
world,
|
|
|
|
|
x,
|
|
|
|
|
y,
|
|
|
|
|
z,
|
|
|
|
|
env.getSource(),
|
|
|
|
|
env.getActor(),
|
|
|
|
|
env.getChatter());
|
2022-08-08 17:40:12 +08:00
|
|
|
}
|
2026-05-23 09:42:20 +02:00
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
|
@Override
|
2026-05-24 07:35:25 +02:00
|
|
|
public List<String> getDescription(T context) {
|
|
|
|
|
TIER currentTier = getter.apply(context);
|
2026-05-23 19:42:15 +02:00
|
|
|
if (Objects.equals(currentTier, notSet)) return description;
|
|
|
|
|
|
|
|
|
|
for (Pair<Block, Integer> pair : hints) {
|
|
|
|
|
TIER pairTier = tierExtractor.convert(pair.getKey(), pair.getValue());
|
|
|
|
|
if (!Objects.equals(pairTier, currentTier)) continue;
|
|
|
|
|
Item item = Item.getItemFromBlock(pair.getKey());
|
|
|
|
|
if (item == null) continue;
|
|
|
|
|
return Collections
|
|
|
|
|
.singletonList(new ItemStack(item, 1, pair.getValue()).getUnlocalizedName() + ".name");
|
|
|
|
|
}
|
2026-05-23 09:42:20 +02:00
|
|
|
return description;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Denote a block using unlocalized names. This can be useful to get around mod loading order issues.
|
|
|
|
|
* <p>
|
2022-08-30 23:27:18 +08:00
|
|
|
* <b>DUE TO HISTORICAL REASONS, THIS WAS CALLED ofBlockUnlocalizedName, BUT IT ACTUALLY USES REGISTRY NAME
|
|
|
|
|
* INSTEAD.</b>
|
|
|
|
|
* <p>
|
2022-07-08 17:14:18 +08:00
|
|
|
* While no immediate error will be thrown, client code should ensure said mod is loaded and said mod is present,
|
|
|
|
|
* otherwise bad things will happen later!
|
|
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static IStructureElement<Object> ofBlockUnlocalizedName(String modid, String unlocalizedName, int meta) {
|
2022-08-08 17:40:12 +08:00
|
|
|
return ofBlockUnlocalizedName(modid, unlocalizedName, meta, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Denote a block using unlocalized names. This can be useful to get around mod loading order issues.
|
|
|
|
|
* <p>
|
2022-08-30 23:27:18 +08:00
|
|
|
* <b>DUE TO HISTORICAL REASONS, THIS WAS CALLED ofBlockUnlocalizedName, BUT IT ACTUALLY USES REGISTRY NAME
|
|
|
|
|
* INSTEAD.</b>
|
|
|
|
|
* <p>
|
2022-08-08 17:40:12 +08:00
|
|
|
* While no immediate error will be thrown, client code should ensure said mod is loaded and said mod is present,
|
2022-08-30 23:27:18 +08:00
|
|
|
* otherwise the element will effectively become an {@link #error()}.
|
2022-08-08 17:40:12 +08:00
|
|
|
* <p>
|
|
|
|
|
* Will place block or hint using the given meta if wildcard is true.
|
|
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static IStructureElement<Object> ofBlockUnlocalizedName(String modid, String registryName, int meta,
|
2022-08-30 23:27:18 +08:00
|
|
|
boolean wildcard) {
|
|
|
|
|
if (StringUtils.isBlank(registryName)) throw new IllegalArgumentException();
|
2022-07-08 17:14:18 +08:00
|
|
|
if (meta < 0) throw new IllegalArgumentException();
|
|
|
|
|
if (meta > 15) throw new IllegalArgumentException();
|
|
|
|
|
if (StringUtils.isBlank(modid)) throw new IllegalArgumentException();
|
2026-05-28 01:08:27 -05:00
|
|
|
return new StructureElement_Bridge<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
private Block block;
|
|
|
|
|
|
|
|
|
|
private Block getBlock() {
|
2022-08-30 23:27:18 +08:00
|
|
|
if (block == null) block = GameRegistry.findBlock(modid, registryName);
|
2022-07-08 17:14:18 +08:00
|
|
|
return block;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean check(Object t, World world, int x, int y, int z) {
|
2022-08-08 17:40:12 +08:00
|
|
|
return world.getBlock(x, y, z) == getBlock() && (wildcard || world.getBlockMetadata(x, y, z) == meta);
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2024-12-14 13:13:34 -05:00
|
|
|
return check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean spawnHint(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-08-08 17:40:12 +08:00
|
|
|
if (getBlock() == null) return error().spawnHint(t, world, x, y, z, trigger);
|
2022-07-08 17:14:18 +08:00
|
|
|
StructureLibAPI.hintParticle(world, x, y, z, getBlock(), meta);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean placeBlock(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-08-08 17:40:12 +08:00
|
|
|
if (getBlock() == null) return error().placeBlock(t, world, x, y, z, trigger);
|
2022-07-08 17:14:18 +08:00
|
|
|
world.setBlock(x, y, z, getBlock(), meta, 2);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
|
2022-09-13 21:22:34 +08:00
|
|
|
@Nullable
|
|
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public BlocksToPlace getBlocksToPlace(Object t, World world, int x, int y, int z, ItemStack trigger,
|
2022-09-13 21:22:34 +08:00
|
|
|
AutoPlaceEnvironment env) {
|
|
|
|
|
if (getBlock() == null) return error().getBlocksToPlace(t, world, x, y, z, trigger, env);
|
|
|
|
|
return BlocksToPlace.create(getBlock(), meta);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public PlaceResult survivalPlaceBlock(Object t, World world, int x, int y, int z, ItemStack trigger,
|
2022-09-11 02:54:11 +08:00
|
|
|
AutoPlaceEnvironment env) {
|
2022-08-08 17:40:12 +08:00
|
|
|
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
|
|
|
|
|
if (getBlock() == null) return PlaceResult.REJECT;
|
2022-09-11 02:54:11 +08:00
|
|
|
return StructureUtility.survivalPlaceBlock(
|
|
|
|
|
getBlock(),
|
|
|
|
|
meta,
|
|
|
|
|
world,
|
|
|
|
|
x,
|
|
|
|
|
y,
|
|
|
|
|
z,
|
|
|
|
|
env.getSource(),
|
|
|
|
|
env.getActor(),
|
|
|
|
|
env.getChatter());
|
2022-08-08 17:40:12 +08:00
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Similiar to the other overload, but allows client code to specify a fallback in case said block was not found
|
|
|
|
|
* later when the element got called.
|
|
|
|
|
* <p>
|
2022-08-30 23:27:18 +08:00
|
|
|
* <b>DUE TO HISTORICAL REASONS, THIS WAS CALLED ofBlockUnlocalizedName, BUT IT ACTUALLY USES REGISTRY NAME
|
|
|
|
|
* INSTEAD.</b>
|
|
|
|
|
* <p>
|
|
|
|
|
* This is slightly different to using the other overload and another element in a
|
|
|
|
|
* {@link #ofChain(IStructureElement[])}. Here fallback will only ever be used if this fails, where ofChain would
|
|
|
|
|
* form an OR relationship even if the mod is loaded and the block exists in registry.
|
2022-07-08 17:14:18 +08:00
|
|
|
*/
|
2022-08-08 17:40:12 +08:00
|
|
|
public static <T> IStructureElement<T> ofBlockUnlocalizedName(String modid, String unlocalizedName, int meta,
|
2026-05-28 01:08:27 -05:00
|
|
|
IStructureElement<? super T> fallback) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (StringUtils.isBlank(unlocalizedName)) throw new IllegalArgumentException();
|
|
|
|
|
if (meta < 0) throw new IllegalArgumentException();
|
|
|
|
|
if (meta > 15) throw new IllegalArgumentException();
|
|
|
|
|
if (StringUtils.isBlank(modid)) throw new IllegalArgumentException();
|
|
|
|
|
if (fallback == null) throw new IllegalArgumentException();
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElement<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
private Block block;
|
|
|
|
|
private boolean initialized;
|
|
|
|
|
|
|
|
|
|
private boolean init() {
|
|
|
|
|
if (!initialized) {
|
|
|
|
|
block = GameRegistry.findBlock(modid, unlocalizedName);
|
|
|
|
|
initialized = true;
|
|
|
|
|
}
|
|
|
|
|
return block != null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean check(T t, World world, int x, int y, int z) {
|
2024-12-14 13:13:34 -05:00
|
|
|
if (init()) return world.getBlock(x, y, z) == block && world.getBlockMetadata(x, y, z) == meta;
|
2022-08-08 17:40:12 +08:00
|
|
|
else return fallback.check(t, world, x, y, z);
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
|
|
|
|
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
if (init()) return world.getBlock(x, y, z) == block && world.getBlockMetadata(x, y, z) == meta;
|
|
|
|
|
else return fallback.couldBeValid(t, world, x, y, z, trigger);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
|
|
|
|
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
if (init()) {
|
|
|
|
|
StructureLibAPI.hintParticle(world, x, y, z, block, meta);
|
|
|
|
|
return true;
|
2022-08-08 17:40:12 +08:00
|
|
|
} else return fallback.spawnHint(t, world, x, y, z, trigger);
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
if (init()) {
|
|
|
|
|
world.setBlock(x, y, z, block, meta, 2);
|
|
|
|
|
return true;
|
2022-08-08 17:40:12 +08:00
|
|
|
} else return fallback.placeBlock(t, world, x, y, z, trigger);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-13 21:22:34 +08:00
|
|
|
@Nullable
|
|
|
|
|
@Override
|
|
|
|
|
public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
|
|
|
|
if (init()) return BlocksToPlace.create(block, meta);
|
|
|
|
|
return fallback.getBlocksToPlace(t, world, x, y, z, trigger, env);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
@Override
|
2022-09-11 02:54:11 +08:00
|
|
|
@Deprecated
|
2022-08-08 17:40:12 +08:00
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
|
|
|
|
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
|
|
|
|
|
if (init()) return StructureUtility.survivalPlaceBlock(block, meta, world, x, y, z, s, actor, chatter);
|
|
|
|
|
return fallback.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
2022-09-11 02:54:11 +08:00
|
|
|
|
|
|
|
|
@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 PlaceResult.SKIP;
|
|
|
|
|
if (init()) return StructureUtility.survivalPlaceBlock(
|
|
|
|
|
block,
|
|
|
|
|
meta,
|
|
|
|
|
world,
|
|
|
|
|
x,
|
|
|
|
|
y,
|
|
|
|
|
z,
|
|
|
|
|
env.getSource(),
|
|
|
|
|
env.getActor(),
|
|
|
|
|
env.getChatter());
|
|
|
|
|
return fallback.survivalPlaceBlock(t, world, x, y, z, trigger, env);
|
|
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-08-30 23:27:18 +08:00
|
|
|
* Accept a set of blocks. Less cumbersome to use than {@link #ofBlocksMapHint(Map, Block, int)} when for any
|
|
|
|
|
* accepted block type we accept only one meta for each.
|
|
|
|
|
* <p>
|
|
|
|
|
* Does not have autoplace.
|
|
|
|
|
*
|
2026-05-28 23:04:03 -05:00
|
|
|
* @param blocksMap Accepted (block, meta) pairs.
|
2022-08-30 23:27:18 +08:00
|
|
|
* @param hintBlock hint block to use
|
|
|
|
|
* @param hintMeta hint meta to use
|
|
|
|
|
* @see #ofBlocksMapHint(Map, Block, int)
|
2022-07-08 17:14:18 +08:00
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static IStructureElementNoPlacement<Object> ofBlocksFlatHint(Map<Block, Integer> blocksMap, Block hintBlock,
|
2022-08-08 17:40:12 +08:00
|
|
|
int hintMeta) {
|
2026-05-28 01:08:27 -05:00
|
|
|
if (blocksMap == null || blocksMap.isEmpty() || hintBlock == null) {
|
2022-07-08 17:14:18 +08:00
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElementNoPlacement<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean check(Object t, World world, int x, int y, int z) {
|
2022-07-08 17:14:18 +08:00
|
|
|
Block worldBlock = world.getBlock(x, y, z);
|
2026-05-28 01:08:27 -05:00
|
|
|
return blocksMap.getOrDefault(worldBlock, MIN_VALUE) == worldBlock.getDamageValue(world, x, y, z);
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2024-12-14 13:13:34 -05:00
|
|
|
return check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean spawnHint(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-07-08 17:14:18 +08:00
|
|
|
StructureLibAPI.hintParticle(world, x, y, z, hintBlock, hintMeta);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-08-30 23:27:18 +08:00
|
|
|
* Accept a set of blocks.
|
|
|
|
|
* <p>
|
|
|
|
|
* Does not have autoplace.
|
|
|
|
|
*
|
2026-05-28 23:04:03 -05:00
|
|
|
* @param blocksMap Accepted (block, meta) pairs.
|
2022-08-30 23:27:18 +08:00
|
|
|
* @param hintBlock hint block to use
|
|
|
|
|
* @param hintMeta hint meta to use
|
|
|
|
|
* @see #ofBlocksFlatHint(Map, Block, int)
|
2022-07-08 17:14:18 +08:00
|
|
|
*/
|
2026-05-28 23:04:03 -05:00
|
|
|
public static IStructureElementNoPlacement<Object> ofBlocksMapHint(
|
|
|
|
|
Map<Block, ? extends Collection<Integer>> blocksMap, Block hintBlock, int hintMeta) {
|
2026-05-28 01:08:27 -05:00
|
|
|
if (blocksMap == null || blocksMap.isEmpty() || hintBlock == null) {
|
2022-07-08 17:14:18 +08:00
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
2026-05-28 01:08:27 -05:00
|
|
|
for (Collection<Integer> value : blocksMap.values()) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (value.isEmpty()) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElementNoPlacement<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean check(Object t, World world, int x, int y, int z) {
|
2022-07-08 17:14:18 +08:00
|
|
|
Block worldBlock = world.getBlock(x, y, z);
|
2026-05-28 01:08:27 -05:00
|
|
|
return defaultIfNull(blocksMap.get(worldBlock), Collections.emptySet())
|
2022-08-08 17:40:12 +08:00
|
|
|
.contains(worldBlock.getDamageValue(world, x, y, z));
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2024-12-14 13:13:34 -05:00
|
|
|
return check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean spawnHint(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-07-08 17:14:18 +08:00
|
|
|
StructureLibAPI.hintParticle(world, x, y, z, hintBlock, hintMeta);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* Accept one (block, meta). Spawn hint particles using an alternative block and meta. Not very useful...
|
|
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static IStructureElementNoPlacement<Object> ofBlockHint(Block block, int meta, Block hintBlock,
|
2022-08-08 17:40:12 +08:00
|
|
|
int hintMeta) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (block == null || hintBlock == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElementNoPlacement<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean check(Object t, World world, int x, int y, int z) {
|
2022-07-08 17:14:18 +08:00
|
|
|
Block worldBlock = world.getBlock(x, y, z);
|
|
|
|
|
return block == worldBlock && meta == worldBlock.getDamageValue(world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2024-12-14 13:13:34 -05:00
|
|
|
return check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean spawnHint(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-07-08 17:14:18 +08:00
|
|
|
StructureLibAPI.hintParticle(world, x, y, z, hintBlock, hintMeta);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* Accept one (block, meta). Same as {@link #ofBlock(Block, int)}, except it explicitly turns off creative/survival
|
|
|
|
|
* build.
|
|
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static IStructureElementNoPlacement<Object> ofBlockHint(Block block, int meta) {
|
2022-07-08 17:14:18 +08:00
|
|
|
return ofBlockHint(block, meta, block, meta);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* Add a block using block adder. Spawn hints using given hint block and meta.
|
|
|
|
|
* <p>
|
|
|
|
|
* Useful when your logic is very complex. Does not support autoplace.
|
|
|
|
|
*/
|
2026-05-28 23:04:03 -05:00
|
|
|
public static <T> IStructureElementNoPlacement<T> ofBlockAdderHint(IBlockAdder<? super T> iBlockAdder,
|
|
|
|
|
Block hintBlock, int hintMeta) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (iBlockAdder == null || hintBlock == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElementNoPlacement<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@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));
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
|
|
|
|
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
// calling iBlockAdder can potentially modify external state
|
|
|
|
|
// therefore we assume this can always be valid.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@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;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-08-30 23:27:18 +08:00
|
|
|
* Accept a set of blocks. Less cumbersome to use than {@link #ofBlocksMap(Map, Block, int)} when for any accepted
|
|
|
|
|
* block type we accept only one meta for each.
|
|
|
|
|
*
|
2026-05-28 23:04:03 -05:00
|
|
|
* @param blocksMap Accepted (block, meta) pairs.
|
2022-08-30 23:27:18 +08:00
|
|
|
* @param defaultBlock default block to place/spawn hint
|
|
|
|
|
* @param defaultMeta default meta to place/spawn hint
|
|
|
|
|
* @see #ofBlocksMapHint(Map, Block, int)
|
2022-07-08 17:14:18 +08:00
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static IStructureElement<Object> ofBlocksFlat(Map<Block, Integer> blocksMap, Block defaultBlock,
|
2022-08-08 17:40:12 +08:00
|
|
|
int defaultMeta) {
|
2026-05-28 01:08:27 -05:00
|
|
|
if (blocksMap == null || blocksMap.isEmpty() || defaultBlock == null) {
|
2022-07-08 17:14:18 +08:00
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
|
|
|
|
if (defaultBlock instanceof ICustomBlockSetting) {
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElement<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-09-13 21:22:34 +08:00
|
|
|
private BlocksToPlace blocksToPlace;
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean check(Object t, World world, int x, int y, int z) {
|
2022-07-08 17:14:18 +08:00
|
|
|
Block worldBlock = world.getBlock(x, y, z);
|
2026-05-28 01:08:27 -05:00
|
|
|
return blocksMap.getOrDefault(worldBlock, MIN_VALUE) == worldBlock.getDamageValue(world, x, y, z);
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2024-12-14 13:13:34 -05:00
|
|
|
return check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean placeBlock(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-07-08 17:14:18 +08:00
|
|
|
((ICustomBlockSetting) defaultBlock).setBlock(world, x, y, z, defaultMeta);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean spawnHint(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-07-08 17:14:18 +08:00
|
|
|
StructureLibAPI.hintParticle(world, x, y, z, defaultBlock, defaultMeta);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
|
2022-09-13 21:22:34 +08:00
|
|
|
@Nullable
|
2022-08-08 17:40:12 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public BlocksToPlace getBlocksToPlace(Object t, World world, int x, int y, int z, ItemStack trigger,
|
2022-09-11 02:54:11 +08:00
|
|
|
AutoPlaceEnvironment env) {
|
2022-09-13 21:22:34 +08:00
|
|
|
if (blocksToPlace == null) {
|
|
|
|
|
ImmutableList.Builder<ItemStack> blocks = ImmutableList.builder();
|
|
|
|
|
Predicate<ItemStack> predicate = s -> true;
|
2026-05-28 01:08:27 -05:00
|
|
|
for (Entry<Block, Integer> e : blocksMap.entrySet()) {
|
2022-09-13 21:22:34 +08:00
|
|
|
Item i = Item.getItemFromBlock(e.getKey());
|
|
|
|
|
int meta = e.getValue();
|
|
|
|
|
if (i instanceof ISpecialItemBlock)
|
|
|
|
|
meta = ((ISpecialItemBlock) i).getItemMetaFromBlockMeta(e.getKey(), meta);
|
|
|
|
|
ItemStack stack = new ItemStack(i, 1, meta);
|
|
|
|
|
blocks.add(stack);
|
|
|
|
|
predicate = predicate.and(ItemStackPredicate.from(stack));
|
|
|
|
|
}
|
|
|
|
|
blocksToPlace = new BlocksToPlace(predicate, blocks.build());
|
|
|
|
|
}
|
|
|
|
|
return blocksToPlace;
|
2022-08-08 17:40:12 +08:00
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
|
|
|
|
} else {
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElement<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-09-13 21:22:34 +08:00
|
|
|
private BlocksToPlace blocksToPlace;
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean check(Object t, World world, int x, int y, int z) {
|
2022-07-08 17:14:18 +08:00
|
|
|
Block worldBlock = world.getBlock(x, y, z);
|
2026-05-28 01:08:27 -05:00
|
|
|
return blocksMap.getOrDefault(worldBlock, MIN_VALUE) == worldBlock.getDamageValue(world, x, y, z);
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2024-12-14 13:13:34 -05:00
|
|
|
return check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean placeBlock(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-07-08 17:14:18 +08:00
|
|
|
world.setBlock(x, y, z, defaultBlock, defaultMeta, 2);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean spawnHint(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-07-08 17:14:18 +08:00
|
|
|
StructureLibAPI.hintParticle(world, x, y, z, defaultBlock, defaultMeta);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
|
|
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public BlocksToPlace getBlocksToPlace(Object t, World world, int x, int y, int z, ItemStack trigger,
|
2022-09-11 02:54:11 +08:00
|
|
|
AutoPlaceEnvironment env) {
|
2022-09-13 21:22:34 +08:00
|
|
|
if (blocksToPlace == null) {
|
|
|
|
|
ImmutableList.Builder<ItemStack> blocks = ImmutableList.builder();
|
|
|
|
|
Predicate<ItemStack> predicate = s -> true;
|
2026-05-28 01:08:27 -05:00
|
|
|
for (Entry<Block, Integer> e : blocksMap.entrySet()) {
|
2022-09-13 21:22:34 +08:00
|
|
|
Item i = Item.getItemFromBlock(e.getKey());
|
|
|
|
|
int meta = e.getValue();
|
|
|
|
|
if (i instanceof ISpecialItemBlock)
|
|
|
|
|
meta = ((ISpecialItemBlock) i).getItemMetaFromBlockMeta(e.getKey(), meta);
|
|
|
|
|
ItemStack stack = new ItemStack(i, 1, meta);
|
|
|
|
|
blocks.add(stack);
|
|
|
|
|
predicate = predicate.and(ItemStackPredicate.from(stack));
|
|
|
|
|
}
|
|
|
|
|
blocksToPlace = new BlocksToPlace(predicate, blocks.build());
|
|
|
|
|
}
|
|
|
|
|
return blocksToPlace;
|
2022-08-08 17:40:12 +08:00
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-08-30 23:27:18 +08:00
|
|
|
* Accept a set of blocks.
|
|
|
|
|
*
|
|
|
|
|
* @param blocsMap Accepted (block, meta) pairs.
|
|
|
|
|
* @param defaultBlock default block to place/spawn hint
|
|
|
|
|
* @param defaultMeta default meta to place/spawn hint
|
|
|
|
|
* @see #ofBlocksMapHint(Map, Block, int)
|
2022-07-08 17:14:18 +08:00
|
|
|
*/
|
2026-05-28 23:04:03 -05:00
|
|
|
public static IStructureElement<Object> ofBlocksMap(Map<Block, ? extends Collection<Integer>> blocsMap,
|
|
|
|
|
Block defaultBlock, int defaultMeta) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (blocsMap == null || blocsMap.isEmpty() || defaultBlock == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
|
|
|
|
for (Collection<Integer> value : blocsMap.values()) {
|
|
|
|
|
if (value.isEmpty()) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (defaultBlock instanceof ICustomBlockSetting) {
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElement<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-09-13 21:22:34 +08:00
|
|
|
private BlocksToPlace blocksToPlace;
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean check(Object t, World world, int x, int y, int z) {
|
2022-07-08 17:14:18 +08:00
|
|
|
Block worldBlock = world.getBlock(x, y, z);
|
2026-05-28 01:08:27 -05:00
|
|
|
return defaultIfNull(blocsMap.get(worldBlock), Collections.emptySet())
|
2022-08-08 17:40:12 +08:00
|
|
|
.contains(worldBlock.getDamageValue(world, x, y, z));
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2024-12-14 13:13:34 -05:00
|
|
|
return check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean placeBlock(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-07-08 17:14:18 +08:00
|
|
|
((ICustomBlockSetting) defaultBlock).setBlock(world, x, y, z, defaultMeta);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean spawnHint(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-07-08 17:14:18 +08:00
|
|
|
StructureLibAPI.hintParticle(world, x, y, z, defaultBlock, defaultMeta);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
|
|
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public BlocksToPlace getBlocksToPlace(Object t, World world, int x, int y, int z, ItemStack trigger,
|
2022-09-11 02:54:11 +08:00
|
|
|
AutoPlaceEnvironment env) {
|
2022-09-13 21:22:34 +08:00
|
|
|
if (blocksToPlace == null) {
|
|
|
|
|
ImmutableList.Builder<ItemStack> blocks = ImmutableList.builder();
|
|
|
|
|
Predicate<ItemStack> predicate = s -> true;
|
2026-05-28 01:08:27 -05:00
|
|
|
for (Entry<Block, ? extends Collection<Integer>> e : blocsMap.entrySet()) {
|
2022-09-13 21:22:34 +08:00
|
|
|
Item i = Item.getItemFromBlock(e.getKey());
|
|
|
|
|
for (int meta : e.getValue()) {
|
|
|
|
|
if (i instanceof ISpecialItemBlock)
|
|
|
|
|
meta = ((ISpecialItemBlock) i).getItemMetaFromBlockMeta(e.getKey(), meta);
|
|
|
|
|
ItemStack stack = new ItemStack(i, 1, meta);
|
|
|
|
|
blocks.add(stack);
|
|
|
|
|
predicate = predicate.and(ItemStackPredicate.from(stack));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
blocksToPlace = new BlocksToPlace(predicate, blocks.build());
|
|
|
|
|
}
|
|
|
|
|
return blocksToPlace;
|
2022-08-08 17:40:12 +08:00
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
|
|
|
|
} else {
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElement<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-09-13 21:22:34 +08:00
|
|
|
private BlocksToPlace blocksToPlace;
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean check(Object t, World world, int x, int y, int z) {
|
2022-07-08 17:14:18 +08:00
|
|
|
Block worldBlock = world.getBlock(x, y, z);
|
2026-05-28 01:08:27 -05:00
|
|
|
return defaultIfNull(blocsMap.get(worldBlock), Collections.emptySet())
|
2022-08-08 17:40:12 +08:00
|
|
|
.contains(worldBlock.getDamageValue(world, x, y, z));
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2024-12-14 13:13:34 -05:00
|
|
|
return check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean placeBlock(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-07-08 17:14:18 +08:00
|
|
|
world.setBlock(x, y, z, defaultBlock, defaultMeta, 2);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean spawnHint(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-07-08 17:14:18 +08:00
|
|
|
StructureLibAPI.hintParticle(world, x, y, z, defaultBlock, defaultMeta);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
|
|
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public BlocksToPlace getBlocksToPlace(Object t, World world, int x, int y, int z, ItemStack trigger,
|
2022-09-11 02:54:11 +08:00
|
|
|
AutoPlaceEnvironment env) {
|
2022-09-13 21:22:34 +08:00
|
|
|
if (blocksToPlace == null) {
|
|
|
|
|
ImmutableList.Builder<ItemStack> blocks = ImmutableList.builder();
|
|
|
|
|
Predicate<ItemStack> predicate = s -> true;
|
2026-05-28 01:08:27 -05:00
|
|
|
for (Entry<Block, ? extends Collection<Integer>> e : blocsMap.entrySet()) {
|
2022-09-13 21:22:34 +08:00
|
|
|
Item i = Item.getItemFromBlock(e.getKey());
|
|
|
|
|
for (int meta : e.getValue()) {
|
|
|
|
|
if (i instanceof ISpecialItemBlock)
|
|
|
|
|
meta = ((ISpecialItemBlock) i).getItemMetaFromBlockMeta(e.getKey(), meta);
|
|
|
|
|
ItemStack stack = new ItemStack(i, 1, meta);
|
|
|
|
|
blocks.add(stack);
|
|
|
|
|
predicate = predicate.and(ItemStackPredicate.from(stack));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
blocksToPlace = new BlocksToPlace(predicate, blocks.build());
|
|
|
|
|
}
|
|
|
|
|
return blocksToPlace;
|
2022-08-08 17:40:12 +08:00
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* Accept a block. Spawn hint/autoplace using another.
|
|
|
|
|
*
|
|
|
|
|
* @param block accepted block
|
|
|
|
|
* @param meta accepted meta
|
|
|
|
|
* @param defaultBlock hint block
|
|
|
|
|
* @param defaultMeta hint meta
|
|
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static IStructureElement<Object> ofBlock(Block block, int meta, Block defaultBlock, int defaultMeta) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (block == null || defaultBlock == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
2025-03-11 22:41:57 +08:00
|
|
|
if (block == Blocks.air) {
|
|
|
|
|
if (PANIC_MODE) {
|
|
|
|
|
throw new IllegalArgumentException("ofBlock() does not accept air. use isAir() instead");
|
|
|
|
|
} else {
|
|
|
|
|
LOGGER.warn("ofBlock() does not accept air. use isAir() instead");
|
|
|
|
|
return isAir();
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
if (block instanceof ICustomBlockSetting) {
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElement<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean check(Object t, World world, int x, int y, int z) {
|
2022-07-08 17:14:18 +08:00
|
|
|
Block worldBlock = world.getBlock(x, y, z);
|
|
|
|
|
return block == worldBlock && meta == worldBlock.getDamageValue(world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2024-12-14 13:13:34 -05:00
|
|
|
return check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean placeBlock(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-07-08 17:14:18 +08:00
|
|
|
((ICustomBlockSetting) defaultBlock).setBlock(world, x, y, z, defaultMeta);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean spawnHint(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-07-08 17:14:18 +08:00
|
|
|
StructureLibAPI.hintParticle(world, x, y, z, defaultBlock, defaultMeta);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
|
|
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public BlocksToPlace getBlocksToPlace(Object t, World world, int x, int y, int z, ItemStack trigger,
|
2022-09-11 02:54:11 +08:00
|
|
|
AutoPlaceEnvironment env) {
|
2022-09-13 21:22:34 +08:00
|
|
|
return BlocksToPlace.create(block, meta);
|
2022-08-08 17:40:12 +08:00
|
|
|
}
|
2026-05-23 09:42:20 +02:00
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public List<String> getDescription(Object context) {
|
2026-05-23 09:42:20 +02:00
|
|
|
Item item = Item.getItemFromBlock(block);
|
|
|
|
|
if (item == null) return null;
|
|
|
|
|
return Collections.singletonList(new ItemStack(item, 1, meta).getUnlocalizedName() + ".name");
|
|
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
|
|
|
|
} else {
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElement<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2026-03-26 18:23:20 +04:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public PlaceResult survivalPlaceBlock(Object t, World world, int x, int y, int z, ItemStack trigger,
|
2026-03-26 18:23:20 +04:00
|
|
|
AutoPlaceEnvironment env) {
|
|
|
|
|
BlocksToPlace e = getBlocksToPlace(t, world, x, y, z, trigger, env);
|
|
|
|
|
IItemSource source = env.getSource();
|
|
|
|
|
EntityPlayer actor = env.getActor();
|
|
|
|
|
Consumer<IChatComponent> chatter = env.getChatter();
|
|
|
|
|
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
|
|
|
|
|
if (e.getStacks() == null) {
|
|
|
|
|
ItemStack taken = source.takeOne(e.getPredicate(), true);
|
|
|
|
|
return StructureUtility.survivalPlaceBlock(
|
|
|
|
|
taken,
|
|
|
|
|
NBTMode.EXACT,
|
|
|
|
|
taken.stackTagCompound,
|
|
|
|
|
false,
|
|
|
|
|
world,
|
|
|
|
|
x,
|
|
|
|
|
y,
|
|
|
|
|
z,
|
|
|
|
|
source,
|
|
|
|
|
actor,
|
|
|
|
|
chatter);
|
|
|
|
|
}
|
|
|
|
|
for (ItemStack stack : e.getStacks()) {
|
|
|
|
|
if (!source.takeOne(stack, true)) {
|
|
|
|
|
IChatComponent name = new ChatComponentText(stack.getDisplayName());
|
|
|
|
|
name.setChatStyle(new ChatStyle().setColor(EnumChatFormatting.YELLOW));
|
|
|
|
|
env.getChatter()
|
|
|
|
|
.accept(new ChatComponentTranslation("structurelib.autoplace.missing_block", name));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
return StructureUtility.survivalPlaceBlock(
|
|
|
|
|
stack,
|
|
|
|
|
NBTMode.EXACT,
|
|
|
|
|
stack.stackTagCompound,
|
|
|
|
|
false,
|
|
|
|
|
world,
|
|
|
|
|
x,
|
|
|
|
|
y,
|
|
|
|
|
z,
|
|
|
|
|
source,
|
|
|
|
|
actor,
|
|
|
|
|
chatter);
|
|
|
|
|
}
|
|
|
|
|
return PlaceResult.REJECT;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean check(Object t, World world, int x, int y, int z) {
|
2022-07-08 17:14:18 +08:00
|
|
|
Block worldBlock = world.getBlock(x, y, z);
|
|
|
|
|
return block == worldBlock && meta == worldBlock.getDamageValue(world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2024-12-14 13:13:34 -05:00
|
|
|
return check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean placeBlock(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-07-08 17:14:18 +08:00
|
|
|
world.setBlock(x, y, z, defaultBlock, defaultMeta, 2);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean spawnHint(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-07-08 17:14:18 +08:00
|
|
|
StructureLibAPI.hintParticle(world, x, y, z, defaultBlock, defaultMeta);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
|
|
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public BlocksToPlace getBlocksToPlace(Object t, World world, int x, int y, int z, ItemStack trigger,
|
2022-09-11 02:54:11 +08:00
|
|
|
AutoPlaceEnvironment env) {
|
2022-09-13 21:22:34 +08:00
|
|
|
return BlocksToPlace.create(block, meta);
|
2022-08-08 17:40:12 +08:00
|
|
|
}
|
2026-05-23 09:42:20 +02:00
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public List<String> getDescription(Object context) {
|
2026-05-23 09:42:20 +02:00
|
|
|
Item item = Item.getItemFromBlock(block);
|
|
|
|
|
if (item == null) return null;
|
|
|
|
|
return Collections.singletonList(new ItemStack(item, 1, meta).getUnlocalizedName() + ".name");
|
|
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-08-30 23:27:18 +08:00
|
|
|
* Same as {@link #ofBlock(Block, int, Block, int)} but ignores target meta id
|
2022-07-08 17:14:18 +08:00
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static IStructureElement<Object> ofBlockAnyMeta(Block block, Block defaultBlock, int defaultMeta) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (block == null || defaultBlock == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
|
|
|
|
if (block instanceof ICustomBlockSetting) {
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElement<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean check(Object t, World world, int x, int y, int z) {
|
2022-07-08 17:14:18 +08:00
|
|
|
return block == world.getBlock(x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2024-12-14 13:13:34 -05:00
|
|
|
return check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean placeBlock(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-07-08 17:14:18 +08:00
|
|
|
((ICustomBlockSetting) defaultBlock).setBlock(world, x, y, z, defaultMeta);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean spawnHint(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-07-08 17:14:18 +08:00
|
|
|
StructureLibAPI.hintParticle(world, x, y, z, defaultBlock, defaultMeta);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
|
|
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public BlocksToPlace getBlocksToPlace(Object t, World world, int x, int y, int z, ItemStack trigger,
|
2022-09-11 02:54:11 +08:00
|
|
|
AutoPlaceEnvironment env) {
|
2022-09-13 21:22:34 +08:00
|
|
|
// there is getSubItems on ItemBlock, but it's client only
|
|
|
|
|
return BlocksToPlace.create(defaultBlock, defaultMeta);
|
2022-08-08 17:40:12 +08:00
|
|
|
}
|
2026-05-23 09:42:20 +02:00
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public List<String> getDescription(Object context) {
|
2026-05-23 09:42:20 +02:00
|
|
|
Item item = Item.getItemFromBlock(block);
|
|
|
|
|
if (item == null) return null;
|
|
|
|
|
return Collections.singletonList(new ItemStack(item, 1, 0).getUnlocalizedName() + ".name");
|
|
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
|
|
|
|
} else {
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElement<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean check(Object t, World world, int x, int y, int z) {
|
2022-07-08 17:14:18 +08:00
|
|
|
return block == world.getBlock(x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2024-12-14 13:13:34 -05:00
|
|
|
return check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean placeBlock(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-07-08 17:14:18 +08:00
|
|
|
world.setBlock(x, y, z, defaultBlock, defaultMeta, 2);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public boolean spawnHint(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-07-08 17:14:18 +08:00
|
|
|
StructureLibAPI.hintParticle(world, x, y, z, defaultBlock, defaultMeta);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
|
|
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public BlocksToPlace getBlocksToPlace(Object t, World world, int x, int y, int z, ItemStack trigger,
|
2022-09-11 02:54:11 +08:00
|
|
|
AutoPlaceEnvironment env) {
|
2022-09-13 21:22:34 +08:00
|
|
|
// there is getSubItems on ItemBlock, but it's client only
|
|
|
|
|
return BlocksToPlace.create(defaultBlock, defaultMeta);
|
2022-08-08 17:40:12 +08:00
|
|
|
}
|
2026-05-23 09:42:20 +02:00
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
|
@Override
|
2026-05-28 01:08:27 -05:00
|
|
|
public List<String> getDescription(Object context) {
|
2026-05-23 09:42:20 +02:00
|
|
|
Item item = Item.getItemFromBlock(block);
|
|
|
|
|
if (item == null) return null;
|
|
|
|
|
return Collections.singletonList(new ItemStack(item, 1, 0).getUnlocalizedName() + ".name");
|
|
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* Accept a single block with a fixed meta. Most primitive form of structure.
|
|
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static IStructureElement<Object> ofBlock(Block block, int meta) {
|
2022-07-08 17:14:18 +08:00
|
|
|
return ofBlock(block, meta, block, meta);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-08-30 23:27:18 +08:00
|
|
|
* Accept a single block, but accept any meta.
|
2022-07-08 17:14:18 +08:00
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static IStructureElement<Object> ofBlockAnyMeta(Block block) {
|
2022-07-08 17:14:18 +08:00
|
|
|
return ofBlockAnyMeta(block, block, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-08-30 23:27:18 +08:00
|
|
|
* Accept a single block, but accept any meta. Spawn hint/autoplace using given meta.
|
2022-07-08 17:14:18 +08:00
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static IStructureElement<Object> ofBlockAnyMeta(Block block, int defaultMeta) {
|
2022-07-08 17:14:18 +08:00
|
|
|
return ofBlockAnyMeta(block, block, defaultMeta);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
// endregion
|
2022-07-08 17:14:18 +08:00
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
// region adders
|
2022-07-08 17:14:18 +08:00
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* Add a block using block adder. Spawn hints/autoplace using given hint block and meta.
|
|
|
|
|
* <p>
|
|
|
|
|
* Useful when your logic is very complex.
|
|
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T> IStructureElement<T> ofBlockAdder(IBlockAdder<? super T> iBlockAdder, Block defaultBlock,
|
2022-08-08 17:40:12 +08:00
|
|
|
int defaultMeta) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (iBlockAdder == null || defaultBlock == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
|
|
|
|
if (defaultBlock instanceof ICustomBlockSetting) {
|
2026-05-28 01:08:27 -05:00
|
|
|
return new StructureElement_Bridge<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@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));
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
|
|
|
|
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
// calling iBlockAdder can potentially modify external state
|
|
|
|
|
// therefore we assume this can always be valid.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@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;
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
|
|
|
|
|
@Override
|
2022-09-11 02:54:11 +08:00
|
|
|
@Deprecated
|
|
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
2022-08-30 23:27:18 +08:00
|
|
|
if (world.getBlock(x, y, z) == defaultBlock && world.getBlockMetadata(x, y, z) == defaultMeta)
|
|
|
|
|
return PlaceResult.SKIP;
|
2022-08-08 17:40:12 +08:00
|
|
|
return StructureUtility.survivalPlaceBlock(
|
2022-09-11 02:54:11 +08:00
|
|
|
defaultBlock,
|
|
|
|
|
defaultMeta,
|
|
|
|
|
world,
|
|
|
|
|
x,
|
|
|
|
|
y,
|
|
|
|
|
z,
|
|
|
|
|
env.getSource(),
|
|
|
|
|
env.getActor(),
|
|
|
|
|
env.getChatter());
|
2022-08-08 17:40:12 +08:00
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
|
|
|
|
} else {
|
2026-05-28 01:08:27 -05:00
|
|
|
return new StructureElement_Bridge<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@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));
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
|
|
|
|
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
// calling iBlockAdder can potentially modify external state
|
|
|
|
|
// therefore we assume this can always be valid.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@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;
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
|
|
|
|
|
@Override
|
2022-09-11 02:54:11 +08:00
|
|
|
@Deprecated
|
|
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
2022-08-30 23:27:18 +08:00
|
|
|
if (world.getBlock(x, y, z) == defaultBlock && world.getBlockMetadata(x, y, z) == defaultMeta)
|
|
|
|
|
return PlaceResult.SKIP;
|
2022-08-08 17:40:12 +08:00
|
|
|
return StructureUtility.survivalPlaceBlock(
|
2022-09-11 02:54:11 +08:00
|
|
|
defaultBlock,
|
|
|
|
|
defaultMeta,
|
|
|
|
|
world,
|
|
|
|
|
x,
|
|
|
|
|
y,
|
|
|
|
|
z,
|
|
|
|
|
env.getSource(),
|
|
|
|
|
env.getActor(),
|
|
|
|
|
env.getChatter());
|
2022-08-08 17:40:12 +08:00
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T> IStructureElement<T> ofBlockAdder(IBlockAdder<? super T> iBlockAdder, int dots) {
|
2022-07-08 17:14:18 +08:00
|
|
|
return ofBlockAdder(iBlockAdder, StructureLibAPI.getBlockHint(), dots - 1);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* Try to add a structure element with a tile entity. Note that tile adder will be called with a null argument at
|
|
|
|
|
* locations without tile entity.
|
|
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T> IStructureElementNoPlacement<T> ofTileAdder(ITileAdder<? super T> iTileAdder, Block hintBlock,
|
2022-08-08 17:40:12 +08:00
|
|
|
int hintMeta) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (iTileAdder == null || hintBlock == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElementNoPlacement<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@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);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
|
|
|
|
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
// calling iTileAdder can potentially modify external state
|
|
|
|
|
// therefore we assume this can always be valid.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@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;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* Try to add a structure element with a particular type of tile entity. Note that tile adder will not be called at
|
|
|
|
|
* locations without a tile entity.
|
|
|
|
|
*/
|
2026-05-28 23:04:03 -05:00
|
|
|
public static <T, E> IStructureElementNoPlacement<T> ofSpecificTileAdder(
|
|
|
|
|
BiPredicate<? super T, ? super E> iTileAdder, Class<? extends E> tileClass, Block hintBlock, int hintMeta) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (iTileAdder == null || hintBlock == null || tileClass == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElementNoPlacement<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
|
|
|
|
public boolean check(T t, World world, int x, int y, int z) {
|
|
|
|
|
TileEntity tileEntity = world.getTileEntity(x, y, z);
|
2026-05-28 01:08:27 -05:00
|
|
|
// This used to check if it's a GT tile. Since this is now a standalone mod we no longer do this
|
2022-07-08 17:14:18 +08:00
|
|
|
return tileClass.isInstance(tileEntity) && iTileAdder.test(t, tileClass.cast(tileEntity));
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
|
|
|
|
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
// calling iTileAdder can potentially modify external state
|
|
|
|
|
// therefore we assume this can always be valid.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@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;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
// 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.
|
2022-07-08 17:14:18 +08:00
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
// endregion
|
2022-07-08 17:14:18 +08:00
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
// region side effects
|
2022-07-08 17:14:18 +08:00
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* Call a callback if downstream element returned true in check.
|
|
|
|
|
*
|
|
|
|
|
* @param onCheckPass side effect
|
|
|
|
|
* @param element downstream
|
|
|
|
|
*/
|
2026-05-28 23:04:03 -05:00
|
|
|
public static <T> IStructureElement<T> onElementPass(Consumer<? super T> onCheckPass,
|
|
|
|
|
IStructureElement<? super T> element) {
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElement<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@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;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
|
|
|
|
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
return element.couldBeValid(t, world, x, y, z, trigger);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@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);
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
|
2022-09-13 21:22:34 +08:00
|
|
|
@Nullable
|
|
|
|
|
@Override
|
|
|
|
|
public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
|
|
|
|
return element.getBlocksToPlace(t, world, x, y, z, trigger, env);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
@Override
|
2022-09-11 02:54:11 +08:00
|
|
|
@Deprecated
|
2022-08-08 17:40:12 +08:00
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
|
|
|
|
return element.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
|
|
|
|
|
}
|
2022-09-11 02:54:11 +08:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
|
|
|
|
return element.survivalPlaceBlock(t, world, x, y, z, trigger, env);
|
|
|
|
|
}
|
2026-05-23 09:42:20 +02:00
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
|
@Override
|
2026-05-24 07:35:25 +02:00
|
|
|
public List<String> getDescription(T context) {
|
|
|
|
|
return element.getDescription(context);
|
2026-05-23 09:42:20 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Wraps an element and overrides its description. Useful for providing human-readable names to elements whose
|
|
|
|
|
* underlying implementation doesn't provide a description (e.g. tiered block elements).
|
|
|
|
|
*
|
|
|
|
|
* @param description the description to use, or null to clear any existing description
|
|
|
|
|
* @param element the element to wrap
|
|
|
|
|
*/
|
2026-05-28 23:04:03 -05:00
|
|
|
public static <T> IStructureElement<T> withDescription(@Nullable List<String> description,
|
|
|
|
|
IStructureElement<? super T> element) {
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElement<>() {
|
2026-05-23 09:42:20 +02:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean check(T t, World world, int x, int y, int z) {
|
|
|
|
|
return element.check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
return element.couldBeValid(t, world, x, y, z, trigger);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
|
@Override
|
|
|
|
|
public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
|
|
|
|
return element.getBlocksToPlace(t, world, x, y, z, trigger, env);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
@Deprecated
|
|
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
|
|
|
|
return element.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
|
|
|
|
return element.survivalPlaceBlock(t, world, x, y, z, trigger, env);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
|
@Override
|
2026-05-24 07:35:25 +02:00
|
|
|
public List<String> getDescription(T context) {
|
2026-05-23 09:42:20 +02:00
|
|
|
return description;
|
|
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* Call a callback if downstream element returned false in check.
|
|
|
|
|
*
|
|
|
|
|
* @param onFail side effect
|
|
|
|
|
* @param element downstream
|
|
|
|
|
*/
|
2026-05-28 23:04:03 -05:00
|
|
|
public static <T> IStructureElement<T> onElementFail(Consumer<? super T> onFail,
|
|
|
|
|
IStructureElement<? super T> element) {
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElement<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@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;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
|
|
|
|
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
return element.couldBeValid(t, world, x, y, z, trigger);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@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);
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
|
2022-09-13 21:22:34 +08:00
|
|
|
@Nullable
|
|
|
|
|
@Override
|
|
|
|
|
public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
|
|
|
|
return element.getBlocksToPlace(t, world, x, y, z, trigger, env);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
@Override
|
2022-09-11 02:54:11 +08:00
|
|
|
@Deprecated
|
2022-08-08 17:40:12 +08:00
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
|
|
|
|
return element.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
|
|
|
|
|
}
|
2022-09-11 02:54:11 +08:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
|
|
|
|
return element.survivalPlaceBlock(t, world, x, y, z, trigger, env);
|
|
|
|
|
}
|
2026-05-23 09:42:20 +02:00
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
|
@Override
|
2026-05-24 07:35:25 +02:00
|
|
|
public List<String> getDescription(T context) {
|
|
|
|
|
return element.getDescription(context);
|
2026-05-23 09:42:20 +02:00
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
// endregion
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Enable this structure element only if given predicate returns true.
|
2022-08-30 23:27:18 +08:00
|
|
|
* <p>
|
2026-06-06 18:10:11 +02:00
|
|
|
* Return REJECT_CONTINUE when survival auto place if given predicate returns false.
|
2022-08-08 17:40:12 +08:00
|
|
|
*/
|
|
|
|
|
public static <T> IStructureElement<T> onlyIf(Predicate<? super T> predicate,
|
|
|
|
|
IStructureElement<? super T> downstream) {
|
2026-06-06 18:10:11 +02:00
|
|
|
return onlyIf(predicate, downstream, PlaceResult.REJECT_CONTINUE);
|
2022-08-08 17:40:12 +08:00
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Enable this structure element only if given predicate returns true.
|
2022-08-30 23:27:18 +08:00
|
|
|
*
|
|
|
|
|
* @param placeResultWhenDisabled value to return for survival auto place when predicate returns false
|
2022-07-08 17:14:18 +08:00
|
|
|
*/
|
2026-05-28 23:04:03 -05:00
|
|
|
public static <T> IStructureElement<T> onlyIf(Predicate<? super T> predicate,
|
|
|
|
|
IStructureElement<? super T> downstream, PlaceResult placeResultWhenDisabled) {
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElement<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
|
|
|
|
public boolean check(T t, World world, int x, int y, int z) {
|
|
|
|
|
return predicate.test(t) && downstream.check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
|
|
|
|
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
// this COULD potentially impact external state
|
|
|
|
|
// but predicate really should not have side effects
|
|
|
|
|
return predicate.test(t) && downstream.couldBeValid(t, world, x, y, z, trigger);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
|
|
|
|
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
return predicate.test(t) && downstream.spawnHint(t, world, x, y, z, trigger);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
return predicate.test(t) && downstream.placeBlock(t, world, x, y, z, trigger);
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
|
2022-09-13 21:22:34 +08:00
|
|
|
@Nullable
|
|
|
|
|
@Override
|
|
|
|
|
public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
|
|
|
|
if (predicate.test(t)) return downstream.getBlocksToPlace(t, world, x, y, z, trigger, env);
|
|
|
|
|
return BlocksToPlace.createEmpty();
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
@Override
|
2022-09-11 02:54:11 +08:00
|
|
|
@Deprecated
|
2022-08-08 17:40:12 +08:00
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
|
|
|
|
if (predicate.test(t))
|
|
|
|
|
return downstream.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
|
|
|
|
|
return placeResultWhenDisabled;
|
|
|
|
|
}
|
2022-09-11 02:54:11 +08:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
|
|
|
|
if (predicate.test(t)) return downstream.survivalPlaceBlock(t, world, x, y, z, trigger, env);
|
|
|
|
|
return placeResultWhenDisabled;
|
|
|
|
|
}
|
2026-05-23 09:42:20 +02:00
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
|
@Override
|
2026-05-24 07:35:25 +02:00
|
|
|
public List<String> getDescription(T context) {
|
|
|
|
|
return downstream.getDescription(context);
|
2026-05-23 09:42:20 +02:00
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-08-30 23:27:18 +08:00
|
|
|
* This allows you to compose different {@link IStructureElement} to form a <b>OR</b> chain. As with any other OR
|
|
|
|
|
* operator, this one exhibits short-circuiting behavior, i.e. it will not call next structure element if previous
|
|
|
|
|
* one succeeded. (*)
|
|
|
|
|
* <p>
|
|
|
|
|
* This allows you e.g. accept both a glass block using {@link #ofBlock(Block, int, Block, int)} and a piece of air
|
|
|
|
|
* using {@link #isAir()}. It will not attempt to capture any errors though, so next one will not be tried if
|
|
|
|
|
* previous one would crash.
|
|
|
|
|
* <p>
|
2026-06-06 18:10:11 +02:00
|
|
|
* (*): For survival auto place, it will: REJECT, if any structure element returns REJECT (and none returned ACCEPT,
|
|
|
|
|
* STOP, ACCEPT_STOP, or SKIP); REJECT_CONTINUE, if all structure elements return REJECT_CONTINUE (none returned
|
|
|
|
|
* REJECT); SKIP, as soon as any structure element returns SKIP; any other result, immediately upon any structure
|
|
|
|
|
* element returning those.
|
2022-08-30 23:27:18 +08:00
|
|
|
* <p>
|
2022-07-08 17:14:18 +08:00
|
|
|
* 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
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T> IStructureElementChain<T> ofChain(IStructureElement<? super T>... elementChain) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (elementChain == null || elementChain.length == 0) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
2026-05-28 01:08:27 -05:00
|
|
|
for (IStructureElement<? super T> iStructureElement : elementChain) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (iStructureElement == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return () -> elementChain;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-08-30 23:27:18 +08:00
|
|
|
* This allows you to compose different {@link IStructureElement} to form a <b>OR</b> chain.
|
|
|
|
|
* <p>
|
|
|
|
|
* Practically no difference with the other overload.
|
|
|
|
|
*
|
|
|
|
|
* @see #ofChain(IStructureElement[])
|
2022-07-08 17:14:18 +08:00
|
|
|
*/
|
|
|
|
|
@SuppressWarnings("unchecked")
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T> IStructureElementChain<T> ofChain(List<? extends IStructureElement<? super T>> elementChain) {
|
2022-07-08 17:14:18 +08:00
|
|
|
return ofChain(elementChain.toArray(new IStructureElement[0]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// region context
|
2022-08-30 23:27:18 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Switch to the extended context object in the downstream element
|
|
|
|
|
*
|
|
|
|
|
* @param elem downstream element
|
|
|
|
|
* @param <CTX> extended context type
|
|
|
|
|
* @param <T> existing context object type
|
|
|
|
|
*/
|
2026-05-28 23:02:02 -05:00
|
|
|
public static <CTX, T extends IWithExtendedContext<? extends CTX>> IStructureElement<T> withContext(
|
2026-05-28 01:08:27 -05:00
|
|
|
IStructureElement<? super CTX> elem) {
|
|
|
|
|
|
|
|
|
|
return new IStructureElement<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
|
|
|
|
public boolean check(T t, World world, int x, int y, int z) {
|
|
|
|
|
return elem.check(t.getCurrentContext(), world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
|
|
|
|
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
return elem.couldBeValid(t.getCurrentContext(), world, x, y, z, trigger);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
|
|
|
|
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
return elem.spawnHint(t.getCurrentContext(), world, x, y, z, trigger);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
return elem.placeBlock(t.getCurrentContext(), world, x, y, z, trigger);
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
|
2022-09-13 21:22:34 +08:00
|
|
|
@Nullable
|
|
|
|
|
@Override
|
|
|
|
|
public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
|
|
|
|
return elem.getBlocksToPlace(t.getCurrentContext(), world, x, y, z, trigger, env);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
@Override
|
2022-09-11 02:54:11 +08:00
|
|
|
@Deprecated
|
2022-08-08 17:40:12 +08:00
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
|
|
|
|
return elem.survivalPlaceBlock(t.getCurrentContext(), world, x, y, z, trigger, s, actor, chatter);
|
|
|
|
|
}
|
2022-09-11 02:54:11 +08:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
|
|
|
|
return elem.survivalPlaceBlock(t.getCurrentContext(), world, x, y, z, trigger, env);
|
|
|
|
|
}
|
2026-05-23 09:42:20 +02:00
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
|
@Override
|
2026-05-24 07:35:25 +02:00
|
|
|
public List<String> getDescription(T context) {
|
|
|
|
|
return elem.getDescription(context.getCurrentContext());
|
2026-05-23 09:42:20 +02:00
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
// endregion
|
2022-07-08 17:14:18 +08:00
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
// region defer
|
2022-07-08 17:14:18 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Similar to defer, but caches the first returned element returned and won't call it again. Initialization is not
|
2022-08-30 23:27:18 +08:00
|
|
|
* thread safe. These both allow the structure element **constructor** to access properties only present on the
|
|
|
|
|
* context object (e.g. GT5 multiblock controller), e.g. hatch texture index to use. Use `lazy` if the data you
|
|
|
|
|
* access will remain constant across different context object.
|
2022-07-08 17:14:18 +08:00
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T> IStructureElementDeferred<T> lazy(Supplier<? extends IStructureElement<? super T>> to) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (to == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
|
|
|
|
return new LazyStructureElement<>(t -> to.get());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-08-30 23:27:18 +08:00
|
|
|
* This will defer the actual instantiation of structure element until the <b>first time</b> structure code is
|
|
|
|
|
* actually called.
|
|
|
|
|
* <p>
|
2022-07-08 17:14:18 +08:00
|
|
|
* Similar to defer, but caches the first returned element returned and won't call it again. Initialization is not
|
2022-08-30 23:27:18 +08:00
|
|
|
* thread safe. This will allow the structure element <b>constructor</b> to access properties only present on the
|
|
|
|
|
* context object (e.g. GT5 multiblock controller), e.g. hatch texture index to use. Use this if the data you access
|
|
|
|
|
* will remain constant.
|
|
|
|
|
*
|
|
|
|
|
* @param to create structure element from the first context object passed in
|
2022-07-08 17:14:18 +08:00
|
|
|
*/
|
2026-05-28 23:04:03 -05:00
|
|
|
public static <T> IStructureElementDeferred<T> lazy(
|
|
|
|
|
Function<? super T, ? extends IStructureElement<? super T>> to) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (to == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
|
|
|
|
return new LazyStructureElement<>(to);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* This will defer the actual instantiation of structure element until the structure code is actually called. This
|
|
|
|
|
* will allow the structure element <b>constructor</b> to access properties only present on the context object (e.g.
|
|
|
|
|
* GT5 multiblock controller), e.g. hatch texture index to use. Use this if it might change for the same structure
|
|
|
|
|
* definition, e.g. if your structure might switch mode and change some parameter, while basically remain the same
|
|
|
|
|
* shape. Using this while {@link #lazy(Supplier)} is enough will not break anything, but would incur unnecessary
|
|
|
|
|
* performance overhead.
|
|
|
|
|
*
|
|
|
|
|
* @param to downstream element supplier
|
|
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T> IStructureElementDeferred<T> defer(Supplier<? extends IStructureElement<? super T>> to) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (to == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElementDeferred<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
|
|
|
|
public boolean check(T t, World world, int x, int y, int z) {
|
|
|
|
|
return to.get().check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
|
|
|
|
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
return to.get().couldBeValid(t, world, x, y, z, trigger);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@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);
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
|
2022-09-13 21:22:34 +08:00
|
|
|
@Nullable
|
|
|
|
|
@Override
|
|
|
|
|
public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
|
|
|
|
return to.get().getBlocksToPlace(t, world, x, y, z, trigger, env);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
@Override
|
2022-09-11 02:54:11 +08:00
|
|
|
@Deprecated
|
2022-08-08 17:40:12 +08:00
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
|
|
|
|
return to.get().survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
|
|
|
|
|
}
|
2022-09-11 02:54:11 +08:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
|
|
|
|
return to.get().survivalPlaceBlock(t, world, x, y, z, trigger, env);
|
|
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* This will defer the actual instantiation of structure element until the structure code is actually called. This
|
|
|
|
|
* will allow the structure element <b>constructor</b> to access properties only present on the context object (e.g.
|
|
|
|
|
* GT5 multiblock controller), e.g. hatch texture index to use. Use this if it might change for the same structure
|
|
|
|
|
* definition, e.g. if your structure might switch mode and change some parameter, while basically remain the same
|
|
|
|
|
* shape. Using this while {@link #lazy(Supplier)} is enough will not break anything, but would incur unnecessary
|
|
|
|
|
* performance overhead.
|
|
|
|
|
*
|
|
|
|
|
* @param to downstream element supplier
|
|
|
|
|
*/
|
2026-05-28 23:04:03 -05:00
|
|
|
public static <T> IStructureElementDeferred<T> defer(
|
|
|
|
|
Function<? super T, ? extends IStructureElement<? super T>> to) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (to == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElementDeferred<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
|
|
|
|
public boolean check(T t, World world, int x, int y, int z) {
|
|
|
|
|
return to.apply(t).check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
|
|
|
|
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
return to.apply(t).couldBeValid(t, world, x, y, z, trigger);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@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);
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
|
2022-09-13 21:22:34 +08:00
|
|
|
@Nullable
|
|
|
|
|
@Override
|
|
|
|
|
public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
|
|
|
|
return to.apply(t).getBlocksToPlace(t, world, x, y, z, trigger, env);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
@Override
|
|
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
|
|
|
|
return to.apply(t).survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
|
|
|
|
|
}
|
2022-09-11 02:54:11 +08:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
|
|
|
|
return to.apply(t).survivalPlaceBlock(t, world, x, y, z, trigger, env);
|
|
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* This is the switch block for structure code.
|
|
|
|
|
* <p>
|
|
|
|
|
* This allows you to extract or compute a key from context object, and lookup a map for actual structure element to
|
|
|
|
|
* use.
|
|
|
|
|
*
|
|
|
|
|
* @param keyExtractor extract a key from the context object
|
|
|
|
|
* @param map all possible structure element
|
|
|
|
|
* @deprecated renamed to partitionBy
|
|
|
|
|
*/
|
|
|
|
|
@Deprecated
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T, K> IStructureElementDeferred<T> defer(Function<? super T, ? extends K> keyExtractor,
|
|
|
|
|
Map<K, ? extends IStructureElement<? super T>> map) {
|
2022-08-30 23:27:18 +08:00
|
|
|
return partitionBy(keyExtractor, map);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This is the switch block for structure code.
|
|
|
|
|
* <p>
|
|
|
|
|
* This allows you to extract or compute a key from context object, and lookup a map for actual structure element to
|
|
|
|
|
* use. This will usually, but not guaranteed, to throw an exception at runtime if keyExtractor returns a key not
|
|
|
|
|
* found in map.
|
|
|
|
|
* <p>
|
|
|
|
|
* Do pay attention to what properties your map has though. You need to pay attention to how they consider equality
|
|
|
|
|
* and how they consider null key/values. Guava ImmutableMap is usually a good enough choice.
|
|
|
|
|
*
|
|
|
|
|
* @param keyExtractor extract a key from the context object
|
|
|
|
|
* @param map all possible structure element
|
|
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T, K> IStructureElementDeferred<T> partitionBy(Function<? super T, ? extends K> keyExtractor,
|
|
|
|
|
Map<K, ? extends IStructureElement<? super T>> map) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (keyExtractor == null || map == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
2022-09-13 21:22:34 +08:00
|
|
|
return defer(keyExtractor.andThen(map::get));
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* This is the switch block for structure code, with default case support.
|
|
|
|
|
* <p>
|
|
|
|
|
* This allows you to extract or compute a key from context object, and lookup a map for actual structure element to
|
|
|
|
|
* use. The fallback will only be used when keyExtractor returns a value not found in given map. This will play
|
|
|
|
|
* nicely in combination with {@link #error()}
|
|
|
|
|
* <p>
|
|
|
|
|
* Do pay attention to what properties your map has though. You need to pay attention to how they consider equality
|
|
|
|
|
* and how they consider null key/values. Guava ImmutableMap is usually a good enough choice.
|
|
|
|
|
*
|
|
|
|
|
* @param keyExtractor extract a key from the context object
|
|
|
|
|
* @param map all possible structure element
|
|
|
|
|
* @param defaultElem element to use when keyExtractor returns a value not found in given map
|
|
|
|
|
* @deprecated renamed to partitionBy
|
|
|
|
|
*/
|
|
|
|
|
@Deprecated
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T, K> IStructureElementDeferred<T> defer(Function<? super T, ? extends K> keyExtractor,
|
|
|
|
|
Map<K, ? extends IStructureElement<? super T>> map, IStructureElement<? super T> defaultElem) {
|
2022-08-30 23:27:18 +08:00
|
|
|
return partitionBy(keyExtractor, map, defaultElem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This is the switch block for structure code, with default case support.
|
|
|
|
|
* <p>
|
|
|
|
|
* This allows you to extract or compute a key from context object, and lookup a map for actual structure element to
|
|
|
|
|
* use. The fallback will only be used when keyExtractor returns a value not found in given map. This will play
|
|
|
|
|
* nicely in combination with {@link #error()}
|
|
|
|
|
* <p>
|
|
|
|
|
* Do pay attention to what properties your map has though. You need to pay attention to how they consider equality
|
|
|
|
|
* and how they consider null key/values. Guava ImmutableMap is usually a good enough choice.
|
|
|
|
|
*
|
|
|
|
|
* @param keyExtractor extract a key from the context object
|
|
|
|
|
* @param map all possible structure element
|
|
|
|
|
* @param defaultElem element to use when keyExtractor returns a value not found in given map
|
|
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T, K> IStructureElementDeferred<T> partitionBy(Function<? super T, ? extends K> keyExtractor,
|
|
|
|
|
Map<K, ? extends IStructureElement<? super T>> map, IStructureElement<? super T> defaultElem) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (keyExtractor == null || map == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
2026-05-28 01:08:27 -05:00
|
|
|
return defer(keyExtractor.andThen(key -> defaultIfNull(map.get(key), defaultElem)));
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* This is the switch block for structure code.
|
|
|
|
|
* <p>
|
|
|
|
|
* This allows you to extract or compute an index from context object, and lookup an array for actual structure
|
|
|
|
|
* element to use. This will usually, but not guaranteed, to throw an exception at runtime if keyExtractor returns
|
|
|
|
|
* an index not within the array bound
|
|
|
|
|
* <p>
|
|
|
|
|
* Do pay place null values in the array.
|
|
|
|
|
*
|
|
|
|
|
* @param keyExtractor extract an index from the context object
|
|
|
|
|
* @param array all possible structure element
|
|
|
|
|
* @deprecated renamed to partitionBy
|
|
|
|
|
*/
|
2022-07-08 17:14:18 +08:00
|
|
|
@SafeVarargs
|
2022-09-06 16:34:46 +08:00
|
|
|
@Deprecated
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T> IStructureElementDeferred<T> defer(Function<? super T, Integer> keyExtractor,
|
|
|
|
|
IStructureElement<? super T>... array) {
|
2022-08-30 23:27:18 +08:00
|
|
|
return partitionBy(keyExtractor, array);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This is the switch block for structure code.
|
|
|
|
|
* <p>
|
|
|
|
|
* This allows you to extract or compute an index from context object, and lookup an array for actual structure
|
|
|
|
|
* element to use. This will usually, but not guaranteed, to throw an exception at runtime if keyExtractor returns
|
|
|
|
|
* an index not within the array bound
|
|
|
|
|
* <p>
|
|
|
|
|
* Do pay place null values in the array.
|
|
|
|
|
*
|
|
|
|
|
* @param keyExtractor extract an index from the context object
|
|
|
|
|
* @param array all possible structure element
|
|
|
|
|
*/
|
|
|
|
|
@SafeVarargs
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T> IStructureElementDeferred<T> partitionBy(Function<? super T, Integer> keyExtractor,
|
|
|
|
|
IStructureElement<? super T>... array) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (keyExtractor == null || array == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
2022-09-13 21:22:34 +08:00
|
|
|
return defer(keyExtractor.andThen(i -> array[i]));
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* This is the switch block for structure code.
|
|
|
|
|
* <p>
|
|
|
|
|
* This allows you to extract or compute an index from context object, and lookup an array for actual structure
|
|
|
|
|
* element to use. This will usually, but not guaranteed, to throw an exception at runtime if keyExtractor returns
|
|
|
|
|
* an index not within the array bound
|
|
|
|
|
* <p>
|
|
|
|
|
* Do pay place null values in the array.
|
|
|
|
|
*
|
|
|
|
|
* @param keyExtractor extract an index from the context object
|
|
|
|
|
* @param array all possible structure element
|
|
|
|
|
* @deprecated renamed to partitionBy
|
|
|
|
|
*/
|
2022-09-06 16:34:46 +08:00
|
|
|
@Deprecated
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T> IStructureElementDeferred<T> defer(Function<? super T, Integer> keyExtractor,
|
|
|
|
|
List<? extends IStructureElement<? super T>> array) {
|
2022-08-30 23:27:18 +08:00
|
|
|
return partitionBy(keyExtractor, array);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This is the switch block for structure code.
|
|
|
|
|
* <p>
|
|
|
|
|
* This allows you to extract or compute an index from context object, and lookup an array for actual structure
|
|
|
|
|
* element to use. This will usually, but not guaranteed, to throw an exception at runtime if keyExtractor returns
|
|
|
|
|
* an index not within the array bound
|
|
|
|
|
* <p>
|
|
|
|
|
* Do pay place null values in the array.
|
|
|
|
|
*
|
|
|
|
|
* @param keyExtractor extract an index from the context object
|
|
|
|
|
* @param array all possible structure element
|
|
|
|
|
*/
|
|
|
|
|
@SuppressWarnings("unchecked")
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T> IStructureElementDeferred<T> partitionBy(Function<? super T, Integer> keyExtractor,
|
|
|
|
|
List<? extends IStructureElement<? super T>> array) {
|
2022-09-06 16:34:46 +08:00
|
|
|
return partitionBy(keyExtractor, array.toArray(new IStructureElement[0]));
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* This will defer the actual instantiation of structure element until the structure code is actually called. This
|
|
|
|
|
* will allow the structure element <b>constructor</b> to access properties only present on the context object (e.g.
|
|
|
|
|
* GT5 multiblock controller), e.g. hatch texture index to use. Use this if it might change for the same structure
|
|
|
|
|
* definition, e.g. if your structure might switch mode and change some parameter, while basically remain the same
|
|
|
|
|
* shape. Using this while {@link #lazy(Supplier)} is enough will not break anything, but would incur unnecessary
|
|
|
|
|
* performance overhead.
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant also passes the trigger item to the function, allowing it to get more info.
|
|
|
|
|
*
|
|
|
|
|
* @param to downstream element supplier
|
|
|
|
|
*/
|
2026-05-28 23:04:03 -05:00
|
|
|
public static <T> IStructureElementDeferred<T> defer(
|
|
|
|
|
BiFunction<? super T, ItemStack, ? extends IStructureElement<? super T>> to) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (to == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElementDeferred<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@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);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
|
|
|
|
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
return to.apply(t, trigger).couldBeValid(t, world, x, y, z, trigger);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@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);
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
|
2022-09-13 21:22:34 +08:00
|
|
|
@Nullable
|
|
|
|
|
@Override
|
|
|
|
|
public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
|
|
|
|
return to.apply(t, trigger).getBlocksToPlace(t, world, x, y, z, trigger, env);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
@Override
|
|
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
|
|
|
|
return to.apply(t, trigger).survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
|
|
|
|
|
}
|
2022-09-11 02:54:11 +08:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
|
|
|
|
return to.apply(t, trigger).survivalPlaceBlock(t, world, x, y, z, trigger, env);
|
|
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* This is the switch block for structure code.
|
|
|
|
|
* <p>
|
|
|
|
|
* This allows you to extract or compute a key from context object, and lookup a map for actual structure element to
|
|
|
|
|
* use. This will usually, but not guaranteed, to throw an exception at runtime if keyExtractor returns a key not
|
|
|
|
|
* found in map.
|
|
|
|
|
* <p>
|
|
|
|
|
* Do pay attention to what properties your map has though. You need to pay attention to how they consider equality
|
|
|
|
|
* and how they consider null key/values. Guava ImmutableMap is usually a good enough choice.
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant also passes the trigger item to the function, allowing it to get more info.
|
|
|
|
|
*
|
|
|
|
|
* @param keyExtractor extract a key from the context object and trigger item
|
|
|
|
|
* @param map all possible structure element
|
|
|
|
|
* @deprecated renamed to partitionBy
|
|
|
|
|
*/
|
|
|
|
|
@Deprecated
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T, K> IStructureElementDeferred<T> defer(BiFunction<? super T, ItemStack, ? extends K> keyExtractor,
|
|
|
|
|
Map<K, ? extends IStructureElement<? super T>> map) {
|
2022-08-30 23:27:18 +08:00
|
|
|
return partitionBy(keyExtractor, map);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This is the switch block for structure code.
|
|
|
|
|
* <p>
|
|
|
|
|
* This allows you to extract or compute a key from context object, and lookup a map for actual structure element to
|
|
|
|
|
* use. This will usually, but not guaranteed, to throw an exception at runtime if keyExtractor returns a key not
|
|
|
|
|
* found in map.
|
|
|
|
|
* <p>
|
|
|
|
|
* Do pay attention to what properties your map has though. You need to pay attention to how they consider equality
|
|
|
|
|
* and how they consider null key/values. Guava ImmutableMap is usually a good enough choice.
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant also passes the trigger item to the function, allowing it to get more info.
|
|
|
|
|
*
|
|
|
|
|
* @param keyExtractor extract a key from the context object and trigger item
|
|
|
|
|
* @param map all possible structure element
|
|
|
|
|
*/
|
2026-05-28 23:04:03 -05:00
|
|
|
public static <T, K> IStructureElementDeferred<T> partitionBy(
|
|
|
|
|
BiFunction<? super T, ItemStack, ? extends K> keyExtractor,
|
2026-05-28 01:08:27 -05:00
|
|
|
Map<K, ? extends IStructureElement<? super T>> map) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (keyExtractor == null || map == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
2022-09-13 21:22:34 +08:00
|
|
|
return defer(keyExtractor.andThen(map::get));
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* This is the switch block for structure code, with default case support.
|
|
|
|
|
* <p>
|
|
|
|
|
* This allows you to extract or compute a key from context object, and lookup a map for actual structure element to
|
|
|
|
|
* use. The fallback will only be used when keyExtractor returns a value not found in given map. This will play
|
|
|
|
|
* nicely in combination with {@link #error()}
|
|
|
|
|
* <p>
|
|
|
|
|
* Do pay attention to what properties your map has though. You need to pay attention to how they consider equality
|
|
|
|
|
* and how they consider null key/values. Guava ImmutableMap is usually a good enough choice.
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant also passes the trigger item to the function, allowing it to get more info.
|
|
|
|
|
*
|
|
|
|
|
* @param keyExtractor extract a key from the context object and trigger item
|
|
|
|
|
* @param map all possible structure element
|
|
|
|
|
* @param defaultElem element to use when keyExtractor returns a value not found in given map
|
|
|
|
|
* @deprecated renamed to partitionBy
|
|
|
|
|
*/
|
|
|
|
|
@Deprecated
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T, K> IStructureElementDeferred<T> defer(BiFunction<? super T, ItemStack, ? extends K> keyExtractor,
|
|
|
|
|
Map<K, ? extends IStructureElement<? super T>> map, IStructureElement<? super T> defaultElem) {
|
2022-08-30 23:27:18 +08:00
|
|
|
return partitionBy(keyExtractor, map, defaultElem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This is the switch block for structure code, with default case support.
|
|
|
|
|
* <p>
|
|
|
|
|
* This allows you to extract or compute a key from context object, and lookup a map for actual structure element to
|
|
|
|
|
* use. The fallback will only be used when keyExtractor returns a value not found in given map. This will play
|
|
|
|
|
* nicely in combination with {@link #error()}
|
|
|
|
|
* <p>
|
|
|
|
|
* Do pay attention to what properties your map has though. You need to pay attention to how they consider equality
|
|
|
|
|
* and how they consider null key/values. Guava ImmutableMap is usually a good enough choice.
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant also passes the trigger item to the function, allowing it to get more info.
|
|
|
|
|
*
|
|
|
|
|
* @param keyExtractor extract a key from the context object and trigger item
|
|
|
|
|
* @param map all possible structure element
|
|
|
|
|
* @param defaultElem element to use when keyExtractor returns a value not found in given map
|
|
|
|
|
*/
|
2026-05-28 23:04:03 -05:00
|
|
|
public static <T, K> IStructureElementDeferred<T> partitionBy(
|
|
|
|
|
BiFunction<? super T, ItemStack, ? extends K> keyExtractor,
|
2026-05-28 01:08:27 -05:00
|
|
|
Map<K, ? extends IStructureElement<? super T>> map, IStructureElement<? super T> defaultElem) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (keyExtractor == null || map == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
2026-05-28 01:08:27 -05:00
|
|
|
return defer(keyExtractor.andThen(key -> defaultIfNull(map.get(key), defaultElem)));
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* This is the switch block for structure code.
|
|
|
|
|
* <p>
|
|
|
|
|
* This allows you to extract or compute an index from context object, and lookup an array for actual structure
|
|
|
|
|
* element to use. This will usually, but not guaranteed, to throw an exception at runtime if keyExtractor returns
|
|
|
|
|
* an index not within the array bound
|
|
|
|
|
* <p>
|
|
|
|
|
* Do pay place null values in the array.
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant also passes the trigger item to the function, allowing it to get more info.
|
|
|
|
|
*
|
|
|
|
|
* @param keyExtractor extract a key from the context object and trigger item
|
|
|
|
|
* @param array all possible structure element
|
|
|
|
|
*/
|
2022-07-08 17:14:18 +08:00
|
|
|
@SafeVarargs
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T> IStructureElementDeferred<T> defer(BiFunction<? super T, ItemStack, Integer> keyExtractor,
|
|
|
|
|
IStructureElement<? super T>... array) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (keyExtractor == null || array == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
2022-09-13 21:22:34 +08:00
|
|
|
return defer(keyExtractor.andThen(i -> array[i]));
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* This is the switch block for structure code.
|
|
|
|
|
* <p>
|
|
|
|
|
* This allows you to extract or compute an index from context object, and lookup an array for actual structure
|
|
|
|
|
* element to use. This will usually, but not guaranteed, to throw an exception at runtime if keyExtractor returns
|
|
|
|
|
* an index not within the array bound
|
|
|
|
|
* <p>
|
|
|
|
|
* Do pay place null values in the array.
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant also passes the trigger item to the function, allowing it to get more info.
|
|
|
|
|
*
|
|
|
|
|
* @param keyExtractor extract a key from the context object and trigger item
|
|
|
|
|
* @param array all possible structure element
|
|
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T> IStructureElementDeferred<T> defer(BiFunction<? super T, ItemStack, Integer> keyExtractor,
|
|
|
|
|
List<? extends IStructureElement<? super T>> array) {
|
2022-09-13 21:22:34 +08:00
|
|
|
return defer(keyExtractor.andThen(array::get));
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* This will defer the actual instantiation of structure element until the <b>first time</b> structure code is
|
|
|
|
|
* actually called.
|
|
|
|
|
* <p>
|
|
|
|
|
* Similar to defer, but caches the first returned element returned and won't call it again. Initialization is not
|
|
|
|
|
* thread safe. This will allow the structure element <b>constructor</b> to access properties only present on the
|
|
|
|
|
* context object (e.g. GT5 multiblock controller), e.g. hatch texture index to use. Use this if the data you access
|
|
|
|
|
* will remain constant.
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant will override the check function of the structure element from second function with the one returned
|
|
|
|
|
* from the structure element from first function.
|
|
|
|
|
*
|
|
|
|
|
* @param toCheck override the check function with the returned element
|
|
|
|
|
* @param to create structure element from the context object passed in
|
|
|
|
|
*/
|
2026-05-28 23:04:03 -05:00
|
|
|
public static <T> IStructureElementDeferred<T> defer(
|
|
|
|
|
Function<? super T, ? extends IStructureElement<? super T>> toCheck,
|
2026-05-28 01:08:27 -05:00
|
|
|
BiFunction<? super T, ItemStack, ? extends IStructureElement<? super T>> to) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (to == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
2026-05-28 01:08:27 -05:00
|
|
|
return new IStructureElementDeferred<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
|
|
|
|
public boolean check(T t, World world, int x, int y, int z) {
|
|
|
|
|
return toCheck.apply(t).check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
@Override
|
|
|
|
|
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
return to.apply(t, trigger).couldBeValid(t, world, x, y, z, trigger);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@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);
|
|
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
|
|
|
|
return to.apply(t, trigger).survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
|
|
|
|
|
}
|
2022-09-11 02:54:11 +08:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
|
|
|
|
return to.apply(t, trigger).survivalPlaceBlock(t, world, x, y, z, trigger, env);
|
|
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* This is the switch block for structure code.
|
|
|
|
|
* <p>
|
|
|
|
|
* This allows you to extract or compute a key from context object, and lookup a map for actual structure element to
|
|
|
|
|
* use. This will usually, but not guaranteed, to throw an exception at runtime if keyExtractor returns a key not
|
|
|
|
|
* found in map.
|
|
|
|
|
* <p>
|
|
|
|
|
* Do pay attention to what properties your map has though. You need to pay attention to how they consider equality
|
|
|
|
|
* and how they consider null key/values. Guava ImmutableMap is usually a good enough choice.
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant also passes the trigger item to the function, allowing it to get more info.
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant will override the check function of the structure element from second function with the one returned
|
|
|
|
|
* from the structure element from first function.
|
|
|
|
|
*
|
|
|
|
|
* @param keyExtractorCheck key extractor of the structure element that will be used for check.
|
|
|
|
|
* @param keyExtractor extract a key from the context object and trigger item
|
|
|
|
|
* @param map all possible structure element
|
|
|
|
|
* @deprecated renamed to partitionBy
|
|
|
|
|
*/
|
|
|
|
|
@Deprecated
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T, K> IStructureElementDeferred<T> defer(Function<? super T, ? extends K> keyExtractorCheck,
|
2026-05-28 23:04:03 -05:00
|
|
|
BiFunction<? super T, ItemStack, ? extends K> keyExtractor,
|
|
|
|
|
Map<K, ? extends IStructureElement<? super T>> map) {
|
2022-08-30 23:27:18 +08:00
|
|
|
return partitionBy(keyExtractorCheck, keyExtractor, map);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This is the switch block for structure code.
|
|
|
|
|
* <p>
|
|
|
|
|
* This allows you to extract or compute a key from context object, and lookup a map for actual structure element to
|
|
|
|
|
* use. This will usually, but not guaranteed, to throw an exception at runtime if keyExtractor returns a key not
|
|
|
|
|
* found in map.
|
|
|
|
|
* <p>
|
|
|
|
|
* Do pay attention to what properties your map has though. You need to pay attention to how they consider equality
|
|
|
|
|
* and how they consider null key/values. Guava ImmutableMap is usually a good enough choice.
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant also passes the trigger item to the function, allowing it to get more info.
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant will override the check function of the structure element from second function with the one returned
|
|
|
|
|
* from the structure element from first function.
|
|
|
|
|
*
|
|
|
|
|
* @param keyExtractorCheck key extractor of the structure element that will be used for check.
|
|
|
|
|
* @param keyExtractor extract a key from the context object and trigger item
|
|
|
|
|
* @param map all possible structure element
|
|
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T, K> IStructureElementDeferred<T> partitionBy(Function<? super T, ? extends K> keyExtractorCheck,
|
2026-05-28 23:04:03 -05:00
|
|
|
BiFunction<? super T, ItemStack, ? extends K> keyExtractor,
|
|
|
|
|
Map<K, ? extends IStructureElement<? super T>> map) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (keyExtractor == null || map == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
2026-06-19 08:31:17 -05:00
|
|
|
return defer(keyExtractorCheck.andThen(map::get), keyExtractor.andThen(map::get));
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* This is the switch block for structure code, with default case.
|
|
|
|
|
* <p>
|
|
|
|
|
* This allows you to extract or compute a key from context object, and lookup a map for actual structure element to
|
|
|
|
|
* use. defaultElem will be used if either extractor returned a key not found in map.
|
|
|
|
|
* <p>
|
|
|
|
|
* Do pay attention to what properties your map has though. You need to pay attention to how they consider equality
|
|
|
|
|
* and how they consider null key/values. Guava ImmutableMap is usually a good enough choice.
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant also passes the trigger item to the function, allowing it to get more info.
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant will override the check function of the structure element from second function with the one returned
|
|
|
|
|
* from the structure element from first function.
|
|
|
|
|
*
|
|
|
|
|
* @param keyExtractorCheck key extractor of the structure element that will be used for check.
|
|
|
|
|
* @param keyExtractor extract a key from the context object and trigger item
|
|
|
|
|
* @param map all possible structure element
|
|
|
|
|
* @deprecated renamed to partitionBy
|
|
|
|
|
*/
|
|
|
|
|
@Deprecated
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T, K> IStructureElementDeferred<T> defer(Function<? super T, ? extends K> keyExtractorCheck,
|
2026-05-28 23:04:03 -05:00
|
|
|
BiFunction<? super T, ItemStack, ? extends K> keyExtractor,
|
|
|
|
|
Map<K, ? extends IStructureElement<? super T>> map, IStructureElement<? super T> defaultElem) {
|
2022-08-30 23:27:18 +08:00
|
|
|
return partitionBy(keyExtractorCheck, keyExtractor, map, defaultElem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This is the switch block for structure code, with default case.
|
|
|
|
|
* <p>
|
|
|
|
|
* This allows you to extract or compute a key from context object, and lookup a map for actual structure element to
|
|
|
|
|
* use. defaultElem will be used if either extractor returned a key not found in map.
|
|
|
|
|
* <p>
|
|
|
|
|
* Do pay attention to what properties your map has though. You need to pay attention to how they consider equality
|
|
|
|
|
* and how they consider null key/values. Guava ImmutableMap is usually a good enough choice.
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant also passes the trigger item to the function, allowing it to get more info.
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant will override the check function of the structure element from second function with the one returned
|
|
|
|
|
* from the structure element from first function.
|
|
|
|
|
*
|
|
|
|
|
* @param keyExtractorCheck key extractor of the structure element that will be used for check.
|
|
|
|
|
* @param keyExtractor extract a key from the context object and trigger item
|
|
|
|
|
* @param map all possible structure element
|
|
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T, K> IStructureElementDeferred<T> partitionBy(Function<? super T, ? extends K> keyExtractorCheck,
|
2026-05-28 23:04:03 -05:00
|
|
|
BiFunction<? super T, ItemStack, ? extends K> keyExtractor,
|
|
|
|
|
Map<K, ? extends IStructureElement<? super T>> map, IStructureElement<? super T> defaultElem) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (keyExtractor == null || map == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
2026-05-28 01:08:27 -05:00
|
|
|
|
2022-09-13 21:22:34 +08:00
|
|
|
return defer(
|
2026-05-28 01:08:27 -05:00
|
|
|
keyExtractorCheck.andThen(k -> defaultIfNull(map.get(k), defaultElem)),
|
|
|
|
|
keyExtractor.andThen(k -> defaultIfNull(map.get(k), defaultElem)));
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* This is the switch block for structure code, with default case.
|
|
|
|
|
* <p>
|
|
|
|
|
* This allows you to extract or compute an index from context object, and lookup an array for actual structure
|
|
|
|
|
* element to use. This will usually, but not guaranteed, to throw an exception at runtime if keyExtractor returns
|
|
|
|
|
* an index not within the array bound
|
|
|
|
|
* <p>
|
|
|
|
|
* Do pay place null values in the array.
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant also passes the trigger item to the function, allowing it to get more info.
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant will override the check function of the structure element from second function with the one returned
|
|
|
|
|
* from the structure element from first function.
|
|
|
|
|
*
|
|
|
|
|
* @param keyExtractorCheck key extractor of the structure element that will be used for check.
|
|
|
|
|
* @param keyExtractor extract a key from the context object and trigger item
|
|
|
|
|
* @param array all possible structure element
|
|
|
|
|
* @deprecated renamed to partitionBy
|
|
|
|
|
*/
|
2022-07-08 17:14:18 +08:00
|
|
|
@SafeVarargs
|
2022-09-06 16:34:46 +08:00
|
|
|
@Deprecated
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T> IStructureElementDeferred<T> defer(Function<? super T, Integer> keyExtractorCheck,
|
|
|
|
|
BiFunction<? super T, ItemStack, Integer> keyExtractor, IStructureElement<? super T>... array) {
|
2022-08-30 23:27:18 +08:00
|
|
|
return partitionBy(keyExtractorCheck, keyExtractor, array);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This is the switch block for structure code, with default case.
|
|
|
|
|
* <p>
|
|
|
|
|
* This allows you to extract or compute an index from context object, and lookup an array for actual structure
|
|
|
|
|
* element to use. This will usually, but not guaranteed, to throw an exception at runtime if keyExtractor returns
|
|
|
|
|
* an index not within the array bound
|
|
|
|
|
* <p>
|
|
|
|
|
* Do pay place null values in the array.
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant also passes the trigger item to the function, allowing it to get more info.
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant will override the check function of the structure element from second function with the one returned
|
|
|
|
|
* from the structure element from first function.
|
|
|
|
|
*
|
|
|
|
|
* @param keyExtractorCheck key extractor of the structure element that will be used for check.
|
|
|
|
|
* @param keyExtractor extract a key from the context object and trigger item
|
|
|
|
|
* @param array all possible structure element
|
|
|
|
|
*/
|
|
|
|
|
@SafeVarargs
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T> IStructureElementDeferred<T> partitionBy(Function<? super T, Integer> keyExtractorCheck,
|
|
|
|
|
BiFunction<? super T, ItemStack, Integer> keyExtractor, IStructureElement<? super T>... array) {
|
2022-07-08 17:14:18 +08:00
|
|
|
if (keyExtractor == null || array == null) {
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
|
}
|
2022-09-13 21:22:34 +08:00
|
|
|
return defer(keyExtractorCheck.andThen(i -> array[i]), keyExtractor.andThen(i -> array[i]));
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
/**
|
|
|
|
|
* This is the switch block for structure code, with default case.
|
|
|
|
|
* <p>
|
|
|
|
|
* This allows you to extract or compute an index from context object, and lookup an array for actual structure
|
|
|
|
|
* element to use. This will usually, but not guaranteed, to throw an exception at runtime if keyExtractor returns
|
|
|
|
|
* an index not within the array bound
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant also passes the trigger item to the function, allowing it to get more info.
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant will override the check function of the structure element from second function with the one returned
|
|
|
|
|
* from the structure element from first function.
|
|
|
|
|
*
|
|
|
|
|
* @param keyExtractorCheck key extractor of the structure element that will be used for check.
|
|
|
|
|
* @param keyExtractor extract a key from the context object and trigger item
|
|
|
|
|
* @param array all possible structure element
|
|
|
|
|
* @deprecated renamed to partitionBy
|
|
|
|
|
*/
|
2022-09-06 16:34:46 +08:00
|
|
|
@Deprecated
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T> IStructureElementDeferred<T> defer(Function<? super T, Integer> keyExtractorCheck,
|
2026-05-28 23:04:03 -05:00
|
|
|
BiFunction<? super T, ItemStack, Integer> keyExtractor,
|
|
|
|
|
List<? extends IStructureElement<? super T>> array) {
|
2022-08-30 23:27:18 +08:00
|
|
|
return partitionBy(keyExtractorCheck, keyExtractor, array);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This is the switch block for structure code, with default case.
|
|
|
|
|
* <p>
|
|
|
|
|
* This allows you to extract or compute an index from context object, and lookup an array for actual structure
|
|
|
|
|
* element to use. This will usually, but not guaranteed, to throw an exception at runtime if keyExtractor returns
|
|
|
|
|
* an index not within the array bound
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant also passes the trigger item to the function, allowing it to get more info.
|
|
|
|
|
* <p>
|
|
|
|
|
* This variant will override the check function of the structure element from second function with the one returned
|
|
|
|
|
* from the structure element from first function.
|
|
|
|
|
*
|
|
|
|
|
* @param keyExtractorCheck key extractor of the structure element that will be used for check.
|
|
|
|
|
* @param keyExtractor extract a key from the context object and trigger item
|
|
|
|
|
* @param array all possible structure element
|
|
|
|
|
*/
|
|
|
|
|
@SuppressWarnings("unchecked")
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T> IStructureElementDeferred<T> partitionBy(Function<? super T, Integer> keyExtractorCheck,
|
2026-05-28 23:04:03 -05:00
|
|
|
BiFunction<? super T, ItemStack, Integer> keyExtractor,
|
|
|
|
|
List<? extends IStructureElement<? super T>> array) {
|
2022-09-06 16:34:46 +08:00
|
|
|
return partitionBy(keyExtractorCheck, keyExtractor, array.toArray(new IStructureElement[0]));
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
// endregion
|
2022-07-08 17:14:18 +08:00
|
|
|
|
2022-08-23 12:29:59 +08:00
|
|
|
// region channels
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* See channels.md in docs folder
|
|
|
|
|
*/
|
2026-05-28 01:08:27 -05:00
|
|
|
public static <T> IStructureElement<T> withChannel(String channel, IStructureElement<? super T> backing) {
|
|
|
|
|
return new IStructureElement<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-08-23 12:29:59 +08:00
|
|
|
public boolean check(T t, World world, int x, int y, int z) {
|
|
|
|
|
return backing.check(t, world, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-14 13:13:34 -05:00
|
|
|
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
|
|
|
|
ItemStack newTrigger = ChannelDataAccessor.withChannel(trigger, channel);
|
|
|
|
|
return backing.couldBeValid(t, world, x, y, z, newTrigger);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-23 12:29:59 +08:00
|
|
|
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-08-23 19:54:36 +08:00
|
|
|
ItemStack newTrigger = ChannelDataAccessor.withChannel(trigger, channel);
|
|
|
|
|
// We know spawnHint will only be called on client side, so the lack of player isn't quite an issue
|
|
|
|
|
// we check if it's null again just in case.
|
|
|
|
|
if (newTrigger == trigger && StructureLib.getCurrentPlayer() != null)
|
|
|
|
|
warnNoExplicitSubChannel(StructureLib.getCurrentPlayer());
|
|
|
|
|
return backing.spawnHint(t, world, x, y, z, newTrigger);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void warnNoExplicitSubChannel(EntityPlayer currentPlayer) {
|
2022-09-02 15:45:13 +08:00
|
|
|
// throttle this warning a bit.
|
|
|
|
|
// I'm sure we can finish a check/autoplace round within 100 milliseconds.
|
|
|
|
|
StructureLibAPI.addThrottledChat(
|
|
|
|
|
new ChatThrottleKey.NoExplicitChannel(channel),
|
|
|
|
|
currentPlayer,
|
|
|
|
|
new ChatComponentTranslation("structurelib.autoplace.warning.no_explicit_channel", channel),
|
|
|
|
|
(short) 100);
|
2022-08-23 12:29:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
2022-08-23 19:54:36 +08:00
|
|
|
// I hope a CREATIVE player know what he is doing...
|
2022-09-02 15:45:13 +08:00
|
|
|
// no warning for yah
|
2022-08-23 12:29:59 +08:00
|
|
|
return backing.placeBlock(t, world, x, y, z, ChannelDataAccessor.withChannel(trigger, channel));
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-13 21:22:34 +08:00
|
|
|
@Nullable
|
|
|
|
|
@Override
|
|
|
|
|
public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
2026-07-03 23:33:08 +08:00
|
|
|
return backing
|
|
|
|
|
.getBlocksToPlace(t, world, x, y, z, ChannelDataAccessor.withChannel(trigger, channel), env);
|
2022-09-13 21:22:34 +08:00
|
|
|
}
|
|
|
|
|
|
2022-09-11 02:54:11 +08:00
|
|
|
@Deprecated
|
2022-08-23 12:29:59 +08:00
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
2022-08-23 19:54:36 +08:00
|
|
|
ItemStack newTrigger = ChannelDataAccessor.withChannel(trigger, channel);
|
|
|
|
|
if (newTrigger == trigger)
|
|
|
|
|
// we will bypass the chatter filter here, as this is a warning that player definitively want to see
|
|
|
|
|
// instead of some false positive error messages like item not find
|
|
|
|
|
warnNoExplicitSubChannel(actor);
|
|
|
|
|
return backing.survivalPlaceBlock(t, world, x, y, z, newTrigger, s, actor, chatter);
|
2022-08-23 12:29:59 +08:00
|
|
|
}
|
2022-09-11 02:54:11 +08:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
|
|
|
|
AutoPlaceEnvironment env) {
|
|
|
|
|
ItemStack newTrigger = ChannelDataAccessor.withChannel(trigger, channel);
|
|
|
|
|
if (newTrigger == trigger)
|
|
|
|
|
// we will bypass the chatter filter here, as this is a warning that player definitively want to see
|
|
|
|
|
// instead of some false positive error messages like item not find
|
|
|
|
|
warnNoExplicitSubChannel(env.getActor());
|
2023-05-04 11:40:03 -04:00
|
|
|
return backing.survivalPlaceBlock(t, world, x, y, z, newTrigger, env);
|
2022-09-11 02:54:11 +08:00
|
|
|
}
|
2026-05-23 09:42:20 +02:00
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
|
@Override
|
2026-05-24 07:35:25 +02:00
|
|
|
public List<String> getDescription(T context) {
|
|
|
|
|
return backing.getDescription(context);
|
2026-05-23 09:42:20 +02:00
|
|
|
}
|
2022-08-23 12:29:59 +08:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
// endregion
|
|
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
/**
|
|
|
|
|
* Used internally, to generate skips for structure definitions
|
|
|
|
|
*/
|
|
|
|
|
public static <T> IStructureNavigate<T> 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 <T> IStructureNavigate<T> 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 <T> IStructureNavigate<T> stepA(int a, int b, int c) {
|
2026-05-28 23:04:03 -05:00
|
|
|
return new IStructureNavigate<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@Override
|
|
|
|
|
public int getStepA() {
|
|
|
|
|
return a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public int getStepB() {
|
|
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public int getStepC() {
|
|
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static <T> IStructureNavigate<T> stepB(int a, int b, int c) {
|
2026-05-28 23:04:03 -05:00
|
|
|
return new IStructureNavigate<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@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 <T> IStructureNavigate<T> stepC(int a, int b, int c) {
|
2026-05-28 23:04:03 -05:00
|
|
|
return new IStructureNavigate<>() {
|
2023-01-30 12:52:27 +00:00
|
|
|
|
2022-07-08 17:14:18 +08:00
|
|
|
@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...
|
|
|
|
|
* <p>
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2022-08-08 17:40:12 +08:00
|
|
|
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) {
|
2022-07-08 17:14:18 +08:00
|
|
|
Map<Block, Set<Integer>> blocks = new TreeMap<>(Comparator.comparing(Block::getUnlocalizedName));
|
|
|
|
|
Set<Class<? extends TileEntity>> tiles = new HashSet<>();
|
|
|
|
|
Set<String> specialTiles = new HashSet<>();
|
2022-08-08 17:40:12 +08:00
|
|
|
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);
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
}));
|
2022-07-08 17:14:18 +08:00
|
|
|
Map<String, Character> map = new HashMap<>();
|
|
|
|
|
StringBuilder builder = new StringBuilder();
|
|
|
|
|
{
|
|
|
|
|
int i = 0;
|
|
|
|
|
char c;
|
2022-08-08 17:40:12 +08:00
|
|
|
builder.append("\n\nStructure:\n").append("\nBlocks:\n");
|
2022-07-08 17:14:18 +08:00
|
|
|
for (Map.Entry<Block, Set<Integer>> entry : blocks.entrySet()) {
|
|
|
|
|
Block block = entry.getKey();
|
|
|
|
|
Set<Integer> 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);
|
2022-08-08 17:40:12 +08:00
|
|
|
builder.append(c).append(" -> ofBlock...(").append(block.getUnlocalizedName()).append(", ")
|
|
|
|
|
.append(meta).append(", ...);\n");
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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);
|
2022-08-08 17:40:12 +08:00
|
|
|
builder.append(c).append(" -> ofTileAdder(").append(tile).append(", ...);\n");
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
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);
|
2022-08-08 17:40:12 +08:00
|
|
|
builder.append(c).append(" -> ofSpecialTileAdder(").append(tile)
|
|
|
|
|
.append(", ...); // You will probably want to change it to something else\n");
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
builder.append("\nOffsets:\n").append(basePositionA).append(' ').append(basePositionB).append(' ')
|
2022-08-08 17:40:12 +08:00
|
|
|
.append(basePositionC).append('\n');
|
2022-07-08 17:14:18 +08:00
|
|
|
if (transpose) {
|
2022-08-08 17:40:12 +08:00
|
|
|
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(' ');
|
|
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
} else {
|
2022-08-08 17:40:12 +08:00
|
|
|
String classification = tileEntityClassifier.apply(tileEntity);
|
|
|
|
|
if (classification == null) {
|
|
|
|
|
classification = tileEntity.getClass().getCanonicalName();
|
|
|
|
|
}
|
|
|
|
|
builder.append(map.get(classification));
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
}),
|
|
|
|
|
() -> builder.append("\",\""),
|
|
|
|
|
() -> {
|
|
|
|
|
builder.setLength(builder.length() - 2);
|
|
|
|
|
builder.append("},\n {\"");
|
|
|
|
|
});
|
2022-07-08 17:14:18 +08:00
|
|
|
builder.setLength(builder.length() - 8);
|
|
|
|
|
builder.append("\n}\n\n");
|
|
|
|
|
} else {
|
2022-08-08 17:40:12 +08:00
|
|
|
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(' ');
|
|
|
|
|
}
|
2022-07-08 17:14:18 +08:00
|
|
|
} else {
|
2022-08-08 17:40:12 +08:00
|
|
|
String classification = tileEntityClassifier.apply(tileEntity);
|
|
|
|
|
if (classification == null) {
|
|
|
|
|
classification = tileEntity.getClass().getCanonicalName();
|
|
|
|
|
}
|
|
|
|
|
builder.append(map.get(classification));
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
2022-08-08 17:40:12 +08:00
|
|
|
}),
|
|
|
|
|
() -> builder.append("\",\n").append(" \""),
|
|
|
|
|
() -> {
|
|
|
|
|
builder.setLength(builder.length() - 7);
|
|
|
|
|
builder.append("\n").append("},{\n").append(" \"");
|
|
|
|
|
});
|
2022-07-08 17:14:18 +08:00
|
|
|
builder.setLength(builder.length() - 8);
|
|
|
|
|
builder.append("}\n\n");
|
|
|
|
|
}
|
|
|
|
|
return (builder.toString().replaceAll("\"\"", "E"));
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-27 04:29:03 -05:00
|
|
|
static <T> boolean iterateV2(IStructureElement<? super T>[] elements, World world, ExtendedFacing extendedFacing,
|
2022-08-08 17:40:12 +08:00
|
|
|
int basePositionX, int basePositionY, int basePositionZ, int basePositionA, int basePositionB,
|
|
|
|
|
int basePositionC, IStructureWalker<T> predicate, String iterateType) {
|
|
|
|
|
// change base position to base offset
|
|
|
|
|
basePositionA = -basePositionA;
|
|
|
|
|
basePositionB = -basePositionB;
|
|
|
|
|
basePositionC = -basePositionC;
|
|
|
|
|
|
|
|
|
|
int[] abc = new int[] { basePositionA, basePositionB, basePositionC };
|
|
|
|
|
int[] xyz = new int[3];
|
|
|
|
|
|
2026-05-27 04:29:03 -05:00
|
|
|
for (IStructureElement<? super T> element : elements) {
|
2022-08-08 17:40:12 +08:00
|
|
|
if (element.isNavigating()) {
|
|
|
|
|
abc[0] = (element.resetA() ? basePositionA : abc[0]) + element.getStepA();
|
|
|
|
|
abc[1] = (element.resetB() ? basePositionB : abc[1]) + element.getStepB();
|
|
|
|
|
abc[2] = (element.resetC() ? basePositionC : abc[2]) + element.getStepC();
|
|
|
|
|
} else {
|
|
|
|
|
extendedFacing.getWorldOffset(abc, xyz);
|
|
|
|
|
xyz[0] += basePositionX;
|
|
|
|
|
xyz[1] += basePositionY;
|
|
|
|
|
xyz[2] += basePositionZ;
|
|
|
|
|
|
2022-08-30 23:27:18 +08:00
|
|
|
if (StructureLibAPI.isDebugEnabled()) StructureLib.LOGGER.info(
|
2022-08-11 23:38:36 +08:00
|
|
|
"Multi [{}, {}, {}] {} step @ {} {}",
|
|
|
|
|
basePositionX,
|
|
|
|
|
basePositionY,
|
|
|
|
|
basePositionZ,
|
|
|
|
|
iterateType,
|
|
|
|
|
Arrays.toString(xyz),
|
|
|
|
|
Arrays.toString(abc));
|
2022-08-08 17:40:12 +08:00
|
|
|
|
|
|
|
|
if (world.blockExists(xyz[0], xyz[1], xyz[2])) {
|
2022-09-15 15:58:17 +08:00
|
|
|
if (StructureLibAPI.isInstrumentEnabled()) {
|
|
|
|
|
StructureElementVisitedEvent.fireEvent(
|
|
|
|
|
world,
|
|
|
|
|
xyz[0],
|
|
|
|
|
xyz[1],
|
|
|
|
|
xyz[2],
|
|
|
|
|
abc[0] - basePositionA,
|
|
|
|
|
abc[1] - basePositionB,
|
|
|
|
|
abc[2] - basePositionC,
|
|
|
|
|
element);
|
|
|
|
|
}
|
2022-09-11 02:54:11 +08:00
|
|
|
if (!predicate.visit(element, world, xyz[0], xyz[1], xyz[2], abc[0], abc[1], abc[2])) {
|
2022-08-30 23:27:18 +08:00
|
|
|
if (StructureLibAPI.isDebugEnabled()) {
|
2022-08-08 17:40:12 +08:00
|
|
|
StructureLib.LOGGER.info(
|
|
|
|
|
"Multi [{}, {}, {}] {} stop @ {} {}",
|
|
|
|
|
basePositionX,
|
|
|
|
|
basePositionY,
|
|
|
|
|
basePositionZ,
|
|
|
|
|
iterateType,
|
|
|
|
|
Arrays.toString(xyz),
|
|
|
|
|
Arrays.toString(abc));
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2022-08-30 23:27:18 +08:00
|
|
|
if (StructureLibAPI.isDebugEnabled()) {
|
2022-08-08 17:40:12 +08:00
|
|
|
StructureLib.LOGGER.info(
|
|
|
|
|
"Multi [{}, {}, {}] {} !blockExists @ {} {}",
|
|
|
|
|
basePositionX,
|
|
|
|
|
basePositionY,
|
|
|
|
|
basePositionZ,
|
|
|
|
|
iterateType,
|
|
|
|
|
Arrays.toString(xyz),
|
|
|
|
|
Arrays.toString(abc));
|
|
|
|
|
}
|
2022-09-11 02:54:11 +08:00
|
|
|
if (!predicate.blockNotLoaded(element, world, xyz[0], xyz[1], xyz[2], abc[0], abc[1], abc[2]))
|
|
|
|
|
return false;
|
2022-08-08 17:40:12 +08:00
|
|
|
}
|
|
|
|
|
abc[0] += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) {
|
2022-07-08 17:14:18 +08:00
|
|
|
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);
|
2022-08-08 17:40:12 +08:00
|
|
|
iBlockPosConsumer
|
|
|
|
|
.consume(world, xyz[0] + basePositionX, xyz[1] + basePositionY, xyz[2] + basePositionZ);
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-08 17:40:12 +08:00
|
|
|
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) {
|
2022-07-08 17:14:18 +08:00
|
|
|
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);
|
2022-08-08 17:40:12 +08:00
|
|
|
iBlockPosConsumer
|
|
|
|
|
.consume(world, xyz[0] + basePositionX, xyz[1] + basePositionY, xyz[2] + basePositionZ);
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
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);
|
2022-08-08 17:40:12 +08:00
|
|
|
iBlockPosConsumer
|
|
|
|
|
.consume(world, xyz[0] + basePositionX, xyz[1] + basePositionY, xyz[2] + basePositionZ);
|
2022-07-08 17:14:18 +08:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
}
|
2021-05-17 03:02:24 +08:00
|
|
|
}
|