From 0999964f1ac556c6e63f97d968bb07ad1fee2ec0 Mon Sep 17 00:00:00 2001 From: reign Date: Mon, 7 Apr 2025 21:03:28 -0400 Subject: [PATCH] Add removing and updating modes to ItemConstructableTrigger --- .github/scripts/test_no_error_reports | 0 addon.gradle | 3 +- dependencies.gradle | 4 + gradlew | 0 .../structurelib/ClientProxy.java | 40 ++++ .../structurelib/CommonProxy.java | 3 + .../structurelib/StructureLib.java | 12 + .../constructable/ConstructableUtility.java | 17 +- .../item/ItemConstructableTrigger.java | 68 +++++- .../StructureProjectorCycleModeMessage.java | 37 ++++ .../structure/IStructureDefinition.java | 100 +++------ .../structure/IStructureElement.java | 6 +- .../structure/StructureUtility.java | 207 +++++++++++++++--- .../assets/structurelib/lang/en_US.lang | 5 + .../BUILDING.png} | Bin .../items/constructableTrigger/REMOVING.png | Bin 0 -> 821 bytes .../items/constructableTrigger/UPDATING.png | Bin 0 -> 724 bytes 17 files changed, 392 insertions(+), 110 deletions(-) mode change 100755 => 100644 .github/scripts/test_no_error_reports mode change 100755 => 100644 gradlew create mode 100644 src/main/java/com/gtnewhorizon/structurelib/net/StructureProjectorCycleModeMessage.java rename src/main/resources/assets/structurelib/textures/items/{itemConstructableTrigger.png => constructableTrigger/BUILDING.png} (100%) create mode 100644 src/main/resources/assets/structurelib/textures/items/constructableTrigger/REMOVING.png create mode 100644 src/main/resources/assets/structurelib/textures/items/constructableTrigger/UPDATING.png diff --git a/.github/scripts/test_no_error_reports b/.github/scripts/test_no_error_reports old mode 100755 new mode 100644 diff --git a/addon.gradle b/addon.gradle index 32cd6394..30e78921 100644 --- a/addon.gradle +++ b/addon.gradle @@ -1,7 +1,7 @@ javadoc { javadocTool = javaToolchains.javadocToolFor { // Last version with frames, but it also has search - languageVersion = JavaLanguageVersion.of(11) + languageVersion = JavaLanguageVersion.of(21) vendor = JvmVendorSpec.ADOPTIUM } @@ -20,7 +20,6 @@ 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) } } diff --git a/dependencies.gradle b/dependencies.gradle index eaa0cd18..147e7467 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -6,4 +6,8 @@ dependencies { testImplementation(platform('org.junit:junit-bom:5.9.2')) testImplementation('org.junit.jupiter:junit-jupiter') + + runtimeOnlyNonPublishable("com.github.GTNewHorizons:GT5-Unofficial:5.09.51.227:dev") { + exclude group: "com.github.GTNewHorizons", module: "StructureLib" + } } diff --git a/gradlew b/gradlew old mode 100755 new mode 100644 diff --git a/src/main/java/com/gtnewhorizon/structurelib/ClientProxy.java b/src/main/java/com/gtnewhorizon/structurelib/ClientProxy.java index 7fdf3bfe..21dc68b4 100644 --- a/src/main/java/com/gtnewhorizon/structurelib/ClientProxy.java +++ b/src/main/java/com/gtnewhorizon/structurelib/ClientProxy.java @@ -20,6 +20,7 @@ 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; @@ -42,17 +43,23 @@ 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 { @@ -259,6 +266,13 @@ 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); @@ -654,4 +668,30 @@ 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()); + } + } } diff --git a/src/main/java/com/gtnewhorizon/structurelib/CommonProxy.java b/src/main/java/com/gtnewhorizon/structurelib/CommonProxy.java index 72e7e372..8660c8fe 100644 --- a/src/main/java/com/gtnewhorizon/structurelib/CommonProxy.java +++ b/src/main/java/com/gtnewhorizon/structurelib/CommonProxy.java @@ -17,6 +17,7 @@ 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; @@ -64,6 +65,8 @@ public class CommonProxy { public void preInit(FMLPreInitializationEvent e) {} + public void init(FMLInitializationEvent e) {} + public long getOverworldTime() { return MinecraftServer.getServer().getEntityWorld().getTotalWorldTime(); } diff --git a/src/main/java/com/gtnewhorizon/structurelib/StructureLib.java b/src/main/java/com/gtnewhorizon/structurelib/StructureLib.java index 0d8504e0..3c36ba86 100644 --- a/src/main/java/com/gtnewhorizon/structurelib/StructureLib.java +++ b/src/main/java/com/gtnewhorizon/structurelib/StructureLib.java @@ -20,6 +20,7 @@ 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; @@ -27,6 +28,7 @@ import com.gtnewhorizon.structurelib.util.XSTR; import cpw.mods.fml.common.Loader; 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.FMLLoadCompleteEvent; import cpw.mods.fml.common.event.FMLPreInitializationEvent; import cpw.mods.fml.common.event.FMLServerStartingEvent; @@ -70,6 +72,11 @@ 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")); @@ -124,6 +131,11 @@ public class StructureLib { isGTLoaded = Loader.isModLoaded("gregtech"); } + @Mod.EventHandler + public void init(FMLInitializationEvent e) { + proxy.init(e); + } + @Mod.EventHandler public void loadComplete(FMLLoadCompleteEvent e) { proxy.loadComplete(e); diff --git a/src/main/java/com/gtnewhorizon/structurelib/alignment/constructable/ConstructableUtility.java b/src/main/java/com/gtnewhorizon/structurelib/alignment/constructable/ConstructableUtility.java index 92bd596f..4e588c31 100644 --- a/src/main/java/com/gtnewhorizon/structurelib/alignment/constructable/ConstructableUtility.java +++ b/src/main/java/com/gtnewhorizon/structurelib/alignment/constructable/ConstructableUtility.java @@ -57,12 +57,12 @@ public class ConstructableUtility { } IConstructable constructable = null; - if (tTileEntity instanceof IConstructableProvider) - constructable = ((IConstructableProvider) tTileEntity).getConstructable(); - else if (tTileEntity instanceof IConstructable) { - constructable = (IConstructable) tTileEntity; + if (tTileEntity instanceof IConstructableProvider aConstructableProvider) + constructable = aConstructableProvider.getConstructable(); + else if (tTileEntity instanceof IConstructable aConstructable) { + constructable = aConstructable; } else if (IMultiblockInfoContainer.contains(tTileEntity.getClass())) { - ExtendedFacing facing = tTileEntity instanceof IAlignment ? ((IAlignment) tTileEntity).getExtendedFacing() + ExtendedFacing facing = tTileEntity instanceof IAlignment alignment ? alignment.getExtendedFacing() : ExtendedFacing.of(ForgeDirection.getOrientation(aSide)); constructable = IMultiblockInfoContainer.get(tTileEntity.getClass()) .toConstructable(tTileEntity, facing); @@ -70,14 +70,13 @@ public class ConstructableUtility { if (constructable == null) return false; - if (aPlayer instanceof EntityPlayerMP) { - EntityPlayerMP playerMP = (EntityPlayerMP) aPlayer; + if (aPlayer instanceof EntityPlayerMP playerMP) { // server side and sneaking (already checked above) // do construct if (aPlayer.capabilities.isCreativeMode) { constructable.construct(aStack, false); - } else if (constructable instanceof ISurvivalConstructable) { - int built = ((ISurvivalConstructable) constructable).survivalConstruct( + } else if (constructable instanceof ISurvivalConstructable aConstructable) { + int built = aConstructable.survivalConstruct( aStack, ConfigurationHandler.INSTANCE.getAutoPlaceBudget(), ISurvivalBuildEnvironment.create(IItemSource.fromPlayer(playerMP), playerMP)); diff --git a/src/main/java/com/gtnewhorizon/structurelib/item/ItemConstructableTrigger.java b/src/main/java/com/gtnewhorizon/structurelib/item/ItemConstructableTrigger.java index 118c954f..d2bebe78 100644 --- a/src/main/java/com/gtnewhorizon/structurelib/item/ItemConstructableTrigger.java +++ b/src/main/java/com/gtnewhorizon/structurelib/item/ItemConstructableTrigger.java @@ -7,9 +7,14 @@ 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; @@ -23,12 +28,73 @@ import cpw.mods.fml.common.FMLCommonHandler; public class ItemConstructableTrigger extends Item { + private static final String TAG_MODE = "mode"; + + private static final IIcon[] textures = new IIcon[3]; + 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 int getMode(ItemStack held) { + if (held == null || !held.hasTagCompound() || !held.getTagCompound().hasKey(TAG_MODE)) { + // default behavior + return 0; + } + // get user-defined mode + return held.getTagCompound().getInteger(TAG_MODE); + } + @Override public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer player) { if (world.isRemote && getMovingObjectPositionFromPlayer(world, player, true) == null) { diff --git a/src/main/java/com/gtnewhorizon/structurelib/net/StructureProjectorCycleModeMessage.java b/src/main/java/com/gtnewhorizon/structurelib/net/StructureProjectorCycleModeMessage.java new file mode 100644 index 00000000..80858eac --- /dev/null +++ b/src/main/java/com/gtnewhorizon/structurelib/net/StructureProjectorCycleModeMessage.java @@ -0,0 +1,37 @@ +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 { + + @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; + } + } +} diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureDefinition.java b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureDefinition.java index 2b6b7732..1c949e17 100644 --- a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureDefinition.java +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureDefinition.java @@ -348,77 +348,47 @@ public interface IStructureDefinition { } if (checkBlocksIfNotNullForceCheckAllIfTrue != null) { - 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"); - } + 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"); if (StructureLibAPI.isDebugEnabled() && success) { StructureLib.LOGGER .info("Multi [" + basePositionX + ", " + basePositionY + ", " + basePositionZ + "] pass"); } return success; } else { - 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, "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"); - } + 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, "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"); } return true; } diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElement.java b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElement.java index 63d1b4b7..f7361eb1 100644 --- a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElement.java +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElement.java @@ -21,6 +21,7 @@ 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; @@ -137,12 +138,13 @@ public interface IStructureElement { */ default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) { + int mode = ItemConstructableTrigger.getMode(trigger); BlocksToPlace e = getBlocksToPlace(t, world, x, y, z, trigger, env); IItemSource source = env.getSource(); EntityPlayer actor = env.getActor(); Consumer chatter = env.getChatter(); if (e != null) { - if (check(t, world, x, y, z)) return PlaceResult.SKIP; + if (mode < 2 && check(t, world, x, y, z)) return PlaceResult.SKIP; if (e.getStacks() == null) { ItemStack taken = source.takeOne(e.getPredicate(), true); return StructureUtility.survivalPlaceBlock( @@ -159,7 +161,7 @@ public interface IStructureElement { chatter); } for (ItemStack stack : e.getStacks()) { - if (!source.takeOne(stack, true)) continue; + if (mode < 2 && !source.takeOne(stack, true)) continue; return StructureUtility.survivalPlaceBlock( stack, NBTMode.EXACT, diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/StructureUtility.java b/src/main/java/com/gtnewhorizon/structurelib/structure/StructureUtility.java index 03bb9e6b..588eed58 100644 --- a/src/main/java/com/gtnewhorizon/structurelib/structure/StructureUtility.java +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/StructureUtility.java @@ -28,6 +28,7 @@ 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; @@ -51,6 +52,7 @@ 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; @@ -206,7 +208,11 @@ public class StructureUtility { @Override public boolean placeBlock(Object o, World world, int x, int y, int z, ItemStack trigger) { - world.setBlock(x, y, z, StructureLibAPI.getBlockHint(), 14, 2); + 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)); return true; } @@ -219,11 +225,16 @@ public class StructureUtility { @Override public PlaceResult survivalPlaceBlock(Object o, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) { - if (check(o, world, x, y, z)) return PlaceResult.SKIP; + int mode = ItemConstructableTrigger.getMode(trigger); + if (mode < 2 && 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)) { - world.setBlock(x, y, z, Blocks.cobblestone, 0, 2); + 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)); } return PlaceResult.REJECT; } @@ -329,30 +340,74 @@ public class StructureUtility { */ public static PlaceResult survivalPlaceBlock(Block block, int meta, World world, int x, int y, int z, IItemSource s, EntityPlayer actor, Consumer chatter) { + if (x == -176 && y == 62 && z == -74) { + new Throwable().printStackTrace(); + } if (block == null) throw new NullPointerException(); - if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT; + int mode = 0; + 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 == 0 && !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 (!s.takeOne(stack, true)) { + if (mode == 1 && !world.isAirBlock(x, y, z)) { + 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 (mode < 2 && !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)) { + if (mode < 2) { + if (block instanceof ICustomBlockSetting) { + ICustomBlockSetting blockCustom = (ICustomBlockSetting) block; + blockCustom.setBlock(world, x, y, z, meta); + } else { + if (mode == 1 && !world.isAirBlock(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 (!s.takeOne(stack, false)) { - // rollback + } + } + if (mode == 2 && world.isAirBlock(x, y, z)) { + return PlaceResult.SKIP; + } + if (mode == 2 || !s.takeOne(stack, false)) { + // 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)); world.setBlockToAir(x, y, z); + if (mode == 2 && !tryGiveOrDropItem(world, actor, toGive)) { + // 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; } @@ -441,27 +496,75 @@ public class StructureUtility { public static PlaceResult survivalPlaceBlock(ItemStack stack, NBTMode nbtMode, NBTTagCompound tag, boolean assumeStackPresent, World world, int x, int y, int z, IItemSource s, EntityPlayer actor, @Nullable Consumer chatter) { + if (x == -176 && y == 62 && z == -74) { + System.out.println("At this fucking coordinate"); + new Throwable().printStackTrace(); + } if (stack == null) throw new NullPointerException(); if (stack.stackSize != 1) throw new IllegalArgumentException(); if (!(stack.getItem() instanceof ItemBlock)) throw new IllegalArgumentException(); - if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT; - if (!assumeStackPresent && !s.takeOne(stack, true)) { + int mode = 0; + 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 == 1 && !world.isAirBlock(x, y, z)) { + 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 (mode == 0 && !StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT; + if (mode < 2 && !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 (!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 + if (mode < 2) { + if (mode == 1 && !world.isAirBlock(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 == 2 && world.isAirBlock(x, y, z)) { + return PlaceResult.SKIP; + } + if (mode == 2 || !s.takeOne(stack, false)) { + // 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)); world.setBlockToAir(x, y, z); + if (mode == 2 && !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 nontheless 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 nonetheless in + * case you want this as a fallback to something else. */ @SuppressWarnings("unchecked") public static IStructureElement isAir() { @@ -470,8 +573,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 nontheless - * 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 + * nonetheless in case you want this as a fallback to something else. */ @SuppressWarnings("unchecked") public static IStructureElement notAir() { @@ -697,7 +800,7 @@ public class StructureUtility { List> hints = allKnownTiers == null ? Collections.emptyList() : allKnownTiers; if (hints.stream().anyMatch(Objects::isNull)) throw new IllegalArgumentException(); IStructureElementCheckOnly check = ofBlocksTiered(tierExtractor, notSet, setter, getter); - return new StructureElement_Bridge() { + return new StructureElement_Bridge<>() { @Override public boolean check(T t, World world, int x, int y, int z) { @@ -754,12 +857,13 @@ public class StructureUtility { @Override public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) { + int mode = ItemConstructableTrigger.getMode(trigger); Pair 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 (Objects.equals(tier, tierExtractor.convert(hint.getKey(), hint.getValue()))) + if (mode < 2 && Objects.equals(tier, tierExtractor.convert(hint.getKey(), hint.getValue()))) return PlaceResult.SKIP; return StructureUtility.survivalPlaceBlock( hint.getKey(), @@ -849,7 +953,8 @@ public class StructureUtility { @Override public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) { - if (check(t, world, x, y, z)) return PlaceResult.SKIP; + int mode = ItemConstructableTrigger.getMode(trigger); + if (mode < 2 && check(t, world, x, y, z)) return PlaceResult.SKIP; if (getBlock() == null) return PlaceResult.REJECT; return StructureUtility.survivalPlaceBlock( getBlock(), @@ -936,7 +1041,8 @@ public class StructureUtility { @Deprecated public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s, EntityPlayerMP actor, Consumer chatter) { - if (check(t, world, x, y, z)) return PlaceResult.SKIP; + int mode = ItemConstructableTrigger.getMode(trigger); + if (mode < 2 && 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); } @@ -944,7 +1050,8 @@ public class StructureUtility { @Override public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) { - if (check(t, world, x, y, z)) return PlaceResult.SKIP; + int mode = ItemConstructableTrigger.getMode(trigger); + if (mode < 2 && check(t, world, x, y, z)) return PlaceResult.SKIP; if (init()) return StructureUtility.survivalPlaceBlock( block, meta, @@ -1572,7 +1679,9 @@ public class StructureUtility { @Deprecated public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) { - if (world.getBlock(x, y, z) == defaultBlock && world.getBlockMetadata(x, y, z) == defaultMeta) + int mode = ItemConstructableTrigger.getMode(trigger); + if (mode < 2 && world.getBlock(x, y, z) == defaultBlock + && world.getBlockMetadata(x, y, z) == defaultMeta) return PlaceResult.SKIP; return StructureUtility.survivalPlaceBlock( defaultBlock, @@ -1618,7 +1727,9 @@ public class StructureUtility { @Deprecated public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) { - if (world.getBlock(x, y, z) == defaultBlock && world.getBlockMetadata(x, y, z) == defaultMeta) + int mode = ItemConstructableTrigger.getMode(trigger); + if (mode < 2 && world.getBlock(x, y, z) == defaultBlock + && world.getBlockMetadata(x, y, z) == defaultMeta) return PlaceResult.SKIP; return StructureUtility.survivalPlaceBlock( defaultBlock, @@ -1878,7 +1989,8 @@ public class StructureUtility { @Deprecated public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s, EntityPlayerMP actor, Consumer chatter) { - if (predicate.test(t)) + int mode = ItemConstructableTrigger.getMode(trigger); + if (mode == 2 || predicate.test(t)) return downstream.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter); return placeResultWhenDisabled; } @@ -1886,7 +1998,9 @@ public class StructureUtility { @Override public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) { - if (predicate.test(t)) return downstream.survivalPlaceBlock(t, world, x, y, z, trigger, env); + int mode = ItemConstructableTrigger.getMode(trigger); + if (mode == 2 || predicate.test(t)) + return downstream.survivalPlaceBlock(t, world, x, y, z, trigger, env); return placeResultWhenDisabled; } }; @@ -3253,4 +3367,35 @@ 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) { + int mode = ItemConstructableTrigger.getMode(trigger); + switch (mode) { + case 0: + onCreate.run(); + break; + case 1: + onUpdate.run(); + break; + case 2: + onRemove.run(); + break; + } + } + + public static boolean tryGiveOrDropItem(World world, EntityPlayer actor, ItemStack stack) { + if (actor.inventory.addItemStackToInventory(stack)) { + actor.inventoryContainer.detectAndSendChanges(); + return true; + } + return world.spawnEntityInWorld(new EntityItem(world, actor.posX, actor.posY, actor.posZ, stack)); + } } diff --git a/src/main/resources/assets/structurelib/lang/en_US.lang b/src/main/resources/assets/structurelib/lang/en_US.lang index e188dac1..b741f628 100644 --- a/src/main/resources/assets/structurelib/lang/en_US.lang +++ b/src/main/resources/assets/structurelib/lang/en_US.lang @@ -22,6 +22,9 @@ 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. @@ -40,6 +43,8 @@ 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 structurelib.autoplace.built_stat=Built %s blocks structurelib.autoplace.complete=§aThis multiblock has completed! diff --git a/src/main/resources/assets/structurelib/textures/items/itemConstructableTrigger.png b/src/main/resources/assets/structurelib/textures/items/constructableTrigger/BUILDING.png similarity index 100% rename from src/main/resources/assets/structurelib/textures/items/itemConstructableTrigger.png rename to src/main/resources/assets/structurelib/textures/items/constructableTrigger/BUILDING.png diff --git a/src/main/resources/assets/structurelib/textures/items/constructableTrigger/REMOVING.png b/src/main/resources/assets/structurelib/textures/items/constructableTrigger/REMOVING.png new file mode 100644 index 0000000000000000000000000000000000000000..adcb90dee9cbcc7cbfe53e8600c0b0f223e6a5fa GIT binary patch literal 821 zcmV-51Iqk~P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGf6951U69E94oEQKA0?J85K~y+TZIe%E zTvZswfA>x1Ihq@*L(?RNl$02nI+)37M%#r7wm66=8fhqG7AAGm;-XaDL>=4WN{xyX zL^mn~1VIbZkrouJh#f=KP+DzmwM|Jw+DR5k=g)h2@3?rAF+$Jo!a3*r?%{lf28100 z9tK`Q%au}TfnxC=z>v#rCX?yF_d{Gawk83;($3C*L^|8s~q++v1zOX1zfd zaD3iv9Xi;*i>dq4NCFHxc_89QIJ+jJu_q52);5sD;NTRi?kRnH_82`jj`{o}oVR|6 zD9teu!c!kOKqs&p&kHzwxP&o>aMF2&$G%pW>`{16xAu4MPZ0r&p(WY-NEx`fm~4djD&lC@mm9k0a?A7Fh~2@Fjsd2UJ=B~SMo}(p zfls!w#EHv4eg!-M2+s=;_7P!!U2^VK@a!}4&VMhH&dW*XRj*x8z4oV)soe@5+OE23 zldvnoo)LDOD0N#9_OJb&8M)hrRj-~?t$I#QYFJKcSmuG}R4Y&GlMP!n(LN+$2ZgmX zn}|)c_wA^bTU?NujWY<**uU{kB_203GY2q@D~S5sw^Na@!ECnSKQjb}#$GoQ6OT5< ztiQ%JcwRs}?ocYtQ!HKt0&=;nWHMd1J?DP`HIPPtGyiB700000NkvXXu0mjf3A}i- literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/structurelib/textures/items/constructableTrigger/UPDATING.png b/src/main/resources/assets/structurelib/textures/items/constructableTrigger/UPDATING.png new file mode 100644 index 0000000000000000000000000000000000000000..c9ed6178e393652942d771591b1f1693bcbfd78d GIT binary patch literal 724 zcmV;_0xSKAP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGf6951U69E94oEQKA0%=J^K~y+Tb(2p> zR8bVhf9E+f>Qyudo(NhNND$&w)EFU2&>w`BwJIj4MQ&Uaw2J^S*iSw0JX%xw8Who)aj@<{zIavj01q?>r z3Zw3*QFlNNo>$M#YsP+8i6_=hUd%X4G9V>IP!etprAZr9X)i z&1$nj+2mic{-rie^MzX19H z3w$dO0C+iiilgu5iFS@t{B)Sh?_(r}XNg|0TV!T82kF;>NKq&yEXu&sP#9_!8L->GQ2JqohgO@N+&+o1AV|w z;8I!^o^45a_?@>!t1E$C-~_M%%w^M74%7hmfSYMq@c#nEi4f{uJ?uOH0000kE! literal 0 HcmV?d00001