diff --git a/src/main/java/appeng/api/config/LockCraftingMode.java b/src/main/java/appeng/api/config/LockCraftingMode.java new file mode 100644 index 00000000000..093bd0cf684 --- /dev/null +++ b/src/main/java/appeng/api/config/LockCraftingMode.java @@ -0,0 +1,29 @@ +package appeng.api.config; + +/** + * The circumstances under which a interface will lock further crafting. + */ +public enum LockCraftingMode { + /** + * Crafting is never locked. + */ + NONE, + /** + * After pushing a pattern to an adjacent machine, the interface will not accept further crafts until a redstone + * pulse is received. + */ + LOCK_UNTIL_PULSE, + /** + * Crafting is locked while the interface is receiving a redstone signal. + */ + LOCK_WHILE_HIGH, + /** + * Crafting is locked while the interface is not receiving a redstone signal. + */ + LOCK_WHILE_LOW, + /** + * After pushing a pattern to an adjacent machine, the interface will not accept further crafts until the primary + * pattern result is returned to the network through the interface. + */ + LOCK_UNTIL_RESULT +} diff --git a/src/main/java/appeng/api/config/Settings.java b/src/main/java/appeng/api/config/Settings.java index 57bef676ce2..f97dba94f92 100644 --- a/src/main/java/appeng/api/config/Settings.java +++ b/src/main/java/appeng/api/config/Settings.java @@ -72,7 +72,9 @@ public enum Settings { CELL_TYPE(EnumSet.allOf(CellType.class)), - STICKY_MODE(EnumSet.of(YesNo.YES, YesNo.NO)); + STICKY_MODE(EnumSet.of(YesNo.YES, YesNo.NO)), + + LOCK_CRAFTING_MODE(EnumSet.allOf(LockCraftingMode.class)); private final EnumSet> values; diff --git a/src/main/java/appeng/api/config/Upgrades.java b/src/main/java/appeng/api/config/Upgrades.java index 24b83109ae8..dd7827eb735 100644 --- a/src/main/java/appeng/api/config/Upgrades.java +++ b/src/main/java/appeng/api/config/Upgrades.java @@ -42,7 +42,8 @@ public enum Upgrades { INVERTER(1), PATTERN_CAPACITY(1), ORE_FILTER(1), - ADVANCED_BLOCKING(1); + ADVANCED_BLOCKING(1), + LOCK_CRAFTING(1); /** * @deprecated use {@link Upgrades#getTier()} diff --git a/src/main/java/appeng/api/definitions/IMaterials.java b/src/main/java/appeng/api/definitions/IMaterials.java index 128946240ee..f17fc6012dd 100644 --- a/src/main/java/appeng/api/definitions/IMaterials.java +++ b/src/main/java/appeng/api/definitions/IMaterials.java @@ -147,4 +147,6 @@ public interface IMaterials { IItemDefinition cardPatternRefiller(); IItemDefinition cardAdvancedBlocking(); + + IItemDefinition cardLockCrafting(); } diff --git a/src/main/java/appeng/block/misc/BlockInterface.java b/src/main/java/appeng/block/misc/BlockInterface.java index cb486f748a5..8faceaf0418 100644 --- a/src/main/java/appeng/block/misc/BlockInterface.java +++ b/src/main/java/appeng/block/misc/BlockInterface.java @@ -12,6 +12,7 @@ import java.util.EnumSet; +import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.world.World; @@ -59,6 +60,14 @@ public boolean onActivated(final World w, final int x, final int y, final int z, return false; } + @Override + public void onNeighborBlockChange(World worldIn, int x, int y, int z, Block neighbor) { + TileInterface tile = this.getTileEntity(worldIn, x, y, z); + if (tile != null) { + tile.getInterfaceDuality().updateRedstoneState(); + } + } + @Override protected boolean hasCustomRotation() { return true; diff --git a/src/main/java/appeng/client/gui/implementations/GuiInterface.java b/src/main/java/appeng/client/gui/implementations/GuiInterface.java index 51cb017689e..e51c4e469c0 100644 --- a/src/main/java/appeng/client/gui/implementations/GuiInterface.java +++ b/src/main/java/appeng/client/gui/implementations/GuiInterface.java @@ -17,6 +17,7 @@ import appeng.api.config.AdvancedBlockingMode; import appeng.api.config.InsertionMode; +import appeng.api.config.LockCraftingMode; import appeng.api.config.Settings; import appeng.api.config.Upgrades; import appeng.api.config.YesNo; @@ -40,6 +41,7 @@ public class GuiInterface extends GuiUpgradeable { private GuiImgButton insertionMode; private GuiImgButton advancedBlockingMode; + private GuiImgButton lockCraftingMode; public GuiInterface(final InventoryPlayer inventoryPlayer, final IInterfaceHost te) { super(new ContainerInterface(inventoryPlayer, te)); @@ -82,6 +84,14 @@ protected void addButtons() { AdvancedBlockingMode.DEFAULT); this.advancedBlockingMode.visible = this.bc.getInstalledUpgrades(Upgrades.ADVANCED_BLOCKING) > 0; this.buttonList.add(advancedBlockingMode); + + this.lockCraftingMode = new GuiImgButton( + this.guiLeft - 18, + this.guiTop + 80, + Settings.LOCK_CRAFTING_MODE, + LockCraftingMode.NONE); + this.lockCraftingMode.visible = this.bc.getInstalledUpgrades(Upgrades.LOCK_CRAFTING) > 0; + this.buttonList.add(lockCraftingMode); } @Override @@ -102,6 +112,10 @@ public void drawFG(final int offsetX, final int offsetY, final int mouseX, final this.advancedBlockingMode.set(((ContainerInterface) this.cvb).getAdvancedBlockingMode()); } + if (this.lockCraftingMode != null) { + this.lockCraftingMode.set(((ContainerInterface) this.cvb).getLockCraftingMode()); + } + this.fontRendererObj.drawString( this.getGuiDisplayName(GuiText.Interface.getLocal()), 8, @@ -146,6 +160,10 @@ protected void actionPerformed(final GuiButton btn) { NetworkHandler.instance .sendToServer(new PacketConfigButton(this.advancedBlockingMode.getSetting(), backwards)); } + + if (btn == this.lockCraftingMode) { + NetworkHandler.instance.sendToServer(new PacketConfigButton(this.lockCraftingMode.getSetting(), backwards)); + } } @Override @@ -154,5 +172,8 @@ protected void handleButtonVisibility() { if (this.advancedBlockingMode != null) { this.advancedBlockingMode.setVisibility(this.bc.getInstalledUpgrades(Upgrades.ADVANCED_BLOCKING) > 0); } + if (this.lockCraftingMode != null) { + this.lockCraftingMode.setVisibility(this.bc.getInstalledUpgrades(Upgrades.LOCK_CRAFTING) > 0); + } } } diff --git a/src/main/java/appeng/client/gui/widgets/GuiImgButton.java b/src/main/java/appeng/client/gui/widgets/GuiImgButton.java index 11e68b39163..3551f99710b 100644 --- a/src/main/java/appeng/client/gui/widgets/GuiImgButton.java +++ b/src/main/java/appeng/client/gui/widgets/GuiImgButton.java @@ -32,6 +32,7 @@ import appeng.api.config.InsertionMode; import appeng.api.config.ItemSubstitution; import appeng.api.config.LevelType; +import appeng.api.config.LockCraftingMode; import appeng.api.config.OperationMode; import appeng.api.config.PatternBeSubstitution; import appeng.api.config.PatternSlotConfig; @@ -624,6 +625,36 @@ public GuiImgButton(final int x, final int y, final Enum idx, final Enum val) { AdvancedBlockingMode.BLOCK_ON_ALL, ButtonToolTips.AdvancedBlockingModeAll, ButtonToolTips.AdvancedBlockingModeAllDesc); + this.registerApp( + 10, + Settings.LOCK_CRAFTING_MODE, + LockCraftingMode.NONE, + ButtonToolTips.LockCraftingMode, + ButtonToolTips.LockCraftingModeNone); + this.registerApp( + 2, + Settings.LOCK_CRAFTING_MODE, + LockCraftingMode.LOCK_UNTIL_PULSE, + ButtonToolTips.LockCraftingMode, + ButtonToolTips.LockCraftingUntilRedstonePulse); + this.registerApp( + 0, + Settings.LOCK_CRAFTING_MODE, + LockCraftingMode.LOCK_WHILE_HIGH, + ButtonToolTips.LockCraftingMode, + ButtonToolTips.LockCraftingWhileRedstoneHigh); + this.registerApp( + 1, + Settings.LOCK_CRAFTING_MODE, + LockCraftingMode.LOCK_WHILE_LOW, + ButtonToolTips.LockCraftingMode, + ButtonToolTips.LockCraftingWhileRedstoneLow); + this.registerApp( + 7, + Settings.LOCK_CRAFTING_MODE, + LockCraftingMode.LOCK_UNTIL_RESULT, + ButtonToolTips.LockCraftingMode, + ButtonToolTips.LockCraftingUntilResultReturned); this.registerApp( 16 + 2, Settings.CRAFTING_MODE, diff --git a/src/main/java/appeng/container/implementations/ContainerInterface.java b/src/main/java/appeng/container/implementations/ContainerInterface.java index ce1bb4339c3..362c9dcf4d9 100644 --- a/src/main/java/appeng/container/implementations/ContainerInterface.java +++ b/src/main/java/appeng/container/implementations/ContainerInterface.java @@ -18,6 +18,7 @@ import appeng.api.config.AdvancedBlockingMode; import appeng.api.config.InsertionMode; +import appeng.api.config.LockCraftingMode; import appeng.api.config.SecurityPermissions; import appeng.api.config.Settings; import appeng.api.config.Upgrades; @@ -46,6 +47,9 @@ public class ContainerInterface extends ContainerUpgradeable implements IOptiona @GuiSync(10) public AdvancedBlockingMode advancedBlockingMode = AdvancedBlockingMode.DEFAULT; + @GuiSync(12) + public LockCraftingMode lockCraftingMode = LockCraftingMode.NONE; + @GuiSync(8) public InsertionMode insertionMode = InsertionMode.DEFAULT; @@ -144,6 +148,7 @@ protected void loadSettingsFromHost(final IConfigManager cm) { this.setInterfaceTerminalMode((YesNo) cm.getSetting(Settings.INTERFACE_TERMINAL)); this.setInsertionMode((InsertionMode) cm.getSetting(Settings.INSERTION_MODE)); this.setAdvancedBlockingMode((AdvancedBlockingMode) cm.getSetting(Settings.ADVANCED_BLOCKING_MODE)); + this.setLockCraftingMode((LockCraftingMode) cm.getSetting(Settings.LOCK_CRAFTING_MODE)); } public YesNo getBlockingMode() { @@ -178,6 +183,14 @@ private void setAdvancedBlockingMode(final AdvancedBlockingMode mode) { this.advancedBlockingMode = mode; } + public LockCraftingMode getLockCraftingMode() { + return this.lockCraftingMode; + } + + private void setLockCraftingMode(LockCraftingMode mode) { + this.lockCraftingMode = mode; + } + public int getPatternCapacityCardsInstalled() { if (Platform.isClient() && isEmpty) return -1; if (myDuality == null) return 0; diff --git a/src/main/java/appeng/core/Registration.java b/src/main/java/appeng/core/Registration.java index 87df560d0c4..4fa826eac05 100644 --- a/src/main/java/appeng/core/Registration.java +++ b/src/main/java/appeng/core/Registration.java @@ -606,6 +606,9 @@ void postInit(final FMLPostInitializationEvent event) { Upgrades.PATTERN_CAPACITY.registerItem(parts.p2PTunnelMEInterface(), 3); Upgrades.ADVANCED_BLOCKING.registerItem(parts.iface(), 1); Upgrades.ADVANCED_BLOCKING.registerItem(blocks.iface(), 1); + Upgrades.LOCK_CRAFTING.registerItem(parts.iface(), 1); + Upgrades.LOCK_CRAFTING.registerItem(blocks.iface(), 1); + Upgrades.LOCK_CRAFTING.registerItem(parts.p2PTunnelMEInterface(), 1); // IO Port! Upgrades.SPEED.registerItem(blocks.iOPort(), 3); diff --git a/src/main/java/appeng/core/api/definitions/ApiMaterials.java b/src/main/java/appeng/core/api/definitions/ApiMaterials.java index a356aead0d2..f707621f96d 100644 --- a/src/main/java/appeng/core/api/definitions/ApiMaterials.java +++ b/src/main/java/appeng/core/api/definitions/ApiMaterials.java @@ -75,6 +75,7 @@ public final class ApiMaterials implements IMaterials { private final IItemDefinition cardOreFilter; private final IItemDefinition cardPatternRefiller; private final IItemDefinition cardAdvancedBlocking; + private final IItemDefinition cardLockCrafting; private final IItemDefinition enderDust; private final IItemDefinition flour; private final IItemDefinition goldDust; @@ -178,6 +179,8 @@ public ApiMaterials(final DefinitionConstructor constructor) { itemMultiMaterial.createMaterial(MaterialType.CardPatternRefiller)); this.cardAdvancedBlocking = new DamagedItemDefinition( itemMultiMaterial.createMaterial(MaterialType.CardAdvancedBlocking)); + this.cardLockCrafting = new DamagedItemDefinition( + itemMultiMaterial.createMaterial(MaterialType.CardLockCrafting)); this.enderDust = new DamagedItemDefinition(itemMultiMaterial.createMaterial(MaterialType.EnderDust)); this.flour = new DamagedItemDefinition(itemMultiMaterial.createMaterial(MaterialType.Flour)); @@ -538,4 +541,9 @@ public IItemDefinition cardPatternRefiller() { public IItemDefinition cardAdvancedBlocking() { return this.cardAdvancedBlocking; } + + @Override + public IItemDefinition cardLockCrafting() { + return this.cardLockCrafting; + } } diff --git a/src/main/java/appeng/core/localization/ButtonToolTips.java b/src/main/java/appeng/core/localization/ButtonToolTips.java index f0aedc0e26d..ebb159765b1 100644 --- a/src/main/java/appeng/core/localization/ButtonToolTips.java +++ b/src/main/java/appeng/core/localization/ButtonToolTips.java @@ -181,6 +181,12 @@ public enum ButtonToolTips { AdvancedBlockingModeDefaultDesc, AdvancedBlockingModeAll, AdvancedBlockingModeAllDesc, + LockCraftingMode, + LockCraftingModeNone, + LockCraftingUntilRedstonePulse, + LockCraftingWhileRedstoneHigh, + LockCraftingWhileRedstoneLow, + LockCraftingUntilResultReturned, CraftingModeStandard, CraftingModeStandardDesc, CraftingModeIgnoreMissing, diff --git a/src/main/java/appeng/core/localization/WailaText.java b/src/main/java/appeng/core/localization/WailaText.java index 797371cdcad..c6eba01d37d 100644 --- a/src/main/java/appeng/core/localization/WailaText.java +++ b/src/main/java/appeng/core/localization/WailaText.java @@ -29,6 +29,11 @@ public enum WailaText { Unlocked, Showing, + CraftingLockedByRedstoneSignal, + CraftingLockedByLackOfRedstoneSignal, + CraftingLockedUntilPulse, + CraftingLockedUntilResult, + Contains, Channels, Booting; diff --git a/src/main/java/appeng/helpers/DualityInterface.java b/src/main/java/appeng/helpers/DualityInterface.java index 68d4d6c5329..ad099b8f5a7 100644 --- a/src/main/java/appeng/helpers/DualityInterface.java +++ b/src/main/java/appeng/helpers/DualityInterface.java @@ -10,6 +10,7 @@ package appeng.helpers; +import java.util.ArrayList; import java.util.Collection; import java.util.EnumSet; import java.util.HashSet; @@ -37,6 +38,7 @@ import appeng.api.config.Actionable; import appeng.api.config.AdvancedBlockingMode; import appeng.api.config.InsertionMode; +import appeng.api.config.LockCraftingMode; import appeng.api.config.Settings; import appeng.api.config.Upgrades; import appeng.api.config.YesNo; @@ -130,6 +132,10 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn private boolean isWorking = false; protected static final boolean EIO = Loader.isModLoaded("EnderIO"); + private YesNo redstoneState = YesNo.UNDECIDED; + private UnlockCraftingEvent unlockEvent; + private List unlockStacks; + public DualityInterface(final AENetworkProxy networkProxy, final IInterfaceHost ih) { this.gridProxy = networkProxy; this.gridProxy.setFlags(GridFlags.REQUIRE_CHANNEL); @@ -139,6 +145,7 @@ public DualityInterface(final AENetworkProxy networkProxy, final IInterfaceHost this.cm.registerSetting(Settings.INTERFACE_TERMINAL, YesNo.YES); this.cm.registerSetting(Settings.INSERTION_MODE, InsertionMode.DEFAULT); this.cm.registerSetting(Settings.ADVANCED_BLOCKING_MODE, AdvancedBlockingMode.DEFAULT); + this.cm.registerSetting(Settings.LOCK_CRAFTING_MODE, LockCraftingMode.NONE); this.iHost = ih; this.craftingTracker = new MultiCraftingTracker(this.iHost, 9); @@ -190,6 +197,11 @@ public void onChangeInventory(final IInventory inv, final int slot, final InvOpe // :P } } + } else if (inv == this.upgrades) { + if (this.getInstalledUpgrades(Upgrades.LOCK_CRAFTING) == 0) { + cm.putSetting(Settings.LOCK_CRAFTING_MODE, LockCraftingMode.NONE); + resetCraftingLock(); + } } } @@ -202,6 +214,23 @@ public void writeToNBT(final NBTTagCompound data) { this.craftingTracker.writeToNBT(data); data.setInteger("priority", this.priority); + if (unlockEvent == UnlockCraftingEvent.PULSE) { + data.setByte("unlockEvent", (byte) 1); + } else if (unlockEvent == UnlockCraftingEvent.RESULT) { + if (unlockStacks != null && !unlockStacks.isEmpty()) { + data.setByte("unlockEvent", (byte) 2); + NBTTagList stackList = new NBTTagList(); + for (IAEItemStack stack : unlockStacks) { + NBTTagCompound stackTag = new NBTTagCompound(); + stack.writeToNBT(stackTag); + stackList.appendTag(stackTag); + } + data.setTag("unlockStacks", stackList); + } else { + AELog.error("Saving interface {}, locked waiting for stack, but stack is null!", iHost); + } + } + final NBTTagList waitingToSend = new NBTTagList(); if (this.waitingToSend != null) { for (final ItemStack is : this.waitingToSend) { @@ -226,6 +255,34 @@ public void readFromNBT(final NBTTagCompound data) { } } + var unlockEventType = data.getByte("unlockEvent"); + this.unlockEvent = switch (unlockEventType) { + case 0 -> null; + case 1 -> UnlockCraftingEvent.PULSE; + case 2 -> UnlockCraftingEvent.RESULT; + default -> { + AELog.error("Unknown unlock event type {} in NBT for interface: {}", unlockEventType, data); + yield null; + } + }; + if (this.unlockEvent == UnlockCraftingEvent.RESULT) { + NBTTagList stackList = data.getTagList("unlockStacks", 10); + for (int index = 0; index < stackList.tagCount(); index++) { + NBTTagCompound stackTag = stackList.getCompoundTagAt(index); + IAEItemStack unlockStack = AEItemStack.loadItemStackFromNBT(stackTag); + if (unlockStack == null) { + AELog.error("Could not load unlock stack for interface from NBT: {}", data); + continue; + } + if (this.unlockStacks == null) { + this.unlockStacks = new ArrayList<>(); + } + this.unlockStacks.add(unlockStack); + } + } else { + this.unlockStacks = null; + } + this.craftingTracker.readFromNBT(data); this.upgrades.readFromNBT(data, "upgrades"); this.config.readFromNBT(data, "config"); @@ -620,6 +677,7 @@ private boolean usePlan(final int x, final IAEItemStack itemStack) { } else if (removed.stackSize != diff) { throw new IllegalStateException("bad attempt at managing inventory. ( removeItems )"); } + onStackReturnedToNetwork(AEItemStack.create(removed)); } } else if (this.craftingTracker.isBusy(x)) { changed = this.handleCrafting(x, adaptor, itemStack); @@ -741,6 +799,12 @@ public void updateSetting(final IConfigManager manager, final Enum settingName, this.cancelCrafting(); } + if (settingName == Settings.LOCK_CRAFTING_MODE) { + if (unlockEvent != null && !unlockEvent.matches((LockCraftingMode) newValue)) { + resetCraftingLock(); + } + } + this.markDirty(); } @@ -809,6 +873,10 @@ public boolean pushPattern(final ICraftingPatternDetails patternDetails, final I return false; } + if (getCraftingLockedReason() != LockCraftingMode.NONE) { + return false; + } + final TileEntity tile = this.iHost.getTileEntity(); final World w = tile.getWorldObj(); @@ -829,6 +897,7 @@ public boolean pushPattern(final ICraftingPatternDetails patternDetails, final I if (te instanceof ICraftingMachine cm) { if (cm.acceptsPlans()) { if (cm.pushPattern(patternDetails, table, s.getOpposite())) { + onPushPatternSuccess(patternDetails); return true; } continue; @@ -849,6 +918,7 @@ public boolean pushPattern(final ICraftingPatternDetails patternDetails, final I } } this.pushItemsOut(possibleDirections); + onPushPatternSuccess(patternDetails); return true; } } else if (EIO && te instanceof IItemDuct) { @@ -858,15 +928,16 @@ public boolean pushPattern(final ICraftingPatternDetails patternDetails, final I if (is != null) { final ItemStack rest = ((IItemDuct) te).insertItem(s.getOpposite(), is); if (!hadAcceptedSome && rest != null && rest.stackSize == is.stackSize) break; // conduit should - // accept all the - // pattern or - // nothing. + // accept all the + // pattern or + // nothing. hadAcceptedSome = true; this.addToSendList(rest); } } if (hadAcceptedSome) { this.pushItemsOut(possibleDirections); + onPushPatternSuccess(patternDetails); return true; } } @@ -906,6 +977,10 @@ public boolean isBusy() { busy = allAreBusy; } + if (this.getCraftingLockedReason() != LockCraftingMode.NONE) { + busy = true; + } + return busy; } @@ -1180,6 +1255,126 @@ public void setPriority(final int newValue) { } } + public void resetCraftingLock() { + if (unlockEvent != null) { + unlockEvent = null; + unlockStacks = null; + saveChanges(); + } + } + + private void onPushPatternSuccess(ICraftingPatternDetails pattern) { + resetCraftingLock(); + + LockCraftingMode lockMode = (LockCraftingMode) cm.getSetting(Settings.LOCK_CRAFTING_MODE); + switch (lockMode) { + case LOCK_UNTIL_PULSE -> { + unlockEvent = UnlockCraftingEvent.PULSE; + saveChanges(); + } + case LOCK_UNTIL_RESULT -> { + unlockEvent = UnlockCraftingEvent.RESULT; + if (unlockStacks == null) { + unlockStacks = new ArrayList<>(); + } + for (IAEItemStack output : pattern.getCondensedOutputs()) { + unlockStacks.add(output.copy()); + } + saveChanges(); + } + } + } + + /** + * Gets if the crafting lock is in effect and why. + * + * @return LockCraftingMode.NONE if the lock isn't in effect + */ + public LockCraftingMode getCraftingLockedReason() { + var lockMode = cm.getSetting(Settings.LOCK_CRAFTING_MODE); + if (lockMode == LockCraftingMode.LOCK_WHILE_LOW && !getRedstoneState()) { + // Crafting locked by redstone signal + return LockCraftingMode.LOCK_WHILE_LOW; + } else if (lockMode == LockCraftingMode.LOCK_WHILE_HIGH && getRedstoneState()) { + return LockCraftingMode.LOCK_WHILE_HIGH; + } else if (unlockEvent != null) { + // Crafting locked by waiting for unlock event + switch (unlockEvent) { + case PULSE -> { + return LockCraftingMode.LOCK_UNTIL_PULSE; + } + case RESULT -> { + return LockCraftingMode.LOCK_UNTIL_RESULT; + } + } + } + return LockCraftingMode.NONE; + } + + /** + * @return Null if {@linkplain #getCraftingLockedReason()} is not {@link LockCraftingMode#LOCK_UNTIL_RESULT}. + */ + public List getUnlockStacks() { + return unlockStacks; + } + + /** + * Called when an ItemStack has been pushed into the network from the internal buffer. Public to enable other + * interface types (mainly AE2FC) to work with locking return mode. + */ + public void onStackReturnedToNetwork(IAEItemStack returnedStack) { + if (unlockEvent != UnlockCraftingEvent.RESULT) { + return; // If we're not waiting for the result, we don't care + } + + if (unlockStacks == null) { + // Actually an error state... + AELog.error("interface was waiting for RESULT, but no result was set"); + unlockEvent = null; + return; + } + boolean changed = false; + for (Iterator iterator = unlockStacks.iterator(); iterator.hasNext();) { + IAEItemStack unlockStack = iterator.next(); + if (unlockStack.equals(returnedStack)) { + changed = true; + unlockStack.decStackSize(returnedStack.getStackSize()); + if (unlockStack.getStackSize() <= 0) { + iterator.remove(); + } + break; + } + } + if (unlockStacks.isEmpty()) { + unlockEvent = null; + unlockStacks = null; + } + if (changed) { + saveChanges(); + } + } + + public void updateRedstoneState() { + // reset cache to undecided + redstoneState = YesNo.UNDECIDED; + + // If we're waiting for a pulse, update immediately + if (unlockEvent == UnlockCraftingEvent.PULSE && getRedstoneState()) { + unlockEvent = null; // Unlocked! + saveChanges(); + } + } + + private boolean getRedstoneState() { + if (redstoneState == YesNo.UNDECIDED) { + TileEntity tile = this.getHost().getTile(); + redstoneState = tile.getWorldObj().isBlockIndirectlyGettingPowered(tile.xCoord, tile.yCoord, tile.zCoord) + ? YesNo.YES + : YesNo.NO; + } + return redstoneState == YesNo.YES; + } + private static class InterfaceRequestSource extends MachineSource { public InterfaceRequestSource(final IActionHost v) { diff --git a/src/main/java/appeng/helpers/UnlockCraftingEvent.java b/src/main/java/appeng/helpers/UnlockCraftingEvent.java new file mode 100644 index 00000000000..37dbd684f9b --- /dev/null +++ b/src/main/java/appeng/helpers/UnlockCraftingEvent.java @@ -0,0 +1,22 @@ +package appeng.helpers; + +import appeng.api.config.LockCraftingMode; + +/** + * The types of event that the interface is waiting for to unlock crafting again. + */ +public enum UnlockCraftingEvent { + + PULSE(LockCraftingMode.LOCK_UNTIL_PULSE), + RESULT(LockCraftingMode.LOCK_UNTIL_RESULT); + + private final LockCraftingMode correspondingMode; + + UnlockCraftingEvent(LockCraftingMode mode) { + this.correspondingMode = mode; + } + + public boolean matches(LockCraftingMode mode) { + return this.correspondingMode == mode; + } +} diff --git a/src/main/java/appeng/integration/modules/waila/PartWailaDataProvider.java b/src/main/java/appeng/integration/modules/waila/PartWailaDataProvider.java index 8b4d23d3a13..8b2c782e52b 100644 --- a/src/main/java/appeng/integration/modules/waila/PartWailaDataProvider.java +++ b/src/main/java/appeng/integration/modules/waila/PartWailaDataProvider.java @@ -28,6 +28,7 @@ import appeng.integration.modules.waila.part.IPartWailaDataProvider; import appeng.integration.modules.waila.part.P2PStateWailaDataProvider; import appeng.integration.modules.waila.part.PartAccessor; +import appeng.integration.modules.waila.part.PartInterfaceDataProvider; import appeng.integration.modules.waila.part.PowerStateWailaDataProvider; import appeng.integration.modules.waila.part.StorageMonitorWailaDataProvider; import appeng.integration.modules.waila.part.Tracer; @@ -68,8 +69,9 @@ public PartWailaDataProvider() { final IPartWailaDataProvider powerState = new PowerStateWailaDataProvider(); final IPartWailaDataProvider p2pState = new P2PStateWailaDataProvider(); final IPartWailaDataProvider partStack = new BasePartWailaDataProvider(); + final IPartWailaDataProvider partInterface = new PartInterfaceDataProvider(); - this.providers = Lists.newArrayList(channel, storageMonitor, powerState, p2pState, partStack); + this.providers = Lists.newArrayList(channel, storageMonitor, powerState, p2pState, partStack, partInterface); } @Override diff --git a/src/main/java/appeng/integration/modules/waila/TileWailaDataProvider.java b/src/main/java/appeng/integration/modules/waila/TileWailaDataProvider.java index 51059f0a1ce..6b7cbabdc13 100644 --- a/src/main/java/appeng/integration/modules/waila/TileWailaDataProvider.java +++ b/src/main/java/appeng/integration/modules/waila/TileWailaDataProvider.java @@ -22,6 +22,7 @@ import appeng.integration.modules.waila.tile.ChargerWailaDataProvider; import appeng.integration.modules.waila.tile.CraftingMonitorWailaDataProvider; +import appeng.integration.modules.waila.tile.InterfaceDataProvider; import appeng.integration.modules.waila.tile.PowerStateWailaDataProvider; import appeng.integration.modules.waila.tile.PowerStorageWailaDataProvider; import mcp.mobius.waila.api.IWailaConfigHandler; @@ -50,8 +51,9 @@ public TileWailaDataProvider() { final IWailaDataProvider energyCell = new PowerStorageWailaDataProvider(); final IWailaDataProvider craftingBlock = new PowerStateWailaDataProvider(); final IWailaDataProvider craftingMonitor = new CraftingMonitorWailaDataProvider(); + final IWailaDataProvider interfaceBlock = new InterfaceDataProvider(); - this.providers = Lists.newArrayList(charger, energyCell, craftingBlock, craftingMonitor); + this.providers = Lists.newArrayList(charger, energyCell, craftingBlock, craftingMonitor, interfaceBlock); } @Override diff --git a/src/main/java/appeng/integration/modules/waila/part/PartInterfaceDataProvider.java b/src/main/java/appeng/integration/modules/waila/part/PartInterfaceDataProvider.java new file mode 100644 index 00000000000..3b9f78f460d --- /dev/null +++ b/src/main/java/appeng/integration/modules/waila/part/PartInterfaceDataProvider.java @@ -0,0 +1,87 @@ +package appeng.integration.modules.waila.part; + +import java.util.List; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; + +import appeng.api.config.LockCraftingMode; +import appeng.api.parts.IPart; +import appeng.api.storage.data.IAEItemStack; +import appeng.core.localization.WailaText; +import appeng.helpers.IInterfaceHost; +import appeng.util.item.AEItemStack; +import mcp.mobius.waila.api.IWailaConfigHandler; +import mcp.mobius.waila.api.IWailaDataAccessor; + +public final class PartInterfaceDataProvider extends BasePartWailaDataProvider { + + private static final String NBT_LOCK_REASON = "craftingLockReason"; + private static final String NBT_LOCK_STACKS = "craftingLockStacks"; + + @Override + public List getWailaBody(IPart part, List currentToolTip, IWailaDataAccessor accessor, + IWailaConfigHandler config) { + if (part instanceof IInterfaceHost) { + if (accessor.getNBTData().hasKey(NBT_LOCK_REASON)) { + String lockReasonText = accessor.getNBTData().getString(NBT_LOCK_REASON); + LockCraftingMode lockReason = LockCraftingMode.valueOf(lockReasonText); + + switch (lockReason) { + case LOCK_UNTIL_PULSE -> currentToolTip.add(WailaText.CraftingLockedUntilPulse.getLocal()); + case LOCK_WHILE_HIGH -> currentToolTip.add(WailaText.CraftingLockedByRedstoneSignal.getLocal()); + case LOCK_WHILE_LOW -> currentToolTip + .add(WailaText.CraftingLockedByLackOfRedstoneSignal.getLocal()); + case LOCK_UNTIL_RESULT -> { + currentToolTip.add(WailaText.CraftingLockedUntilResult.getLocal()); + + if (accessor.getNBTData().hasKey(NBT_LOCK_STACKS)) { + NBTTagList stackList = accessor.getNBTData().getTagList(NBT_LOCK_STACKS, 10); + for (int index = 0; index < stackList.tagCount(); index++) { + NBTTagCompound stackTag = stackList.getCompoundTagAt(index); + AEItemStack stack = (AEItemStack) AEItemStack.loadItemStackFromNBT(stackTag); + + if (stack != null) { + currentToolTip.add("> " + stack.getStackSize() + " " + stack.getDisplayName()); + } else { + currentToolTip.add("ERROR"); + } + } + } else { + currentToolTip.add("ERROR"); + } + } + } + } + } + + return currentToolTip; + } + + @Override + public NBTTagCompound getNBTData(EntityPlayerMP player, IPart part, TileEntity te, NBTTagCompound tag, World world, + int x, int y, int z) { + if (part instanceof IInterfaceHost) { + var interfaceDuality = ((IInterfaceHost) part).getInterfaceDuality(); + + tag.setString(NBT_LOCK_REASON, interfaceDuality.getCraftingLockedReason().name()); + LockCraftingMode lock = interfaceDuality.getCraftingLockedReason(); + if (lock == LockCraftingMode.LOCK_UNTIL_RESULT) { + List unlockStacks = interfaceDuality.getUnlockStacks(); + if (unlockStacks != null && !unlockStacks.isEmpty()) { + NBTTagList stackList = new NBTTagList(); + for (IAEItemStack stack : unlockStacks) { + NBTTagCompound stackTag = new NBTTagCompound(); + stack.writeToNBT(stackTag); + stackList.appendTag(stackTag); + } + tag.setTag(NBT_LOCK_STACKS, stackList); + } + } + } + return tag; + } +} diff --git a/src/main/java/appeng/integration/modules/waila/tile/InterfaceDataProvider.java b/src/main/java/appeng/integration/modules/waila/tile/InterfaceDataProvider.java new file mode 100644 index 00000000000..e71c0b42caa --- /dev/null +++ b/src/main/java/appeng/integration/modules/waila/tile/InterfaceDataProvider.java @@ -0,0 +1,88 @@ +package appeng.integration.modules.waila.tile; + +import java.util.List; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; + +import appeng.api.config.LockCraftingMode; +import appeng.api.storage.data.IAEItemStack; +import appeng.core.localization.WailaText; +import appeng.helpers.IInterfaceHost; +import appeng.integration.modules.waila.BaseWailaDataProvider; +import appeng.util.item.AEItemStack; +import mcp.mobius.waila.api.IWailaConfigHandler; +import mcp.mobius.waila.api.IWailaDataAccessor; + +public final class InterfaceDataProvider extends BaseWailaDataProvider { + + private static final String NBT_LOCK_REASON = "craftingLockReason"; + private static final String NBT_LOCK_STACKS = "craftingLockStacks"; + + @Override + public List getWailaBody(ItemStack itemStack, List currentToolTip, IWailaDataAccessor accessor, + IWailaConfigHandler config) { + if (accessor.getTileEntity() instanceof IInterfaceHost) { + if (accessor.getNBTData().hasKey(NBT_LOCK_REASON)) { + String lockReasonText = accessor.getNBTData().getString(NBT_LOCK_REASON); + LockCraftingMode lockReason = LockCraftingMode.valueOf(lockReasonText); + + switch (lockReason) { + case LOCK_UNTIL_PULSE -> currentToolTip.add(WailaText.CraftingLockedUntilPulse.getLocal()); + case LOCK_WHILE_HIGH -> currentToolTip.add(WailaText.CraftingLockedByRedstoneSignal.getLocal()); + case LOCK_WHILE_LOW -> currentToolTip + .add(WailaText.CraftingLockedByLackOfRedstoneSignal.getLocal()); + case LOCK_UNTIL_RESULT -> { + currentToolTip.add(WailaText.CraftingLockedUntilResult.getLocal()); + + if (accessor.getNBTData().hasKey(NBT_LOCK_STACKS)) { + NBTTagList stackList = accessor.getNBTData().getTagList(NBT_LOCK_STACKS, 10); + for (int index = 0; index < stackList.tagCount(); index++) { + NBTTagCompound stackTag = stackList.getCompoundTagAt(index); + AEItemStack stack = (AEItemStack) AEItemStack.loadItemStackFromNBT(stackTag); + + if (stack != null) { + currentToolTip.add("> " + stack.getStackSize() + " " + stack.getDisplayName()); + } else { + currentToolTip.add("ERROR"); + } + } + } else { + currentToolTip.add("ERROR"); + } + } + } + } + } + + return currentToolTip; + } + + @Override + public NBTTagCompound getNBTData(EntityPlayerMP player, TileEntity te, NBTTagCompound tag, World world, int x, + int y, int z) { + if (te instanceof IInterfaceHost) { + var interfaceDuality = ((IInterfaceHost) te).getInterfaceDuality(); + + tag.setString(NBT_LOCK_REASON, interfaceDuality.getCraftingLockedReason().name()); + LockCraftingMode lock = interfaceDuality.getCraftingLockedReason(); + if (lock == LockCraftingMode.LOCK_UNTIL_RESULT) { + List unlockStacks = interfaceDuality.getUnlockStacks(); + if (unlockStacks != null && !unlockStacks.isEmpty()) { + NBTTagList stackList = new NBTTagList(); + for (IAEItemStack stack : unlockStacks) { + NBTTagCompound stackTag = new NBTTagCompound(); + stack.writeToNBT(stackTag); + stackList.appendTag(stackTag); + } + tag.setTag(NBT_LOCK_STACKS, stackList); + } + } + } + return tag; + } +} diff --git a/src/main/java/appeng/items/materials/ItemMultiMaterial.java b/src/main/java/appeng/items/materials/ItemMultiMaterial.java index 6ec57dc468f..45924bd4a62 100644 --- a/src/main/java/appeng/items/materials/ItemMultiMaterial.java +++ b/src/main/java/appeng/items/materials/ItemMultiMaterial.java @@ -142,6 +142,7 @@ public Upgrades getType(final ItemStack itemstack) { case CardCrafting -> Upgrades.CRAFTING; case CardPatternRefiller -> Upgrades.PATTERN_REFILLER; case CardAdvancedBlocking -> Upgrades.ADVANCED_BLOCKING; + case CardLockCrafting -> Upgrades.LOCK_CRAFTING; case CardSticky -> Upgrades.STICKY; default -> null; }; diff --git a/src/main/java/appeng/items/materials/MaterialType.java b/src/main/java/appeng/items/materials/MaterialType.java index afa1fd7d3bf..7bfb1cb0c66 100644 --- a/src/main/java/appeng/items/materials/MaterialType.java +++ b/src/main/java/appeng/items/materials/MaterialType.java @@ -116,7 +116,8 @@ public enum MaterialType { EmptyAdvancedStorageCell(61, AEFeature.XtremeStorageCells), CardPatternRefiller(62), CardAdvancedBlocking(63), - CardSticky(64); + CardSticky(64), + CardLockCrafting(65); private final EnumSet features; // IIcon for the material. diff --git a/src/main/java/appeng/parts/automation/UpgradeInventory.java b/src/main/java/appeng/parts/automation/UpgradeInventory.java index 213283b67aa..65fef7aee16 100644 --- a/src/main/java/appeng/parts/automation/UpgradeInventory.java +++ b/src/main/java/appeng/parts/automation/UpgradeInventory.java @@ -37,6 +37,7 @@ public abstract class UpgradeInventory extends AppEngInternalInventory implement private int oreFilterUpgrades = 0; private int patternCapacityUpgrades = 0; private int advancedBlockingUpgrades = 0; + private int lockCraftingUpgrades = 0; private int stickyUpgrades = 0; public UpgradeInventory(final IAEAppEngInventory parent, final int s) { @@ -86,6 +87,7 @@ public int getInstalledUpgrades(final Upgrades u) { case CRAFTING -> this.craftingUpgrades; case ORE_FILTER -> this.oreFilterUpgrades; case ADVANCED_BLOCKING -> this.advancedBlockingUpgrades; + case LOCK_CRAFTING -> this.lockCraftingUpgrades; case STICKY -> this.stickyUpgrades; default -> 0; }; @@ -95,7 +97,7 @@ public int getInstalledUpgrades(final Upgrades u) { private void updateUpgradeInfo() { this.cached = true; - this.patternCapacityUpgrades = this.stickyUpgrades = this.inverterUpgrades = this.capacityUpgrades = this.redstoneUpgrades = this.speedUpgrades = this.superSpeedUpgrades = this.fuzzyUpgrades = this.craftingUpgrades = this.oreFilterUpgrades = this.advancedBlockingUpgrades = 0; + this.patternCapacityUpgrades = this.stickyUpgrades = this.inverterUpgrades = this.capacityUpgrades = this.redstoneUpgrades = this.speedUpgrades = this.superSpeedUpgrades = this.fuzzyUpgrades = this.craftingUpgrades = this.oreFilterUpgrades = this.advancedBlockingUpgrades = this.lockCraftingUpgrades = 0; for (final ItemStack is : this) { if (is == null || is.getItem() == null || !(is.getItem() instanceof IUpgradeModule)) { @@ -114,6 +116,7 @@ private void updateUpgradeInfo() { case CRAFTING -> this.craftingUpgrades++; case ORE_FILTER -> this.oreFilterUpgrades++; case ADVANCED_BLOCKING -> this.advancedBlockingUpgrades++; + case LOCK_CRAFTING -> this.lockCraftingUpgrades++; case STICKY -> this.stickyUpgrades++; default -> {} } @@ -131,6 +134,7 @@ private void updateUpgradeInfo() { this.oreFilterUpgrades = Math.min(this.oreFilterUpgrades, this.getMaxInstalled(Upgrades.ORE_FILTER)); this.advancedBlockingUpgrades = Math .min(this.advancedBlockingUpgrades, this.getMaxInstalled(Upgrades.ADVANCED_BLOCKING)); + this.lockCraftingUpgrades = Math.min(this.lockCraftingUpgrades, this.getMaxInstalled(Upgrades.LOCK_CRAFTING)); this.stickyUpgrades = Math.min(this.stickyUpgrades, this.getMaxInstalled(Upgrades.STICKY)); } diff --git a/src/main/java/appeng/parts/misc/PartInterface.java b/src/main/java/appeng/parts/misc/PartInterface.java index d8a7f1064f8..c62ba2dc422 100644 --- a/src/main/java/appeng/parts/misc/PartInterface.java +++ b/src/main/java/appeng/parts/misc/PartInterface.java @@ -198,6 +198,11 @@ public IInventory getInventoryByName(final String name) { return this.duality.getInventoryByName(name); } + @Override + public void onNeighborChanged() { + this.duality.updateRedstoneState(); + } + @Override public boolean onPartActivate(final EntityPlayer p, final Vec3 pos) { if (p.isSneaking()) { diff --git a/src/main/java/appeng/parts/p2p/PartP2PInterface.java b/src/main/java/appeng/parts/p2p/PartP2PInterface.java index 7af0a0f30e5..22a3d0f7a94 100644 --- a/src/main/java/appeng/parts/p2p/PartP2PInterface.java +++ b/src/main/java/appeng/parts/p2p/PartP2PInterface.java @@ -173,6 +173,11 @@ public IInventory getInventoryByName(final String name) { return this.duality.getInventoryByName(name); } + @Override + public void onNeighborChanged() { + this.duality.updateRedstoneState(); + } + @Override public boolean onPartActivate(final EntityPlayer p, final Vec3 pos) { AppEngInternalInventory patterns = (AppEngInternalInventory) this.duality.getPatterns(); diff --git a/src/main/resources/assets/appliedenergistics2/lang/en_US.lang b/src/main/resources/assets/appliedenergistics2/lang/en_US.lang index 4c9177862de..ce2824f29a2 100644 --- a/src/main/resources/assets/appliedenergistics2/lang/en_US.lang +++ b/src/main/resources/assets/appliedenergistics2/lang/en_US.lang @@ -458,6 +458,13 @@ gui.tooltips.appliedenergistics2.AdvancedBlockingModeDefaultDesc=Blocking interf gui.tooltips.appliedenergistics2.AdvancedBlockingModeAll=Loose Mode gui.tooltips.appliedenergistics2.AdvancedBlockingModeAllDesc=Blocking interfaces will block on any item in this ME system. +gui.tooltips.appliedenergistics2.LockCraftingMode=Lock Crafting +gui.tooltips.appliedenergistics2.LockCraftingModeNone=Never +gui.tooltips.appliedenergistics2.LockCraftingUntilRedstonePulse=Until redstone pulse is received +gui.tooltips.appliedenergistics2.LockCraftingUntilResultReturned=Until crafting result is returned +gui.tooltips.appliedenergistics2.LockCraftingWhileRedstoneHigh=With redstone signal +gui.tooltips.appliedenergistics2.LockCraftingWhileRedstoneLow=Without redstone signal + gui.tooltips.appliedenergistics2.CraftingModeStandard=Standard Crafting Mode gui.tooltips.appliedenergistics2.CraftingModeStandardDesc=Only starts craft if all ingredients are available in the ME System. gui.tooltips.appliedenergistics2.CraftingModeIgnoreMissing=Ignore Missing Crafting Mode @@ -511,6 +518,10 @@ waila.appliedenergistics2.P2PInputOneOutput=Linked (Input Side) waila.appliedenergistics2.P2PInputManyOutputs=Linked (Input Side) - %d Outputs waila.appliedenergistics2.P2POutput=Linked (Output Side) waila.appliedenergistics2.Booting=Network is booting +waila.appliedenergistics2.CraftingLockedByLackOfRedstoneSignal=Locked by lack of redstone signal +waila.appliedenergistics2.CraftingLockedByRedstoneSignal=Locked by redstone signal +waila.appliedenergistics2.CraftingLockedUntilPulse=Waiting for redstone pulse to unlock +waila.appliedenergistics2.CraftingLockedUntilResult=Waiting for results to unlock # Items item.appliedenergistics2.ItemBasicStorageCell.1k.name=1k ME Storage Cell @@ -554,6 +565,7 @@ item.appliedenergistics2.ItemMaterial.CardSuperSpeed.name=Hyper-Acceleration Car item.appliedenergistics2.ItemMaterial.CardOreFilter.name=Oredictionary Filter Card item.appliedenergistics2.ItemMaterial.CardPatternRefiller.name=Pattern Refiller Card item.appliedenergistics2.ItemMaterial.CardAdvancedBlocking.name=Advanced Blocking Card +item.appliedenergistics2.ItemMaterial.CardLockCrafting.name=Locking Card item.appliedenergistics2.ItemMaterial.CardSticky.name=Sticky Card item.appliedenergistics2.ItemMaterial.Cell2SpatialPart.name=2³ Spatial Component diff --git a/src/main/resources/assets/appliedenergistics2/recipes/materials/cards.recipe b/src/main/resources/assets/appliedenergistics2/recipes/materials/cards.recipe index 682fff4a2f3..f4d952891f9 100644 --- a/src/main/resources/assets/appliedenergistics2/recipes/materials/cards.recipe +++ b/src/main/resources/assets/appliedenergistics2/recipes/materials/cards.recipe @@ -57,3 +57,7 @@ shapeless= shapeless= ae2:ItemMaterial.AdvCard ae2:ItemPart.StorageBus ae2:ItemPart.LevelEmitter -> ae2:ItemMaterial.CardAdvancedBlocking + +shapeless= + ae2:ItemMaterial.AdvCard ae2:ItemMaterial.BlankPattern ae2:ItemMaterial.EngProcessor + -> ae2:ItemMaterial.CardLockCrafting diff --git a/src/main/resources/assets/appliedenergistics2/textures/guis/states.png b/src/main/resources/assets/appliedenergistics2/textures/guis/states.png index 7b477a79418..be9428e893d 100644 Binary files a/src/main/resources/assets/appliedenergistics2/textures/guis/states.png and b/src/main/resources/assets/appliedenergistics2/textures/guis/states.png differ diff --git a/src/main/resources/assets/appliedenergistics2/textures/items/ItemMaterial.CardLockCrafting.png b/src/main/resources/assets/appliedenergistics2/textures/items/ItemMaterial.CardLockCrafting.png new file mode 100644 index 00000000000..ea45e154cf0 Binary files /dev/null and b/src/main/resources/assets/appliedenergistics2/textures/items/ItemMaterial.CardLockCrafting.png differ