diff --git a/src/main/java/gregtech/api/block/machines/BlockMachine.java b/src/main/java/gregtech/api/block/machines/BlockMachine.java
index 5cac56bb49c..ba610144bff 100644
--- a/src/main/java/gregtech/api/block/machines/BlockMachine.java
+++ b/src/main/java/gregtech/api/block/machines/BlockMachine.java
@@ -19,7 +19,7 @@
import gregtech.api.util.Mods;
import gregtech.client.renderer.handler.MetaTileEntityRenderer;
import gregtech.common.creativetab.GTCreativeTabs;
-import gregtech.common.items.MetaItems;
+import gregtech.common.items.behaviors.spray.AbstractSprayBehavior;
import gregtech.integration.ctm.IFacadeWrapper;
import net.minecraft.block.Block;
@@ -257,9 +257,10 @@ public EnumFacing[] getValidRotations(@NotNull World world, @NotNull BlockPos po
public boolean recolorBlock(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing side,
@NotNull EnumDyeColor color) {
MetaTileEntity metaTileEntity = getMetaTileEntity(world, pos);
- if (metaTileEntity == null || metaTileEntity.getPaintingColor() == color.colorValue)
+ if (metaTileEntity == null || metaTileEntity.getPaintingColor() == color.colorValue) {
return false;
- metaTileEntity.setPaintingColor(color.colorValue);
+ }
+ metaTileEntity.setPaintingColor(color.colorValue, side);
return true;
}
@@ -326,15 +327,8 @@ public void onBlockPlacedBy(World worldIn, @NotNull BlockPos pos, @NotNull IBloc
}
// Color machines on place if holding spray can in off-hand
- if (placer instanceof EntityPlayer) {
- ItemStack offhand = placer.getHeldItemOffhand();
- for (int i = 0; i < EnumDyeColor.values().length; i++) {
- if (offhand.isItemEqual(MetaItems.SPRAY_CAN_DYES[i].getStackForm())) {
- MetaItems.SPRAY_CAN_DYES[i].getBehaviours().get(0).onItemUse((EntityPlayer) placer, worldIn,
- pos, EnumHand.OFF_HAND, EnumFacing.UP, 0, 0, 0);
- break;
- }
- }
+ if (placer instanceof EntityPlayer player) {
+ AbstractSprayBehavior.handleExternalSpray(player, EnumHand.OFF_HAND, worldIn, pos, EnumFacing.UP);
}
metaTileEntity.onPlacement(placer);
diff --git a/src/main/java/gregtech/api/color/ColorMode.java b/src/main/java/gregtech/api/color/ColorMode.java
new file mode 100644
index 00000000000..c00584d376c
--- /dev/null
+++ b/src/main/java/gregtech/api/color/ColorMode.java
@@ -0,0 +1,39 @@
+package gregtech.api.color;
+
+import net.minecraft.item.EnumDyeColor;
+
+public enum ColorMode {
+
+ /**
+ * Only try spraying a block to an {@link EnumDyeColor}.
+ */
+ DYE(true, false),
+ /**
+ * Only try spraying a block to an ARGB value.
+ */
+ ARGB(false, true),
+ /**
+ * Try spraying the block to an {@link EnumDyeColor}, and if that failed fall back to ARGB.
+ */
+ PREFER_DYE(true, true),
+ /**
+ * Try spraying the block to an ARGB value, and if that failed fall back to {@link EnumDyeColor}.
+ */
+ PREFER_ARGB(true, true);
+
+ private final boolean dye;
+ private final boolean argb;
+
+ ColorMode(boolean dye, boolean argb) {
+ this.dye = dye;
+ this.argb = argb;
+ }
+
+ public boolean dye() {
+ return dye;
+ }
+
+ public boolean argb() {
+ return argb;
+ }
+}
diff --git a/src/main/java/gregtech/api/color/ColorModeSupport.java b/src/main/java/gregtech/api/color/ColorModeSupport.java
new file mode 100644
index 00000000000..481de6ae113
--- /dev/null
+++ b/src/main/java/gregtech/api/color/ColorModeSupport.java
@@ -0,0 +1,51 @@
+package gregtech.api.color;
+
+import net.minecraft.item.EnumDyeColor;
+import net.minecraft.util.text.ITextComponent;
+import net.minecraft.util.text.TextComponentTranslation;
+
+import com.cleanroommc.modularui.api.drawable.IKey;
+import org.jetbrains.annotations.NotNull;
+
+public enum ColorModeSupport {
+
+ /**
+ * This block only supports being colored to an {@link EnumDyeColor}.
+ */
+ DYE_ONLY("gregtech.color_mode.error.argb"),
+ /**
+ * This block only supports being colored to an ARGB value.
+ */
+ ARGB_ONLY("gregtech.color_mode.error.dye"),
+ /**
+ * This block supports being colored to a {@link EnumDyeColor} or ARGB value.
+ */
+ EITHER("gregtech.color_mode.error.either");
+
+ @NotNull
+ private final String errorKey;
+
+ ColorModeSupport(@NotNull String errorKey) {
+ this.errorKey = errorKey;
+ }
+
+ public @NotNull String getErrorTranslationKey() {
+ return errorKey;
+ }
+
+ public @NotNull IKey getErrorKey() {
+ return IKey.lang(errorKey);
+ }
+
+ public @NotNull ITextComponent getErrorText() {
+ return new TextComponentTranslation(errorKey);
+ }
+
+ public boolean supportsMode(@NotNull ColorMode colorMode) {
+ return switch (this) {
+ case DYE_ONLY -> colorMode.dye();
+ case ARGB_ONLY -> colorMode.argb();
+ case EITHER -> true;
+ };
+ }
+}
diff --git a/src/main/java/gregtech/api/color/ColoredBlockContainer.java b/src/main/java/gregtech/api/color/ColoredBlockContainer.java
new file mode 100644
index 00000000000..7758981bc0c
--- /dev/null
+++ b/src/main/java/gregtech/api/color/ColoredBlockContainer.java
@@ -0,0 +1,124 @@
+package gregtech.api.color;
+
+import gregtech.api.color.containers.AE2ColorContainer;
+import gregtech.api.color.containers.BedColorContainer;
+import gregtech.api.color.containers.GTPipeColorContainer;
+import gregtech.api.color.containers.MTEColorContainer;
+import gregtech.api.color.containers.VanillaColorContainer;
+import gregtech.api.util.GTUtility;
+import gregtech.api.util.Mods;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.EnumDyeColor;
+import net.minecraft.util.EnumActionResult;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Used to provide a consistent interface for dealing with colored blocks, whether vanilla or modded.
+ * Inspired by GT5u's ColoredBlockContainer
+ */
+public abstract class ColoredBlockContainer {
+
+ @NotNull
+ private static final Map CONTAINERS = new Object2ObjectOpenHashMap<>(5);
+
+ public static void registerContainer(@NotNull ColoredBlockContainer container) {
+ Objects.requireNonNull(container, "A null ColoredBlockContainer cannot be registered!");
+ ResourceLocation id = container.id;
+ Objects.requireNonNull(id, "A ColoredBlockContainer cannot have a null ID!");
+ if (CONTAINERS.containsKey(id)) {
+ throw new IllegalArgumentException(
+ String.format("A ColoredBlockContainer with an ID of %s already exists!", id));
+ }
+
+ CONTAINERS.put(id, container);
+ }
+
+ /**
+ * Get the color container for the block or tile entity at the provided position.
+ * Will return {@code null} if no container was valid.
+ */
+ public static @Nullable ColoredBlockContainer getContainer(@NotNull World world, @NotNull BlockPos pos,
+ @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player) {
+ for (ColoredBlockContainer container : CONTAINERS.values()) {
+ if (container.isBlockValid(world, pos, facing, player)) {
+ return container;
+ }
+ }
+
+ return null;
+ }
+
+ @ApiStatus.Internal
+ public static void registerCEuContainers() {
+ registerContainer(new GTPipeColorContainer(GTUtility.gregtechId("pipe")));
+ registerContainer(new MTEColorContainer(GTUtility.gregtechId("mte")));
+ if (Mods.AppliedEnergistics2.isModLoaded()) {
+ registerContainer(new AE2ColorContainer(GTUtility.gregtechId("ae2")));
+ }
+ registerContainer(new VanillaColorContainer(GTUtility.gregtechId("vanilla")));
+ registerContainer(new BedColorContainer(GTUtility.gregtechId("bed")));
+ }
+
+ @NotNull
+ protected final ResourceLocation id;
+
+ public ColoredBlockContainer(@NotNull ResourceLocation id) {
+ this.id = id;
+ }
+
+ public abstract boolean isBlockValid(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player);
+
+ public @NotNull EnumActionResult setColor(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player, @Nullable EnumDyeColor newColor) {
+ return EnumActionResult.PASS;
+ }
+
+ public @NotNull EnumActionResult setColor(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player, int newColor) {
+ return EnumActionResult.PASS;
+ }
+
+ public @NotNull EnumActionResult removeColor(@NotNull World world, @NotNull BlockPos pos,
+ @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player) {
+ return EnumActionResult.PASS;
+ }
+
+ public @Nullable EnumDyeColor getColor(@NotNull World world, @NotNull BlockPos pos,
+ @NotNull EnumFacing facing, @NotNull EntityPlayer player) {
+ return null;
+ }
+
+ public int getColorInt(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player) {
+ EnumDyeColor dyeColor = getColor(world, pos, facing, player);
+ return dyeColor == null ? -1 : dyeColor.colorValue;
+ }
+
+ public boolean colorMatches(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player, @Nullable EnumDyeColor color) {
+ return getColor(world, pos, facing, player) == color;
+ }
+
+ public boolean colorMatches(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player, int color) {
+ return getColorInt(world, pos, facing, player) == color;
+ }
+
+ public abstract @NotNull ColorModeSupport getSupportedColorMode();
+}
diff --git a/src/main/java/gregtech/api/color/containers/AE2ColorContainer.java b/src/main/java/gregtech/api/color/containers/AE2ColorContainer.java
new file mode 100644
index 00000000000..7c691c0eb12
--- /dev/null
+++ b/src/main/java/gregtech/api/color/containers/AE2ColorContainer.java
@@ -0,0 +1,82 @@
+package gregtech.api.color.containers;
+
+import gregtech.api.color.ColorModeSupport;
+import gregtech.api.color.ColoredBlockContainer;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.EnumDyeColor;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumActionResult;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+
+import appeng.api.implementations.tiles.IColorableTile;
+import appeng.api.util.AEColor;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class AE2ColorContainer extends ColoredBlockContainer {
+
+ public AE2ColorContainer(@NotNull ResourceLocation id) {
+ super(id);
+ }
+
+ @Override
+ public @NotNull EnumActionResult setColor(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player, @Nullable EnumDyeColor newColor) {
+ if (newColor == null) {
+ return removeColor(world, pos, facing, player);
+ }
+
+ if (colorMatches(world, pos, facing, player, newColor)) {
+ return EnumActionResult.PASS;
+ }
+
+ TileEntity te = world.getTileEntity(pos);
+ if (te instanceof IColorableTile colorableTile) {
+ if (colorableTile.getColor().dye != newColor) {
+ return colorableTile.recolourBlock(facing, AEColor.values()[newColor.ordinal()], player) ?
+ EnumActionResult.SUCCESS : EnumActionResult.FAIL;
+ }
+ }
+
+ return EnumActionResult.PASS;
+ }
+
+ @Override
+ public @NotNull EnumActionResult removeColor(@NotNull World world, @NotNull BlockPos pos,
+ @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player) {
+ TileEntity te = world.getTileEntity(pos);
+ if (te instanceof IColorableTile colorableTile && colorableTile.getColor() != AEColor.TRANSPARENT) {
+ return colorableTile.recolourBlock(facing, AEColor.TRANSPARENT, player) ? EnumActionResult.SUCCESS :
+ EnumActionResult.PASS;
+ }
+
+ return EnumActionResult.PASS;
+ }
+
+ @Override
+ public @Nullable EnumDyeColor getColor(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player) {
+ TileEntity te = world.getTileEntity(pos);
+ if (te instanceof IColorableTile colorableTile) {
+ return colorableTile.getColor().dye;
+ }
+
+ return null;
+ }
+
+ @Override
+ public boolean isBlockValid(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player) {
+ return world.getTileEntity(pos) instanceof IColorableTile;
+ }
+
+ @Override
+ public @NotNull ColorModeSupport getSupportedColorMode() {
+ return ColorModeSupport.DYE_ONLY;
+ }
+}
diff --git a/src/main/java/gregtech/api/color/containers/BedColorContainer.java b/src/main/java/gregtech/api/color/containers/BedColorContainer.java
new file mode 100644
index 00000000000..1ffd73746cf
--- /dev/null
+++ b/src/main/java/gregtech/api/color/containers/BedColorContainer.java
@@ -0,0 +1,73 @@
+package gregtech.api.color.containers;
+
+import gregtech.api.color.ColorModeSupport;
+import gregtech.api.color.ColoredBlockContainer;
+
+import net.minecraft.block.BlockBed;
+import net.minecraft.block.BlockHorizontal;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.EnumDyeColor;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.tileentity.TileEntityBed;
+import net.minecraft.util.EnumActionResult;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class BedColorContainer extends ColoredBlockContainer {
+
+ public BedColorContainer(@NotNull ResourceLocation id) {
+ super(id);
+ }
+
+ @Override
+ public @NotNull EnumActionResult setColor(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player, @Nullable EnumDyeColor newColor) {
+ // There are no uncolored beds.
+ if (newColor == null) {
+ return EnumActionResult.FAIL;
+ } else if (colorMatches(world, pos, facing, player, newColor)) {
+ return EnumActionResult.PASS;
+ }
+
+ IBlockState bedPart1 = world.getBlockState(pos);
+ BlockBed.EnumPartType partOfBed1 = bedPart1.getValue(BlockBed.PART);
+ EnumFacing bedFacing = bedPart1.getValue(BlockHorizontal.FACING);
+
+ // The faced direction is always the direction of the foot -> head.
+ BlockPos otherPartPos = pos.offset(partOfBed1 == BlockBed.EnumPartType.FOOT ? bedFacing :
+ bedFacing.getOpposite());
+
+ TileEntity bed1TE = world.getTileEntity(pos);
+ TileEntity bed2TE = world.getTileEntity(otherPartPos);
+ if (!(bed1TE instanceof TileEntityBed bed1 && bed2TE instanceof TileEntityBed bed2)) {
+ return EnumActionResult.FAIL;
+ }
+
+ bed1.setColor(newColor);
+ bed2.setColor(newColor);
+ return EnumActionResult.SUCCESS;
+ }
+
+ @Override
+ public @Nullable EnumDyeColor getColor(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player) {
+ return world.getTileEntity(pos) instanceof TileEntityBed bedTE ? bedTE.getColor() : null;
+ }
+
+ @Override
+ public boolean isBlockValid(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player) {
+ return world.getTileEntity(pos) instanceof TileEntityBed;
+ }
+
+ @Override
+ public @NotNull ColorModeSupport getSupportedColorMode() {
+ return ColorModeSupport.DYE_ONLY;
+ }
+}
diff --git a/src/main/java/gregtech/api/color/containers/GTPipeColorContainer.java b/src/main/java/gregtech/api/color/containers/GTPipeColorContainer.java
new file mode 100644
index 00000000000..c1ab65d3093
--- /dev/null
+++ b/src/main/java/gregtech/api/color/containers/GTPipeColorContainer.java
@@ -0,0 +1,101 @@
+package gregtech.api.color.containers;
+
+import gregtech.api.color.ColorModeSupport;
+import gregtech.api.color.ColoredBlockContainer;
+import gregtech.api.pipenet.tile.IPipeTile;
+import gregtech.api.util.ColorUtil;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.EnumDyeColor;
+import net.minecraft.util.EnumActionResult;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class GTPipeColorContainer extends ColoredBlockContainer {
+
+ public GTPipeColorContainer(@NotNull ResourceLocation id) {
+ super(id);
+ }
+
+ @Override
+ public @NotNull EnumActionResult setColor(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player, @Nullable EnumDyeColor newColor) {
+ if (newColor == null) {
+ return removeColor(world, pos, facing, player);
+ }
+
+ if (colorMatches(world, pos, facing, player, newColor.colorValue)) {
+ return EnumActionResult.PASS;
+ }
+
+ if (world.getTileEntity(pos) instanceof IPipeTile, ?>pipeTile) {
+ pipeTile.setPaintingColor(newColor.colorValue);
+ return EnumActionResult.SUCCESS;
+ }
+
+ return EnumActionResult.PASS;
+ }
+
+ @Override
+ public @NotNull EnumActionResult setColor(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player, int newColor) {
+ if (newColor == -1) {
+ return removeColor(world, pos, facing, player);
+ }
+
+ if (world.getTileEntity(pos) instanceof IPipeTile, ?>pipeTile) {
+ if (pipeTile.isPainted() && colorMatches(world, pos, facing, player, newColor)) {
+ return EnumActionResult.PASS;
+ } else {
+ pipeTile.setPaintingColor(newColor);
+ return EnumActionResult.SUCCESS;
+ }
+ }
+
+ return EnumActionResult.PASS;
+ }
+
+ @Override
+ public @NotNull EnumActionResult removeColor(@NotNull World world, @NotNull BlockPos pos,
+ @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player) {
+ if (world.getTileEntity(pos) instanceof IPipeTile, ?>pipeTile && pipeTile.isPainted()) {
+ pipeTile.setPaintingColor(-1);
+ return EnumActionResult.SUCCESS;
+ }
+
+ return EnumActionResult.PASS;
+ }
+
+ @Override
+ public @Nullable EnumDyeColor getColor(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player) {
+ return ColorUtil.getDyeColorFromRGB(getColorInt(world, pos, facing, player));
+ }
+
+ @Override
+ public int getColorInt(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player) {
+ if (world.getTileEntity(pos) instanceof IPipeTile, ?>pipeTile && pipeTile.isPainted()) {
+ return pipeTile.getPaintingColor();
+ }
+
+ return -1;
+ }
+
+ @Override
+ public boolean isBlockValid(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player) {
+ return world.getTileEntity(pos) instanceof IPipeTile, ?>;
+ }
+
+ @Override
+ public @NotNull ColorModeSupport getSupportedColorMode() {
+ return ColorModeSupport.EITHER;
+ }
+}
diff --git a/src/main/java/gregtech/api/color/containers/MTEColorContainer.java b/src/main/java/gregtech/api/color/containers/MTEColorContainer.java
new file mode 100644
index 00000000000..721f6ea14cc
--- /dev/null
+++ b/src/main/java/gregtech/api/color/containers/MTEColorContainer.java
@@ -0,0 +1,108 @@
+package gregtech.api.color.containers;
+
+import gregtech.api.color.ColorModeSupport;
+import gregtech.api.color.ColoredBlockContainer;
+import gregtech.api.metatileentity.MetaTileEntity;
+import gregtech.api.util.ColorUtil;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.EnumDyeColor;
+import net.minecraft.util.EnumActionResult;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import static gregtech.api.util.GTUtility.getMetaTileEntity;
+
+public class MTEColorContainer extends ColoredBlockContainer {
+
+ public MTEColorContainer(@NotNull ResourceLocation id) {
+ super(id);
+ }
+
+ @Override
+ public @NotNull EnumActionResult setColor(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player, @Nullable EnumDyeColor newColor) {
+ if (newColor == null) {
+ return removeColor(world, pos, facing, player);
+ }
+
+ if (colorMatches(world, pos, facing, player, newColor.colorValue)) {
+ return EnumActionResult.PASS;
+ }
+
+ MetaTileEntity mte = getMetaTileEntity(world, pos);
+ if (mte != null && mte.canBeModifiedBy(player)) {
+ mte.setPaintingColor(newColor, facing);
+ return EnumActionResult.SUCCESS;
+ }
+
+ return EnumActionResult.PASS;
+ }
+
+ @Override
+ public @NotNull EnumActionResult setColor(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player, int newColor) {
+ if (newColor == -1) {
+ return removeColor(world, pos, facing, player);
+ }
+
+ if (colorMatches(world, pos, facing, player, newColor)) {
+ return EnumActionResult.PASS;
+ }
+
+ MetaTileEntity mte = getMetaTileEntity(world, pos);
+ if (mte != null && mte.canBeModifiedBy(player)) {
+ mte.setPaintingColor(newColor, facing);
+ return EnumActionResult.SUCCESS;
+ }
+
+ return EnumActionResult.PASS;
+ }
+
+ @Override
+ public @NotNull EnumActionResult removeColor(@NotNull World world, @NotNull BlockPos pos,
+ @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player) {
+ MetaTileEntity mte = getMetaTileEntity(world, pos);
+ if (mte != null && mte.isPainted() && mte.canBeModifiedBy(player)) {
+ mte.setPaintingColor(-1, facing);
+ return EnumActionResult.SUCCESS;
+ }
+
+ return EnumActionResult.PASS;
+ }
+
+ @Override
+ public @Nullable EnumDyeColor getColor(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player) {
+ return ColorUtil.getDyeColorFromRGB(getColorInt(world, pos, facing, player));
+ }
+
+ @Override
+ public int getColorInt(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player) {
+ MetaTileEntity mte = getMetaTileEntity(world, pos);
+ if (mte != null) {
+ return mte.getPaintingColor();
+ }
+
+ return -1;
+ }
+
+ @Override
+ public boolean isBlockValid(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player) {
+ MetaTileEntity mte = getMetaTileEntity(world, pos);
+ return mte != null && mte.isValid();
+ }
+
+ @Override
+ public @NotNull ColorModeSupport getSupportedColorMode() {
+ return ColorModeSupport.EITHER;
+ }
+}
diff --git a/src/main/java/gregtech/api/color/containers/VanillaColorContainer.java b/src/main/java/gregtech/api/color/containers/VanillaColorContainer.java
new file mode 100644
index 00000000000..aab9449da51
--- /dev/null
+++ b/src/main/java/gregtech/api/color/containers/VanillaColorContainer.java
@@ -0,0 +1,142 @@
+package gregtech.api.color.containers;
+
+import gregtech.api.color.ColorModeSupport;
+import gregtech.api.color.ColoredBlockContainer;
+
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockColored;
+import net.minecraft.block.BlockStainedGlass;
+import net.minecraft.block.BlockStainedGlassPane;
+import net.minecraft.block.properties.IProperty;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.EnumDyeColor;
+import net.minecraft.util.EnumActionResult;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Map;
+
+public class VanillaColorContainer extends ColoredBlockContainer {
+
+ private static final BiMap TRANSFORMATIONS = ImmutableBiMap.of(
+ Blocks.GLASS, Blocks.STAINED_GLASS,
+ Blocks.GLASS_PANE, Blocks.STAINED_GLASS_PANE,
+ Blocks.HARDENED_CLAY, Blocks.STAINED_HARDENED_CLAY);
+
+ private static final Map> PROPERTY_MAP = ImmutableMap.of(
+ Blocks.GLASS, BlockStainedGlass.COLOR,
+ Blocks.GLASS_PANE, BlockStainedGlassPane.COLOR,
+ Blocks.HARDENED_CLAY, BlockColored.COLOR);
+
+ public VanillaColorContainer(@NotNull ResourceLocation id) {
+ super(id);
+ }
+
+ @Override
+ public @NotNull EnumActionResult setColor(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player, @Nullable EnumDyeColor newColor) {
+ if (newColor == null) {
+ return removeColor(world, pos, facing, player);
+ }
+
+ if (colorMatches(world, pos, facing, player, newColor)) {
+ return EnumActionResult.PASS;
+ }
+
+ IBlockState state = world.getBlockState(pos);
+ Block block = state.getBlock();
+
+ if (TRANSFORMATIONS.containsKey(block)) {
+ IBlockState newBlockState = TRANSFORMATIONS.get(block)
+ .getDefaultState()
+ .withProperty(PROPERTY_MAP.get(block), newColor);
+ return world.setBlockState(pos, newBlockState) ? EnumActionResult.SUCCESS : EnumActionResult.FAIL;
+ }
+
+ return block.recolorBlock(world, pos, facing, newColor) ? EnumActionResult.SUCCESS : EnumActionResult.FAIL;
+ }
+
+ @Override
+ public @NotNull EnumActionResult removeColor(@NotNull World world, @NotNull BlockPos pos,
+ @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player) {
+ IBlockState state = world.getBlockState(pos);
+ Block block = state.getBlock();
+
+ if (TRANSFORMATIONS.containsValue(block)) {
+ IBlockState newBlockState = TRANSFORMATIONS.inverse()
+ .get(block)
+ .getDefaultState();
+ return world.setBlockState(pos, newBlockState) ? EnumActionResult.SUCCESS : EnumActionResult.FAIL;
+ } else {
+ for (IProperty> prop : state.getPropertyKeys()) {
+ if (prop.getName().equals("color") && prop.getValueClass() == EnumDyeColor.class) {
+ IBlockState defaultState = block.getDefaultState();
+ EnumDyeColor defaultColor = EnumDyeColor.WHITE;
+ try {
+ // try to read the default color value from the default state instead of just
+ // blindly setting it to default state, and potentially resetting other values
+ defaultColor = (EnumDyeColor) defaultState.getValue(prop);
+ } catch (IllegalArgumentException ignored) {
+ // no default color, we may have to fallback to WHITE here
+ // other mods that have custom behavior can be done as
+ // special cases above on a case-by-case basis
+ }
+
+ return block.recolorBlock(world, pos, facing, defaultColor) ? EnumActionResult.SUCCESS :
+ EnumActionResult.FAIL;
+ }
+ }
+ }
+
+ return EnumActionResult.PASS;
+ }
+
+ @Override
+ public @Nullable EnumDyeColor getColor(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player) {
+ IBlockState state = world.getBlockState(pos);
+ for (IProperty> prop : state.getPropertyKeys()) {
+ if (prop.getValueClass() == EnumDyeColor.class) {
+ // noinspection unchecked <- grr, shakes fist
+ return state.getValue((IProperty) prop);
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public boolean isBlockValid(@NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player) {
+ IBlockState state = world.getBlockState(pos);
+ Block block = state.getBlock();
+
+ if (TRANSFORMATIONS.containsKey(block) || TRANSFORMATIONS.containsValue(block)) {
+ return true;
+ }
+
+ for (IProperty> prop : state.getPropertyKeys()) {
+ if (prop.getValueClass() == EnumDyeColor.class) {
+ return !world.isAirBlock(pos);
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public @NotNull ColorModeSupport getSupportedColorMode() {
+ return ColorModeSupport.DYE_ONLY;
+ }
+}
diff --git a/src/main/java/gregtech/api/items/gui/ItemUIFactory.java b/src/main/java/gregtech/api/items/gui/ItemUIFactory.java
index f1444152f5f..0b41926fdb1 100644
--- a/src/main/java/gregtech/api/items/gui/ItemUIFactory.java
+++ b/src/main/java/gregtech/api/items/gui/ItemUIFactory.java
@@ -39,6 +39,7 @@ default GTGuiTheme getUITheme() {
return GTGuiTheme.STANDARD;
}
+ // TODO: change to abstract once MUI2 port is complete
@Override
default ModularPanel buildUI(HandGuiData guiData, PanelSyncManager guiSyncManager, UISettings settings) {
return null;
diff --git a/src/main/java/gregtech/api/items/metaitem/MetaItem.java b/src/main/java/gregtech/api/items/metaitem/MetaItem.java
index 235e97214e9..b5d83db5a7a 100644
--- a/src/main/java/gregtech/api/items/metaitem/MetaItem.java
+++ b/src/main/java/gregtech/api/items/metaitem/MetaItem.java
@@ -12,7 +12,20 @@
import gregtech.api.items.OreDictNames;
import gregtech.api.items.gui.ItemUIFactory;
import gregtech.api.items.gui.PlayerInventoryHolder;
-import gregtech.api.items.metaitem.stats.*;
+import gregtech.api.items.metaitem.stats.IEnchantabilityHelper;
+import gregtech.api.items.metaitem.stats.IFoodBehavior;
+import gregtech.api.items.metaitem.stats.IItemBehaviour;
+import gregtech.api.items.metaitem.stats.IItemCapabilityProvider;
+import gregtech.api.items.metaitem.stats.IItemColorProvider;
+import gregtech.api.items.metaitem.stats.IItemComponent;
+import gregtech.api.items.metaitem.stats.IItemContainerItemProvider;
+import gregtech.api.items.metaitem.stats.IItemDurabilityManager;
+import gregtech.api.items.metaitem.stats.IItemMaxStackSizeProvider;
+import gregtech.api.items.metaitem.stats.IItemModelDispatcher;
+import gregtech.api.items.metaitem.stats.IItemNameProvider;
+import gregtech.api.items.metaitem.stats.IItemUseManager;
+import gregtech.api.items.metaitem.stats.IMouseEventHandler;
+import gregtech.api.items.metaitem.stats.ISubItemHandler;
import gregtech.api.unification.OreDictUnifier;
import gregtech.api.unification.material.Material;
import gregtech.api.unification.ore.OrePrefix;
@@ -628,7 +641,7 @@ public void addInformation(@NotNull ItemStack itemStack, @Nullable World worldIn
fluid == null ? "" : fluid.getLocalizedName()));
if (fluidHandler instanceof IFilteredFluidContainer filtered &&
- filtered.getFilter() instanceof IPropertyFluidFilter propertyFilter) {
+ filtered.getFilter() instanceof IPropertyFluidFilter>propertyFilter) {
propertyFilter.appendTooltips(lines, false, true);
}
}
@@ -738,6 +751,15 @@ public void getSubItems(@NotNull CreativeTabs tab, @NotNull NonNullList getMetaItem() {
private IItemDurabilityManager durabilityManager;
private IEnchantabilityHelper enchantabilityHelper;
private IItemModelDispatcher itemModelDispatcher;
+ private IMouseEventHandler mouseEventHandler;
private EnumRarity rarity;
private int burnValue = 0;
@@ -878,42 +901,46 @@ public MetaValueItem addComponents(IItemComponent... stats) {
protected void addItemComponentsInternal(IItemComponent... stats) {
for (IItemComponent itemComponent : stats) {
- if (itemComponent instanceof IItemNameProvider) {
- this.nameProvider = (IItemNameProvider) itemComponent;
+ if (itemComponent instanceof IItemNameProvider iItemNameProvider) {
+ this.nameProvider = iItemNameProvider;
}
- if (itemComponent instanceof IItemMaxStackSizeProvider) {
- this.stackSizeProvider = (IItemMaxStackSizeProvider) itemComponent;
+ if (itemComponent instanceof IItemMaxStackSizeProvider iItemMaxStackSizeProvider) {
+ this.stackSizeProvider = iItemMaxStackSizeProvider;
}
- if (itemComponent instanceof ISubItemHandler) {
- this.subItemHandler = (ISubItemHandler) itemComponent;
+ if (itemComponent instanceof ISubItemHandler iSubItemHandler) {
+ this.subItemHandler = iSubItemHandler;
}
- if (itemComponent instanceof IItemContainerItemProvider) {
- this.containerItemProvider = (IItemContainerItemProvider) itemComponent;
+ if (itemComponent instanceof IItemContainerItemProvider iItemContainerItemProvider) {
+ this.containerItemProvider = iItemContainerItemProvider;
}
- if (itemComponent instanceof IItemDurabilityManager) {
- this.durabilityManager = (IItemDurabilityManager) itemComponent;
+ if (itemComponent instanceof IItemDurabilityManager iItemDurabilityManager) {
+ this.durabilityManager = iItemDurabilityManager;
}
- if (itemComponent instanceof IItemUseManager) {
- this.useManager = (IItemUseManager) itemComponent;
+ if (itemComponent instanceof IItemUseManager iItemUseManager) {
+ this.useManager = iItemUseManager;
}
- if (itemComponent instanceof IFoodBehavior) {
- this.useManager = new FoodUseManager((IFoodBehavior) itemComponent);
+ if (itemComponent instanceof IFoodBehavior iFoodBehavior) {
+ this.useManager = iFoodBehavior.createFoodUseManager();
}
- if (itemComponent instanceof ItemUIFactory) {
- this.uiManager = (ItemUIFactory) itemComponent;
+ if (itemComponent instanceof ItemUIFactory itemUIFactory) {
+ this.uiManager = itemUIFactory;
}
- if (itemComponent instanceof IFilter.Factory) {
- this.filterBehavior = (IFilter.Factory) itemComponent;
+ if (itemComponent instanceof IFilter.Factory filterFactory) {
+ this.filterBehavior = filterFactory;
}
- if (itemComponent instanceof IItemColorProvider) {
- this.colorProvider = (IItemColorProvider) itemComponent;
+ if (itemComponent instanceof IItemColorProvider iItemColorProvider) {
+ this.colorProvider = iItemColorProvider;
}
- if (itemComponent instanceof IItemBehaviour) {
- this.behaviours.add((IItemBehaviour) itemComponent);
- ((IItemBehaviour) itemComponent).addPropertyOverride(getMetaItem());
+ if (itemComponent instanceof IItemBehaviour iItemBehaviour) {
+ this.behaviours.add(iItemBehaviour);
+ iItemBehaviour.addPropertyOverride(getMetaItem());
}
- if (itemComponent instanceof IEnchantabilityHelper) {
- this.enchantabilityHelper = (IEnchantabilityHelper) itemComponent;
+ if (itemComponent instanceof IEnchantabilityHelper iEnchantabilityHelper) {
+ this.enchantabilityHelper = iEnchantabilityHelper;
+ }
+ // noinspection PatternVariableHidesField
+ if (itemComponent instanceof IMouseEventHandler mouseEventHandler) {
+ this.mouseEventHandler = mouseEventHandler;
}
if (itemComponent instanceof IItemModelDispatcher iItemModelDispatcher) {
this.itemModelDispatcher = iItemModelDispatcher;
@@ -983,6 +1010,11 @@ public IItemModelDispatcher getItemModelDispatcher() {
return itemModelDispatcher;
}
+ @Nullable
+ public IMouseEventHandler getMouseEventHandler() {
+ return mouseEventHandler;
+ }
+
public int getBurnValue() {
return burnValue;
}
diff --git a/src/main/java/gregtech/api/items/metaitem/stats/IFoodBehavior.java b/src/main/java/gregtech/api/items/metaitem/stats/IFoodBehavior.java
index 2fc74c6d9a1..b36a96cdac0 100644
--- a/src/main/java/gregtech/api/items/metaitem/stats/IFoodBehavior.java
+++ b/src/main/java/gregtech/api/items/metaitem/stats/IFoodBehavior.java
@@ -1,9 +1,12 @@
package gregtech.api.items.metaitem.stats;
+import gregtech.api.items.metaitem.FoodUseManager;
+
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.EnumAction;
import net.minecraft.item.ItemStack;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
@@ -23,4 +26,9 @@ default ItemStack onFoodEaten(ItemStack stack, EntityPlayer player) {
}
void addInformation(ItemStack itemStack, List lines);
+
+ @NotNull
+ default FoodUseManager createFoodUseManager() {
+ return new FoodUseManager(this);
+ }
}
diff --git a/src/main/java/gregtech/api/items/metaitem/stats/IItemDurabilityManager.java b/src/main/java/gregtech/api/items/metaitem/stats/IItemDurabilityManager.java
index ad45a50dfee..e25676e6700 100644
--- a/src/main/java/gregtech/api/items/metaitem/stats/IItemDurabilityManager.java
+++ b/src/main/java/gregtech/api/items/metaitem/stats/IItemDurabilityManager.java
@@ -9,7 +9,7 @@
public interface IItemDurabilityManager extends IItemComponent {
- /** The durability remaining on this item (double from 0 to 1). */
+ /** The durability remaining on this item (double from 0 to 1 as the durability is used up). */
double getDurabilityForDisplay(ItemStack itemStack);
/** The first and last colors of a gradient. Default to Green durability gradient (null Pair). */
diff --git a/src/main/java/gregtech/api/items/metaitem/stats/IMouseEventHandler.java b/src/main/java/gregtech/api/items/metaitem/stats/IMouseEventHandler.java
new file mode 100644
index 00000000000..d71a8b87357
--- /dev/null
+++ b/src/main/java/gregtech/api/items/metaitem/stats/IMouseEventHandler.java
@@ -0,0 +1,80 @@
+package gregtech.api.items.metaitem.stats;
+
+import gregtech.api.items.metaitem.MetaItem;
+import gregtech.core.network.packets.PacketItemMouseEvent;
+
+import net.minecraft.client.entity.EntityPlayerSP;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.network.PacketBuffer;
+import net.minecraft.util.EnumHand;
+import net.minecraftforge.client.event.MouseEvent;
+import net.minecraftforge.fml.relauncher.Side;
+import net.minecraftforge.fml.relauncher.SideOnly;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.function.Consumer;
+
+/**
+ * Implement on your {@link IItemComponent} to handle mouse events while the corresponding item is selected on the main
+ * hotbar.
+ * {@link MouseEvent#getX()}: The absolute position of the cursor on the X axis
+ * {@link MouseEvent#getY()}: The absolute position of the cursor on the Y axis
+ * {@link MouseEvent#getDx()}: The delta of cursor movement on the X axis
+ * {@link MouseEvent#getDy()}: The delta of cursor movement on the Y axis
+ * {@link MouseEvent#getDwheel()}: The scroll wheel delta:
+ * {@code -120} = scrolling down
+ * {@code 0} = no scrolling
+ * {@code 120} = scrolling up
+ * {@link MouseEvent#getButton()}: Which mouse button is being reported
+ * {@code -1} = no click
+ * {@code 0} = left click
+ * {@code 1} = right click
+ * {@code 2} = middle click
+ * {@link MouseEvent#isButtonstate()}: If the reported mouse button has been pressed down or released:
+ * {@code true} = pressed
+ * {@code false} = unpressed
+ */
+public interface IMouseEventHandler extends IItemComponent {
+
+ /**
+ * Handle a mouse event on the client side.
+ * Try to only act on mouse clicks or scrolls as looking around spams this event.
+ *
+ * @param event the event
+ * @param playerClient the player object on the client side
+ * @param stack the stack the player is holding in their main hand
+ */
+ @SideOnly(Side.CLIENT)
+ void handleMouseEventClient(@NotNull MouseEvent event, @NotNull EntityPlayerSP playerClient,
+ @NotNull EnumHand hand, @NotNull ItemStack stack);
+
+ default void sendToServer(@NotNull EnumHand hand, @NotNull Consumer<@NotNull PacketBuffer> bufferWriter) {
+ PacketItemMouseEvent.toServer(bufferWriter, hand);
+ }
+
+ /**
+ * Handle the received mouse event on the server side.
+ *
+ * @param buf the packet containing the data from the client event
+ * @param playerServer the server side counterpart of the client player
+ * @param stack the stack the player was holding upon receiving the packet
+ */
+ void handleMouseEventServer(@NotNull PacketBuffer buf, @NotNull EntityPlayerMP playerServer,
+ @NotNull EnumHand hand, @NotNull ItemStack stack);
+
+ static @Nullable IMouseEventHandler getHandler(@NotNull ItemStack stack) {
+ Item item = stack.getItem();
+
+ if (item instanceof MetaItem>metaItem) {
+ return metaItem.getMouseEventHandler(stack);
+ } else if (item instanceof IMouseEventHandler itemHandler) {
+ return itemHandler;
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/gregtech/api/items/toolitem/IGTTool.java b/src/main/java/gregtech/api/items/toolitem/IGTTool.java
index d37e8ec3643..b8000b4d7e0 100644
--- a/src/main/java/gregtech/api/items/toolitem/IGTTool.java
+++ b/src/main/java/gregtech/api/items/toolitem/IGTTool.java
@@ -649,7 +649,7 @@ default AoESymmetrical getAoEDefinition(ItemStack stack) {
default double definition$getDurabilityForDisplay(ItemStack stack) {
int damage = stack.getItem().getDamage(stack);
int maxDamage = stack.getItem().getMaxDamage(stack);
- return (double) damage / (double) maxDamage;
+ return GTUtility.calculateDurabilityFromDamageTaken(damage, maxDamage);
}
@Nullable
diff --git a/src/main/java/gregtech/api/items/toolitem/ItemGTToolbelt.java b/src/main/java/gregtech/api/items/toolitem/ItemGTToolbelt.java
index 6f0fd069c92..35eb1dd0fed 100644
--- a/src/main/java/gregtech/api/items/toolitem/ItemGTToolbelt.java
+++ b/src/main/java/gregtech/api/items/toolitem/ItemGTToolbelt.java
@@ -3,6 +3,7 @@
import gregtech.api.GregTechAPI;
import gregtech.api.items.IDyeableItem;
import gregtech.api.items.gui.ItemUIFactory;
+import gregtech.api.items.metaitem.stats.IMouseEventHandler;
import gregtech.api.items.toolitem.behavior.IToolBehavior;
import gregtech.api.metatileentity.MetaTileEntityHolder;
import gregtech.api.mui.GTGuiTextures;
@@ -13,30 +14,38 @@
import gregtech.api.util.LocalizationUtils;
import gregtech.api.util.TextFormattingUtil;
import gregtech.client.utils.TooltipHelper;
-import gregtech.common.items.behaviors.ColorSprayBehaviour;
+import gregtech.common.ConfigHolder;
+import gregtech.common.items.behaviors.spray.AbstractSprayBehavior;
import gregtech.common.metatileentities.multi.multiblockpart.MetaTileEntityMaintenanceHatch;
import gregtech.core.network.packets.PacketToolbeltSelectionChange;
+import gregtech.core.sound.GTSoundEvents;
import net.minecraft.block.state.IBlockState;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.resources.I18n;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.ai.attributes.AttributeModifier;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemTool;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.network.PacketBuffer;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
+import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
+import net.minecraftforge.client.event.MouseEvent;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.INBTSerializable;
@@ -75,7 +84,7 @@
import static gregtech.api.items.toolitem.ToolHelper.MATERIAL_KEY;
-public class ItemGTToolbelt extends ItemGTTool implements IDyeableItem {
+public class ItemGTToolbelt extends ItemGTTool implements IDyeableItem, IMouseEventHandler {
private static final ThreadLocal lastSlot = ThreadLocal.withInitial(() -> -999);
private static final ThreadLocal lastPlayer = ThreadLocal.withInitial(() -> null);
@@ -423,11 +432,37 @@ public ICapabilityProvider initCapabilities(@NotNull ItemStack stack, NBTTagComp
}
@SideOnly(Side.CLIENT)
- public void changeSelectedToolMousewheel(int direction, ItemStack stack) {
- ToolStackHandler handler = getHandler(stack);
- if (direction < 0) handler.incrementSelectedSlot();
- else handler.decrementSelectedSlot();
- PacketToolbeltSelectionChange.toServer(handler.selectedSlot);
+ public void handleMouseEventClient(@NotNull MouseEvent event, @NotNull EntityPlayerSP playerClient,
+ @NotNull EnumHand hand, @NotNull ItemStack stack) {
+ if (!ConfigHolder.client.toolbeltConfig.enableToolbeltScrollingCapture || hand != EnumHand.MAIN_HAND) return;
+ if (event.getDwheel() != 0 && playerClient.isSneaking()) {
+ // vanilla code in GuiIngame line 1235 does not copy the stack before storing it in the highlighting
+ // item stack, so unless we copy the stack the tool highlight will not refresh.
+ ItemStack copy = stack.copy();
+ ToolStackHandler handler = getHandler(copy);
+ if (event.getDwheel() < 0) {
+ handler.incrementSelectedSlot();
+ } else {
+ handler.decrementSelectedSlot();
+ }
+
+ sendToServer(hand, buf -> buf.writeInt(handler.selectedSlot));
+ InventoryPlayer inv = Minecraft.getMinecraft().player.inventory;
+ inv.mainInventory.set(inv.currentItem, stack);
+ event.setCanceled(true);
+ }
+ }
+
+ @Override
+ public void handleMouseEventServer(@NotNull PacketBuffer buf, @NotNull EntityPlayerMP playerServer,
+ @NotNull EnumHand hand, @NotNull ItemStack stack) {
+ // Should never happen, but just in case.
+ if (hand != EnumHand.MAIN_HAND) return;
+ if (stack.getItem() instanceof ItemGTToolbelt toolbelt) {
+ playerServer.getServerWorld().playSound(null, playerServer.posX, playerServer.posY, playerServer.posZ,
+ GTSoundEvents.CLICK, SoundCategory.PLAYERS, 2F, 1F);
+ toolbelt.setSelectedTool(buf.readInt(), stack);
+ }
}
@SideOnly(Side.CLIENT)
@@ -469,34 +504,20 @@ public void setSelectedTool(int slot, ItemStack stack) {
@NotNull BlockPos pos, @NotNull EnumFacing side, float hitX,
float hitY, float hitZ, @NotNull EnumHand hand) {
EnumActionResult result = IDyeableItem.super.onItemUseFirst(player, world, pos, side, hitX, hitY, hitZ, hand);
- if (result == EnumActionResult.PASS) {
- ItemStack stack = player.getHeldItem(hand);
- ToolStackHandler handler = getHandler(stack);
- if (handler.getSelectedStack().isEmpty() &&
- world.getTileEntity(pos) instanceof MetaTileEntityHolder holder &&
- holder.getMetaTileEntity() instanceof MetaTileEntityMaintenanceHatch maintenance) {
- maintenance.fixMaintenanceProblemsWithToolbelt(player, this, stack);
- return EnumActionResult.SUCCESS;
- }
- return super.onItemUseFirst(player, world, pos, side, hitX, hitY, hitZ, hand);
- } else return result;
- }
+ if (result != EnumActionResult.PASS) return result;
- @Override
- public @NotNull EnumActionResult onItemUse(@NotNull EntityPlayer player, @NotNull World world,
- @NotNull BlockPos pos, @NotNull EnumHand hand,
- @NotNull EnumFacing facing, float hitX, float hitY, float hitZ) {
- ToolStackHandler handler = getHandler(player.getHeldItem(hand));
- ItemStack selected = handler.getSelectedStack();
- if (!selected.isEmpty()) {
- ColorSprayBehaviour spray = ColorSprayBehaviour.getBehavior(selected);
- if (spray != null) {
- EnumActionResult result = spray.useFromToolbelt(player, world, pos, hand, facing, hitX, hitY, hitZ,
- selected);
- if (result != EnumActionResult.PASS) return result;
- }
+ ItemStack thisToolBelt = player.getHeldItem(hand);
+ ToolStackHandler handler = getHandler(thisToolBelt);
+ ItemStack selectedToolBeltStack = handler.getSelectedStack();
+ if (selectedToolBeltStack.isEmpty() && world.getTileEntity(pos) instanceof MetaTileEntityHolder holder &&
+ holder.getMetaTileEntity() instanceof MetaTileEntityMaintenanceHatch maintenance) {
+ maintenance.fixMaintenanceProblemsWithToolbelt(player, this, thisToolBelt);
+ return EnumActionResult.SUCCESS;
+ } else if (AbstractSprayBehavior.isSprayCan(selectedToolBeltStack)) {
+ return AbstractSprayBehavior.handleExternalSpray(player, world, pos, side, selectedToolBeltStack);
}
- return super.onItemUse(player, world, pos, hand, facing, hitX, hitY, hitZ);
+
+ return super.onItemUseFirst(player, world, pos, side, hitX, hitY, hitZ, hand);
}
@Override
diff --git a/src/main/java/gregtech/api/items/toolitem/ToolHelper.java b/src/main/java/gregtech/api/items/toolitem/ToolHelper.java
index f696c1684e4..26fe5de6a5c 100644
--- a/src/main/java/gregtech/api/items/toolitem/ToolHelper.java
+++ b/src/main/java/gregtech/api/items/toolitem/ToolHelper.java
@@ -3,8 +3,6 @@
import gregtech.api.GTValues;
import gregtech.api.capability.GregtechCapabilities;
import gregtech.api.capability.IElectricItem;
-import gregtech.api.items.metaitem.MetaItem;
-import gregtech.api.items.metaitem.stats.IItemBehaviour;
import gregtech.api.items.toolitem.aoe.AoESymmetrical;
import gregtech.api.recipes.Recipe;
import gregtech.api.recipes.RecipeMaps;
@@ -17,7 +15,7 @@
import gregtech.api.util.function.QuintFunction;
import gregtech.common.ConfigHolder;
import gregtech.common.items.MetaItems;
-import gregtech.common.items.behaviors.ColorSprayBehaviour;
+import gregtech.common.items.behaviors.spray.AbstractSprayBehavior;
import gregtech.tools.enchants.EnchantmentHardHammer;
import net.minecraft.advancements.CriteriaTriggers;
@@ -374,10 +372,13 @@ public static void onActionDone(@NotNull EntityPlayer player, @NotNull World wor
/**
* @return if any of the specified tool classes exists in the tool
*/
- public static boolean isTool(ItemStack tool, String... toolClasses) {
+ public static boolean isTool(@NotNull ItemStack tool, String... toolClasses) {
+ if (tool.isEmpty()) return false;
+
if (toolClasses.length == 1) {
return tool.getItem().getToolClasses(tool).contains(toolClasses[0]);
}
+
for (String toolClass : tool.getItem().getToolClasses(tool)) {
for (String specified : toolClasses) {
if (toolClass.equals(specified)) {
@@ -385,42 +386,43 @@ public static boolean isTool(ItemStack tool, String... toolClasses) {
}
}
}
+
return false;
}
/**
* @return if the itemstack should be considered a utility item and thus can be put into toolbelts.
*/
- public static boolean isUtilityItem(ItemStack utility) {
- return isTool(utility) || isSpraycan(utility);
+ public static boolean isUtilityItem(@NotNull ItemStack utility) {
+ return isTool(utility) || isSprayCan(utility);
}
/**
* @return if the itemstack should be considered a tool
*/
- public static boolean isTool(ItemStack tool) {
+ public static boolean isTool(@NotNull ItemStack tool) {
+ if (tool.isEmpty()) return false;
return tool.getItem() instanceof ItemTool || tool.getItem() instanceof IGTTool;
}
/**
- * @return if the itemstack should be considered a spraycan
+ * @return if the itemstack should be considered a spray can
*/
- public static boolean isSpraycan(ItemStack spraycan) {
- if (spraycan.getItem() instanceof MetaItem>meta) {
- for (IItemBehaviour behaviour : meta.getBehaviours(spraycan)) {
- if (behaviour instanceof ColorSprayBehaviour) return true;
- }
- }
- return false;
+ public static boolean isSprayCan(@NotNull ItemStack sprayCan) {
+ if (sprayCan.isEmpty()) return false;
+ return AbstractSprayBehavior.getSprayCanBehavior(sprayCan) != null;
}
/**
* Return if all the specified tool classes exists in the tool
*/
- public static boolean areTools(ItemStack tool, String... toolClasses) {
+ public static boolean areTools(@NotNull ItemStack tool, String... toolClasses) {
+ if (tool.isEmpty()) return false;
+
if (toolClasses.length == 1) {
return tool.getItem().getToolClasses(tool).contains(toolClasses[0]);
}
+
return tool.getItem().getToolClasses(tool).containsAll(new ObjectArraySet(toolClasses));
}
diff --git a/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java b/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java
index e3ca9cab534..592f2e94f01 100644
--- a/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java
+++ b/src/main/java/gregtech/api/metatileentity/MetaTileEntity.java
@@ -47,11 +47,13 @@
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.I18n;
import net.minecraft.creativetab.CreativeTabs;
+import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
+import net.minecraft.item.EnumDyeColor;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.PacketBuffer;
@@ -1288,6 +1290,14 @@ public void setFrontFacing(EnumFacing frontFacing) {
}
public void setPaintingColor(int paintingColor) {
+ setPaintingColor(paintingColor, null);
+ }
+
+ public void setPaintingColor(@Nullable EnumDyeColor color, @Nullable EnumFacing side) {
+ setPaintingColor(color == null ? -1 : color.colorValue, side);
+ }
+
+ public void setPaintingColor(int paintingColor, @Nullable EnumFacing side) {
this.paintingColor = paintingColor;
if (getWorld() != null && !getWorld().isRemote) {
notifyBlockUpdate();
@@ -1529,6 +1539,12 @@ public UUID getOwner() {
return owner;
}
+ public boolean canBeModifiedBy(@NotNull Entity entity) {
+ UUID owner = getOwner();
+ if (owner == null) return true;
+ return owner.equals(entity.getUniqueID());
+ }
+
public final void toggleMuffled() {
muffled = !muffled;
if (!getWorld().isRemote) {
diff --git a/src/main/java/gregtech/api/mui/GTGuiTextures.java b/src/main/java/gregtech/api/mui/GTGuiTextures.java
index 998ad9c1d03..33f47b49f72 100644
--- a/src/main/java/gregtech/api/mui/GTGuiTextures.java
+++ b/src/main/java/gregtech/api/mui/GTGuiTextures.java
@@ -708,6 +708,7 @@ private static String id(String path) {
"textures/gui/progress_bar/progress_bar_fluid_rig_depletion.png", 190, 14);
// MISC
+ public static final UITexture RGB_GRADIENT = fullImage("textures/gui/widget/rgb_gradient.png");
public static void init() {/**/}
diff --git a/src/main/java/gregtech/api/mui/drawable/DynamicColorRectangle.java b/src/main/java/gregtech/api/mui/drawable/DynamicColorRectangle.java
new file mode 100644
index 00000000000..4c372647dc6
--- /dev/null
+++ b/src/main/java/gregtech/api/mui/drawable/DynamicColorRectangle.java
@@ -0,0 +1,31 @@
+package gregtech.api.mui.drawable;
+
+import com.cleanroommc.modularui.api.drawable.IDrawable;
+import com.cleanroommc.modularui.drawable.GuiDraw;
+import com.cleanroommc.modularui.screen.viewport.GuiContext;
+import com.cleanroommc.modularui.theme.WidgetTheme;
+import com.cleanroommc.modularui.utils.Color;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.function.IntSupplier;
+
+public class DynamicColorRectangle implements IDrawable {
+
+ @NotNull
+ private final IntSupplier colorSupplier;
+
+ public DynamicColorRectangle(@NotNull IntSupplier colorSupplier) {
+ this.colorSupplier = colorSupplier;
+ }
+
+ @Override
+ public void draw(GuiContext context, int x0, int y0, int width, int height, WidgetTheme widgetTheme) {
+ if (canApplyTheme()) {
+ Color.setGlColor(widgetTheme.getColor());
+ } else {
+ Color.setGlColorOpaque(Color.WHITE.main);
+ }
+
+ GuiDraw.drawRect(x0, y0, width, height, colorSupplier.getAsInt());
+ }
+}
diff --git a/src/main/java/gregtech/api/pipenet/PipeNetWalker.java b/src/main/java/gregtech/api/pipenet/PipeNetWalker.java
index 3b70d1cf3b4..1a39f8a6e4d 100644
--- a/src/main/java/gregtech/api/pipenet/PipeNetWalker.java
+++ b/src/main/java/gregtech/api/pipenet/PipeNetWalker.java
@@ -10,6 +10,7 @@
import net.minecraft.world.World;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
@@ -30,11 +31,13 @@
public abstract class PipeNetWalker> {
protected PipeNetWalker root;
+ @NotNull
private final World world;
private Set walked;
private final List nextPipeFacings = new ArrayList<>(5);
private final List nextPipes = new ArrayList<>(5);
private List> walkers;
+ @NotNull
private final BlockPos.MutableBlockPos currentPos;
private T currentPipe;
private EnumFacing from = null;
@@ -43,7 +46,7 @@ public abstract class PipeNetWalker> {
private boolean running;
private boolean failed = false;
- protected PipeNetWalker(World world, BlockPos sourcePipe, int walkedBlocks) {
+ protected PipeNetWalker(@NotNull World world, @NotNull BlockPos sourcePipe, int walkedBlocks) {
this.world = Objects.requireNonNull(world);
this.walkedBlocks = walkedBlocks;
this.currentPos = new BlockPos.MutableBlockPos(Objects.requireNonNull(sourcePipe));
@@ -189,6 +192,7 @@ private boolean checkPos() {
if (!getBasePipeClass().isAssignableFrom(thisPipe.getClass())) {
return false;
}
+ // noinspection unchecked
currentPipe = (T) thisPipe;
}
T pipeTile = currentPipe;
@@ -203,6 +207,7 @@ private boolean checkPos() {
TileEntity tile = pipeTile.getNeighbor(accessSide);
if (tile != null && getBasePipeClass().isAssignableFrom(tile.getClass())) {
+ // noinspection unchecked
T otherPipe = (T) tile;
if (!otherPipe.isConnected(accessSide.getOpposite()) ||
otherPipe.isFaceBlocked(accessSide.getOpposite()) || isWalked(otherPipe))
@@ -233,11 +238,11 @@ public boolean isRunning() {
return root.running;
}
- public World getWorld() {
+ public @NotNull World getWorld() {
return world;
}
- public BlockPos getCurrentPos() {
+ public @NotNull BlockPos getCurrentPos() {
return currentPos;
}
diff --git a/src/main/java/gregtech/api/pipenet/block/BlockPipe.java b/src/main/java/gregtech/api/pipenet/block/BlockPipe.java
index 24bd0ce911e..b99896e4429 100644
--- a/src/main/java/gregtech/api/pipenet/block/BlockPipe.java
+++ b/src/main/java/gregtech/api/pipenet/block/BlockPipe.java
@@ -17,7 +17,7 @@
import gregtech.common.ConfigHolder;
import gregtech.common.blocks.BlockFrame;
import gregtech.common.blocks.MetaBlocks;
-import gregtech.common.items.MetaItems;
+import gregtech.common.items.behaviors.spray.AbstractSprayBehavior;
import gregtech.integration.ctm.IFacadeWrapper;
import net.minecraft.block.Block;
@@ -80,36 +80,22 @@ public BlockPipe() {
disableStats();
}
- public static Cuboid6 getSideBox(EnumFacing side, float thickness) {
+ public static Cuboid6 getSideBox(@Nullable EnumFacing side, float thickness) {
float min = (1.0f - thickness) / 2.0f, max = min + thickness;
float faceMin = 0f, faceMax = 1f;
if (side == null)
return new Cuboid6(min, min, min, max, max, max);
- Cuboid6 cuboid;
- switch (side) {
- case WEST:
- cuboid = new Cuboid6(faceMin, min, min, min, max, max);
- break;
- case EAST:
- cuboid = new Cuboid6(max, min, min, faceMax, max, max);
- break;
- case NORTH:
- cuboid = new Cuboid6(min, min, faceMin, max, max, min);
- break;
- case SOUTH:
- cuboid = new Cuboid6(min, min, max, max, max, faceMax);
- break;
- case UP:
- cuboid = new Cuboid6(min, max, min, max, faceMax, max);
- break;
- case DOWN:
- cuboid = new Cuboid6(min, faceMin, min, max, min, max);
- break;
- default:
- cuboid = new Cuboid6(min, min, min, max, max, max);
- }
- return cuboid;
+ return switch (side) {
+ case WEST -> new Cuboid6(faceMin, min, min, min, max, max);
+ case EAST -> new Cuboid6(max, min, min, faceMax, max, max);
+ case NORTH -> new Cuboid6(min, min, faceMin, max, max, min);
+ case SOUTH -> new Cuboid6(min, min, max, max, max, faceMax);
+ case UP -> new Cuboid6(min, max, min, max, faceMax, max);
+ case DOWN -> new Cuboid6(min, faceMin, min, max, min, max);
+ // noinspection UnnecessaryDefault
+ default -> new Cuboid6(min, min, min, max, max, max);
+ };
}
/**
@@ -189,15 +175,8 @@ public void onBlockPlacedBy(@NotNull World worldIn, @NotNull BlockPos pos, @NotN
setTileEntityData((TileEntityPipeBase) pipeTile, stack);
// Color pipes/cables on place if holding spray can in off-hand
- if (placer instanceof EntityPlayer) {
- ItemStack offhand = placer.getHeldItemOffhand();
- for (int i = 0; i < EnumDyeColor.values().length; i++) {
- if (offhand.isItemEqual(MetaItems.SPRAY_CAN_DYES[i].getStackForm())) {
- MetaItems.SPRAY_CAN_DYES[i].getBehaviours().get(0).onItemUse((EntityPlayer) placer, worldIn,
- pos, EnumHand.OFF_HAND, EnumFacing.UP, 0, 0, 0);
- break;
- }
- }
+ if (placer instanceof EntityPlayer player) {
+ AbstractSprayBehavior.handleExternalSpray(player, EnumHand.OFF_HAND, worldIn, pos, EnumFacing.UP);
}
}
}
@@ -592,7 +571,7 @@ protected boolean isThisPipeBlock(Block block) {
/**
* Just returns proper pipe tile entity
*/
- public IPipeTile getPipeTileEntity(IBlockAccess world, BlockPos selfPos) {
+ public @Nullable IPipeTile getPipeTileEntity(IBlockAccess world, BlockPos selfPos) {
TileEntity tileEntityAtPos = world.getTileEntity(selfPos);
return getPipeTileEntity(tileEntityAtPos);
}
@@ -664,20 +643,28 @@ private List getCollisionBox(IBlockAccess world, BlockPos pos, @
return result;
}
- public boolean hasPipeCollisionChangingItem(IBlockAccess world, BlockPos pos, Entity entity) {
- if (entity instanceof EntityPlayer) {
- return hasPipeCollisionChangingItem(world, pos, ((EntityPlayer) entity).getHeldItem(EnumHand.MAIN_HAND)) ||
- hasPipeCollisionChangingItem(world, pos, ((EntityPlayer) entity).getHeldItem(EnumHand.OFF_HAND)) ||
- entity.isSneaking() && isHoldingPipe((EntityPlayer) entity);
+ public boolean hasPipeCollisionChangingItem(@NotNull IBlockAccess world, @NotNull BlockPos pos,
+ @Nullable Entity entity) {
+ if (entity instanceof EntityPlayer entityPlayer) {
+ return hasPipeCollisionChangingItem(world, pos, entityPlayer,
+ entityPlayer.getHeldItem(EnumHand.MAIN_HAND)) ||
+ hasPipeCollisionChangingItem(world, pos, entityPlayer,
+ entityPlayer.getHeldItem(EnumHand.OFF_HAND)) ||
+ entity.isSneaking() && isHoldingPipe(entityPlayer);
}
return false;
}
public abstract boolean isHoldingPipe(EntityPlayer player);
- public boolean hasPipeCollisionChangingItem(IBlockAccess world, BlockPos pos, ItemStack stack) {
+ public boolean hasPipeCollisionChangingItem(@NotNull IBlockAccess world, @NotNull BlockPos pos,
+ @NotNull EntityPlayer player, @NotNull ItemStack stack) {
if (isPipeTool(stack)) return true;
+ if (player.isSneaking() && AbstractSprayBehavior.isSprayCan(stack)) {
+ return true;
+ }
+
IPipeTile pipeTile = getPipeTileEntity(world, pos);
if (pipeTile == null) return false;
diff --git a/src/main/java/gregtech/api/pipenet/tile/TileEntityPipeBase.java b/src/main/java/gregtech/api/pipenet/tile/TileEntityPipeBase.java
index bce5904f2dd..afb623ede45 100644
--- a/src/main/java/gregtech/api/pipenet/tile/TileEntityPipeBase.java
+++ b/src/main/java/gregtech/api/pipenet/tile/TileEntityPipeBase.java
@@ -145,7 +145,7 @@ public IPipeTile setSupportsTicking() {
public BlockPipe getPipeBlock() {
if (pipeBlock == null) {
Block block = getBlockState().getBlock();
- // noinspection unchecked
+ // noinspection unchecked,rawtypes
this.pipeBlock = block instanceof BlockPipe blockPipe ? blockPipe : null;
}
return pipeBlock;
@@ -216,20 +216,17 @@ public void setConnection(EnumFacing side, boolean connected, boolean fromNeighb
}
TileEntity tile = getNeighbor(side);
// block connections if Pipe Types do not match
- if (connected &&
- tile instanceof IPipeTile pipeTile &&
+ if (connected && tile instanceof IPipeTile, ?>pipeTile &&
pipeTile.getPipeType().getClass() != this.getPipeType().getClass()) {
return;
}
connections = withSideConnection(connections, side, connected);
updateNetworkConnection(side, connected);
- writeCustomData(UPDATE_CONNECTIONS, buffer -> {
- buffer.writeVarInt(connections);
- });
+ writeCustomData(UPDATE_CONNECTIONS, buffer -> buffer.writeVarInt(connections));
markDirty();
- if (!fromNeighbor && tile instanceof IPipeTile pipeTile) {
+ if (!fromNeighbor && tile instanceof IPipeTile, ?>pipeTile) {
syncPipeConnections(side, pipeTile);
}
}
diff --git a/src/main/java/gregtech/api/unification/material/Material.java b/src/main/java/gregtech/api/unification/material/Material.java
index f258eacef73..81c16ea67a3 100644
--- a/src/main/java/gregtech/api/unification/material/Material.java
+++ b/src/main/java/gregtech/api/unification/material/Material.java
@@ -32,6 +32,7 @@
import gregtech.api.unification.material.registry.MaterialRegistry;
import gregtech.api.unification.ore.OrePrefix;
import gregtech.api.unification.stack.MaterialStack;
+import gregtech.api.util.ColorUtil;
import gregtech.api.util.FluidTooltipUtil;
import gregtech.api.util.GTUtility;
import gregtech.api.util.LocalizationUtils;
@@ -917,7 +918,7 @@ public Builder color(int color) {
*/
public Builder color(@Range(from = 0, to = 255) int r, @Range(from = 0, to = 255) int g,
@Range(from = 0, to = 255) int b) {
- return color(GTUtility.combineRGB(r, g, b));
+ return color(ColorUtil.combineRGB(r, g, b));
}
public Builder colorAverage() {
diff --git a/src/main/java/gregtech/api/util/ColorUtil.java b/src/main/java/gregtech/api/util/ColorUtil.java
new file mode 100644
index 00000000000..993ec3010cb
--- /dev/null
+++ b/src/main/java/gregtech/api/util/ColorUtil.java
@@ -0,0 +1,98 @@
+package gregtech.api.util;
+
+import net.minecraft.item.EnumDyeColor;
+
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.Range;
+
+public class ColorUtil {
+
+ public static final EnumDyeColor[] DYE_COLORS = EnumDyeColor.values();
+
+ public static int combineRGB(@Range(from = 0, to = 255) int r, @Range(from = 0, to = 255) int g,
+ @Range(from = 0, to = 255) int b) {
+ return (r << 16) | (g << 8) | b;
+ }
+
+ public static int combineARGB(@Range(from = 0, to = 255) int a, @Range(from = 0, to = 255) int r,
+ @Range(from = 0, to = 255) int g, @Range(from = 0, to = 255) int b) {
+ return (a << 24) | (r << 16) | (g << 8) | b;
+ }
+
+ public static @Nullable EnumDyeColor getDyeColorFromRGB(int color) {
+ if (color == -1) return null;
+
+ for (EnumDyeColor dyeColor : DYE_COLORS) {
+ if (color == dyeColor.colorValue) {
+ return dyeColor;
+ }
+ }
+
+ return null;
+ }
+
+ public enum ARGBHelper {
+
+ ALPHA(0xFF000000, 24),
+ RED(0xFF0000, 16),
+ GREEN(0xFF00, 8),
+ BLUE(0xFF, 0);
+
+ public final int overlay;
+ public final int invertedOverlay;
+ public final int shift;
+
+ ARGBHelper(int overlay, int shift) {
+ this.overlay = overlay;
+ this.invertedOverlay = ~overlay;
+ this.shift = shift;
+ }
+
+ /**
+ * Isolate this channel as an integer from 0 to 255.
+ * Example: {@code GREEN.isolateAndShift(0xDEADBEEF)} will return {@code 0xBE} or {@code 190}.
+ */
+ public @Range(from = 0, to = 0xFF) int isolateAndShift(int value) {
+ return (value >> shift) & 0xFF;
+ }
+
+ /**
+ * Remove the other two colors from the integer encoded ARGB and set the alpha to 255.
+ * Will always return {@code 0xFF000000} if called on {@link #ALPHA}.
+ * Unlike {@link #isolateAndShift(int)}, this will not be between 0 and 255.
+ * Example: {@code GREEN.isolateWithFullAlpha(0xDEADBEEF)} will return {@code 0xFF00BE00} or {@code -16728576}.
+ */
+ public int isolateWithFullAlpha(int value) {
+ return (value & overlay) | ALPHA.overlay;
+ }
+
+ /**
+ * Set the value of this channel in an integer encoded ARGB value.
+ */
+ public int replace(int originalARGB, @Range(from = 0, to = 0xFF) int value) {
+ return (originalARGB & invertedOverlay) | (value << shift);
+ }
+
+ /**
+ * The same as {@link #replace(int, int)} but will just return the value shifted to this channel.
+ */
+ public int replace(@Range(from = 0, to = 0xFF) int value) {
+ return value << shift;
+ }
+
+ /**
+ * Add a value to this channel's value. Can overflow in this channel, but will not affect the other channels.
+ */
+ public int add(int originalARGB, @Range(from = 0, to = 0xFF) int value) {
+ return replace(originalARGB, (isolateAndShift(originalARGB) + value) & 0xFF);
+ }
+
+ /**
+ * Subtract a value from this channel's value. Can underflow in this channel, but will not affect the other
+ * channels.
+ */
+ public int subtract(int originalARGB, @Range(from = 0, to = 0xFF) int value) {
+ return replace(originalARGB, (isolateAndShift(originalARGB) - value) & 0xFF);
+ }
+ }
+}
diff --git a/src/main/java/gregtech/api/util/GTUtility.java b/src/main/java/gregtech/api/util/GTUtility.java
index 597f8ef6095..9bc3b5a1cf0 100644
--- a/src/main/java/gregtech/api/util/GTUtility.java
+++ b/src/main/java/gregtech/api/util/GTUtility.java
@@ -63,7 +63,6 @@
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import org.jetbrains.annotations.Range;
import java.util.AbstractList;
import java.util.ArrayList;
@@ -460,7 +459,7 @@ public int size() {
};
}
- public static NBTTagCompound getOrCreateNbtCompound(ItemStack stack) {
+ public static @NotNull NBTTagCompound getOrCreateNbtCompound(@NotNull ItemStack stack) {
NBTTagCompound compound = stack.getTagCompound();
if (compound == null) {
compound = new NBTTagCompound();
@@ -756,10 +755,17 @@ public static String lowerUnderscoreToUpperCamel(String string) {
return result.toString();
}
- public static MetaTileEntity getMetaTileEntity(IBlockAccess world, BlockPos pos) {
+ /**
+ * Get the {@link MetaTileEntity} at the position in the given world.
+ *
+ * @param world the world to check
+ * @param pos the position of the mte
+ * @return the mte if there is one at the position, otherwise null
+ */
+ public static @Nullable MetaTileEntity getMetaTileEntity(@Nullable IBlockAccess world, @Nullable BlockPos pos) {
if (world == null || pos == null) return null;
TileEntity te = world.getTileEntity(pos);
- return te instanceof IGregTechTileEntity ? ((IGregTechTileEntity) te).getMetaTileEntity() : null;
+ return te instanceof IGregTechTileEntity gtte ? gtte.getMetaTileEntity() : null;
}
public static MetaTileEntity getMetaTileEntity(ItemStack stack) {
@@ -790,7 +796,7 @@ public static boolean canSeeSunClearly(@NotNull World world, @NotNull BlockPos b
return world.isDaytime();
}
- public static MapColor getMapColor(int rgb) {
+ public static @NotNull MapColor getMapColor(int rgb) {
MapColor color = MapColor.BLACK;
int originalR = (rgb >> 16) & 0xFF;
int originalG = (rgb >> 8) & 0xFF;
@@ -1002,11 +1008,6 @@ public static long scaleVoltage(long voltage, int workingTier) {
return Math.min(voltage, GTValues.VA[workingTier]);
}
- public static int combineRGB(@Range(from = 0, to = 255) int r, @Range(from = 0, to = 255) int g,
- @Range(from = 0, to = 255) int b) {
- return (r << 16) | (g << 8) | b;
- }
-
/**
* @param map the map to get from
* @param key the key to retrieve with
@@ -1024,6 +1025,28 @@ public static int combineRGB(@Range(from = 0, to = 255) int r, @Range(from = 0,
return map.get(key.toWildcard());
}
+ /**
+ * Calculate the durability percentage based on how much damage something has taken
+ *
+ * @param damageTaken how many points of durability damage the item has
+ * @param maxDurability the maximum durability the item can have
+ * @return 0 = full durability, 1 = zero durability
+ */
+ public static double calculateDurabilityFromDamageTaken(int damageTaken, int maxDurability) {
+ return (double) damageTaken / maxDurability;
+ }
+
+ /**
+ * Calculate the durability percentage based on how much durability out of the maximum is remaining
+ *
+ * @param remainingDurability how much durability out of the maximum the item has left
+ * @param maxDurability the maximum durability the item can have
+ * @return 0 = full durability, 1 = zero durability
+ */
+ public static double calculateDurabilityFromRemaining(int remainingDurability, int maxDurability) {
+ return (double) (maxDurability - remainingDurability) / maxDurability;
+ }
+
public static boolean areFluidStacksEqual(@Nullable FluidStack a, @Nullable FluidStack b) {
if (a == b) return true;
if (a == null) return false;
diff --git a/src/main/java/gregtech/client/ClientProxy.java b/src/main/java/gregtech/client/ClientProxy.java
index f9b712309a2..4150dd68120 100644
--- a/src/main/java/gregtech/client/ClientProxy.java
+++ b/src/main/java/gregtech/client/ClientProxy.java
@@ -3,6 +3,7 @@
import gregtech.api.GTValues;
import gregtech.api.fluids.GTFluidRegistration;
import gregtech.api.items.metaitem.MetaOreDictItem;
+import gregtech.api.items.metaitem.stats.IMouseEventHandler;
import gregtech.api.items.toolitem.IGTTool;
import gregtech.api.items.toolitem.ItemGTToolbelt;
import gregtech.api.unification.OreDictUnifier;
@@ -46,7 +47,6 @@
import net.minecraft.client.resources.I18n;
import net.minecraft.client.resources.SimpleReloadableResourceManager;
import net.minecraft.entity.player.EntityPlayer;
-import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.init.Items;
import net.minecraft.inventory.ContainerPlayer;
import net.minecraft.inventory.ContainerWorkbench;
@@ -55,6 +55,7 @@
import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.EnumHand;
import net.minecraft.util.text.TextFormatting;
import net.minecraftforge.client.GuiIngameForge;
import net.minecraftforge.client.event.ModelBakeEvent;
@@ -352,19 +353,20 @@ public boolean isFancyGraphics() {
@SubscribeEvent(priority = EventPriority.HIGH)
public static void onMouseEvent(@NotNull MouseEvent event) {
- if (!ConfigHolder.client.toolbeltConfig.enableToolbeltScrollingCapture) return;
EntityPlayerSP player = Minecraft.getMinecraft().player;
- if (event.getDwheel() != 0 && player.isSneaking()) {
- ItemStack stack = player.getHeldItemMainhand();
- if (stack.getItem() instanceof ItemGTToolbelt toolbelt) {
- // vanilla code in GuiIngame line 1235 does not copy the stack before storing it in the highlighting
- // item stack, so unless we copy the stack the tool highlight will not refresh.
- stack = stack.copy();
- toolbelt.changeSelectedToolMousewheel(event.getDwheel(), stack);
- InventoryPlayer inv = Minecraft.getMinecraft().player.inventory;
- inv.mainInventory.set(inv.currentItem, stack);
- event.setCanceled(true);
- }
+
+ handleItemEvent(event, player, EnumHand.MAIN_HAND);
+ if (!event.isCanceled()) {
+ handleItemEvent(event, player, EnumHand.OFF_HAND);
+ }
+ }
+
+ private static void handleItemEvent(@NotNull MouseEvent event, @NotNull EntityPlayerSP playerClient,
+ @NotNull EnumHand hand) {
+ ItemStack heldStack = playerClient.getHeldItem(hand);
+ IMouseEventHandler mouseEventHandler = IMouseEventHandler.getHandler(heldStack);
+ if (mouseEventHandler != null) {
+ mouseEventHandler.handleMouseEventClient(event, playerClient, hand, heldStack);
}
}
diff --git a/src/main/java/gregtech/common/ConfigHolder.java b/src/main/java/gregtech/common/ConfigHolder.java
index 93cbb1dbdcf..a3c55b53384 100644
--- a/src/main/java/gregtech/common/ConfigHolder.java
+++ b/src/main/java/gregtech/common/ConfigHolder.java
@@ -717,6 +717,10 @@ public static class ToolOptions {
@Config.RangeInt(min = 1, max = 100)
@Config.SlidingOption
public int magnetDelay = 10;
+
+ @Config.Comment({ "The maximum amount of pipes a recursive spray can do at once", "Default: 128 blocks" })
+ @Config.RangeInt(min = 1, max = 1024)
+ public int maxRecursiveSprayLength = 128;
}
public static class ArmorHud {
diff --git a/src/main/java/gregtech/common/ToolEventHandlers.java b/src/main/java/gregtech/common/ToolEventHandlers.java
index e503b539547..7f5db87a4ae 100644
--- a/src/main/java/gregtech/common/ToolEventHandlers.java
+++ b/src/main/java/gregtech/common/ToolEventHandlers.java
@@ -19,6 +19,7 @@
import gregtech.api.pipenet.tile.TileEntityPipeBase;
import gregtech.api.util.GTUtility;
import gregtech.api.util.TaskScheduler;
+import gregtech.common.items.behaviors.spray.AbstractSprayBehavior;
import gregtech.common.items.tool.rotation.CustomBlockRotations;
import gregtech.common.items.tool.rotation.ICustomRotationBehavior;
@@ -303,10 +304,12 @@ private static void postRenderDamagedBlocks() {
@SideOnly(Side.CLIENT)
private static boolean shouldRenderGridOverlays(@NotNull IBlockState state, @Nullable TileEntity tile,
- ItemStack mainHand, ItemStack offHand, boolean isSneaking) {
+ @NotNull ItemStack mainHand, @NotNull ItemStack offHand,
+ boolean isSneaking) {
if (state.getBlock() instanceof BlockPipe, ?, ?>pipe) {
if (isSneaking &&
- (mainHand.isEmpty() || mainHand.getItem().getClass() == Item.getItemFromBlock(pipe).getClass())) {
+ (mainHand.isEmpty() || mainHand.getItem().getClass() == Item.getItemFromBlock(pipe).getClass() ||
+ AbstractSprayBehavior.isSprayCan(mainHand) || AbstractSprayBehavior.isSprayCan(offHand))) {
return true;
} else {
Set mainToolClasses = mainHand.getItem().getToolClasses(mainHand);
diff --git a/src/main/java/gregtech/common/items/MetaItem1.java b/src/main/java/gregtech/common/items/MetaItem1.java
index b528f3c1041..e10a06c201c 100644
--- a/src/main/java/gregtech/common/items/MetaItem1.java
+++ b/src/main/java/gregtech/common/items/MetaItem1.java
@@ -34,7 +34,6 @@
import gregtech.common.creativetab.GTCreativeTabs;
import gregtech.common.entities.GTBoatEntity.GTBoatType;
import gregtech.common.items.behaviors.ClipboardBehavior;
-import gregtech.common.items.behaviors.ColorSprayBehaviour;
import gregtech.common.items.behaviors.DataItemBehavior;
import gregtech.common.items.behaviors.DoorBehavior;
import gregtech.common.items.behaviors.DynamiteBehaviour;
@@ -59,6 +58,8 @@
import gregtech.common.items.behaviors.monitorplugin.FakeGuiPluginBehavior;
import gregtech.common.items.behaviors.monitorplugin.OnlinePicPluginBehavior;
import gregtech.common.items.behaviors.monitorplugin.TextPluginBehavior;
+import gregtech.common.items.behaviors.spray.CreativeSprayBehavior;
+import gregtech.common.items.behaviors.spray.DurabilitySprayBehavior;
import gregtech.core.sound.GTSoundEvents;
import net.minecraft.client.resources.I18n;
@@ -89,17 +90,27 @@ public void getSubItems(@NotNull CreativeTabs tab, @NotNull NonNullList.MetaValueItem item : metaItems.values()) {
if (!item.isInCreativeTab(tab)) continue;
- int itemMetaData = item.getMetaValue();
- if (itemMetaData >= 1006 && itemMetaData <= 1010) continue;
+ int itemMeta = item.getMetaValue();
+ // Skip extra molds, see below
+ if (itemMeta >= 1006 && itemMeta <= 1010) continue;
+ // Skip creative spray can, see below
+ if (itemMeta == 30) continue;
item.getSubItemHandler().getSubItems(item.getStackForm(), tab, subItems);
- if (itemMetaData == 29) {
+ // Add the extra molds after the last 'original'
+ if (itemMeta == 29) {
for (MetaItem>.MetaValueItem moldItem : SHAPE_MOLDS) {
+ // Skip the 'original' molds
if (moldItem.getMetaValue() < 1006) continue;
moldItem.getSubItemHandler().getSubItems(moldItem.getStackForm(), tab, subItems);
}
}
+
+ // Add the creative spray can after the last normal spray can
+ if (itemMeta == 77) {
+ subItems.add(SPRAY_CREATIVE.getStackForm());
+ }
}
}
@@ -167,7 +178,8 @@ public void registerSubItems() {
SHAPE_MOLDS[17] = SHAPE_MOLD_ROUND = addItem(29, "shape.mold.round")
.setRecyclingData(new RecyclingData(new MaterialStack(Materials.Steel, M * 4)));
- // Free ID: 30
+ SPRAY_CREATIVE = addItem(30, "spray.creative")
+ .addComponents(new CreativeSprayBehavior());
// Extruder Shapes: ID 31-59
SHAPE_EXTRUDERS[0] = SHAPE_EXTRUDER_PLATE = addItem(31, "shape.extruder.plate")
@@ -215,14 +227,14 @@ public void registerSubItems() {
// out of registry order so it can reference the Empty Spray Can
SPRAY_SOLVENT = addItem(60, "spray.solvent").setMaxStackSize(1)
- .addComponents(new ColorSprayBehaviour(SPRAY_EMPTY.getStackForm(), 1024, -1))
+ .addComponents(new DurabilitySprayBehavior(SPRAY_EMPTY.getStackForm(), 1024, null))
.setCreativeTabs(GTCreativeTabs.TAB_GREGTECH_TOOLS);
- for (int i = 0; i < EnumDyeColor.values().length; i++) {
- SPRAY_CAN_DYES[i] = addItem(62 + i, "spray.can.dyes." + EnumDyeColor.values()[i].getName())
+ for (EnumDyeColor color : EnumDyeColor.values()) {
+ SPRAY_CAN_DYES.put(color, addItem(62 + color.ordinal(), "spray.can.dyes." + color.getName())
.setMaxStackSize(1)
- .addComponents(new ColorSprayBehaviour(SPRAY_EMPTY.getStackForm(), 512, i))
- .setCreativeTabs(GTCreativeTabs.TAB_GREGTECH_TOOLS);
+ .addComponents(new DurabilitySprayBehavior(SPRAY_EMPTY.getStackForm(), 512, color))
+ .setCreativeTabs(GTCreativeTabs.TAB_GREGTECH_TOOLS));
}
// Fluid Cells: ID 78-88
diff --git a/src/main/java/gregtech/common/items/MetaItems.java b/src/main/java/gregtech/common/items/MetaItems.java
index 23c49bb279f..176a8eaa83a 100644
--- a/src/main/java/gregtech/common/items/MetaItems.java
+++ b/src/main/java/gregtech/common/items/MetaItems.java
@@ -104,6 +104,7 @@ private MetaItems() {}
public static MetaItem>.MetaValueItem SPRAY_SOLVENT;
public static MetaItem>.MetaValueItem SPRAY_EMPTY;
+ public static MetaItem>.MetaValueItem SPRAY_CREATIVE;
public static MetaItem>.MetaValueItem FLUID_CELL;
public static MetaItem>.MetaValueItem FLUID_CELL_UNIVERSAL;
@@ -515,8 +516,8 @@ private MetaItems() {}
public static final MetaItem>.MetaValueItem[] DYE_ONLY_ITEMS = new MetaItem.MetaValueItem[EnumDyeColor
.values().length];
- public static final MetaItem>.MetaValueItem[] SPRAY_CAN_DYES = new MetaItem.MetaValueItem[EnumDyeColor
- .values().length];
+ public static final EnumMap.MetaValueItem> SPRAY_CAN_DYES = new EnumMap<>(
+ EnumDyeColor.class);
public static MetaItem>.MetaValueItem TURBINE_ROTOR;
diff --git a/src/main/java/gregtech/common/items/armor/PowerlessJetpack.java b/src/main/java/gregtech/common/items/armor/PowerlessJetpack.java
index 17a87b8ca16..a507583db53 100644
--- a/src/main/java/gregtech/common/items/armor/PowerlessJetpack.java
+++ b/src/main/java/gregtech/common/items/armor/PowerlessJetpack.java
@@ -283,10 +283,11 @@ public Behaviour(int internalCapacity) {
public double getDurabilityForDisplay(@NotNull ItemStack itemStack) {
IFluidHandlerItem fluidHandlerItem = itemStack
.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null);
- if (fluidHandlerItem == null) return 0;
+ if (fluidHandlerItem == null) return 1.0d;
IFluidTankProperties fluidTankProperties = fluidHandlerItem.getTankProperties()[0];
FluidStack fluidStack = fluidTankProperties.getContents();
- return fluidStack == null ? 0 : (double) fluidStack.amount / (double) fluidTankProperties.getCapacity();
+ return fluidStack == null ? 1.0d :
+ GTUtility.calculateDurabilityFromRemaining(fluidStack.amount, fluidTankProperties.getCapacity());
}
@Nullable
diff --git a/src/main/java/gregtech/common/items/behaviors/AbstractMaterialPartBehavior.java b/src/main/java/gregtech/common/items/behaviors/AbstractMaterialPartBehavior.java
index a4abe287529..67b4d671a18 100644
--- a/src/main/java/gregtech/common/items/behaviors/AbstractMaterialPartBehavior.java
+++ b/src/main/java/gregtech/common/items/behaviors/AbstractMaterialPartBehavior.java
@@ -8,6 +8,7 @@
import gregtech.api.unification.material.Material;
import gregtech.api.unification.material.Materials;
import gregtech.api.unification.material.properties.PropertyKey;
+import gregtech.api.util.GTUtility;
import gregtech.api.util.LocalizationUtils;
import net.minecraft.client.resources.I18n;
@@ -89,7 +90,6 @@ public int getItemStackColor(ItemStack itemStack, int tintIndex) {
@Override
public double getDurabilityForDisplay(ItemStack itemStack) {
- int maxDurability = getPartMaxDurability(itemStack);
- return (double) (maxDurability - getPartDamage(itemStack)) / (double) maxDurability;
+ return GTUtility.calculateDurabilityFromDamageTaken(getPartDamage(itemStack), getPartMaxDurability(itemStack));
}
}
diff --git a/src/main/java/gregtech/common/items/behaviors/AbstractUsableBehaviour.java b/src/main/java/gregtech/common/items/behaviors/AbstractUsableBehaviour.java
deleted file mode 100644
index 1b7528c0c59..00000000000
--- a/src/main/java/gregtech/common/items/behaviors/AbstractUsableBehaviour.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package gregtech.common.items.behaviors;
-
-import gregtech.api.items.metaitem.stats.IItemBehaviour;
-
-import net.minecraft.entity.player.EntityPlayer;
-import net.minecraft.item.ItemStack;
-import net.minecraft.nbt.NBTTagCompound;
-import net.minecraft.util.EnumHand;
-import net.minecraftforge.common.util.Constants.NBT;
-
-public class AbstractUsableBehaviour implements IItemBehaviour {
-
- public final int totalUses;
-
- public AbstractUsableBehaviour(int totalUses) {
- this.totalUses = totalUses;
- }
-
- public boolean useItemDurability(EntityPlayer player, EnumHand hand, ItemStack stack, ItemStack replacementStack) {
- int usesLeft = getUsesLeft(stack);
- if (!player.capabilities.isCreativeMode) {
- if (--usesLeft <= 0) {
- if (replacementStack.isEmpty()) {
- // if replacement stack is empty, just shrink resulting stack
- stack.shrink(1);
- } else {
- // otherwise, update held item to replacement stack
- player.setHeldItem(hand, replacementStack);
- }
- return true;
- }
- setUsesLeft(stack, usesLeft);
- }
- return true;
- }
-
- public final int getUsesLeft(ItemStack stack) {
- NBTTagCompound tagCompound = stack.getTagCompound();
- if (tagCompound == null || !tagCompound.hasKey("GT.UsesLeft", NBT.TAG_INT))
- return totalUses;
- return tagCompound.getInteger("GT.UsesLeft");
- }
-
- public static void setUsesLeft(ItemStack itemStack, int usesLeft) {
- NBTTagCompound tagCompound = itemStack.getTagCompound();
- if (tagCompound == null) {
- tagCompound = new NBTTagCompound();
- itemStack.setTagCompound(tagCompound);
- }
- tagCompound.setInteger("GT.UsesLeft", usesLeft);
- }
-}
diff --git a/src/main/java/gregtech/common/items/behaviors/ColorSprayBehaviour.java b/src/main/java/gregtech/common/items/behaviors/ColorSprayBehaviour.java
deleted file mode 100644
index 72d164afd19..00000000000
--- a/src/main/java/gregtech/common/items/behaviors/ColorSprayBehaviour.java
+++ /dev/null
@@ -1,240 +0,0 @@
-package gregtech.common.items.behaviors;
-
-import gregtech.api.items.metaitem.MetaItem;
-import gregtech.api.items.metaitem.stats.IItemBehaviour;
-import gregtech.api.items.metaitem.stats.IItemDurabilityManager;
-import gregtech.api.metatileentity.MetaTileEntity;
-import gregtech.api.metatileentity.interfaces.IGregTechTileEntity;
-import gregtech.api.pipenet.tile.IPipeTile;
-import gregtech.api.util.GradientUtil;
-import gregtech.api.util.Mods;
-import gregtech.core.sound.GTSoundEvents;
-
-import net.minecraft.block.Block;
-import net.minecraft.block.BlockColored;
-import net.minecraft.block.BlockStainedGlass;
-import net.minecraft.block.BlockStainedGlassPane;
-import net.minecraft.block.properties.IProperty;
-import net.minecraft.block.state.IBlockState;
-import net.minecraft.client.resources.I18n;
-import net.minecraft.entity.player.EntityPlayer;
-import net.minecraft.init.Blocks;
-import net.minecraft.item.EnumDyeColor;
-import net.minecraft.item.ItemStack;
-import net.minecraft.tileentity.TileEntity;
-import net.minecraft.util.ActionResult;
-import net.minecraft.util.EnumActionResult;
-import net.minecraft.util.EnumFacing;
-import net.minecraft.util.EnumHand;
-import net.minecraft.util.SoundCategory;
-import net.minecraft.util.math.BlockPos;
-import net.minecraft.world.World;
-
-import appeng.api.util.AEColor;
-import appeng.tile.networking.TileCableBus;
-import org.apache.commons.lang3.tuple.Pair;
-import org.jetbrains.annotations.Nullable;
-
-import java.awt.*;
-import java.util.List;
-
-public class ColorSprayBehaviour extends AbstractUsableBehaviour implements IItemDurabilityManager {
-
- private final ItemStack empty;
- private final EnumDyeColor color;
- private final Pair durabilityBarColors;
-
- public ColorSprayBehaviour(ItemStack empty, int totalUses, int color) {
- super(totalUses);
- this.empty = empty;
- EnumDyeColor[] colors = EnumDyeColor.values();
- this.color = color >= colors.length || color < 0 ? null : colors[color];
- // default to a gray color if this.color is null (like for solvent spray)
- int colorValue = this.color == null ? 0x969696 : this.color.colorValue;
- this.durabilityBarColors = GradientUtil.getGradient(colorValue, 10);
- }
-
- @Override
- public ActionResult onItemUse(EntityPlayer player, World world, BlockPos pos, EnumHand hand,
- EnumFacing facing, float hitX, float hitY, float hitZ) {
- ItemStack stack = player.getHeldItem(hand);
- if (!player.canPlayerEdit(pos, facing, stack)) {
- return ActionResult.newResult(EnumActionResult.FAIL, player.getHeldItem(hand));
- }
- if (!tryPaintBlock(player, world, pos, facing)) {
- return ActionResult.newResult(EnumActionResult.PASS, player.getHeldItem(hand));
- }
- useItemDurability(player, hand, stack, empty.copy());
- world.playSound(null, player.posX, player.posY, player.posZ, GTSoundEvents.SPRAY_CAN_TOOL,
- SoundCategory.PLAYERS, 1.0f, 1.0f);
- return ActionResult.newResult(EnumActionResult.SUCCESS, player.getHeldItem(hand));
- }
-
- @Nullable
- public static ColorSprayBehaviour getBehavior(ItemStack spraycan) {
- if (!(spraycan.getItem() instanceof MetaItem>meta)) return null;
- for (IItemBehaviour behaviour : meta.getBehaviours(spraycan)) {
- if (behaviour instanceof ColorSprayBehaviour spray) return spray;
- }
- return null;
- }
-
- public EnumActionResult useFromToolbelt(EntityPlayer player, World world, BlockPos pos, EnumHand hand,
- EnumFacing facing, float hitX, float hitY, float hitZ, ItemStack spraycan) {
- if (!player.canPlayerEdit(pos, facing, spraycan)) {
- return EnumActionResult.FAIL;
- }
- if (!tryPaintBlock(player, world, pos, facing)) {
- return EnumActionResult.PASS;
- }
- useItemDurability(player, hand, spraycan, empty.copy());
- world.playSound(null, player.posX, player.posY, player.posZ, GTSoundEvents.SPRAY_CAN_TOOL,
- SoundCategory.PLAYERS, 1.0f, 1.0f);
- return EnumActionResult.SUCCESS;
- }
-
- private boolean tryPaintBlock(EntityPlayer player, World world, BlockPos pos, EnumFacing side) {
- IBlockState blockState = world.getBlockState(pos);
- Block block = blockState.getBlock();
- if (color == null) {
- return tryStripBlockColor(player, world, pos, block, side);
- }
- return block.recolorBlock(world, pos, side, this.color) || tryPaintSpecialBlock(player, world, pos, block);
- }
-
- private boolean tryPaintSpecialBlock(EntityPlayer player, World world, BlockPos pos, Block block) {
- if (block == Blocks.GLASS) {
- IBlockState newBlockState = Blocks.STAINED_GLASS.getDefaultState()
- .withProperty(BlockStainedGlass.COLOR, this.color);
- world.setBlockState(pos, newBlockState);
- return true;
- }
- if (block == Blocks.GLASS_PANE) {
- IBlockState newBlockState = Blocks.STAINED_GLASS_PANE.getDefaultState()
- .withProperty(BlockStainedGlassPane.COLOR, this.color);
- world.setBlockState(pos, newBlockState);
- return true;
- }
- if (block == Blocks.HARDENED_CLAY) {
- IBlockState newBlockState = Blocks.STAINED_HARDENED_CLAY.getDefaultState()
- .withProperty(BlockColored.COLOR, this.color);
- world.setBlockState(pos, newBlockState);
- return true;
- }
- if (Mods.AppliedEnergistics2.isModLoaded()) {
- TileEntity te = world.getTileEntity(pos);
- if (te instanceof TileCableBus) {
- TileCableBus cable = (TileCableBus) te;
- // do not try to recolor if it already is this color
- if (cable.getColor().ordinal() != color.ordinal()) {
- cable.recolourBlock(null, AEColor.values()[color.ordinal()], player);
- return true;
- }
- }
- }
- return false;
- }
-
- @SuppressWarnings("unchecked, rawtypes")
- private static boolean tryStripBlockColor(EntityPlayer player, World world, BlockPos pos, Block block,
- EnumFacing side) {
- // MC special cases
- if (block == Blocks.STAINED_GLASS) {
- world.setBlockState(pos, Blocks.GLASS.getDefaultState());
- return true;
- }
- if (block == Blocks.STAINED_GLASS_PANE) {
- world.setBlockState(pos, Blocks.GLASS_PANE.getDefaultState());
- return true;
- }
- if (block == Blocks.STAINED_HARDENED_CLAY) {
- world.setBlockState(pos, Blocks.HARDENED_CLAY.getDefaultState());
- return true;
- }
-
- // MTE special case
- TileEntity te = world.getTileEntity(pos);
- if (te instanceof IGregTechTileEntity) {
- MetaTileEntity mte = ((IGregTechTileEntity) te).getMetaTileEntity();
- if (mte != null) {
- if (mte.isPainted()) {
- mte.setPaintingColor(-1);
- return true;
- } else return false;
- }
- }
-
- // TileEntityPipeBase special case
- if (te instanceof IPipeTile) {
- IPipeTile, ?> pipe = (IPipeTile, ?>) te;
- if (pipe.isPainted()) {
- pipe.setPaintingColor(-1);
- return true;
- } else return false;
- }
-
- // AE2 cable special case
- if (Mods.AppliedEnergistics2.isModLoaded()) {
- if (te instanceof TileCableBus) {
- TileCableBus cable = (TileCableBus) te;
- // do not try to strip color if it is already colorless
- if (cable.getColor() != AEColor.TRANSPARENT) {
- cable.recolourBlock(null, AEColor.TRANSPARENT, player);
- return true;
- } else return false;
- }
- }
-
- // General case
- IBlockState state = world.getBlockState(pos);
- for (IProperty prop : state.getProperties().keySet()) {
- if (prop.getName().equals("color") && prop.getValueClass() == EnumDyeColor.class) {
- IBlockState defaultState = block.getDefaultState();
- EnumDyeColor defaultColor = EnumDyeColor.WHITE;
- try {
- // try to read the default color value from the default state instead of just
- // blindly setting it to default state, and potentially resetting other values
- defaultColor = (EnumDyeColor) defaultState.getValue(prop);
- } catch (IllegalArgumentException ignored) {
- // no default color, we may have to fallback to WHITE here
- // other mods that have custom behavior can be done as
- // special cases above on a case-by-case basis
- }
- block.recolorBlock(world, pos, side, defaultColor);
- return true;
- }
- }
-
- return false;
- }
-
- @Override
- public void addInformation(ItemStack itemStack, List lines) {
- int remainingUses = getUsesLeft(itemStack);
- if (color != null) {
- lines.add(I18n.format("behaviour.paintspray." + this.color.getTranslationKey() + ".tooltip"));
- } else {
- lines.add(I18n.format("behaviour.paintspray.solvent.tooltip"));
- }
- lines.add(I18n.format("behaviour.paintspray.uses", remainingUses));
- if (color != null) {
- lines.add(I18n.format("behaviour.paintspray.offhand"));
- }
- }
-
- @Override
- public double getDurabilityForDisplay(ItemStack itemStack) {
- return (double) getUsesLeft(itemStack) / totalUses;
- }
-
- @Nullable
- @Override
- public Pair getDurabilityColorsForDisplay(ItemStack itemStack) {
- return durabilityBarColors;
- }
-
- @Override
- public boolean doDamagedStateColors(ItemStack itemStack) {
- return false;
- }
-}
diff --git a/src/main/java/gregtech/common/items/behaviors/FoamSprayerBehavior.java b/src/main/java/gregtech/common/items/behaviors/FoamSprayerBehavior.java
index 7a059220e34..2af2697c925 100644
--- a/src/main/java/gregtech/common/items/behaviors/FoamSprayerBehavior.java
+++ b/src/main/java/gregtech/common/items/behaviors/FoamSprayerBehavior.java
@@ -8,6 +8,7 @@
import gregtech.api.items.metaitem.stats.ISubItemHandler;
import gregtech.api.recipes.ModHandler;
import gregtech.api.unification.material.Materials;
+import gregtech.api.util.GTUtility;
import gregtech.api.util.GradientUtil;
import gregtech.common.blocks.BlockFrame;
import gregtech.common.blocks.MetaBlocks;
@@ -90,7 +91,8 @@ public double getDurabilityForDisplay(ItemStack itemStack) {
if (fluidHandlerItem == null) return 0;
IFluidTankProperties fluidTankProperties = fluidHandlerItem.getTankProperties()[0];
FluidStack fluidStack = fluidTankProperties.getContents();
- return fluidStack == null ? 0 : (double) fluidStack.amount / (double) fluidTankProperties.getCapacity();
+ return fluidStack == null ? 0 :
+ GTUtility.calculateDurabilityFromRemaining(fluidStack.amount, fluidTankProperties.getCapacity());
}
@Nullable
diff --git a/src/main/java/gregtech/common/items/behaviors/LighterBehaviour.java b/src/main/java/gregtech/common/items/behaviors/LighterBehaviour.java
index 66941091218..579c899e31b 100644
--- a/src/main/java/gregtech/common/items/behaviors/LighterBehaviour.java
+++ b/src/main/java/gregtech/common/items/behaviors/LighterBehaviour.java
@@ -227,13 +227,14 @@ public double getDurabilityForDisplay(ItemStack itemStack) {
IFluidHandlerItem fluidHandlerItem = itemStack
.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null);
if (fluidHandlerItem == null) return 0.0;
- IFluidTankProperties properties = fluidHandlerItem.getTankProperties()[0];
- FluidStack fluidStack = properties.getContents();
- return fluidStack == null ? 0.0 : (double) fluidStack.amount / (double) properties.getCapacity();
+ IFluidTankProperties fluidTankProperties = fluidHandlerItem.getTankProperties()[0];
+ FluidStack fluidStack = fluidTankProperties.getContents();
+ return fluidStack == null ? 0.0 :
+ GTUtility.calculateDurabilityFromRemaining(fluidStack.amount, fluidTankProperties.getCapacity());
}
if (hasMultipleUses) {
// Matchbox
- return (double) getUsesLeft(itemStack) / (double) maxUses;
+ return GTUtility.calculateDurabilityFromRemaining(getUsesLeft(itemStack), maxUses);
}
// no durability for Match
return 0.0;
diff --git a/src/main/java/gregtech/common/items/behaviors/spray/AbstractSprayBehavior.java b/src/main/java/gregtech/common/items/behaviors/spray/AbstractSprayBehavior.java
new file mode 100644
index 00000000000..3eb7b37fbe2
--- /dev/null
+++ b/src/main/java/gregtech/common/items/behaviors/spray/AbstractSprayBehavior.java
@@ -0,0 +1,242 @@
+package gregtech.common.items.behaviors.spray;
+
+import gregtech.api.color.ColorMode;
+import gregtech.api.color.ColorModeSupport;
+import gregtech.api.color.ColoredBlockContainer;
+import gregtech.api.cover.CoverRayTracer;
+import gregtech.api.items.metaitem.MetaItem;
+import gregtech.api.items.metaitem.stats.IItemBehaviour;
+import gregtech.api.pipenet.block.BlockPipe;
+import gregtech.api.pipenet.tile.IPipeTile;
+import gregtech.common.ConfigHolder;
+import gregtech.core.sound.GTSoundEvents;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.EnumDyeColor;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.EnumActionResult;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.EnumHand;
+import net.minecraft.util.SoundCategory;
+import net.minecraft.util.SoundEvent;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.RayTraceResult;
+import net.minecraft.world.World;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.Range;
+
+public abstract class AbstractSprayBehavior implements IItemBehaviour {
+
+ /**
+ * Get the color of the spray can.
+ * {@code null} = solvent
+ */
+ public abstract @Nullable EnumDyeColor getColor(@NotNull ItemStack sprayCan);
+
+ public int getColorInt(@NotNull ItemStack sprayCan) {
+ EnumDyeColor color = getColor(sprayCan);
+ return color == null ? -1 : color.colorValue;
+ }
+
+ public @Range(from = -1, to = 15) int getColorOrdinal(@NotNull ItemStack sprayCan) {
+ EnumDyeColor color = getColor(sprayCan);
+ return color == null ? -1 : color.ordinal();
+ }
+
+ @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+ public boolean canSpray(@NotNull ItemStack sprayCan) {
+ return true;
+ }
+
+ public void onSpray(@NotNull EntityPlayer player, @NotNull ItemStack sprayCan) {
+ //
+ }
+
+ public boolean hasSpraySound(@NotNull ItemStack sprayCan) {
+ return true;
+ }
+
+ public @NotNull SoundEvent getSpraySound(@NotNull ItemStack sprayCan) {
+ return GTSoundEvents.SPRAY_CAN_TOOL;
+ }
+
+ public int getMaximumSprayLength(@NotNull ItemStack sprayCan) {
+ return ConfigHolder.tools.maxRecursiveSprayLength;
+ }
+
+ public static @Nullable AbstractSprayBehavior getSprayCanBehavior(@NotNull ItemStack sprayCan) {
+ if (!(sprayCan.getItem() instanceof MetaItem>metaItem)) return null;
+
+ for (IItemBehaviour behaviour : metaItem.getBehaviours(sprayCan)) {
+ if (behaviour instanceof AbstractSprayBehavior sprayBehavior) {
+ return sprayBehavior;
+ }
+ }
+
+ return null;
+ }
+
+ public static boolean isSprayCan(@NotNull ItemStack stack) {
+ return getSprayCanBehavior(stack) != null;
+ }
+
+ /**
+ * Call from your items
+ * {@link Item#onItemUseFirst(EntityPlayer, World, BlockPos, EnumFacing, float, float, float, EnumHand)}
+ * or the meta item equivalent to check if block is sprayable early enough in the click handling chain.
+ */
+ @SuppressWarnings("UnusedReturnValue")
+ public static @NotNull EnumActionResult handleExternalSpray(@NotNull EntityPlayer player, @NotNull EnumHand hand,
+ @NotNull World world, @NotNull BlockPos pos,
+ @NotNull EnumFacing facing) {
+ return handleExternalSpray(player, world, pos, facing, player.getHeldItem(hand));
+ }
+
+ /**
+ * Call from your items
+ * {@link Item#onItemUseFirst(EntityPlayer, World, BlockPos, EnumFacing, float, float, float, EnumHand)}
+ * or the meta item equivalent to check if block is sprayable early enough in the click handling chain.
+ */
+ public static @NotNull EnumActionResult handleExternalSpray(@NotNull EntityPlayer player,
+ @NotNull World world, @NotNull BlockPos pos,
+ @NotNull EnumFacing facing,
+ @NotNull ItemStack sprayCan) {
+ AbstractSprayBehavior sprayBehavior = getSprayCanBehavior(sprayCan);
+ if (sprayBehavior == null) {
+ return EnumActionResult.PASS;
+ } else {
+ return sprayBehavior.spray(player, world, pos, facing, sprayCan);
+ }
+ }
+
+ @Override
+ public EnumActionResult onItemUseFirst(EntityPlayer player, World world, BlockPos pos, EnumFacing side, float hitX,
+ float hitY, float hitZ, EnumHand hand) {
+ ItemStack sprayCan = player.getHeldItem(hand);
+ EnumActionResult result = spray(player, world, pos, side, sprayCan);
+ if (hasSpraySound(sprayCan) && result == EnumActionResult.SUCCESS) {
+ world.playSound(null, player.posX, player.posY, player.posZ, getSpraySound(sprayCan), SoundCategory.PLAYERS,
+ 1.0f, 1.0f);
+ }
+ return result;
+ }
+
+ @SuppressWarnings("ConstantValue")
+ protected @NotNull EnumActionResult spray(@NotNull EntityPlayer player, @NotNull World world, @NotNull BlockPos pos,
+ @NotNull EnumFacing facing, @NotNull ItemStack sprayCan) {
+ if (!canSpray(sprayCan)) {
+ return EnumActionResult.PASS;
+ } else if (!player.canPlayerEdit(pos, facing, sprayCan)) {
+ return EnumActionResult.FAIL;
+ }
+
+ if (player.isSneaking()) {
+ if (world.getBlockState(pos).getBlock() instanceof BlockPipe, ?, ?>blockPipe) {
+ RayTraceResult hitResult = blockPipe.getServerCollisionRayTrace(player, pos, world);
+ if (hitResult != null) {
+ EnumFacing hitSide = CoverRayTracer.determineGridSideHit(hitResult);
+ IPipeTile, ?> firstPipe = blockPipe.getPipeTileEntity(world, pos);
+ int color = getColorInt(sprayCan);
+ if (hitSide != null && firstPipe != null && firstPipe.isConnected(hitSide) &&
+ (firstPipe.isPainted() ? firstPipe.getPaintingColor() != color : color != -1)) {
+ if (world.isRemote) return EnumActionResult.SUCCESS;
+ traversePipes(firstPipe, hitSide, player, sprayCan, color);
+ return EnumActionResult.SUCCESS;
+ }
+ }
+ }
+ }
+
+ ColoredBlockContainer colorContainer = ColoredBlockContainer.getContainer(world, pos, facing, player);
+ if (colorContainer == null) {
+ return EnumActionResult.PASS;
+ }
+
+ ColorModeSupport containerColorMode = colorContainer.getSupportedColorMode();
+ ColorMode sprayColorMode = getColorMode(sprayCan);
+ if (!containerColorMode.supportsMode(sprayColorMode)) {
+ if (!world.isRemote) {
+ player.sendStatusMessage(containerColorMode.getErrorText(), true);
+ }
+
+ return EnumActionResult.FAIL;
+ }
+
+ return switch (sprayColorMode) {
+ case DYE -> colorContainer.setColor(world, pos, facing, player, getColor(sprayCan));
+ case ARGB -> colorContainer.setColor(world, pos, facing, player, getColorInt(sprayCan));
+ case PREFER_DYE -> {
+ EnumActionResult result = null;
+ if (containerColorMode.supportsMode(ColorMode.DYE)) {
+ result = colorContainer.setColor(world, pos, facing, player, getColor(sprayCan));
+ } else if (result != EnumActionResult.SUCCESS && containerColorMode.supportsMode(ColorMode.ARGB)) {
+ result = colorContainer.setColor(world, pos, facing, player, getColorInt(sprayCan));
+ } else if (result == null) {
+ throw new IllegalStateException(
+ "Container mode didn't support either color mode, this shouldn't be possible!");
+ }
+
+ yield result;
+ }
+ case PREFER_ARGB -> {
+ EnumActionResult result = null;
+ if (containerColorMode.supportsMode(ColorMode.ARGB)) {
+ result = colorContainer.setColor(world, pos, facing, player, getColorInt(sprayCan));
+ } else if (result != EnumActionResult.SUCCESS && containerColorMode.supportsMode(ColorMode.DYE)) {
+ result = colorContainer.setColor(world, pos, facing, player, getColor(sprayCan));
+ } else if (result == null) {
+ throw new IllegalStateException(
+ "Container mode didn't support either color mode, this shouldn't be possible!");
+ }
+
+ yield result;
+ }
+ };
+ }
+
+ public abstract @NotNull ColorMode getColorMode(@NotNull ItemStack sprayCan);
+
+ protected void traversePipes(@NotNull IPipeTile, ?> pipeTile, @NotNull EnumFacing facing,
+ @NotNull EntityPlayer player, @NotNull ItemStack sprayCan, int color) {
+ if (canPipeBePainted(pipeTile, color) && pipeTile.getNeighbor(facing) instanceof IPipeTile, ?>nextPipe) {
+ pipeTile.setPaintingColor(color);
+ onSpray(player, sprayCan);
+ pipeTile = nextPipe;
+ } else {
+ return;
+ }
+
+ for (int count = 1; count < getMaximumSprayLength(sprayCan) && canSpray(sprayCan); count++) {
+ if (canPipeBePainted(pipeTile, color)) {
+ pipeTile.setPaintingColor(color);
+ onSpray(player, sprayCan);
+ } else {
+ break;
+ }
+
+ if (pipeTile.getNumConnections() == 2) {
+ int connections = pipeTile.getConnections();
+ connections &= ~(1 << facing.getOpposite().getIndex());
+ for (EnumFacing other : EnumFacing.VALUES) {
+ if ((connections & (1 << other.getIndex())) != 0) {
+ facing = other;
+ if (pipeTile.getNeighbor(facing) instanceof IPipeTile, ?>neighboringPipe) {
+ pipeTile = neighboringPipe;
+ } else {
+ break;
+ }
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ }
+
+ private static boolean canPipeBePainted(@NotNull IPipeTile, ?> pipeTile, int color) {
+ return pipeTile.isPainted() ? pipeTile.getPaintingColor() != color : color != -1;
+ }
+}
diff --git a/src/main/java/gregtech/common/items/behaviors/spray/CreativeSprayBehavior.java b/src/main/java/gregtech/common/items/behaviors/spray/CreativeSprayBehavior.java
new file mode 100644
index 00000000000..2b29aef47ca
--- /dev/null
+++ b/src/main/java/gregtech/common/items/behaviors/spray/CreativeSprayBehavior.java
@@ -0,0 +1,331 @@
+package gregtech.common.items.behaviors.spray;
+
+import gregtech.api.color.ColorMode;
+import gregtech.api.color.ColoredBlockContainer;
+import gregtech.api.items.gui.ItemUIFactory;
+import gregtech.api.items.metaitem.stats.IItemColorProvider;
+import gregtech.api.items.metaitem.stats.IItemNameProvider;
+import gregtech.api.items.metaitem.stats.IMouseEventHandler;
+import gregtech.api.mui.GTGuiTextures;
+import gregtech.api.mui.GTGuis;
+import gregtech.api.mui.drawable.DynamicColorRectangle;
+import gregtech.api.mui.factory.MetaItemGuiFactory;
+import gregtech.api.util.GTUtility;
+import gregtech.common.items.MetaItems;
+
+import net.minecraft.client.entity.EntityPlayerSP;
+import net.minecraft.client.resources.I18n;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.EnumDyeColor;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.network.PacketBuffer;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.EnumHand;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.RayTraceResult;
+import net.minecraft.world.World;
+import net.minecraftforge.client.event.MouseEvent;
+import net.minecraftforge.common.util.Constants;
+import net.minecraftforge.fml.relauncher.Side;
+import net.minecraftforge.fml.relauncher.SideOnly;
+
+import codechicken.lib.raytracer.RayTracer;
+import com.cleanroommc.modularui.api.drawable.IKey;
+import com.cleanroommc.modularui.drawable.GuiTextures;
+import com.cleanroommc.modularui.drawable.ItemDrawable;
+import com.cleanroommc.modularui.factory.HandGuiData;
+import com.cleanroommc.modularui.screen.ModularPanel;
+import com.cleanroommc.modularui.screen.UISettings;
+import com.cleanroommc.modularui.value.BoolValue;
+import com.cleanroommc.modularui.value.DoubleValue;
+import com.cleanroommc.modularui.value.IntValue;
+import com.cleanroommc.modularui.value.sync.BooleanSyncValue;
+import com.cleanroommc.modularui.value.sync.IntSyncValue;
+import com.cleanroommc.modularui.value.sync.PanelSyncManager;
+import com.cleanroommc.modularui.value.sync.SyncHandlers;
+import com.cleanroommc.modularui.widgets.PageButton;
+import com.cleanroommc.modularui.widgets.PagedWidget;
+import com.cleanroommc.modularui.widgets.SliderWidget;
+import com.cleanroommc.modularui.widgets.SlotGroupWidget;
+import com.cleanroommc.modularui.widgets.ToggleButton;
+import com.cleanroommc.modularui.widgets.layout.Flow;
+import com.cleanroommc.modularui.widgets.textfield.TextFieldWidget;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.function.BooleanSupplier;
+
+import static gregtech.api.util.ColorUtil.*;
+
+public class CreativeSprayBehavior extends AbstractSprayBehavior implements ItemUIFactory, IItemColorProvider,
+ IItemNameProvider, IMouseEventHandler {
+
+ private static final String KEY_COLOR = "color";
+ private static final String KEY_USES_RGB = "usesRGB";
+ private static final String KEY_RGB_COLOR = "rgbColor";
+
+ @Override
+ public ModularPanel buildUI(HandGuiData guiData, PanelSyncManager guiSyncManager, UISettings settings) {
+ ItemStack usedStack = guiData.getUsedItemStack();
+ IntSyncValue colorSync = SyncHandlers.intNumber(() -> getColorOrdinal(usedStack),
+ newColor -> setColorOrdinal(usedStack, newColor));
+ guiSyncManager.syncValue(KEY_COLOR, 0, colorSync);
+ BooleanSyncValue usesRGBSync = SyncHandlers.bool(() -> usesRGB(usedStack), bool -> useRGB(usedStack, bool));
+ guiSyncManager.syncValue(KEY_USES_RGB, 0, usesRGBSync);
+ // Doesn't use getColorInt because the slider widgets take a moment to update so it ended up showing the normal
+ // can colors when you switched to RGB mode.
+ IntSyncValue rgbColorSync = SyncHandlers.intNumber(
+ () -> GTUtility.getOrCreateNbtCompound(usedStack).getInteger(KEY_RGB_COLOR),
+ newColor -> setColor(usedStack, newColor));
+ guiSyncManager.syncValue(KEY_RGB_COLOR, 0, rgbColorSync);
+
+ PagedWidget.Controller pageController = new PagedWidget.Controller();
+
+ return GTGuis.createPanel(usedStack, 176, 95)
+ .child(Flow.row()
+ .widthRel(1.0f)
+ .leftRel(0.5f)
+ .margin(3, 0)
+ .coverChildrenHeight()
+ .topRel(0.0f, 3, 1.0f)
+ .child(new PageButton(0, pageController)
+ .tab(GuiTextures.TAB_TOP, 0)
+ .overlay(new ItemDrawable(MetaItems.SPRAY_EMPTY.getStackForm())
+ .asIcon()
+ .size(16))
+ .addTooltipLine(IKey.lang("metaitem.spray.creative.mode.normal")))
+ .child(new PageButton(1, pageController)
+ .tab(GuiTextures.TAB_TOP, 0)
+ .overlay(GTGuiTextures.RGB_GRADIENT.asIcon()
+ .size(16))
+ .addTooltipLine(IKey.lang("metaitem.spray.creative.mode.rgb"))))
+ .child(IKey.lang("metaitem.spray.creative.name_base")
+ .asWidget()
+ .left(7)
+ .top(7))
+ .child(new PagedWidget<>()
+ .margin(7, 7, 22, 7)
+ .widthRel(1.0f)
+ .heightRel(1.0f)
+ .controller(pageController)
+ .onPageChange(usesRGBSync::setIntValue)
+ .initialPage(usesRGBSync.getIntValue())
+ .addPage(SlotGroupWidget.builder()
+ .matrix("SCCCCCCCC",
+ "CCCCCCCC")
+ .key('S', new ToggleButton()
+ .size(18)
+ .value(new BoolValue.Dynamic(
+ () -> colorSync.getIntValue() == -1 && !usesRGBSync.getBoolValue(),
+ $ -> {
+ if (!usesRGBSync.getBoolValue()) colorSync.setIntValue(-1);
+ }))
+ .overlay(new ItemDrawable(MetaItems.SPRAY_SOLVENT.getStackForm())
+ .asIcon()
+ .size(16))
+ .addTooltipLine(IKey.lang("metaitem.spray.creative.solvent")))
+ .key('C', index -> {
+ EnumDyeColor color = EnumDyeColor.values()[index];
+ return new ToggleButton()
+ .size(18)
+ .value(new BoolValue.Dynamic(
+ () -> colorSync.getIntValue() == index &&
+ !usesRGBSync.getBoolValue(),
+ $ -> {
+ if (!usesRGBSync.getBoolValue()) colorSync.setIntValue(index);
+ }))
+ .overlay(
+ new ItemDrawable(MetaItems.SPRAY_CAN_DYES.get(color).getStackForm())
+ .asIcon()
+ .size(16))
+ .addTooltipLine(IKey.lang("metaitem.spray.creative." + color));
+ })
+ .build()
+ .alignY(0.5f))
+ .addPage(Flow.column()
+ .widthRel(1.0f)
+ .heightRel(1.0f)
+ .child(createColorRow(ARGBHelper.RED, rgbColorSync, usesRGBSync::getBoolValue))
+ .child(createColorRow(ARGBHelper.GREEN, rgbColorSync, usesRGBSync::getBoolValue))
+ .child(createColorRow(ARGBHelper.BLUE, rgbColorSync, usesRGBSync::getBoolValue))));
+ }
+
+ protected static Flow createColorRow(@NotNull ARGBHelper helper, @NotNull IntSyncValue rgbColorSync,
+ @NotNull BooleanSupplier allowSetting) {
+ return Flow.row()
+ .widthRel(1.0f)
+ .coverChildrenHeight()
+ .child(new TextFieldWidget()
+ .width(30)
+ .setNumbers(0, 255)
+ .value(new IntValue.Dynamic(() -> helper.isolateAndShift(rgbColorSync.getIntValue()),
+ colorDigit -> {
+ if (allowSetting.getAsBoolean()) {
+ int newColor = helper.replace(rgbColorSync.getIntValue(), colorDigit);
+ rgbColorSync.setIntValue(newColor);
+ }
+ })))
+ .child(new SliderWidget()
+ .width(132)
+ .bounds(0.0D, 255.0d)
+ .value(new DoubleValue.Dynamic(
+ () -> (double) helper.isolateAndShift(rgbColorSync.getIntValue()),
+ colorDigit -> {
+ if (allowSetting.getAsBoolean()) {
+ int newColor = helper.replace(rgbColorSync.getIntValue(), (int) colorDigit);
+ rgbColorSync.setIntValue(newColor);
+ }
+ }))
+ .background(
+ new DynamicColorRectangle(() -> helper.isolateWithFullAlpha(rgbColorSync.getIntValue()))
+ .asIcon()
+ .margin(4, 0)
+ .height(8))
+ .addTooltipLine(IKey.lang("metaitem.spray.creative.tip." + helper.toString().toLowerCase())));
+ }
+
+ @Override
+ public @Nullable EnumDyeColor getColor(@NotNull ItemStack sprayCan) {
+ NBTTagCompound tag = GTUtility.getOrCreateNbtCompound(sprayCan);
+ if (tag.hasKey(KEY_COLOR, Constants.NBT.TAG_INT)) {
+ int color = tag.getInteger(KEY_COLOR);
+ if (color < 0 || color > 15) return null;
+ return EnumDyeColor.values()[color];
+ }
+
+ return null;
+ }
+
+ @Override
+ public int getColorInt(@NotNull ItemStack sprayCan) {
+ NBTTagCompound tag = GTUtility.getOrCreateNbtCompound(sprayCan);
+ return tag.getBoolean(KEY_USES_RGB) ? tag.getInteger(KEY_RGB_COLOR) : super.getColorInt(sprayCan);
+ }
+
+ @Override
+ public @NotNull ColorMode getColorMode(@NotNull ItemStack sprayCan) {
+ return usesRGB(sprayCan) ? ColorMode.ARGB : ColorMode.DYE;
+ }
+
+ public void setColor(@NotNull ItemStack sprayCan, @Nullable EnumDyeColor color) {
+ GTUtility.getOrCreateNbtCompound(sprayCan).setInteger(KEY_COLOR, color == null ? -1 : color.ordinal());
+ }
+
+ public void setColorOrdinal(@NotNull ItemStack sprayCan, int ordinal) {
+ GTUtility.getOrCreateNbtCompound(sprayCan).setInteger(KEY_COLOR,
+ ordinal >= 0 && ordinal <= 15 ? ordinal : -1);
+ }
+
+ public void setColor(@NotNull ItemStack sprayCan, int argbColor) {
+ GTUtility.getOrCreateNbtCompound(sprayCan).setInteger(KEY_RGB_COLOR, argbColor);
+ }
+
+ public boolean usesRGB(@NotNull ItemStack sprayCan) {
+ return GTUtility.getOrCreateNbtCompound(sprayCan).getBoolean(KEY_USES_RGB);
+ }
+
+ public void useRGB(@NotNull ItemStack sprayCan, boolean bool) {
+ GTUtility.getOrCreateNbtCompound(sprayCan).setBoolean(KEY_USES_RGB, bool);
+ }
+
+ @Override
+ public int getItemStackColor(ItemStack sprayCan, int tintIndex) {
+ return tintIndex == 1 ? getColorInt(sprayCan) : 0xFFFFFF;
+ }
+
+ @Override
+ public String getItemStackDisplayName(ItemStack sprayCan, String unlocalizedName) {
+ String colorString;
+ if (usesRGB(sprayCan)) {
+ colorString = String.format("0x%06X", getColorInt(sprayCan) & 0xFFFFFF);
+ } else {
+ EnumDyeColor color = getColor(sprayCan);
+ colorString = color == null ? I18n.format("metaitem.spray.creative.solvent") :
+ I18n.format("metaitem.spray.creative." + color);
+ }
+
+ return I18n.format(unlocalizedName, colorString);
+ }
+
+ @SideOnly(Side.CLIENT)
+ @Override
+ public void handleMouseEventClient(@NotNull MouseEvent event, @NotNull EntityPlayerSP playerClient,
+ @NotNull EnumHand hand, @NotNull ItemStack sprayCan) {
+ // Middle click pressed down
+ if (event.getButton() == 2 && event.isButtonstate()) {
+ event.setCanceled(true);
+ if (tryCopyColor(playerClient, hand, sprayCan)) return;
+
+ // If the player wasn't looking at a colored block, open gui
+ sendToServer(hand, buf -> buf.writeByte(0));
+ }
+ }
+
+ protected boolean tryCopyColor(@NotNull EntityPlayerSP playerClient, @NotNull EnumHand hand,
+ @NotNull ItemStack sprayCan) {
+ RayTraceResult rayTrace = RayTracer.retrace(playerClient);
+ if (rayTrace == null || rayTrace.typeOfHit != RayTraceResult.Type.BLOCK) return false;
+
+ World world = playerClient.world;
+ BlockPos pos = rayTrace.getBlockPos();
+ EnumFacing facing = rayTrace.sideHit;
+ ColoredBlockContainer container = ColoredBlockContainer.getContainer(world, pos, facing, playerClient);
+ if (container == null) return false;
+
+ return switch (getColorMode(sprayCan)) {
+ case DYE, PREFER_DYE -> {
+ if (tryCopyDyeColor(container, world, pos, facing, playerClient, hand, sprayCan)) {
+ yield true;
+ }
+
+ yield tryCopyARGBColor(container, world, pos, facing, playerClient, hand, sprayCan);
+ }
+ case ARGB, PREFER_ARGB -> {
+ if (tryCopyARGBColor(container, world, pos, facing, playerClient, hand, sprayCan)) {
+ yield true;
+ }
+
+ yield tryCopyDyeColor(container, world, pos, facing, playerClient, hand, sprayCan);
+ }
+ };
+ }
+
+ protected boolean tryCopyDyeColor(@NotNull ColoredBlockContainer container, @NotNull World world,
+ @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayerSP playerClient, @NotNull EnumHand hand,
+ @NotNull ItemStack sprayCan) {
+ EnumDyeColor blockColor = container.getColor(world, pos, facing, playerClient);
+ if (blockColor == null || blockColor == getColor(sprayCan)) return false;
+
+ setColor(sprayCan, blockColor);
+ sendToServer(hand, buf -> buf
+ .writeByte(2)
+ .writeByte(blockColor.ordinal()));
+ return true;
+ }
+
+ protected boolean tryCopyARGBColor(@NotNull ColoredBlockContainer container, @NotNull World world,
+ @NotNull BlockPos pos, @NotNull EnumFacing facing,
+ @NotNull EntityPlayerSP playerClient, @NotNull EnumHand hand,
+ @NotNull ItemStack sprayCan) {
+ int blockColor = container.getColorInt(world, pos, facing, playerClient);
+ if (blockColor == -1 || blockColor == getColorInt(sprayCan)) return false;
+
+ setColor(sprayCan, blockColor);
+ sendToServer(hand, buf -> buf
+ .writeByte(1)
+ .writeInt(blockColor));
+ return true;
+ }
+
+ @Override
+ public void handleMouseEventServer(@NotNull PacketBuffer buf, @NotNull EntityPlayerMP playerServer,
+ @NotNull EnumHand hand, @NotNull ItemStack sprayCan) {
+ switch (buf.readByte()) {
+ case 0 -> MetaItemGuiFactory.open(playerServer, EnumHand.MAIN_HAND);
+ case 1 -> setColor(sprayCan, buf.readInt());
+ case 2 -> setColor(sprayCan, EnumDyeColor.values()[buf.readByte()]);
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/items/behaviors/spray/DurabilitySprayBehavior.java b/src/main/java/gregtech/common/items/behaviors/spray/DurabilitySprayBehavior.java
new file mode 100644
index 00000000000..3b63fb8a0b8
--- /dev/null
+++ b/src/main/java/gregtech/common/items/behaviors/spray/DurabilitySprayBehavior.java
@@ -0,0 +1,135 @@
+package gregtech.common.items.behaviors.spray;
+
+import gregtech.api.color.ColorMode;
+import gregtech.api.items.metaitem.stats.IItemDurabilityManager;
+import gregtech.api.util.GTUtility;
+import gregtech.api.util.GradientUtil;
+
+import net.minecraft.client.resources.I18n;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.init.SoundEvents;
+import net.minecraft.item.EnumDyeColor;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.Constants;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.awt.*;
+import java.util.List;
+
+public class DurabilitySprayBehavior extends AbstractSprayBehavior implements IItemDurabilityManager {
+
+ public static final String NBT_KEY = "GT.UsesLeft";
+
+ @Nullable
+ private final EnumDyeColor color;
+ @NotNull
+ private final Pair durabilityBarColors;
+ @NotNull
+ private final ItemStack replacementStack;
+ public final int maxUses;
+
+ public DurabilitySprayBehavior(@NotNull ItemStack replacementStack, int maxUses, @Nullable EnumDyeColor color) {
+ this.color = color;
+ this.replacementStack = replacementStack;
+ this.maxUses = maxUses;
+ this.durabilityBarColors = GradientUtil.getGradient(color == null ? 0x969696 : color.colorValue, 10);
+ }
+
+ public DurabilitySprayBehavior(int maxUses, @Nullable EnumDyeColor color) {
+ this(ItemStack.EMPTY, maxUses, color);
+ }
+
+ @Override
+ public boolean canSpray(@NotNull ItemStack stack) {
+ return getUsesLeft(stack) > 0;
+ }
+
+ @Override
+ public void onSpray(@NotNull EntityPlayer player, @NotNull ItemStack sprayCan) {
+ if (player.capabilities.isCreativeMode) return;
+
+ if (damageCan(sprayCan)) {
+ if (replacementStack.isEmpty()) {
+ // If replacement stack is empty, just shrink resulting stack
+ sprayCan.shrink(1);
+ } else {
+ // Update held item to replacement stack
+ sprayCan.setItemDamage(replacementStack.getItemDamage());
+ // Clear NBT from old can
+ sprayCan.setTagCompound(null);
+ // Play sound manually since we aren't using player.setHeldItem(...)
+ player.playSound(SoundEvents.ITEM_ARMOR_EQUIP_GENERIC, 1.0f, 1.0f);
+ }
+ }
+ }
+
+ protected boolean damageCan(@NotNull ItemStack sprayCan) {
+ int usesLeft = getUsesLeft(sprayCan) - 1;
+ setUsesLeft(sprayCan, usesLeft);
+ return usesLeft == 0;
+ }
+
+ @Override
+ public @Nullable EnumDyeColor getColor(@NotNull ItemStack stack) {
+ return this.color;
+ }
+
+ @Override
+ public @NotNull ColorMode getColorMode(@NotNull ItemStack sprayCan) {
+ return ColorMode.DYE;
+ }
+
+ protected int getUsesLeft(@NotNull ItemStack stack) {
+ if (stack.isItemEqual(replacementStack)) return 0;
+
+ NBTTagCompound tagCompound = GTUtility.getOrCreateNbtCompound(stack);
+ if (!tagCompound.hasKey(NBT_KEY, Constants.NBT.TAG_INT)) {
+ tagCompound.setInteger(NBT_KEY, maxUses);
+ return maxUses;
+ }
+
+ return tagCompound.getInteger(NBT_KEY);
+ }
+
+ protected static void setUsesLeft(@NotNull ItemStack itemStack, int usesLeft) {
+ GTUtility.getOrCreateNbtCompound(itemStack).setInteger(NBT_KEY, usesLeft);
+ }
+
+ @Override
+ public void addInformation(ItemStack itemStack, List lines) {
+ int remainingUses = getUsesLeft(itemStack);
+ EnumDyeColor color = getColor(itemStack);
+
+ if (color != null) {
+ lines.add(I18n.format("behaviour.paintspray." + color.getTranslationKey() + ".tooltip"));
+ } else {
+ lines.add(I18n.format("behaviour.paintspray.solvent.tooltip"));
+ }
+
+ lines.add(I18n.format("behaviour.paintspray.uses", remainingUses));
+
+ if (color != null) {
+ lines.add(I18n.format("behaviour.paintspray.offhand"));
+ }
+ }
+
+ @Override
+ public double getDurabilityForDisplay(ItemStack itemStack) {
+ return GTUtility.calculateDurabilityFromRemaining(getUsesLeft(itemStack), maxUses);
+ }
+
+ @Nullable
+ @Override
+ public Pair getDurabilityColorsForDisplay(ItemStack itemStack) {
+ return durabilityBarColors;
+ }
+
+ @Override
+ public boolean doDamagedStateColors(ItemStack itemStack) {
+ return false;
+ }
+}
diff --git a/src/main/java/gregtech/core/CoreModule.java b/src/main/java/gregtech/core/CoreModule.java
index f4121bb3ecb..9a1ba311e03 100644
--- a/src/main/java/gregtech/core/CoreModule.java
+++ b/src/main/java/gregtech/core/CoreModule.java
@@ -6,6 +6,7 @@
import gregtech.api.block.IHeatingCoilBlockStats;
import gregtech.api.block.coil.CoilManager;
import gregtech.api.capability.SimpleCapabilityManager;
+import gregtech.api.color.ColoredBlockContainer;
import gregtech.api.cover.CoverDefinition;
import gregtech.api.cover.CoverUIFactory;
import gregtech.api.fluids.GTFluidRegistration;
@@ -62,6 +63,7 @@
import gregtech.core.network.packets.PacketClipboardNBTUpdate;
import gregtech.core.network.packets.PacketClipboardUIWidgetUpdate;
import gregtech.core.network.packets.PacketFluidVeinList;
+import gregtech.core.network.packets.PacketItemMouseEvent;
import gregtech.core.network.packets.PacketKeysPressed;
import gregtech.core.network.packets.PacketNotifyCapeChange;
import gregtech.core.network.packets.PacketPluginSynced;
@@ -134,7 +136,7 @@ public Logger getLogger() {
}
@Override
- public void preInit(FMLPreInitializationEvent event) {
+ public void preInit(@NotNull FMLPreInitializationEvent event) {
GregTechAPIInternal.preInit();
GregTechAPI.advancementManager = AdvancementManager.getInstance();
AdvancementTriggers.register();
@@ -252,10 +254,11 @@ public void registerPackets() {
GregTechAPI.networkHandler.registerPacket(PacketClipboardNBTUpdate.class);
GregTechAPI.networkHandler.registerPacket(PacketToolbeltSelectionChange.Server.class);
GregTechAPI.networkHandler.registerPacket(PacketToolbeltSelectionChange.Client.class);
+ GregTechAPI.networkHandler.registerPacket(PacketItemMouseEvent.class);
}
@Override
- public void init(FMLInitializationEvent event) {
+ public void init(@NotNull FMLInitializationEvent event) {
// freeze once addon preInit is finished
for (MTERegistry registry : mteManager.getRegistries()) {
registry.freeze();
@@ -294,7 +297,7 @@ public void init(FMLInitializationEvent event) {
}
@Override
- public void postInit(FMLPostInitializationEvent event) {
+ public void postInit(@NotNull FMLPostInitializationEvent event) {
proxy.onPostLoad();
BedrockFluidVeinHandler.recalculateChances(true);
// registers coil types for the BlastTemperatureProperty used in Blast Furnace Recipes
@@ -309,15 +312,16 @@ public void postInit(FMLPostInitializationEvent event) {
}
ModHandler.postInit();
+ ColoredBlockContainer.registerCEuContainers();
}
@Override
- public void loadComplete(FMLLoadCompleteEvent event) {
+ public void loadComplete(@NotNull FMLLoadCompleteEvent event) {
proxy.onLoadComplete();
}
@Override
- public void serverStarting(FMLServerStartingEvent event) {
+ public void serverStarting(@NotNull FMLServerStartingEvent event) {
CommandManager commandManager = CommandManager.getInstance();
GregTechAPI.commandManager = commandManager;
commandManager.registerServerCommand(event);
@@ -336,7 +340,7 @@ public void serverStarting(FMLServerStartingEvent event) {
}
@Override
- public void serverStarted(FMLServerStartedEvent event) {
+ public void serverStarted(@NotNull FMLServerStartedEvent event) {
if (FMLCommonHandler.instance().getEffectiveSide() == Side.SERVER) {
World world = FMLCommonHandler.instance().getMinecraftServerInstance().getEntityWorld();
if (!world.isRemote) {
@@ -354,7 +358,7 @@ public void serverStarted(FMLServerStartedEvent event) {
}
@Override
- public void serverStopped(FMLServerStoppedEvent event) {
+ public void serverStopped(@NotNull FMLServerStoppedEvent event) {
VirtualEnderRegistry.clearMaps();
CapesRegistry.clearMaps();
}
diff --git a/src/main/java/gregtech/core/network/packets/PacketItemMouseEvent.java b/src/main/java/gregtech/core/network/packets/PacketItemMouseEvent.java
new file mode 100644
index 00000000000..1a346846c76
--- /dev/null
+++ b/src/main/java/gregtech/core/network/packets/PacketItemMouseEvent.java
@@ -0,0 +1,60 @@
+package gregtech.core.network.packets;
+
+import gregtech.api.GregTechAPI;
+import gregtech.api.items.metaitem.stats.IMouseEventHandler;
+import gregtech.api.network.IPacket;
+import gregtech.api.network.IServerExecutor;
+
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.network.NetHandlerPlayServer;
+import net.minecraft.network.PacketBuffer;
+import net.minecraft.util.EnumHand;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.function.Consumer;
+
+public class PacketItemMouseEvent implements IPacket, IServerExecutor {
+
+ @NotNull
+ private final ByteBuf internalBuffer;
+
+ public PacketItemMouseEvent() {
+ this.internalBuffer = Unpooled.buffer();
+ }
+
+ @Override
+ public void encode(PacketBuffer buf) {
+ buf.writeBytes(internalBuffer);
+ }
+
+ @Override
+ public void decode(PacketBuffer buf) {
+ internalBuffer.writeBytes(buf);
+ }
+
+ public static void toServer(@NotNull Consumer<@NotNull PacketBuffer> bufferWriter, @NotNull EnumHand hand) {
+ PacketItemMouseEvent packet = new PacketItemMouseEvent();
+ PacketBuffer buf = new PacketBuffer(packet.internalBuffer);
+ buf.writeByte(hand.ordinal());
+ bufferWriter.accept(buf);
+
+ GregTechAPI.networkHandler.sendToServer(packet);
+ }
+
+ @Override
+ public void executeServer(NetHandlerPlayServer handler) {
+ EntityPlayerMP player = handler.player;
+ EnumHand hand = EnumHand.values()[internalBuffer.readByte()];
+ ItemStack stack = player.getHeldItem(hand);
+
+ IMouseEventHandler mouseEventHandler = IMouseEventHandler.getHandler(stack);
+ if (mouseEventHandler != null) {
+ mouseEventHandler.handleMouseEventServer(new PacketBuffer(internalBuffer.asReadOnly()), player, hand,
+ stack);
+ }
+ }
+}
diff --git a/src/main/java/gregtech/loaders/recipe/MachineRecipeLoader.java b/src/main/java/gregtech/loaders/recipe/MachineRecipeLoader.java
index 065e864dfd0..c6fb33bc9dc 100644
--- a/src/main/java/gregtech/loaders/recipe/MachineRecipeLoader.java
+++ b/src/main/java/gregtech/loaders/recipe/MachineRecipeLoader.java
@@ -445,7 +445,7 @@ private static void registerAssemblerRecipes() {
CANNER_RECIPES.recipeBuilder()
.inputs(MetaItems.SPRAY_EMPTY.getStackForm())
.fluidInputs(Materials.CHEMICAL_DYES[i].getFluid(GTValues.L * 4))
- .outputs(MetaItems.SPRAY_CAN_DYES[i].getStackForm())
+ .outputs(SPRAY_CAN_DYES.get(EnumDyeColor.values()[i]).getStackForm())
.EUt(VA[ULV]).duration(200)
.buildAndRegister();
diff --git a/src/main/resources/assets/gregtech/lang/en_us.lang b/src/main/resources/assets/gregtech/lang/en_us.lang
index 2eddc0b2147..b8417b9507e 100644
--- a/src/main/resources/assets/gregtech/lang/en_us.lang
+++ b/src/main/resources/assets/gregtech/lang/en_us.lang
@@ -330,21 +330,30 @@ metaitem.shape.extruder.rotor.tooltip=Extruder Shape for making Rotors
metaitem.spray.empty.name=Spray Can (Empty)
metaitem.spray.empty.tooltip=Can be filled with sprays of various colors
-
-metaitem.plant.ball.name=Plantball
-
-# Fluid Cells
-metaitem.fluid_cell.empty=Empty
-metaitem.fluid_cell.name=%s Cell
-metaitem.fluid_cell.universal.empty=Empty
-metaitem.fluid_cell.universal.name=%s Universal Cell
-metaitem.fluid_cell.glass_vial.name=%s Glass Vial
-metaitem.large_fluid_cell.steel.name=%s Steel Cell
-metaitem.large_fluid_cell.aluminium.name=%s Aluminium Cell
-metaitem.large_fluid_cell.stainless_steel.name=%s Stainless Steel Cell
-metaitem.large_fluid_cell.titanium.name=%s Titanium Cell
-metaitem.large_fluid_cell.tungstensteel.name=%s Tungstensteel Cell
-
+metaitem.spray.creative.name=Creative Spray Can (%s)
+metaitem.spray.creative.name_base=Creative Spray Can
+metaitem.spray.creative.mode.normal=Normal Mode
+metaitem.spray.creative.mode.rgb=RGB Mode
+metaitem.spray.creative.tip.red=Red
+metaitem.spray.creative.tip.green=Green
+metaitem.spray.creative.tip.blue=Blue
+metaitem.spray.creative.solvent=Solvent
+metaitem.spray.creative.white=White
+metaitem.spray.creative.orange=Orange
+metaitem.spray.creative.magenta=Magenta
+metaitem.spray.creative.lightBlue=Light Blue
+metaitem.spray.creative.yellow=Yellow
+metaitem.spray.creative.lime=Lime
+metaitem.spray.creative.pink=Pink
+metaitem.spray.creative.gray=Gray
+metaitem.spray.creative.silver=Gray
+metaitem.spray.creative.cyan=Cyan
+metaitem.spray.creative.purple=Purple
+metaitem.spray.creative.blue=Blue
+metaitem.spray.creative.brown=Brown
+metaitem.spray.creative.green=Green
+metaitem.spray.creative.red=Red
+metaitem.spray.creative.black=Black
metaitem.spray.solvent.name=Spray Can (Solvent)
metaitem.spray.can.dyes.white.name=Spray Can (White)
metaitem.spray.can.dyes.orange.name=Spray Can (Orange)
@@ -363,6 +372,27 @@ metaitem.spray.can.dyes.green.name=Spray Can (Green)
metaitem.spray.can.dyes.red.name=Spray Can (Red)
metaitem.spray.can.dyes.black.name=Spray Can (Black)
+gregtech.color_mode.error.dye=Block doesn't support dye coloring!
+gregtech.color_mode.error.argb=Block doesn't support ARGB coloring!
+# This shouldn't ever show up in game, but exists for completions sake
+gregtech.color_mode.error.either=Block doesn't support coloring!
+
+
+metaitem.plant.ball.name=Plantball
+
+# Fluid Cells
+metaitem.fluid_cell.empty=Empty
+metaitem.fluid_cell.name=%s Cell
+metaitem.fluid_cell.universal.empty=Empty
+metaitem.fluid_cell.universal.name=%s Universal Cell
+metaitem.fluid_cell.glass_vial.name=%s Glass Vial
+metaitem.large_fluid_cell.steel.name=%s Steel Cell
+metaitem.large_fluid_cell.aluminium.name=%s Aluminium Cell
+metaitem.large_fluid_cell.stainless_steel.name=%s Stainless Steel Cell
+metaitem.large_fluid_cell.titanium.name=%s Titanium Cell
+metaitem.large_fluid_cell.tungstensteel.name=%s Tungstensteel Cell
+
+
metaitem.tool.matches.name=Match
metaitem.tool.matchbox.name=Match Box
metaitem.tool.matchbox.tooltip=This is not a Car
diff --git a/src/main/resources/assets/gregtech/models/item/metaitems/spray.creative.json b/src/main/resources/assets/gregtech/models/item/metaitems/spray.creative.json
new file mode 100644
index 00000000000..bbc05432e18
--- /dev/null
+++ b/src/main/resources/assets/gregtech/models/item/metaitems/spray.creative.json
@@ -0,0 +1,7 @@
+{
+ "parent": "item/handheld",
+ "textures": {
+ "layer0": "gregtech:items/metaitems/spray.creative/base",
+ "layer1": "gregtech:items/metaitems/spray.creative/overlay"
+ }
+}
diff --git a/src/main/resources/assets/gregtech/textures/gui/widget/rgb_gradient.png b/src/main/resources/assets/gregtech/textures/gui/widget/rgb_gradient.png
new file mode 100644
index 00000000000..22c5b25b65b
Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/gui/widget/rgb_gradient.png differ
diff --git a/src/main/resources/assets/gregtech/textures/items/metaitems/spray.creative/base.png b/src/main/resources/assets/gregtech/textures/items/metaitems/spray.creative/base.png
new file mode 100644
index 00000000000..31101e7fa3b
Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/items/metaitems/spray.creative/base.png differ
diff --git a/src/main/resources/assets/gregtech/textures/items/metaitems/spray.creative/overlay.png b/src/main/resources/assets/gregtech/textures/items/metaitems/spray.creative/overlay.png
new file mode 100644
index 00000000000..ab642f86050
Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/items/metaitems/spray.creative/overlay.png differ