diff --git a/dependencies.gradle b/dependencies.gradle index 1a44f0d..d4a2fbb 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -34,10 +34,13 @@ * For more details, see https://docs.gradle.org/8.0.1/userguide/java_library_plugin.html#sec:java_library_configurations_graph */ dependencies { - compileOnly("com.github.GTNewHorizons:NotEnoughItems:2.7.77-GTNH:dev") + implementation("com.github.GTNewHorizons:NotEnoughItems:2.7.77-GTNH:dev") implementation("com.github.GTNewHorizons:GTNHLib:0.6.39:dev") compileOnly("com.github.GTNewHorizons:BetterQuesting:3.7.11-GTNH:dev") - implementation("com.github.GTNewHorizons:ModularUI2:2.2.16-1.7.10:dev") + implementation("com.github.GTNewHorizons:ModularUI2:2.2.18-1.7.10:dev") + // implementation("com.github.GTNewHorizons:ModularUI2:99.99:dev") + implementation("com.github.GTNewHorizons:StructureLib:1.4.18:dev") + implementation("com.github.GTNewHorizons:GT5-Unofficial:5.09.51.440:dev") } // deps may transitively add Baubles, so we replace it diff --git a/src/main/java/com/cubefury/vendingmachine/ClientProxy.java b/src/main/java/com/cubefury/vendingmachine/ClientProxy.java index d92aff8..2e6bd39 100644 --- a/src/main/java/com/cubefury/vendingmachine/ClientProxy.java +++ b/src/main/java/com/cubefury/vendingmachine/ClientProxy.java @@ -1,8 +1,9 @@ package com.cubefury.vendingmachine; import net.minecraft.entity.player.EntityPlayer; +import net.minecraftforge.common.MinecraftForge; -import com.cubefury.vendingmachine.integration.nei.IMCforNEI; +import com.cubefury.vendingmachine.integration.nei.NEIConfig; import cpw.mods.fml.client.FMLClientHandler; import cpw.mods.fml.common.event.FMLInitializationEvent; @@ -13,14 +14,13 @@ public class ClientProxy extends CommonProxy { @Override public void preInit(FMLPreInitializationEvent event) { super.preInit(event); - - IMCforNEI.IMCSender(); } // Override CommonProxy methods here, if you want a different behaviour on the client (e.g. registering renders). // Don't forget to call the super methods as well. @Override public void init(FMLInitializationEvent event) { + MinecraftForge.EVENT_BUS.register(new NEIConfig()); super.init(event); } diff --git a/src/main/java/com/cubefury/vendingmachine/Config.java b/src/main/java/com/cubefury/vendingmachine/Config.java index cf22851..8972650 100644 --- a/src/main/java/com/cubefury/vendingmachine/Config.java +++ b/src/main/java/com/cubefury/vendingmachine/Config.java @@ -6,11 +6,13 @@ public class Config { - private static final String CONFIG_CATEGORY_GUI = "GUI"; + private static final String CONFIG_CATEGORY_VM = "Vending Machine Settings"; public static String data_dir = "vendingmachine"; public static String config_dir = "config/vendingmachine"; - public static int gui_refresh_interval = 1; + public static int gui_refresh_interval = 20; + public static int dispense_frequency = 10; + public static int dispense_amount = 16; public static File worldDir = null; @@ -22,9 +24,18 @@ public static void init(File configFile) { config_dir = configuration .getString("config_dir", Configuration.CATEGORY_GENERAL, config_dir, "Configuration directory"); - configuration.addCustomCategoryComment(CONFIG_CATEGORY_GUI, "GUI Settings"); + configuration.addCustomCategoryComment(CONFIG_CATEGORY_VM, "Vending Machine Settings"); gui_refresh_interval = configuration - .getInt("gui_refresh_interval", CONFIG_CATEGORY_GUI, gui_refresh_interval, 1, 3600, ""); + .getInt("gui_refresh_interval", CONFIG_CATEGORY_VM, gui_refresh_interval, 20, 3600, "In number of ticks"); + dispense_frequency = configuration + .getInt("dispense_frequency", CONFIG_CATEGORY_VM, dispense_frequency, 1, 9000, "In number of ticks"); + dispense_amount = configuration.getInt( + "dispense_amount", + CONFIG_CATEGORY_VM, + dispense_amount, + 1, + Integer.MAX_VALUE, + "Number of items per dispense cycle"); if (configuration.hasChanged()) { configuration.save(); diff --git a/src/main/java/com/cubefury/vendingmachine/VendingMachine.java b/src/main/java/com/cubefury/vendingmachine/VendingMachine.java index 9ee141e..9041006 100644 --- a/src/main/java/com/cubefury/vendingmachine/VendingMachine.java +++ b/src/main/java/com/cubefury/vendingmachine/VendingMachine.java @@ -1,13 +1,10 @@ package com.cubefury.vendingmachine; -import net.minecraft.block.Block; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import com.cubefury.vendingmachine.blocks.BlockVendingMachine; -import com.cubefury.vendingmachine.blocks.ItemBlockVendingMachine; -import com.cubefury.vendingmachine.blocks.TileVendingMachine; +import com.cubefury.vendingmachine.gui.WidgetThemes; +import com.cubefury.vendingmachine.items.VMItems; import com.cubefury.vendingmachine.network.PacketTypeRegistry; import com.cubefury.vendingmachine.network.SerializedPacket; import com.cubefury.vendingmachine.util.ItemPlaceholder; @@ -43,10 +40,12 @@ public class VendingMachine { @Mod.Instance(MODID) public static VendingMachine instance; - public static Block vendingMachine = new BlockVendingMachine(); - - public static boolean isNeiLoaded = false; public static boolean isBqLoaded = false; + public static boolean isGtLoaded = false; + public static boolean isAeLoaded = false; + + public static int CONTROLLER_MTE_ID = 2741; + // public static int ME_UPLINK_MTE_ID = 2742; @SidedProxy( clientSide = "com.cubefury.vendingmachine.ClientProxy", @@ -67,28 +66,35 @@ public void preInit(FMLPreInitializationEvent event) { network.registerMessage(SerializedPacket.HandleClient.class, SerializedPacket.class, 0, Side.CLIENT); network.registerMessage(SerializedPacket.HandleServer.class, SerializedPacket.class, 0, Side.SERVER); + // ModularUI + WidgetThemes.register(); + } @Mod.EventHandler // load "Do your mod setup. Build whatever data structures you care about. Register recipes." (Remove if not needed) public void init(FMLInitializationEvent event) { - proxy.init(event); - GameRegistry.registerBlock(vendingMachine, ItemBlockVendingMachine.class, "vending_machine"); - GameRegistry.registerTileEntity(TileVendingMachine.class, "vending_machine"); + isBqLoaded = Loader.isModLoaded("betterquesting"); + isGtLoaded = Loader.isModLoaded("gregtech"); + isAeLoaded = Loader.isModLoaded("appliedenergistics2"); + + LOG.info("Better Questing Integration enabled: {}", isBqLoaded); + LOG.info("Gregtech Integration enabled: {}", isGtLoaded); + LOG.info("AE2 Integration enabled {}", isAeLoaded); GameRegistry.registerItem(ItemPlaceholder.placeholder, "placeholder"); + if (isGtLoaded) { + VMItems.registerMultis(); + } + + proxy.init(event); } @Mod.EventHandler // postInit "Handle interaction with other mods, complete your setup based on this." (Remove if not needed) public void postInit(FMLPostInitializationEvent event) { - isNeiLoaded = Loader.isModLoaded("NotEnoughItems"); - isBqLoaded = Loader.isModLoaded("betterquesting"); - - LOG.info("NEI Integration enabled: {}", isNeiLoaded); - LOG.info("Better Questing Integration enabled: {}", isBqLoaded); proxy.postInit(event); } diff --git a/src/main/java/com/cubefury/vendingmachine/blocks/BlockVendingMachine.java b/src/main/java/com/cubefury/vendingmachine/blocks/BlockVendingMachine.java deleted file mode 100644 index 8a287b2..0000000 --- a/src/main/java/com/cubefury/vendingmachine/blocks/BlockVendingMachine.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.cubefury.vendingmachine.blocks; - -import net.minecraft.block.Block; -import net.minecraft.block.BlockContainer; -import net.minecraft.block.material.Material; -import net.minecraft.client.renderer.texture.IIconRegister; -import net.minecraft.creativetab.CreativeTabs; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.IIcon; -import net.minecraft.world.World; - -import com.cubefury.vendingmachine.VendingMachine; - -import cpw.mods.fml.relauncher.Side; -import cpw.mods.fml.relauncher.SideOnly; - -public class BlockVendingMachine extends BlockContainer { - - private IIcon topIcon; - - public BlockVendingMachine() { - super(Material.iron); - this.setHardness(1); - this.setBlockName("vendingmachine.vending_machine"); - this.setBlockTextureName("vendingmachine:vending_machine"); - this.setCreativeTab(CreativeTabs.tabDecorations); - } - - @Override - public TileEntity createNewTileEntity(World worldIn, int meta) { - return new TileVendingMachine(); - } - - /** - * Called upon block activation (right click on the block.) - */ - @Override - public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int side, float hitX, - float hitY, float hitZ) { - if (!world.isRemote) { - player.openGui(VendingMachine.instance, 0, world, x, y, z); - } - return true; - } - - @Override - public void breakBlock(World world, int x, int y, int z, Block block, int meta) { - TileVendingMachine vendingMachine = (TileVendingMachine) world.getTileEntity(x, y, z); - // TODO: Drop items in input slots. - } - - /** - * Gets the block's texture. Args: side, meta - */ - @SideOnly(Side.CLIENT) - public IIcon getIcon(int side, int meta) { - return (side == 0 || side == 1) ? topIcon : blockIcon; - } - - @SideOnly(Side.CLIENT) - public void registerBlockIcons(IIconRegister p_149651_1_) { - this.blockIcon = p_149651_1_.registerIcon(this.getTextureName() + "_side"); - this.topIcon = p_149651_1_.registerIcon(this.getTextureName() + "_top"); - } -} diff --git a/src/main/java/com/cubefury/vendingmachine/blocks/ItemBlockVendingMachine.java b/src/main/java/com/cubefury/vendingmachine/blocks/ItemBlockVendingMachine.java deleted file mode 100644 index 2a35cd0..0000000 --- a/src/main/java/com/cubefury/vendingmachine/blocks/ItemBlockVendingMachine.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.cubefury.vendingmachine.blocks; - -import java.util.List; - -import net.minecraft.block.Block; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.ItemBlock; -import net.minecraft.item.ItemStack; - -import com.cubefury.vendingmachine.util.Translator; - -import cpw.mods.fml.relauncher.Side; -import cpw.mods.fml.relauncher.SideOnly; - -public class ItemBlockVendingMachine extends ItemBlock { - - public ItemBlockVendingMachine(Block block) { - super(block); - } - - @Override - @SideOnly(Side.CLIENT) - public void addInformation(ItemStack stack, EntityPlayer player, List tooltip, boolean f3_h) { - tooltip.add(""); - tooltip.add("§o" + Translator.translate("tooltip.vendingmachine")); - } - -} diff --git a/src/main/java/com/cubefury/vendingmachine/blocks/MTEVendingMachine.java b/src/main/java/com/cubefury/vendingmachine/blocks/MTEVendingMachine.java new file mode 100644 index 0000000..f329527 --- /dev/null +++ b/src/main/java/com/cubefury/vendingmachine/blocks/MTEVendingMachine.java @@ -0,0 +1,479 @@ +package com.cubefury.vendingmachine.blocks; + +import static com.gtnewhorizon.structurelib.structure.StructureUtility.lazy; +import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock; + +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraftforge.common.util.Constants; +import net.minecraftforge.common.util.ForgeDirection; + +import org.jetbrains.annotations.NotNull; + +import com.cleanroommc.modularui.utils.item.ItemStackHandler; +import com.cubefury.vendingmachine.Config; +import com.cubefury.vendingmachine.VendingMachine; +import com.cubefury.vendingmachine.blocks.gui.MTEVendingMachineGui; +import com.cubefury.vendingmachine.blocks.gui.TradeItemDisplay; +import com.cubefury.vendingmachine.network.handlers.NetAvailableTradeSync; +import com.cubefury.vendingmachine.network.handlers.NetTradeRequestSync; +import com.cubefury.vendingmachine.network.handlers.NetTradeStateSync; +import com.cubefury.vendingmachine.trade.Trade; +import com.cubefury.vendingmachine.trade.TradeDatabase; +import com.cubefury.vendingmachine.trade.TradeRequest; +import com.cubefury.vendingmachine.util.BigItemStack; +import com.gtnewhorizon.structurelib.StructureLibAPI; +import com.gtnewhorizon.structurelib.alignment.IAlignment; +import com.gtnewhorizon.structurelib.alignment.IAlignmentLimits; +import com.gtnewhorizon.structurelib.alignment.IAlignmentProvider; +import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable; +import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing; +import com.gtnewhorizon.structurelib.structure.IStructureDefinition; +import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment; + +import gregtech.api.GregTechAPI; +import gregtech.api.covers.CoverRegistry; +import gregtech.api.enums.Textures; +import gregtech.api.interfaces.ISecondaryDescribable; +import gregtech.api.interfaces.ITexture; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.implementations.MTEMultiBlockBase; +import gregtech.api.render.TextureFactory; +import gregtech.api.util.GTUtility; +import gregtech.api.util.MultiblockTooltipBuilder; + +public class MTEVendingMachine extends MTEMultiBlockBase + implements ISurvivalConstructable, ISecondaryDescribable, IAlignment { + + public static final int CUSTOM_UI_HEIGHT = 300; + + public static final int INPUT_SLOTS = 6; + public static final int OUTPUT_SLOTS = 4; + + public static final int MAX_TRADES = 300; + + public static final int STRUCTURE_CHECK_TICKS = 20; + + private static final IStructureDefinition STRUCTURE_DEFINITION = IStructureDefinition + .builder() + .addShape("main", new String[][] { { "cc", "c~", "cc" } }) + .addElement('c', lazy(t -> ofBlock(GregTechAPI.sBlockCasings11, 0))) + .build(); + private static final ITexture[] FACING_SIDE = { + TextureFactory.of(Textures.BlockIcons.MACHINE_CASING_ITEM_PIPE_TIN) }; + private static final ITexture[] FACING_FRONT = { + TextureFactory.of(Textures.BlockIcons.MACHINE_CASING_BRICKEDBLASTFURNACE_INACTIVE) }; + private static final ITexture[] FACING_ACTIVE = { + TextureFactory.of(Textures.BlockIcons.MACHINE_CASING_BRICKEDBLASTFURNACE_ACTIVE), TextureFactory.builder() + .addIcon(Textures.BlockIcons.MACHINE_CASING_BRICKEDBLASTFURNACE_ACTIVE_GLOW) + .glow() + .build() }; + + private MultiblockTooltipBuilder tooltipBuilder; + + public int mUpdate = 0; + public boolean mMachine = false; + + public ItemStackHandler inputItems = new ItemStackHandler(INPUT_SLOTS); + public ItemStackHandler outputItems = new ItemStackHandler(OUTPUT_SLOTS); + public Queue outputBuffer = new ConcurrentLinkedQueue<>(); + + public final Queue pendingTrades = new LinkedBlockingQueue<>(); + private boolean newBufferedOutputs = false; + private int ticksSinceOutput = 0; + + public MTEVendingMachine(final int aID, final String aName, final String aNameRegional) { + super(aID, aName, aNameRegional); + } + + public void sendTradeRequest(TradeItemDisplay trade) { + IGregTechTileEntity baseTile = getBaseMetaTileEntity(); + if (baseTile == null) { + return; + } + NetTradeRequestSync.sendTradeRequest( + trade, + baseTile.getWorld(), + baseTile.getXCoord(), + baseTile.getYCoord(), + baseTile.getZCoord()); + } + + public void addTradeRequest(TradeRequest trade) { + VendingMachine.LOG.info("received new trade request"); + this.pendingTrades.add(trade); + } + + public void dispenseItems() { + if (!this.pendingTrades.isEmpty()) { + TradeRequest tradeRequest = this.pendingTrades.poll(); + if (!processTradeOnServer(tradeRequest)) { + VendingMachine.LOG.warn( + "Unable to complete trade. Either input items changed after trade submission, or a double click was sent."); + } + NetTradeRequestSync.sendAck(tradeRequest.player); + } + if ( + this.newBufferedOutputs + || (!this.outputBuffer.isEmpty() && this.ticksSinceOutput % Config.dispense_frequency == 0) + ) { + int remainingDispensables = Config.dispense_amount; + while (!this.outputBuffer.isEmpty() && remainingDispensables > 0) { + ItemStack next = this.outputBuffer.peek(); + + if (next == null) { // impossible, but just in case + this.outputBuffer.poll(); + } else { + ItemStack nextCopy = next.copy(); + nextCopy.stackSize = 1; + for (int i = 0; i < MTEVendingMachine.OUTPUT_SLOTS && remainingDispensables > 0 + && next.stackSize > 0; i++) { + // check for existing stacks + ItemStack cur = this.outputItems.getStackInSlot(i); + if (cur != null) { + ItemStack curCopy = cur.copy(); + curCopy.stackSize = 1; + if ( + ItemStack.areItemStacksEqual(curCopy, nextCopy) + && ItemStack.areItemStackTagsEqual(curCopy, nextCopy) + ) { + int change = Math.min( + Math.min(remainingDispensables, curCopy.getMaxStackSize() - cur.stackSize), + next.stackSize); + cur.stackSize += change; + this.outputItems.setStackInSlot(i, cur); + next.stackSize -= change; + remainingDispensables -= change; + } + } + } + for (int i = 0; i < MTEVendingMachine.OUTPUT_SLOTS && remainingDispensables > 0 + && next.stackSize > 0; i++) { + // make new stack + ItemStack cur = this.outputItems.getStackInSlot(i); + if (cur == null) { + int change = Math.min(remainingDispensables, next.stackSize); + ItemStack output = next.copy(); + output.stackSize = change; + this.outputItems.setStackInSlot(i, output); + remainingDispensables -= change; + next.stackSize -= change; + } + } + + if (next.stackSize == 0) { + this.outputBuffer.poll(); + } else { // outputs full or dispensed enough items this cycle + break; + } + } + } + } + ticksSinceOutput = this.newBufferedOutputs ? 0 : ticksSinceOutput + 1; + this.newBufferedOutputs = false; + } + + private boolean processTradeOnServer(TradeRequest tradeRequest) { + if ( + tradeRequest == null || !TradeDatabase.INSTANCE.getTradeGroups() + .get(tradeRequest.tradeGroup) + .canExecuteTrade(tradeRequest.playerID) + ) { + return false; + } + ItemStack[] inputSlots = new ItemStack[MTEVendingMachine.INPUT_SLOTS]; + for (int i = 0; i < MTEVendingMachine.INPUT_SLOTS; i++) { + ItemStack curStack = this.inputItems.getStackInSlot(i); + inputSlots[i] = curStack == null ? null : curStack.copy(); + } + + Trade trade = TradeDatabase.INSTANCE.getTradeGroupFromId(tradeRequest.tradeGroup) + .getTrades() + .get(tradeRequest.tradeGroupOrder); + for (BigItemStack stack : trade.fromItems) { + ItemStack requiredStack = stack.getBaseStack(); + int requiredAmount = stack.stackSize; + // Remove Items from last stacks if possible + for (int i = MTEVendingMachine.INPUT_SLOTS - 1; i >= 0 && requiredAmount > 0; i--) { + if (inputSlots[i] == null) { + continue; + } + ItemStack tmp = inputSlots[i].copy(); + tmp.stackSize = 1; + if ( + ItemStack.areItemStacksEqual(requiredStack, tmp) + && ItemStack.areItemStackTagsEqual(requiredStack, tmp) + ) { + if (requiredAmount >= inputSlots[i].stackSize) { + requiredAmount -= inputSlots[i].stackSize; + inputSlots[i] = null; + } else { + inputSlots[i].stackSize -= requiredAmount; + requiredAmount = 0; + } + } + } + if (requiredAmount > 0) { + return false; + } + } + + for (int i = 0; i < MTEVendingMachine.INPUT_SLOTS; i++) { + this.inputItems.setStackInSlot(i, inputSlots[i]); + } + + for (BigItemStack toItem : trade.toItems) { + if (toItem == null) continue; + this.outputBuffer.addAll(toItem.getCombinedStacks()); + this.newBufferedOutputs = true; + } + TradeDatabase.INSTANCE.getTradeGroups() + .get(tradeRequest.tradeGroup) + .executeTrade(tradeRequest.playerID); + NetTradeStateSync.sendTradeState(tradeRequest.player, false); + return true; + } + + @Override + public boolean getDefaultHasMaintenanceChecks() { + return false; + } + + public MTEVendingMachine(String aName) { + super(aName); + } + + @Override + public String[] getStructureDescription(ItemStack stackSize) { + return getTooltip().getStructureHint(); + } + + protected MultiblockTooltipBuilder getTooltip() { + if (tooltipBuilder == null) { + tooltipBuilder = new MultiblockTooltipBuilder(); + tooltipBuilder.addMachineType("Vending Machine") + .addInfo("Who even restocks this...") + .beginStructureBlock(2, 3, 1, false) + .addController("Middle") + .addOtherStructurePart("Tin Item Pipe Casings", "Everything except the controller") + .toolTipFinisher(); + } + return tooltipBuilder; + } + + @Override + protected boolean forceUseMui2() { + return true; + } + + @Override + protected @NotNull MTEVendingMachineGui getGui() { + if (VendingMachine.proxy.isClient()) { + NetAvailableTradeSync.requestSync(); + NetTradeStateSync.requestSync(); + } + return new MTEVendingMachineGui(this, CUSTOM_UI_HEIGHT); + } + + @Override + public int getGUIHeight() { + return CUSTOM_UI_HEIGHT; + } + + @Override + public boolean isTeleporterCompatible() { + return false; + } + + @Override + public boolean isFacingValid(ForgeDirection facing) { + return (facing.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) == 0; + } + + @Override + public ITexture[] getTexture(IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, ForgeDirection facing, + int colorIndex, boolean active, boolean redstoneLevel) { + if (side == facing) { + return active ? FACING_ACTIVE : FACING_FRONT; + } + return FACING_SIDE; + } + + @Override + public String[] getDescription() { + return getCurrentDescription(); + } + + @Override + public boolean allowCoverOnSide(ForgeDirection side, ItemStack coverItem) { + return (CoverRegistry.getCoverPlacer(coverItem) + .allowOnPrimitiveBlock()) && (super.allowCoverOnSide(side, coverItem)); + } + + @Override + public String[] getPrimaryDescription() { + return getTooltip().getInformation(); + } + + @Override + public String[] getSecondaryDescription() { + return getTooltip().getStructureInformation(); + } + + @Override + public void saveNBTData(NBTTagCompound aNBT) { + super.saveNBTData(aNBT); + if (inputItems != null) { + aNBT.setTag("inputs", inputItems.serializeNBT()); + } + if (outputItems != null) { + aNBT.setTag("outputs", outputItems.serializeNBT()); + } + NBTTagList pendingOutputs = new NBTTagList(); + for (ItemStack itemStack : outputBuffer) { + pendingOutputs.appendTag(itemStack.writeToNBT(new NBTTagCompound())); + } + aNBT.setTag("outputBuffer", pendingOutputs); + } + + @Override + public void loadNBTData(NBTTagCompound aNBT) { + super.loadNBTData(aNBT); + if (inputItems != null) { + inputItems.deserializeNBT(aNBT.getCompoundTag("inputs")); + } + if (outputItems != null) { + outputItems.deserializeNBT(aNBT.getCompoundTag("outputs")); + } + NBTTagList pendingOutputs = aNBT.getTagList("outputBuffer", Constants.NBT.TAG_COMPOUND); + for (int i = 0; i < pendingOutputs.tagCount(); i++) { + outputBuffer.add(GTUtility.loadItem(pendingOutputs.getCompoundTagAt(i))); + } + } + + @Override + public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } + + @Override + public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side, + ItemStack aStack) { + return false; + } + + @Override + public byte getTileEntityBaseType() { + return 0; + } + + @Override + public ExtendedFacing getExtendedFacing() { + return ExtendedFacing.of(getBaseMetaTileEntity().getFrontFacing()); + } + + @Override + public void setExtendedFacing(ExtendedFacing alignment) { + getBaseMetaTileEntity().setFrontFacing(alignment.getDirection()); + } + + @Override + public IAlignmentLimits getAlignmentLimits() { + return (d, r, f) -> (d.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) == 0; + } + + @Override + public IMetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) { + return new MTEVendingMachine(this.mName); + } + + @Override + public boolean checkMachine(IGregTechTileEntity aBaseMetaTileEntity, ItemStack aStack) { + if (getBaseMetaTileEntity() == null) { + VendingMachine.LOG.warn("Check machine failed as Base MTE is null"); + return false; + } + return STRUCTURE_DEFINITION.check( + this, + "main", + getBaseMetaTileEntity().getWorld(), + getExtendedFacing(), + getBaseMetaTileEntity().getXCoord(), + getBaseMetaTileEntity().getYCoord(), + getBaseMetaTileEntity().getZCoord(), + 1, + 1, + 0, + !mMachine); + } + + @Override + public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTimer) { + if ((aBaseMetaTileEntity.isClientSide()) && (aBaseMetaTileEntity.isActive())) { + // spawn something maybe + } + if (aBaseMetaTileEntity.isServerSide()) { + dispenseItems(); + if (this.mUpdate++ % STRUCTURE_CHECK_TICKS == 0) { + this.mMachine = checkMachine(aBaseMetaTileEntity, null); + aBaseMetaTileEntity.setActive(this.mMachine); + } + } + } + + public boolean getActive() { + return this.getBaseMetaTileEntity() != null && this.getBaseMetaTileEntity() + .isActive(); + } + + @Override + public void onFirstTick(IGregTechTileEntity aBaseMetaTileEntity) { + super.onFirstTick(aBaseMetaTileEntity); + if (aBaseMetaTileEntity.isClientSide()) + StructureLibAPI.queryAlignment((IAlignmentProvider) aBaseMetaTileEntity); + } + + @Override + public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) { + if (mMachine) return -1; + return STRUCTURE_DEFINITION.survivalBuild( + this, + stackSize, + "main", + getBaseMetaTileEntity().getWorld(), + getExtendedFacing(), + getBaseMetaTileEntity().getXCoord(), + getBaseMetaTileEntity().getYCoord(), + getBaseMetaTileEntity().getZCoord(), + 1, + 1, + 0, + elementBudget, + env, + false); + } + + @Override + public void construct(ItemStack stackSize, boolean hintsOnly) { + STRUCTURE_DEFINITION.buildOrHints( + this, + stackSize, + "main", + getBaseMetaTileEntity().getWorld(), + getExtendedFacing(), + getBaseMetaTileEntity().getXCoord(), + getBaseMetaTileEntity().getYCoord(), + getBaseMetaTileEntity().getZCoord(), + 1, + 1, + 0, + hintsOnly); + } +} diff --git a/src/main/java/com/cubefury/vendingmachine/blocks/TileVendingMachine.java b/src/main/java/com/cubefury/vendingmachine/blocks/TileVendingMachine.java deleted file mode 100644 index fb33224..0000000 --- a/src/main/java/com/cubefury/vendingmachine/blocks/TileVendingMachine.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.cubefury.vendingmachine.blocks; - -import java.util.List; - -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.inventory.IInventory; -import net.minecraft.item.ItemStack; -import net.minecraft.tileentity.TileEntity; - -import com.cubefury.vendingmachine.VendingMachine; -import com.cubefury.vendingmachine.trade.TradeGroupWrapper; - -public class TileVendingMachine extends TileEntity implements IInventory { - - public List visibleTrades; - - public TileVendingMachine() { - super(); - } - - @Override - public int getSizeInventory() { - return 0; - } - - @Override - public ItemStack getStackInSlot(int idx) { - return null; - } - - @Override - public ItemStack decrStackSize(int index, int count) { - return null; - } - - @Override - public ItemStack getStackInSlotOnClosing(int index) { - return null; - } - - @Override - public void setInventorySlotContents(int index, ItemStack stack) {} - - @Override - public String getInventoryName() { - return VendingMachine.vendingMachine.getLocalizedName(); - } - - @Override - public boolean hasCustomInventoryName() { - return false; - } - - @Override - public int getInventoryStackLimit() { - return 0; - } - - @Override - public boolean isUseableByPlayer(EntityPlayer player) { - return player.getDistanceSq(this.xCoord, this.yCoord, this.zCoord) < 256; - } - - @Override - public void openInventory() {} - - @Override - public void closeInventory() {} - - @Override - public boolean isItemValidForSlot(int index, ItemStack stack) { - return false; - } - - @Override - public void updateEntity() { - return; - } - -} diff --git a/src/main/java/com/cubefury/vendingmachine/blocks/gui/MTEVendingMachineGui.java b/src/main/java/com/cubefury/vendingmachine/blocks/gui/MTEVendingMachineGui.java new file mode 100644 index 0000000..93f1d45 --- /dev/null +++ b/src/main/java/com/cubefury/vendingmachine/blocks/gui/MTEVendingMachineGui.java @@ -0,0 +1,421 @@ +package com.cubefury.vendingmachine.blocks.gui; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.minecraft.entity.item.EntityItem; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.World; + +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.api.widget.IWidget; +import com.cleanroommc.modularui.factory.PosGuiData; +import com.cleanroommc.modularui.screen.ModularPanel; +import com.cleanroommc.modularui.screen.UISettings; +import com.cleanroommc.modularui.utils.Alignment; +import com.cleanroommc.modularui.value.sync.BooleanSyncValue; +import com.cleanroommc.modularui.value.sync.PanelSyncManager; +import com.cleanroommc.modularui.widget.ParentWidget; +import com.cleanroommc.modularui.widget.SingleChildWidget; +import com.cleanroommc.modularui.widgets.ListWidget; +import com.cleanroommc.modularui.widgets.PageButton; +import com.cleanroommc.modularui.widgets.PagedWidget; +import com.cleanroommc.modularui.widgets.SlotGroupWidget; +import com.cleanroommc.modularui.widgets.ToggleButton; +import com.cleanroommc.modularui.widgets.layout.Column; +import com.cleanroommc.modularui.widgets.layout.Flow; +import com.cleanroommc.modularui.widgets.layout.Row; +import com.cleanroommc.modularui.widgets.slot.ItemSlot; +import com.cleanroommc.modularui.widgets.slot.ModularSlot; +import com.cubefury.vendingmachine.VendingMachine; +import com.cubefury.vendingmachine.blocks.MTEVendingMachine; +import com.cubefury.vendingmachine.gui.GuiTextures; +import com.cubefury.vendingmachine.gui.WidgetThemes; +import com.cubefury.vendingmachine.trade.TradeCategory; +import com.cubefury.vendingmachine.trade.TradeDatabase; +import com.cubefury.vendingmachine.util.BigItemStack; +import com.cubefury.vendingmachine.util.Translator; + +import gregtech.api.metatileentity.implementations.gui.MTEMultiBlockBaseGui; +import gregtech.api.modularui2.GTGuiTextures; +import gregtech.api.modularui2.GTWidgetThemes; + +public class MTEVendingMachineGui extends MTEMultiBlockBaseGui { + + private final MTEVendingMachine base; + private final int height; + + public static boolean forceRefresh = false; + + private boolean ejectItems = false; + private final Map> displayedTrades = new HashMap<>(); + private final List tradeCategories = new ArrayList<>(); + + private PosGuiData guiData; + private PagedWidget.Controller tabController; + private SearchBar searchBar; + + public static final int ITEMS_PER_ROW = 3; + public static final int ITEM_HEIGHT = 25; + public static final int ITEM_WIDTH = 47; + private static final int ROW_SEPARATOR_HEIGHT = 5; + + public MTEVendingMachineGui(MTEVendingMachine base, int height) { + super(base); + this.base = base; + this.height = height; + + this.tradeCategories.add(TradeCategory.ALL); + this.tradeCategories.addAll(TradeDatabase.INSTANCE.getTradeCategories()); + + for (TradeCategory c : this.tradeCategories) { + displayedTrades.put(c, new ArrayList<>(MTEVendingMachine.MAX_TRADES)); + for (int i = 0; i < MTEVendingMachine.MAX_TRADES; i++) { + displayedTrades.get(c) + .add(new TradeItemDisplayWidget(null)); + } + } + + this.tabController = VendingMachine.proxy.isClient() ? new PagedWidget.Controller() : null; + this.searchBar = VendingMachine.proxy.isClient() ? createSearchBar() : null; + } + + public MTEVendingMachine getBase() { + return base; + } + + public static void setForceRefresh() { + forceRefresh = true; + } + + @Override + public ModularPanel build(PosGuiData guiData, PanelSyncManager syncManager, UISettings uiSettings) { + this.guiData = guiData; + + registerSyncValues(syncManager); + ModularPanel panel = new TradeMainPanel("MTEMultiBlockBase", this, guiData, syncManager).size(178, height) + .padding(4); + panel.child(createCategoryTabs(this.tabController)); + Flow mainColumn = new Column().width(170); + if (VendingMachine.proxy.isClient()) { // client side filtering + mainColumn.child(createTitleTextStyle(base.getLocalName())) + .child(this.searchBar) + .child(createTradeUI((TradeMainPanel) panel, this.tabController)); + } + mainColumn.child(createInventoryRow(panel, syncManager)); + panel.child(mainColumn); + panel.child( + new Column().size(20) + .right(5)); + panel.child(createIOColumn(syncManager)); + return panel; + } + + public IWidget createCategoryTabs(PagedWidget.Controller tabController) { + Flow tabColumn = new Column().width(40) + .height(100) + .left(-29) + .top(40) + .coverChildren(); + + for (int i = 0; i < this.tradeCategories.size(); i++) { + int index = i; + tabColumn.child( + new PageButton(i, tabController).tab(GuiTextures.TAB_LEFT, -1) + .overlay( + this.tradeCategories.get(i) + .getTexture() + .asIcon() + .margin(6) + .center()) + .tooltipBuilder(builder -> { + builder.clearText(); + builder.addLine( + Translator.translate( + this.tradeCategories.get(index) + .getUnlocalized_name())); + })); + } + return tabColumn; + } + + // why is the original method private lmao + private IWidget createTitleTextStyle(String title) { + return new SingleChildWidget<>().coverChildren() + .topRel(0, -4, 1) + .leftRel(0, -4, 0) + .widgetTheme(GTWidgetThemes.BACKGROUND_TITLE) + .child( + IKey.str(title) + .asWidget() + .alignment(Alignment.Center) + .widgetTheme(GTWidgetThemes.TEXT_TITLE) + .marginLeft(5) + .marginRight(5) + .marginTop(5) + .marginBottom(1)); + } + + private SearchBar createSearchBar() { + return new SearchBar(this).width(162) + .left(3) + .top(5) + .height(10); + } + + private void ejectItems() { + if (!this.guiData.isClient()) { + if (base.getBaseMetaTileEntity() == null) { + VendingMachine.LOG.info("Unable to eject items as the base MTE for the Vending Machine was null."); + } else { + World world = base.getBaseMetaTileEntity() + .getWorld(); + int posX = base.getBaseMetaTileEntity() + .getXCoord(); + int posY = base.getBaseMetaTileEntity() + .getYCoord(); + int posZ = base.getBaseMetaTileEntity() + .getZCoord(); + int offsetX = base.getExtendedFacing() + .getDirection().offsetX; + int offsetY = base.getExtendedFacing() + .getDirection().offsetY; + int offsetZ = base.getExtendedFacing() + .getDirection().offsetZ; + for (int i = 0; i < MTEVendingMachine.INPUT_SLOTS; i++) { + ItemStack stack = base.inputItems.getStackInSlot(i); + if (stack != null) { + ItemStack extracted = base.inputItems.extractItem(i, stack.stackSize, false); + if (extracted == null) { // if somehow it got pulled out already + continue; + } + final EntityItem itemEntity = new EntityItem( + world, + posX + offsetX * 0.5, + posY + offsetY * 0.5, + posZ + offsetZ * 0.5, + new ItemStack(extracted.getItem(), extracted.stackSize, extracted.getItemDamage())); + if (extracted.hasTagCompound()) { + itemEntity.getEntityItem() + .setTagCompound( + (NBTTagCompound) extracted.getTagCompound() + .copy()); + } + itemEntity.delayBeforeCanPickup = 0; + itemEntity.motionX = 0.05f * offsetX; + itemEntity.motionY = 0.05f * offsetY; + itemEntity.motionZ = 0.05f * offsetZ; + world.spawnEntityInWorld(itemEntity); + } + } + } + } + ejectItems = false; + } + + private IWidget createIOColumn(PanelSyncManager syncManager) { + return new ParentWidget<>().excludeAreaInNEI() + .width(50) + .height(160) + .right(-48) + .top(40) + .widgetTheme(WidgetThemes.BACKGROUND_SIDEPANEL) + .child( + new Column().child( + GuiTextures.INPUT_SPRITE.asWidget() + .leftRel(0.5f) + .top(8) + .width(30) + .height(20)) + .child( + new Row().child(createInputRow(syncManager).center()) + .top(20) + .height(18 * 3)) + .child( + new Row().child( + new ToggleButton().overlay(GTGuiTextures.OVERLAY_BUTTON_CYCLIC) + .tooltipBuilder(t -> t.addLine(IKey.lang("vendingmachine.gui.item_eject"))) + .syncHandler("ejectItems") + .center()) + .top(80) + .height(18)) + .child( + GuiTextures.OUTPUT_SPRITE.asWidget() + .leftRel(0.5f) + .bottom(34) + .width(30) + .height(20)) + .child( + new Row().child(createOutputSlots().center()) + .bottom(6) + .height(18 * 2)) + .right(1)); + } + + private SlotGroupWidget createInputRow(PanelSyncManager syncManager) { + return SlotGroupWidget.builder() + .matrix("II", "II", "II") + .key('I', index -> { + return new ItemSlot().slot( + new ModularSlot(base.inputItems, index).slotGroup("inputSlotGroup") + .changeListener((newItem, onlyAmountChanged, client, init) -> { + if (guiData.isClient()) { + forceRefresh = true; + } + })); + }) + .build(); + } + + private SlotGroupWidget createOutputSlots() { + // we use slot group widget in case we want to increase the number of output slots in the future + return SlotGroupWidget.builder() + .matrix("II", "II") + .key('I', index -> { + ModularSlot ms = new ModularSlot(base.outputItems, index).accessibility(false, true) + .slotGroup("outputSlotGroup"); + ms.changeListener((newItem, onlyAmountChanged, client, init) -> {}); + return new ItemSlot().slot(ms); + }) + .build(); + } + + // spotless:off + private IWidget createTradeUI(TradeMainPanel rootPanel, PagedWidget.Controller tabController) { + PagedWidget paged = new PagedWidget<>() + .width(162) + .debugName("paged") + .controller(tabController) + .background(GuiTextures.TEXT_FIELD_BACKGROUND) + .heightRel(0.5f); + for (TradeCategory category : this.tradeCategories) { + ListWidget tradeList = new ListWidget<>().debugName("items").heightRel(1.0f) + .width(156) + .margin(1) + .collapseDisabledChild(true); + + // Higher first row top margin + Flow row = new TradeRow().height(ITEM_HEIGHT).margin(1).marginTop(4); + + for (int i = 0; i < MTEVendingMachine.MAX_TRADES; i++) { + int index = i; + displayedTrades.get(category).get(i).setRootPanel(rootPanel); + row.child(displayedTrades.get(category).get(i) + .tooltipDynamic(builder -> { + builder.clearText(); + synchronized (displayedTrades) { + if (index < displayedTrades.get(category).size()) { + TradeItemDisplay cur = displayedTrades.get(category).get(index).getDisplay(); + if (cur != null) { + for (BigItemStack toItem : cur.toItems) { + builder.addLine(IKey.str(toItem.stackSize + " " + toItem.getBaseStack().getDisplayName()).style(IKey.AQUA)); + // builder.add(new ItemDrawable(toItem.getBaseStack())); + } + builder.emptyLine(); + builder.addLine(IKey.str(Translator.translate("vendingmachine.gui.required_inputs")).style(IKey.DARK_GREEN, IKey.ITALIC)); + for (BigItemStack fromItem : cur.fromItems) { + builder.addLine(IKey.str(fromItem.stackSize + " " + fromItem.getBaseStack().getDisplayName()).style(IKey.DARK_GREEN)); + } + + builder.emptyLine(); + builder.addLine(IKey.str(cur.label).style(IKey.GRAY)); + } + } + } + }) + .tooltipAutoUpdate(true) + .setEnabledIf(slot -> ((TradeItemDisplayWidget) slot).getDisplay() != null) + .margin(2)); + if (i % ITEMS_PER_ROW == ITEMS_PER_ROW - 1) { + tradeList.child(row); + + row = new TradeRow().height(ITEM_HEIGHT).margin(1); + } + } + if (row.hasChildren()) { + tradeList.child(row); + } + tradeList.child(new Row().height(2)); // bottom padding for last row + paged.addPage(tradeList); + } + + return new Row().child(paged.top(0)) + .left(3) + .top(24); + } + // spotless:on + + // why is the original method private lmao + private IWidget createInventoryRow(ModularPanel panel, PanelSyncManager syncManager) { + return new Row().widthRel(1) + .height(76) + .alignX(0) + .bottom(5) + .childIf( + base.doesBindPlayerInventory(), + SlotGroupWidget.playerInventory(false) + .marginLeft(4)); + } + + @Override + protected void registerSyncValues(PanelSyncManager syncManager) { + super.registerSyncValues(syncManager); + syncManager.registerSlotGroup("inputSlotGroup", 7, true); + syncManager.registerSlotGroup("outputSlotGroup", 1, false); + + BooleanSyncValue ejectItemsSyncer = new BooleanSyncValue(() -> this.ejectItems, val -> { + this.ejectItems = val; + if (this.ejectItems) { + ejectItems(); + } + }); + syncManager.syncValue("ejectItems", ejectItemsSyncer); + } + + public void attemptPurchase(TradeItemDisplay display) { + submitTradesToServer(display); + forceRefresh = true; + } + + private void submitTradesToServer(TradeItemDisplay trade) { + if (!trade.tradeableNow || !trade.enabled) { + return; + } + base.sendTradeRequest(trade); + } + + public static void resetForceRefresh() { + forceRefresh = false; + } + + public void updateSlots(Map> trades) { + synchronized (displayedTrades) { + for (Map.Entry> entry : displayedTrades.entrySet()) { + int displayedSize = trades.get(entry.getKey()) == null ? 0 + : trades.get(entry.getKey()) + .size(); + for (int i = 0; i < MTEVendingMachine.MAX_TRADES; i++) { + if (i < displayedSize) { + displayedTrades.get(entry.getKey()) + .get(i) + .setDisplay( + trades.get(entry.getKey()) + .get(i)); + } else { + displayedTrades.get(entry.getKey()) + .get(i) + .setDisplay(null); + } + } + } + } + } + + public String getSearchBarText() { + return this.searchBar.getText(); + } + +} diff --git a/src/main/java/com/cubefury/vendingmachine/blocks/gui/SearchBar.java b/src/main/java/com/cubefury/vendingmachine/blocks/gui/SearchBar.java new file mode 100644 index 0000000..a758b07 --- /dev/null +++ b/src/main/java/com/cubefury/vendingmachine/blocks/gui/SearchBar.java @@ -0,0 +1,101 @@ +package com.cubefury.vendingmachine.blocks.gui; + +import java.awt.Point; + +import org.jetbrains.annotations.NotNull; + +import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; +import com.cleanroommc.modularui.theme.WidgetTextFieldTheme; +import com.cleanroommc.modularui.widgets.textfield.BaseTextFieldWidget; +import com.cubefury.vendingmachine.gui.GuiTextures; +import com.cubefury.vendingmachine.util.Translator; + +public class SearchBar extends BaseTextFieldWidget { + + private MTEVendingMachineGui gui; + private String previousText; + + public SearchBar(MTEVendingMachineGui gui) { + super(); + + this.gui = gui; + + background(GuiTextures.TEXT_FIELD_BACKGROUND); + setText(""); + this.previousText = ""; + hintText(Translator.translate("vendingmachine.gui.search")); + } + + @Override + public void onFocus(ModularGuiContext context) { + super.onFocus(context); + Point main = this.handler.getMainCursor(); + if (main.x == 0) { + this.handler.setCursor(main.y, getText().length(), true, true); + } + } + + @Override + public boolean canHover() { + return true; + } + + @Override + public void drawForeground(ModularGuiContext context) { + if ( + hasTooltip() && getScrollData().isScrollBarActive(getScrollArea()) + && isHoveringFor(getTooltip().getShowUpTimer()) + ) { + getTooltip().draw(getContext()); + } + } + + @NotNull + public String getText() { + if ( + this.handler.getText() + .isEmpty() + ) { + return ""; + } + if ( + this.handler.getText() + .size() > 1 + ) { + throw new IllegalStateException("TextFieldWidget can only have one line!"); + } + return this.handler.getText() + .get(0); + } + + @Override + protected void setupDrawText(ModularGuiContext context, WidgetTextFieldTheme widgetTheme) { + this.renderer.setSimulate(false); + this.renderer.setPos(getArea().getPadding().left, 0); + this.renderer.setScale(this.scale); + this.renderer.setAlignment(this.textAlignment, -1, getArea().height); + } + + public void setText(@NotNull String text) { + if ( + this.handler.getText() + .isEmpty() + ) { + this.handler.getText() + .add(text); + } else { + this.handler.getText() + .set(0, text); + } + } + + @Override + public void onUpdate() { + super.onUpdate(); + String curText = getText(); + if (!curText.equals(previousText)) { + gui.setForceRefresh(); + } + previousText = curText; + } +} diff --git a/src/main/java/com/cubefury/vendingmachine/blocks/gui/TradeItemDisplay.java b/src/main/java/com/cubefury/vendingmachine/blocks/gui/TradeItemDisplay.java new file mode 100644 index 0000000..95548c0 --- /dev/null +++ b/src/main/java/com/cubefury/vendingmachine/blocks/gui/TradeItemDisplay.java @@ -0,0 +1,125 @@ +package com.cubefury.vendingmachine.blocks.gui; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraftforge.common.util.Constants; + +import com.cubefury.vendingmachine.util.BigItemStack; +import com.cubefury.vendingmachine.util.NBTConverter; + +import codechicken.nei.api.ItemFilter; + +public class TradeItemDisplay { + + public List fromItems; + public List toItems; + public ItemStack display; + public UUID tgID; + public int tradeGroupOrder; + public String label; + public long cooldown; + public String cooldownText; + public boolean hasCooldown; + public boolean enabled; + public boolean tradeableNow; + + public TradeItemDisplay(List fromItems, List toItems, ItemStack display, UUID tgID, + int tradeGroupOrder, String label, long cooldown, String cooldownText, boolean hasCooldown, boolean enabled, + boolean tradeableNow) { + this.fromItems = fromItems; + this.toItems = toItems; + this.display = display; + this.tgID = tgID; + this.tradeGroupOrder = tradeGroupOrder; + this.label = label; + this.cooldown = cooldown; + this.cooldownText = cooldownText; + this.hasCooldown = hasCooldown; + this.enabled = enabled; + this.tradeableNow = tradeableNow; + } + + public static TradeItemDisplay readFromNBT(NBTTagCompound nbt) { + List newFromItems = new ArrayList<>(); + NBTTagList fromItemsList = nbt.getTagList("fromItems", Constants.NBT.TAG_COMPOUND); + for (int i = 0; i < fromItemsList.tagCount(); i++) { + newFromItems.add(BigItemStack.loadItemStackFromNBT(fromItemsList.getCompoundTagAt(i))); + } + List newToItems = new ArrayList<>(); + NBTTagList toItemsList = nbt.getTagList("toItems", Constants.NBT.TAG_COMPOUND); + for (int i = 0; i < fromItemsList.tagCount(); i++) { + newToItems.add(BigItemStack.loadItemStackFromNBT(toItemsList.getCompoundTagAt(i))); + } + return new TradeItemDisplay( + newFromItems, + newToItems, + ItemStack.loadItemStackFromNBT(nbt.getCompoundTag("display")), + NBTConverter.UuidValueType.TRADEGROUP.readId(nbt), + nbt.getInteger("tradeGroupOrder"), + nbt.getString("label"), + nbt.getLong("cooldown"), + nbt.getString("cooldownText"), + nbt.getBoolean("hasCooldown"), + nbt.getBoolean("enabled"), + nbt.getBoolean("tradeableNow")); + } + + public NBTTagCompound writeToNBT(NBTTagCompound nbt) { + NBTTagList fromItemsNBT = new NBTTagList(); + for (BigItemStack bis : this.fromItems) { + fromItemsNBT.appendTag(bis.writeToNBT(new NBTTagCompound())); + } + nbt.setTag("fromItems", fromItemsNBT); + NBTTagList toItemsNBT = new NBTTagList(); + for (BigItemStack bis : this.toItems) { + toItemsNBT.appendTag(bis.writeToNBT(new NBTTagCompound())); + } + nbt.setTag("toItems", toItemsNBT); + nbt.setTag("display", this.display.writeToNBT(new NBTTagCompound())); + NBTConverter.UuidValueType.TRADEGROUP.writeId(this.tgID, nbt); + nbt.setInteger("tradeGroupOrder", this.tradeGroupOrder); + nbt.setString("label", this.label); + nbt.setLong("cooldown", this.cooldown); + nbt.setString("cooldownText", this.cooldownText); + nbt.setBoolean("hasCooldown", this.hasCooldown); + nbt.setBoolean("enabled", this.enabled); + nbt.setBoolean("tradeableNow", this.tradeableNow); + + return nbt; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof TradeItemDisplay)) { + return false; + } + TradeItemDisplay other = (TradeItemDisplay) obj; + return this.fromItems.equals(other.fromItems) && this.toItems.equals(other.toItems) + && ItemStack.areItemStacksEqual(this.display, other.display) + && ItemStack.areItemStackTagsEqual(this.display, other.display) + && this.tgID == other.tgID + && this.tradeGroupOrder == other.tradeGroupOrder + && this.label.equals(other.label) + && this.cooldown == other.cooldown + && this.cooldownText.equals(other.cooldownText) + && this.hasCooldown == other.hasCooldown + && this.enabled == other.enabled + && this.tradeableNow == other.tradeableNow; + } + + public boolean satisfiesSearch(ItemFilter filter, String searchStringNoCase) { + if (filter == null) { + return this.label.toLowerCase() + .contains(searchStringNoCase); + } + return filter.matches(this.display) || this.toItems.stream() + .anyMatch(bis -> filter.matches(bis.getBaseStack())) + || this.fromItems.stream() + .anyMatch(bis -> filter.matches(bis.getBaseStack())); + } +} diff --git a/src/main/java/com/cubefury/vendingmachine/blocks/gui/TradeItemDisplayWidget.java b/src/main/java/com/cubefury/vendingmachine/blocks/gui/TradeItemDisplayWidget.java new file mode 100644 index 0000000..a40be03 --- /dev/null +++ b/src/main/java/com/cubefury/vendingmachine/blocks/gui/TradeItemDisplayWidget.java @@ -0,0 +1,103 @@ +package com.cubefury.vendingmachine.blocks.gui; + +import net.minecraft.item.ItemStack; + +import org.jetbrains.annotations.NotNull; + +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.api.value.IValue; +import com.cleanroommc.modularui.api.widget.Interactable; +import com.cleanroommc.modularui.drawable.DynamicDrawable; +import com.cleanroommc.modularui.drawable.GuiDraw; +import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; +import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.utils.Platform; +import com.cleanroommc.modularui.value.sync.GenericSyncValue; +import com.cleanroommc.modularui.value.sync.SyncHandler; +import com.cleanroommc.modularui.widgets.ItemDisplayWidget; +import com.cubefury.vendingmachine.gui.GuiTextures; + +public class TradeItemDisplayWidget extends ItemDisplayWidget implements Interactable { + + private TradeMainPanel rootPanel; + private boolean pressed = false; + private IValue value; + + private TradeItemDisplay display; + + public TradeItemDisplayWidget(TradeItemDisplay display) { + height(MTEVendingMachineGui.ITEM_HEIGHT); + width(MTEVendingMachineGui.ITEM_WIDTH); + background( + new DynamicDrawable(() -> pressed ? GuiTextures.TRADE_BUTTON_PRESSED : GuiTextures.TRADE_BUTTON_UNPRESSED)); + + this.display = display; + this.item((ItemStack) null); + } + + public void setDisplay(TradeItemDisplay display) { + this.display = display; + this.item(display == null ? null : display.display); + } + + public TradeItemDisplay getDisplay() { + return this.display; + } + + public @NotNull Interactable.Result onMousePressed(int mouseButton) { + if (rootPanel.shiftHeld) { + rootPanel.attemptPurchase(this.display); + pressed = true; + return Result.SUCCESS; + } + return Result.IGNORE; + } + + @Override + public void draw(ModularGuiContext context, WidgetTheme widgetTheme) { + ItemStack item = value.getValue(); + if (!Platform.isStackEmpty(item)) { + GuiDraw.drawText(" " + this.display.display.stackSize, 4, 9, 1.0f, 0x0, false); + GuiDraw.drawItem(item, 26, 4, 16, 16, context.getCurrentDrawingZ()); + if (this.display.tradeableNow) { + GuiDraw.drawOutline(1, 1, 45, 23, 0x883CFF00, 2); + } + if (this.display.hasCooldown || !this.display.enabled) { + GuiDraw.drawRoundedRect(1, 1, 45, 23, 0xBB000000, 1, 1); + } + this.overlay( + IKey.str(display.hasCooldown ? this.display.cooldownText : "") + .style(IKey.WHITE)); + } + } + + @Override + public boolean isValidSyncHandler(SyncHandler syncHandler) { + if (syncHandler instanceof GenericSyncValuegenericSyncValue && genericSyncValue.isOfType(ItemStack.class)) { + this.value = genericSyncValue.cast(); + return true; + } + return false; + } + + public ItemDisplayWidget item(IValue itemSupplier) { + this.value = itemSupplier; + setValue(itemSupplier); + return this; + } + + @Override + public void onMouseEndHover() { + pressed = false; + } + + @Override + public boolean onMouseRelease(int mouseButton) { + pressed = false; + return true; + } + + public void setRootPanel(TradeMainPanel rootPanel) { + this.rootPanel = rootPanel; + } +} diff --git a/src/main/java/com/cubefury/vendingmachine/blocks/gui/TradeMainPanel.java b/src/main/java/com/cubefury/vendingmachine/blocks/gui/TradeMainPanel.java new file mode 100644 index 0000000..9e23077 --- /dev/null +++ b/src/main/java/com/cubefury/vendingmachine/blocks/gui/TradeMainPanel.java @@ -0,0 +1,248 @@ +package com.cubefury.vendingmachine.blocks.gui; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +import org.jetbrains.annotations.NotNull; + +import com.cleanroommc.modularui.factory.PosGuiData; +import com.cleanroommc.modularui.screen.ModularPanel; +import com.cleanroommc.modularui.value.sync.PanelSyncManager; +import com.cubefury.vendingmachine.Config; +import com.cubefury.vendingmachine.blocks.MTEVendingMachine; +import com.cubefury.vendingmachine.storage.NameCache; +import com.cubefury.vendingmachine.trade.Trade; +import com.cubefury.vendingmachine.trade.TradeCategory; +import com.cubefury.vendingmachine.trade.TradeDatabase; +import com.cubefury.vendingmachine.trade.TradeGroup; +import com.cubefury.vendingmachine.trade.TradeGroupWrapper; +import com.cubefury.vendingmachine.trade.TradeManager; +import com.cubefury.vendingmachine.util.BigItemStack; +import com.cubefury.vendingmachine.util.Translator; + +import codechicken.nei.SearchField; +import codechicken.nei.api.ItemFilter; + +public class TradeMainPanel extends ModularPanel { + + public boolean shiftHeld = false; + private final MTEVendingMachineGui gui; + private final PanelSyncManager syncManager; + private final PosGuiData guiData; + private EntityPlayer player = null; + private int ticksOpen = 0; + + public TradeMainPanel(@NotNull String name, MTEVendingMachineGui gui, PosGuiData guiData, + PanelSyncManager syncManager) { + super(name); + this.gui = gui; + this.guiData = guiData; + this.syncManager = syncManager; + } + + @Override + public boolean onKeyPressed(char typedChar, int keyCode) { + // left or right shift + if (keyCode == 0x2A || keyCode == 0x36) { + shiftHeld = true; + } + return super.onKeyPressed(typedChar, keyCode); + } + + @Override + public boolean onKeyRelease(char typedChar, int keyCode) { + // left or right shift + if (keyCode == 0x2A || keyCode == 0x36) { + shiftHeld = false; + } + return super.onKeyRelease(typedChar, keyCode); + } + + public void updateGui() { + boolean test = true; + if (test) { + List testTGW = new ArrayList<>(); + for (Map.Entry entry : TradeDatabase.INSTANCE.getTradeGroups() + .entrySet()) { + testTGW.add(new TradeGroupWrapper(entry.getValue(), -1, true)); + } + Map> trades = formatTrades(testTGW); + gui.updateSlots(trades); + } else { + Map> trades = formatTrades( + TradeManager.INSTANCE.getTrades(NameCache.INSTANCE.getUUIDFromPlayer(syncManager.getPlayer()))); + gui.updateSlots(trades); + } + } + + @Override + public void onUpdate() { + + super.onUpdate(); + if (!this.guiData.isClient()) { + return; + } + if (this.player == null && this.syncManager.isInitialised()) { + this.player = syncManager.getPlayer(); + } + if (gui.forceRefresh || (this.ticksOpen % Config.gui_refresh_interval == 0 && player != null && !shiftHeld)) { + updateGui(); + gui.resetForceRefresh(); + } + this.ticksOpen += 1; + } + + public String convertCooldownText(long cd) { + if (cd < 60) { + return cd + Translator.translate("vendingmachine.gui.cooldown_display.second"); + } + if (cd < 3600) { + return cd / 60 + Translator.translate("vendingmachine.gui.cooldown_display.minute"); + } + if (cd < 86400) { + return cd / 3600 + Translator.translate("vendingmachine.gui.cooldown_display.hour"); + } + return cd / 86400 + Translator.translate("vendingmachine.gui.cooldown_display.day"); // doom.jpg + } + + public ItemStack convertToItemStack(BigItemStack stack) { + ItemStack display = stack.getCombinedStacks() + .get(0); + display.stackSize = stack.stackSize; + return display; + } + + public boolean checkItemsSatisfied(List trade, Map availableItems) { + for (BigItemStack bis : trade) { + BigItemStack base = bis.copy(); + base.stackSize = 1; // shouldn't need this, but just in case + if (availableItems.get(base) == null || availableItems.get(base) < bis.stackSize) { + return false; + } + } + return true; + } + + public Map getAvailableItems() { + Map items = new HashMap<>(); + for (int i = 0; i < MTEVendingMachine.INPUT_SLOTS; i++) { + ItemStack stack = this.gui.getBase().inputItems.getStackInSlot(i); + if (stack != null) { + BigItemStack tmp = new BigItemStack(stack); + tmp.stackSize = 1; + items.putIfAbsent(tmp, 0); + items.replace(tmp, items.get(tmp) + stack.stackSize); + } + } + return items; + } + + public Map> formatTrades(List tradeGroups) { + + Map availableItems = this.guiData.isClient() && this.gui.getBase() != null + ? getAvailableItems() + : new HashMap<>(); + + Map> trades = new HashMap<>(); + trades.put(TradeCategory.ALL, new ArrayList<>()); + + for (TradeGroupWrapper tgw : tradeGroups) { + List tradeList = tgw.trade() + .getTrades(); + TradeCategory category = tgw.trade() + .getCategory(); + trades.putIfAbsent(category, new ArrayList<>()); + for (int i = 0; i < tradeList.size(); i++) { + Trade trade = tgw.trade() + .getTrades() + .get(i); + BigItemStack displayItem = trade.toItems.get(0); + TradeItemDisplay tid = new TradeItemDisplay( + trade.fromItems, + trade.toItems, + convertToItemStack(displayItem == null ? trade.displayItem : displayItem), + tgw.trade() + .getId(), + i, + tgw.trade() + .getLabel(), + tgw.cooldown(), + convertCooldownText(tgw.cooldown()), + tgw.cooldown() > 0, + tgw.enabled(), + checkItemsSatisfied(trade.fromItems, availableItems)); + + trades.get(category) + .add(tid); + trades.get(TradeCategory.ALL) + .add(tid); + } + } + + String searchString = gui.getSearchBarText(); + ItemFilter filter = SearchField.getFilter(searchString); + + for (TradeCategory category : trades.keySet()) { + List filteredTrades = trades.get(category); + filteredTrades = filteredTrades.stream() + .filter(tid -> tid.satisfiesSearch(filter, searchString.toLowerCase())) + .collect(Collectors.toList()); + filteredTrades.sort((a, b) -> { + // null case + if (a == null && b == null) return 0; + if (a == null) return 1; + if (b == null) return -1; + if (a.display.getItem() == null && b.display.getItem() == null) return 0; + if (a.display.getItem() == null) return 1; + if (b.display.getItem() == null) return -1; + + // enabled or has cooldown + int rankA = getRank(a); + int rankB = getRank(b); + + if (rankA != rankB) { + return Integer.compare(rankA, rankB); + } + + // cooldown time + int cooldownCmp = Long.compare(b.cooldown, a.cooldown); + if (cooldownCmp != 0) return cooldownCmp; + + // display item ordering + int idCmp = Integer + .compare(Item.getIdFromItem(a.display.getItem()), Item.getIdFromItem(b.display.getItem())); + if (idCmp != 0) return idCmp; + int dmgCmp = Integer.compare(a.display.getItemDamage(), b.display.getItemDamage()); + if (dmgCmp != 0) return dmgCmp; + + // sort by tradegroup Order + return Integer.compare(a.tradeGroupOrder, b.tradeGroupOrder); + + }); + trades.replace(category, filteredTrades); + } + return trades; + } + + private static int getRank(TradeItemDisplay t) { + if (!t.enabled) { + return 5; + } + if (t.tradeableNow) { + return t.hasCooldown ? 2 : 1; + } + return t.hasCooldown ? 4 : 3; + } + + public void attemptPurchase(TradeItemDisplay display) { + gui.attemptPurchase(display); + } +} diff --git a/src/main/java/com/cubefury/vendingmachine/blocks/gui/TradeQueueAdapter.java b/src/main/java/com/cubefury/vendingmachine/blocks/gui/TradeQueueAdapter.java new file mode 100644 index 0000000..97d0f77 --- /dev/null +++ b/src/main/java/com/cubefury/vendingmachine/blocks/gui/TradeQueueAdapter.java @@ -0,0 +1,57 @@ +package com.cubefury.vendingmachine.blocks.gui; + +import java.io.IOException; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Queue; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.PacketBuffer; + +import org.jetbrains.annotations.NotNull; + +import com.cleanroommc.modularui.utils.serialization.IByteBufAdapter; + +public class TradeQueueAdapter implements IByteBufAdapter> { + + @Override + public Queue deserialize(PacketBuffer buffer) throws IOException { + int size = buffer.readVarIntFromBuffer(); + Queue queue = new LinkedList<>(); + + for (int i = 0; i < size; i++) { + NBTTagCompound tag = buffer.readNBTTagCompoundFromBuffer(); + if (tag != null) { + queue.add(TradeItemDisplay.readFromNBT(tag)); + } + } + return queue; + } + + @Override + public void serialize(PacketBuffer buffer, Queue queue) throws IOException { + buffer.writeVarIntToBuffer(queue.size()); + for (TradeItemDisplay item : queue) { + buffer.writeNBTTagCompoundToBuffer(item.writeToNBT(new NBTTagCompound())); + } + } + + @Override + public boolean areEqual(@NotNull Queue t1, @NotNull Queue t2) { + if (t1.size() != t2.size()) { + return false; + } + + Iterator it1 = t1.iterator(); + Iterator it2 = t2.iterator(); + + while (it1.hasNext() && it2.hasNext()) { + TradeItemDisplay d1 = it1.next(); + TradeItemDisplay d2 = it2.next(); + if (!d1.equals(d2)) { + return false; + } + } + return true; + } +} diff --git a/src/main/java/com/cubefury/vendingmachine/blocks/gui/TradeRow.java b/src/main/java/com/cubefury/vendingmachine/blocks/gui/TradeRow.java new file mode 100644 index 0000000..a4ec70b --- /dev/null +++ b/src/main/java/com/cubefury/vendingmachine/blocks/gui/TradeRow.java @@ -0,0 +1,16 @@ +package com.cubefury.vendingmachine.blocks.gui; + +import com.cleanroommc.modularui.api.widget.IWidget; +import com.cleanroommc.modularui.widgets.layout.Row; + +public class TradeRow extends Row { + + public TradeRow() { + super(); + this.collapseDisabledChild(true) + .setEnabledIf( + r -> r.getChildren() + .stream() + .anyMatch(IWidget::isEnabled)); + } +} diff --git a/src/main/java/com/cubefury/vendingmachine/gui/GuiTextures.java b/src/main/java/com/cubefury/vendingmachine/gui/GuiTextures.java new file mode 100644 index 0000000..5892355 --- /dev/null +++ b/src/main/java/com/cubefury/vendingmachine/gui/GuiTextures.java @@ -0,0 +1,76 @@ +package com.cubefury.vendingmachine.gui; + +import com.cleanroommc.modularui.api.GuiAxis; +import com.cleanroommc.modularui.drawable.TabTexture; +import com.cleanroommc.modularui.drawable.UITexture; +import com.cubefury.vendingmachine.VendingMachine; + +public final class GuiTextures { + + public static final UITexture OVERLAY_TRADE_AVAILABLE_HIGHLIGHT = UITexture.builder() + .location(VendingMachine.MODID, "gui/overlay/trade_available") + .imageSize(47, 25) + .adaptable(6) + .name("trade_available_highlight") + .build(); + + public static final UITexture OVERLAY_TRADE_DISABLED = UITexture.builder() + .location(VendingMachine.MODID, "gui/overlay/trade_disabled") + .imageSize(47, 25) + .adaptable(4) + .canApplyTheme() + .name("trade_disabled") + .build(); + + public static final UITexture TRADE_AVAILABLE_BACKGROUND = UITexture.builder() + .location(VendingMachine.MODID, "gui/background/trade_available") + .imageSize(18, 18) + .canApplyTheme() + .name("trade_available_background") + .build(); + + public static final UITexture SIDE_PANEL_BACKGROUND = UITexture.builder() + .location(VendingMachine.MODID, "gui/background/panel_side") + .imageSize(195, 136) + .adaptable(4) + .canApplyTheme() + .name("panel_side_background") + .build(); + + public static final UITexture TEXT_FIELD_BACKGROUND = UITexture.builder() + .location(VendingMachine.MODID, "gui/background/text_field_light_gray") + .imageSize(61, 12) + .adaptable(1) + .canApplyTheme() + .name("text_field_background") + .build(); + + public static final UITexture TRADE_BUTTON_UNPRESSED = UITexture.builder() + .location(VendingMachine.MODID, "gui/background/trade_button_unpressed_color_corrected") + .imageSize(195, 136) + .adaptable(4) + .name("trade_button_unpressed") + .build(); + + public static final UITexture TRADE_BUTTON_PRESSED = UITexture.builder() + .location(VendingMachine.MODID, "gui/background/trade_button_pressed_color_corrected") + .imageSize(195, 136) + .adaptable(4) + .name("trade_button_pressed") + .build(); + + public static final UITexture INPUT_SPRITE = UITexture.builder() + .location(VendingMachine.MODID, "gui/background/input") + .imageSize(30, 20) + .name("background_input") + .build(); + + public static final UITexture OUTPUT_SPRITE = UITexture.builder() + .location(VendingMachine.MODID, "gui/background/output") + .imageSize(30, 20) + .name("background_output") + .build(); + + public static final TabTexture TAB_LEFT = TabTexture + .of(UITexture.fullImage(VendingMachine.MODID, "gui/tabs_left", true), GuiAxis.X, false, 32, 28, 4); +} diff --git a/src/main/java/com/cubefury/vendingmachine/gui/WidgetThemes.java b/src/main/java/com/cubefury/vendingmachine/gui/WidgetThemes.java new file mode 100644 index 0000000..caf00cf --- /dev/null +++ b/src/main/java/com/cubefury/vendingmachine/gui/WidgetThemes.java @@ -0,0 +1,23 @@ +package com.cubefury.vendingmachine.gui; + +import com.cleanroommc.modularui.api.IThemeApi; +import com.cleanroommc.modularui.drawable.UITexture; +import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.utils.Color; + +public final class WidgetThemes { + + public static final String BACKGROUND_SIDEPANEL = "background_side_panel"; + + public static void register() { + IThemeApi themeApi = IThemeApi.get(); + registerThemedTexture(themeApi, BACKGROUND_SIDEPANEL, GuiTextures.SIDE_PANEL_BACKGROUND); + } + + private static void registerThemedTexture(IThemeApi themeApi, String textureThemeId, UITexture background) { + themeApi.registerWidgetTheme( + textureThemeId, + new WidgetTheme(background, null, Color.WHITE.main, 0xFF404040, false), + WidgetTheme::new); + } +} diff --git a/src/main/java/com/cubefury/vendingmachine/integration/nei/IMCforNEI.java b/src/main/java/com/cubefury/vendingmachine/integration/nei/IMCforNEI.java deleted file mode 100644 index 7e4e9c9..0000000 --- a/src/main/java/com/cubefury/vendingmachine/integration/nei/IMCforNEI.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.cubefury.vendingmachine.integration.nei; - -import net.minecraft.nbt.NBTTagCompound; - -import com.cubefury.vendingmachine.VendingMachine; - -import cpw.mods.fml.common.event.FMLInterModComms; - -public class IMCforNEI { - - public static void IMCSender() { - - sendCatalyst("vendingmachine", "vendingmachine:vending_machine"); - sendHandler("vendingmachine", "vendingmachine:vending_machine"); - } - - private static void sendHandler(String name, String itemStack) { - NBTTagCompound nbt = new NBTTagCompound(); - nbt.setString("handler", name); - nbt.setString("modName", VendingMachine.NAME); - nbt.setString("modId", VendingMachine.MODID); - nbt.setBoolean("modRequired", true); - nbt.setString("itemName", itemStack); - nbt.setInteger("handlerHeight", 105); - nbt.setInteger("handlerWidth", 166); - nbt.setInteger("maxRecipesPerPage", 3); - nbt.setInteger("yShift", 0); - FMLInterModComms.sendMessage("NotEnoughItems", "registerHandlerInfo", nbt); - } - - private static void sendCatalyst(String name, String itemStack, int priority) { - NBTTagCompound nbt = new NBTTagCompound(); - nbt.setString("handlerID", name); - nbt.setString("itemName", itemStack); - nbt.setInteger("priority", priority); - FMLInterModComms.sendMessage("NotEnoughItems", "registerCatalystInfo", nbt); - } - - private static void sendCatalyst(String name, String itemStack) { - sendCatalyst(name, itemStack, 0); - } -} diff --git a/src/main/java/com/cubefury/vendingmachine/integration/nei/NEIConfig.java b/src/main/java/com/cubefury/vendingmachine/integration/nei/NEIConfig.java index 0877a12..9a16209 100644 --- a/src/main/java/com/cubefury/vendingmachine/integration/nei/NEIConfig.java +++ b/src/main/java/com/cubefury/vendingmachine/integration/nei/NEIConfig.java @@ -1,22 +1,22 @@ package com.cubefury.vendingmachine.integration.nei; -import java.util.ArrayList; -import java.util.List; - import com.cubefury.vendingmachine.Tags; import com.cubefury.vendingmachine.VendingMachine; +import com.cubefury.vendingmachine.items.VMItems; import codechicken.nei.api.API; import codechicken.nei.api.IConfigureNEI; +import codechicken.nei.event.NEIRegisterHandlerInfosEvent; +import codechicken.nei.recipe.HandlerInfo; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; public class NEIConfig implements IConfigureNEI { - public static List instances = new ArrayList<>(); - @Override public void loadConfig() { - API.registerRecipeHandler(new NeiRecipeHandler()); - API.registerUsageHandler(new NeiRecipeHandler()); + NeiRecipeHandler handler = new NeiRecipeHandler(); + API.addRecipeCatalyst(VMItems.vendingMachine, "vendingmachine", 0); + handler.addHandler(); } @Override @@ -29,4 +29,11 @@ public String getVersion() { return Tags.VERSION; } + @SubscribeEvent + public void registerHandlerInfo(NEIRegisterHandlerInfosEvent event) { + event.registerHandlerInfo( + new HandlerInfo.Builder("vendingmachine", VendingMachine.NAME, VendingMachine.MODID).setMaxRecipesPerPage(3) + .setDisplayStack(VMItems.vendingMachine) + .build()); + } } diff --git a/src/main/java/com/cubefury/vendingmachine/integration/nei/NeiRecipeHandler.java b/src/main/java/com/cubefury/vendingmachine/integration/nei/NeiRecipeHandler.java index bb0e3d8..6b5477e 100644 --- a/src/main/java/com/cubefury/vendingmachine/integration/nei/NeiRecipeHandler.java +++ b/src/main/java/com/cubefury/vendingmachine/integration/nei/NeiRecipeHandler.java @@ -43,9 +43,13 @@ import codechicken.lib.gui.GuiDraw; import codechicken.nei.NEIServerUtils; import codechicken.nei.PositionedStack; +import codechicken.nei.api.API; +import codechicken.nei.recipe.GuiCraftingRecipe; import codechicken.nei.recipe.GuiRecipe; import codechicken.nei.recipe.TemplateRecipeHandler; +import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.Optional; +import cpw.mods.fml.common.event.FMLInterModComms; public class NeiRecipeHandler extends TemplateRecipeHandler { @@ -63,6 +67,19 @@ public class NeiRecipeHandler extends TemplateRecipeHandler { private Rectangle lastHoveredTextArea = null; private UUID lastHoveredQuestId = null; + public NeiRecipeHandler() {} + + public void addHandler() { + FMLInterModComms.sendRuntimeMessage( + FMLCommonHandler.instance() + .findContainerFor(VendingMachine.MODID), + "NEIPlugins", + "register-crafting-handler", + "vendingmachine@" + this.getRecipeName() + "@" + this.getOverlayIdentifier()); + GuiCraftingRecipe.craftinghandlers.add(this); + API.registerUsageHandler(this); + } + private UUID getCurrentPlayerUUID() { if (currentPlayerId == null) { currentPlayerId = NameCache.INSTANCE.getUUIDFromPlayer(Minecraft.getMinecraft().thePlayer); @@ -344,4 +361,8 @@ public List getOtherStacks() { } } + @Override + public void loadTransferRects() { + transferRects.add(new RecipeTransferRect(new Rectangle(75, 0, 16, 24), getOverlayIdentifier())); + } } diff --git a/src/main/java/com/cubefury/vendingmachine/items/VMItems.java b/src/main/java/com/cubefury/vendingmachine/items/VMItems.java new file mode 100644 index 0000000..5b478b5 --- /dev/null +++ b/src/main/java/com/cubefury/vendingmachine/items/VMItems.java @@ -0,0 +1,23 @@ +package com.cubefury.vendingmachine.items; + +import net.minecraft.item.ItemStack; + +import com.cubefury.vendingmachine.VendingMachine; +import com.cubefury.vendingmachine.blocks.MTEVendingMachine; + +import cpw.mods.fml.common.Optional; + +public class VMItems { + + public static ItemStack vendingMachine; + + public VMItems() {} + + @Optional.Method(modid = "gregtech") + public static void registerMultis() { + vendingMachine = new MTEVendingMachine( + VendingMachine.CONTROLLER_MTE_ID, + "multimachine.vendingmachine", + "Vending Machine").getStackForm(1); + } +} diff --git a/src/main/java/com/cubefury/vendingmachine/network/PacketTypeRegistry.java b/src/main/java/com/cubefury/vendingmachine/network/PacketTypeRegistry.java index 2d298f3..7237519 100644 --- a/src/main/java/com/cubefury/vendingmachine/network/PacketTypeRegistry.java +++ b/src/main/java/com/cubefury/vendingmachine/network/PacketTypeRegistry.java @@ -17,7 +17,7 @@ import com.cubefury.vendingmachine.network.handlers.NetNameSync; import com.cubefury.vendingmachine.network.handlers.NetSatisfiedQuestSync; import com.cubefury.vendingmachine.network.handlers.NetTradeDbSync; -import com.cubefury.vendingmachine.network.handlers.NetTradeOutputSync; +import com.cubefury.vendingmachine.network.handlers.NetTradeRequestSync; import com.cubefury.vendingmachine.network.handlers.NetTradeStateSync; public class PacketTypeRegistry implements IPacketRegistry { @@ -30,8 +30,8 @@ public class PacketTypeRegistry implements IPacketRegistry { public void init() { NetTradeDbSync.registerHandler(); NetTradeStateSync.registerHandler(); - NetTradeOutputSync.registerHandler(); NetAvailableTradeSync.registerHandler(); + NetTradeRequestSync.registerHandler(); NetSatisfiedQuestSync.registerHandler(); NetNameSync.registerHandler(); diff --git a/src/main/java/com/cubefury/vendingmachine/network/handlers/NetTradeOutputSync.java b/src/main/java/com/cubefury/vendingmachine/network/handlers/NetTradeOutputSync.java deleted file mode 100644 index 4a0e40d..0000000 --- a/src/main/java/com/cubefury/vendingmachine/network/handlers/NetTradeOutputSync.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.cubefury.vendingmachine.network.handlers; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; -import net.minecraft.util.ResourceLocation; -import net.minecraftforge.common.util.Constants; - -import com.cubefury.vendingmachine.VendingMachine; -import com.cubefury.vendingmachine.api.network.UnserializedPacket; -import com.cubefury.vendingmachine.api.util.Tuple2; -import com.cubefury.vendingmachine.network.PacketSender; -import com.cubefury.vendingmachine.network.PacketTypeRegistry; -import com.cubefury.vendingmachine.storage.NameCache; -import com.cubefury.vendingmachine.trade.TradeManager; -import com.cubefury.vendingmachine.util.BigItemStack; -import com.cubefury.vendingmachine.util.JsonHelper; -import com.cubefury.vendingmachine.util.NBTConverter; - -import cpw.mods.fml.relauncher.Side; -import cpw.mods.fml.relauncher.SideOnly; - -public class NetTradeOutputSync { - - private static final ResourceLocation ID_NAME = new ResourceLocation("vendingmachine:trade_output_sync"); - - public static void registerHandler() { - PacketTypeRegistry.INSTANCE.registerServerHandler(ID_NAME, NetTradeOutputSync::onServer); - - if (VendingMachine.proxy.isClient()) { - PacketTypeRegistry.INSTANCE.registerClientHandler(ID_NAME, NetTradeOutputSync::onClient); - } - } - - public static void sendReward(EntityPlayerMP player, NBTTagList pending) { - NBTTagCompound payload = new NBTTagCompound(); - UUID playerId = NameCache.INSTANCE.getUUIDFromPlayer(player); - NBTConverter.UuidValueType.PLAYER.writeId(playerId, payload); - payload.setTag("pending", pending); - - PacketSender.INSTANCE.sendToPlayers(new UnserializedPacket(ID_NAME, payload), player); - } - - public static void onServer(Tuple2 message) { - VendingMachine.LOG.warn("Impossible trade output sync request received on server..."); - } - - @SideOnly(Side.CLIENT) - public static void onClient(NBTTagCompound message) { - UUID playerId = NBTConverter.UuidValueType.PLAYER.readId(message); - NBTTagList pending = message.getTagList("pending", Constants.NBT.TAG_COMPOUND); - List pendingItems = new ArrayList<>(); - for (int i = 0; i < pending.tagCount(); i++) { - pendingItems.add(JsonHelper.JsonToItemStack(pending.getCompoundTagAt(i))); - } - TradeManager.INSTANCE.addPending(playerId, pendingItems); - - // we let the auto refresh on the Vending Machine - // container handle the dispensing - } -} diff --git a/src/main/java/com/cubefury/vendingmachine/network/handlers/NetTradeRequestSync.java b/src/main/java/com/cubefury/vendingmachine/network/handlers/NetTradeRequestSync.java new file mode 100644 index 0000000..1fa532d --- /dev/null +++ b/src/main/java/com/cubefury/vendingmachine/network/handlers/NetTradeRequestSync.java @@ -0,0 +1,83 @@ +package com.cubefury.vendingmachine.network.handlers; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; +import net.minecraftforge.common.DimensionManager; + +import com.cubefury.vendingmachine.VendingMachine; +import com.cubefury.vendingmachine.api.network.UnserializedPacket; +import com.cubefury.vendingmachine.api.util.Tuple2; +import com.cubefury.vendingmachine.blocks.MTEVendingMachine; +import com.cubefury.vendingmachine.blocks.gui.MTEVendingMachineGui; +import com.cubefury.vendingmachine.blocks.gui.TradeItemDisplay; +import com.cubefury.vendingmachine.network.PacketSender; +import com.cubefury.vendingmachine.network.PacketTypeRegistry; +import com.cubefury.vendingmachine.storage.NameCache; +import com.cubefury.vendingmachine.trade.TradeRequest; +import com.cubefury.vendingmachine.util.NBTConverter; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; + +public class NetTradeRequestSync { + + private static final ResourceLocation ID_NAME = new ResourceLocation("vendingmachine:traderequest_sync"); + + public static void registerHandler() { + PacketTypeRegistry.INSTANCE.registerServerHandler(ID_NAME, NetTradeRequestSync::onServer); + + if (VendingMachine.proxy.isClient()) { + PacketTypeRegistry.INSTANCE.registerClientHandler(ID_NAME, NetTradeRequestSync::onClient); + } + } + + public static void sendTradeRequest(TradeItemDisplay trade, World world, int x, int y, int z) { + NBTTagCompound payload = new NBTTagCompound(); + NBTConverter.UuidValueType.TRADEGROUP.writeId(trade.tgID, payload); + payload.setInteger("tradeGroupOrder", trade.tradeGroupOrder); + payload.setInteger("dim", world.provider.dimensionId); + payload.setInteger("x", x); + payload.setInteger("y", y); + payload.setInteger("z", z); + PacketSender.INSTANCE.sendToServer(new UnserializedPacket(ID_NAME, payload)); + } + + public static void sendAck(EntityPlayerMP player) { + PacketSender.INSTANCE.sendToPlayers(new UnserializedPacket(ID_NAME, new NBTTagCompound()), player); + } + + public static void onServer(Tuple2 message) { + World world = DimensionManager.getWorld( + message.first() + .getInteger("dim")); + TileEntity te = world.getTileEntity( + message.first() + .getInteger("x"), + message.first() + .getInteger("y"), + message.first() + .getInteger("z")); + if ( + te instanceof IGregTechTileEntity + && ((IGregTechTileEntity) te).getMetaTileEntity() instanceof MTEVendingMachine + ) { + ((MTEVendingMachine) ((IGregTechTileEntity) te).getMetaTileEntity()).addTradeRequest( + new TradeRequest( + message.second(), + NameCache.INSTANCE.getUUIDFromPlayer(message.second()), + NBTConverter.UuidValueType.TRADEGROUP.readId(message.first()), + message.first() + .getInteger("tradeGroupOrder"), + (MTEVendingMachine) ((IGregTechTileEntity) te).getMetaTileEntity())); + } + } + + @SideOnly(Side.CLIENT) + public static void onClient(NBTTagCompound message) { + MTEVendingMachineGui.setForceRefresh(); + } +} diff --git a/src/main/java/com/cubefury/vendingmachine/network/handlers/NetTradeStateSync.java b/src/main/java/com/cubefury/vendingmachine/network/handlers/NetTradeStateSync.java index 9bf9c06..7c3d619 100644 --- a/src/main/java/com/cubefury/vendingmachine/network/handlers/NetTradeStateSync.java +++ b/src/main/java/com/cubefury/vendingmachine/network/handlers/NetTradeStateSync.java @@ -7,9 +7,7 @@ import net.minecraft.client.Minecraft; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.common.util.Constants; import com.cubefury.vendingmachine.VendingMachine; import com.cubefury.vendingmachine.api.network.UnserializedPacket; @@ -17,11 +15,7 @@ import com.cubefury.vendingmachine.network.PacketSender; import com.cubefury.vendingmachine.network.PacketTypeRegistry; import com.cubefury.vendingmachine.storage.NameCache; -import com.cubefury.vendingmachine.trade.Trade; import com.cubefury.vendingmachine.trade.TradeDatabase; -import com.cubefury.vendingmachine.trade.TradeGroup; -import com.cubefury.vendingmachine.util.BigItemStack; -import com.cubefury.vendingmachine.util.JsonHelper; import com.cubefury.vendingmachine.util.NBTConverter; import cpw.mods.fml.relauncher.Side; @@ -58,51 +52,21 @@ public static void sendTradeState(@Nullable EntityPlayerMP player, boolean merge } @SideOnly(Side.CLIENT) - public static void getTrades() { + public static void requestSync() { NBTTagCompound payload = new NBTTagCompound(); payload.setString("requestType", "getTrades"); PacketSender.INSTANCE.sendToServer(new UnserializedPacket(ID_NAME, payload)); } - @SideOnly(Side.CLIENT) - public static void claimTrade(UUID tradeGroup, Trade trade) { - NBTTagCompound payload = new NBTTagCompound(); - payload.setString("requestType", "claimTrade"); - NBTConverter.UuidValueType.TRADEGROUP.writeId(tradeGroup, payload); - - NBTTagList pendingOutput = new NBTTagList(); - for (BigItemStack stack : trade.toItems) { - pendingOutput.appendTag(JsonHelper.ItemStackToJson(stack, new NBTTagCompound())); - } - payload.setTag("pendingOutput", pendingOutput); - - PacketSender.INSTANCE.sendToServer(new UnserializedPacket(ID_NAME, payload)); - } - public static void onServer(Tuple2 message) { TradeDatabase db = TradeDatabase.INSTANCE; String requestType = message.first() .getString("requestType"); - switch (requestType) { - case "claimTrade": - UUID playerId = NameCache.INSTANCE.getUUIDFromPlayer(message.second()); - NBTTagList pendingOutput = message.first() - .getTagList("pendingOutput", Constants.NBT.TAG_COMPOUND); - TradeGroup tg = db.getTradeGroupFromId(NBTConverter.UuidValueType.TRADEGROUP.readId(message.first())); - if (tg.attemptExecuteTrade(playerId)) { - NetTradeOutputSync.sendReward(message.second(), pendingOutput); - sendTradeState(message.second(), false); - } else { - VendingMachine.LOG - .warn("Player {} made invalid reward claim attempt for trade group {}", playerId, tg.getId()); - } - break; // technically we can let this continue cuz we sendTradeState in both cases... - case "getTrades": - sendTradeState(message.second(), false); - break; - default: - VendingMachine.LOG.warn("Unknown trade state sync request type received: {}", requestType); + if (requestType.equals("getTrades")) { + sendTradeState(message.second(), false); + } else { + VendingMachine.LOG.warn("Unknown trade state sync request type received: {}", requestType); } } diff --git a/src/main/java/com/cubefury/vendingmachine/trade/TradeCategory.java b/src/main/java/com/cubefury/vendingmachine/trade/TradeCategory.java new file mode 100644 index 0000000..da740a3 --- /dev/null +++ b/src/main/java/com/cubefury/vendingmachine/trade/TradeCategory.java @@ -0,0 +1,73 @@ +package com.cubefury.vendingmachine.trade; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import com.cleanroommc.modularui.drawable.UITexture; +import com.cubefury.vendingmachine.VendingMachine; + +public enum TradeCategory { + + UNKNOWN("unknown", "vendingmachine.category.unknown", "gui/icons/unknown.png"), + ALL("all", "vendingmachine.category.all", "gui/icons/all.png"), + COMPONENTS("components", "vendingmachine.category.components", "gui/icons/components.png"), + RAW("raw", "vendingmachine.category.raw", "gui/icons/raw.png"), + FARMING("farming", "vendingmachine.category.farming", "gui/icons/farming.png"), + CHEMISTRY("chemistry", "vendingmachine.category.chemistry", "gui/icons/chemistry.png"), + MAGIC("magic", "vendingmachine.category.magic", "gui/icons/magic.png"), + BEES("bees", "vendingmachine.category.bees", "gui/icons/bees.png"), + MISC("misc", "vendingmachine.category.misc", "gui/icons/misc.png"); + + private final String key; + private final String unlocalized_name; + private final UITexture texture; + + private static final Map ENUM_MAP; + + private static final int size_x = 32; + private static final int size_y = 32; + + TradeCategory(String key, String unlocalized_name, String texture) { + this.key = key; + this.unlocalized_name = unlocalized_name; + this.texture = UITexture.builder() + .location(VendingMachine.MODID, texture) + .imageSize(size_x, size_y) + .name(unlocalized_name) + .build(); + } + + static { + Map map = new HashMap<>(); + for (TradeCategory instance : TradeCategory.values()) { + map.put(instance.getKey(), instance); + } + ENUM_MAP = Collections.unmodifiableMap(map); + } + + public String getKey() { + return this.key; + } + + public String getUnlocalized_name() { + return this.unlocalized_name; + } + + public UITexture getTexture() { + return this.texture; + } + + public static TradeCategory ofString(String key) { + if (ENUM_MAP.get(key) == null) { + VendingMachine.LOG.warn("Unknown trade category {}, defaulting to UNKNOWN", key); + return UNKNOWN; + } + return ENUM_MAP.get(key); + } + + @Override + public String toString() { + return getUnlocalized_name(); + } +} diff --git a/src/main/java/com/cubefury/vendingmachine/trade/TradeDatabase.java b/src/main/java/com/cubefury/vendingmachine/trade/TradeDatabase.java index 7251fa6..71007ae 100644 --- a/src/main/java/com/cubefury/vendingmachine/trade/TradeDatabase.java +++ b/src/main/java/com/cubefury/vendingmachine/trade/TradeDatabase.java @@ -1,7 +1,12 @@ package com.cubefury.vendingmachine.trade; +import java.util.ArrayList; +import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; import net.minecraft.nbt.NBTTagCompound; @@ -22,11 +27,13 @@ public class TradeDatabase { public static final TradeDatabase INSTANCE = new TradeDatabase(); public int version = -1; private final Map tradeGroups = new HashMap<>(); + private final Map> tradeCategories = new HashMap<>(); private TradeDatabase() {} public void clear() { tradeGroups.clear(); + tradeCategories.clear(); } public void clearTradeState(UUID player) { @@ -45,6 +52,16 @@ public Map getTradeGroups() { return tradeGroups; } + public List getTradeCategories() { + List tradeCategoryList = new ArrayList<>(tradeCategories.keySet()); + tradeCategoryList.sort(Comparator.comparing(TradeCategory::ordinal)); + return tradeCategoryList; + } + + public Set getTradeGroupsFromCategory(TradeCategory category) { + return tradeCategories.get(category); + } + public int getTradeCount() { return tradeGroups.values() .stream() @@ -61,27 +78,33 @@ public void readFromNBT(NBTTagCompound nbt, boolean merge) { BqAdapter.INSTANCE.resetQuestTriggers(null); } } - int newIdCount = 0; + int newMetadataCount = 0; this.version = nbt.getInteger("version"); NBTTagList trades = nbt.getTagList("tradeGroups", Constants.NBT.TAG_COMPOUND); for (int i = 0; i < trades.tagCount(); i++) { TradeGroup tg = new TradeGroup(); - newIdCount += tg.readFromNBT(trades.getCompoundTagAt(i)) ? 1 : 0; + newMetadataCount += tg.readFromNBT(trades.getCompoundTagAt(i)) ? 1 : 0; if (tradeGroups.containsKey(tg.getId())) { VendingMachine.LOG.warn("Multiple trade groups with id {} exist in the file!", tg); continue; } + tradeCategories.computeIfAbsent(tg.getCategory(), k -> new HashSet<>()); + tradeCategories.get(tg.getCategory()) + .add(tg.getId()); + tradeGroups.put(tg.getId(), tg); } - if (newIdCount > 0) { - VendingMachine.LOG.info("Updating {} new trades with UUIDs", newIdCount); + if (newMetadataCount > 0) { + VendingMachine.LOG.info("Appended metadata to {} new trades", newMetadataCount); DirtyDbMarker.markDirty(); } - if (VendingMachine.proxy.isClient() && VendingMachine.isNeiLoaded) { + if (VendingMachine.proxy.isClient()) { refreshNeiCache(); } + TradeManager.INSTANCE.recomputeAvailableTrades(null); - VendingMachine.LOG.info("Loaded {} trade groups containing {} trades.", getTradeGroupCount(), getTradeCount()); + VendingMachine.LOG + .info("Loaded {} trade groups containing {} trade groups.", getTradeGroupCount(), getTradeCount()); } public NBTTagCompound writeToNBT(NBTTagCompound nbt) { @@ -129,6 +152,7 @@ public NBTTagCompound writeTradeStateToNBT(NBTTagCompound nbt, UUID player) { } @SideOnly(Side.CLIENT) + @Optional.Method(modid = "NotEnoughItems") public void refreshNeiCache() { NeiRecipeCache.refreshCache(); } diff --git a/src/main/java/com/cubefury/vendingmachine/trade/TradeGroup.java b/src/main/java/com/cubefury/vendingmachine/trade/TradeGroup.java index 030fd7a..5702483 100644 --- a/src/main/java/com/cubefury/vendingmachine/trade/TradeGroup.java +++ b/src/main/java/com/cubefury/vendingmachine/trade/TradeGroup.java @@ -1,6 +1,7 @@ package com.cubefury.vendingmachine.trade; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -14,6 +15,7 @@ import com.cubefury.vendingmachine.VendingMachine; import com.cubefury.vendingmachine.api.trade.ICondition; +import com.cubefury.vendingmachine.handlers.SaveLoadHandler; import com.cubefury.vendingmachine.integration.betterquesting.BqAdapter; import com.cubefury.vendingmachine.integration.betterquesting.BqCondition; import com.cubefury.vendingmachine.util.NBTConverter; @@ -26,6 +28,9 @@ public class TradeGroup { private final List trades = new ArrayList<>(); public int cooldown = -1; public int maxTrades = -1; + public String label = ""; + private TradeCategory category = TradeCategory.UNKNOWN; + private String original_category_str = ""; private final Set requirementSet = new HashSet<>(); // List of completed conditions for each player @@ -80,6 +85,10 @@ public List getTrades() { return trades; } + public TradeCategory getCategory() { + return category; + } + public List getRequirements() { return new ArrayList<>(requirementSet); } @@ -123,38 +132,52 @@ public TradeHistory getTradeState(UUID player) { public void setTradeState(UUID player, TradeHistory history) { synchronized (tradeState) { tradeState.put(player, history); + } } - public boolean attemptExecuteTrade(UUID player) { + public boolean canExecuteTrade(UUID player) { List availableTrades = TradeManager.INSTANCE.getTrades(player); for (TradeGroupWrapper trade : availableTrades) { if (trade == null) { // shouldn't happen continue; } - if (trade.trade().id == this.id && trade.enabled() && cooldown < 0) { - TradeHistory newTradeHistory = getTradeState(player); - newTradeHistory.executeTrade(); - setTradeState(player, newTradeHistory); + if (trade.trade().id.equals(this.id) && trade.enabled() && trade.cooldown() < 0) { return true; } } return false; } + public void executeTrade(UUID player) { + TradeHistory newTradeHistory = getTradeState(player); + newTradeHistory.executeTrade(); + setTradeState(player, newTradeHistory); + SaveLoadHandler.INSTANCE.writeTradeState(Collections.singleton(player)); + } + public boolean readFromNBT(NBTTagCompound nbt) { this.trades.clear(); this.requirementSet.clear(); - boolean generatedRandomUUID = false; + boolean generatedMetadata = false; if (nbt.hasKey("id")) { this.id = NBTConverter.UuidValueType.TRADEGROUP.readId(nbt.getCompoundTag("id")); } else { this.id = UUID.randomUUID(); - generatedRandomUUID = true; + generatedMetadata = true; + } + if (nbt.hasKey("category")) { + this.original_category_str = nbt.getString("category"); + this.category = TradeCategory.ofString(original_category_str); + } else { + this.original_category_str = TradeCategory.UNKNOWN.getUnlocalized_name(); + this.category = TradeCategory.UNKNOWN; + generatedMetadata = true; } this.cooldown = nbt.getInteger("cooldown"); this.maxTrades = nbt.getInteger("maxTrades"); + this.label = nbt.getString("label"); NBTTagList tradeList = nbt.getTagList("trades", Constants.NBT.TAG_COMPOUND); for (int i = 0; i < tradeList.tagCount(); i++) { NBTTagCompound trade = tradeList.getCompoundTagAt(i); @@ -171,13 +194,15 @@ public boolean readFromNBT(NBTTagCompound nbt) { BqAdapter.INSTANCE.addQuestTrigger(bqc.getQuestId(), this); } } - return generatedRandomUUID; + return generatedMetadata; } public NBTTagCompound writeToNBT(NBTTagCompound nbt) { nbt.setTag("id", NBTConverter.UuidValueType.TRADEGROUP.writeId(this.id)); nbt.setInteger("cooldown", this.cooldown); nbt.setInteger("maxTrades", this.maxTrades); + nbt.setString("label", this.label); + nbt.setString("category", this.category.getKey()); NBTTagList tList = new NBTTagList(); for (Trade t : trades) { tList.appendTag(t.writeToNBT(new NBTTagCompound())); @@ -191,6 +216,10 @@ public NBTTagCompound writeToNBT(NBTTagCompound nbt) { return nbt; } + public String getLabel() { + return this.label; + } + @Optional.Method(modid = "betterquesting") public void removeAllSatisfiedBqConditions(UUID player) { synchronized (tradeState) { diff --git a/src/main/java/com/cubefury/vendingmachine/trade/TradeManager.java b/src/main/java/com/cubefury/vendingmachine/trade/TradeManager.java index aab753a..77137a3 100644 --- a/src/main/java/com/cubefury/vendingmachine/trade/TradeManager.java +++ b/src/main/java/com/cubefury/vendingmachine/trade/TradeManager.java @@ -111,7 +111,7 @@ public List getTrades(UUID player) { if ( tg.cooldown != -1 && lastTradeTime != -1 && (currentTimestamp - lastTradeTime) / 1000 < tg.cooldown ) { - cooldownRemaining = (currentTimestamp - lastTradeTime) / 1000; + cooldownRemaining = tg.cooldown - (currentTimestamp - lastTradeTime) / 1000; } else { cooldownRemaining = -1; } diff --git a/src/main/java/com/cubefury/vendingmachine/trade/TradeRequest.java b/src/main/java/com/cubefury/vendingmachine/trade/TradeRequest.java new file mode 100644 index 0000000..4c64d0b --- /dev/null +++ b/src/main/java/com/cubefury/vendingmachine/trade/TradeRequest.java @@ -0,0 +1,25 @@ +package com.cubefury.vendingmachine.trade; + +import java.util.UUID; + +import net.minecraft.entity.player.EntityPlayerMP; + +import com.cubefury.vendingmachine.blocks.MTEVendingMachine; + +public class TradeRequest { + + public EntityPlayerMP player; + public UUID playerID; + public UUID tradeGroup; + public int tradeGroupOrder; + MTEVendingMachine target; + + public TradeRequest(EntityPlayerMP player, UUID playerID, UUID tradeGroup, int tradeGroupOrder, + MTEVendingMachine target) { + this.player = player; + this.playerID = playerID; + this.tradeGroup = tradeGroup; + this.tradeGroupOrder = tradeGroupOrder; + this.target = target; + } +} diff --git a/src/main/java/com/cubefury/vendingmachine/util/BigItemStack.java b/src/main/java/com/cubefury/vendingmachine/util/BigItemStack.java index 3b07bdd..adeef95 100644 --- a/src/main/java/com/cubefury/vendingmachine/util/BigItemStack.java +++ b/src/main/java/com/cubefury/vendingmachine/util/BigItemStack.java @@ -142,16 +142,27 @@ public BigItemStack copy() { @Override public boolean equals(Object stack) { - if (stack == baseStack) return true; + if (stack == baseStack) return true; // idk why this was in BQ but ok if (stack instanceof ItemStack) { return baseStack.isItemEqual((ItemStack) stack) && ItemStack.areItemStackTagsEqual(baseStack, (ItemStack) stack); } + if (stack instanceof BigItemStack) { + BigItemStack castStack = (BigItemStack) stack; + return baseStack.isItemEqual(castStack.baseStack) && baseStack.stackSize == castStack.stackSize + && ItemStack.areItemStackTagsEqual(baseStack, castStack.baseStack); + } + return super.equals(stack); } + @Override + public int hashCode() { + return writeToNBT(new NBTTagCompound()).hashCode(); + } + @Nullable public static BigItemStack loadItemStackFromNBT(@Nonnull NBTTagCompound nbt) // Can load normal ItemStack NBTs. Does // NOT deal with placeholders diff --git a/src/main/resources/assets/vendingmachine/lang/en_US.lang b/src/main/resources/assets/vendingmachine/lang/en_US.lang index b900271..be90e15 100644 --- a/src/main/resources/assets/vendingmachine/lang/en_US.lang +++ b/src/main/resources/assets/vendingmachine/lang/en_US.lang @@ -1,5 +1,3 @@ -tile.vendingmachine.vending_machine.name=Vending Machine - item.vendingmachine.placeholder.name=Placeholder Item tooltip.vendingmachine=Who's even restocking this... @@ -11,3 +9,23 @@ vendingmachine.gui.requirement.betterquesting.missing=Missing Quest vendingmachine.gui.neiColor.conditionDefault=000000 vendingmachine.gui.neiColor.conditionSatisfied=55D441 vendingmachine.gui.neiColor.conditionUnsatisfied=A87A5E +vendingmachine.gui.item_eject=Eject Items +vendingmachine.gui.search=Search +vendingmachine.gui.required_inputs=Requires: + +vendingmachine.gui.cooldown_display.second=s +vendingmachine.gui.cooldown_display.minute=m +vendingmachine.gui.cooldown_display.hour=h +vendingmachine.gui.cooldown_display.day=d + +gt.blockmachines.multimachine.vendingmachine.name=Vending Machine + +vendingmachine.category.unknown=Unknown Category +vendingmachine.category.all=All Items +vendingmachine.category.components=Components +vendingmachine.category.raw=Raw Materials +vendingmachine.category.farming=Farming +vendingmachine.category.chemistry=Chemistry +vendingmachine.category.magic=Magic +vendingmachine.category.bees=Bees +vendingmachine.category.misc=Miscellaneous diff --git a/src/main/resources/assets/vendingmachine/textures/gui/background/input.png b/src/main/resources/assets/vendingmachine/textures/gui/background/input.png new file mode 100644 index 0000000..67cd44c Binary files /dev/null and b/src/main/resources/assets/vendingmachine/textures/gui/background/input.png differ diff --git a/src/main/resources/assets/vendingmachine/textures/gui/background/output.png b/src/main/resources/assets/vendingmachine/textures/gui/background/output.png new file mode 100644 index 0000000..8aa56a6 Binary files /dev/null and b/src/main/resources/assets/vendingmachine/textures/gui/background/output.png differ diff --git a/src/main/resources/assets/vendingmachine/textures/gui/background/panel_side.png b/src/main/resources/assets/vendingmachine/textures/gui/background/panel_side.png new file mode 100644 index 0000000..030802c Binary files /dev/null and b/src/main/resources/assets/vendingmachine/textures/gui/background/panel_side.png differ diff --git a/src/main/resources/assets/vendingmachine/textures/gui/background/text_field_light_gray.png b/src/main/resources/assets/vendingmachine/textures/gui/background/text_field_light_gray.png new file mode 100644 index 0000000..a663835 Binary files /dev/null and b/src/main/resources/assets/vendingmachine/textures/gui/background/text_field_light_gray.png differ diff --git a/src/main/resources/assets/vendingmachine/textures/gui/background/trade_available.png b/src/main/resources/assets/vendingmachine/textures/gui/background/trade_available.png new file mode 100644 index 0000000..ef1ac0c Binary files /dev/null and b/src/main/resources/assets/vendingmachine/textures/gui/background/trade_available.png differ diff --git a/src/main/resources/assets/vendingmachine/textures/gui/background/trade_button_pressed.png b/src/main/resources/assets/vendingmachine/textures/gui/background/trade_button_pressed.png new file mode 100644 index 0000000..2b58e44 Binary files /dev/null and b/src/main/resources/assets/vendingmachine/textures/gui/background/trade_button_pressed.png differ diff --git a/src/main/resources/assets/vendingmachine/textures/gui/background/trade_button_pressed_color_corrected.png b/src/main/resources/assets/vendingmachine/textures/gui/background/trade_button_pressed_color_corrected.png new file mode 100644 index 0000000..1c15f01 Binary files /dev/null and b/src/main/resources/assets/vendingmachine/textures/gui/background/trade_button_pressed_color_corrected.png differ diff --git a/src/main/resources/assets/vendingmachine/textures/gui/background/trade_button_unpressed.png b/src/main/resources/assets/vendingmachine/textures/gui/background/trade_button_unpressed.png new file mode 100644 index 0000000..f94c958 Binary files /dev/null and b/src/main/resources/assets/vendingmachine/textures/gui/background/trade_button_unpressed.png differ diff --git a/src/main/resources/assets/vendingmachine/textures/gui/background/trade_button_unpressed_color_corrected.png b/src/main/resources/assets/vendingmachine/textures/gui/background/trade_button_unpressed_color_corrected.png new file mode 100644 index 0000000..13c9e01 Binary files /dev/null and b/src/main/resources/assets/vendingmachine/textures/gui/background/trade_button_unpressed_color_corrected.png differ diff --git a/src/main/resources/assets/vendingmachine/textures/gui/icons/all.png b/src/main/resources/assets/vendingmachine/textures/gui/icons/all.png new file mode 100644 index 0000000..20ae98f Binary files /dev/null and b/src/main/resources/assets/vendingmachine/textures/gui/icons/all.png differ diff --git a/src/main/resources/assets/vendingmachine/textures/gui/icons/bees.png b/src/main/resources/assets/vendingmachine/textures/gui/icons/bees.png new file mode 100644 index 0000000..09fad74 Binary files /dev/null and b/src/main/resources/assets/vendingmachine/textures/gui/icons/bees.png differ diff --git a/src/main/resources/assets/vendingmachine/textures/gui/icons/chemistry.png b/src/main/resources/assets/vendingmachine/textures/gui/icons/chemistry.png new file mode 100644 index 0000000..a3c751d Binary files /dev/null and b/src/main/resources/assets/vendingmachine/textures/gui/icons/chemistry.png differ diff --git a/src/main/resources/assets/vendingmachine/textures/gui/icons/components.png b/src/main/resources/assets/vendingmachine/textures/gui/icons/components.png new file mode 100644 index 0000000..23359d7 Binary files /dev/null and b/src/main/resources/assets/vendingmachine/textures/gui/icons/components.png differ diff --git a/src/main/resources/assets/vendingmachine/textures/gui/icons/farming.png b/src/main/resources/assets/vendingmachine/textures/gui/icons/farming.png new file mode 100644 index 0000000..e6a2850 Binary files /dev/null and b/src/main/resources/assets/vendingmachine/textures/gui/icons/farming.png differ diff --git a/src/main/resources/assets/vendingmachine/textures/gui/icons/magic.png b/src/main/resources/assets/vendingmachine/textures/gui/icons/magic.png new file mode 100644 index 0000000..a91a627 Binary files /dev/null and b/src/main/resources/assets/vendingmachine/textures/gui/icons/magic.png differ diff --git a/src/main/resources/assets/vendingmachine/textures/gui/icons/misc.png b/src/main/resources/assets/vendingmachine/textures/gui/icons/misc.png new file mode 100644 index 0000000..1413982 Binary files /dev/null and b/src/main/resources/assets/vendingmachine/textures/gui/icons/misc.png differ diff --git a/src/main/resources/assets/vendingmachine/textures/gui/icons/raw.png b/src/main/resources/assets/vendingmachine/textures/gui/icons/raw.png new file mode 100644 index 0000000..5c7fe4c Binary files /dev/null and b/src/main/resources/assets/vendingmachine/textures/gui/icons/raw.png differ diff --git a/src/main/resources/assets/vendingmachine/textures/gui/icons/unknown.png b/src/main/resources/assets/vendingmachine/textures/gui/icons/unknown.png new file mode 100644 index 0000000..25bddf9 Binary files /dev/null and b/src/main/resources/assets/vendingmachine/textures/gui/icons/unknown.png differ diff --git a/src/main/resources/assets/vendingmachine/textures/gui/nei_header.png b/src/main/resources/assets/vendingmachine/textures/gui/nei_header.png new file mode 100644 index 0000000..963b277 Binary files /dev/null and b/src/main/resources/assets/vendingmachine/textures/gui/nei_header.png differ diff --git a/src/main/resources/assets/vendingmachine/textures/gui/tabs_left.png b/src/main/resources/assets/vendingmachine/textures/gui/tabs_left.png new file mode 100644 index 0000000..0c532c6 Binary files /dev/null and b/src/main/resources/assets/vendingmachine/textures/gui/tabs_left.png differ diff --git a/src/main/resources/assets/vendingmachine/textures/items/placeholder.png b/src/main/resources/assets/vendingmachine/textures/items/placeholder.png index b8c76f5..06caf89 100644 Binary files a/src/main/resources/assets/vendingmachine/textures/items/placeholder.png and b/src/main/resources/assets/vendingmachine/textures/items/placeholder.png differ diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info index 6275554..c02ed51 100644 --- a/src/main/resources/mcmod.info +++ b/src/main/resources/mcmod.info @@ -3,12 +3,12 @@ "modList": [{ "modid": "${modId}", "name": "${modName}", - "description": "An example mod for Minecraft 1.7.10 with Forge focused on a stable setup.", + "description": "Adds a vending machine.", "version": "${modVersion}", "mcversion": "${minecraftVersion}", - "url": "https://github.com/SinTh0r4s/MyMod", + "url": "https://github.com/cubefury/VendingMachine", "updateUrl": "", - "authorList": ["SinTho0r4s"], + "authorList": ["cubefury"], "credits": "", "logoFile": "", "screenshots": [],