diff --git a/src/main/java/gregtech/api/items/IDyeableItem.java b/src/main/java/gregtech/api/items/IDyeableItem.java new file mode 100644 index 00000000000..fe5625480e8 --- /dev/null +++ b/src/main/java/gregtech/api/items/IDyeableItem.java @@ -0,0 +1,79 @@ +package gregtech.api.items; + +import net.minecraft.block.BlockCauldron; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumActionResult; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.common.util.Constants; + +import org.jetbrains.annotations.NotNull; + +public interface IDyeableItem { + + String COLOR_KEY = "gt_color"; + + default boolean hasColor(ItemStack stack) { + NBTTagCompound nbttagcompound = stack.getTagCompound(); + return nbttagcompound != null && nbttagcompound.hasKey(COLOR_KEY, Constants.NBT.TAG_INT); + } + + default int getColor(ItemStack stack) { + NBTTagCompound nbttagcompound = stack.getTagCompound(); + if (nbttagcompound != null && nbttagcompound.hasKey(COLOR_KEY, Constants.NBT.TAG_INT)) { + return nbttagcompound.getInteger(COLOR_KEY); + } + return getDefaultColor(stack); + } + + default int getDefaultColor(ItemStack stack) { + return 0xFFFFFF; + } + + default void removeColor(ItemStack stack) { + NBTTagCompound nbttagcompound = stack.getTagCompound(); + if (nbttagcompound != null && nbttagcompound.hasKey(COLOR_KEY)) { + nbttagcompound.removeTag(COLOR_KEY); + } + } + + default void setColor(ItemStack stack, int color) { + NBTTagCompound nbttagcompound = stack.getTagCompound(); + if (nbttagcompound == null) { + nbttagcompound = new NBTTagCompound(); + stack.setTagCompound(nbttagcompound); + } + nbttagcompound.setInteger(COLOR_KEY, color); + } + + default @NotNull EnumActionResult onItemUseFirst(@NotNull EntityPlayer player, @NotNull World world, + @NotNull BlockPos pos, @NotNull EnumFacing side, float hitX, + float hitY, float hitZ, @NotNull EnumHand hand) { + ItemStack stack = player.getHeldItem(hand); + if (this.hasColor(stack)) { + IBlockState iblockstate = world.getBlockState(pos); + if (iblockstate.getBlock() instanceof BlockCauldron cauldron) { + int water = iblockstate.getValue(BlockCauldron.LEVEL); + if (water > 0) { + this.removeColor(stack); + cauldron.setWaterLevel(world, pos, iblockstate, water - 1); + return EnumActionResult.SUCCESS; + } + } + } + return EnumActionResult.PASS; + } + + /** + * Controls whether the dyeing recipe simply removes the dyeable item from the crafting grid, + * or calls {@link net.minecraftforge.common.ForgeHooks#getContainerItem(ItemStack)} on it. + */ + default boolean shouldGetContainerItem() { + return true; + } +} diff --git a/src/main/java/gregtech/api/items/toolitem/IGTTool.java b/src/main/java/gregtech/api/items/toolitem/IGTTool.java index 94ae45effb9..3701560d080 100644 --- a/src/main/java/gregtech/api/items/toolitem/IGTTool.java +++ b/src/main/java/gregtech/api/items/toolitem/IGTTool.java @@ -5,15 +5,12 @@ import gregtech.api.capability.GregtechCapabilities; import gregtech.api.capability.impl.CombinedCapabilityProvider; import gregtech.api.capability.impl.ElectricItem; -import gregtech.api.gui.GuiTextures; -import gregtech.api.gui.ModularUI; -import gregtech.api.gui.widgets.ClickButtonWidget; -import gregtech.api.gui.widgets.DynamicLabelWidget; import gregtech.api.items.gui.ItemUIFactory; -import gregtech.api.items.gui.PlayerInventoryHolder; import gregtech.api.items.metaitem.ElectricStats; import gregtech.api.items.toolitem.aoe.AoESymmetrical; import gregtech.api.items.toolitem.behavior.IToolBehavior; +import gregtech.api.mui.GTGuiTextures; +import gregtech.api.mui.GTGuis; import gregtech.api.recipes.ModHandler; import gregtech.api.unification.OreDictUnifier; import gregtech.api.unification.material.Material; @@ -63,6 +60,19 @@ import appeng.api.implementations.items.IAEWrench; import buildcraft.api.tools.IToolWrench; import cofh.api.item.IToolHammer; +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.api.widget.IWidget; +import com.cleanroommc.modularui.api.widget.Interactable; +import com.cleanroommc.modularui.factory.HandGuiData; +import com.cleanroommc.modularui.factory.ItemGuiFactory; +import com.cleanroommc.modularui.screen.ModularPanel; +import com.cleanroommc.modularui.utils.Alignment; +import com.cleanroommc.modularui.value.sync.IntSyncValue; +import com.cleanroommc.modularui.value.sync.PanelSyncManager; +import com.cleanroommc.modularui.widgets.ButtonWidget; +import com.cleanroommc.modularui.widgets.TextWidget; +import com.cleanroommc.modularui.widgets.layout.Column; +import com.cleanroommc.modularui.widgets.layout.Row; import com.enderio.core.common.interfaces.IOverlayRenderAware; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; @@ -207,7 +217,7 @@ default ItemStack get(Material material) { // Set behaviours NBTTagCompound behaviourTag = getBehaviorsTag(stack); - getToolStats().getBehaviors().forEach(behavior -> behavior.addBehaviorNBT(stack, behaviourTag)); + getBehaviors(stack).forEach(behavior -> behavior.addBehaviorNBT(stack, behaviourTag)); if (aoeDefinition != AoESymmetrical.none()) { behaviourTag.setInteger(MAX_AOE_COLUMN_KEY, aoeDefinition.column); @@ -409,14 +419,14 @@ default AoESymmetrical getAoEDefinition(ItemStack stack) { } default boolean definition$hitEntity(ItemStack stack, EntityLivingBase target, EntityLivingBase attacker) { - getToolStats().getBehaviors().forEach(behavior -> behavior.hitEntity(stack, target, attacker)); + getBehaviors(stack).forEach(behavior -> behavior.hitEntity(stack, target, attacker)); damageItem(stack, attacker, getToolStats().getToolDamagePerAttack(stack)); return true; } default boolean definition$onBlockStartBreak(ItemStack stack, BlockPos pos, EntityPlayer player) { if (player.world.isRemote) return false; - getToolStats().getBehaviors().forEach(behavior -> behavior.onBlockStartBreak(stack, pos, player)); + getBehaviors(stack).forEach(behavior -> behavior.onBlockStartBreak(stack, pos, player)); if (!player.isSneaking()) { EntityPlayerMP playerMP = (EntityPlayerMP) player; @@ -458,7 +468,7 @@ default AoESymmetrical getAoEDefinition(ItemStack stack) { default boolean definition$onBlockDestroyed(ItemStack stack, World worldIn, IBlockState state, BlockPos pos, EntityLivingBase entityLiving) { if (!worldIn.isRemote) { - getToolStats().getBehaviors() + getBehaviors(stack) .forEach(behavior -> behavior.onBlockDestroyed(stack, worldIn, state, pos, entityLiving)); if ((double) state.getBlockHardness(worldIn, pos) != 0.0D) { @@ -522,7 +532,7 @@ default AoESymmetrical getAoEDefinition(ItemStack stack) { default boolean definition$canDisableShield(ItemStack stack, ItemStack shield, EntityLivingBase entity, EntityLivingBase attacker) { - return getToolStats().getBehaviors().stream() + return getBehaviors(stack).stream() .anyMatch(behavior -> behavior.canDisableShield(stack, shield, entity, attacker)); } @@ -570,7 +580,7 @@ default AoESymmetrical getAoEDefinition(ItemStack stack) { } default boolean definition$onEntitySwing(EntityLivingBase entityLiving, ItemStack stack) { - getToolStats().getBehaviors().forEach(behavior -> behavior.onEntitySwing(entityLiving, stack)); + getBehaviors(stack).forEach(behavior -> behavior.onEntitySwing(entityLiving, stack)); return false; } @@ -622,7 +632,7 @@ default AoESymmetrical getAoEDefinition(ItemStack stack) { if (isElectric()) { providers.add(ElectricStats.createElectricItem(0L, getElectricTier()).createProvider(stack)); } - for (IToolBehavior behavior : getToolStats().getBehaviors()) { + for (IToolBehavior behavior : getBehaviors(stack)) { ICapabilityProvider behaviorProvider = behavior.createProvider(stack, nbt); if (behaviorProvider != null) { providers.add(behaviorProvider); @@ -633,10 +643,15 @@ default AoESymmetrical getAoEDefinition(ItemStack stack) { return new CombinedCapabilityProvider(providers); } + @NotNull + default List getBehaviors(ItemStack stack) { + return getToolStats().getBehaviors(); + } + default EnumActionResult definition$onItemUseFirst(@NotNull EntityPlayer player, @NotNull World world, @NotNull BlockPos pos, @NotNull EnumFacing facing, float hitX, float hitY, float hitZ, @NotNull EnumHand hand) { - for (IToolBehavior behavior : getToolStats().getBehaviors()) { + for (IToolBehavior behavior : getBehaviors(player.getHeldItem(hand))) { if (behavior.onItemUseFirst(player, world, pos, facing, hitX, hitY, hitZ, hand) == EnumActionResult.SUCCESS) { return EnumActionResult.SUCCESS; @@ -648,8 +663,9 @@ default AoESymmetrical getAoEDefinition(ItemStack stack) { default EnumActionResult definition$onItemUse(EntityPlayer player, World world, BlockPos pos, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ) { - for (IToolBehavior behavior : getToolStats().getBehaviors()) { - if (behavior.onItemUse(player, world, pos, hand, facing, hitX, hitY, hitZ) == EnumActionResult.SUCCESS) { + for (IToolBehavior behavior : getBehaviors(player.getHeldItem(hand))) { + if (behavior.onItemUse(player, world, pos, hand, facing, hitX, hitY, hitZ) == + EnumActionResult.SUCCESS) { return EnumActionResult.SUCCESS; } } @@ -662,12 +678,12 @@ default AoESymmetrical getAoEDefinition(ItemStack stack) { if (!world.isRemote) { // TODO: relocate to keybind action when keybind PR happens if (player.isSneaking() && getMaxAoEDefinition(stack) != AoESymmetrical.none()) { - PlayerInventoryHolder.openHandItemUI(player, hand); + ItemGuiFactory.open((EntityPlayerMP) player, hand); return ActionResult.newResult(EnumActionResult.SUCCESS, stack); } } - for (IToolBehavior behavior : getToolStats().getBehaviors()) { + for (IToolBehavior behavior : getBehaviors(stack)) { if (behavior.onItemRightClick(world, player, hand).getType() == EnumActionResult.SUCCESS) { return ActionResult.newResult(EnumActionResult.SUCCESS, stack); } @@ -762,10 +778,10 @@ default AoESymmetrical getAoEDefinition(ItemStack stack) { tooltip.add(I18n.format("item.gt.tool.behavior.relocate_mining")); } - if (!addedBehaviorNewLine && !toolStats.getBehaviors().isEmpty()) { + if (!addedBehaviorNewLine && !getBehaviors(stack).isEmpty()) { tooltip.add(""); } - toolStats.getBehaviors().forEach(behavior -> behavior.addInformation(stack, world, tooltip, flag)); + getBehaviors(stack).forEach(behavior -> behavior.addInformation(stack, world, tooltip, flag)); // unique tooltip String uniqueTooltip = "item.gt.tool." + getToolId() + ".tooltip"; @@ -878,6 +894,7 @@ default ModelResourceLocation getModelLocation() { // Sound Playing default void playCraftingSound(EntityPlayer player, ItemStack stack) { + stack = toolbeltPassthrough(stack); // player null check for things like auto-crafters if (ConfigHolder.client.toolCraftingSounds && getSound() != null && player != null) { if (canPlaySound(stack)) { @@ -903,45 +920,78 @@ default void playSound(EntityPlayer player) { } } - default ModularUI createUI(PlayerInventoryHolder holder, EntityPlayer entityPlayer) { - NBTTagCompound tag = getBehaviorsTag(holder.getCurrentItem()); - AoESymmetrical defaultDefinition = getMaxAoEDefinition(holder.getCurrentItem()); - return ModularUI.builder(GuiTextures.BORDERED_BACKGROUND, 120, 80) - .label(6, 10, "item.gt.tool.aoe.columns") - .label(49, 10, "item.gt.tool.aoe.rows") - .label(79, 10, "item.gt.tool.aoe.layers") - .widget(new ClickButtonWidget(15, 24, 20, 20, "+", data -> { - AoESymmetrical.increaseColumn(tag, defaultDefinition); - holder.markAsDirty(); - })) - .widget(new ClickButtonWidget(15, 44, 20, 20, "-", data -> { - AoESymmetrical.decreaseColumn(tag, defaultDefinition); - holder.markAsDirty(); - })) - .widget(new ClickButtonWidget(50, 24, 20, 20, "+", data -> { - AoESymmetrical.increaseRow(tag, defaultDefinition); - holder.markAsDirty(); - })) - .widget(new ClickButtonWidget(50, 44, 20, 20, "-", data -> { - AoESymmetrical.decreaseRow(tag, defaultDefinition); - holder.markAsDirty(); - })) - .widget(new ClickButtonWidget(85, 24, 20, 20, "+", data -> { - AoESymmetrical.increaseLayer(tag, defaultDefinition); - holder.markAsDirty(); - })) - .widget(new ClickButtonWidget(85, 44, 20, 20, "-", data -> { - AoESymmetrical.decreaseLayer(tag, defaultDefinition); - holder.markAsDirty(); - })) - .widget(new DynamicLabelWidget(23, 65, () -> Integer.toString( - 1 + 2 * AoESymmetrical.getColumn(getBehaviorsTag(holder.getCurrentItem()), defaultDefinition)))) - .widget(new DynamicLabelWidget(58, 65, () -> Integer.toString( - 1 + 2 * AoESymmetrical.getRow(getBehaviorsTag(holder.getCurrentItem()), defaultDefinition)))) - .widget(new DynamicLabelWidget(93, 65, - () -> Integer.toString(1 + - AoESymmetrical.getLayer(getBehaviorsTag(holder.getCurrentItem()), defaultDefinition)))) - .build(holder, entityPlayer); + @Override + default ModularPanel buildUI(HandGuiData guiData, PanelSyncManager manager) { + final var usedStack = guiData.getUsedItemStack(); + final var behaviorsTag = getBehaviorsTag(usedStack); + final var defaultDefinition = getMaxAoEDefinition(usedStack); + + var columnValue = new IntSyncValue( + () -> AoESymmetrical.getColumn(behaviorsTag, defaultDefinition), + i -> AoESymmetrical.setColumn(behaviorsTag, i, defaultDefinition)); + var rowValue = new IntSyncValue( + () -> AoESymmetrical.getRow(behaviorsTag, defaultDefinition), + i -> AoESymmetrical.setRow(behaviorsTag, i, defaultDefinition)); + var layerValue = new IntSyncValue( + () -> AoESymmetrical.getLayer(behaviorsTag, defaultDefinition), + i -> AoESymmetrical.setLayer(behaviorsTag, i, defaultDefinition)); + + manager.syncValue("row_value", rowValue); + manager.syncValue("column_value", columnValue); + manager.syncValue("layer_value", layerValue); + + return GTGuis.createPanel(usedStack.getTranslationKey(), 120, 80) + .child(new Row() + .widthRel(1f) + .margin(4, 0) + .alignY(0.5f) + .coverChildrenHeight() + .mainAxisAlignment(Alignment.MainAxis.SPACE_BETWEEN) + .child(createColumn(columnValue, "columns", true, defaultDefinition.column)) + .child(createColumn(rowValue, "rows", true, defaultDefinition.row)) + .child(createColumn(layerValue, "layers", false, defaultDefinition.layer))); + } + + default Column createColumn(IntSyncValue syncValue, String lang, boolean shouldDouble, int max) { + final var display = IKey.dynamic( + () -> String.valueOf(1 + (shouldDouble ? 2 * syncValue.getIntValue() : syncValue.getIntValue()))); + + IWidget increaseButton = new ButtonWidget<>() + .size(9, 18) + .background(GTGuiTextures.MC_BUTTON) + .overlay(GTGuiTextures.PLUS) + .disableHoverBackground() + .onMousePressed(data -> { + int val = syncValue.getIntValue(); + if (val < max) syncValue.setIntValue(++val); + Interactable.playButtonClickSound(); + return true; + }); + + IWidget decreaseButton = new ButtonWidget<>() + .size(9, 18) + .background(GTGuiTextures.MC_BUTTON) + .overlay(GTGuiTextures.MINUS) + .disableHoverBackground() + .onMousePressed(data -> { + int val = syncValue.getIntValue(); + if (val > 0) syncValue.setIntValue(--val); + Interactable.playButtonClickSound(); + return true; + }); + + return new Column() + .coverChildren() + .child(new TextWidget(IKey.lang("item.gt.tool.aoe." + lang)) + .marginBottom(5)) + .child(new Row() + .coverChildren() + .marginBottom(5) + .child(increaseButton) + .child(decreaseButton)) + .child(new TextWidget(display) + .alignment(Alignment.Center) + .widthRel(1f)); } Set getToolClasses(ItemStack stack); diff --git a/src/main/java/gregtech/api/items/toolitem/ItemGTHoe.java b/src/main/java/gregtech/api/items/toolitem/ItemGTHoe.java index 13e14e8e574..1dd69871cb4 100644 --- a/src/main/java/gregtech/api/items/toolitem/ItemGTHoe.java +++ b/src/main/java/gregtech/api/items/toolitem/ItemGTHoe.java @@ -314,7 +314,7 @@ public EnumActionResult onItemUse(@NotNull EntityPlayer player, @NotNull World w @Override public boolean hitEntity(@NotNull ItemStack stack, @NotNull EntityLivingBase target, @NotNull EntityLivingBase attacker) { - getToolStats().getBehaviors().forEach(behavior -> behavior.hitEntity(stack, target, attacker)); + getBehaviors(stack).forEach(behavior -> behavior.hitEntity(stack, target, attacker)); // damage by 1, as this is what vanilla does ToolHelper.damageItem(stack, attacker, 1); return true; diff --git a/src/main/java/gregtech/api/items/toolitem/ItemGTToolbelt.java b/src/main/java/gregtech/api/items/toolitem/ItemGTToolbelt.java new file mode 100644 index 00000000000..01acd7b993c --- /dev/null +++ b/src/main/java/gregtech/api/items/toolitem/ItemGTToolbelt.java @@ -0,0 +1,772 @@ +package gregtech.api.items.toolitem; + +import gregtech.api.GregTechAPI; +import gregtech.api.items.IDyeableItem; +import gregtech.api.items.gui.ItemUIFactory; +import gregtech.api.items.toolitem.behavior.IToolBehavior; +import gregtech.api.metatileentity.MetaTileEntityHolder; +import gregtech.api.mui.GTGuiTextures; +import gregtech.api.mui.GTGuis; +import gregtech.api.unification.material.Material; +import gregtech.api.unification.material.properties.PropertyKey; +import gregtech.api.unification.material.properties.ToolProperty; +import gregtech.api.util.LocalizationUtils; +import gregtech.api.util.TextFormattingUtil; +import gregtech.client.utils.TooltipHelper; +import gregtech.common.items.behaviors.ColorSprayBehaviour; +import gregtech.common.metatileentities.multi.multiblockpart.MetaTileEntityMaintenanceHatch; +import gregtech.core.network.packets.PacketToolbeltSelectionChange; + +import net.minecraft.block.state.IBlockState; +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.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.util.EnumActionResult; +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.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.ICapabilityProvider; +import net.minecraftforge.common.util.INBTSerializable; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraftforge.items.CapabilityItemHandler; +import net.minecraftforge.items.ItemStackHandler; +import net.minecraftforge.oredict.OreDictionary; +import net.minecraftforge.oredict.OreIngredient; + +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.factory.HandGuiData; +import com.cleanroommc.modularui.screen.ModularPanel; +import com.cleanroommc.modularui.value.sync.PanelSyncManager; +import com.cleanroommc.modularui.value.sync.SyncHandlers; +import com.cleanroommc.modularui.widgets.ItemSlot; +import com.cleanroommc.modularui.widgets.layout.Grid; +import com.cleanroommc.modularui.widgets.slot.SlotGroup; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Multimap; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Range; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.IntSupplier; +import java.util.function.Supplier; + +import static gregtech.api.items.toolitem.ToolHelper.MATERIAL_KEY; + +public class ItemGTToolbelt extends ItemGTTool implements IDyeableItem { + + private static final ThreadLocal lastSlot = ThreadLocal.withInitial(() -> -999); + private static final ThreadLocal lastPlayer = ThreadLocal.withInitial(() -> null); + + public ItemGTToolbelt(String domain, String id, Supplier markerItem, IToolBehavior... behaviors) { + super(domain, id, -1, + new ToolDefinitionBuilder().behaviors(behaviors).cannotAttack().attackSpeed(-2.4F).build(), + null, false, new HashSet<>(), "toolbelt", new ArrayList<>(), + markerItem); + } + + public int getSelectedSlot(@NotNull ItemStack toolbelt) { + return getHandler(toolbelt).getSelectedSlot(); + } + + public int getSlotCount(@NotNull ItemStack toolbelt) { + return getHandler(toolbelt).getSlots(); + } + + public @NotNull ItemStack getSelectedTool(@NotNull ItemStack toolbelt) { + return getHandler(toolbelt).getSelectedStack(); + } + + @NotNull + public ItemStack getToolInSlot(@NotNull ItemStack toolbelt, int slot) { + ToolStackHandler handler = getHandler(toolbelt); + if (slot < 0 || slot >= handler.getSlots()) return ItemStack.EMPTY; + return handler.getStackInSlot(slot); + } + + @Override + public ModularPanel buildUI(HandGuiData guiData, PanelSyncManager guiSyncManager) { + final var usedStack = guiData.getUsedItemStack(); + final var handler = getHandler(usedStack); + final var selected = handler.getSelectedStack(); + if (!selected.isEmpty() && selected.getItem() instanceof ItemUIFactory factory) { + return factory.buildUI(guiData, guiSyncManager); + } + + int heightBonus = (handler.getSlots() / 9) * 18; + + SlotGroup group = new SlotGroup("toolbelt_inventory", 9); + guiSyncManager.registerSlotGroup(group); + + List slots = new ArrayList<>(); + for (int i = 0; i < handler.getSlots(); i++) { + slots.add(new ItemSlot()); + } + + return GTGuis.createPanel(usedStack.getTranslationKey(), 176, 120 + heightBonus + 12) + .child(IKey.str(usedStack.getDisplayName()).asWidget() + .pos(5, 5) + .height(12)) + .child(new Grid() + .margin(0) + .leftRel(0.5f) + .top(7 + 12) + .coverChildren() + .mapTo(group.getRowSize(), slots, (index, value) -> value + .slot(SyncHandlers.itemSlot(handler, index) + .slotGroup(group) + .changeListener( + (newItem, onlyAmountChanged, client, init) -> handler + .onContentsChanged(index))) + .background(GTGuiTextures.SLOT, GTGuiTextures.TOOL_SLOT_OVERLAY) + .debugName("slot_" + index)) + .debugName("toolbelt_inventory")) + .bindPlayerInventory(); + } + + @Override + public @NotNull List getBehaviors(ItemStack stack) { + ItemStack selected = getHandler(stack).getSelectedStack(); + if (selected.isEmpty()) return super.getBehaviors(stack); + else if (selected.getItem() instanceof IGTTool tool) { + return tool.getBehaviors(selected); + } else return Collections.emptyList(); + } + + @Override + public float getDestroySpeed(@NotNull ItemStack stack, @NotNull IBlockState state) { + ItemStack selected = getHandler(stack).getSelectedStack(); + if (!selected.isEmpty()) { + return selected.getItem().getDestroySpeed(selected, state); + } else return definition$getDestroySpeed(stack, state); + } + + @Override + public boolean hitEntity(@NotNull ItemStack stack, @NotNull EntityLivingBase target, + @NotNull EntityLivingBase attacker) { + ItemStack selected = getHandler(stack).getSelectedStack(); + if (!selected.isEmpty()) { + return selected.getItem().hitEntity(selected, target, attacker); + } else return definition$hitEntity(stack, target, attacker); + } + + @Override + public boolean onBlockStartBreak(@NotNull ItemStack itemstack, @NotNull BlockPos pos, + @NotNull EntityPlayer player) { + ItemStack selected = getHandler(itemstack).getSelectedStack(); + if (!selected.isEmpty()) { + return selected.getItem().onBlockStartBreak(selected, pos, player); + } else return definition$onBlockStartBreak(itemstack, pos, player); + } + + @Override + public boolean onBlockDestroyed(@NotNull ItemStack stack, @NotNull World worldIn, @NotNull IBlockState state, + @NotNull BlockPos pos, @NotNull EntityLivingBase entityLiving) { + ItemStack selected = getHandler(stack).getSelectedStack(); + if (!selected.isEmpty()) { + return selected.getItem().onBlockDestroyed(selected, worldIn, state, pos, entityLiving); + } else return definition$onBlockDestroyed(stack, worldIn, state, pos, entityLiving); + } + + @Override + public boolean getIsRepairable(@NotNull ItemStack toRepair, @NotNull ItemStack repair) { + ItemStack selected = getHandler(toRepair).getSelectedStack(); + if (!selected.isEmpty()) { + return selected.getItem().getIsRepairable(selected, repair); + } else return definition$getIsRepairable(toRepair, repair); + } + + @NotNull + @Override + public Multimap getAttributeModifiers(@NotNull EntityEquipmentSlot slot, + @NotNull ItemStack stack) { + ItemStack selected = getHandler(stack).getSelectedStack(); + if (!selected.isEmpty()) { + return selected.getItem().getAttributeModifiers(slot, selected); + } else return definition$getAttributeModifiers(slot, stack); + } + + @Override + public int getHarvestLevel(@NotNull ItemStack stack, @NotNull String toolClass, @Nullable EntityPlayer player, + @Nullable IBlockState blockState) { + ItemStack selected = getHandler(stack).getSelectedStack(); + if (!selected.isEmpty()) { + return selected.getItem().getHarvestLevel(stack, toolClass, player, blockState); + } else return definition$getHarvestLevel(stack, toolClass, player, blockState); + } + + @NotNull + @Override + public Set getToolClasses(@NotNull ItemStack stack) { + return getHandler(stack).getToolClasses(true); + } + + @Override + public boolean canDisableShield(@NotNull ItemStack stack, @NotNull ItemStack shield, + @NotNull EntityLivingBase entity, @NotNull EntityLivingBase attacker) { + ItemStack selected = getHandler(stack).getSelectedStack(); + if (!selected.isEmpty()) { + return selected.getItem().canDisableShield(selected, shield, entity, attacker); + } else return definition$canDisableShield(stack, shield, entity, attacker); + } + + @Override + public boolean doesSneakBypassUse(@NotNull ItemStack stack, @NotNull IBlockAccess world, @NotNull BlockPos pos, + @NotNull EntityPlayer player) { + ItemStack selected = getHandler(stack).getSelectedStack(); + if (!selected.isEmpty()) { + return selected.getItem().doesSneakBypassUse(selected, world, pos, player); + } else return definition$doesSneakBypassUse(stack, world, pos, player); + } + + @Override + public boolean onEntitySwing(@NotNull EntityLivingBase entityLiving, @NotNull ItemStack stack) { + ItemStack selected = getHandler(stack).getSelectedStack(); + if (!selected.isEmpty()) { + return selected.getItem().onEntitySwing(entityLiving, selected); + } else return definition$onEntitySwing(entityLiving, stack); + } + + @Override + public boolean canDestroyBlockInCreative(@NotNull World world, @NotNull BlockPos pos, @NotNull ItemStack stack, + @NotNull EntityPlayer player) { + ItemStack selected = getHandler(stack).getSelectedStack(); + if (!selected.isEmpty()) { + return selected.getItem().canDestroyBlockInCreative(world, pos, selected, player); + } else return definition$canDestroyBlockInCreative(world, pos, stack, player); + } + + @Override + public boolean shouldCauseReequipAnimation(@NotNull ItemStack oldStack, @NotNull ItemStack newStack, + boolean slotChanged) { + return false; + } + + @Override + public boolean isDamaged(@NotNull ItemStack stack) { + ItemStack selected = getHandler(stack).getSelectedStack(); + if (!selected.isEmpty()) { + return selected.getItem().isDamaged(selected); + } else return definition$isDamaged(stack); + } + + @Override + public int getDamage(@NotNull ItemStack stack) { + ItemStack selected = getHandler(stack).getSelectedStack(); + if (!selected.isEmpty()) { + return selected.getItem().getDamage(selected); + } else return definition$getDamage(stack); + } + + @Override + public int getMaxDamage(@NotNull ItemStack stack) { + ItemStack selected = getHandler(stack).getSelectedStack(); + if (!selected.isEmpty()) { + return selected.getItem().getMaxDamage(selected); + } else return definition$getMaxDamage(stack); + } + + @Override + public void setDamage(@NotNull ItemStack stack, int damage) { + ItemStack selected = getHandler(stack).getSelectedStack(); + if (!selected.isEmpty()) { + selected.getItem().setDamage(selected, damage); + } else definition$setDamage(stack, damage); + } + + @Override + public double getDurabilityForDisplay(@NotNull ItemStack stack) { + ItemStack selected = getHandler(stack).getSelectedStack(); + if (!selected.isEmpty()) { + double dis = selected.getItem().getDurabilityForDisplay(selected); + // vanillaesque tools need to be inverted + if (selected.getItem() instanceof ItemTool) dis = 1 - dis; + return dis; + } else return definition$getDurabilityForDisplay(stack); + } + + @Override + @SideOnly(Side.CLIENT) + public void addInformation(@NotNull ItemStack stack, @Nullable World world, @NotNull List tooltip, + @NotNull ITooltipFlag flag) { + ToolStackHandler handler = getHandler(stack); + ItemStack selected = handler.getSelectedStack(); + if (!selected.isEmpty()) { + selected.getItem().addInformation(selected, world, tooltip, flag); + } else { + if (stack.getItemDamage() > 0) { + int damageRemaining = this.getTotalMaxDurability(stack) - stack.getItemDamage() + 1; + tooltip.add(I18n.format("item.gt.tool.tooltip.general_uses", + TextFormattingUtil.formatNumbers(damageRemaining))); + } + tooltip.add(I18n.format("item.gt.tool.toolbelt.size", + TextFormattingUtil.formatNumbers(handler.getSlots()))); + tooltip.add(""); + if (TooltipHelper.isShiftDown()) { + tooltip.add(I18n.format("item.gt.tool.toolbelt.tooltip")); + tooltip.add(""); + tooltip.add(I18n.format("item.gt.tool.toolbelt.paint")); + tooltip.add(""); + tooltip.add(I18n.format("item.gt.tool.toolbelt.maintenance")); + } else tooltip.add(I18n.format("gregtech.tooltip.hold_shift")); + } + if (TooltipHelper.isCtrlDown()) { + tooltip.add(""); + for (int i = 0; i < handler.getSlots(); i++) { + ItemStack tool = handler.getStackInSlot(i); + String name = tool.isEmpty() ? "x" : tool.getDisplayName(); + tooltip.add(I18n.format( + handler.selectedSlot == i ? "item.gt.tool.toolbelt.selected" : "item.gt.tool.toolbelt.tool", + i + 1, name)); + } + } else tooltip.add(I18n.format("gregtech.tooltip.hold_ctrl")); + } + + @Override + public boolean canHarvestBlock(@NotNull IBlockState state, @NotNull ItemStack stack) { + ItemStack selected = getHandler(stack).getSelectedStack(); + if (!selected.isEmpty()) { + return selected.getItem().canHarvestBlock(state, selected); + } else return ToolHelper.isToolEffective(state, getToolClasses(stack), getTotalHarvestLevel(stack)); + } + + @Override + public void wrenchUsed(EntityPlayer player, EnumHand hand, ItemStack wrench, RayTraceResult rayTrace) { + ItemStack selected = getHandler(wrench).getSelectedStack(); + if (!selected.isEmpty() && selected.getItem() instanceof IGTTool tool) { + tool.wrenchUsed(player, hand, selected, rayTrace); + } else super.wrenchUsed(player, hand, wrench, rayTrace); + } + + @Override + public void toolUsed(ItemStack item, EntityLivingBase user, BlockPos pos) { + ItemStack selected = getHandler(item).getSelectedStack(); + if (!selected.isEmpty() && selected.getItem() instanceof IGTTool tool) { + tool.toolUsed(selected, user, pos); + } else super.toolUsed(item, user, pos); + } + + @Override + public void used(@NotNull EnumHand hand, @NotNull EntityPlayer player, @NotNull BlockPos pos) { + ItemStack selected = getHandler(player.getHeldItem(hand)).getSelectedStack(); + if (!selected.isEmpty() && selected.getItem() instanceof IGTTool tool) { + tool.used(hand, player, pos); + } else super.used(hand, player, pos); + } + + @Override + public boolean hasContainerItem(@NotNull ItemStack stack) { + return true; + } + + @Override + public @NotNull ItemStack getContainerItem(@NotNull ItemStack stack) { + if (getHandler(stack).dealCraftDamageToSelected()) { + return stack.copy(); + } + return super.getContainerItem(stack); + } + + public void setOnCraftIngredient(ItemStack stack, Ingredient ingredient) { + int match = getHandler(stack).checkIngredientAgainstTools(ingredient); + if (match != -1) { + setSelectedTool(match, stack); + PacketToolbeltSelectionChange.toClient(match, + lastSlot.get(), (EntityPlayerMP) lastPlayer.get()); + } + } + + public boolean damageAgainstMaintenanceProblem(ItemStack stack, String toolClass, + @Nullable EntityPlayer entityPlayer) { + return getHandler(stack).checkMaintenanceAgainstTools(toolClass, true, entityPlayer); + } + + public boolean supportsIngredient(ItemStack stack, Ingredient ingredient) { + return getHandler(stack).checkIngredientAgainstTools(ingredient) != -1; + } + + public boolean supportsTool(ItemStack stack, ItemStack tool) { + return getHandler(stack).checkToolAgainstTools(tool) != -1; + } + + private ToolStackHandler getHandler(ItemStack stack) { + return (ToolStackHandler) stack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null); + } + + @Override + public ICapabilityProvider initCapabilities(@NotNull ItemStack stack, NBTTagCompound nbt) { + return new ToolbeltCapabilityProvider(stack); + } + + @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); + } + + @SideOnly(Side.CLIENT) + public void changeSelectedToolHotkey(int slot, ItemStack stack) { + ToolStackHandler handler = getHandler(stack); + if (slot < 0 || slot >= handler.getSlots()) handler.selectedSlot = -1; + else handler.selectedSlot = slot; + PacketToolbeltSelectionChange.toServer(handler.selectedSlot); + } + + /** + * For use by {@link PacketToolbeltSelectionChange} only! + */ + @ApiStatus.Internal + public void setSelectedTool(int slot, ItemStack stack) { + ToolStackHandler handler = getHandler(stack); + if (slot < 0 || slot >= handler.getSlots()) + handler.selectedSlot = -1; + else handler.selectedSlot = slot; + } + + @Override + public @NotNull String getItemStackDisplayName(@NotNull ItemStack stack) { + ItemStack tool = getHandler(stack).getSelectedStack(); + String selectedToolDisplay = ""; + if (!tool.isEmpty()) { + selectedToolDisplay = " (" + tool.getDisplayName() + ")"; + } + getHandler(stack).disablePassthrough(); + String name = LocalizationUtils.format(getTranslationKey(), getToolMaterial(stack).getLocalizedName(), + selectedToolDisplay); + getHandler(stack).enablePassthrough(); + return name; + } + + @Override + public @NotNull EnumActionResult onItemUseFirst(@NotNull EntityPlayer player, @NotNull World world, + @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; + } + + @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; + } + } + return super.onItemUse(player, world, pos, hand, facing, hitX, hitY, hitZ); + } + + @Override + public int getColor(ItemStack stack, int tintIndex) { + if (tintIndex == 0) { + return this.getColor(stack); + } + getHandler(stack).disablePassthrough(); + int color = super.getColor(stack, tintIndex); + getHandler(stack).enablePassthrough(); + return color; + } + + @Override + public int getDefaultColor(ItemStack stack) { + return 0xA06540; + } + + @Override + public boolean shouldGetContainerItem() { + return false; + } + + public static boolean checkIngredientAgainstToolbelt(@NotNull ItemStack input, @NotNull OreIngredient ingredient) { + if (input.getItem() instanceof ItemGTToolbelt toolbelt) { + if (toolbelt.supportsIngredient(input, ingredient)) { + toolbelt.setOnCraftIngredient(input, ingredient); + return true; + } + } + return false; + } + + public static void setCraftingSlot(int slot, EntityPlayer player) { + lastSlot.set(slot); + lastPlayer.set(player); + } + + public static boolean checkToolAgainstToolbelt(@NotNull ItemStack toolbelt, @NotNull ItemStack tool) { + if (toolbelt.getItem() instanceof ItemGTToolbelt belt && ToolHelper.isUtilityItem(tool)) { + return belt.supportsTool(toolbelt, tool); + } + return false; + } + + protected static class ToolbeltCapabilityProvider implements ICapabilityProvider, INBTSerializable { + + protected final IntSupplier slotCountSupplier; + + private @Nullable ToolStackHandler handler; + + public ToolbeltCapabilityProvider(ItemStack stack) { + slotCountSupplier = () -> { + if (!ToolHelper.hasMaterial(stack)) return 4; + NBTTagCompound toolTag = stack.getOrCreateSubCompound(ToolHelper.TOOL_TAG_KEY); + Material material = GregTechAPI.materialManager.getMaterial(toolTag.getString(MATERIAL_KEY)); + if (material == null) return 4; + ToolProperty toolProperty = material.getProperty(PropertyKey.TOOL); + return Math.min(8, 2 + toolProperty.getToolHarvestLevel()); + }; + } + + @Override + public boolean hasCapability(@NotNull Capability capability, EnumFacing facing) { + return capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY; + } + + @Override + public T getCapability(@NotNull Capability capability, EnumFacing facing) { + if (capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) + return CapabilityItemHandler.ITEM_HANDLER_CAPABILITY.cast(this.getHandler()); + else return null; + } + + @Override + public NBTTagCompound serializeNBT() { + return this.getHandler().serializeNBT(); + } + + @Override + public void deserializeNBT(NBTTagCompound nbt) { + // .copy() prevents double damage ticks in singleplayer + this.getHandler().deserializeNBT(nbt.copy()); + } + + protected @NotNull ToolStackHandler getHandler() { + int size = slotCountSupplier.getAsInt(); + if (handler == null) handler = new ToolStackHandler(size); + else if (handler.getSlots() != size) handler.setSize(size); + return handler; + } + } + + @Override + public NBTTagCompound getNBTShareTag(ItemStack stack) { + NBTTagCompound tag = new NBTTagCompound(); + if (stack.getTagCompound() != null) { + tag.setTag("NBT", stack.getTagCompound()); + } + tag.setTag("Cap", getHandler(stack).serializeNBT()); + return tag; + } + + @Override + public void readNBTShareTag(ItemStack stack, NBTTagCompound nbt) { + // cap syncing is handled separately, we only need it on the share tag so that changes are detected properly. + stack.setTagCompound(nbt == null ? null : (nbt.hasKey("NBT") ? nbt.getCompoundTag("NBT") : null)); + } + + protected static class ToolStackHandler extends ItemStackHandler { + + private static final Set EMPTY = ImmutableSet.of(); + + private @Range(from = -1, to = 128) int selectedSlot = -1; + + protected ItemTool[] tools; + protected IGTTool[] gtTools; + protected final Set toolClasses = new ObjectOpenHashSet<>(); + + private boolean passthrough = true; + + public ToolStackHandler(int size) { + setSize(size); + } + + public void incrementSelectedSlot() { + if ((this.selectedSlot += 1) >= this.getSlots()) this.selectedSlot = -1; + } + + public void decrementSelectedSlot() { + if ((this.selectedSlot -= 1) < -1) this.selectedSlot = this.getSlots() - 1; + } + + public int getSelectedSlot() { + if (passthrough) return selectedSlot; + else return -1; + } + + public void setSelectedSlot(int selectedSlot) { + this.selectedSlot = Math.min(getSlots() - 1, Math.max(selectedSlot, -1)); + } + + public void enablePassthrough() { + this.passthrough = true; + } + + public void disablePassthrough() { + this.passthrough = false; + } + + public @NotNull ItemStack getSelectedStack() { + if (getSelectedSlot() == -1) return ItemStack.EMPTY; + else return this.getStackInSlot(getSelectedSlot()); + } + + public Set getToolClasses(boolean defaultEmpty) { + ItemStack selectedStack = getSelectedStack(); + if (!selectedStack.isEmpty()) { + if (selectedStack.getItem() instanceof ItemTool tool) { + return tool.getToolClasses(selectedStack); + } else if (selectedStack.getItem() instanceof IGTTool tool) { + return tool.getToolClasses(selectedStack); + } + } + if (defaultEmpty) return EMPTY; + return toolClasses; + } + + @Override + public boolean isItemValid(int slot, @NotNull ItemStack stack) { + Item item = stack.getItem(); + if (item instanceof ItemGTToolbelt) return false; + return ToolHelper.isUtilityItem(stack); + } + + @Override + protected void onContentsChanged(int slot) { + if (this.selectedSlot == slot) this.selectedSlot = -1; + this.updateSlot(slot); + this.update(); + + super.onContentsChanged(slot); + } + + @Override + public NBTTagCompound serializeNBT() { + NBTTagCompound tag = super.serializeNBT(); + if (this.selectedSlot != -1) tag.setByte("SelectedSlot", (byte) this.selectedSlot); + return tag; + } + + @Override + public void deserializeNBT(NBTTagCompound nbt) { + super.deserializeNBT(nbt); + if (nbt.hasKey("SelectedSlot")) this.selectedSlot = nbt.getByte("SelectedSlot"); + } + + @Override + public void setSize(int size) { + super.setSize(size); + this.gtTools = new IGTTool[size]; + this.tools = new ItemTool[size]; + } + + @Override + protected void onLoad() { + for (int i = 0; i < getSlots(); i++) { + this.updateSlot(i); + } + this.update(); + } + + protected void updateSlot(int slot) { + Item item = this.getStackInSlot(slot).getItem(); + if (item instanceof ItemTool tool) { + tools[slot] = tool; + } else { + tools[slot] = null; + } + if (item instanceof IGTTool tool) { + gtTools[slot] = tool; + } else { + gtTools[slot] = null; + } + } + + protected void update() { + this.toolClasses.clear(); + for (int i = 0; i < getSlots(); i++) { + if (tools[i] != null) this.toolClasses.addAll(tools[i].getToolClasses(stacks.get(i))); + } + } + + public boolean checkMaintenanceAgainstTools(String toolClass, boolean doCraftingDamage, + @Nullable EntityPlayer entityPlayer) { + for (int i = 0; i < this.getSlots(); i++) { + ItemStack stack = this.getStackInSlot(i); + if (ToolHelper.isTool(stack, toolClass)) { + if (doCraftingDamage) ToolHelper.damageItemWhenCrafting(stack, entityPlayer); + return true; + } + } + return false; + } + + public int checkIngredientAgainstTools(Ingredient ingredient) { + for (int i = 0; i < this.getSlots(); i++) { + ItemStack stack = this.getStackInSlot(i); + if (ingredient.test(stack)) { + return i; + } + } + return -1; + } + + public void dealCraftDamageToSlot(int slot) { + ItemStack stack = this.getStackInSlot(slot); + this.setStackInSlot(slot, stack.getItem().getContainerItem(stack)); + } + + public boolean dealCraftDamageToSelected() { + if (selectedSlot != -1) { + dealCraftDamageToSlot(selectedSlot); + return true; + } else return false; + } + + public int checkToolAgainstTools(ItemStack tool) { + for (int i = 0; i < this.getSlots(); i++) { + ItemStack stack = this.getStackInSlot(i); + if (OreDictionary.itemMatches(stack, tool, false)) { + return i; + } + } + return -1; + } + } +} diff --git a/src/main/java/gregtech/api/items/toolitem/ToolHelper.java b/src/main/java/gregtech/api/items/toolitem/ToolHelper.java index 8b1a8ef147a..3dc2f38b11d 100644 --- a/src/main/java/gregtech/api/items/toolitem/ToolHelper.java +++ b/src/main/java/gregtech/api/items/toolitem/ToolHelper.java @@ -3,6 +3,8 @@ 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; @@ -15,6 +17,7 @@ import gregtech.api.util.function.QuintFunction; import gregtech.common.ConfigHolder; import gregtech.common.items.MetaItems; +import gregtech.common.items.behaviors.ColorSprayBehaviour; import gregtech.tools.enchants.EnchantmentHardHammer; import net.minecraft.advancements.CriteriaTriggers; @@ -35,6 +38,7 @@ import net.minecraft.init.Blocks; import net.minecraft.init.Enchantments; import net.minecraft.item.ItemStack; +import net.minecraft.item.ItemTool; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.play.server.SPacketBlockChange; import net.minecraft.stats.StatBase; @@ -194,13 +198,28 @@ public static void registerToolSymbol(Character symbol, IGTTool tool) { } public static NBTTagCompound getToolTag(ItemStack stack) { + stack = toolbeltPassthrough(stack); return stack.getOrCreateSubCompound(TOOL_TAG_KEY); } + public static ItemStack toolbeltPassthrough(ItemStack stack) { + if (stack.getItem() instanceof ItemGTToolbelt toolbelt && hasMaterial(stack)) { + ItemStack selected = toolbelt.getSelectedTool(stack); + if (!selected.isEmpty()) stack = selected; + } + return stack; + } + public static NBTTagCompound getBehaviorsTag(ItemStack stack) { + stack = toolbeltPassthrough(stack); return stack.getOrCreateSubCompound(BEHAVIOURS_TAG_KEY); } + public static void setBehaviorsTag(ItemStack stack, NBTTagCompound tag) { + stack = toolbeltPassthrough(stack); + stack.setTagInfo(BEHAVIOURS_TAG_KEY, tag); + } + public static ItemStack getAndSetToolData(IGTTool tool, Material material, int maxDurability, int harvestLevel, float toolSpeed, float attackDamage) { ItemStack stack = tool.getRaw(); @@ -230,6 +249,13 @@ public static ItemStack getAndSetToolData(IGTTool tool, Material material, int m * @param entity entity that has damaged this stack */ public static void damageItemWhenCrafting(@NotNull ItemStack stack, @Nullable EntityLivingBase entity) { + if (stack.getItem() instanceof ItemGTToolbelt toolbelt) { + ItemStack selectedStack = toolbelt.getSelectedTool(stack); + if (!selectedStack.isEmpty()) { + damageItemWhenCrafting(selectedStack, entity); + return; + } + } int damage = 2; if (stack.getItem() instanceof IGTTool) { damage = ((IGTTool) stack.getItem()).getToolStats().getToolDamagePerCraft(stack); @@ -264,58 +290,63 @@ public static void damageItem(@NotNull ItemStack stack, @Nullable EntityLivingBa * @param damage how much damage the stack will take */ public static void damageItem(@NotNull ItemStack stack, @Nullable EntityLivingBase entity, int damage) { - if (!(stack.getItem() instanceof IGTTool)) { + if (!(stack.getItem() instanceof IGTTool tool)) { if (entity != null) stack.damageItem(damage, entity); - } else { - if (stack.getTagCompound() != null && stack.getTagCompound().getBoolean(UNBREAKABLE_KEY)) { + return; + } else if (stack.getItem() instanceof ItemGTToolbelt toolbelt) { + ItemStack selectedStack = toolbelt.getSelectedTool(stack); + if (!selectedStack.isEmpty()) { + damageItem(selectedStack, entity, damage); return; } - IGTTool tool = (IGTTool) stack.getItem(); - if (!(entity instanceof EntityPlayer) || !((EntityPlayer) entity).capabilities.isCreativeMode) { - Random random = entity == null ? GTValues.RNG : entity.getRNG(); - if (tool.isElectric()) { - int electricDamage = damage * ConfigHolder.machines.energyUsageMultiplier; - IElectricItem electricItem = stack.getCapability(GregtechCapabilities.CAPABILITY_ELECTRIC_ITEM, - null); - if (electricItem != null) { - electricItem.discharge(electricDamage, tool.getElectricTier(), true, false, false); - if (electricItem.getCharge() > 0 && - random.nextInt(100) >= ConfigHolder.tools.rngDamageElectricTools) { - return; - } - } else { - throw new IllegalStateException( - "Electric tool does not have an attached electric item capability."); - } - } - int unbreakingLevel = EnchantmentHelper.getEnchantmentLevel(Enchantments.UNBREAKING, stack); - int negated = 0; - for (int k = 0; unbreakingLevel > 0 && k < damage; k++) { - if (EnchantmentDurability.negateDamage(stack, unbreakingLevel, random)) { - negated++; + } + if (stack.getTagCompound() != null && stack.getTagCompound().getBoolean(UNBREAKABLE_KEY)) { + return; + } + if (!(entity instanceof EntityPlayer) || !((EntityPlayer) entity).capabilities.isCreativeMode) { + Random random = entity == null ? GTValues.RNG : entity.getRNG(); + if (tool.isElectric()) { + int electricDamage = damage * ConfigHolder.machines.energyUsageMultiplier; + IElectricItem electricItem = stack.getCapability(GregtechCapabilities.CAPABILITY_ELECTRIC_ITEM, + null); + if (electricItem != null) { + electricItem.discharge(electricDamage, tool.getElectricTier(), true, false, false); + if (electricItem.getCharge() > 0 && + random.nextInt(100) >= ConfigHolder.tools.rngDamageElectricTools) { + return; } + } else { + throw new IllegalStateException( + "Electric tool does not have an attached electric item capability."); } - damage -= negated; - if (damage <= 0) { - return; - } - int newDurability = stack.getItemDamage() + damage; - if (entity instanceof EntityPlayerMP) { - CriteriaTriggers.ITEM_DURABILITY_CHANGED.trigger((EntityPlayerMP) entity, stack, newDurability); + } + int unbreakingLevel = EnchantmentHelper.getEnchantmentLevel(Enchantments.UNBREAKING, stack); + int negated = 0; + for (int k = 0; unbreakingLevel > 0 && k < damage; k++) { + if (EnchantmentDurability.negateDamage(stack, unbreakingLevel, random)) { + negated++; } - stack.setItemDamage(newDurability); - if (newDurability > stack.getMaxDamage()) { - if (entity instanceof EntityPlayer) { - StatBase stat = StatList.getObjectBreakStats(stack.getItem()); - if (stat != null) { - ((EntityPlayer) entity).addStat(stat); - } - } - if (entity != null) { - entity.renderBrokenItemStack(stack); + } + damage -= negated; + if (damage <= 0) { + return; + } + int newDurability = stack.getItemDamage() + damage; + if (entity instanceof EntityPlayerMP) { + CriteriaTriggers.ITEM_DURABILITY_CHANGED.trigger((EntityPlayerMP) entity, stack, newDurability); + } + stack.setItemDamage(newDurability); + if (newDurability > stack.getMaxDamage()) { + if (entity instanceof EntityPlayer) { + StatBase stat = StatList.getObjectBreakStats(stack.getItem()); + if (stat != null) { + ((EntityPlayer) entity).addStat(stat); } - stack.shrink(1); } + if (entity != null) { + entity.renderBrokenItemStack(stack); + } + stack.shrink(1); } } } @@ -330,6 +361,7 @@ public static void damageItem(@NotNull ItemStack stack, @Nullable EntityLivingBa */ public static void onActionDone(@NotNull EntityPlayer player, @NotNull World world, @NotNull EnumHand hand) { ItemStack stack = player.getHeldItem(hand); + stack = toolbeltPassthrough(stack); IGTTool tool = (IGTTool) stack.getItem(); ToolHelper.damageItem(stack, player); if (tool.getSound() != null) { @@ -356,6 +388,32 @@ 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); + } + + /** + * @return if the itemstack should be considered a tool + */ + public static boolean isTool(ItemStack tool) { + return tool.getItem() instanceof ItemTool || tool.getItem() instanceof IGTTool; + } + + /** + * @return if the itemstack should be considered a spraycan + */ + 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; + } + /** * Return if all the specified tool classes exists in the tool */ @@ -373,6 +431,7 @@ public static boolean areTools(ItemStack tool, String... toolClasses) { * @return The level of Fortune or Looting that the tool is enchanted with, or zero */ public static int getFortuneOrLootingLevel(ItemStack tool) { + tool = toolbeltPassthrough(tool); if (tool.getItem() instanceof ItemGTSword) { return EnchantmentHelper.getEnchantmentLevel(Enchantments.LOOTING, tool); } else if (tool.getItem() instanceof IGTTool) { @@ -394,6 +453,7 @@ public static AoESymmetrical getAoEDefinition(ItemStack stack) { * AoE Block Breaking Routine. */ public static boolean areaOfEffectBlockBreakRoutine(ItemStack stack, EntityPlayerMP player) { + stack = toolbeltPassthrough(stack); int currentDurability = getToolTag(stack).getInteger(DURABILITY_KEY); int maximumDurability = getToolTag(stack).getInteger(MAX_DURABILITY_KEY); int remainingUses = maximumDurability - currentDurability; @@ -410,7 +470,9 @@ public static boolean areaOfEffectBlockBreakRoutine(ItemStack stack, EntityPlaye return true; } // If the tool is an electric tool, catch the tool breaking and cancel the remaining AOE - else if (!player.getHeldItemMainhand().isItemEqualIgnoreDurability(stack)) { + ItemStack tool = player.getHeldItemMainhand(); + tool = toolbeltPassthrough(tool); + if (!tool.isItemEqualIgnoreDurability(stack)) { return true; } } @@ -422,6 +484,7 @@ else if (!player.getHeldItemMainhand().isItemEqualIgnoreDurability(stack)) { public static Set iterateAoE(ItemStack stack, AoESymmetrical aoeDefinition, World world, EntityPlayer player, RayTraceResult rayTraceResult, QuintFunction function) { + stack = toolbeltPassthrough(stack); if (aoeDefinition != AoESymmetrical.none() && rayTraceResult != null && rayTraceResult.typeOfHit == RayTraceResult.Type.BLOCK && rayTraceResult.sideHit != null) { int column = aoeDefinition.column; @@ -669,7 +732,6 @@ public static void applyHammerDropConversion(ItemStack tool, IBlockState state, } } - @SuppressWarnings("BooleanMethodIsAlwaysInverted") public static boolean breakBlockRoutine(EntityPlayerMP player, ItemStack tool, BlockPos pos) { // This is *not* a vanilla/forge convention, Forge never added "shears" to ItemShear's tool classes. if (isTool(tool, ToolClasses.SHEARS) && shearBlockRoutine(player, tool, pos) == 0) { @@ -720,6 +782,7 @@ public static boolean breakBlockRoutine(EntityPlayerMP player, ItemStack tool, B */ public static int shearBlockRoutine(EntityPlayerMP player, ItemStack tool, BlockPos pos) { if (!player.isCreative()) { + tool = toolbeltPassthrough(tool); World world = player.world; IBlockState state = world.getBlockState(pos); if (state.getBlock() instanceof IShearable) { @@ -778,8 +841,15 @@ public static ItemStack getSilkTouchDrop(@NotNull IBlockState state) { } public static void playToolSound(ItemStack stack, EntityPlayer player) { + stack = toolbeltPassthrough(stack); if (stack.getItem() instanceof IGTTool) { ((IGTTool) stack.getItem()).playSound(player); } } + + public static boolean hasMaterial(ItemStack stack) { + var tag = stack.getTagCompound(); + if (tag == null || !tag.hasKey(TOOL_TAG_KEY)) return false; + return tag.getCompoundTag(TOOL_TAG_KEY).hasKey(MATERIAL_KEY); + } } diff --git a/src/main/java/gregtech/api/items/toolitem/aoe/AoESymmetrical.java b/src/main/java/gregtech/api/items/toolitem/aoe/AoESymmetrical.java index a1dfc249012..8f3276f6212 100644 --- a/src/main/java/gregtech/api/items/toolitem/aoe/AoESymmetrical.java +++ b/src/main/java/gregtech/api/items/toolitem/aoe/AoESymmetrical.java @@ -3,6 +3,7 @@ import gregtech.api.items.toolitem.ToolHelper; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.math.MathHelper; import net.minecraftforge.common.util.Constants; import com.google.common.base.Preconditions; @@ -73,70 +74,46 @@ public static int getLayer(NBTTagCompound tag, AoESymmetrical defaultDefinition) return defaultDefinition.layer; } + public static void setColumn(NBTTagCompound tag, int i, AoESymmetrical defaultDefinition) { + tag.setInteger(ToolHelper.AOE_COLUMN_KEY, MathHelper.clamp(i, 0, defaultDefinition.column)); + } + + public static void setRow(NBTTagCompound tag, int i, AoESymmetrical defaultDefinition) { + tag.setInteger(ToolHelper.AOE_ROW_KEY, MathHelper.clamp(i, 0, defaultDefinition.row)); + } + + public static void setLayer(NBTTagCompound tag, int i, AoESymmetrical defaultDefinition) { + tag.setInteger(ToolHelper.AOE_LAYER_KEY, MathHelper.clamp(i, 0, defaultDefinition.layer)); + } + public static void increaseColumn(NBTTagCompound tag, AoESymmetrical defaultDefinition) { - if (!tag.hasKey(ToolHelper.AOE_COLUMN_KEY, Constants.NBT.TAG_INT)) { - tag.setInteger(ToolHelper.AOE_COLUMN_KEY, defaultDefinition.column); - } else { - int currentColumn = tag.getInteger(ToolHelper.AOE_COLUMN_KEY); - if (currentColumn < defaultDefinition.column) { - tag.setInteger(ToolHelper.AOE_COLUMN_KEY, currentColumn + 1); - } - } + int currentColumn = getColumn(tag, defaultDefinition); + setColumn(tag, currentColumn + 1, defaultDefinition); } public static void increaseRow(NBTTagCompound tag, AoESymmetrical defaultDefinition) { - if (!tag.hasKey(ToolHelper.AOE_ROW_KEY, Constants.NBT.TAG_INT)) { - tag.setInteger(ToolHelper.AOE_ROW_KEY, defaultDefinition.row); - } else { - int currentRow = tag.getInteger(ToolHelper.AOE_ROW_KEY); - if (currentRow < defaultDefinition.row) { - tag.setInteger(ToolHelper.AOE_ROW_KEY, currentRow + 1); - } - } + int currentRow = getRow(tag, defaultDefinition); + setRow(tag, currentRow + 1, defaultDefinition); } public static void increaseLayer(NBTTagCompound tag, AoESymmetrical defaultDefinition) { - if (!tag.hasKey(ToolHelper.AOE_LAYER_KEY, Constants.NBT.TAG_INT)) { - tag.setInteger(ToolHelper.AOE_LAYER_KEY, defaultDefinition.layer); - } else { - int currentLayer = tag.getInteger(ToolHelper.AOE_LAYER_KEY); - if (currentLayer < defaultDefinition.layer) { - tag.setInteger(ToolHelper.AOE_LAYER_KEY, currentLayer + 1); - } - } + int currentLayer = getLayer(tag, defaultDefinition); + setLayer(tag, currentLayer + 1, defaultDefinition); } public static void decreaseColumn(NBTTagCompound tag, AoESymmetrical defaultDefinition) { - if (!tag.hasKey(ToolHelper.AOE_COLUMN_KEY, Constants.NBT.TAG_INT)) { - tag.setInteger(ToolHelper.AOE_COLUMN_KEY, defaultDefinition.column); - } else { - int currentColumn = tag.getInteger(ToolHelper.AOE_COLUMN_KEY); - if (currentColumn > 0) { - tag.setInteger(ToolHelper.AOE_COLUMN_KEY, currentColumn - 1); - } - } + int currentColumn = getColumn(tag, defaultDefinition); + setColumn(tag, currentColumn - 1, defaultDefinition); } public static void decreaseRow(NBTTagCompound tag, AoESymmetrical defaultDefinition) { - if (!tag.hasKey(ToolHelper.AOE_ROW_KEY, Constants.NBT.TAG_INT)) { - tag.setInteger(ToolHelper.AOE_ROW_KEY, defaultDefinition.row); - } else { - int currentRow = tag.getInteger(ToolHelper.AOE_ROW_KEY); - if (currentRow > 0) { - tag.setInteger(ToolHelper.AOE_ROW_KEY, currentRow - 1); - } - } + int currentRow = getRow(tag, defaultDefinition); + setRow(tag, currentRow - 1, defaultDefinition); } public static void decreaseLayer(NBTTagCompound tag, AoESymmetrical defaultDefinition) { - if (!tag.hasKey(ToolHelper.AOE_LAYER_KEY, Constants.NBT.TAG_INT)) { - tag.setInteger(ToolHelper.AOE_LAYER_KEY, defaultDefinition.layer); - } else { - int currentLayer = tag.getInteger(ToolHelper.AOE_LAYER_KEY); - if (currentLayer > 0) { - tag.setInteger(ToolHelper.AOE_LAYER_KEY, currentLayer - 1); - } - } + int currentLayer = getLayer(tag, defaultDefinition); + setLayer(tag, currentLayer - 1, defaultDefinition); } public static AoESymmetrical none() { diff --git a/src/main/java/gregtech/api/items/toolitem/behavior/IToolBehavior.java b/src/main/java/gregtech/api/items/toolitem/behavior/IToolBehavior.java index 4131cf59218..77dd5679b68 100644 --- a/src/main/java/gregtech/api/items/toolitem/behavior/IToolBehavior.java +++ b/src/main/java/gregtech/api/items/toolitem/behavior/IToolBehavior.java @@ -79,7 +79,10 @@ default boolean canDisableShield(ItemStack stack, ItemStack shield, EntityLiving } /** - * Called when a Block is right-clicked with this Item, but before the block is activated + * Called when a Block is right-clicked with this Item, but before the block is activated. + * If actions not going through {@link gregtech.api.items.toolitem.ToolHelper} are performed, such as + * {@link ItemStack#shrink(int)}, don't forget to perform toolbelt passthrough via + * {@link gregtech.api.items.toolitem.ToolHelper#toolbeltPassthrough(ItemStack)} * * @param player the player clicking with the item * @param world the world in which the block is clicked @@ -97,7 +100,10 @@ default EnumActionResult onItemUseFirst(@NotNull EntityPlayer player, @NotNull W } /** - * Called when a Block is right-clicked with this Item + * Called when a Block is right-clicked with this Item. + * If actions not going through {@link gregtech.api.items.toolitem.ToolHelper} are performed, such as + * {@link ItemStack#shrink(int)}, don't forget to perform toolbelt passthrough via + * {@link gregtech.api.items.toolitem.ToolHelper#toolbeltPassthrough(ItemStack)} * * @param player the player clicking with the item * @param world the world in which the block is clicked @@ -117,6 +123,9 @@ default EnumActionResult onItemUse(@NotNull EntityPlayer player, @NotNull World /** * Called when the equipped item is right-clicked. + * If actions not going through {@link gregtech.api.items.toolitem.ToolHelper} are performed, such as + * {@link ItemStack#shrink(int)}, don't forget to perform toolbelt passthrough via + * {@link gregtech.api.items.toolitem.ToolHelper#toolbeltPassthrough(ItemStack)} * * @param world the world in which the click happened * @param player the player clicking the item diff --git a/src/main/java/gregtech/api/mui/GTGuiTextures.java b/src/main/java/gregtech/api/mui/GTGuiTextures.java index 4f0345f49fc..e2abb861c4b 100644 --- a/src/main/java/gregtech/api/mui/GTGuiTextures.java +++ b/src/main/java/gregtech/api/mui/GTGuiTextures.java @@ -2,6 +2,8 @@ import gregtech.api.GTValues; +import com.cleanroommc.modularui.api.drawable.IDrawable; +import com.cleanroommc.modularui.api.drawable.IKey; import com.cleanroommc.modularui.drawable.UITexture; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -176,6 +178,9 @@ public static class IDs { public static final UITexture OREDICT_WAITING = fullImage("textures/gui/widget/ore_filter/waiting.png"); public static final UITexture OREDICT_WARN = fullImage("textures/gui/widget/ore_filter/warn.png"); + public static final IDrawable PLUS = IKey.str("+").asIcon().marginLeft(1); + public static final IDrawable MINUS = IKey.str("-").asIcon().marginLeft(1); + public static final UITexture[] MANUAL_IO_OVERLAY_IN = slice("textures/gui/overlay/manual_io_overlay_in.png", 18, 18 * 3, 18, 18, true); public static final UITexture[] MANUAL_IO_OVERLAY_OUT = slice("textures/gui/overlay/manual_io_overlay_out.png", @@ -353,6 +358,8 @@ public static class IDs { public static final UITexture BUTTON_CROSS = fullImage("textures/gui/widget/button_cross.png"); public static final UITexture BUTTON_REDSTONE_ON = fullImage("textures/gui/widget/button_redstone_on.png"); public static final UITexture BUTTON_REDSTONE_OFF = fullImage("textures/gui/widget/button_redstone_off.png"); + public static final UITexture BUTTON_THROTTLE_PLUS = fullImage("textures/gui/widget/button_throttle_plus.png"); + public static final UITexture BUTTON_THROTTLE_MINUS = fullImage("textures/gui/widget/button_throttle_minus.png"); // PROGRESS BARS public static final UITexture PROGRESS_BAR_ARC_FURNACE = progressBar( diff --git a/src/main/java/gregtech/asm/GregTechTransformer.java b/src/main/java/gregtech/asm/GregTechTransformer.java index 9a13badeaf5..c3b906dd4ae 100644 --- a/src/main/java/gregtech/asm/GregTechTransformer.java +++ b/src/main/java/gregtech/asm/GregTechTransformer.java @@ -222,17 +222,24 @@ public byte[] transform(String name, String transformedName, byte[] basicClass) // 0); // return classWriter.toByteArray(); // } + // case OreIngredientVisitor.TARGET_CLASS_NAME: { + // ClassReader classReader = new ClassReader(basicClass); + // ClassWriter classWriter = new ClassWriter(0); + // classReader.accept(new TargetClassVisitor(classWriter, OreIngredientVisitor.TARGET_METHOD, + // OreIngredientVisitor::new), 0); + // return classWriter.toByteArray(); + // } + /* + * if (EnchantmentCanApplyVisitor.CLASS_TO_MAPPING_MAP.containsKey(internalName)) { + * ObfMapping methodMapping = EnchantmentCanApplyVisitor.CLASS_TO_MAPPING_MAP.get(internalName); + * ClassReader classReader = new ClassReader(basicClass); + * ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); + * classReader.accept(new TargetClassVisitor(classWriter, methodMapping, + * mv -> new EnchantmentCanApplyVisitor(mv, methodMapping)), ClassReader.EXPAND_FRAMES); + * return classWriter.toByteArray(); + * } + */ } - /* - * if (EnchantmentCanApplyVisitor.CLASS_TO_MAPPING_MAP.containsKey(internalName)) { - * ObfMapping methodMapping = EnchantmentCanApplyVisitor.CLASS_TO_MAPPING_MAP.get(internalName); - * ClassReader classReader = new ClassReader(basicClass); - * ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); - * classReader.accept(new TargetClassVisitor(classWriter, methodMapping, - * mv -> new EnchantmentCanApplyVisitor(mv, methodMapping)), ClassReader.EXPAND_FRAMES); - * return classWriter.toByteArray(); - * } - */ return basicClass; } } diff --git a/src/main/java/gregtech/client/ClientProxy.java b/src/main/java/gregtech/client/ClientProxy.java index 35969198260..03783021f60 100644 --- a/src/main/java/gregtech/client/ClientProxy.java +++ b/src/main/java/gregtech/client/ClientProxy.java @@ -4,10 +4,12 @@ import gregtech.api.fluids.GTFluidRegistration; import gregtech.api.items.metaitem.MetaOreDictItem; import gregtech.api.items.toolitem.IGTTool; +import gregtech.api.items.toolitem.ItemGTToolbelt; import gregtech.api.unification.OreDictUnifier; import gregtech.api.unification.material.Materials; import gregtech.api.unification.stack.UnificationEntry; import gregtech.api.util.FluidTooltipUtil; +import gregtech.api.util.GTLog; import gregtech.api.util.IBlockOre; import gregtech.api.util.Mods; import gregtech.client.model.customtexture.CustomTextureModelHandler; @@ -34,10 +36,16 @@ import net.minecraft.block.Block; import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.client.gui.GuiIngame; +import net.minecraft.client.gui.ScaledResolution; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.RenderHelper; import net.minecraft.client.renderer.texture.TextureMap; 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; @@ -47,7 +55,11 @@ import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.client.GuiIngameForge; +import net.minecraftforge.client.event.ModelBakeEvent; import net.minecraftforge.client.event.ModelRegistryEvent; +import net.minecraftforge.client.event.MouseEvent; +import net.minecraftforge.client.event.RenderGameOverlayEvent; import net.minecraftforge.client.event.TextureStitchEvent; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.Constants; @@ -135,6 +147,14 @@ public static void registerModels(ModelRegistryEvent event) { ToolItems.registerModels(); } + @SubscribeEvent + @SideOnly(Side.CLIENT) + public static void registerBakedModels(ModelBakeEvent event) { + GTLog.logger.info("Registering special item models"); + MetaItems.registerBakedModels(event); + ToolItems.registerBakedModels(event); + } + @SubscribeEvent public static void addMaterialFormulaHandler(@NotNull ItemTooltipEvent event) { ItemStack itemStack = event.getItemStack(); @@ -326,4 +346,83 @@ private static boolean hasActuallyAdvancedInfo(List tooltip) { public boolean isFancyGraphics() { return Minecraft.getMinecraft().gameSettings.fancyGraphics; } + + @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); + } + } + } + + @SubscribeEvent + public static void onRenderGameOverlayPostEvent(RenderGameOverlayEvent.Post event) { + if (!ConfigHolder.client.toolbeltConfig.enableToolbeltHotbarDisplay) return; + if (event.getType() == RenderGameOverlayEvent.ElementType.HOTBAR) { + if (Minecraft.getMinecraft().ingameGUI instanceof GuiIngameForge gui) { + ItemStack stack = Minecraft.getMinecraft().player.getHeldItemMainhand(); + if (stack.getItem() instanceof ItemGTToolbelt toolbelt) { + renderToolbeltHotbar(gui, stack, toolbelt, event.getResolution(), event.getPartialTicks()); + } + } + } + } + + private static void renderToolbeltHotbar(GuiIngameForge gui, ItemStack stack, ItemGTToolbelt toolbelt, + ScaledResolution sr, float partialTicks) { + Minecraft mc = Minecraft.getMinecraft(); + int offset = 31; + int slots = Math.min(9, toolbelt.getSlotCount(stack)); + GuiIngameForge.left_height += offset - 6; + if (slots > 4) { + GuiIngameForge.right_height += offset - 6; + } + if (mc.getRenderViewEntity() instanceof EntityPlayer entityplayer) { + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + mc.getTextureManager().bindTexture(GuiIngame.WIDGETS_TEX_PATH); + int i = sr.getScaledWidth() / 2; + float f = gui.zLevel; + gui.zLevel = -90.0F; + GlStateManager.enableBlend(); + GlStateManager.tryBlendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, + GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, + GlStateManager.DestFactor.ZERO); + // draw the left side of the hotbar + gui.drawTexturedModalRect(i - 91, sr.getScaledHeight() - 22 - offset, 0, 0, slots * 20 - 18, 22); + // draw the endpiece to the hotbar + gui.drawTexturedModalRect(i - 91 + slots * 20 - 18, sr.getScaledHeight() - 22 - offset, 162, 0, 20, 22); + int selected = toolbelt.getSelectedSlot(stack); + if (selected != -1) { + gui.drawTexturedModalRect(i - 91 - 1 + selected * 20, sr.getScaledHeight() - 22 - 1 - offset, 0, 22, 24, + 24); + } + gui.zLevel = -80f; + GlStateManager.enableRescaleNormal(); + RenderHelper.enableGUIStandardItemLighting(); + + for (int l = 0; l < slots; ++l) { + ItemStack stack1 = toolbelt.getToolInSlot(stack, l); + if (stack1.isEmpty()) continue; + int i1 = i - 90 + l * 20 + 2; + int j1 = sr.getScaledHeight() - 16 - 3 - offset; + gui.renderHotbarItem(i1, j1, partialTicks, entityplayer, stack1); + } + + gui.zLevel = f; + + RenderHelper.disableStandardItemLighting(); + GlStateManager.disableRescaleNormal(); + GlStateManager.disableBlend(); + } + } } diff --git a/src/main/java/gregtech/client/renderer/handler/ToolbeltRenderer.java b/src/main/java/gregtech/client/renderer/handler/ToolbeltRenderer.java new file mode 100644 index 00000000000..bdeacb59ff8 --- /dev/null +++ b/src/main/java/gregtech/client/renderer/handler/ToolbeltRenderer.java @@ -0,0 +1,60 @@ +package gregtech.client.renderer.handler; + +import gregtech.api.items.toolitem.ItemGTToolbelt; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.RenderItem; +import net.minecraft.client.renderer.block.model.IBakedModel; +import net.minecraft.client.renderer.block.model.ItemCameraTransforms; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.model.IModelState; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import codechicken.lib.render.item.IItemRenderer; +import codechicken.lib.util.TransformUtils; + +@SideOnly(Side.CLIENT) +public class ToolbeltRenderer implements IItemRenderer { + + private final IBakedModel toolbeltModel; + + public ToolbeltRenderer(IBakedModel toolbeltModel) { + this.toolbeltModel = toolbeltModel; + } + + @Override + public void renderItem(ItemStack stack, ItemCameraTransforms.TransformType transformType) { + if (stack.getItem() instanceof ItemGTToolbelt toolbelt) { + GlStateManager.pushMatrix(); + GlStateManager.translate(0.5F, 0.5F, 0.5F); + + RenderItem renderItem = Minecraft.getMinecraft().getRenderItem(); + + ItemStack selected = toolbelt.getSelectedTool(stack); + if (!selected.isEmpty()) { + IBakedModel selectedModel = renderItem.getItemModelWithOverrides(selected, null, null); + renderItem.renderItem(selected, selectedModel); + } + renderItem.renderItem(stack, toolbeltModel); + + GlStateManager.popMatrix(); + } + } + + @Override + public IModelState getTransforms() { + return TransformUtils.DEFAULT_TOOL; + } + + @Override + public boolean isAmbientOcclusion() { + return false; + } + + @Override + public boolean isGui3d() { + return false; + } +} diff --git a/src/main/java/gregtech/client/utils/ToolChargeBarRenderer.java b/src/main/java/gregtech/client/utils/ToolChargeBarRenderer.java index de982d07450..c8ffd11b817 100644 --- a/src/main/java/gregtech/client/utils/ToolChargeBarRenderer.java +++ b/src/main/java/gregtech/client/utils/ToolChargeBarRenderer.java @@ -5,6 +5,7 @@ import gregtech.api.items.metaitem.MetaItem; import gregtech.api.items.metaitem.stats.IItemDurabilityManager; import gregtech.api.items.toolitem.IGTTool; +import gregtech.api.items.toolitem.ItemGTToolbelt; import gregtech.api.items.toolitem.ToolHelper; import gregtech.api.util.GTUtility; @@ -113,6 +114,13 @@ private static void overpaintVanillaRenderBug(BufferBuilder worldrenderer, int x } public static void renderBarsTool(IGTTool tool, ItemStack stack, int xPosition, int yPosition) { + if (tool instanceof ItemGTToolbelt toolbelt) { + ItemStack selected = toolbelt.getSelectedTool(stack); + if (!selected.isEmpty() && selected.getItem() instanceof IGTTool toool) { + tool = toool; + stack = selected; + } + } boolean renderedDurability = false; NBTTagCompound tag = GTUtility.getOrCreateNbtCompound(stack); if (!tag.getBoolean(ToolHelper.UNBREAKABLE_KEY)) { diff --git a/src/main/java/gregtech/common/ConfigHolder.java b/src/main/java/gregtech/common/ConfigHolder.java index 7127a2ab688..5ecec7ab1b0 100644 --- a/src/main/java/gregtech/common/ConfigHolder.java +++ b/src/main/java/gregtech/common/ConfigHolder.java @@ -381,6 +381,9 @@ public static class ClientOptions { @Config.Name("Shader Options") public ShaderOptions shader = new ShaderOptions(); + @Config.Name("Toolbelt Config") + public ToolbeltConfig toolbeltConfig = new ToolbeltConfig(); + @Config.Comment({ "Terminal root path.", "Default: {.../config}/gregtech/terminal" }) @Config.RequiresMcRestart public String terminalRootPath = "gregtech/terminal"; @@ -554,6 +557,22 @@ public static class ShaderOptions { @Config.RangeDouble(min = 0) public double step = 1; } + + public static class ToolbeltConfig { + + @Config.Comment({ "Enable the capturing of hotbar scroll while sneaking by a selected toolbelt.", + "Default: true" }) + public boolean enableToolbeltScrollingCapture = true; + + @Config.Comment({ "Enable the capturing of hotbar keypresses while sneaking by a selected toolbelt.", + "Default: true" }) + public boolean enableToolbeltKeypressCapture = true; + + @Config.Comment({ + "Enable the display of a second hotbar representing the toolbelt's inventory when one is selected.", + "Default: true" }) + public boolean enableToolbeltHotbarDisplay = true; + } } public static class FusionBloom { diff --git a/src/main/java/gregtech/common/ToolEventHandlers.java b/src/main/java/gregtech/common/ToolEventHandlers.java index 62195e49791..e503b539547 100644 --- a/src/main/java/gregtech/common/ToolEventHandlers.java +++ b/src/main/java/gregtech/common/ToolEventHandlers.java @@ -165,6 +165,7 @@ public static void onHarvestDrops(@NotNull BlockEvent.HarvestDropsEvent event) { // only try once, so future water placement does not get eaten too return false; }); + stack = ToolHelper.toolbeltPassthrough(stack); ((IGTTool) stack.getItem()).playSound(player); } } diff --git a/src/main/java/gregtech/common/crafting/DyeableRecipes.java b/src/main/java/gregtech/common/crafting/DyeableRecipes.java new file mode 100644 index 00000000000..fa975fd4197 --- /dev/null +++ b/src/main/java/gregtech/common/crafting/DyeableRecipes.java @@ -0,0 +1,142 @@ +package gregtech.common.crafting; + +import gregtech.api.items.IDyeableItem; + +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.RecipesArmorDyes; +import net.minecraft.util.NonNullList; +import net.minecraft.world.World; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * Composed almost entirely of code taken from {@link RecipesArmorDyes} and modified to apply to {@link IDyeableItem} + * instead of just leather armor. Thus, will be difficult to parse. + */ +public final class DyeableRecipes extends RecipesArmorDyes { + + @ApiStatus.Internal + public DyeableRecipes() { + this.setRegistryName("gt_dyeable_dyeing"); + } + + /** + * Used to check if a recipe matches current crafting inventory + */ + @Override + public boolean matches(InventoryCrafting inv, @NotNull World worldIn) { + ItemStack itemstack = ItemStack.EMPTY; + List list = new ObjectArrayList<>(); + + for (int i = 0; i < inv.getSizeInventory(); ++i) { + ItemStack itemstack1 = inv.getStackInSlot(i); + + if (!itemstack1.isEmpty()) { + if (itemstack1.getItem() instanceof IDyeableItem) { + if (!itemstack.isEmpty()) { + return false; + } + + itemstack = itemstack1; + } else { + if (!net.minecraftforge.oredict.DyeUtils.isDye(itemstack1)) { + return false; + } + + list.add(itemstack1); + } + } + } + + return !itemstack.isEmpty() && !list.isEmpty(); + } + + /** + * Returns an Item that is the result of this recipe + */ + @Override + public @NotNull ItemStack getCraftingResult(InventoryCrafting inv) { + ItemStack itemstack = ItemStack.EMPTY; + int[] aint = new int[3]; + int i = 0; + int j = 0; + IDyeableItem dyeable = null; + + for (int k = 0; k < inv.getSizeInventory(); ++k) { + ItemStack itemstack1 = inv.getStackInSlot(k); + + if (!itemstack1.isEmpty()) { + if (itemstack1.getItem() instanceof IDyeableItem) { + dyeable = (IDyeableItem) itemstack1.getItem(); + + itemstack = itemstack1.copy(); + itemstack.setCount(1); + + if (dyeable.hasColor(itemstack1)) { + int l = dyeable.getColor(itemstack); + float f = (float) (l >> 16 & 255) / 255.0F; + float f1 = (float) (l >> 8 & 255) / 255.0F; + float f2 = (float) (l & 255) / 255.0F; + i = (int) ((float) i + Math.max(f, Math.max(f1, f2)) * 255.0F); + aint[0] = (int) ((float) aint[0] + f * 255.0F); + aint[1] = (int) ((float) aint[1] + f1 * 255.0F); + aint[2] = (int) ((float) aint[2] + f2 * 255.0F); + ++j; + } + } else { + if (!net.minecraftforge.oredict.DyeUtils.isDye(itemstack1)) { + return ItemStack.EMPTY; + } + + float[] afloat = net.minecraftforge.oredict.DyeUtils.colorFromStack(itemstack1).get() + .getColorComponentValues(); + int l1 = (int) (afloat[0] * 255.0F); + int i2 = (int) (afloat[1] * 255.0F); + int j2 = (int) (afloat[2] * 255.0F); + i += Math.max(l1, Math.max(i2, j2)); + aint[0] += l1; + aint[1] += i2; + aint[2] += j2; + ++j; + } + } + } + + if (dyeable == null) { + return ItemStack.EMPTY; + } else { + int i1 = aint[0] / j; + int j1 = aint[1] / j; + int k1 = aint[2] / j; + float f3 = (float) i / (float) j; + float f4 = (float) Math.max(i1, Math.max(j1, k1)); + i1 = (int) ((float) i1 * f3 / f4); + j1 = (int) ((float) j1 * f3 / f4); + k1 = (int) ((float) k1 * f3 / f4); + int k2 = (i1 << 8) + j1; + k2 = (k2 << 8) + k1; + // prevent consuming dye when the color would not change + if (dyeable.hasColor(itemstack) && dyeable.getColor(itemstack) == k2) return ItemStack.EMPTY; + dyeable.setColor(itemstack, k2); + return itemstack; + } + } + + @Override + public @NotNull NonNullList getRemainingItems(InventoryCrafting inv) { + NonNullList nonnulllist = NonNullList.withSize(inv.getSizeInventory(), ItemStack.EMPTY); + + for (int i = 0; i < nonnulllist.size(); ++i) { + ItemStack itemstack = inv.getStackInSlot(i); + if (!(itemstack.getItem() instanceof IDyeableItem item && !item.shouldGetContainerItem())) + nonnulllist.set(i, net.minecraftforge.common.ForgeHooks.getContainerItem(itemstack)); + } + + return nonnulllist; + } +} diff --git a/src/main/java/gregtech/common/crafting/GTShapedOreRecipe.java b/src/main/java/gregtech/common/crafting/GTShapedOreRecipe.java index a014bd89292..fce807d0f04 100644 --- a/src/main/java/gregtech/common/crafting/GTShapedOreRecipe.java +++ b/src/main/java/gregtech/common/crafting/GTShapedOreRecipe.java @@ -43,9 +43,10 @@ public GTShapedOreRecipe(boolean isClearing, ResourceLocation group, @NotNull It } // a copy of the CraftingHelper.ShapedPrimer.parseShaped method. - // the on difference is calling getIngredient of this class. + // the only difference is calling getIngredient of this class. - public static CraftingHelper.ShapedPrimer parseShaped(boolean isClearing, Object... recipe) { + public static CraftingHelper.ShapedPrimer parseShaped(boolean isClearing, + Object... recipe) { CraftingHelper.ShapedPrimer ret = new CraftingHelper.ShapedPrimer(); StringBuilder shape = new StringBuilder(); int idx = 0; @@ -126,23 +127,19 @@ public static CraftingHelper.ShapedPrimer parseShaped(boolean isClearing, Object return ret; } - // a copy of the CraftingHelper getIngredient method. - // the only difference is checking for a filled bucket and making - // it an GTFluidCraftingIngredient protected static Ingredient getIngredient(boolean isClearing, Object obj) { - if (obj instanceof Ingredient) return (Ingredient) obj; - else if (obj instanceof ItemStack) { - ItemStack ing = (ItemStack) obj; - if (ing.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null)) { - IFluidHandlerItem handler = ing.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, + if (obj instanceof Ingredient ing) return ing; + else if (obj instanceof ItemStack stk) { + if (stk.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null)) { + IFluidHandlerItem handler = stk.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null); if (handler != null) { FluidStack drained = handler.drain(Integer.MAX_VALUE, false); if (drained != null && drained.amount > 0) { - return new GTFluidCraftingIngredient(((ItemStack) obj).copy()); + return new GTFluidCraftingIngredient(stk.copy()); } if (!isClearing) { - ItemStack i = ((ItemStack) obj).copy(); + ItemStack i = (stk).copy(); try { return ingredientNBT.newInstance(i); } catch (IllegalAccessException | InvocationTargetException | InstantiationException e) { @@ -152,12 +149,13 @@ else if (obj instanceof ItemStack) { } } } - return Ingredient.fromStacks(((ItemStack) obj).copy()); - } else if (obj instanceof Item) return Ingredient.fromItem((Item) obj); - else if (obj instanceof Block) - return Ingredient.fromStacks(new ItemStack((Block) obj, 1, OreDictionary.WILDCARD_VALUE)); - else if (obj instanceof String) return new OreIngredient((String) obj); - else if (obj instanceof JsonElement) + return Ingredient.fromStacks(stk.copy()); + } else if (obj instanceof Item itm) return Ingredient.fromItem(itm); + else if (obj instanceof Block blk) + return Ingredient.fromStacks(new ItemStack(blk, 1, OreDictionary.WILDCARD_VALUE)); + else if (obj instanceof String str) { + return new OreIngredient(str); + } else if (obj instanceof JsonElement) throw new IllegalArgumentException("JsonObjects must use getIngredient(JsonObject, JsonContext)"); return null; diff --git a/src/main/java/gregtech/common/crafting/GTShapelessOreRecipe.java b/src/main/java/gregtech/common/crafting/GTShapelessOreRecipe.java index 6d0ee689b32..c2f92e47c41 100644 --- a/src/main/java/gregtech/common/crafting/GTShapelessOreRecipe.java +++ b/src/main/java/gregtech/common/crafting/GTShapelessOreRecipe.java @@ -1,42 +1,24 @@ package gregtech.common.crafting; -import gregtech.api.util.GTLog; -import gregtech.api.util.GTStringUtils; - -import net.minecraft.block.Block; import net.minecraft.inventory.InventoryCrafting; -import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.Ingredient; import net.minecraft.util.NonNullList; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.common.crafting.IngredientNBT; -import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fluids.capability.CapabilityFluidHandler; -import net.minecraftforge.fluids.capability.IFluidHandlerItem; -import net.minecraftforge.fml.relauncher.ReflectionHelper; -import net.minecraftforge.oredict.OreDictionary; -import net.minecraftforge.oredict.OreIngredient; import net.minecraftforge.oredict.ShapelessOreRecipe; -import com.google.gson.JsonElement; import org.jetbrains.annotations.NotNull; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; - public class GTShapelessOreRecipe extends ShapelessOreRecipe { boolean isClearing; - public static Constructor ingredientNBT = ReflectionHelper.findConstructor(IngredientNBT.class, - ItemStack.class); public GTShapelessOreRecipe(boolean isClearing, ResourceLocation group, @NotNull ItemStack result, Object... recipe) { super(group, result); this.isClearing = isClearing; for (Object in : recipe) { - Ingredient ing = getIngredient(isClearing, in); + Ingredient ing = GTShapedOreRecipe.getIngredient(isClearing, in); if (ing != null) { input.add(ing); this.isSimple = this.isSimple && ing.isSimple(); @@ -51,43 +33,6 @@ public GTShapelessOreRecipe(boolean isClearing, ResourceLocation group, @NotNull } } - // a copy of the CraftingHelper getIngredient method. - // the only difference is checking for a filled bucket and making - // it an GTFluidCraftingIngredient - private static Ingredient getIngredient(boolean isClearing, Object obj) { - if (obj instanceof Ingredient) return (Ingredient) obj; - else if (obj instanceof ItemStack) { - ItemStack ing = (ItemStack) obj; - if (ing.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null)) { - IFluidHandlerItem handler = ing.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, - null); - if (handler != null) { - FluidStack drained = handler.drain(Integer.MAX_VALUE, false); - if (drained != null && drained.amount > 0) { - return new GTFluidCraftingIngredient(((ItemStack) obj).copy()); - } - if (!isClearing) { - ItemStack i = ((ItemStack) obj).copy(); - try { - return ingredientNBT.newInstance(i); - } catch (IllegalAccessException | InvocationTargetException | InstantiationException e) { - GTLog.logger.error("Failure to instantiate an IngredientNBT of item {}", - GTStringUtils.prettyPrintItemStack(i)); - } - } - } - } - return Ingredient.fromStacks(((ItemStack) obj).copy()); - } else if (obj instanceof Item) return Ingredient.fromItem((Item) obj); - else if (obj instanceof Block) - return Ingredient.fromStacks(new ItemStack((Block) obj, 1, OreDictionary.WILDCARD_VALUE)); - else if (obj instanceof String) return new OreIngredient((String) obj); - else if (obj instanceof JsonElement) - throw new IllegalArgumentException("JsonObjects must use getIngredient(JsonObject, JsonContext)"); - - return null; - } - @Override public @NotNull NonNullList getRemainingItems(@NotNull InventoryCrafting inv) { if (isClearing) { diff --git a/src/main/java/gregtech/common/items/MetaItems.java b/src/main/java/gregtech/common/items/MetaItems.java index 303588e6dc8..eaa75bc932f 100644 --- a/src/main/java/gregtech/common/items/MetaItems.java +++ b/src/main/java/gregtech/common/items/MetaItems.java @@ -19,7 +19,6 @@ import net.minecraft.util.ResourceLocation; import net.minecraftforge.client.event.ModelBakeEvent; import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; @@ -653,7 +652,6 @@ public static void registerColors() { } } - @SubscribeEvent @SideOnly(Side.CLIENT) public static void registerBakedModels(ModelBakeEvent event) { GTLog.logger.info("Registering special item models"); diff --git a/src/main/java/gregtech/common/items/ToolItems.java b/src/main/java/gregtech/common/items/ToolItems.java index 5545387ef7c..a7003d8f4c3 100644 --- a/src/main/java/gregtech/common/items/ToolItems.java +++ b/src/main/java/gregtech/common/items/ToolItems.java @@ -4,17 +4,22 @@ import gregtech.api.items.toolitem.*; import gregtech.api.unification.OreDictUnifier; import gregtech.api.unification.material.Materials; +import gregtech.client.renderer.handler.ToolbeltRenderer; import gregtech.common.items.tool.*; import gregtech.core.sound.GTSoundEvents; import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.block.model.ModelResourceLocation; import net.minecraft.enchantment.EnumEnchantmentType; import net.minecraft.entity.monster.EntityGolem; import net.minecraft.entity.monster.EntitySpider; import net.minecraft.init.Enchantments; import net.minecraft.init.SoundEvents; import net.minecraft.item.ItemStack; +import net.minecraftforge.client.event.ModelBakeEvent; import net.minecraftforge.client.model.ModelLoader; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; import org.jetbrains.annotations.NotNull; @@ -58,6 +63,7 @@ public final class ToolItems { public static IGTTool WIRECUTTER_LV; public static IGTTool WIRECUTTER_HV; public static IGTTool WIRECUTTER_IV; + public static ItemGTToolbelt TOOLBELT; private ToolItems() {/**/} @@ -66,6 +72,8 @@ public static List getAllTools() { } public static void init() { + TOOLBELT = (ItemGTToolbelt) register(new ItemGTToolbelt(GTValues.MODID, "toolbelt", + null, OpenGUIBehavior.INSTANCE)); SWORD = register(ItemGTSword.Builder.of(GTValues.MODID, "sword") .toolStats(b -> b.attacking() .attackDamage(3.0F).attackSpeed(-2.4F)) @@ -369,6 +377,12 @@ public static void registerModels() { TOOLS.forEach(tool -> ModelLoader.setCustomModelResourceLocation(tool.get(), 0, tool.getModelLocation())); } + @SideOnly(Side.CLIENT) + public static void registerBakedModels(ModelBakeEvent event) { + ModelResourceLocation loc = TOOLBELT.getModelLocation(); + event.getModelRegistry().putObject(loc, new ToolbeltRenderer(event.getModelRegistry().getObject(loc))); + } + public static void registerColors() { TOOLS.forEach( tool -> Minecraft.getMinecraft().getItemColors().registerItemColorHandler(tool::getColor, tool.get())); @@ -380,7 +394,9 @@ public static void registerOreDict() { if (tool.getOreDictName() != null) { OreDictUnifier.registerOre(stack, tool.getOreDictName()); } - tool.getSecondaryOreDicts().forEach(oreDict -> OreDictUnifier.registerOre(stack, oreDict)); + tool.getSecondaryOreDicts().forEach(oreDict -> { + OreDictUnifier.registerOre(stack, oreDict); + }); }); } } diff --git a/src/main/java/gregtech/common/items/behaviors/ColorSprayBehaviour.java b/src/main/java/gregtech/common/items/behaviors/ColorSprayBehaviour.java index a52777f70d6..72d164afd19 100644 --- a/src/main/java/gregtech/common/items/behaviors/ColorSprayBehaviour.java +++ b/src/main/java/gregtech/common/items/behaviors/ColorSprayBehaviour.java @@ -1,5 +1,7 @@ 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; @@ -68,6 +70,29 @@ public ActionResult onItemUse(EntityPlayer player, World world, Block 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(); diff --git a/src/main/java/gregtech/common/items/tool/GrassPathBehavior.java b/src/main/java/gregtech/common/items/tool/GrassPathBehavior.java index 4dc960a6a3f..cdb694fbb44 100644 --- a/src/main/java/gregtech/common/items/tool/GrassPathBehavior.java +++ b/src/main/java/gregtech/common/items/tool/GrassPathBehavior.java @@ -40,6 +40,7 @@ public EnumActionResult onItemUse(@NotNull EntityPlayer player, @NotNull World w if (facing == EnumFacing.DOWN) return EnumActionResult.PASS; ItemStack stack = player.getHeldItem(hand); + AoESymmetrical aoeDefinition = ToolHelper.getAoEDefinition(stack); Set blocks; diff --git a/src/main/java/gregtech/common/items/tool/HarvestCropsBehavior.java b/src/main/java/gregtech/common/items/tool/HarvestCropsBehavior.java index d1ac7a4be93..519b275192e 100644 --- a/src/main/java/gregtech/common/items/tool/HarvestCropsBehavior.java +++ b/src/main/java/gregtech/common/items/tool/HarvestCropsBehavior.java @@ -42,6 +42,7 @@ public EnumActionResult onItemUse(@NotNull EntityPlayer player, @NotNull World w if (world.isRemote) { return EnumActionResult.PASS; } + ItemStack stack = player.getHeldItem(hand); AoESymmetrical aoeDefinition = ToolHelper.getAoEDefinition(stack); diff --git a/src/main/java/gregtech/common/items/tool/HoeGroundBehavior.java b/src/main/java/gregtech/common/items/tool/HoeGroundBehavior.java index 36477d16801..9fef46e5e5d 100644 --- a/src/main/java/gregtech/common/items/tool/HoeGroundBehavior.java +++ b/src/main/java/gregtech/common/items/tool/HoeGroundBehavior.java @@ -47,6 +47,7 @@ public EnumActionResult onItemUse(@NotNull EntityPlayer player, @NotNull World w if (facing == EnumFacing.DOWN) return EnumActionResult.PASS; ItemStack stack = player.getHeldItem(hand); + AoESymmetrical aoeDefinition = ToolHelper.getAoEDefinition(stack); Set blocks; diff --git a/src/main/java/gregtech/common/items/tool/OpenGUIBehavior.java b/src/main/java/gregtech/common/items/tool/OpenGUIBehavior.java new file mode 100644 index 00000000000..4d0a43d71b0 --- /dev/null +++ b/src/main/java/gregtech/common/items/tool/OpenGUIBehavior.java @@ -0,0 +1,30 @@ +package gregtech.common.items.tool; + +import gregtech.api.items.toolitem.behavior.IToolBehavior; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ActionResult; +import net.minecraft.util.EnumActionResult; +import net.minecraft.util.EnumHand; +import net.minecraft.world.World; + +import com.cleanroommc.modularui.factory.ItemGuiFactory; +import org.jetbrains.annotations.NotNull; + +public class OpenGUIBehavior implements IToolBehavior { + + public static final OpenGUIBehavior INSTANCE = new OpenGUIBehavior(); + + protected OpenGUIBehavior() {} + + @Override + public @NotNull ActionResult onItemRightClick(@NotNull World world, @NotNull EntityPlayer player, + @NotNull EnumHand hand) { + if (!world.isRemote) { + ItemGuiFactory.open((EntityPlayerMP) player, hand); + } + return ActionResult.newResult(EnumActionResult.SUCCESS, player.getHeldItem(hand)); + } +} diff --git a/src/main/java/gregtech/common/items/tool/TorchPlaceBehavior.java b/src/main/java/gregtech/common/items/tool/TorchPlaceBehavior.java index d9d5f86ef69..8edee2b537b 100644 --- a/src/main/java/gregtech/common/items/tool/TorchPlaceBehavior.java +++ b/src/main/java/gregtech/common/items/tool/TorchPlaceBehavior.java @@ -40,8 +40,7 @@ protected TorchPlaceBehavior() {/**/} public EnumActionResult onItemUse(@NotNull EntityPlayer player, @NotNull World world, @NotNull BlockPos pos, @NotNull EnumHand hand, @NotNull EnumFacing facing, float hitX, float hitY, float hitZ) { - ItemStack stack = player.getHeldItem(hand); - NBTTagCompound behaviourTag = ToolHelper.getBehaviorsTag(stack); + NBTTagCompound behaviourTag = ToolHelper.getBehaviorsTag(player.getHeldItem(hand)); if (behaviourTag.getBoolean(ToolHelper.TORCH_PLACING_KEY)) { int cachedTorchSlot; ItemStack slotStack; diff --git a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityMaintenanceHatch.java b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityMaintenanceHatch.java index 31c87a58251..6c8e3cd2e97 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityMaintenanceHatch.java +++ b/src/main/java/gregtech/common/metatileentities/multi/multiblockpart/MetaTileEntityMaintenanceHatch.java @@ -9,6 +9,7 @@ import gregtech.api.gui.widgets.ClickButtonWidget; import gregtech.api.gui.widgets.SlotWidget; import gregtech.api.items.itemhandlers.GTItemStackHandler; +import gregtech.api.items.toolitem.ItemGTToolbelt; import gregtech.api.items.toolitem.ToolClasses; import gregtech.api.items.toolitem.ToolHelper; import gregtech.api.metatileentity.MetaTileEntity; @@ -45,6 +46,7 @@ import codechicken.lib.render.CCRenderState; import codechicken.lib.render.pipeline.IVertexOperation; import codechicken.lib.vec.Matrix4; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -260,24 +262,12 @@ private void fixProblemsWithTools(byte problems, EntityPlayer entityPlayer) { if (((problems >> index) & 1) == 0) { proceed = true; switch (index) { - case 0: - toolsToMatch.set(0, ToolClasses.WRENCH); - break; - case 1: - toolsToMatch.set(1, ToolClasses.SCREWDRIVER); - break; - case 2: - toolsToMatch.set(2, ToolClasses.SOFT_MALLET); - break; - case 3: - toolsToMatch.set(3, ToolClasses.HARD_HAMMER); - break; - case 4: - toolsToMatch.set(4, ToolClasses.WIRE_CUTTER); - break; - case 5: - toolsToMatch.set(5, ToolClasses.CROWBAR); - break; + case 0 -> toolsToMatch.set(0, ToolClasses.WRENCH); + case 1 -> toolsToMatch.set(1, ToolClasses.SCREWDRIVER); + case 2 -> toolsToMatch.set(2, ToolClasses.SOFT_MALLET); + case 3 -> toolsToMatch.set(3, ToolClasses.HARD_HAMMER); + case 4 -> toolsToMatch.set(4, ToolClasses.WIRE_CUTTER); + case 5 -> toolsToMatch.set(5, ToolClasses.CROWBAR); } } } @@ -285,38 +275,66 @@ private void fixProblemsWithTools(byte problems, EntityPlayer entityPlayer) { return; } + mainfor: for (int i = 0; i < toolsToMatch.size(); i++) { String toolToMatch = toolsToMatch.get(i); if (toolToMatch != null) { // Try to use the item in the player's "hand" (under the cursor) ItemStack heldItem = entityPlayer.inventory.getItemStack(); - if (ToolHelper.isTool(heldItem, toolToMatch)) { + if (heldItem.getItem() instanceof ItemGTToolbelt toolbelt) { + if (toolbelt.damageAgainstMaintenanceProblem(heldItem, toolToMatch, entityPlayer)) { + ((IMaintenance) getController()).setMaintenanceFixed(i); + setTaped(false); + continue; + } + } else if (ToolHelper.isTool(heldItem, toolToMatch)) { fixProblemWithTool(i, heldItem, entityPlayer); if (toolsToMatch.stream().allMatch(Objects::isNull)) { return; } + continue; } // Then try all the remaining inventory slots for (ItemStack itemStack : entityPlayer.inventory.mainInventory) { - if (ToolHelper.isTool(itemStack, toolToMatch)) { + if (itemStack.getItem() instanceof ItemGTToolbelt toolbelt) { + if (toolbelt.damageAgainstMaintenanceProblem(itemStack, toolToMatch, entityPlayer)) { + ((IMaintenance) getController()).setMaintenanceFixed(i); + setTaped(false); + continue mainfor; + } + } else if (ToolHelper.isTool(itemStack, toolToMatch)) { fixProblemWithTool(i, itemStack, entityPlayer); if (toolsToMatch.stream().allMatch(Objects::isNull)) { return; } + continue mainfor; } } + } + } + } - for (ItemStack stack : entityPlayer.inventory.mainInventory) { - if (ToolHelper.isTool(stack, toolToMatch)) { - ((IMaintenance) this.getController()).setMaintenanceFixed(i); - ToolHelper.damageItemWhenCrafting(stack, entityPlayer); - if (toolsToMatch.stream().allMatch(Objects::isNull)) { - return; - } - } + @ApiStatus.Internal + public void fixMaintenanceProblemsWithToolbelt(@NotNull EntityPlayer entityPlayer, ItemGTToolbelt toolbelt, + ItemStack toolbeltStack) { + byte problems = ((IMaintenance) this.getController()).getMaintenanceProblems(); + for (byte index = 0; index < 6; index++) { + if (((problems >> index) & 1) == 0) { + String toolToMatch = switch (index) { + case 0 -> ToolClasses.WRENCH; + case 1 -> ToolClasses.SCREWDRIVER; + case 2 -> ToolClasses.SOFT_MALLET; + case 3 -> ToolClasses.HARD_HAMMER; + case 4 -> ToolClasses.WIRE_CUTTER; + case 5 -> ToolClasses.CROWBAR; + default -> null; + }; + if (toolbelt.damageAgainstMaintenanceProblem(toolbeltStack, toolToMatch, entityPlayer)) { + ((IMaintenance) getController()).setMaintenanceFixed(index); + setTaped(false); } } } diff --git a/src/main/java/gregtech/core/CoreModule.java b/src/main/java/gregtech/core/CoreModule.java index aecbe907d48..c964db8563f 100644 --- a/src/main/java/gregtech/core/CoreModule.java +++ b/src/main/java/gregtech/core/CoreModule.java @@ -66,6 +66,7 @@ import gregtech.core.network.packets.PacketPluginSynced; import gregtech.core.network.packets.PacketRecoverMTE; import gregtech.core.network.packets.PacketReloadShaders; +import gregtech.core.network.packets.PacketToolbeltSelectionChange; import gregtech.core.network.packets.PacketUIClientAction; import gregtech.core.network.packets.PacketUIOpen; import gregtech.core.network.packets.PacketUIWidgetUpdate; @@ -245,6 +246,8 @@ public void registerPackets() { GregTechAPI.networkHandler.registerPacket(PacketNotifyCapeChange.class); GregTechAPI.networkHandler.registerPacket(PacketReloadShaders.class); GregTechAPI.networkHandler.registerPacket(PacketClipboardNBTUpdate.class); + GregTechAPI.networkHandler.registerPacket(PacketToolbeltSelectionChange.Server.class); + GregTechAPI.networkHandler.registerPacket(PacketToolbeltSelectionChange.Client.class); } @Override diff --git a/src/main/java/gregtech/core/network/packets/PacketToolbeltSelectionChange.java b/src/main/java/gregtech/core/network/packets/PacketToolbeltSelectionChange.java new file mode 100644 index 00000000000..d9c41f61bc5 --- /dev/null +++ b/src/main/java/gregtech/core/network/packets/PacketToolbeltSelectionChange.java @@ -0,0 +1,102 @@ +package gregtech.core.network.packets; + +import gregtech.api.GregTechAPI; +import gregtech.api.items.toolitem.ItemGTToolbelt; +import gregtech.api.network.IClientExecutor; +import gregtech.api.network.IPacket; +import gregtech.api.network.IServerExecutor; +import gregtech.core.sound.GTSoundEvents; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.client.network.NetHandlerPlayClient; +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.SoundCategory; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +public abstract class PacketToolbeltSelectionChange implements IPacket { + + protected int slot; + + public PacketToolbeltSelectionChange() {} + + public PacketToolbeltSelectionChange(int slot) { + this.slot = slot; + } + + @Override + public void encode(PacketBuffer buf) { + buf.writeByte(slot); + } + + @Override + public void decode(PacketBuffer buf) { + this.slot = buf.readByte(); + } + + public static void toClient(int slot, int matrixSlot, EntityPlayerMP player) { + if (player == null) return; + GregTechAPI.networkHandler.sendTo(new Client(slot, matrixSlot), player); + } + + public static void toServer(int slot) { + GregTechAPI.networkHandler.sendToServer(new Server(slot)); + } + + public static class Server extends PacketToolbeltSelectionChange implements IServerExecutor { + + public Server() {} + + public Server(int slot) { + super(slot); + } + + @Override + public void executeServer(NetHandlerPlayServer handler) { + EntityPlayerMP player = handler.player; + ItemStack stack = player.getHeldItemMainhand(); + if (stack.getItem() instanceof ItemGTToolbelt toolbelt) { + player.getServerWorld().playSound(null, player.posX, player.posY, player.posZ, GTSoundEvents.CLICK, + SoundCategory.PLAYERS, 2F, 1F); + toolbelt.setSelectedTool(slot, stack); + } + } + } + + public static class Client extends PacketToolbeltSelectionChange implements IClientExecutor { + + int index; + + public Client() {} + + public Client(int slot, int index) { + super(slot); + this.index = index; + } + + @Override + public void encode(PacketBuffer buf) { + super.encode(buf); + buf.writeVarInt(index); + } + + @Override + public void decode(PacketBuffer buf) { + super.decode(buf); + this.index = buf.readVarInt(); + } + + @Override + @SideOnly(Side.CLIENT) + public void executeClient(NetHandlerPlayClient handler) { + EntityPlayerSP player = Minecraft.getMinecraft().player; + ItemStack stack = player.openContainer.getSlot(index).getStack(); + if (stack.getItem() instanceof ItemGTToolbelt toolbelt) + toolbelt.setSelectedTool(slot, stack); + } + } +} diff --git a/src/main/java/gregtech/core/sound/GTSoundEvents.java b/src/main/java/gregtech/core/sound/GTSoundEvents.java index 53fd8067972..80f5caccd3b 100644 --- a/src/main/java/gregtech/core/sound/GTSoundEvents.java +++ b/src/main/java/gregtech/core/sound/GTSoundEvents.java @@ -57,6 +57,7 @@ public class GTSoundEvents { // Misc public static SoundEvent DEFAULT_ALARM; + public static SoundEvent CLICK; public static void register() { FORGE_HAMMER = soundManager.registerSound("tick.forge_hammer"); @@ -102,5 +103,6 @@ public static void register() { BREAKDOWN_MECHANICAL = soundManager.registerSound("maintenance.mechanical"); DEFAULT_ALARM = soundManager.registerSound("alarm"); + CLICK = soundManager.registerSound("click"); } } diff --git a/src/main/java/gregtech/integration/jei/JustEnoughItemsModule.java b/src/main/java/gregtech/integration/jei/JustEnoughItemsModule.java index 5ce3cff1cb9..5a182dbadde 100644 --- a/src/main/java/gregtech/integration/jei/JustEnoughItemsModule.java +++ b/src/main/java/gregtech/integration/jei/JustEnoughItemsModule.java @@ -29,6 +29,7 @@ import gregtech.common.blocks.MetaBlocks; import gregtech.common.gui.widget.craftingstation.CraftingSlotWidget; import gregtech.common.items.MetaItems; +import gregtech.common.items.ToolItems; import gregtech.common.metatileentities.MetaTileEntities; import gregtech.integration.IntegrationSubmodule; import gregtech.integration.jei.basic.GTFluidVeinCategory; @@ -84,6 +85,9 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static gregtech.api.unification.material.info.MaterialFlags.GENERATE_BOLT_SCREW; +import static gregtech.api.unification.material.info.MaterialFlags.GENERATE_RING; + @JEIPlugin @GregTechModule( moduleID = GregTechModules.MODULE_JEI, @@ -237,11 +241,22 @@ public void register(IModRegistry registry) { } List oreByproductList = new ArrayList<>(); + List materialTreeList = new ArrayList<>(); for (Material material : GregTechAPI.materialManager.getRegisteredMaterials()) { if (material.hasProperty(PropertyKey.ORE)) { oreByproductList.add(new OreByProduct(material)); } + if (material.hasProperty(PropertyKey.DUST)) { + materialTreeList.add(new MaterialTree(material)); + } + if (material.hasFlag(GENERATE_BOLT_SCREW) && material.hasFlag(GENERATE_RING) && + material.hasProperty(PropertyKey.TOOL)) { + registry.addIngredientInfo(ToolItems.TOOLBELT.get(material), VanillaTypes.ITEM, + "item.gt.tool.toolbelt.tooltip", "item.gt.tool.toolbelt.paint", "item.gt.tool.toolbelt.dye", + "item.gt.tool.toolbelt.maintenance"); + } } + String oreByProductId = GTValues.MODID + ":" + "ore_by_product"; registry.addRecipes(oreByproductList, oreByProductId); MetaTileEntity[][] machineLists = { @@ -259,12 +274,6 @@ public void register(IModRegistry registry) { } // Material Tree - List materialTreeList = new ArrayList<>(); - for (Material material : GregTechAPI.materialManager.getRegisteredMaterials()) { - if (material.hasProperty(PropertyKey.DUST)) { - materialTreeList.add(new MaterialTree(material)); - } - } registry.addRecipes(materialTreeList, GTValues.MODID + ":" + "material_tree"); // Ore Veins diff --git a/src/main/java/gregtech/loaders/recipe/GTRecipeManager.java b/src/main/java/gregtech/loaders/recipe/GTRecipeManager.java index e91a9bd2c7a..2b757b217c1 100644 --- a/src/main/java/gregtech/loaders/recipe/GTRecipeManager.java +++ b/src/main/java/gregtech/loaders/recipe/GTRecipeManager.java @@ -1,11 +1,13 @@ package gregtech.loaders.recipe; import gregtech.api.event.MaterialInfoEvent; +import gregtech.common.crafting.DyeableRecipes; import gregtech.loaders.recipe.handlers.DecompositionRecipeHandler; import gregtech.loaders.recipe.handlers.RecipeHandlerList; import gregtech.loaders.recipe.handlers.ToolRecipeHandler; import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.common.registry.ForgeRegistries; public final class GTRecipeManager { @@ -21,6 +23,7 @@ public static void load() { MetaTileEntityLoader.init(); MetaTileEntityMachineRecipeLoader.init(); RecipeHandlerList.register(); + ForgeRegistries.RECIPES.register(new DyeableRecipes()); } public static void loadLatest() { diff --git a/src/main/java/gregtech/loaders/recipe/handlers/ToolRecipeHandler.java b/src/main/java/gregtech/loaders/recipe/handlers/ToolRecipeHandler.java index 1997631a38c..84e5cd91450 100644 --- a/src/main/java/gregtech/loaders/recipe/handlers/ToolRecipeHandler.java +++ b/src/main/java/gregtech/loaders/recipe/handlers/ToolRecipeHandler.java @@ -19,6 +19,7 @@ import gregtech.common.items.MetaItems; import gregtech.common.items.ToolItems; +import net.minecraft.init.Items; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.Ingredient; import net.minecraft.util.ResourceLocation; @@ -215,6 +216,14 @@ private static void processTool(OrePrefix prefix, Material material, ToolPropert } } + if (material.hasFlag(GENERATE_BOLT_SCREW) && material.hasFlag(GENERATE_RING)) { + addToolRecipe(material, ToolItems.TOOLBELT, false, + "SSS", "LLL", "RSR", + 'L', Items.LEATHER, + 'S', new UnificationEntry(OrePrefix.screw, material), + 'R', new UnificationEntry(OrePrefix.ring, material)); + } + addToolRecipe(material, ToolItems.SCREWDRIVER, true, " fS", " Sh", "W ", 'S', rod, diff --git a/src/main/java/gregtech/mixins/forge/GuiIngameForgeMixin.java b/src/main/java/gregtech/mixins/forge/GuiIngameForgeMixin.java new file mode 100644 index 00000000000..b34c7b1d416 --- /dev/null +++ b/src/main/java/gregtech/mixins/forge/GuiIngameForgeMixin.java @@ -0,0 +1,30 @@ +package gregtech.mixins.forge; + +import gregtech.api.items.toolitem.ItemGTToolbelt; +import gregtech.common.ConfigHolder; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiIngame; +import net.minecraftforge.client.GuiIngameForge; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(GuiIngameForge.class) +public class GuiIngameForgeMixin extends GuiIngame { + + private GuiIngameForgeMixin(Minecraft mcIn) { + super(mcIn); + } + + @ModifyExpressionValue(method = "renderToolHighlight", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/client/gui/ScaledResolution;getScaledHeight()I")) + private int shiftToolHighlightText(int y) { + if (ConfigHolder.client.toolbeltConfig.enableToolbeltHotbarDisplay && + highlightingItemStack.getItem() instanceof ItemGTToolbelt) + return y - 31 + 6; + else return y; + } +} diff --git a/src/main/java/gregtech/mixins/forge/OreIngredientMixin.java b/src/main/java/gregtech/mixins/forge/OreIngredientMixin.java new file mode 100644 index 00000000000..b9a3641343c --- /dev/null +++ b/src/main/java/gregtech/mixins/forge/OreIngredientMixin.java @@ -0,0 +1,22 @@ +package gregtech.mixins.forge; + +import gregtech.api.items.toolitem.ItemGTToolbelt; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.oredict.OreIngredient; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(OreIngredient.class) +public abstract class OreIngredientMixin { + + @Inject(method = "apply(Lnet/minecraft/item/ItemStack;)Z", at = @At("HEAD"), cancellable = true) + private void checkToolbelt(ItemStack input, CallbackInfoReturnable cir) { + if (ItemGTToolbelt.checkIngredientAgainstToolbelt(input, (OreIngredient) (Object) this)) { + cir.setReturnValue(true); + } + } +} diff --git a/src/main/java/gregtech/mixins/jei/BasicRecipeTransferHandlerServerMixin.java b/src/main/java/gregtech/mixins/jei/BasicRecipeTransferHandlerServerMixin.java new file mode 100644 index 00000000000..cbd4a6c140d --- /dev/null +++ b/src/main/java/gregtech/mixins/jei/BasicRecipeTransferHandlerServerMixin.java @@ -0,0 +1,79 @@ +package gregtech.mixins.jei; + +import gregtech.api.items.toolitem.ToolHelper; + +import net.minecraft.item.ItemStack; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import com.llamalad7.mixinextras.sugar.Share; +import com.llamalad7.mixinextras.sugar.ref.LocalRef; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import mezz.jei.transfer.BasicRecipeTransferHandlerServer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Iterator; +import java.util.Map; + +@Mixin(value = BasicRecipeTransferHandlerServer.class, remap = false) +public class BasicRecipeTransferHandlerServerMixin { + + /** + * @reason create the list used to indicate what slots contain tools, and thus should be ignored on the 2nd+ checks + */ + @Inject(method = "removeItemsFromInventory", at = @At("HEAD")) + private static void gregtechCEu$initCompletedList(CallbackInfoReturnable> cir, + @Share("completed") LocalRef completed) { + completed.set(new IntArrayList()); + } + + /** + * @reason add the index of the entry to the list of skipped locations on the 2nd+ checks + */ + @Inject(method = "removeItemsFromInventory", + at = @At(value = "INVOKE", + target = "Ljava/util/Map;put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", + ordinal = 1)) + private static void gregtechCEu$markItemStackAsCompleted(CallbackInfoReturnable> cir, + @Local(ordinal = 0) Map.Entry entry, + @Local(ordinal = 2) ItemStack removedItemStack, + @Share("completed") LocalRef completed) { + // mark tool stacks as 'permanently' satisfied + if (removedItemStack != null && ToolHelper.isTool(removedItemStack)) completed.get().add(entry.getKey()); + } + + /** + * @reason if the given location is marked as completed, skip processing this slot and check the next slot + */ + @ModifyExpressionValue(method = "removeItemsFromInventory", + at = @At(value = "INVOKE", target = "Ljava/util/Iterator;hasNext()Z", ordinal = 0)) + private static boolean gregtechCEu$skipCompletedStacks(boolean original, + @Local(ordinal = 0) Iterator> iterator, + @Share("completed") LocalRef completed, + @Share("iterator_next") LocalRef> next) { + if (!original) return false; + next.set(iterator.next()); + while (completed.get().contains(next.get().getKey())) { + if (!iterator.hasNext()) return false; + next.set(iterator.next()); + } + return true; + } + + /** + * @reason use the next variable that respects the completed status + */ + @WrapOperation(method = "removeItemsFromInventory", + at = @At(value = "INVOKE", target = "Ljava/util/Iterator;next()Ljava/lang/Object;", ordinal = 0)) + private static Object gregtechCEu$useCorrectIteratorNext(Iterator> instance, + Operation> original, + @Share("completed") LocalRef completed, + @Share("iterator_next") LocalRef> next) { + return next.get() == null ? original.call(instance) : next.get(); + } +} diff --git a/src/main/java/gregtech/mixins/jei/StackHelperMixin.java b/src/main/java/gregtech/mixins/jei/StackHelperMixin.java new file mode 100644 index 00000000000..16906220c90 --- /dev/null +++ b/src/main/java/gregtech/mixins/jei/StackHelperMixin.java @@ -0,0 +1,22 @@ +package gregtech.mixins.jei; + +import gregtech.api.items.toolitem.ItemGTToolbelt; + +import net.minecraft.item.ItemStack; + +import mezz.jei.startup.StackHelper; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(StackHelper.class) +public class StackHelperMixin { + + @Inject(method = "isEquivalent", at = @At(value = "RETURN", ordinal = 2), cancellable = true, remap = false) + private void toolbeltIsEquivalent(ItemStack lhs, ItemStack rhs, CallbackInfoReturnable cir) { + if (ItemGTToolbelt.checkToolAgainstToolbelt(rhs, lhs)) { + cir.setReturnValue(true); + } + } +} diff --git a/src/main/java/gregtech/mixins/minecraft/ContainerMixin.java b/src/main/java/gregtech/mixins/minecraft/ContainerMixin.java new file mode 100644 index 00000000000..68261223856 --- /dev/null +++ b/src/main/java/gregtech/mixins/minecraft/ContainerMixin.java @@ -0,0 +1,29 @@ +package gregtech.mixins.minecraft; + +import gregtech.api.items.toolitem.ItemGTToolbelt; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.ClickType; +import net.minecraft.inventory.Container; +import net.minecraft.item.ItemStack; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(Container.class) +public abstract class ContainerMixin { + + @Inject(method = "slotClick", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/item/ItemStack;splitStack(I)Lnet/minecraft/item/ItemStack;", + ordinal = 1)) + private void setPlayer(int slotId, int dragType, ClickType clickTypeIn, EntityPlayer player, + CallbackInfoReturnable cir) { + var playerStack = player.inventory.getItemStack(); + if (playerStack.getItem() instanceof ItemGTToolbelt) { + ItemGTToolbelt.setCraftingSlot(slotId, player); + } + } +} diff --git a/src/main/java/gregtech/mixins/minecraft/MinecraftMixin.java b/src/main/java/gregtech/mixins/minecraft/MinecraftMixin.java index 31ada3545f0..0bba8ad4f03 100644 --- a/src/main/java/gregtech/mixins/minecraft/MinecraftMixin.java +++ b/src/main/java/gregtech/mixins/minecraft/MinecraftMixin.java @@ -1,12 +1,19 @@ package gregtech.mixins.minecraft; +import gregtech.api.items.toolitem.ItemGTToolbelt; +import gregtech.common.ConfigHolder; + import net.minecraft.client.Minecraft; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.item.ItemStack; import net.minecraft.util.text.TextComponentTranslation; import org.lwjgl.input.Keyboard; +import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(Minecraft.class) @@ -19,4 +26,20 @@ public void addGregTechDebugMessage(int auxKey, CallbackInfoReturnable .printChatMessage(new TextComponentTranslation("gregtech.debug.f3_h.enabled")); } } + + @Redirect(method = "processKeyBinds", + at = @At(value = "FIELD", + target = "Lnet/minecraft/entity/player/InventoryPlayer;currentItem:I", + opcode = Opcodes.PUTFIELD)) + public void interceptHotbarKeybindsForToolbelt(InventoryPlayer inventoryPlayer, int i) { + if (!ConfigHolder.client.toolbeltConfig.enableToolbeltKeypressCapture) return; + if (inventoryPlayer.player.isSneaking()) { + ItemStack stack = inventoryPlayer.player.getHeldItemMainhand(); + if (stack.getItem() instanceof ItemGTToolbelt toolbelt) { + toolbelt.changeSelectedToolHotkey(i, stack); + return; + } + } + inventoryPlayer.currentItem = i; + } } diff --git a/src/main/java/gregtech/mixins/minecraft/RecipeRepairItemMixin.java b/src/main/java/gregtech/mixins/minecraft/RecipeRepairItemMixin.java index 5cd275b6cf8..dd76f8d959c 100644 --- a/src/main/java/gregtech/mixins/minecraft/RecipeRepairItemMixin.java +++ b/src/main/java/gregtech/mixins/minecraft/RecipeRepairItemMixin.java @@ -1,6 +1,7 @@ package gregtech.mixins.minecraft; import gregtech.api.items.toolitem.IGTTool; +import gregtech.api.items.toolitem.ItemGTToolbelt; import gregtech.api.items.toolitem.ToolHelper; import net.minecraft.inventory.InventoryCrafting; @@ -36,7 +37,8 @@ public class RecipeRepairItemMixin { ItemStack itemstack1 = list.get(0); if (itemstack.getItem() instanceof IGTTool first && itemstack1.getItem() instanceof IGTTool second) { - if (first.isElectric() || second.isElectric()) { + if (first.isElectric() || second.isElectric() || + first instanceof ItemGTToolbelt || second instanceof ItemGTToolbelt) { cir.setReturnValue(false); } else { cir.setReturnValue(first.getToolMaterial(itemstack) == second.getToolMaterial(itemstack1)); diff --git a/src/main/resources/assets/gregtech/lang/en_us.lang b/src/main/resources/assets/gregtech/lang/en_us.lang index 389d68cd3ce..ea8f15425cc 100644 --- a/src/main/resources/assets/gregtech/lang/en_us.lang +++ b/src/main/resources/assets/gregtech/lang/en_us.lang @@ -1042,6 +1042,14 @@ item.gt.tool.plunger.tooltip=§8Removes Fluids from Machines item.gt.tool.wire_cutter_lv.name=%s Wire Cutter (LV) item.gt.tool.wire_cutter_hv.name=%s Wire Cutter (HV) item.gt.tool.wire_cutter_iv.name=%s Wire Cutter (IV) +item.gt.tool.toolbelt.name=%s Toolbelt%s +item.gt.tool.toolbelt.size=§a%s Tool Slots +item.gt.tool.toolbelt.tooltip=Holds GT or vanilla-esque tools that can be manually selected for in-world use, automatically selected for crafting, or stored for maintenance repair. +item.gt.tool.toolbelt.paint=Also supports Spray Cans. +item.gt.tool.toolbelt.dye=Can be dyed like leather equipment, and the dye can be removed via water cauldron, also like leather equipment. +item.gt.tool.toolbelt.maintenance=Repairs maintenance problems with only inserted tools upon interacting with a maintenance hatch in-world. +item.gt.tool.toolbelt.tool=§aSlot %s: §f%s +item.gt.tool.toolbelt.selected=- §aSlot %s: §f%s item.gt.tool.tooltip.crafting_uses=§a%s Crafting Uses @@ -6158,6 +6166,9 @@ gregtech.subtitle.entity.portal_opening=Portal opens gregtech.subtitle.entity.portal_closing=Portal closes gregtech.subtitle.maintenance.electrical=Electrical breakdown gregtech.subtitle.maintenance.mechanical=Mechanical breakdown +gregtech.subtitle.alarm=Alarm Whines +gregtech.subtitle.click=Something Clicks + # Debug messaging gregtech.debug.f3_h.enabled=GregTech has modified the debug info! For Developers: enable the misc:debug config option in the GregTech config file to see more diff --git a/src/main/resources/assets/gregtech/models/item/tools/toolbelt.json b/src/main/resources/assets/gregtech/models/item/tools/toolbelt.json new file mode 100644 index 00000000000..25e4069ef67 --- /dev/null +++ b/src/main/resources/assets/gregtech/models/item/tools/toolbelt.json @@ -0,0 +1,7 @@ +{ + "parent": "item/handheld", + "textures": { + "layer0": "gregtech:items/tools/toolbelt_belt", + "layer1": "gregtech:items/tools/toolbelt_loops" + } +} diff --git a/src/main/resources/assets/gregtech/sounds.json b/src/main/resources/assets/gregtech/sounds.json index 10977e9cf2d..b5bd3bcdcc0 100644 --- a/src/main/resources/assets/gregtech/sounds.json +++ b/src/main/resources/assets/gregtech/sounds.json @@ -397,5 +397,15 @@ "stream" : false } ] + }, + "click" : { + "subtitle" : "gregtech.subtitle.click", + "category" : "block", + "sounds" : [ + { + "name" : "gregtech:click", + "stream" : false + } + ] } } diff --git a/src/main/resources/assets/gregtech/sounds/click.ogg b/src/main/resources/assets/gregtech/sounds/click.ogg new file mode 100644 index 00000000000..c523d784b79 Binary files /dev/null and b/src/main/resources/assets/gregtech/sounds/click.ogg differ diff --git a/src/main/resources/assets/gregtech/textures/items/tools/toolbelt_belt.png b/src/main/resources/assets/gregtech/textures/items/tools/toolbelt_belt.png new file mode 100644 index 00000000000..0918698052f Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/items/tools/toolbelt_belt.png differ diff --git a/src/main/resources/assets/gregtech/textures/items/tools/toolbelt_loops.png b/src/main/resources/assets/gregtech/textures/items/tools/toolbelt_loops.png new file mode 100644 index 00000000000..6944f44d2ae Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/items/tools/toolbelt_loops.png differ diff --git a/src/main/resources/gregtech_at.cfg b/src/main/resources/gregtech_at.cfg index 76892c9fc05..b1286b391a8 100644 --- a/src/main/resources/gregtech_at.cfg +++ b/src/main/resources/gregtech_at.cfg @@ -84,3 +84,10 @@ public net.minecraft.entity.item.EntityItem field_145804_b # pickupDelay # Explosion public net.minecraft.world.Explosion field_77283_e # exploder + +# GuiIngame +public net.minecraft.client.gui.GuiIngame field_110330_c # WIDGETS_TEX_PATH +public net.minecraft.client.gui.GuiIngame func_184044_a(IIFLnet/minecraft/entity/player/EntityPlayer;Lnet/minecraft/item/ItemStack;)V # renderHotbarItem + +# Gui +public net.minecraft.client.gui.Gui field_73735_i # zLevel diff --git a/src/main/resources/mixins.gregtech.forge.json b/src/main/resources/mixins.gregtech.forge.json index 81605c0c9dc..61138099624 100644 --- a/src/main/resources/mixins.gregtech.forge.json +++ b/src/main/resources/mixins.gregtech.forge.json @@ -4,8 +4,10 @@ "target": "@env(DEFAULT)", "minVersion": "0.8", "compatibilityLevel": "JAVA_8", - "mixins" : [ + "mixins": [ + "GuiIngameForgeMixin", "ModelLoaderRegistryMixin", + "OreIngredientMixin", "SpecialArmorPropertiesMixin" ], "client": [], diff --git a/src/main/resources/mixins.gregtech.jei.json b/src/main/resources/mixins.gregtech.jei.json index 2520843c7d6..3e7545b737d 100644 --- a/src/main/resources/mixins.gregtech.jei.json +++ b/src/main/resources/mixins.gregtech.jei.json @@ -1,12 +1,14 @@ { - "package" : "gregtech.mixins.jei", - "refmap" : "mixins.gregtech.refmap.json", - "target" : "@env(DEFAULT)", - "minVersion" : "0.8", - "compatibilityLevel" : "JAVA_8", - "mixins" : [ - "JEITooltipMixin" + "package": "gregtech.mixins.jei", + "refmap": "mixins.gregtech.refmap.json", + "target": "@env(DEFAULT)", + "minVersion": "0.8", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "BasicRecipeTransferHandlerServerMixin", + "JEITooltipMixin", + "StackHelperMixin" ], - "client" : [], - "server" : [] + "client": [], + "server": [] } diff --git a/src/main/resources/mixins.gregtech.minecraft.json b/src/main/resources/mixins.gregtech.minecraft.json index 50eeb75fb65..85676f99055 100644 --- a/src/main/resources/mixins.gregtech.minecraft.json +++ b/src/main/resources/mixins.gregtech.minecraft.json @@ -9,6 +9,7 @@ }, "mixins": [ "BlockConcretePowderMixin", + "ContainerMixin", "BlockRenderLayerMixin", "DamageSourceMixin", "EnchantmentCanApplyMixin",