From 43fc39c99b41d00e5e18419bd84c6f851fb9e5a8 Mon Sep 17 00:00:00 2001 From: kstvr32 <109012629+kstvr32@users.noreply.github.com> Date: Sat, 14 Dec 2024 13:13:34 -0500 Subject: [PATCH] Add error hint when block is invalid (#31) Co-authored-by: kstvr32 Co-authored-by: Martin Robertz --- .../structurelib/StructureLib.java | 4 + .../gui/GuiScreenConfigureChannels.java | 78 ++++++- .../structure/IStructureDefinition.java | 15 +- .../structure/IStructureElement.java | 15 ++ .../structure/IStructureElementChain.java | 10 + .../structure/IStructureElementCheckOnly.java | 5 + .../structure/IStructureNavigate.java | 5 + .../structure/LazyStructureElement.java | 5 + .../structure/StructureUtility.java | 194 +++++++++++++++++- .../structurelib/textures/gui/channels.png | Bin 6738 -> 2825 bytes 10 files changed, 315 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/gtnewhorizon/structurelib/StructureLib.java b/src/main/java/com/gtnewhorizon/structurelib/StructureLib.java index c441bc40..0d8504e0 100644 --- a/src/main/java/com/gtnewhorizon/structurelib/StructureLib.java +++ b/src/main/java/com/gtnewhorizon/structurelib/StructureLib.java @@ -100,6 +100,8 @@ public class StructureLib { } }; + public static boolean isGTLoaded; + @Mod.EventHandler public void preInit(FMLPreInitializationEvent e) { ConfigurationHandler.INSTANCE.init(e.getSuggestedConfigurationFile()); @@ -118,6 +120,8 @@ public class StructureLib { if (Loader.isModLoaded(STRUCTURECOMPAT_MODID)) { COMPAT = Loader.instance().getIndexedModList().get(STRUCTURECOMPAT_MODID).getMod(); } + + isGTLoaded = Loader.isModLoaded("gregtech"); } @Mod.EventHandler diff --git a/src/main/java/com/gtnewhorizon/structurelib/gui/GuiScreenConfigureChannels.java b/src/main/java/com/gtnewhorizon/structurelib/gui/GuiScreenConfigureChannels.java index 314c2519..10dcba09 100644 --- a/src/main/java/com/gtnewhorizon/structurelib/gui/GuiScreenConfigureChannels.java +++ b/src/main/java/com/gtnewhorizon/structurelib/gui/GuiScreenConfigureChannels.java @@ -25,6 +25,15 @@ import com.gtnewhorizon.structurelib.gui.GuiScrollableList.IGuiScreen; public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen { + private static final int ADD_BTN = 0; + private static final int UNSET_BTN = 1; + private static final int WIPE_BTN = 2; + private static final int SHOW_ERROR_BTN = 3; + private static final int GT_NO_HATCH_BTN = 4; + + private static final String SHOW_ERROR_CHANNEL = "show_error"; + private static final String GT_NO_HATCH_CHANNEL = "gt_no_hatch"; + private static final int KEY_MAX_WIDTH = 50; private final ItemStack trigger; private final GuiChannelsList list; @@ -128,7 +137,7 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen addButton( new GuiButton( - 0, + ADD_BTN, guiLeft + 12, guiTop + 157, 47, @@ -136,7 +145,7 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen I18n.format("item.structurelib.constructableTrigger.gui.add"))); addButton( new GuiButton( - 1, + UNSET_BTN, guiLeft + 65, guiTop + 157, 47, @@ -144,13 +153,27 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen I18n.format("item.structurelib.constructableTrigger.gui.unset"))); addButton( new GuiButton( - 2, + WIPE_BTN, guiLeft + 118, guiTop + 157, 47, 20, I18n.format("item.structurelib.constructableTrigger.gui.wipe"))); + addButton( + new GuiButton( + SHOW_ERROR_BTN, + StructureLib.isGTLoaded ? guiLeft + 12 : guiLeft + 52, + guiTop + 180, + 73, + 20, + "")); + + // only show GT hatch button if GT is loaded + if (StructureLib.isGTLoaded) { + addButton(new GuiButton(GT_NO_HATCH_BTN, guiLeft + 92, guiTop + 180, 73, 20, "")); + } + updateButtons(); } @@ -171,7 +194,7 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen @Override public int getYSize() { - return 188; + return 211; } @Override @@ -250,10 +273,27 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen // STACKOVERFLOW! String keyText = key.getText(); boolean existing = !StringUtils.isEmpty(keyText) && ChannelDataAccessor.hasSubChannel(trigger, keyText); - getButtonList().get(0).displayString = existing ? I18n.format("item.structurelib.constructableTrigger.gui.set") + getButtonList().get(ADD_BTN).displayString = existing + ? I18n.format("item.structurelib.constructableTrigger.gui.set") : I18n.format("item.structurelib.constructableTrigger.gui.add"); - getButtonList().get(0).enabled = !StringUtils.isBlank(value.getText()); - getButtonList().get(1).enabled = existing && !StringUtils.isBlank(value.getText()); + getButtonList().get(ADD_BTN).enabled = !StringUtils.isBlank(value.getText()); + getButtonList().get(UNSET_BTN).enabled = existing && !StringUtils.isBlank(value.getText()); + + if (ChannelDataAccessor.hasSubChannel(trigger, SHOW_ERROR_CHANNEL)) { + getButtonList().get(SHOW_ERROR_BTN).displayString = "Hide Errors"; + } else { + getButtonList().get(SHOW_ERROR_BTN).displayString = "Show Errors"; + } + + // this button only exists if GT is loaded. + if (StructureLib.isGTLoaded) { + if (ChannelDataAccessor.hasSubChannel(trigger, GT_NO_HATCH_CHANNEL)) { + getButtonList().get(GT_NO_HATCH_BTN).displayString = "Hatches"; + } else { + getButtonList().get(GT_NO_HATCH_BTN).displayString = "No Hatch"; + } + } + } private int getValue() { @@ -268,18 +308,35 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen protected void actionPerformed(GuiButton btn) { if (btn == null) return; switch (btn.id) { - case 0: + case ADD_BTN: int value = getValue(); if (value <= 0) return; ChannelDataAccessor.setChannelData(trigger, key.getText(), value); break; - case 1: + case UNSET_BTN: ChannelDataAccessor.unsetChannelData(trigger, key.getText()); break; - case 2: + case WIPE_BTN: ChannelDataAccessor.wipeChannelData(trigger); break; + case SHOW_ERROR_BTN: + if (ChannelDataAccessor.hasSubChannel(trigger, SHOW_ERROR_CHANNEL)) { + ChannelDataAccessor.unsetChannelData(trigger, SHOW_ERROR_CHANNEL); + } else { + ChannelDataAccessor.setChannelData(trigger, SHOW_ERROR_CHANNEL, 1); + } + break; + case GT_NO_HATCH_BTN: + if (ChannelDataAccessor.hasSubChannel(trigger, GT_NO_HATCH_CHANNEL)) { + ChannelDataAccessor.unsetChannelData(trigger, GT_NO_HATCH_CHANNEL); + } else { + ChannelDataAccessor.setChannelData(trigger, GT_NO_HATCH_CHANNEL, 1); + } + break; } + + updateButtons(); + super.actionPerformed(btn); } @@ -350,6 +407,7 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen Entry e = getElementAt(elementIndex); if (e != null) { ChannelDataAccessor.unsetChannelData(trigger, e.getKey()); + updateButtons(); return false; } } diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureDefinition.java b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureDefinition.java index 8c152071..2b6b7732 100644 --- a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureDefinition.java +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureDefinition.java @@ -12,6 +12,7 @@ import net.minecraft.world.World; import com.gtnewhorizon.structurelib.StructureLib; 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; @@ -50,7 +51,7 @@ import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing; * itself. *
  • The "C" position would be the number of blocks between you and controller, not counting controller itself.
  • * - * + * * @param Type of the context object. Usually this will be the multiblock controller. */ public interface IStructureDefinition { @@ -72,7 +73,7 @@ public interface IStructureDefinition { /** * Run a structure check. - * + * * @param object context object. usually multiblock controller. * @param piece the structure piece's string identifier. * @param world the world object this check takes place in. @@ -107,7 +108,7 @@ public interface IStructureDefinition { /** * Spawn hint particles. Should not be called on server side. - * + * * @param object context object. usually multiblock controller. * @param piece the structure piece's string identifier. * @param world the world object this check takes place in. @@ -320,7 +321,7 @@ public interface IStructureDefinition { /** * Low level utility. - * + * * @param object context object. usually multiblock controller. * @param trigger The trigger item that contains channel data. * @param elements the structure piece @@ -394,6 +395,10 @@ public interface IStructureDefinition { 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"); @@ -420,7 +425,7 @@ public interface IStructureDefinition { /** * Create a new instance of builder. - * + * * @param type of context object */ static StructureDefinition.Builder builder() { diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElement.java b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElement.java index eb16fcc5..63d1b4b7 100644 --- a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElement.java +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElement.java @@ -32,6 +32,16 @@ public interface IStructureElement { boolean check(T t, World world, int x, int y, int z); + /** + * Pure (stateless and side effect free) function to test if current block could be valid. Used to give user hints + * about incorrectly placed blocks given a controller and trigger item. If couldBeValid(...) == false for a set of + * channels, with those same tiers, check(...) == false. Not required, defaults to true which is always safe but + * won't give the user error hints. + */ + default boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return true; + } + boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger); boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger); @@ -185,6 +195,11 @@ public interface IStructureElement { return IStructureElement.this.check(t, world, x, y, z); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return IStructureElement.this.couldBeValid(t, world, x, y, z, trigger); + } + @Override public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { return IStructureElement.this.spawnHint(t, world, x, y, z, trigger); diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementChain.java b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementChain.java index 819990fd..c527c76a 100644 --- a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementChain.java +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementChain.java @@ -31,6 +31,16 @@ public interface IStructureElementChain extends IStructureElement { return false; } + @Override + default boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + for (IStructureElement fallback : fallbacks()) { + if (fallback.couldBeValid(t, world, x, y, z, trigger)) { + return true; + } + } + return false; + } + @Override default boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { for (IStructureElement fallback : fallbacks()) { diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementCheckOnly.java b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementCheckOnly.java index 03f2b859..ee8144a5 100644 --- a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementCheckOnly.java +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementCheckOnly.java @@ -14,6 +14,11 @@ public interface IStructureElementCheckOnly extends IStructureElement { return false; } + @Override + default boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return true; + } + @Override default boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { return false; diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureNavigate.java b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureNavigate.java index 6ce52c52..602a4993 100644 --- a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureNavigate.java +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureNavigate.java @@ -17,6 +17,11 @@ interface IStructureNavigate extends IStructureElement { return true; } + @Override + default boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return true; + } + @Override default boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { return true; diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/LazyStructureElement.java b/src/main/java/com/gtnewhorizon/structurelib/structure/LazyStructureElement.java index 3f8cfda3..0959ee67 100644 --- a/src/main/java/com/gtnewhorizon/structurelib/structure/LazyStructureElement.java +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/LazyStructureElement.java @@ -32,6 +32,11 @@ class LazyStructureElement implements IStructureElementDeferred { return get(t).check(t, world, x, y, z); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return get(t).couldBeValid(t, world, x, y, z, trigger); + } + @Override public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { return get(t).placeBlock(t, world, x, y, z, trigger); diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/StructureUtility.java b/src/main/java/com/gtnewhorizon/structurelib/structure/StructureUtility.java index a6baa3c9..f44e3095 100644 --- a/src/main/java/com/gtnewhorizon/structurelib/structure/StructureUtility.java +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/StructureUtility.java @@ -155,6 +155,11 @@ public class StructureUtility { return world.isAirBlock(x, y, z); } + @Override + public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) { + return check(t, world, x, y, z); + } + @Override public boolean spawnHint(Object o, World world, int x, int y, int z, ItemStack trigger) { StructureLibAPI.hintParticle(world, x, y, z, StructureLibAPI.getBlockHint(), 13); @@ -186,6 +191,11 @@ public class StructureUtility { return !world.isAirBlock(x, y, z); } + @Override + public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) { + return check(t, world, x, y, z); + } + @Override public boolean spawnHint(Object o, World world, int x, int y, int z, ItemStack trigger) { StructureLibAPI.hintParticle(world, x, y, z, StructureLibAPI.getBlockHint(), 14); @@ -225,6 +235,11 @@ public class StructureUtility { return false; } + @Override + public boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) { + return check(t, world, x, y, z); + } + @Override public boolean spawnHint(Object o, World world, int x, int y, int z, ItemStack trigger) { StructureLibAPI.hintParticle(world, x, y, z, StructureLibAPI.getBlockHint(), 15); @@ -479,6 +494,11 @@ public class StructureUtility { return true; } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return check(t, world, x, y, z); + } + @Override public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { StructureLibAPI.hintParticle(world, x, y, z, StructureLibAPI.getBlockHint(), meta); @@ -499,6 +519,11 @@ public class StructureUtility { return true; } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return check(t, world, x, y, z); + } + @Override public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { StructureLibAPI.hintParticle(world, x, y, z, icons.get()); @@ -519,6 +544,11 @@ public class StructureUtility { return true; } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return check(t, world, x, y, z); + } + @Override public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { StructureLibAPI.hintParticleTinted(world, x, y, z, icons.get(), RGBa); @@ -667,9 +697,23 @@ public class StructureUtility { } private Pair getHint(ItemStack trigger) { + if (hints.isEmpty()) return null; return hints.get(Math.min(Math.max(trigger.stackSize, 1), hints.size()) - 1); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + Pair hint = getHint(trigger); + if (hint == null) return true; + TIER hintTier = tierExtractor.convert(hint.getKey(), hint.getValue()); + Block block = world.getBlock(x, y, z); + int meta = world.getBlockMetadata(x, y, z); + TIER worldTier = tierExtractor.convert(block, meta); + + // if the block is in the same tier as the hint block, this could be valid + return Objects.equals(hintTier, worldTier); + } + @Override public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { Pair hint = getHint(trigger); @@ -767,6 +811,11 @@ public class StructureUtility { return world.getBlock(x, y, z) == getBlock() && (wildcard || world.getBlockMetadata(x, y, z) == meta); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return check(t, world, x, y, z); + } + @Override public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { if (getBlock() == null) return error().spawnHint(t, world, x, y, z, trigger); @@ -841,10 +890,16 @@ public class StructureUtility { @Override public boolean check(T t, World world, int x, int y, int z) { - if (init()) return world.getBlock(x, y, z) != block && world.getBlockMetadata(x, y, z) == meta; + if (init()) return world.getBlock(x, y, z) == block && world.getBlockMetadata(x, y, z) == meta; else return fallback.check(t, world, x, y, z); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + if (init()) return world.getBlock(x, y, z) == block && world.getBlockMetadata(x, y, z) == meta; + else return fallback.couldBeValid(t, world, x, y, z, trigger); + } + @Override public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { if (init()) { @@ -921,6 +976,11 @@ public class StructureUtility { return blocsMap.getOrDefault(worldBlock, MIN_VALUE) == worldBlock.getDamageValue(world, x, y, z); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return check(t, world, x, y, z); + } + @Override public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { StructureLibAPI.hintParticle(world, x, y, z, hintBlock, hintMeta); @@ -958,6 +1018,11 @@ public class StructureUtility { .contains(worldBlock.getDamageValue(world, x, y, z)); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return check(t, world, x, y, z); + } + @Override public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { StructureLibAPI.hintParticle(world, x, y, z, hintBlock, hintMeta); @@ -982,6 +1047,11 @@ public class StructureUtility { return block == worldBlock && meta == worldBlock.getDamageValue(world, x, y, z); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return check(t, world, x, y, z); + } + @Override public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { StructureLibAPI.hintParticle(world, x, y, z, hintBlock, hintMeta); @@ -1016,6 +1086,13 @@ public class StructureUtility { return iBlockAdder.apply(t, worldBlock, worldBlock.getDamageValue(world, x, y, z)); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + // calling iBlockAdder can potentially modify external state + // therefore we assume this can always be valid. + return true; + } + @Override public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { StructureLibAPI.hintParticle(world, x, y, z, hintBlock, hintMeta); @@ -1049,6 +1126,11 @@ public class StructureUtility { return blocsMap.getOrDefault(worldBlock, MIN_VALUE) == worldBlock.getDamageValue(world, x, y, z); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return check(t, world, x, y, z); + } + @Override public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { ((ICustomBlockSetting) defaultBlock).setBlock(world, x, y, z, defaultMeta); @@ -1093,6 +1175,11 @@ public class StructureUtility { return blocsMap.getOrDefault(worldBlock, MIN_VALUE) == worldBlock.getDamageValue(world, x, y, z); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return check(t, world, x, y, z); + } + @Override public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { world.setBlock(x, y, z, defaultBlock, defaultMeta, 2); @@ -1158,6 +1245,11 @@ public class StructureUtility { .contains(worldBlock.getDamageValue(world, x, y, z)); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return check(t, world, x, y, z); + } + @Override public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { ((ICustomBlockSetting) defaultBlock).setBlock(world, x, y, z, defaultMeta); @@ -1203,6 +1295,11 @@ public class StructureUtility { .contains(worldBlock.getDamageValue(world, x, y, z)); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return check(t, world, x, y, z); + } + @Override public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { world.setBlock(x, y, z, defaultBlock, defaultMeta, 2); @@ -1260,6 +1357,11 @@ public class StructureUtility { return block == worldBlock && meta == worldBlock.getDamageValue(world, x, y, z); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return check(t, world, x, y, z); + } + @Override public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { ((ICustomBlockSetting) defaultBlock).setBlock(world, x, y, z, defaultMeta); @@ -1287,6 +1389,11 @@ public class StructureUtility { return block == worldBlock && meta == worldBlock.getDamageValue(world, x, y, z); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return check(t, world, x, y, z); + } + @Override public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { world.setBlock(x, y, z, defaultBlock, defaultMeta, 2); @@ -1323,6 +1430,11 @@ public class StructureUtility { return block == world.getBlock(x, y, z); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return check(t, world, x, y, z); + } + @Override public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { ((ICustomBlockSetting) defaultBlock).setBlock(world, x, y, z, defaultMeta); @@ -1350,6 +1462,11 @@ public class StructureUtility { return block == world.getBlock(x, y, z); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return check(t, world, x, y, z); + } + @Override public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { world.setBlock(x, y, z, defaultBlock, defaultMeta, 2); @@ -1416,6 +1533,13 @@ public class StructureUtility { return iBlockAdder.apply(t, worldBlock, worldBlock.getDamageValue(world, x, y, z)); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + // calling iBlockAdder can potentially modify external state + // therefore we assume this can always be valid. + return true; + } + @Override public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { ((ICustomBlockSetting) defaultBlock).setBlock(world, x, y, z, defaultMeta); @@ -1455,6 +1579,13 @@ public class StructureUtility { return iBlockAdder.apply(t, worldBlock, worldBlock.getDamageValue(world, x, y, z)); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + // calling iBlockAdder can potentially modify external state + // therefore we assume this can always be valid. + return true; + } + @Override public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { world.setBlock(x, y, z, defaultBlock, defaultMeta, 2); @@ -1510,6 +1641,13 @@ public class StructureUtility { return iTileAdder.apply(t, tileEntity); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + // calling iTileAdder can potentially modify external state + // therefore we assume this can always be valid. + return true; + } + @Override public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { StructureLibAPI.hintParticle(world, x, y, z, hintBlock, hintMeta); @@ -1536,6 +1674,13 @@ public class StructureUtility { return tileClass.isInstance(tileEntity) && iTileAdder.test(t, tileClass.cast(tileEntity)); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + // calling iTileAdder can potentially modify external state + // therefore we assume this can always be valid. + return true; + } + @Override public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { StructureLibAPI.hintParticle(world, x, y, z, hintBlock, hintMeta); @@ -1569,6 +1714,11 @@ public class StructureUtility { return check; } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return element.couldBeValid(t, world, x, y, z, trigger); + } + @Override public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { return element.placeBlock(t, world, x, y, z, trigger); @@ -1620,6 +1770,11 @@ public class StructureUtility { return check; } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return element.couldBeValid(t, world, x, y, z, trigger); + } + @Override public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { return element.placeBlock(t, world, x, y, z, trigger); @@ -1678,6 +1833,13 @@ public class StructureUtility { return predicate.test(t) && downstream.check(t, world, x, y, z); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + // this COULD potentially impact external state + // but predicate really should not have side effects + return predicate.test(t) && downstream.couldBeValid(t, world, x, y, z, trigger); + } + @Override public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { return predicate.test(t) && downstream.spawnHint(t, world, x, y, z, trigger); @@ -1774,6 +1936,11 @@ public class StructureUtility { return elem.check(t.getCurrentContext(), world, x, y, z); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return elem.couldBeValid(t.getCurrentContext(), world, x, y, z, trigger); + } + @Override public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { return elem.spawnHint(t.getCurrentContext(), world, x, y, z, trigger); @@ -1861,6 +2028,11 @@ public class StructureUtility { return to.get().check(t, world, x, y, z); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return to.get().couldBeValid(t, world, x, y, z, trigger); + } + @Override public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { return to.get().placeBlock(t, world, x, y, z, trigger); @@ -1914,6 +2086,11 @@ public class StructureUtility { return to.apply(t).check(t, world, x, y, z); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return to.apply(t).couldBeValid(t, world, x, y, z, trigger); + } + @Override public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { return to.apply(t).placeBlock(t, world, x, y, z, trigger); @@ -2126,6 +2303,11 @@ public class StructureUtility { return to.apply(t, null).check(t, world, x, y, z); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return to.apply(t, trigger).couldBeValid(t, world, x, y, z, trigger); + } + @Override public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { return to.apply(t, trigger).placeBlock(t, world, x, y, z, trigger); @@ -2318,6 +2500,11 @@ public class StructureUtility { return toCheck.apply(t).check(t, world, x, y, z); } + @Override + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + return to.apply(t, trigger).couldBeValid(t, world, x, y, z, trigger); + } + @Override public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) { return to.apply(t, trigger).placeBlock(t, world, x, y, z, trigger); @@ -2562,6 +2749,11 @@ public class StructureUtility { return backing.check(t, world, x, y, z); } + public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) { + ItemStack newTrigger = ChannelDataAccessor.withChannel(trigger, channel); + return backing.couldBeValid(t, world, x, y, z, newTrigger); + } + public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) { ItemStack newTrigger = ChannelDataAccessor.withChannel(trigger, channel); // We know spawnHint will only be called on client side, so the lack of player isn't quite an issue diff --git a/src/main/resources/assets/structurelib/textures/gui/channels.png b/src/main/resources/assets/structurelib/textures/gui/channels.png index c76cdf33446316a04eb6393364377cd34ad275a3..4726863dbf86452e2de318a3937175b7f9999f02 100644 GIT binary patch literal 2825 zcmeHJeNYo;8ei&11++S2MXJuYtXB_uZn9q?*_D7oXoxisffgLmy4l@?t;ue>yFh}X zRBOGq<1y{k)}9D+WmNicw%V~D(kUJYSFdOBhN+HYp<3)wu#^uE(edlv4GE<)&e;EZ znKzsFefIbK-skx}&-+eZbmiJ-#Vv?~AZV5&$CeL4F<=@4&6o~;N|Z(qSnLq9vRsa= zEZC*U9?>U2P;$qfSDeYVHH+G|kzf4oC>HNbke}O=l60hVPRcV28n&RHjkV7-4ZhjE z?^JvK`N!fHFWj+nw`pDcx&yD(yn0~Iyd$sF-K}cy#4}O2P ze237x|77v%eQ)mX?5~ggcjlBGHm&otGk5pWx0!S)wsCanJ4dg7Te=GUbm>&;QWDvF zdFT9{H_wgGZA~q#x;8JiKCXgRR$khCVBw=%*VGQvjYMlMfiGK>aF&?oEBxToAB$V6 zS_V?SeErtN)h`yjbZ*|d^V@Hzd7~}uk8ZA9)kj=NeA%Al(+HeWqEaDXJbY$3?2JERKKfktZb^O zCcwUN``zrs;QXq0-#fN=#ntJx>t8o<4}N9->uA!U<;RU*e=+l5=TIf`EftTRJA*yl zKj5wJS)Q}1Xl~I5AJ5+Z$f*sDBR{zWd0XA^%_Y}nH6hAL`m3WxP?gv~&RP|M;^*q$ z80asxi4Zg`Q!Fgjik+(&PWBsEUUmzHpg#c65R{e?46xiLL4)0bSCp*CU{f;!i@X&n zGC474AWJ9}b1D^~pfb0RtK7uVJd%+XMZ&}knCoUE0*4{m zCM#0xbirA&BEXb^GGJ(SP%I}9Kmx;Q3h!a^ZI4Gtz{-l0YFdCX8Y?O)3>Bn7R=h@> zrfDNa7zqLe2viM88XH6k!myIhd8B~f>toA3>kDZX$P zJZBVqf*-(Cup2(HmEGZVO`!Fxy`n!5_5cb`pfu6*vo^noM}HFzKe{<^o8bEeGMc9j z1Y$5YnJd%daoDVgzA3}YoX9ibsmH_9sho#LO(xEaQkX!a7Q#%Sq{nRHQ$1$NWFewF z4oTHmi4$}lz-$l!i3Jdt2s43tD3(MimUW{n&Rb9qL1HXHQdBC7NB#2@5wsrbn;ehM z!vh|{!;=Ef2`EVt7L;-mENbB>93?FjiIE&nlDvSxJjXmPD}EM~O!Tu}!5EOd2&^m1 ztaLf72w}h?OP7z;JOB#%TavAT44YATLd%&BG*j?p9z!VD8VB`7NB zEu9R<3?#^2SDpdE0|{BZ?SN}IB_>M|SWQ+6W!Yy%bZ1!i8Gq*lhl6J|*2ZcA;KB%k z!7L1c7ZNywQw(YP16X2Fu*{2|(EXs{qXdIb+IU@aL>25FnlMKWNP+O|BvZub6X9^L zGb}frg36W)JQ6W-rL5!?z-0jXa2zJyqt?8e!&x^^qh<<}nZjru^a@X*Cbx;8a5H5w z(R9>bl|5Pos|YK-Ks6u^XuGgDaB`#`DN$rasi2Du1VS+jI!Pb|HcrYT&8Q#t$+@K& z?1CK0kO-VdjcKHF?{&3Q0=KlN*&m_OKepTc1H=6U}Iui1ye>qs=?+cl3 zm)F{hw;xi+e!O~ZWJIY927~l`qHgwg4WYT|pMTx2_5Ug3ANX?}&sJ4gySlrVP6-TO z$8W5Ct@RT2w{usH^qsVJ&VZJ+@4aii+$;y_8v zioMYNM%#*I|Gd(+f=fD6{4e!zzZ;I(xaLkmwYsNhN*wg^NXB7ElBD*9FT5HvG!tr> z`m4uhv#Ya1x6`YenwmN*@2H{0(6TeZKX+{2+ib8y@aq4M->L$>(d%?NGqC8PU1C#L z-A9)iKnHV)y_o#pEz>rRWOWo+Yp#q|c0XU!@&4VL!4R~q>%kMm-do{6D))b!$XI)N z$lT>p8=`rX5pYtx|2 p|D4Kb?;L3MLUV?D$?BVZzpv}4*>J3NYqh?iBRki2ICJCn{{hghkO2Sy literal 6738 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

    wAXw0_-$AV8v!SY_ zg?%5D42lUsfh;B-WHLY`9*?s^66iP@(uzUABJoxPJOCQeGr1zc-$OoZ4(KhU$@s)tSYR<$ zmKK&oEXEp-or-!AaSg~9K(#1C#bQwSDN1A$LxR$QsHMp&6#|%qg|Zk%{o?J|D_~f!6(HK)H2xyTYb23f{YifuQObZVJ zq5b1a!G4vq|4lJ4=?pB5&a_5a5}-ODV62%?JuwMLBAtjMU@Zw&L~H9G(FI(VP(HG(;FZ1vp^Z-Hsa`IFB{-Wy_T|dRZPbvSZu3vQh6azn{ z{HwbD&*)P7`(X+UhfY8u=uydg-;f>jn59JbaHRkfvQKtp!G1`h%5(D<0D!u->?;Qx z$<%>_%0hRlv+{G5*_v90T*pmg03iR;o#NmFy%>L3$*IM_VsA4xRvVVS)oYhG_x2D- zJYFikAx?9Mox7}_(6WJ}pn7dY&nYp`K)4`y_KJ6Blr9*OUzayqORo+TN*B(z+u>I< zUT~ssWnqj9ymzcsbkK|bcm&luO8>xazQn<5#CyNqFAq?W6X%vFy?c5bs8Z6HIQUE? z4@?7KfW7Qh{=YP`gOm-kLF-=AJe>rfwdnQxkFDcmWeO1d$4PZts&~`in|S~nh=uY1 zRQ|ih#LfwYY5Q3(UG$MYX0|EY}Mg3;3F?p!tEK2 z7>}~kR~w=mNTX6EuPg2bQ07TXuj;4|^+d1h5tHgaRoqQhk&8`#cz9`gf%jip0BM`@ ZTRAsJ-=S^=*eh8K?#?SH#f}@d{};Q?{`vp_