Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef2fc209f1 | ||
|
|
a05267a566 | ||
|
|
dd3413dcaa | ||
|
|
26b88a5f27 | ||
|
|
87903fead7 | ||
|
|
1166c651d2 | ||
|
|
7f42cc1de8 | ||
|
|
34394ef8a5 | ||
|
|
ff3904ec1a | ||
|
|
c3671609d3 | ||
|
|
7c0d3f4bd6 | ||
|
|
cd4f3617be | ||
|
|
256bc60d35 | ||
|
|
70426ab7b3 | ||
|
|
0030c02e9a | ||
|
|
7382db1bbd | ||
|
|
b669cbecf1 | ||
|
|
6283931667 | ||
|
|
3b3bc4b9ad | ||
|
|
bef4fdd949 | ||
|
|
4a06c8f95b | ||
|
|
241bed1989 | ||
|
|
f5594c25db | ||
|
|
7cda901ae5 |
No files matched your search
@@ -1,7 +1,7 @@
|
||||
javadoc {
|
||||
javadocTool = javaToolchains.javadocToolFor {
|
||||
// Last version with frames, but it also has search
|
||||
languageVersion = JavaLanguageVersion.of(21)
|
||||
languageVersion = JavaLanguageVersion.of(11)
|
||||
vendor = JvmVendorSpec.ADOPTIUM
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ javadoc {
|
||||
overview "src/main/javadoc/overview.html"
|
||||
links 'https://docs.oracle.com/javase/8/docs/api/'
|
||||
links 'https://skmedix.github.io/ForgeJavaDocs/javadoc/forge/1.7.10-10.13.4.1614/'
|
||||
addBooleanOption("-frames", true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
// Add your dependencies here
|
||||
|
||||
dependencies {
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.6.19:dev")
|
||||
implementation("com.github.GTNewHorizons:NotEnoughItems:2.7.38-GTNH:dev")
|
||||
|
||||
compileOnly("com.github.GTNewHorizons:ServerUtilities:2.1.41-pre")
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.7.1:dev")
|
||||
implementation("com.github.GTNewHorizons:NotEnoughItems:2.8.21-GTNH:dev")
|
||||
|
||||
testImplementation(platform('org.junit:junit-bom:5.9.2'))
|
||||
testImplementation('org.junit.jupiter:junit-jupiter')
|
||||
|
||||
// runtimeOnlyNonPublishable("com.github.GTNewHorizons:GT5-Unofficial:5.09.51.234:dev") {
|
||||
// exclude group: "com.github.GTNewHorizons", module: "StructureLib"
|
||||
// }
|
||||
}
|
||||
@@ -37,7 +37,7 @@ remoteMappings = https\://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/co
|
||||
|
||||
# Select a default username for testing your mod. You can always override this per-run by running
|
||||
# `./gradlew runClient --username=AnotherPlayer`, or configuring this command in your IDE.
|
||||
developmentEnvironmentUserName = "Developer"
|
||||
developmentEnvironmentUserName = Developer
|
||||
|
||||
# Enables using modern Java syntax (up to version 17) via Jabel, while still targeting JVM 8.
|
||||
# See https://github.com/bsideup/jabel for details on how this works.
|
||||
@@ -96,7 +96,9 @@ usesMixinDebug = false
|
||||
# Specify the location of your implementation of IMixinConfigPlugin. Leave it empty otherwise.
|
||||
mixinPlugin =
|
||||
|
||||
# Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail!
|
||||
# Specify the package that contains all of your Mixins. The package must exist or
|
||||
# the build will fail. If you have a package property defined in your mixins.<modid>.json,
|
||||
# it must match with this or the build will fail.
|
||||
mixinsPackage = mixins
|
||||
|
||||
# Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin!
|
||||
@@ -151,7 +153,7 @@ modrinthProjectId =
|
||||
# type can be one of [project, version],
|
||||
# and the name is the Modrinth project or version slug/id of the other mod.
|
||||
# Example: required-project:fplib;optional-project:gasstation;incompatible-project:gregtech
|
||||
# Note: GTNH Mixins is automatically set as a required dependency if usesMixins = true
|
||||
# Note: UniMixins is automatically set as a required dependency if usesMixins = true.
|
||||
modrinthRelations =
|
||||
|
||||
# Publishing to CurseForge requires you to set the CURSEFORGE_TOKEN environment variable to one of your CurseForge API tokens.
|
||||
|
||||
@@ -17,7 +17,7 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.38'
|
||||
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.47'
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.culling.Frustrum;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.client.settings.KeyBinding;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
@@ -43,23 +42,17 @@ import org.lwjgl.opengl.GL11;
|
||||
import com.gtnewhorizon.structurelib.entity.fx.WeightlessParticleFX;
|
||||
import com.gtnewhorizon.structurelib.net.RegistryOrderSyncMessage;
|
||||
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
|
||||
import com.gtnewhorizon.structurelib.net.StructureProjectorCycleModeMessage;
|
||||
|
||||
import cpw.mods.fml.client.config.GuiConfig;
|
||||
import cpw.mods.fml.client.event.ConfigChangedEvent;
|
||||
import cpw.mods.fml.client.registry.ClientRegistry;
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
import cpw.mods.fml.common.event.FMLInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import cpw.mods.fml.common.eventhandler.EventPriority;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import cpw.mods.fml.common.gameevent.InputEvent;
|
||||
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent;
|
||||
import cpw.mods.fml.common.gameevent.TickEvent.Phase;
|
||||
import cpw.mods.fml.common.network.FMLNetworkEvent;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
public class ClientProxy extends CommonProxy {
|
||||
|
||||
@@ -266,13 +259,6 @@ public class ClientProxy extends CommonProxy {
|
||||
MinecraftForge.EVENT_BUS.register(new ForgeEventHandler());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FMLInitializationEvent e) {
|
||||
super.init(e);
|
||||
|
||||
MinecraftForge.EVENT_BUS.register(new KeybindHandler());
|
||||
}
|
||||
|
||||
static void markTextureUsed(IIcon icon) {
|
||||
if (StructureLib.COMPAT instanceof IStructureCompat)
|
||||
((IStructureCompat) StructureLib.COMPAT).markTextureUsed(icon);
|
||||
@@ -668,30 +654,4 @@ public class ClientProxy extends CommonProxy {
|
||||
p.endSection();
|
||||
}
|
||||
}
|
||||
|
||||
public static class KeybindHandler {
|
||||
|
||||
private static final KeyBinding CYCLE_PROJECTOR_MODE = new KeyBinding(
|
||||
"item.structurelib.constructableTrigger.keys.cycleMode",
|
||||
0,
|
||||
"item.structurelib.constructableTrigger.keyCategory");
|
||||
|
||||
public KeybindHandler() {
|
||||
FMLCommonHandler.instance().bus().register(this);
|
||||
// If somebody wants to make a UI element for this, be my guest
|
||||
ClientRegistry.registerKeyBinding(CYCLE_PROJECTOR_MODE);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@SubscribeEvent
|
||||
public void key(InputEvent.KeyInputEvent event) {
|
||||
if (CYCLE_PROJECTOR_MODE.isPressed()) {
|
||||
cycleProjectorMode();
|
||||
}
|
||||
}
|
||||
|
||||
private static void cycleProjectorMode() {
|
||||
StructureLib.net.sendToServer(new StructureProjectorCycleModeMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@ import net.minecraftforge.common.MinecraftForge;
|
||||
import com.gtnewhorizon.structurelib.net.ErrorHintParticleMessage;
|
||||
import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
|
||||
|
||||
import cpw.mods.fml.common.event.FMLInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
@@ -65,8 +64,6 @@ public class CommonProxy {
|
||||
|
||||
public void preInit(FMLPreInitializationEvent e) {}
|
||||
|
||||
public void init(FMLInitializationEvent e) {}
|
||||
|
||||
public long getOverworldTime() {
|
||||
return MinecraftServer.getServer().getEntityWorld().getTotalWorldTime();
|
||||
}
|
||||
|
||||
@@ -27,14 +27,12 @@ import com.gtnewhorizon.structurelib.net.AlignmentMessage;
|
||||
import com.gtnewhorizon.structurelib.net.ErrorHintParticleMessage;
|
||||
import com.gtnewhorizon.structurelib.net.RegistryOrderSyncMessage;
|
||||
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
|
||||
import com.gtnewhorizon.structurelib.net.StructureProjectorCycleModeMessage;
|
||||
import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
|
||||
import com.gtnewhorizon.structurelib.util.InventoryUtility;
|
||||
import com.gtnewhorizon.structurelib.util.XSTR;
|
||||
|
||||
import cpw.mods.fml.common.Mod;
|
||||
import cpw.mods.fml.common.SidedProxy;
|
||||
import cpw.mods.fml.common.event.FMLInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLInterModComms;
|
||||
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
@@ -79,11 +77,6 @@ public class StructureLib {
|
||||
net.registerMessage(SetChannelDataMessage.Handler.class, SetChannelDataMessage.class, 3, Side.SERVER);
|
||||
net.registerMessage(ErrorHintParticleMessage.Handler.class, ErrorHintParticleMessage.class, 4, Side.CLIENT);
|
||||
net.registerMessage(RegistryOrderSyncMessage.Handler.class, RegistryOrderSyncMessage.class, 5, Side.SERVER);
|
||||
net.registerMessage(
|
||||
StructureProjectorCycleModeMessage.Handler.class,
|
||||
StructureProjectorCycleModeMessage.class,
|
||||
6,
|
||||
Side.SERVER);
|
||||
|
||||
try {
|
||||
DEBUG_MODE = Boolean.parseBoolean(System.getProperty("structurelib.debug"));
|
||||
@@ -135,11 +128,6 @@ public class StructureLib {
|
||||
DevelopHelper.onPreInit();
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
public void init(FMLInitializationEvent e) {
|
||||
proxy.init(e);
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
public void loadComplete(FMLLoadCompleteEvent e) {
|
||||
proxy.loadComplete(e);
|
||||
|
||||
@@ -14,8 +14,28 @@ import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation;
|
||||
|
||||
public interface IAlignmentLimits {
|
||||
|
||||
/**
|
||||
* Can be placed in any orientation.
|
||||
*/
|
||||
IAlignmentLimits UNLIMITED = (direction, rotation, flip) -> true;
|
||||
|
||||
/**
|
||||
* Must be standing upright; i.e., top side of the block facing up.
|
||||
*/
|
||||
IAlignmentLimits UPRIGHT = (direction, rotation, flip) -> direction.offsetY == 0
|
||||
&& (flip.isVerticallyFliped() ? rotation.isUpsideDown() : rotation.isNotRotated());
|
||||
|
||||
/**
|
||||
* Can be in any orientation except upside down; i.e., top side of the block facing down.
|
||||
*/
|
||||
IAlignmentLimits NOT_UPSIDE_DOWN = (direction, rotation, flip) -> direction.offsetY != 0
|
||||
|| (flip.isVerticallyFliped() ? !rotation.isNotRotated() : !rotation.isUpsideDown());
|
||||
|
||||
/**
|
||||
* Front side of the block must be facing up.
|
||||
*/
|
||||
IAlignmentLimits FACING_UP = (direction, rotation, flip) -> direction == ForgeDirection.UP;
|
||||
|
||||
boolean isNewExtendedFacingValid(ForgeDirection direction, Rotation rotation, Flip flip);
|
||||
|
||||
default boolean isNewExtendedFacingValid(ExtendedFacing alignment) {
|
||||
|
||||
@@ -5,7 +5,11 @@ 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 {
|
||||
@@ -91,4 +95,32 @@ 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());
|
||||
}
|
||||
}
|
||||
@@ -57,12 +57,12 @@ public class ConstructableUtility {
|
||||
}
|
||||
|
||||
IConstructable constructable = null;
|
||||
if (tTileEntity instanceof IConstructableProvider aConstructableProvider)
|
||||
constructable = aConstructableProvider.getConstructable();
|
||||
else if (tTileEntity instanceof IConstructable aConstructable) {
|
||||
constructable = aConstructable;
|
||||
if (tTileEntity instanceof IConstructableProvider)
|
||||
constructable = ((IConstructableProvider) tTileEntity).getConstructable();
|
||||
else if (tTileEntity instanceof IConstructable) {
|
||||
constructable = (IConstructable) tTileEntity;
|
||||
} else if (IMultiblockInfoContainer.contains(tTileEntity.getClass())) {
|
||||
ExtendedFacing facing = tTileEntity instanceof IAlignment alignment ? alignment.getExtendedFacing()
|
||||
ExtendedFacing facing = tTileEntity instanceof IAlignment ? ((IAlignment) tTileEntity).getExtendedFacing()
|
||||
: ExtendedFacing.of(ForgeDirection.getOrientation(aSide));
|
||||
constructable = IMultiblockInfoContainer.<TileEntity>get(tTileEntity.getClass())
|
||||
.toConstructable(tTileEntity, facing);
|
||||
@@ -70,13 +70,14 @@ public class ConstructableUtility {
|
||||
|
||||
if (constructable == null) return false;
|
||||
|
||||
if (aPlayer instanceof EntityPlayerMP playerMP) {
|
||||
if (aPlayer instanceof EntityPlayerMP) {
|
||||
EntityPlayerMP playerMP = (EntityPlayerMP) aPlayer;
|
||||
// server side and sneaking (already checked above)
|
||||
// do construct
|
||||
if (aPlayer.capabilities.isCreativeMode) {
|
||||
constructable.construct(aStack, false);
|
||||
} else if (constructable instanceof ISurvivalConstructable aConstructable) {
|
||||
int built = aConstructable.survivalConstruct(
|
||||
} else if (constructable instanceof ISurvivalConstructable) {
|
||||
int built = ((ISurvivalConstructable) constructable).survivalConstruct(
|
||||
aStack,
|
||||
ConfigurationHandler.INSTANCE.getAutoPlaceBudget(),
|
||||
ISurvivalBuildEnvironment.create(IItemSource.fromPlayer(playerMP), playerMP));
|
||||
|
||||
@@ -24,6 +24,7 @@ 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 {
|
||||
@@ -166,6 +167,7 @@ public enum ExtendedFacing {
|
||||
|
||||
private final String name;
|
||||
private final IntegerAxisSwap integerAxisSwap;
|
||||
private final StructureRelativeCoords coordinateSystem;
|
||||
|
||||
ExtendedFacing(String name) {
|
||||
this.name = name;
|
||||
@@ -247,6 +249,7 @@ 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) {
|
||||
@@ -437,4 +440,8 @@ public enum ExtendedFacing {
|
||||
public ForgeDirection getRelativeForwardInWorld() {
|
||||
return c.getOpposite();
|
||||
}
|
||||
|
||||
public StructureRelativeCoords asCoordinateSystem() {
|
||||
return coordinateSystem;
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,10 @@ public class BlockHint extends Block {
|
||||
|
||||
@Override
|
||||
public IIcon getIcon(int aSide, int aMeta) {
|
||||
// For numbered hints, top and bottom are blank
|
||||
if (aSide <= 1 && aMeta <= 11) {
|
||||
return hint[13];
|
||||
}
|
||||
return hint[aMeta];
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,20 @@
|
||||
package com.gtnewhorizon.structurelib.core;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader;
|
||||
import com.gtnewhorizon.structurelib.mixins.Mixins;
|
||||
|
||||
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
|
||||
|
||||
@IFMLLoadingPlugin.MCVersion("1.7.10")
|
||||
public class StructureLibCore implements IFMLLoadingPlugin, IEarlyMixinLoader {
|
||||
|
||||
public static final Logger log = LogManager.getLogger("StructureLibCore");
|
||||
|
||||
@Override
|
||||
public String getMixinConfig() {
|
||||
return "mixins.structurelib.early.json";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getMixins(Set<String> loadedCoreMods) {
|
||||
return Mixins.getEarlyMixins(loadedCoreMods);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getASMTransformerClass() {
|
||||
return new String[0];
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -50,4 +36,14 @@ public class StructureLibCore implements IFMLLoadingPlugin, IEarlyMixinLoader {
|
||||
public String getAccessTransformerClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMixinConfig() {
|
||||
return "mixins.structurelib.early.json";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getMixins(Set<String> loadedCoreMods) {
|
||||
return Arrays.asList("blockChangeNotifier.MixinForgeHooks", "blockChangeNotifier.MixinWorld");
|
||||
}
|
||||
}
|
||||
@@ -164,40 +164,17 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
|
||||
list.onGuiInit(this);
|
||||
autoCompleteList.onGuiInit(this);
|
||||
|
||||
addButton(
|
||||
new GuiButton(
|
||||
ADD_BTN,
|
||||
guiLeft + 12,
|
||||
guiTop + 157,
|
||||
47,
|
||||
20,
|
||||
I18n.format("item.structurelib.constructableTrigger.gui.add")));
|
||||
addButton(
|
||||
new GuiButton(
|
||||
UNSET_BTN,
|
||||
guiLeft + 65,
|
||||
guiTop + 157,
|
||||
47,
|
||||
20,
|
||||
I18n.format("item.structurelib.constructableTrigger.gui.unset")));
|
||||
addButton(
|
||||
new GuiButton(
|
||||
WIPE_BTN,
|
||||
guiLeft + 118,
|
||||
guiTop + 157,
|
||||
47,
|
||||
20,
|
||||
I18n.format("item.structurelib.constructableTrigger.gui.wipe")));
|
||||
addButton(new GuiButton(ADD_BTN, guiLeft + 12, guiTop + 157, 47, 20, I18n.format(I18N_PREFIX + "add")));
|
||||
addButton(new GuiButton(UNSET_BTN, guiLeft + 65, guiTop + 157, 47, 20, I18n.format(I18N_PREFIX + "unset")));
|
||||
addButton(new GuiButton(WIPE_BTN, guiLeft + 118, guiTop + 157, 47, 20, I18n.format(I18N_PREFIX + "wipe")));
|
||||
|
||||
updateButtons();
|
||||
tooltipSplitCache.clear();
|
||||
info = StreamSupport
|
||||
.stream(
|
||||
Splitter.on("\\n").split(
|
||||
StatCollector.translateToLocal("item.structurelib.constructableTrigger.gui.info"))
|
||||
.spliterator(),
|
||||
Splitter.on("\\n").split(StatCollector.translateToLocal(I18N_PREFIX + "info")).spliterator(),
|
||||
false)
|
||||
.flatMap(line -> fontRendererObj.listFormattedStringToWidth(line, width * 3 / 5).stream())
|
||||
.flatMap(line -> fontRendererObj.listFormattedStringToWidth(line, width * 1 / 3).stream())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@@ -241,8 +218,7 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
|
||||
actionPerformed(but);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<GuiButton> getButtonList() {
|
||||
public List<GuiButton> getButtonList() {
|
||||
return buttonList;
|
||||
}
|
||||
|
||||
@@ -322,11 +298,10 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (key.textboxKeyTyped(aChar, aKey)) {
|
||||
if (key.textboxKeyTyped(aChar, aKey) || value.textboxKeyTyped(aChar, aKey)) {
|
||||
updateButtons();
|
||||
return;
|
||||
}
|
||||
if (value.textboxKeyTyped(aChar, aKey)) return;
|
||||
super.keyTyped(aChar, aKey);
|
||||
}
|
||||
|
||||
@@ -337,7 +312,8 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
|
||||
boolean existing = !StringUtils.isEmpty(keyText) && ChannelDataAccessor.hasSubChannel(trigger, keyText);
|
||||
getButtonList().get(ADD_BTN).displayString = existing ? I18n.format(I18N_PREFIX + "set")
|
||||
: I18n.format(I18N_PREFIX + "add");
|
||||
getButtonList().get(ADD_BTN).enabled = !StringUtils.isBlank(value.getText())
|
||||
getButtonList().get(ADD_BTN).enabled = !StringUtils.isBlank(key.getText())
|
||||
&& !StringUtils.isBlank(value.getText())
|
||||
&& Integer.parseInt(value.getText()) > 0;
|
||||
getButtonList().get(UNSET_BTN).enabled = existing && !StringUtils.isBlank(value.getText());
|
||||
|
||||
@@ -397,22 +373,10 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
|
||||
int topLeftY = (this.height - this.getYSize()) / 2;
|
||||
drawTexturedModalRect(topLeftX, topLeftY, 0, 0, getXSize(), getYSize());
|
||||
list.drawScreen(mX, mY, partialTick);
|
||||
fontRendererObj.drawString(
|
||||
I18n.format("item.structurelib.constructableTrigger.gui.title"),
|
||||
guiLeft + 12,
|
||||
guiTop + 9,
|
||||
0);
|
||||
fontRendererObj.drawString(
|
||||
I18n.format("item.structurelib.constructableTrigger.gui.key"),
|
||||
guiLeft + 12,
|
||||
guiTop + 122,
|
||||
0);
|
||||
fontRendererObj.drawString(I18n.format(I18N_PREFIX + "title"), guiLeft + 12, guiTop + 9, 0);
|
||||
fontRendererObj.drawString(I18n.format(I18N_PREFIX + "key"), guiLeft + 12, guiTop + 122, 0);
|
||||
key.drawTextBox();
|
||||
fontRendererObj.drawString(
|
||||
I18n.format("item.structurelib.constructableTrigger.gui.value"),
|
||||
guiLeft + 12,
|
||||
guiTop + 142,
|
||||
0);
|
||||
fontRendererObj.drawString(I18n.format(I18N_PREFIX + "value"), guiLeft + 12, guiTop + 142, 0);
|
||||
value.drawTextBox();
|
||||
}
|
||||
|
||||
@@ -455,7 +419,7 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
|
||||
tooltip.addAll(
|
||||
fontRendererObj.listFormattedStringToWidth(
|
||||
StatCollector.translateToLocalFormatted(
|
||||
"item.structurelib.constructableTrigger.gui.channels.from",
|
||||
I18N_PREFIX + "channels.from",
|
||||
Loader.instance().getIndexedModList().get(e.getKey()).getName()),
|
||||
maxLine));
|
||||
tooltip.addAll(
|
||||
|
||||
@@ -7,14 +7,9 @@ import static net.minecraft.util.StatCollector.translateToLocalFormatted;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.client.renderer.texture.IIconRegister;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagInt;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.util.StatCollector;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
@@ -25,86 +20,20 @@ import com.gtnewhorizon.structurelib.alignment.constructable.ConstructableUtilit
|
||||
|
||||
public class ItemConstructableTrigger extends Item {
|
||||
|
||||
private static final String TAG_MODE = "mode";
|
||||
|
||||
private static final IIcon[] textures = new IIcon[3];
|
||||
|
||||
public enum TriggerMode {
|
||||
BUILDING,
|
||||
UPDATING,
|
||||
REMOVING
|
||||
}
|
||||
|
||||
public ItemConstructableTrigger() {
|
||||
setUnlocalizedName("structurelib.constructableTrigger");
|
||||
setTextureName(MOD_ID + ":itemConstructableTrigger");
|
||||
setCreativeTab(StructureLib.creativeTab);
|
||||
}
|
||||
|
||||
public static void cycleMode(ItemStack held) {
|
||||
if (held.getTagCompound() == null) held.setTagCompound(new NBTTagCompound());
|
||||
if (!held.getTagCompound().hasKey(TAG_MODE)) held.getTagCompound().setTag(TAG_MODE, new NBTTagInt(0));
|
||||
held.getTagCompound().setInteger(TAG_MODE, (held.getTagCompound().getInteger(TAG_MODE) + 1) % 3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerIcons(IIconRegister register) {
|
||||
textures[0] = register.registerIcon(MOD_ID + ":constructableTrigger/BUILDING");
|
||||
textures[1] = register.registerIcon(MOD_ID + ":constructableTrigger/UPDATING");
|
||||
textures[2] = register.registerIcon(MOD_ID + ":constructableTrigger/REMOVING");
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIcon getIcon(ItemStack stack, int renderPass, EntityPlayer player, ItemStack usingItem, int useRemaining) {
|
||||
return getIcon(stack, renderPass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIcon getIcon(ItemStack stack, int pass) {
|
||||
if (!stack.hasTagCompound() || !stack.getTagCompound().hasKey(TAG_MODE)) {
|
||||
return textures[0];
|
||||
}
|
||||
return textures[stack.getTagCompound().getInteger(TAG_MODE)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getItemStackDisplayName(ItemStack stack) {
|
||||
if (!stack.hasTagCompound() || !stack.getTagCompound().hasKey(TAG_MODE)) {
|
||||
return getDisplayName(0);
|
||||
}
|
||||
return getDisplayName(stack.getTagCompound().getInteger(TAG_MODE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIcon getIconIndex(ItemStack stack) {
|
||||
return getIcon(stack, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIcon getIconFromDamage(int damage) {
|
||||
return textures[damage];
|
||||
}
|
||||
|
||||
private String getDisplayName(int mode) {
|
||||
return StatCollector.translateToLocal("item.structurelib.constructableTrigger.name") + " - "
|
||||
+ StatCollector.translateToLocal("item.structurelib.constructableTrigger.modes." + mode);
|
||||
}
|
||||
|
||||
public static TriggerMode getMode(ItemStack held) {
|
||||
if (held == null || !held.hasTagCompound() || !held.getTagCompound().hasKey(TAG_MODE)) {
|
||||
// default behavior
|
||||
return TriggerMode.BUILDING;
|
||||
}
|
||||
// get user-defined mode
|
||||
return TriggerMode.values()[held.getTagCompound().getInteger(TAG_MODE)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer player) {
|
||||
if (!world.isRemote && getMovingObjectPositionFromPlayer(world, player, true) == null) {
|
||||
if (player.isSneaking()) {
|
||||
StructureLib.instance().proxy().displayConfigGUI("registries");
|
||||
} else {
|
||||
if (getMovingObjectPositionFromPlayer(world, player, true) == null) {
|
||||
// channel gui is opened from server and config gui from client
|
||||
if (!world.isRemote && !player.isSneaking()) {
|
||||
player.openGui(StructureLib.instance(), 0, world, player.inventory.currentItem, 0, 0);
|
||||
} else if (world.isRemote && player.isSneaking()) {
|
||||
StructureLib.instance().proxy().displayConfigGUI("registries");
|
||||
}
|
||||
}
|
||||
return super.onItemRightClick(stack, world, player);
|
||||
|
||||
@@ -1,188 +0,0 @@
|
||||
package com.gtnewhorizon.structurelib.mixins;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.gtnewhorizon.structurelib.core.StructureLibCore;
|
||||
|
||||
import cpw.mods.fml.relauncher.FMLLaunchHandler;
|
||||
|
||||
public enum Mixins {
|
||||
|
||||
BLOCK_CHANGE_NOTIFIER(new Builder("Block Change Notifier").setPhase(Phase.EARLY)
|
||||
.addMixinClasses("blockChangeNotifier.MixinForgeHooks", "blockChangeNotifier.MixinWorld")
|
||||
.addTargetedMod(TargetedMod.VANILLA).setSide(Side.BOTH).setApplyIf(() -> true));
|
||||
|
||||
private final List<String> mixinClasses;
|
||||
private final List<TargetedMod> targetedMods;
|
||||
private final List<TargetedMod> excludedMods;
|
||||
private final Supplier<Boolean> applyIf;
|
||||
private final Phase phase;
|
||||
private final Side side;
|
||||
|
||||
Mixins(Builder builder) {
|
||||
this.mixinClasses = builder.mixinClasses;
|
||||
this.targetedMods = builder.targetedMods;
|
||||
this.excludedMods = builder.excludedMods;
|
||||
this.applyIf = builder.applyIf;
|
||||
this.phase = builder.phase;
|
||||
this.side = builder.side;
|
||||
if (this.mixinClasses.isEmpty()) {
|
||||
throw new RuntimeException("No mixin class specified for Mixin : " + this.name());
|
||||
}
|
||||
if (this.targetedMods.isEmpty()) {
|
||||
throw new RuntimeException("No targeted mods specified for Mixin : " + this.name());
|
||||
}
|
||||
if (this.applyIf == null) {
|
||||
throw new RuntimeException("No ApplyIf function specified for Mixin : " + this.name());
|
||||
}
|
||||
if (this.phase == null) {
|
||||
throw new RuntimeException("No Phase specified for Mixin : " + this.name());
|
||||
}
|
||||
if (this.side == null) {
|
||||
throw new RuntimeException("No Side function specified for Mixin : " + this.name());
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> getEarlyMixins(Set<String> loadedCoreMods) {
|
||||
final List<String> mixins = new ArrayList<>();
|
||||
final List<String> notLoading = new ArrayList<>();
|
||||
for (Mixins mixin : Mixins.values()) {
|
||||
if (mixin.phase == Phase.EARLY) {
|
||||
if (mixin.shouldLoad(loadedCoreMods, Collections.emptySet())) {
|
||||
mixins.addAll(mixin.mixinClasses);
|
||||
} else {
|
||||
notLoading.addAll(mixin.mixinClasses);
|
||||
}
|
||||
}
|
||||
}
|
||||
StructureLibCore.log.info("Not loading the following EARLY mixins: {}", notLoading.toString());
|
||||
return mixins;
|
||||
}
|
||||
|
||||
public static List<String> getLateMixins(Set<String> loadedMods) {
|
||||
// NOTE: Any targetmod here needs a modid, not a coremod id
|
||||
final List<String> mixins = new ArrayList<>();
|
||||
final List<String> notLoading = new ArrayList<>();
|
||||
for (Mixins mixin : Mixins.values()) {
|
||||
if (mixin.phase == Phase.LATE) {
|
||||
if (mixin.shouldLoad(Collections.emptySet(), loadedMods)) {
|
||||
mixins.addAll(mixin.mixinClasses);
|
||||
} else {
|
||||
notLoading.addAll(mixin.mixinClasses);
|
||||
}
|
||||
}
|
||||
}
|
||||
StructureLibCore.log.info("Not loading the following LATE mixins: {}", notLoading.toString());
|
||||
return mixins;
|
||||
}
|
||||
|
||||
private boolean shouldLoadSide() {
|
||||
return side == Side.BOTH || (side == Side.SERVER && FMLLaunchHandler.side().isServer())
|
||||
|| (side == Side.CLIENT && FMLLaunchHandler.side().isClient());
|
||||
}
|
||||
|
||||
private boolean allModsLoaded(List<TargetedMod> targetedMods, Set<String> loadedCoreMods, Set<String> loadedMods) {
|
||||
if (targetedMods.isEmpty()) return false;
|
||||
|
||||
for (TargetedMod target : targetedMods) {
|
||||
if (target == TargetedMod.VANILLA) continue;
|
||||
|
||||
// Check coremod first
|
||||
if (!loadedCoreMods.isEmpty() && target.coreModClass != null
|
||||
&& !loadedCoreMods.contains(target.coreModClass))
|
||||
return false;
|
||||
else if (!loadedMods.isEmpty() && target.modId != null && !loadedMods.contains(target.modId)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean noModsLoaded(List<TargetedMod> targetedMods, Set<String> loadedCoreMods, Set<String> loadedMods) {
|
||||
if (targetedMods.isEmpty()) return true;
|
||||
|
||||
for (TargetedMod target : targetedMods) {
|
||||
if (target == TargetedMod.VANILLA) continue;
|
||||
|
||||
// Check coremod first
|
||||
if (!loadedCoreMods.isEmpty() && target.coreModClass != null
|
||||
&& loadedCoreMods.contains(target.coreModClass))
|
||||
return false;
|
||||
else if (!loadedMods.isEmpty() && target.modId != null && loadedMods.contains(target.modId)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean shouldLoad(Set<String> loadedCoreMods, Set<String> loadedMods) {
|
||||
return (shouldLoadSide() && applyIf.get()
|
||||
&& allModsLoaded(targetedMods, loadedCoreMods, loadedMods)
|
||||
&& noModsLoaded(excludedMods, loadedCoreMods, loadedMods));
|
||||
}
|
||||
|
||||
private static class Builder {
|
||||
|
||||
private final String name;
|
||||
private final List<String> mixinClasses = new ArrayList<>();
|
||||
private final List<TargetedMod> targetedMods = new ArrayList<>();
|
||||
private final List<TargetedMod> excludedMods = new ArrayList<>();
|
||||
private Supplier<Boolean> applyIf = null;
|
||||
private Phase phase = null;
|
||||
private Side side = null;
|
||||
|
||||
public Builder(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Builder addMixinClasses(String... mixinClasses) {
|
||||
this.mixinClasses.addAll(Arrays.asList(mixinClasses));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setPhase(Phase phase) {
|
||||
if (this.phase != null) {
|
||||
throw new RuntimeException("Trying to define Phase twice for " + this.name);
|
||||
}
|
||||
this.phase = phase;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setSide(Side side) {
|
||||
if (this.side != null) {
|
||||
throw new RuntimeException("Trying to define Side twice for " + this.name);
|
||||
}
|
||||
this.side = side;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setApplyIf(Supplier<Boolean> applyIf) {
|
||||
this.applyIf = applyIf;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addTargetedMod(TargetedMod mod) {
|
||||
this.targetedMods.add(mod);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addExcludedMod(TargetedMod mod) {
|
||||
this.excludedMods.add(mod);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
private enum Side {
|
||||
BOTH,
|
||||
CLIENT,
|
||||
SERVER
|
||||
}
|
||||
|
||||
private enum Phase {
|
||||
EARLY,
|
||||
LATE,
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.gtnewhorizon.structurelib.net;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
|
||||
|
||||
import cpw.mods.fml.common.network.simpleimpl.IMessage;
|
||||
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler;
|
||||
import cpw.mods.fml.common.network.simpleimpl.MessageContext;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class StructureProjectorCycleModeMessage implements IMessage {
|
||||
|
||||
@Override
|
||||
public void fromBytes(ByteBuf buf) {
|
||||
// no op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(ByteBuf buf) {
|
||||
// no op
|
||||
}
|
||||
|
||||
public static class Handler implements IMessageHandler<StructureProjectorCycleModeMessage, IMessage> {
|
||||
|
||||
@Override
|
||||
public IMessage onMessage(StructureProjectorCycleModeMessage message, MessageContext ctx) {
|
||||
EntityPlayerMP player = ctx.getServerHandler().playerEntity;
|
||||
final ItemStack held = player.getHeldItem();
|
||||
if (held != null && held.getItem() instanceof ItemConstructableTrigger) {
|
||||
ItemConstructableTrigger.cycleMode(held);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,11 @@ 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;
|
||||
@@ -15,6 +18,8 @@ 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.
|
||||
@@ -319,6 +324,65 @@ public interface IStructureDefinition<T> {
|
||||
return walker.getBuilt();
|
||||
}
|
||||
|
||||
default void iterate(String piece, World world, ExtendedFacing extendedFacing, int basePositionX, int basePositionY,
|
||||
int basePositionZ, int basePositionA, int basePositionB, int basePositionC, IStructureWalker<T> predicate) {
|
||||
StructureUtility.iterateV2(
|
||||
getStructureFor(piece),
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
predicate,
|
||||
"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.
|
||||
*
|
||||
@@ -348,47 +412,77 @@ public interface IStructureDefinition<T> {
|
||||
}
|
||||
|
||||
if (checkBlocksIfNotNullForceCheckAllIfTrue != null) {
|
||||
boolean success = StructureUtility.iterateV2(
|
||||
elements,
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
checkBlocksIfNotNullForceCheckAllIfTrue ? (e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z)
|
||||
: skipBlockUnloaded((e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z)),
|
||||
checkBlocksIfNotNullForceCheckAllIfTrue ? "check" : "check force");
|
||||
boolean success;
|
||||
if (checkBlocksIfNotNullForceCheckAllIfTrue) {
|
||||
success = StructureUtility.iterateV2(
|
||||
elements,
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
(e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z),
|
||||
"check");
|
||||
} else {
|
||||
success = StructureUtility.iterateV2(
|
||||
elements,
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
skipBlockUnloaded((e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z)),
|
||||
"check force");
|
||||
}
|
||||
if (StructureLibAPI.isDebugEnabled() && success) {
|
||||
StructureLib.LOGGER
|
||||
.info("Multi [" + basePositionX + ", " + basePositionY + ", " + basePositionZ + "] pass");
|
||||
}
|
||||
return success;
|
||||
} else {
|
||||
StructureUtility.iterateV2(
|
||||
elements,
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
hintsOnly ? ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
|
||||
e.spawnHint(object, world, x, y, z, trigger);
|
||||
if (ChannelDataAccessor.hasSubChannel(trigger, StructureLibAPI.CHANNEL_SHOW_ERROR)
|
||||
&& !e.couldBeValid(object, world, x, y, z, trigger)) {
|
||||
StructureLibAPI.markHintParticleError(StructureLib.getCurrentPlayer(), world, x, y, z);
|
||||
}
|
||||
return true;
|
||||
}) : ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
|
||||
e.placeBlock(object, world, x, y, z, trigger);
|
||||
return true;
|
||||
}),
|
||||
hintsOnly ? "spawnHint" : "placeBlock");
|
||||
if (hintsOnly) {
|
||||
StructureUtility.iterateV2(
|
||||
elements,
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
|
||||
e.spawnHint(object, world, x, y, z, trigger);
|
||||
if (ChannelDataAccessor.hasSubChannel(trigger, StructureLibAPI.CHANNEL_SHOW_ERROR)
|
||||
&& !e.couldBeValid(object, world, x, y, z, trigger)) {
|
||||
StructureLibAPI.markHintParticleError(StructureLib.getCurrentPlayer(), world, x, y, z);
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
"spawnHint");
|
||||
} else {
|
||||
StructureUtility.iterateV2(
|
||||
elements,
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
|
||||
e.placeBlock(object, world, x, y, z, trigger);
|
||||
return true;
|
||||
}),
|
||||
"placeBlock");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ package com.gtnewhorizon.structurelib.structure;
|
||||
|
||||
import static com.gtnewhorizon.structurelib.StructureLib.LOGGER;
|
||||
import static com.gtnewhorizon.structurelib.StructureLib.PANIC_MODE;
|
||||
import static com.gtnewhorizon.structurelib.item.ItemConstructableTrigger.TriggerMode.REMOVING;
|
||||
import static com.gtnewhorizon.structurelib.structure.StructureUtility.checkForRemoving;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -23,7 +21,6 @@ import net.minecraft.util.IChatComponent;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import com.gtnewhorizon.structurelib.StructureLibAPI;
|
||||
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
|
||||
import com.gtnewhorizon.structurelib.util.ItemStackPredicate;
|
||||
import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode;
|
||||
|
||||
@@ -140,14 +137,12 @@ public interface IStructureElement<T> {
|
||||
*/
|
||||
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
ItemConstructableTrigger.TriggerMode mode = ItemConstructableTrigger.getMode(trigger);
|
||||
BlocksToPlace e = getBlocksToPlace(t, world, x, y, z, trigger, env);
|
||||
IItemSource source = env.getSource();
|
||||
EntityPlayer actor = env.getActor();
|
||||
Consumer<IChatComponent> chatter = env.getChatter();
|
||||
if (e != null) {
|
||||
PlaceResult skipOrRejectBasedOnCheck = checkForRemoving(mode, check(t, world, x, y, z), world, x, y, z);
|
||||
if (skipOrRejectBasedOnCheck != PlaceResult.ACCEPT) return skipOrRejectBasedOnCheck;
|
||||
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
|
||||
if (e.getStacks() == null) {
|
||||
ItemStack taken = source.takeOne(e.getPredicate(), true);
|
||||
return StructureUtility.survivalPlaceBlock(
|
||||
@@ -164,7 +159,7 @@ public interface IStructureElement<T> {
|
||||
chatter);
|
||||
}
|
||||
for (ItemStack stack : e.getStacks()) {
|
||||
if (mode != REMOVING && !source.takeOne(stack, true)) continue;
|
||||
if (!source.takeOne(stack, true)) continue;
|
||||
return StructureUtility.survivalPlaceBlock(
|
||||
stack,
|
||||
NBTMode.EXACT,
|
||||
|
||||
@@ -2,8 +2,25 @@ package com.gtnewhorizon.structurelib.structure;
|
||||
|
||||
import net.minecraft.world.World;
|
||||
|
||||
interface IStructureWalker<T> {
|
||||
/**
|
||||
* A functional interface that is used to iterate over an arbitrary structure.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface IStructureWalker<T> {
|
||||
|
||||
/**
|
||||
* Called once per structure element.
|
||||
*
|
||||
* @param element The current element
|
||||
* @param world The current world
|
||||
* @param x The absolute X position of the element in world coordinates
|
||||
* @param y The absolute Y position of the element in world coordinates
|
||||
* @param z The absolute Z position of the element in world coordinates
|
||||
* @param a The relative A position of the element in structure-local coordinates (pre transform)
|
||||
* @param b The relative B position of the element in structure-local coordinates (pre transform)
|
||||
* @param c The relative C position of the element in structure-local coordinates (pre transform)
|
||||
* @return False to stop iterating, true to continue.
|
||||
*/
|
||||
boolean visit(IStructureElement<T> element, World world, int x, int y, int z, int a, int b, int c);
|
||||
|
||||
default boolean blockNotLoaded(IStructureElement<T> element, World world, int x, int y, int z, int a, int b,
|
||||
|
||||
@@ -4,33 +4,49 @@ 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.Arrays;
|
||||
import java.util.ArrayList;
|
||||
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, IStructureElement<T>[]> structures, Map<String, Set<Vec3Impl>> occupiedSpaces,
|
||||
Map<String, Position<StructureDefinitionCoords>> controllers,
|
||||
Map<String, ListMultimap<Character, Position<StructureDefinitionCoords>>> socketLocations) {
|
||||
this.elements = elements;
|
||||
this.shapes = shapes;
|
||||
this.structures = structures;
|
||||
this.occupiedSpaces = occupiedSpaces;
|
||||
this.controllers = controllers;
|
||||
this.socketLocations = socketLocations;
|
||||
}
|
||||
|
||||
public static class Builder<T> {
|
||||
@@ -41,12 +57,18 @@ 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<>();
|
||||
}
|
||||
@@ -82,7 +104,69 @@ 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 void compileShape(String name, String[][] structurePiece) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
if (structurePiece.length > 0) {
|
||||
for (String[] strings : structurePiece) {
|
||||
if (strings.length > 0) {
|
||||
@@ -95,6 +179,11 @@ 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;
|
||||
@@ -103,6 +192,15 @@ 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;
|
||||
@@ -110,6 +208,7 @@ 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++;
|
||||
@@ -156,68 +255,62 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
|
||||
if (built.contains("-")) {
|
||||
addElement('-', isAir());
|
||||
}
|
||||
|
||||
shapes.put(name, built);
|
||||
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;
|
||||
}
|
||||
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
@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()));
|
||||
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;
|
||||
}
|
||||
|
||||
private Set<Integer> findMissing() {
|
||||
return shapes.values().stream().flatMapToInt(CharSequence::chars)
|
||||
.filter(i -> !elements.containsKey((char) i)).boxed().collect(Collectors.toSet());
|
||||
private CharOpenHashSet findMissing() {
|
||||
CharOpenHashSet missing = new CharOpenHashSet();
|
||||
|
||||
for (String shape : shapes.values()) {
|
||||
for (char c : shape.toCharArray()) {
|
||||
if (!elements.containsKey(c)) {
|
||||
missing.add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return missing;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,4 +339,61 @@ 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;
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,6 @@ package com.gtnewhorizon.structurelib.structure;
|
||||
|
||||
import static com.gtnewhorizon.structurelib.StructureLib.LOGGER;
|
||||
import static com.gtnewhorizon.structurelib.StructureLib.PANIC_MODE;
|
||||
import static com.gtnewhorizon.structurelib.item.ItemConstructableTrigger.TriggerMode.BUILDING;
|
||||
import static com.gtnewhorizon.structurelib.item.ItemConstructableTrigger.TriggerMode.REMOVING;
|
||||
import static com.gtnewhorizon.structurelib.item.ItemConstructableTrigger.TriggerMode.UPDATING;
|
||||
import static java.lang.Integer.MIN_VALUE;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -31,7 +28,6 @@ import java.util.function.Supplier;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.item.EntityItem;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.init.Blocks;
|
||||
@@ -55,16 +51,13 @@ import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.StructureLibAPI;
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
|
||||
import com.gtnewhorizon.structurelib.structure.IStructureElement.PlaceResult;
|
||||
import com.gtnewhorizon.structurelib.structure.adders.IBlockAdder;
|
||||
import com.gtnewhorizon.structurelib.structure.adders.ITileAdder;
|
||||
import com.gtnewhorizon.structurelib.util.ItemStackPredicate;
|
||||
import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode;
|
||||
import com.gtnewhorizon.structurelib.util.Vec3Impl;
|
||||
import com.gtnewhorizon.structurelib.xmod.ServerUtilitiesCompat;
|
||||
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.common.registry.GameRegistry;
|
||||
|
||||
/**
|
||||
@@ -153,8 +146,6 @@ public class StructureUtility {
|
||||
|
||||
private static final String NICE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz=|!@#$%&()[]{};:<>/?_,.*^'`";
|
||||
|
||||
private static final boolean isSULoaded = Loader.isModLoaded("serverutilities");
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static final Map<Vec3Impl, IStructureNavigate> STEP = new HashMap<>();
|
||||
|
||||
@@ -215,11 +206,7 @@ public class StructureUtility {
|
||||
|
||||
@Override
|
||||
public boolean placeBlock(Object o, World world, int x, int y, int z, ItemStack trigger) {
|
||||
placeRemoveOrUpdate(
|
||||
trigger,
|
||||
() -> world.setBlock(x, y, z, StructureLibAPI.getBlockHint(), 14, 2),
|
||||
() -> world.setBlock(x, y, z, StructureLibAPI.getBlockHint(), 14, 2),
|
||||
() -> world.setBlock(x, y, z, Blocks.air, 0, 2));
|
||||
world.setBlock(x, y, z, StructureLibAPI.getBlockHint(), 14, 2);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -232,16 +219,11 @@ public class StructureUtility {
|
||||
@Override
|
||||
public PlaceResult survivalPlaceBlock(Object o, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
ItemConstructableTrigger.TriggerMode mode = ItemConstructableTrigger.getMode(trigger);
|
||||
if (mode != REMOVING && check(o, world, x, y, z)) return PlaceResult.SKIP;
|
||||
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
|
||||
if (env.getSource().takeOne(new ItemStack(Blocks.cobblestone), false)) {
|
||||
placeRemoveOrUpdate(
|
||||
trigger,
|
||||
() -> world.setBlock(x, y, z, Blocks.cobblestone, 0, 2),
|
||||
() -> world.setBlock(x, y, z, Blocks.cobblestone, 0, 2),
|
||||
() -> world.setBlock(x, y, z, Blocks.air, 0, 2));
|
||||
world.setBlock(x, y, z, Blocks.cobblestone, 0, 2);
|
||||
}
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
@@ -348,84 +330,29 @@ public class StructureUtility {
|
||||
public static PlaceResult survivalPlaceBlock(Block block, int meta, World world, int x, int y, int z, IItemSource s,
|
||||
EntityPlayer actor, Consumer<IChatComponent> chatter) {
|
||||
if (block == null) throw new NullPointerException();
|
||||
ItemConstructableTrigger.TriggerMode mode = BUILDING;
|
||||
if (actor.getHeldItem() != null && actor.getHeldItem().getItem() instanceof ItemConstructableTrigger) {
|
||||
// best effort lookup if the item in the player's hand is a trigger, and if it is, get the mode from the
|
||||
// ...trigger instead of assuming default behavior
|
||||
mode = ItemConstructableTrigger.getMode(actor.getHeldItem());
|
||||
}
|
||||
if (mode == BUILDING && !StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor))
|
||||
return PlaceResult.REJECT;
|
||||
if (mode == UPDATING) {
|
||||
if (!world.isAirBlock(x, y, z)) {
|
||||
if (world.getTileEntity(x, y, z) instanceof TileEntity) {
|
||||
// We're in updating mode and the block we're looking at might be important, so let's just leave it
|
||||
// alone
|
||||
// since it's probably not something we want to replace with something else
|
||||
return PlaceResult.SKIP;
|
||||
}
|
||||
Block blockInWorld = world.getBlock(x, y, z);
|
||||
if (blockInWorld.isAssociatedBlock(block) && blockInWorld.getDamageValue(world, x, y, z) == meta) {
|
||||
// This is the block we are looking for, move along
|
||||
return PlaceResult.SKIP;
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
ItemStack stack = new ItemStack(itemBlock, 1, itemMeta);
|
||||
if (mode != REMOVING) {
|
||||
if (!s.takeOne(stack, true)) {
|
||||
if (chatter != null) chatter.accept(
|
||||
new ChatComponentTranslation(
|
||||
"structurelib.autoplace.error.no_simple_block",
|
||||
stack.func_151000_E()));
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
if (block instanceof ICustomBlockSetting) {
|
||||
ICustomBlockSetting blockCustom = (ICustomBlockSetting) block;
|
||||
blockCustom.setBlock(world, x, y, z, meta);
|
||||
} else {
|
||||
if (mode == UPDATING && !world.isAirBlock(x, y, z) && checkPermission(world, actor, x, y, z)) {
|
||||
// We're in updating mode and there's a block in our way (grr), so let's remove whatever is there
|
||||
// ...and drop it to the player
|
||||
ItemStack toGive = new ItemStack(world.getBlock(x, y, z));
|
||||
toGive.setItemDamage(world.getBlock(x, y, z).getDamageValue(world, x, y, z));
|
||||
if (!world.setBlock(x, y, z, Blocks.air, 0, 4) || !tryGiveOrDropItem(world, actor, toGive)) {
|
||||
// We either weren't able to give the player the block that was there, or we weren't able to
|
||||
// remove
|
||||
// the block, either way, we need to reject the update. Also, since we hid the block update from
|
||||
// the client in an effort to make the exchange as seamless as possible, we need to now notify
|
||||
// the client if we did successfully remove the block.
|
||||
if (world.isAirBlock(x, y, z)) world.notifyBlockChange(x, y, z, Blocks.air);
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
}
|
||||
if (!stack.copy()
|
||||
.tryPlaceItemIntoWorld(actor, world, x, y, z, ForgeDirection.UP.ordinal(), 0.5f, 0.5f, 0.5f)) {
|
||||
if (!s.takeOne(stack, true)) {
|
||||
if (chatter != null) chatter.accept(
|
||||
new ChatComponentTranslation(
|
||||
"structurelib.autoplace.error.no_simple_block",
|
||||
stack.func_151000_E()));
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
if (block instanceof ICustomBlockSetting) {
|
||||
ICustomBlockSetting blockCustom = (ICustomBlockSetting) block;
|
||||
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 (mode == REMOVING || !s.takeOne(stack, false)) {
|
||||
// If we're in removing mode, or if the user didn't have the block in their inventory, and we're in some
|
||||
// other mode, let's check some stuff
|
||||
if (world.isAirBlock(x, y, z)) {
|
||||
return PlaceResult.SKIP;
|
||||
}
|
||||
ItemStack toGive = new ItemStack(world.getBlock(x, y, z));
|
||||
toGive.setItemDamage(world.getBlock(x, y, z).getDamageValue(world, x, y, z));
|
||||
if (mode == REMOVING && !checkPermission(world, actor, x, y, z)) {
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
if (!s.takeOne(stack, false)) {
|
||||
// rollback
|
||||
world.setBlockToAir(x, y, z);
|
||||
if (mode == REMOVING && !tryGiveOrDropItem(world, actor, toGive)) {
|
||||
// We're in removing mode and something went wrong while trying to give the player the block we removed,
|
||||
// let's just fail for now
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
}
|
||||
return PlaceResult.ACCEPT;
|
||||
}
|
||||
@@ -517,82 +444,24 @@ public class StructureUtility {
|
||||
if (stack == null) throw new NullPointerException();
|
||||
if (stack.stackSize != 1) throw new IllegalArgumentException();
|
||||
if (!(stack.getItem() instanceof ItemBlock)) throw new IllegalArgumentException();
|
||||
ItemConstructableTrigger.TriggerMode mode = BUILDING;
|
||||
if (actor.getHeldItem() != null && actor.getHeldItem().getItem() instanceof ItemConstructableTrigger) {
|
||||
// best effort lookup if the item in the player's hand is a trigger, and if it is, get the mode from the
|
||||
// ...trigger instead of assuming default behavior
|
||||
mode = ItemConstructableTrigger.getMode(actor.getHeldItem());
|
||||
if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT;
|
||||
if (!assumeStackPresent && !s.takeOne(stack, true)) {
|
||||
if (chatter != null) chatter.accept(
|
||||
new ChatComponentTranslation("structurelib.autoplace.error.no_item_stack", stack.func_151000_E()));
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
if (mode != REMOVING) {
|
||||
if (mode == BUILDING) {
|
||||
if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) {
|
||||
return PlaceResult.SKIP;
|
||||
}
|
||||
}
|
||||
if (mode == UPDATING) {
|
||||
if (!world.isAirBlock(x, y, z)) {
|
||||
if (world.getTileEntity(x, y, z) instanceof TileEntity) {
|
||||
return PlaceResult.SKIP;
|
||||
}
|
||||
Block blockInWorld = world.getBlock(x, y, z);
|
||||
ItemStack blockInWorldItem = new ItemStack(blockInWorld);
|
||||
blockInWorldItem.setItemDamage(blockInWorld.getDamageValue(world, x, y, z));
|
||||
if (blockInWorldItem.isItemEqual(stack)) {
|
||||
// This is the block we are looking for, move along
|
||||
return PlaceResult.SKIP;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!assumeStackPresent && !s.takeOne(stack, true)) {
|
||||
if (chatter != null) chatter.accept(
|
||||
new ChatComponentTranslation(
|
||||
"structurelib.autoplace.error.no_item_stack",
|
||||
stack.func_151000_E()));
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
if (mode == UPDATING && !world.isAirBlock(x, y, z) && checkPermission(world, actor, x, y, z)) {
|
||||
// We're in updating mode and there's a block in our way (grr), so let's remove whatever is there
|
||||
// ...and drop it to the player
|
||||
ItemStack toGive = new ItemStack(world.getBlock(x, y, z));
|
||||
toGive.setItemDamage(world.getBlock(x, y, z).getDamageValue(world, x, y, z));
|
||||
if (!world.setBlock(x, y, z, Blocks.air, 0, 4) || !tryGiveOrDropItem(world, actor, toGive)) {
|
||||
// We either weren't able to give the player the block that was there, or we weren't able to remove
|
||||
// the block, either way, we need to reject the update. Also, since we hid the block update from
|
||||
// the client in an effort to make the exchange as seamless as possible, we need to now notify
|
||||
// the client if we did successfully remove the block.
|
||||
if (world.isAirBlock(x, y, z)) world.notifyBlockChange(x, y, z, Blocks.air);
|
||||
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 (mode == REMOVING || !s.takeOne(stack, false)) {
|
||||
if (world.isAirBlock(x, y, z)) {
|
||||
return PlaceResult.SKIP;
|
||||
}
|
||||
// If the user didn't have the item in their inventory, or we're in removing mode, we set it to air
|
||||
ItemStack toGive = new ItemStack(world.getBlock(x, y, z));
|
||||
toGive.setItemDamage(world.getBlock(x, y, z).getDamageValue(world, x, y, z));
|
||||
if (mode == REMOVING && !checkPermission(world, actor, x, y, z)) {
|
||||
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);
|
||||
if (mode == REMOVING && !tryGiveOrDropItem(world, actor, toGive)) {
|
||||
// We were in removing mode and something went wrong while trying to give the player the block we
|
||||
// ...removed, let's just fail for now
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
}
|
||||
return PlaceResult.ACCEPT;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 nonetheless in
|
||||
* case you want this as a fallback to something else.
|
||||
* 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.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> IStructureElement<T> isAir() {
|
||||
@@ -601,8 +470,8 @@ public class StructureUtility {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* nonetheless in case you want this as a fallback to something else.
|
||||
* 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.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> IStructureElement<T> notAir() {
|
||||
@@ -768,7 +637,20 @@ public class StructureUtility {
|
||||
* given tier, then player will be allowed to use multiple kinds of blocks, but their tier is guaranteed to be the
|
||||
* same.
|
||||
* <p>
|
||||
* <b>Example Implementation:</b>
|
||||
* <h3>WARNING</h3> <b>You SHOULD NOT return notSet from your tierExtractor.</b> If you do so, we will have this
|
||||
* chain of events:
|
||||
* <ul>
|
||||
* <li>at check start, tier is reset to notSet
|
||||
* <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
|
||||
* <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>
|
||||
* <h3>Example Implementation</h3>
|
||||
* <p>
|
||||
* Assume you have 16 tier, each map to one particular block's 16 different meta. You will usually want something
|
||||
* like this
|
||||
@@ -795,7 +677,7 @@ public class StructureUtility {
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Important bits:
|
||||
* 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>
|
||||
@@ -804,19 +686,8 @@ public class StructureUtility {
|
||||
* <li>notSet is never a tier returned by ITierConverter.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* <p>
|
||||
* <b>You SHOULD NOT return notSet from your tierExtractor.</b> If you do so, we will have this chain of events:
|
||||
* <ul>
|
||||
* <li>at check start, tier is reset to notSet
|
||||
* <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
|
||||
* <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>
|
||||
* As a friendly reminder, the above characteristics are only for this simply demo, not as a general restriction
|
||||
* imposed by ofBlocksTiered.
|
||||
*
|
||||
* @param tierExtractor a function to extract tier info from a block. This function can return null and will never
|
||||
* be passed a null block or an invalid block meta. If this function returns null, then the
|
||||
@@ -838,7 +709,7 @@ public class StructureUtility {
|
||||
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);
|
||||
return new StructureElement_Bridge<>() {
|
||||
return new StructureElement_Bridge<T>() {
|
||||
|
||||
@Override
|
||||
public boolean check(T t, World world, int x, int y, int z) {
|
||||
@@ -863,11 +734,6 @@ public class StructureUtility {
|
||||
return Objects.equals(hintTier, worldTier);
|
||||
}
|
||||
|
||||
private boolean isInAllKnownTiers(Block b) {
|
||||
return allKnownTiers != null
|
||||
&& allKnownTiers.stream().anyMatch(pair -> pair.getLeft().isAssociatedBlock(b));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
Pair<Block, Integer> hint = getHint(trigger);
|
||||
@@ -900,17 +766,13 @@ public class StructureUtility {
|
||||
@Override
|
||||
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
ItemConstructableTrigger.TriggerMode mode = ItemConstructableTrigger.getMode(trigger);
|
||||
Pair<Block, Integer> hint = getHint(trigger);
|
||||
if (hint == null) return PlaceResult.REJECT; // TODO or SKIP?
|
||||
Block block = world.getBlock(x, y, z);
|
||||
int meta = world.getBlockMetadata(x, y, z);
|
||||
TIER tier = tierExtractor.convert(block, meta);
|
||||
if (mode != REMOVING && (Objects.equals(tier, tierExtractor.convert(hint.getKey(), hint.getValue()))))
|
||||
if (Objects.equals(tier, tierExtractor.convert(hint.getKey(), hint.getValue())))
|
||||
return PlaceResult.SKIP;
|
||||
if (mode == REMOVING && (!isInAllKnownTiers(block) && !world.isAirBlock(x, y, z))) {
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
return StructureUtility.survivalPlaceBlock(
|
||||
hint.getKey(),
|
||||
hint.getValue(),
|
||||
@@ -999,9 +861,7 @@ public class StructureUtility {
|
||||
@Override
|
||||
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
ItemConstructableTrigger.TriggerMode mode = ItemConstructableTrigger.getMode(trigger);
|
||||
PlaceResult skipOrRejectBasedOnCheck = checkForRemoving(mode, check(t, world, x, y, z), world, x, y, z);
|
||||
if (skipOrRejectBasedOnCheck != PlaceResult.ACCEPT) return skipOrRejectBasedOnCheck;
|
||||
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
|
||||
if (getBlock() == null) return PlaceResult.REJECT;
|
||||
return StructureUtility.survivalPlaceBlock(
|
||||
getBlock(),
|
||||
@@ -1088,9 +948,7 @@ public class StructureUtility {
|
||||
@Deprecated
|
||||
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
||||
ItemConstructableTrigger.TriggerMode mode = ItemConstructableTrigger.getMode(trigger);
|
||||
PlaceResult skipOrRejectBasedOnCheck = checkForRemoving(mode, check(t, world, x, y, z), world, x, y, z);
|
||||
if (skipOrRejectBasedOnCheck != PlaceResult.ACCEPT) return skipOrRejectBasedOnCheck;
|
||||
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);
|
||||
}
|
||||
@@ -1098,9 +956,7 @@ public class StructureUtility {
|
||||
@Override
|
||||
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
ItemConstructableTrigger.TriggerMode mode = ItemConstructableTrigger.getMode(trigger);
|
||||
PlaceResult skipOrRejectBasedOnCheck = checkForRemoving(mode, check(t, world, x, y, z), world, x, y, z);
|
||||
if (skipOrRejectBasedOnCheck != PlaceResult.ACCEPT) return skipOrRejectBasedOnCheck;
|
||||
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
|
||||
if (init()) return StructureUtility.survivalPlaceBlock(
|
||||
block,
|
||||
meta,
|
||||
@@ -1728,15 +1584,8 @@ public class StructureUtility {
|
||||
@Deprecated
|
||||
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
ItemConstructableTrigger.TriggerMode mode = ItemConstructableTrigger.getMode(trigger);
|
||||
PlaceResult skipOrRejectBasedOnCheck = checkForRemoving(
|
||||
mode,
|
||||
world.getBlock(x, y, z) == defaultBlock && world.getBlockMetadata(x, y, z) == defaultMeta,
|
||||
world,
|
||||
x,
|
||||
y,
|
||||
z);
|
||||
if (skipOrRejectBasedOnCheck != PlaceResult.ACCEPT) return skipOrRejectBasedOnCheck;
|
||||
if (world.getBlock(x, y, z) == defaultBlock && world.getBlockMetadata(x, y, z) == defaultMeta)
|
||||
return PlaceResult.SKIP;
|
||||
return StructureUtility.survivalPlaceBlock(
|
||||
defaultBlock,
|
||||
defaultMeta,
|
||||
@@ -1781,15 +1630,8 @@ public class StructureUtility {
|
||||
@Deprecated
|
||||
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
ItemConstructableTrigger.TriggerMode mode = ItemConstructableTrigger.getMode(trigger);
|
||||
PlaceResult skipOrRejectBasedOnCheck = checkForRemoving(
|
||||
mode,
|
||||
world.getBlock(x, y, z) == defaultBlock && world.getBlockMetadata(x, y, z) == defaultMeta,
|
||||
world,
|
||||
x,
|
||||
y,
|
||||
z);
|
||||
if (skipOrRejectBasedOnCheck != PlaceResult.ACCEPT) return skipOrRejectBasedOnCheck;
|
||||
if (world.getBlock(x, y, z) == defaultBlock && world.getBlockMetadata(x, y, z) == defaultMeta)
|
||||
return PlaceResult.SKIP;
|
||||
return StructureUtility.survivalPlaceBlock(
|
||||
defaultBlock,
|
||||
defaultMeta,
|
||||
@@ -2048,7 +1890,6 @@ public class StructureUtility {
|
||||
@Deprecated
|
||||
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
||||
ItemConstructableTrigger.TriggerMode mode = ItemConstructableTrigger.getMode(trigger);
|
||||
if (predicate.test(t))
|
||||
return downstream.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
|
||||
return placeResultWhenDisabled;
|
||||
@@ -2057,7 +1898,6 @@ public class StructureUtility {
|
||||
@Override
|
||||
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
ItemConstructableTrigger.TriggerMode mode = ItemConstructableTrigger.getMode(trigger);
|
||||
if (predicate.test(t)) return downstream.survivalPlaceBlock(t, world, x, y, z, trigger, env);
|
||||
return placeResultWhenDisabled;
|
||||
}
|
||||
@@ -3425,57 +3265,4 @@ public class StructureUtility {
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a creation, removal, or update operation at the given position
|
||||
*
|
||||
* @param trigger ItemStack for the trigger item
|
||||
* @param onCreate Operation to be performed when the mode of the trigger is set to building mode
|
||||
* @param onUpdate Operation to be performed when the mode of the trigger is set to updating mode
|
||||
* @param onRemove Operation to be performed when the mode of the trigger is set to removing mode
|
||||
*/
|
||||
public static void placeRemoveOrUpdate(ItemStack trigger, Runnable onCreate, Runnable onUpdate, Runnable onRemove) {
|
||||
ItemConstructableTrigger.TriggerMode mode = ItemConstructableTrigger.getMode(trigger);
|
||||
switch (mode) {
|
||||
case BUILDING:
|
||||
onCreate.run();
|
||||
break;
|
||||
case UPDATING:
|
||||
onUpdate.run();
|
||||
break;
|
||||
case REMOVING:
|
||||
onRemove.run();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean tryGiveOrDropItem(World world, EntityPlayer actor, ItemStack stack) {
|
||||
if (actor.inventory.addItemStackToInventory(stack)) {
|
||||
actor.inventoryContainer.detectAndSendChanges();
|
||||
return true;
|
||||
}
|
||||
EntityItem toSpawn = new EntityItem(world, actor.posX, actor.posY, actor.posZ, stack);
|
||||
// For some reason these things sometimes spawn with the velocity of an F1 car, so let's prevent that
|
||||
toSpawn.motionX = 0;
|
||||
toSpawn.motionY = 0;
|
||||
toSpawn.motionZ = 0;
|
||||
return world.spawnEntityInWorld(toSpawn);
|
||||
}
|
||||
|
||||
public static boolean checkPermission(World world, EntityPlayer actor, int x, int y, int z) {
|
||||
boolean res = true;
|
||||
if (isSULoaded) {
|
||||
res = ServerUtilitiesCompat.checkPermission(world, actor, x, z);
|
||||
}
|
||||
res = res && !world.getBlock(x, y, z).isAssociatedBlock(Blocks.bedrock);
|
||||
// maybe check mining level who knows :shrug:
|
||||
return res;
|
||||
}
|
||||
|
||||
public static PlaceResult checkForRemoving(ItemConstructableTrigger.TriggerMode mode, boolean checkResult,
|
||||
World world, int x, int y, int z) {
|
||||
if (mode != REMOVING && checkResult) return PlaceResult.SKIP;
|
||||
else if (mode != BUILDING && !checkResult && !world.isAirBlock(x, y, z)) return PlaceResult.REJECT;
|
||||
return PlaceResult.ACCEPT;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package com.gtnewhorizon.structurelib.xmod;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import cpw.mods.fml.common.Optional;
|
||||
import serverutils.data.ClaimedChunks;
|
||||
import serverutils.lib.data.ForgePlayer;
|
||||
import serverutils.lib.data.ForgeTeam;
|
||||
import serverutils.lib.data.Universe;
|
||||
import serverutils.lib.math.ChunkDimPos;
|
||||
|
||||
public class ServerUtilitiesCompat {
|
||||
|
||||
@Optional.Method(modid = "serverutilities")
|
||||
public static boolean checkPermission(World world, EntityPlayer actor, int x, int z) {
|
||||
final ForgeTeam owningTeam = ClaimedChunks.instance
|
||||
.getChunkTeam(new ChunkDimPos(x >> 4, z >> 4, world.provider.dimensionId));
|
||||
if (owningTeam == null) {
|
||||
return true;
|
||||
}
|
||||
final ForgePlayer currentPlayer = new ForgePlayer(
|
||||
Universe.get(),
|
||||
actor.getUniqueID(),
|
||||
actor.getCommandSenderName());
|
||||
return owningTeam.isAlly(currentPlayer) || owningTeam.isMember(currentPlayer);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,9 +22,6 @@ item.structurelib.frontRotationTool.desc.1=Rotates only the front panel,
|
||||
item.structurelib.frontRotationTool.desc.2=which allows structure rotation.
|
||||
|
||||
item.structurelib.constructableTrigger.name=Multiblock Structure Hologram Projector
|
||||
item.structurelib.constructableTrigger.modes.0=Building Mode
|
||||
item.structurelib.constructableTrigger.modes.1=Updating Mode
|
||||
item.structurelib.constructableTrigger.modes.2=Removing Mode
|
||||
item.structurelib.constructableTrigger.desc.0=Triggers Constructable Interface
|
||||
item.structurelib.constructableTrigger.desc.1=Shows multiblock construction schematic,
|
||||
item.structurelib.constructableTrigger.desc.2=use on the multiblock controller.
|
||||
@@ -37,17 +34,11 @@ item.structurelib.constructableTrigger.gui.title=Sub Channels
|
||||
item.structurelib.constructableTrigger.gui.key=Key:
|
||||
item.structurelib.constructableTrigger.gui.value=Value:
|
||||
item.structurelib.constructableTrigger.gui.add=Add
|
||||
item.structurelib.constructableTrigger.gui.set=Set
|
||||
item.structurelib.constructableTrigger.gui.unset=Unset
|
||||
item.structurelib.constructableTrigger.gui.wipe=§4Wipe
|
||||
item.structurelib.constructableTrigger.gui.error.show=Show Errors
|
||||
item.structurelib.constructableTrigger.gui.error.hide=Hide Errors
|
||||
item.structurelib.constructableTrigger.gui.gt_no_hatch.enable=No Hatch
|
||||
item.structurelib.constructableTrigger.gui.gt_no_hatch.disable=Hatches
|
||||
item.structurelib.constructableTrigger.keyCategory=Multiblock Structure Hologram Projector Keys
|
||||
item.structurelib.constructableTrigger.keys.cycleMode=Cycle Projector Mode
|
||||
item.structurelib.constructableTrigger.gui.set=Update
|
||||
item.structurelib.constructableTrigger.gui.unset=Remove
|
||||
item.structurelib.constructableTrigger.gui.wipe=§4Clear
|
||||
item.structurelib.constructableTrigger.gui.channels.from=§rMod: §9§o%s
|
||||
item.structurelib.constructableTrigger.gui.info=Many multiblocks support some degree of variation in its structure and use sub channels to indicate which variant to use.\n Sub channels are has a sequence of lower case characters as key and a positive integer as value. Most known sub channels should be available as typeahead completions.\nYou can also drag and drop items from NEI to set the corresponding sub channel value. These items all have a tooltip of §oIndicates value X for sub channel Y§r.
|
||||
item.structurelib.constructableTrigger.gui.info=Many multiblocks support some degree of variation in their structure and a sub channel allows indication of which variant to use.\n All sub channels have a sequence of lowercase characters as key and a positive integer as value. Most known sub channels should be available as typeahead completions.\n You can also drag and drop items from NEI to set the corresponding sub channel value. These items all have a tooltip of "§oIndicates§r §ovalue§r §oX§r §ofor§r §osub§r §ochannel§r §oY§r".
|
||||
|
||||
channels.structurelib.show_errors=When set, will highlight parts of the multiblock that is definitely wrong
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 273 B After Width: | Height: | Size: 176 B |
|
Before Width: | Height: | Size: 279 B After Width: | Height: | Size: 208 B |
|
Before Width: | Height: | Size: 296 B After Width: | Height: | Size: 212 B |
|
Before Width: | Height: | Size: 301 B After Width: | Height: | Size: 234 B |
|
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 196 B |
|
Before Width: | Height: | Size: 281 B After Width: | Height: | Size: 186 B |
|
Before Width: | Height: | Size: 285 B After Width: | Height: | Size: 186 B |
|
Before Width: | Height: | Size: 289 B After Width: | Height: | Size: 204 B |
|
Before Width: | Height: | Size: 293 B After Width: | Height: | Size: 184 B |
|
Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 198 B |
|
Before Width: | Height: | Size: 296 B After Width: | Height: | Size: 204 B |
|
Before Width: | Height: | Size: 297 B After Width: | Height: | Size: 225 B |
|
Before Width: | Height: | Size: 255 B After Width: | Height: | Size: 133 B |
|
Before Width: | Height: | Size: 301 B After Width: | Height: | Size: 183 B |
|
Before Width: | Height: | Size: 306 B After Width: | Height: | Size: 197 B |
|
Before Width: | Height: | Size: 281 B After Width: | Height: | Size: 164 B |
|
Before Width: | Height: | Size: 821 B |
|
Before Width: | Height: | Size: 724 B |
|
Before Width: | Height: | Size: 667 B After Width: | Height: | Size: 667 B |
@@ -0,0 +1,102 @@
|
||||
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
|
||||