mirror of
https://github.com/GTNewHorizons/StructureLib.git
synced 2026-11-09 23:13:59 +00:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
287e8317c9 | ||
|
|
95d1533342 | ||
|
|
19179ec894 | ||
|
|
d8b301457f |
No files matched your search
@@ -5,11 +5,7 @@ import static java.lang.Math.abs;
|
||||
import net.minecraft.util.Vec3;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.Direction;
|
||||
import com.gtnewhorizon.structurelib.coords.CoordinateSystem;
|
||||
import com.gtnewhorizon.structurelib.coords.Position;
|
||||
import com.gtnewhorizon.structurelib.util.Vec3Impl;
|
||||
|
||||
public class IntegerAxisSwap {
|
||||
@@ -95,32 +91,4 @@ public class IntegerAxisSwap {
|
||||
out[1] = forFirstAxis.get1() * point[0] + forSecondAxis.get1() * point[1] + forThirdAxis.get1() * point[2];
|
||||
out[2] = forFirstAxis.get2() * point[0] + forSecondAxis.get2() * point[1] + forThirdAxis.get2() * point[2];
|
||||
}
|
||||
|
||||
public Vector3i translate(Vector3i point) {
|
||||
return new Vector3i(
|
||||
forFirstAxis.get0() * point.x() + forFirstAxis.get1() * point.y() + forFirstAxis.get2() * point.z(),
|
||||
forSecondAxis.get0() * point.x() + forSecondAxis.get1() * point.y() + forSecondAxis.get2() * point.z(),
|
||||
forThirdAxis.get0() * point.x() + forThirdAxis.get1() * point.y() + forThirdAxis.get2() * point.z());
|
||||
}
|
||||
|
||||
public Vector3i inverseTranslate(Vector3i point) {
|
||||
return new Vector3i(
|
||||
forFirstAxis.get0() * point.x() + forSecondAxis.get0() * point.y() + forThirdAxis.get0() * point.z(),
|
||||
forFirstAxis.get1() * point.x() + forSecondAxis.get1() * point.y() + forThirdAxis.get1() * point.z(),
|
||||
forFirstAxis.get2() * point.x() + forSecondAxis.get2() * point.y() + forThirdAxis.get2() * point.z());
|
||||
}
|
||||
|
||||
public <C extends CoordinateSystem<C, ?>> Position<C> translate(Position<?> point) {
|
||||
return new Position<>(
|
||||
forFirstAxis.get0() * point.x() + forFirstAxis.get1() * point.y() + forFirstAxis.get2() * point.z(),
|
||||
forSecondAxis.get0() * point.x() + forSecondAxis.get1() * point.y() + forSecondAxis.get2() * point.z(),
|
||||
forThirdAxis.get0() * point.x() + forThirdAxis.get1() * point.y() + forThirdAxis.get2() * point.z());
|
||||
}
|
||||
|
||||
public <C extends CoordinateSystem<C, ?>> Position<C> inverseTranslate(Position<?> point) {
|
||||
return new Position<>(
|
||||
forFirstAxis.get0() * point.x() + forSecondAxis.get0() * point.y() + forThirdAxis.get0() * point.z(),
|
||||
forFirstAxis.get1() * point.x() + forSecondAxis.get1() * point.y() + forThirdAxis.get1() * point.z(),
|
||||
forFirstAxis.get2() * point.x() + forSecondAxis.get2() * point.y() + forThirdAxis.get2() * point.z());
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,6 @@ import net.minecraftforge.common.util.ForgeDirection;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.gtnewhorizon.structurelib.alignment.IAlignment;
|
||||
import com.gtnewhorizon.structurelib.alignment.IntegerAxisSwap;
|
||||
import com.gtnewhorizon.structurelib.coords.StructureRelativeCoords;
|
||||
import com.gtnewhorizon.structurelib.util.Vec3Impl;
|
||||
|
||||
public enum ExtendedFacing {
|
||||
@@ -167,7 +166,6 @@ public enum ExtendedFacing {
|
||||
|
||||
private final String name;
|
||||
private final IntegerAxisSwap integerAxisSwap;
|
||||
private final StructureRelativeCoords coordinateSystem;
|
||||
|
||||
ExtendedFacing(String name) {
|
||||
this.name = name;
|
||||
@@ -249,7 +247,6 @@ public enum ExtendedFacing {
|
||||
this.b = b;
|
||||
this.c = c;
|
||||
integerAxisSwap = new IntegerAxisSwap(a, b, c);
|
||||
coordinateSystem = new StructureRelativeCoords(integerAxisSwap);
|
||||
}
|
||||
|
||||
public static ExtendedFacing of(ForgeDirection direction, Rotation rotation, Flip flip) {
|
||||
@@ -440,8 +437,4 @@ public enum ExtendedFacing {
|
||||
public ForgeDirection getRelativeForwardInWorld() {
|
||||
return c.getOpposite();
|
||||
}
|
||||
|
||||
public StructureRelativeCoords asCoordinateSystem() {
|
||||
return coordinateSystem;
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package com.gtnewhorizon.structurelib.coords;
|
||||
|
||||
/**
|
||||
* Controller-relative coordinates. The controller is at 0,0,0. No axis swapping takes place, each axis points in the
|
||||
* same directions as the matching world axis.
|
||||
*/
|
||||
public class ControllerRelativeCoords implements CoordinateSystem<ControllerRelativeCoords, WorldCoords> {
|
||||
|
||||
public final int controllerX, controllerY, controllerZ;
|
||||
|
||||
public ControllerRelativeCoords(int controllerX, int controllerY, int controllerZ) {
|
||||
this.controllerX = controllerX;
|
||||
this.controllerY = controllerY;
|
||||
this.controllerZ = controllerZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position<ControllerRelativeCoords> translate(Position<WorldCoords> position) {
|
||||
return translate(position, controllerX, controllerY, controllerZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position<WorldCoords> translateInverse(Position<ControllerRelativeCoords> position) {
|
||||
return translateInverse(position, controllerX, controllerY, controllerZ);
|
||||
}
|
||||
|
||||
public static Position<ControllerRelativeCoords> translate(Position<WorldCoords> position, int controllerX,
|
||||
int controllerY, int controllerZ) {
|
||||
position.sub(controllerX, controllerY, controllerZ);
|
||||
|
||||
return CoordinateSystem.transmute(position);
|
||||
}
|
||||
|
||||
public static Position<WorldCoords> translateInverse(Position<ControllerRelativeCoords> position, int controllerX,
|
||||
int controllerY, int controllerZ) {
|
||||
position.add(controllerX, controllerY, controllerZ);
|
||||
|
||||
return CoordinateSystem.transmute(position);
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package com.gtnewhorizon.structurelib.coords;
|
||||
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
|
||||
/**
|
||||
* A coordinate system is a statically-checked helper for determining which coordinates a {@link Position} has.
|
||||
* Translation methods never copy a {@link Position} - if a position must remain unchanged, it must be copied
|
||||
* separately. Most coordinate systems will have static methods that allow you to avoid allocations, though some (such
|
||||
* as the ones provided by {@link ExtendedFacing#asCoordinateSystem()}) have no reason to be static.
|
||||
*
|
||||
* @param <Self>
|
||||
* @param <Parent>
|
||||
*/
|
||||
public interface CoordinateSystem<Self extends CoordinateSystem<Self, Parent>, Parent extends CoordinateSystem<Parent, ?>> {
|
||||
|
||||
/**
|
||||
* Translates a position from the parent coordinate system into this one.
|
||||
*/
|
||||
Position<Self> translate(Position<Parent> position);
|
||||
|
||||
/**
|
||||
* Translates a position from this coordinate system into the parent.
|
||||
*/
|
||||
Position<Parent> translateInverse(Position<Self> position);
|
||||
|
||||
/**
|
||||
* Helper method that converts a position's coordinate system. Should not be used unless you know it's correct.
|
||||
*/
|
||||
static <P1 extends CoordinateSystem<P1, ?>, P2 extends CoordinateSystem<P2, ?>> Position<P1> transmute(
|
||||
Position<P2> pos) {
|
||||
// noinspection unchecked
|
||||
return (Position<P1>) pos;
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package com.gtnewhorizon.structurelib.coords;
|
||||
|
||||
import net.minecraft.util.MathHelper;
|
||||
import net.minecraft.util.Vec3;
|
||||
import net.minecraft.world.ChunkPosition;
|
||||
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.blockpos.BlockPos;
|
||||
import com.gtnewhorizon.structurelib.util.Vec3Impl;
|
||||
|
||||
/**
|
||||
* A {@link BlockPos} with a statically-checked coordinate system.
|
||||
*/
|
||||
public class Position<C extends CoordinateSystem<C, ?>> extends BlockPos {
|
||||
|
||||
public Position() {
|
||||
|
||||
}
|
||||
|
||||
public Position(int x, int y, int z) {
|
||||
super(x, y, z);
|
||||
}
|
||||
|
||||
public Position(float x, float y, float z) {
|
||||
super(MathHelper.floor_float(x), MathHelper.floor_float(y), MathHelper.floor_float(z));
|
||||
}
|
||||
|
||||
public Position(double x, double y, double z) {
|
||||
super(MathHelper.floor_double(x), MathHelper.floor_double(y), MathHelper.floor_double(z));
|
||||
}
|
||||
|
||||
public Position(ChunkPosition chunkPosition) {
|
||||
super(chunkPosition);
|
||||
}
|
||||
|
||||
public Position(Vector3i v) {
|
||||
super(v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
public Position(Vector3f v) {
|
||||
this(v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
public Position(Vec3 v) {
|
||||
this(v.xCoord, v.yCoord, v.zCoord);
|
||||
}
|
||||
|
||||
public Position(Vec3Impl v) {
|
||||
this(v.get0(), v.get1(), v.get2());
|
||||
}
|
||||
|
||||
public Position<C> copy() {
|
||||
return new Position<>(x, y, z);
|
||||
}
|
||||
|
||||
public Vector3i toVector3i() {
|
||||
return new Vector3i(x, y, z);
|
||||
}
|
||||
|
||||
public Vector3f toVector3f() {
|
||||
return new Vector3f(x, y, z);
|
||||
}
|
||||
|
||||
public Vec3 toVec3() {
|
||||
return Vec3.createVectorHelper(x, y, z);
|
||||
}
|
||||
|
||||
public Vec3Impl toVec3Impl() {
|
||||
return new Vec3Impl(x, y, z);
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package com.gtnewhorizon.structurelib.coords;
|
||||
|
||||
/// The structure definition-local coords. 0,0,0 is the front-most left-most top-most element. Coordinates increase as
|
||||
/// you go down, right, or back.
|
||||
public class StructureDefinitionCoords implements CoordinateSystem<StructureDefinitionCoords, StructureRelativeCoords> {
|
||||
|
||||
public final int offsetX, offsetY, offsetZ;
|
||||
|
||||
public StructureDefinitionCoords(int offsetX, int offsetY, int offsetZ) {
|
||||
this.offsetX = offsetX;
|
||||
this.offsetY = offsetY;
|
||||
this.offsetZ = offsetZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position<StructureDefinitionCoords> translate(Position<StructureRelativeCoords> position) {
|
||||
return translate(position, offsetX, offsetY, offsetZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position<StructureRelativeCoords> translateInverse(Position<StructureDefinitionCoords> position) {
|
||||
return translateInverse(position, offsetX, offsetY, offsetZ);
|
||||
}
|
||||
|
||||
public static Position<StructureDefinitionCoords> translate(Position<StructureRelativeCoords> position, int offsetX,
|
||||
int offsetY, int offsetZ) {
|
||||
position.add(offsetX, offsetY, offsetZ);
|
||||
|
||||
return CoordinateSystem.transmute(position);
|
||||
}
|
||||
|
||||
public static Position<StructureRelativeCoords> translateInverse(Position<StructureDefinitionCoords> position,
|
||||
int offsetX, int offsetY, int offsetZ) {
|
||||
position.sub(offsetX, offsetY, offsetZ);
|
||||
|
||||
return CoordinateSystem.transmute(position);
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package com.gtnewhorizon.structurelib.coords;
|
||||
|
||||
import com.gtnewhorizon.structurelib.alignment.IntegerAxisSwap;
|
||||
|
||||
/**
|
||||
* Coordinates relative to the structure. 0,0,0 is the controller. 0,0,1 is the block immediately behind the controller
|
||||
* (looking at it from its face). 1,0,0 is the block to the right of the controller. 0,1,0 is the block below the
|
||||
* controller.
|
||||
*/
|
||||
public class StructureRelativeCoords implements CoordinateSystem<StructureRelativeCoords, ControllerRelativeCoords> {
|
||||
|
||||
public final IntegerAxisSwap axisSwap;
|
||||
|
||||
public StructureRelativeCoords(IntegerAxisSwap axisSwap) {
|
||||
this.axisSwap = axisSwap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position<StructureRelativeCoords> translate(Position<ControllerRelativeCoords> position) {
|
||||
position.set(axisSwap.translate(position));
|
||||
|
||||
return CoordinateSystem.transmute(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position<ControllerRelativeCoords> translateInverse(Position<StructureRelativeCoords> position) {
|
||||
position.set(axisSwap.inverseTranslate(position));
|
||||
|
||||
return CoordinateSystem.transmute(position);
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package com.gtnewhorizon.structurelib.coords;
|
||||
|
||||
/**
|
||||
* World block coordinates
|
||||
*/
|
||||
public class WorldCoords implements CoordinateSystem<WorldCoords, WorldCoords> {
|
||||
|
||||
public static final WorldCoords INSTANCE = new WorldCoords();
|
||||
|
||||
private WorldCoords() {}
|
||||
|
||||
@Override
|
||||
public Position<WorldCoords> translate(Position<WorldCoords> position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position<WorldCoords> translateInverse(Position<WorldCoords> position) {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,8 @@ package com.gtnewhorizon.structurelib.structure;
|
||||
import static com.gtnewhorizon.structurelib.structure.IStructureWalker.ignoreBlockUnloaded;
|
||||
import static com.gtnewhorizon.structurelib.structure.IStructureWalker.skipBlockUnloaded;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
@@ -18,8 +15,6 @@ import com.gtnewhorizon.structurelib.StructureLibAPI;
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
import com.gtnewhorizon.structurelib.coords.Position;
|
||||
import com.gtnewhorizon.structurelib.coords.StructureDefinitionCoords;
|
||||
|
||||
/**
|
||||
* This is the structure definition of your multi. You will have one of these for each multi.
|
||||
@@ -340,49 +335,6 @@ public interface IStructureDefinition<T> {
|
||||
"customIterate");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the controller position for a piece. Controller positions are denoted by a tilde.
|
||||
*
|
||||
* @param piece The piece name
|
||||
* @return The controller position, or null if it doesn't exist for the given piece
|
||||
*/
|
||||
Position<StructureDefinitionCoords> getControllerPosition(String piece);
|
||||
|
||||
/**
|
||||
* Gets the coordinate system for a piece. This is used to translate structure definition coordinates into structure
|
||||
* relative coordinates.
|
||||
*
|
||||
* @param piece The piece
|
||||
* @return The coordinate system
|
||||
* @throws IllegalArgumentException When the piece does not exist
|
||||
*/
|
||||
StructureDefinitionCoords getCoordinateSystem(String piece);
|
||||
|
||||
/**
|
||||
* Gets a socket from a piece.
|
||||
*
|
||||
* @param piece The piece name
|
||||
* @param socket The socket character
|
||||
* @return The socket's position
|
||||
* @throws IllegalArgumentException When the piece does not exist
|
||||
* @throws IllegalArgumentException When the socket does not exist
|
||||
* @throws IllegalStateException When more than one socket exists with this character
|
||||
* @see StructureDefinition.Builder#addSocket(char, char)
|
||||
*/
|
||||
Position<StructureDefinitionCoords> getSocket(String piece, char socket);
|
||||
|
||||
/**
|
||||
* Gets all sockets with a given character for a piece.
|
||||
*
|
||||
* @param piece The piece name
|
||||
* @param socket The socket character
|
||||
* @return The list of socket positions, or an empty list if the socket is not present
|
||||
* @throws IllegalArgumentException When the piece does not exist
|
||||
* @see StructureDefinition.Builder#addSocket(char, char)
|
||||
*/
|
||||
@Nonnull
|
||||
List<Position<StructureDefinitionCoords>> getAllSockets(String piece, char socket);
|
||||
|
||||
/**
|
||||
* Low level utility.
|
||||
*
|
||||
|
||||
@@ -5,6 +5,7 @@ import static com.gtnewhorizon.structurelib.StructureLib.PANIC_MODE;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@@ -42,6 +43,30 @@ public interface IStructureElement<T> {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a human-readable description of what block(s) this element accepts. Used for diagnostic messages when
|
||||
* structure checks fail (e.g. "expected Input Bus at (1, 2, 3)").
|
||||
* <p>
|
||||
* Unlike {@link #getBlocksToPlace}, which returns the full set of concrete block variants for autoplacing (e.g.
|
||||
* every tier of Input Bus), this method returns <b>category-level</b> descriptions suitable for display to the
|
||||
* player (e.g. just "Input Bus"). It also requires no parameters, no world, trigger item, or environment, making it
|
||||
* usable in contexts where those are unavailable.
|
||||
* <p>
|
||||
* Implementations should return <b>lang keys</b> (e.g. {@code "tile.blockGlass.name"}) rather than pre-translated
|
||||
* display names. The caller is responsible for translating these keys on the client side via
|
||||
* {@code StatCollector.translateToLocal()}. If a key has no matching translation, {@code translateToLocal} returns
|
||||
* the input string unchanged, so pre-translated fallback strings also work.
|
||||
*
|
||||
* @param context the context object (e.g. the multiblock controller), used to determine state-dependent
|
||||
* descriptions
|
||||
* @return a list of accepted block descriptions as lang keys (or display names as fallback), or null if no
|
||||
* description is available
|
||||
*/
|
||||
@Nullable
|
||||
default List<String> getDescription(T context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger);
|
||||
|
||||
boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger);
|
||||
@@ -71,7 +96,7 @@ public interface IStructureElement<T> {
|
||||
"Default implementation of survivalPlaceBlock hit! Things aren't going to work well! IStructureElement class: {}",
|
||||
getClass().getName());
|
||||
if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT;
|
||||
return PlaceResult.SKIP;
|
||||
return PlaceResult.REJECT_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -181,7 +206,7 @@ public interface IStructureElement<T> {
|
||||
if (StructureLibAPI.isDebugEnabled()) LOGGER.info(
|
||||
"Fallback shim code of survivalPlaceBlock hit! Things aren't going to work well! IStructureElement class: {}",
|
||||
getClass().getName());
|
||||
return PlaceResult.SKIP;
|
||||
return PlaceResult.REJECT_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,6 +229,12 @@ public interface IStructureElement<T> {
|
||||
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return IStructureElement.this.spawnHint(t, world, x, y, z, trigger);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> getDescription(T context) {
|
||||
return IStructureElement.this.getDescription(context);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -237,16 +268,21 @@ public interface IStructureElement<T> {
|
||||
|
||||
enum PlaceResult {
|
||||
/**
|
||||
* This element either exists already, or does not yet have an implementation for survivalPlaceBlock, or some
|
||||
* other unforeseen situations. TODO this definition doesn't seem right. Should we separate SKIP from EXISTS, or
|
||||
* SKIP from ERROR?
|
||||
* The element's check passed, the position is already valid. No placement is needed. In the walker: continue
|
||||
* iteration (no error, not counted as placed). In ofChain: short-circuits, remaining alternatives are skipped.
|
||||
*/
|
||||
SKIP,
|
||||
/**
|
||||
* This element's space is occupied by other stuff and require player attention, or player missing required
|
||||
* resource, or the block cannot be placed due to obscure mechanisms, or some other unforeseen situations.
|
||||
* The element cannot handle this position (occupied, missing resources, etc.). In the walker: stop iteration
|
||||
* with error particles. In ofChain: try next alternative.
|
||||
*/
|
||||
REJECT,
|
||||
/**
|
||||
* This element has no survivalPlaceBlock implementation (default fallback), or otherwise cannot place but is
|
||||
* not an error condition. In the walker: continue iteration silently (like SKIP). In ofChain: try next
|
||||
* alternative (like REJECT), so a better-suited element can handle it.
|
||||
*/
|
||||
REJECT_CONTINUE,
|
||||
/**
|
||||
* Autoplace cannot proceed within this tick. To proceed further would require stopping the placement and wait
|
||||
* for next round.
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.gtnewhorizon.structurelib.structure;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@@ -61,6 +63,19 @@ public interface IStructureElementChain<T> extends IStructureElement<T> {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
default List<String> getDescription(T context) {
|
||||
Set<String> descriptions = new LinkedHashSet<>();
|
||||
for (IStructureElement<T> fallback : fallbacks()) {
|
||||
List<String> desc = fallback.getDescription(context);
|
||||
if (desc != null) {
|
||||
descriptions.addAll(desc);
|
||||
}
|
||||
}
|
||||
return descriptions.isEmpty() ? null : new ArrayList<>(descriptions);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
default BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
@@ -82,47 +97,51 @@ public interface IStructureElementChain<T> extends IStructureElement<T> {
|
||||
@Override
|
||||
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s,
|
||||
EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
||||
boolean haveSkip = false;
|
||||
boolean allContinue = true;
|
||||
List<IChatComponent> bufferedNoise = new ArrayList<>();
|
||||
for (IStructureElement<T> fallback : fallbacks()) {
|
||||
PlaceResult result = fallback.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, bufferedNoise::add);
|
||||
switch (result) {
|
||||
case REJECT_CONTINUE:
|
||||
break;
|
||||
case REJECT:
|
||||
allContinue = false;
|
||||
break;
|
||||
case SKIP:
|
||||
haveSkip = true;
|
||||
break;
|
||||
bufferedNoise.forEach(chatter);
|
||||
return PlaceResult.SKIP;
|
||||
default:
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// dump all that noise back into upstream
|
||||
bufferedNoise.forEach(chatter);
|
||||
// TODO need reconsider to ensure this is the right course of action
|
||||
return haveSkip ? PlaceResult.SKIP : PlaceResult.REJECT;
|
||||
return allContinue ? PlaceResult.REJECT_CONTINUE : PlaceResult.REJECT;
|
||||
}
|
||||
|
||||
@Override
|
||||
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
boolean haveSkip = false;
|
||||
boolean allContinue = true;
|
||||
List<IChatComponent> bufferedNoise = new ArrayList<>();
|
||||
for (IStructureElement<T> fallback : fallbacks()) {
|
||||
PlaceResult result = fallback
|
||||
.survivalPlaceBlock(t, world, x, y, z, trigger, env.withChatter(bufferedNoise::add));
|
||||
switch (result) {
|
||||
case REJECT_CONTINUE:
|
||||
break;
|
||||
case REJECT:
|
||||
allContinue = false;
|
||||
break;
|
||||
case SKIP:
|
||||
haveSkip = true;
|
||||
break;
|
||||
bufferedNoise.forEach(env.getChatter());
|
||||
return PlaceResult.SKIP;
|
||||
default:
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// dump all that noise back into upstream
|
||||
bufferedNoise.forEach(env.getChatter());
|
||||
// TODO need reconsider to ensure this is the right course of action
|
||||
return haveSkip ? PlaceResult.SKIP : PlaceResult.REJECT;
|
||||
return allContinue ? PlaceResult.REJECT_CONTINUE : PlaceResult.REJECT;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -27,12 +27,12 @@ public interface IStructureElementCheckOnly<T> extends IStructureElement<T> {
|
||||
@Override
|
||||
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s,
|
||||
EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
||||
return PlaceResult.SKIP;
|
||||
return PlaceResult.REJECT_CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
return PlaceResult.SKIP;
|
||||
return PlaceResult.REJECT_CONTINUE;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizon.structurelib.structure;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -65,4 +66,10 @@ class LazyStructureElement<T> implements IStructureElementDeferred<T> {
|
||||
AutoPlaceEnvironment env) {
|
||||
return get(t).survivalPlaceBlock(t, world, x, y, z, trigger, env);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> getDescription(T context) {
|
||||
return elem != null ? elem.getDescription(context) : null;
|
||||
}
|
||||
}
|
||||
@@ -4,49 +4,33 @@ import static com.gtnewhorizon.structurelib.structure.StructureUtility.isAir;
|
||||
import static com.gtnewhorizon.structurelib.structure.StructureUtility.notAir;
|
||||
import static com.gtnewhorizon.structurelib.structure.StructureUtility.step;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import com.google.common.collect.MultimapBuilder;
|
||||
import com.gtnewhorizon.structurelib.coords.Position;
|
||||
import com.gtnewhorizon.structurelib.coords.StructureDefinitionCoords;
|
||||
import com.gtnewhorizon.structurelib.util.Vec3Impl;
|
||||
|
||||
import it.unimi.dsi.fastutil.chars.Char2CharOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.chars.CharOpenHashSet;
|
||||
|
||||
public class StructureDefinition<T> implements IStructureDefinition<T> {
|
||||
|
||||
private final Map<Character, IStructureElement<T>> elements;
|
||||
private final Map<String, String> shapes;
|
||||
private final Map<String, IStructureElement<T>[]> structures;
|
||||
private final Map<String, Set<Vec3Impl>> occupiedSpaces;
|
||||
private final Map<String, Position<StructureDefinitionCoords>> controllers;
|
||||
private final Map<String, ListMultimap<Character, Position<StructureDefinitionCoords>>> socketLocations;
|
||||
|
||||
public static <B> Builder<B> builder() {
|
||||
return new Builder<>();
|
||||
}
|
||||
|
||||
private StructureDefinition(Map<Character, IStructureElement<T>> elements, Map<String, String> shapes,
|
||||
Map<String, IStructureElement<T>[]> structures, Map<String, Set<Vec3Impl>> occupiedSpaces,
|
||||
Map<String, Position<StructureDefinitionCoords>> controllers,
|
||||
Map<String, ListMultimap<Character, Position<StructureDefinitionCoords>>> socketLocations) {
|
||||
Map<String, IStructureElement<T>[]> structures, Map<String, Set<Vec3Impl>> occupiedSpaces) {
|
||||
this.elements = elements;
|
||||
this.shapes = shapes;
|
||||
this.structures = structures;
|
||||
this.occupiedSpaces = occupiedSpaces;
|
||||
this.controllers = controllers;
|
||||
this.socketLocations = socketLocations;
|
||||
}
|
||||
|
||||
public static class Builder<T> {
|
||||
@@ -57,18 +41,12 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
|
||||
private char d = '\uD000';
|
||||
private final Map<Vec3Impl, Character> navigates;
|
||||
private final Map<Character, IStructureElement<T>> elements;
|
||||
private final Map<String, String[][]> uncompiledShapes;
|
||||
private final Map<String, String> shapes;
|
||||
private final Map<String, Set<Vec3Impl>> occupiedSpaces;
|
||||
|
||||
private final Map<String, Position<StructureDefinitionCoords>> controllers = new HashMap<>();
|
||||
private final Char2CharOpenHashMap socketTranslations = new Char2CharOpenHashMap();
|
||||
private final Map<String, ListMultimap<Character, Position<StructureDefinitionCoords>>> socketLocations = new HashMap<>();
|
||||
|
||||
private Builder() {
|
||||
navigates = new HashMap<>();
|
||||
elements = new HashMap<>();
|
||||
uncompiledShapes = new HashMap<>();
|
||||
shapes = new HashMap<>();
|
||||
occupiedSpaces = new HashMap<>();
|
||||
}
|
||||
@@ -104,69 +82,7 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
|
||||
* @return this builder
|
||||
*/
|
||||
public Builder<T> addShape(String name, String[][] structurePiece) {
|
||||
uncompiledShapes.put(name, structurePiece);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use the unboxed version
|
||||
*/
|
||||
@Deprecated
|
||||
public Builder<T> addElement(Character name, IStructureElement<T> structurePiece) {
|
||||
elements.putIfAbsent(name, structurePiece);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> addElement(char name, IStructureElement<T> structurePiece) {
|
||||
elements.putIfAbsent(name, structurePiece);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Denotes {@code name} as a socket. Sockets are virtual structure elements that record their position in the
|
||||
* structure for future retrieval. They are transformed into real structure elements and do not need their own
|
||||
* element (see {@code replacement}).
|
||||
*
|
||||
* @param name The socket name character
|
||||
* @param replacement The replacement character
|
||||
* @see IStructureDefinition#getSocket(String, char)
|
||||
* @see IStructureDefinition#getAllSockets(String, char)
|
||||
*/
|
||||
public Builder<T> addSocket(char name, char replacement) {
|
||||
socketTranslations.put(name, replacement);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #addSocket(char, char)} but without the replacement. The structure must have a structure element
|
||||
* with the given character.
|
||||
*
|
||||
* @param name The socket name character
|
||||
*/
|
||||
public Builder<T> addSocket(char name) {
|
||||
// Adding a name -> name entry like this isn't ideal but this is the cleanest solution
|
||||
socketTranslations.put(name, name);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IStructureDefinition<T> build() {
|
||||
uncompiledShapes.forEach(this::compileShape);
|
||||
|
||||
Map<String, IStructureElement<T>[]> structures = compileStructureMap();
|
||||
|
||||
return new StructureDefinition<>(
|
||||
new HashMap<>(elements),
|
||||
new HashMap<>(shapes),
|
||||
structures,
|
||||
occupiedSpaces,
|
||||
controllers,
|
||||
socketLocations);
|
||||
}
|
||||
|
||||
private Builder<T> compileShape(String name, String[][] structurePiece) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
if (structurePiece.length > 0) {
|
||||
for (String[] strings : structurePiece) {
|
||||
if (strings.length > 0) {
|
||||
@@ -179,11 +95,6 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
|
||||
}
|
||||
builder.setLength(builder.length() - 1);
|
||||
}
|
||||
|
||||
// Always create a map for this piece, even if it doesn't have any sockets
|
||||
// noinspection UnstableApiUsage
|
||||
socketLocations.put(name, MultimapBuilder.hashKeys().arrayListValues().build());
|
||||
|
||||
Set<Vec3Impl> occupiedSpace = new HashSet<>();
|
||||
// these track the global current location
|
||||
int aa = 0, bb = 0, cc = 0;
|
||||
@@ -192,15 +103,6 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
|
||||
// I do know these are pretty bad variable naming, but I have no better idea
|
||||
for (int i = 0; i < builder.length(); i++) {
|
||||
char ch = builder.charAt(i);
|
||||
|
||||
// Translate the element char if it's a socket, and record the socket position
|
||||
if (socketTranslations.containsKey(ch)) {
|
||||
socketLocations.get(name).put(ch, new Position<>(aa, bb, cc));
|
||||
|
||||
ch = socketTranslations.get(ch);
|
||||
builder.setCharAt(i, ch);
|
||||
}
|
||||
|
||||
if (ch == ' ') {
|
||||
builder.setCharAt(i, A);
|
||||
ch = A;
|
||||
@@ -208,7 +110,6 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
|
||||
builder.setCharAt(i, A);
|
||||
ch = A;
|
||||
occupiedSpace.add(Vec3Impl.getFromPool(aa, bb, cc));
|
||||
controllers.put(name, new Position<>(aa, bb, cc));
|
||||
}
|
||||
if (ch == A) {
|
||||
aa++;
|
||||
@@ -276,58 +177,64 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, IStructureElement<T>[]> compileStructureMap() {
|
||||
CharOpenHashSet missing = findMissing();
|
||||
|
||||
if (!missing.isEmpty()) {
|
||||
throw new RuntimeException("Missing Structure Element bindings for: " + missing);
|
||||
}
|
||||
|
||||
Map<String, IStructureElement<T>[]> map = new HashMap<>();
|
||||
|
||||
for (Entry<String, String> e : shapes.entrySet()) {
|
||||
char[] chars = e.getValue().toCharArray();
|
||||
IStructureElement<T>[] compiled = new IStructureElement[chars.length];
|
||||
|
||||
int a = 0, b = 0, c = 0;
|
||||
|
||||
for (int i = 0; i < chars.length; i++) {
|
||||
char ch = chars[i];
|
||||
|
||||
IStructureElement<T> element = this.elements.get(ch);
|
||||
|
||||
compiled[i] = element;
|
||||
|
||||
if (element.isNavigating()) {
|
||||
a = (element.resetA() ? 0 : a) + element.getStepA();
|
||||
b = (element.resetB() ? 0 : b) + element.getStepB();
|
||||
c = (element.resetC() ? 0 : c) + element.getStepC();
|
||||
} else {
|
||||
a++;
|
||||
}
|
||||
}
|
||||
|
||||
if (map.put(e.getKey(), compiled) != null) {
|
||||
throw new IllegalStateException("Duplicate shape: " + e.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
/**
|
||||
* @deprecated use the unboxed version
|
||||
*/
|
||||
@Deprecated
|
||||
public Builder<T> addElement(Character name, IStructureElement<T> structurePiece) {
|
||||
elements.putIfAbsent(name, structurePiece);
|
||||
return this;
|
||||
}
|
||||
|
||||
private CharOpenHashSet findMissing() {
|
||||
CharOpenHashSet missing = new CharOpenHashSet();
|
||||
public Builder<T> addElement(char name, IStructureElement<T> structurePiece) {
|
||||
elements.putIfAbsent(name, structurePiece);
|
||||
return this;
|
||||
}
|
||||
|
||||
for (String shape : shapes.values()) {
|
||||
for (char c : shape.toCharArray()) {
|
||||
if (!elements.containsKey(c)) {
|
||||
missing.add(c);
|
||||
}
|
||||
}
|
||||
public IStructureDefinition<T> build() {
|
||||
Map<String, IStructureElement<T>[]> structures = compileStructureMap();
|
||||
return new StructureDefinition<>(
|
||||
new HashMap<>(elements),
|
||||
new HashMap<>(shapes),
|
||||
structures,
|
||||
occupiedSpaces);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, IStructureElement<T>[]> compileElementSetMap() {
|
||||
Set<Integer> missing = findMissing();
|
||||
if (missing.isEmpty()) {
|
||||
return shapes.entrySet().stream().collect(
|
||||
Collectors.toMap(
|
||||
Map.Entry::getKey,
|
||||
e -> e.getValue().chars().mapToObj(c -> (char) c).distinct().map(elements::get)
|
||||
.toArray(IStructureElement[]::new)));
|
||||
} else {
|
||||
throw new RuntimeException(
|
||||
"Missing Structure Element bindings for (chars as integers): "
|
||||
+ Arrays.toString(missing.toArray()));
|
||||
}
|
||||
}
|
||||
|
||||
return missing;
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, IStructureElement<T>[]> compileStructureMap() {
|
||||
Set<Integer> missing = findMissing();
|
||||
if (missing.isEmpty()) {
|
||||
return shapes.entrySet().stream().collect(
|
||||
Collectors.toMap(
|
||||
Map.Entry::getKey,
|
||||
e -> e.getValue().chars().mapToObj(c -> elements.get((char) c))
|
||||
.toArray(IStructureElement[]::new)));
|
||||
} else {
|
||||
throw new RuntimeException(
|
||||
"Missing Structure Element bindings for (chars as integers): "
|
||||
+ Arrays.toString(missing.toArray()));
|
||||
}
|
||||
}
|
||||
|
||||
private Set<Integer> findMissing() {
|
||||
return shapes.values().stream().flatMapToInt(CharSequence::chars)
|
||||
.filter(i -> !elements.containsKey((char) i)).boxed().collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,61 +263,4 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
|
||||
if (occupiedSpace == null) throw new NoSuchElementException(name);
|
||||
return occupiedSpace.contains(new Vec3Impl(offsetA, offsetB, offsetC));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position<StructureDefinitionCoords> getControllerPosition(String piece) {
|
||||
return controllers.get(piece);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinitionCoords getCoordinateSystem(String piece) {
|
||||
var controller = controllers.get(piece);
|
||||
|
||||
if (controller == null) {
|
||||
throw new IllegalArgumentException("Invalid piece: " + piece);
|
||||
}
|
||||
|
||||
return new StructureDefinitionCoords(controller.x, controller.y, controller.z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position<StructureDefinitionCoords> getSocket(String piece, char socket) {
|
||||
var piecePositions = socketLocations.get(piece);
|
||||
|
||||
if (piecePositions == null) {
|
||||
throw new IllegalArgumentException("Invalid piece: " + piece);
|
||||
}
|
||||
|
||||
var positions = piecePositions.get(socket);
|
||||
|
||||
if (positions.isEmpty()) {
|
||||
throw new IllegalArgumentException("Socket " + socket + " for piece " + piece + " does not exist");
|
||||
}
|
||||
|
||||
if (positions.size() > 1) {
|
||||
throw new IllegalStateException(
|
||||
"Socket " + socket
|
||||
+ " for piece "
|
||||
+ piece
|
||||
+ " has several positions, but expected one and only one");
|
||||
}
|
||||
|
||||
return positions.get(0).copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public List<Position<StructureDefinitionCoords>> getAllSockets(String piece, char socket) {
|
||||
var piecePositions = socketLocations.get(piece);
|
||||
|
||||
if (piecePositions == null) {
|
||||
throw new IllegalArgumentException("Invalid piece: " + piece);
|
||||
}
|
||||
|
||||
var list = new ArrayList<>(piecePositions.get(socket));
|
||||
|
||||
list.replaceAll(Position::copy);
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -707,6 +707,29 @@ public class StructureUtility {
|
||||
public static <T, TIER> IStructureElement<T> ofBlocksTiered(ITierConverter<TIER> tierExtractor,
|
||||
@Nullable List<Pair<Block, Integer>> allKnownTiers, @Nullable TIER notSet, BiConsumer<T, TIER> setter,
|
||||
Function<T, TIER> getter) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #ofBlocksTiered(ITierConverter, List, Object, BiConsumer, Function)}, but with an explicit
|
||||
* description that will be returned by {@link IStructureElement#getDescription(Object)}.
|
||||
*
|
||||
* @param description the description lang keys to attach to this element, or null for no description
|
||||
* @see #ofBlocksTiered(ITierConverter, List, Object, BiConsumer, Function)
|
||||
*/
|
||||
public static <T, TIER> IStructureElement<T> ofBlocksTiered(ITierConverter<TIER> tierExtractor,
|
||||
@Nullable List<Pair<Block, Integer>> allKnownTiers, @Nullable TIER notSet, BiConsumer<T, TIER> setter,
|
||||
Function<T, TIER> getter, @Nullable List<String> description) {
|
||||
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);
|
||||
@@ -768,7 +791,7 @@ public class StructureUtility {
|
||||
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
Pair<Block, Integer> hint = getHint(trigger);
|
||||
if (hint == null) return PlaceResult.REJECT; // TODO or SKIP?
|
||||
if (hint == null) return PlaceResult.REJECT;
|
||||
Block block = world.getBlock(x, y, z);
|
||||
int meta = world.getBlockMetadata(x, y, z);
|
||||
TIER tier = tierExtractor.convert(block, meta);
|
||||
@@ -785,6 +808,24 @@ public class StructureUtility {
|
||||
env.getActor(),
|
||||
env.getChatter());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> getDescription(T context) {
|
||||
TIER currentTier = getter.apply(context);
|
||||
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");
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1408,6 +1449,14 @@ public class StructureUtility {
|
||||
AutoPlaceEnvironment env) {
|
||||
return BlocksToPlace.create(block, meta);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> getDescription(T context) {
|
||||
Item item = Item.getItemFromBlock(block);
|
||||
if (item == null) return null;
|
||||
return Collections.singletonList(new ItemStack(item, 1, meta).getUnlocalizedName() + ".name");
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return new IStructureElement<T>() {
|
||||
@@ -1487,6 +1536,14 @@ public class StructureUtility {
|
||||
AutoPlaceEnvironment env) {
|
||||
return BlocksToPlace.create(block, meta);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> getDescription(T context) {
|
||||
Item item = Item.getItemFromBlock(block);
|
||||
if (item == null) return null;
|
||||
return Collections.singletonList(new ItemStack(item, 1, meta).getUnlocalizedName() + ".name");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1529,6 +1586,14 @@ public class StructureUtility {
|
||||
// there is getSubItems on ItemBlock, but it's client only
|
||||
return BlocksToPlace.create(defaultBlock, defaultMeta);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> getDescription(T context) {
|
||||
Item item = Item.getItemFromBlock(block);
|
||||
if (item == null) return null;
|
||||
return Collections.singletonList(new ItemStack(item, 1, 0).getUnlocalizedName() + ".name");
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return new IStructureElement<T>() {
|
||||
@@ -1561,6 +1626,14 @@ public class StructureUtility {
|
||||
// there is getSubItems on ItemBlock, but it's client only
|
||||
return BlocksToPlace.create(defaultBlock, defaultMeta);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> getDescription(T context) {
|
||||
Item item = Item.getItemFromBlock(block);
|
||||
if (item == null) return null;
|
||||
return Collections.singletonList(new ItemStack(item, 1, 0).getUnlocalizedName() + ".name");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1824,6 +1897,71 @@ public class StructureUtility {
|
||||
AutoPlaceEnvironment env) {
|
||||
return element.survivalPlaceBlock(t, world, x, y, z, trigger, env);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> getDescription(T context) {
|
||||
return element.getDescription(context);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public static <B extends IStructureElement<T>, T> IStructureElement<T> withDescription(
|
||||
@Nullable List<String> description, B element) {
|
||||
return new IStructureElement<T>() {
|
||||
|
||||
@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
|
||||
public List<String> getDescription(T context) {
|
||||
return description;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1880,6 +2018,12 @@ public class StructureUtility {
|
||||
AutoPlaceEnvironment env) {
|
||||
return element.survivalPlaceBlock(t, world, x, y, z, trigger, env);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> getDescription(T context) {
|
||||
return element.getDescription(context);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1888,11 +2032,11 @@ public class StructureUtility {
|
||||
/**
|
||||
* Enable this structure element only if given predicate returns true.
|
||||
* <p>
|
||||
* Return SKIP when survival auto place if given predicate returns false.
|
||||
* Return REJECT_CONTINUE when survival auto place if given predicate returns false.
|
||||
*/
|
||||
public static <T> IStructureElement<T> onlyIf(Predicate<? super T> predicate,
|
||||
IStructureElement<? super T> downstream) {
|
||||
return onlyIf(predicate, downstream, PlaceResult.SKIP);
|
||||
return onlyIf(predicate, downstream, PlaceResult.REJECT_CONTINUE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1949,6 +2093,12 @@ public class StructureUtility {
|
||||
if (predicate.test(t)) return downstream.survivalPlaceBlock(t, world, x, y, z, trigger, env);
|
||||
return placeResultWhenDisabled;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> getDescription(T context) {
|
||||
return downstream.getDescription(context);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1961,10 +2111,10 @@ public class StructureUtility {
|
||||
* 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>
|
||||
* (*): For survival auto place, it will * REJECT, if all structure element REJECT * SKIP, if 1 or more structure
|
||||
* element SKIP and the rest structure element (0 or more) REJECT * any other result, **immediately** upon any
|
||||
* structure element returns these other results. This behavior is not 100% fixed and might change later on, but we
|
||||
* will send the notice on a best effort basis.
|
||||
* (*): 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.
|
||||
* <p>
|
||||
* 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.
|
||||
@@ -2046,6 +2196,12 @@ public class StructureUtility {
|
||||
AutoPlaceEnvironment env) {
|
||||
return elem.survivalPlaceBlock(t.getCurrentContext(), world, x, y, z, trigger, env);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> getDescription(T context) {
|
||||
return elem.getDescription(context.getCurrentContext());
|
||||
}
|
||||
};
|
||||
}
|
||||
// endregion
|
||||
@@ -2883,6 +3039,12 @@ public class StructureUtility {
|
||||
warnNoExplicitSubChannel(env.getActor());
|
||||
return backing.survivalPlaceBlock(t, world, x, y, z, newTrigger, env);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> getDescription(T context) {
|
||||
return backing.getDescription(context);
|
||||
}
|
||||
};
|
||||
}
|
||||
// endregion
|
||||
|
||||
+2
-1
@@ -43,9 +43,10 @@ class SurvivalBuildStructureWalker<T> implements IStructureWalker<T> {
|
||||
env.offsetABC[2] = c;
|
||||
env.setSource(params.getSource());
|
||||
PlaceResult placeResult = element.survivalPlaceBlock(object, world, x, y, z, trigger, env);
|
||||
if (placeResult != PlaceResult.SKIP && built == -1) built = 0;
|
||||
if (placeResult != PlaceResult.SKIP && placeResult != PlaceResult.REJECT_CONTINUE && built == -1) built = 0;
|
||||
switch (placeResult) {
|
||||
case SKIP:
|
||||
case REJECT_CONTINUE:
|
||||
return true;
|
||||
case ACCEPT:
|
||||
if (check) element.check(object, world, x, y, z);
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
package com.gtnewhorizon.structurelib.coords;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
|
||||
import com.gtnewhorizon.structurelib.structure.StructureUtility;
|
||||
|
||||
// spotless:off
|
||||
class CoordinateTests {
|
||||
|
||||
private IStructureDefinition<?> getDefinition() {
|
||||
String[][] main = {
|
||||
{ " ", " ", " ", " ", " " },
|
||||
{ " ", " ", " ", " ", " " },
|
||||
{ " ", " ", " X ", " ", " " },
|
||||
{ " ", " ", " YY ", " ", " " },
|
||||
{ " ~ ", " ", " ", " ", " " }
|
||||
};
|
||||
|
||||
return IStructureDefinition.builder()
|
||||
.addShape("main", StructureUtility.transpose(main))
|
||||
.addSocket('X', ' ')
|
||||
.addSocket('Y', ' ')
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSocketAndCoordSystemTranslationToWorld() {
|
||||
IStructureDefinition<?> def = getDefinition();
|
||||
|
||||
// Note: the position is the same object between all of these transforms, it is never copied, its generic just
|
||||
// changes
|
||||
|
||||
var p = def.getSocket("main", 'X');
|
||||
Assertions.assertEquals(new Position<>(2, 2, 2), p);
|
||||
|
||||
var p2 = def.getCoordinateSystem("main").translateInverse(p);
|
||||
Assertions.assertEquals(new Position<>(0, -2, 2), p2);
|
||||
|
||||
var p3 = ExtendedFacing.NORTH_NORMAL_NONE.asCoordinateSystem().translateInverse(p2);
|
||||
Assertions.assertEquals(new Position<>(0, 2, 2), p3);
|
||||
|
||||
var p4 = ControllerRelativeCoords.translateInverse(p3, 5, 5, 5);
|
||||
Assertions.assertEquals(new Position<>(5, 7, 7), p4);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSocketAndCoordSystemTranslationToStructureDef() {
|
||||
IStructureDefinition<?> def = getDefinition();
|
||||
|
||||
// Note: the position is the same object between all of these transforms, it is never copied, its generic just
|
||||
// changes
|
||||
|
||||
var p = new Position<WorldCoords>(5, 7, 7);
|
||||
|
||||
var p2 = ControllerRelativeCoords.translate(p, 5, 5, 5);
|
||||
Assertions.assertEquals(new Position<>(0, 2, 2), p2);
|
||||
|
||||
var p3 = ExtendedFacing.NORTH_NORMAL_NONE.asCoordinateSystem().translate(p2);
|
||||
Assertions.assertEquals(new Position<>(0, -2, 2), p3);
|
||||
|
||||
var p4 = def.getCoordinateSystem("main").translate(p3);
|
||||
Assertions.assertEquals(new Position<>(2, 2, 2), p4);
|
||||
|
||||
Assertions.assertEquals(def.getSocket("main", 'X'), p4);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSocketGetters() {
|
||||
IStructureDefinition<?> def = getDefinition();
|
||||
|
||||
Assertions.assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> def.getSocket("invalid", 'X'));
|
||||
Assertions.assertThrows(
|
||||
IllegalStateException.class,
|
||||
() -> def.getSocket("main", 'Y'));
|
||||
Assertions.assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> def.getSocket("main", 'Z'));
|
||||
|
||||
Assertions.assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> def.getAllSockets("invalid", 'X'));
|
||||
|
||||
Assertions.assertEquals(
|
||||
Collections.emptyList(),
|
||||
def.getAllSockets("main", 'Z'));
|
||||
Assertions.assertEquals(
|
||||
Arrays.asList(new Position<>(2, 2, 2)),
|
||||
def.getAllSockets("main", 'X'));
|
||||
Assertions.assertEquals(
|
||||
Arrays.asList(new Position<>(2, 3, 2), new Position<>(3, 3, 2)),
|
||||
def.getAllSockets("main", 'Y'));
|
||||
}
|
||||
}
|
||||
// spotless:on
|
||||
Reference in new issue
Block a user