diff options
Diffstat (limited to 'src/main/java/gregtech/api')
8 files changed, 531 insertions, 21 deletions
diff --git a/src/main/java/gregtech/api/enums/Dyes.java b/src/main/java/gregtech/api/enums/Dyes.java index 1dedcb6af0..72113b554a 100644 --- a/src/main/java/gregtech/api/enums/Dyes.java +++ b/src/main/java/gregtech/api/enums/Dyes.java @@ -6,6 +6,8 @@ import net.minecraft.util.EnumChatFormatting; import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidStack; +import org.jetbrains.annotations.Contract; + import gregtech.api.interfaces.IColorModulationContainer; import gregtech.api.objects.GTArrayList; import gregtech.api.util.GTUtility; @@ -123,4 +125,19 @@ public enum Dyes implements IColorModulationContainer { public static Dyes getDyeFromIndex(short index) { return index != -1 ? Dyes.get(index) : Dyes.MACHINE_METAL; } + + /** + * Transforms a dye index between the GT index for this color and the vanilla index for this color. + * + * @param color an integer between 0 and 15 + * @return the transformed color + */ + @Contract(pure = true) + public static int transformDyeIndex(final int color) { + if (color < 0 || color > 15) { + throw new IllegalArgumentException("Color passed to transformColor must be between 0 and 15"); + } + + return (~(byte) color) & 0xF; + } } diff --git a/src/main/java/gregtech/api/enums/SoundResource.java b/src/main/java/gregtech/api/enums/SoundResource.java index f6ca0485dc..eea2cf477e 100644 --- a/src/main/java/gregtech/api/enums/SoundResource.java +++ b/src/main/java/gregtech/api/enums/SoundResource.java @@ -74,7 +74,9 @@ public enum SoundResource { GT_MACHINES_MULTI_LATHE_LOOP(241, GregTech.ID, "machines.MultiLatheLoop"), GT_MACHINES_MULTI_AUTOCLAVE_LOOP(242, GregTech.ID, "machines.MultiAutoclaveLoop"), - GT_SPRAYCAN_SHAKE(243, GregTech.ID, "items.spraycan"), + GT_SPRAYCAN_SHAKE(243, GregTech.ID, "items.spraycan_shake"), + GT_SPRAYCAN_LOCK(244, GregTech.ID, "items.spraycan_lock"), + GT_SPRAYCAN_UNLOCK(245, GregTech.ID, "items.spraycan_unlock"), GUI_BUTTON_DOWN(-1, GregTech.ID, "gui.buttonDown"), GUI_BUTTON_UP(-1, GregTech.ID, "gui.buttonUp"), diff --git a/src/main/java/gregtech/api/interfaces/IItemBehaviour.java b/src/main/java/gregtech/api/interfaces/IItemBehaviour.java index 8c5dad6dc2..1f728025a7 100644 --- a/src/main/java/gregtech/api/interfaces/IItemBehaviour.java +++ b/src/main/java/gregtech/api/interfaces/IItemBehaviour.java @@ -9,9 +9,12 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.projectile.EntityArrow; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; +import com.google.common.collect.ImmutableList; + import gregtech.api.enums.SubTag; import gregtech.api.items.MetaBaseItem; @@ -21,6 +24,33 @@ public interface IItemBehaviour<E extends Item> { return false; } + default boolean onMiddleClick(E aItem, ItemStack aStack, EntityPlayer aPlayer) { + return false; + } + + /** + * Suppresses standard block activation for a {@link gregtech.common.blocks.BlockMachines GT machine block}. Put + * your item's right click activation in + * {@link #onItemUse(Item, ItemStack, EntityPlayer, World, int, int, int, int, float, float, float) onItemUse} + * for best results. + * <p> + * Typically used when the item needs support for the Ring of Loki (from Botania.) If you don't care about that, + * using + * {@link #onItemUseFirst(Item, ItemStack, EntityPlayer, World, int, int, int, ForgeDirection, float, float, float) + * onItemUseFirst} + * instead of {@link #onItemUse(Item, ItemStack, EntityPlayer, World, int, int, int, int, float, float, float) + * onItemUse} + * will act before block activation with a little less overhead. + * + * @param player the player making the request + * @param tileEntity the tile entity that is attempting to be activated + * @param side the side of the tile entity that the player clicked on + * @return true if standard block activation should be suppressed + */ + default boolean shouldInterruptBlockActivation(EntityPlayer player, TileEntity tileEntity, ForgeDirection side) { + return false; + } + boolean onLeftClickEntity(E aItem, ItemStack aStack, EntityPlayer aPlayer, Entity aEntity); boolean onItemUse(E aItem, ItemStack aStack, EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ, @@ -33,6 +63,10 @@ public interface IItemBehaviour<E extends Item> { List<String> getAdditionalToolTips(E aItem, List<String> aList, ItemStack aStack); + default List<String> getAdditionalToolTipsWhileSneaking(E aItem, List<String> aList, ItemStack aStack) { + return ImmutableList.of(); + } + void onUpdate(E aItem, ItemStack aStack, World aWorld, Entity aPlayer, int aTimer, boolean aIsInHand); boolean isItemStackUsable(E aItem, ItemStack aStack); diff --git a/src/main/java/gregtech/api/items/GTGenericItem.java b/src/main/java/gregtech/api/items/GTGenericItem.java index 4f0994bb12..5febfcc173 100644 --- a/src/main/java/gregtech/api/items/GTGenericItem.java +++ b/src/main/java/gregtech/api/items/GTGenericItem.java @@ -151,7 +151,11 @@ public class GTGenericItem extends Item implements IProjectileItem { @Override public ItemStack dispenseStack(IBlockSource aSource, ItemStack aStack) { - return ((GTGenericItem) aStack.getItem()).onDispense(aSource, aStack); + final GTGenericItem item = (GTGenericItem) aStack.getItem(); + if (item != null) { + return item.onDispense(aSource, aStack); + } + return aStack; } @Override diff --git a/src/main/java/gregtech/api/items/MetaBaseItem.java b/src/main/java/gregtech/api/items/MetaBaseItem.java index 32e4bba392..fa47043fcc 100644 --- a/src/main/java/gregtech/api/items/MetaBaseItem.java +++ b/src/main/java/gregtech/api/items/MetaBaseItem.java @@ -24,6 +24,8 @@ import net.minecraftforge.common.util.ForgeDirection; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.IFluidContainerItem; +import com.gtnewhorizons.modularui.api.KeyboardUtil; + import gregtech.api.enums.SubTag; import gregtech.api.interfaces.IItemBehaviour; import gregtech.api.util.GTLanguageManager; @@ -123,6 +125,10 @@ public abstract class MetaBaseItem extends GTGenericItem return forEachBehavior(aStack, behavior -> behavior.onLeftClick(this, aStack, aPlayer)); } + public boolean onMiddleClick(ItemStack aStack, EntityPlayer aPlayer) { + return forEachBehavior(aStack, behavior -> behavior.onMiddleClick(this, aStack, aPlayer)); + } + @Override public boolean onLeftClickEntity(ItemStack aStack, EntityPlayer aPlayer, Entity aEntity) { use(aStack, 0, aPlayer); @@ -260,9 +266,13 @@ public abstract class MetaBaseItem extends GTGenericItem "" + formatNumbers(tStats[0])) + EnumChatFormatting.GRAY); } - ArrayList<IItemBehaviour<MetaBaseItem>> tList = mItemBehaviors.get((short) getDamage(aStack)); - if (tList != null) for (IItemBehaviour<MetaBaseItem> tBehavior : tList) - aList = tBehavior.getAdditionalToolTips(this, aList, aStack); + ArrayList<IItemBehaviour<MetaBaseItem>> behaviours = mItemBehaviors.get((short) getDamage(aStack)); + if (behaviours != null) { + for (IItemBehaviour<MetaBaseItem> behavior : behaviours) { + aList = !KeyboardUtil.isShiftKeyDown() ? behavior.getAdditionalToolTips(this, aList, aStack) + : behavior.getAdditionalToolTipsWhileSneaking(this, aList, aStack); + } + } addAdditionalToolTips(aList, aStack, aPlayer); } diff --git a/src/main/java/gregtech/api/items/MetaGeneratedItem.java b/src/main/java/gregtech/api/items/MetaGeneratedItem.java index 449b1ced24..0d8c1f1b32 100644 --- a/src/main/java/gregtech/api/items/MetaGeneratedItem.java +++ b/src/main/java/gregtech/api/items/MetaGeneratedItem.java @@ -244,6 +244,12 @@ public abstract class MetaGeneratedItem extends MetaBaseItem implements IGT_Item return this; } + @SuppressWarnings("UnusedReturnValue") + public final MetaGeneratedItem setSubIcons(int metaValue, int length) { + mIconList[metaValue] = Arrays.copyOf(mIconList[metaValue], length + 1); + return this; + } + /** * * @param aMetaValue the Meta Value of the Item you want to set it to. [0 - 32765] diff --git a/src/main/java/gregtech/api/net/GTPacketInfiniteSpraycan.java b/src/main/java/gregtech/api/net/GTPacketInfiniteSpraycan.java index 66ad89590f..302937f6ba 100644 --- a/src/main/java/gregtech/api/net/GTPacketInfiniteSpraycan.java +++ b/src/main/java/gregtech/api/net/GTPacketInfiniteSpraycan.java @@ -1,5 +1,7 @@ package gregtech.api.net; +import java.nio.charset.StandardCharsets; + import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.ItemStack; import net.minecraft.network.INetHandler; @@ -16,16 +18,24 @@ import io.netty.buffer.ByteBuf; public class GTPacketInfiniteSpraycan extends GTPacketNew { - private boolean wasSneaking; + private Action action; + private int newColor; private EntityPlayerMP player; public GTPacketInfiniteSpraycan() { super(true); } - public GTPacketInfiniteSpraycan(boolean wasSneaking) { + public GTPacketInfiniteSpraycan(Action action) { + super(false); + this.action = action; + this.newColor = -1; + } + + public GTPacketInfiniteSpraycan(Action action, int newColor) { super(false); - this.wasSneaking = wasSneaking; + this.action = action; + this.newColor = newColor; } @Override @@ -35,12 +45,21 @@ public class GTPacketInfiniteSpraycan extends GTPacketNew { @Override public void encode(final ByteBuf aOut) { - aOut.writeBoolean(wasSneaking); + final byte[] name = action.name() + .getBytes(StandardCharsets.UTF_8); + aOut.writeInt(newColor); + aOut.writeInt(name.length); + aOut.writeBytes(name); } @Override public GTPacketNew decode(final ByteArrayDataInput aData) { - return new GTPacketInfiniteSpraycan(aData.readBoolean()); + final int newColor = aData.readInt(); + final int length = aData.readInt(); + final byte[] name = new byte[length]; + aData.readFully(name, 0, length); + + return new GTPacketInfiniteSpraycan(Action.valueOf(new String(name, StandardCharsets.UTF_8)), newColor); } @Override @@ -53,24 +72,94 @@ public class GTPacketInfiniteSpraycan extends GTPacketNew { ItemStack currentItemStack = player.inventory.getCurrentItem(); if (currentItemStack != null && currentItemStack.getItem() instanceof MetaBaseItem item) { item.forEachBehavior(currentItemStack, behavior -> { - if (behavior instanceof BehaviourSprayColorInfinite spraycanBehavior) { - spraycanBehavior.setNewColor(currentItemStack, wasSneaking); + if (behavior instanceof BehaviourSprayColorInfinite spraycanBehavior + && action.execute(spraycanBehavior, currentItemStack, player, newColor)) { player.sendSlotContents(player.inventoryContainer, player.inventory.currentItem, currentItemStack); + return true; + } + + return false; + }); + } + } + + public enum Action { - GTUtility.sendSoundToPlayers( - player.worldObj, - SoundResource.GT_SPRAYCAN_SHAKE, - 1.0F, - 1, - (int) player.posX, - (int) player.posY, - (int) player.posZ); + INCREMENT_COLOR { + + @Override + boolean execute(final BehaviourSprayColorInfinite behaviour, final ItemStack itemStack, + final EntityPlayerMP player, final int newColor) { + if (!behaviour.isLocked(itemStack)) { + behaviour.incrementColor(itemStack, player.isSneaking()); + playShakeSound(player); return true; } + return false; + } + }, + LOCK_CAN { + + @Override + boolean execute(final BehaviourSprayColorInfinite behavior, final ItemStack itemStack, + final EntityPlayerMP player, final int newColor) { + if (behavior.toggleLock(itemStack)) { + Action.playLockSound(player); + } else { + Action.playUnlockSound(player); + } + return true; + } + }, + SET_COLOR { + @Override + boolean execute(final BehaviourSprayColorInfinite behavior, final ItemStack itemStack, + final EntityPlayerMP player, final int newColor) { + if (newColor != -1) { + behavior.setColor(itemStack, (byte) newColor); + Action.playShakeSound(player); + return true; + } return false; - }); + } + }; + + private static void playShakeSound(final EntityPlayerMP player) { + GTUtility.sendSoundToPlayers( + player.worldObj, + SoundResource.GT_SPRAYCAN_SHAKE, + 1.0F, + 1, + (int) player.posX, + (int) player.posY, + (int) player.posZ); + } + + private static void playLockSound(final EntityPlayerMP player) { + GTUtility.sendSoundToPlayers( + player.worldObj, + SoundResource.GT_SPRAYCAN_LOCK, + 1.0F, + 1, + (int) player.posX, + (int) player.posY, + (int) player.posZ); } + + private static void playUnlockSound(final EntityPlayerMP player) { + GTUtility.sendSoundToPlayers( + player.worldObj, + SoundResource.GT_SPRAYCAN_UNLOCK, + 1.0F, + 1, + (int) player.posX, + (int) player.posY, + (int) player.posZ); + } + + abstract boolean execute(final BehaviourSprayColorInfinite behavior, ItemStack itemStack, EntityPlayerMP player, + final int newColor); } } diff --git a/src/main/java/gregtech/api/util/ColoredBlockContainer.java b/src/main/java/gregtech/api/util/ColoredBlockContainer.java new file mode 100644 index 0000000000..82956ff0c0 --- /dev/null +++ b/src/main/java/gregtech/api/util/ColoredBlockContainer.java @@ -0,0 +1,348 @@ +package gregtech.api.util; + +import java.util.Optional; +import java.util.Set; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockColored; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +import com.google.common.collect.BiMap; +import com.google.common.collect.ImmutableBiMap; +import com.google.common.collect.ImmutableSet; + +import appeng.api.implementations.tiles.IColorableTile; +import appeng.api.util.AEColor; +import appeng.block.networking.BlockCableBus; +import appeng.integration.IntegrationRegistry; +import appeng.integration.IntegrationType; +import appeng.integration.abstraction.IFMP; +import appeng.tile.networking.TileCableBus; +import gregtech.api.enums.Dyes; +import gregtech.api.interfaces.tileentity.IColoredTileEntity; + +/** + * Used to provide a consistent interface for dealing with colors of blocks for the various spray can items. + * <p> + * Call {@link #getInstance(EntityPlayer, MovingObjectPosition)} or + * {@link #getInstance(World, int, int, int, ForgeDirection, EntityPlayer)} + * to acquire an instance of this class. + */ +public abstract class ColoredBlockContainer { + + private final static ColoredBlockContainer NULL_INSTANCE = new NullContainer(); + + /** + * Sets the color of the block. + * + * @param newColor a color from 0-15 + * @return true if the block was changed + */ + public abstract boolean setColor(int newColor); + + /** + * Removes the color of the block. + * + * @return true if color was removed + */ + public abstract boolean removeColor(); + + /** + * Returns if the requested block is colorable. + * + * @return true if the block is colorable + */ + public boolean isValid() { + return true; + } + + /** + * Get the color of the block. + * + * @return an Optional with the color of the block inside, or {@link Optional#empty()} if the block is uncolored + * or invalid + */ + public abstract Optional<Integer> getColor(); + + private ColoredBlockContainer() {} + + public static ColoredBlockContainer getInstance(EntityPlayer player, MovingObjectPosition position) { + if (position == null || position.typeOfHit != MovingObjectPosition.MovingObjectType.BLOCK) { + return NULL_INSTANCE; + } + return getInstance( + player.worldObj, + position.blockX, + position.blockY, + position.blockZ, + ForgeDirection.getOrientation(position.sideHit), + player); + } + + public static ColoredBlockContainer getInstance(EntityPlayer player, TileEntity tileEntity, ForgeDirection side) { + if (tileEntity == null) { + return NULL_INSTANCE; + } + + return getInstance(player.worldObj, tileEntity.xCoord, tileEntity.yCoord, tileEntity.zCoord, side, player); + } + + public static ColoredBlockContainer getInstance(World world, int x, int y, int z, ForgeDirection side, + EntityPlayer player) { + final Block block = world.getBlock(x, y, z); + + // The vanilla method returns Blocks.air instead of null for a negative result + if (block != Blocks.air) { + if (VanillaBlockContainer.ALLOWED_VANILLA_BLOCKS.contains(block) || block instanceof BlockColored) { + return new VanillaBlockContainer(block, world, x, y, z, side); + } else if (block instanceof final BlockCableBus bus) { + return new AE2BlockCableBusContainer(bus, world, x, y, z, side, player); + } else { + final TileEntity tileEntity = world.getTileEntity(x, y, z); + + if (tileEntity instanceof final IColorableTile colorableTile) { + return new AE2ColorableTileContainer(colorableTile, side, player); + } else if (tileEntity instanceof final IColoredTileEntity coloredTileEntity) { + return new GTColoredBlockContainer(coloredTileEntity); + } + } + } + return NULL_INSTANCE; + } + + /** + * Provides functionality for various types of vanilla blocks that use their metadata value for color. Also performs + * some transformations of blocks, e.g. glass is transformed to stained glass when sprayed. + */ + private static class VanillaBlockContainer extends ColoredBlockContainer { + + private static final Set<Block> ALLOWED_VANILLA_BLOCKS = ImmutableSet.of( + Blocks.glass, + Blocks.glass_pane, + Blocks.stained_glass, + Blocks.stained_glass_pane, + Blocks.carpet, + Blocks.hardened_clay, + Blocks.stained_hardened_clay); + private static final BiMap<Block, Block> TRANSFORMATIONS = ImmutableBiMap.of( + Blocks.glass, + Blocks.stained_glass, + Blocks.glass_pane, + Blocks.stained_glass_pane, + Blocks.hardened_clay, + Blocks.stained_hardened_clay); + + private final int originalColor; + private final World world; + private final int x; + private final int y; + private final int z; + private final Block block; + private final ForgeDirection side; + + public VanillaBlockContainer(final Block block, final World world, final int x, final int y, final int z, + final ForgeDirection side) { + this.world = world; + this.x = x; + this.y = y; + this.z = z; + this.block = block; + this.side = side; + originalColor = world.getBlockMetadata(x, y, z); + } + + @Override + public Optional<Integer> getColor() { + return Optional.of(Dyes.transformDyeIndex(originalColor)); + } + + @Override + public boolean setColor(final int newColor) { + final int transformedColor = Dyes.transformDyeIndex(newColor); + + if (TRANSFORMATIONS.containsKey(block)) { + world.setBlock(x, y, z, TRANSFORMATIONS.get(block), transformedColor, 3); + return true; + } else { + if (originalColor != transformedColor) { + if (block instanceof BlockColored) { + return block.recolourBlock(world, x, y, z, side, transformedColor); + } + return world.setBlockMetadataWithNotify(x, y, z, transformedColor, 3); + } + } + + return block.recolourBlock(world, x, y, z, side, transformedColor); + } + + @Override + public boolean removeColor() { + if (TRANSFORMATIONS.containsValue(block)) { + world.setBlock( + x, + y, + z, + TRANSFORMATIONS.inverse() + .get(block), + 0, + 3); + return true; + } + + return false; + } + } + + /** + * Provides functionality for full AE2 blocks like the ME Chest and Security Terminal. + */ + private static class AE2ColorableTileContainer extends ColoredBlockContainer { + + private final IColorableTile colorableTile; + private final ForgeDirection side; + private final EntityPlayer player; + + public AE2ColorableTileContainer(final IColorableTile colorableTile, final ForgeDirection side, + final EntityPlayer player) { + this.colorableTile = colorableTile; + this.side = side; + this.player = player; + } + + @Override + public Optional<Integer> getColor() { + return Optional.of( + Dyes.transformDyeIndex( + colorableTile.getColor() + .ordinal())); + } + + @Override + public boolean setColor(final int newColor) { + return colorableTile.recolourBlock(side, AEColor.values()[Dyes.transformDyeIndex(newColor)], player); + } + + @Override + public boolean removeColor() { + return colorableTile.recolourBlock(side, AEColor.Transparent, player); + } + } + + /** + * Provides functionality for AE2 cables and other multipart things that go on cables. + */ + private static class AE2BlockCableBusContainer extends ColoredBlockContainer { + + private final BlockCableBus bus; + private final World world; + private final int x; + private final int y; + private final int z; + private final ForgeDirection side; + private final EntityPlayer player; + + public AE2BlockCableBusContainer(final BlockCableBus bus, final World world, final int x, final int y, + final int z, final ForgeDirection side, final EntityPlayer player) { + this.bus = bus; + this.world = world; + this.x = x; + this.y = y; + this.z = z; + this.side = side; + this.player = player; + } + + @Override + public boolean setColor(final int newColor) { + return bus.recolourBlock(world, x, y, z, side, Dyes.transformDyeIndex(newColor), player); + } + + @Override + public boolean removeColor() { + return bus.recolourBlock(world, x, y, z, side, AEColor.Transparent.ordinal(), player); + } + + @Override + public Optional<Integer> getColor() { + final TileEntity te = world.getTileEntity(x, y, z); + AEColor aeColor = null; + + // Code stolen from an AE2 private method. + if (te instanceof final TileCableBus cableBus) { + aeColor = cableBus.getCableBus().getColor(); + } else if (IntegrationRegistry.INSTANCE.isEnabled(IntegrationType.FMP)) { + aeColor = ((IFMP) IntegrationRegistry.INSTANCE.getInstance(IntegrationType.FMP)).getCableContainer(te).getColor(); + } + + return aeColor == null ? Optional.empty() : Optional.of(Dyes.transformDyeIndex(aeColor.ordinal())); + } + } + + /** + * Provides functionality for GT machines, cables, pipes, etc. + */ + private static class GTColoredBlockContainer extends ColoredBlockContainer { + + private final IColoredTileEntity coloredTileEntity; + + public GTColoredBlockContainer(final IColoredTileEntity coloredTileEntity) { + this.coloredTileEntity = coloredTileEntity; + } + + @Override + public boolean setColor(final int newColor) { + coloredTileEntity.setColorization((byte) newColor); + return true; + } + + @Override + public boolean removeColor() { + if (coloredTileEntity.getColorization() > -1) { + coloredTileEntity.setColorization((byte) -1); + return true; + } + return false; + } + + @Override + public Optional<Integer> getColor() { + final int colorization = coloredTileEntity.getColorization(); + if (colorization == -1) { + return Optional.empty(); + } + return Optional.of(colorization); + } + } + + /** + * Returned when the block is invalid or otherwise has no color functionality. Calling {@link #setColor(int)} does + * nothing, so it's safe to call without verifying the exact instance of the returned {@link ColoredBlockContainer}. + */ + private static class NullContainer extends ColoredBlockContainer { + + @Override + public boolean setColor(final int newColor) { + return false; + } + + @Override + public boolean removeColor() { + return false; + } + + @Override + public Optional<Integer> getColor() { + return Optional.empty(); + } + + @Override + public boolean isValid() { + return false; + } + } +} |