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 IPipeTilepipeTile) { + 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 IPipeTilepipeTile) { + 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 IPipeTilepipeTile && 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 IPipeTilepipeTile && 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 IPropertyFluidFilterpropertyFilter) { 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 MetaItemmetaItem) { + 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 MetaItemmeta) { - 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 IPipeTilepipeTile && 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 IPipeTilepipeTile) { 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 BlockPipepipe) { 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 MetaItemmeta)) 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 MetaItemmetaItem)) 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 BlockPipeblockPipe) { + 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 IPipeTilenextPipe) { + 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 IPipeTileneighboringPipe) { + 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