/* * Copyright (C) 2022-2024 NotEnoughUpdates contributors * * This file is part of NotEnoughUpdates. * * NotEnoughUpdates is free software: you can redistribute it * and/or modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * NotEnoughUpdates is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with NotEnoughUpdates. If not, see . */ package io.github.moulberry.notenoughupdates.miscfeatures; import io.github.moulberry.notenoughupdates.NotEnoughUpdates; import io.github.moulberry.notenoughupdates.autosubscribe.NEUAutoSubscribe; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; import net.minecraft.init.Blocks; import net.minecraft.item.ItemStack; import net.minecraft.network.play.server.S23PacketBlockChange; import net.minecraft.util.BlockPos; import net.minecraftforge.client.event.ClientChatReceivedEvent; import net.minecraftforge.event.entity.player.PlayerInteractEvent; import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.TickEvent; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; @NEUAutoSubscribe public class ItemCooldowns { private static final Pattern COOLDOWN_LORE = Pattern.compile("\\u00a78Cooldown: \\u00a7a(\\d+)s"); private static final Pattern PICKAXE_ABILITY_ACTIVATION = Pattern.compile("\\u00a7r\\u00a7aYou used your \\u00a7r\\u00a7..+ \\u00a7r\\u00a7aPickaxe Ability!\\u00a7r"); private static final Pattern BONZO_ABILITY_ACTIVATION = Pattern.compile( "\\u00a7r\\u00a7aYour \\u00a7r\\u00a7[9|5](\\u269A )*Bonzo's Mask \\u00a7r\\u00a7asaved your life!\\u00a7r"); private static final Pattern SPIRIT_ABILITY_ACTIVATION = Pattern.compile( "\\u00a7r\\u00a76Second Wind Activated\\u00a7r\\u00a7a! \\u00a7r\\u00a7aYour Spirit Mask saved your life!\\u00a7r"); private static final Pattern SPRAYONATOR_ACTIVATION = Pattern.compile("§r§a§lSPRAYONATOR! §r§7You sprayed §r§aPlot §r§7- §r§b.* §r§7with §r§a.*§r§7!§r"); private static final Map durabilityOverrideMap = new HashMap<>(); public static long pickaxeUseCooldownMillisRemaining = -1; private static long treecapitatorCooldownMillisRemaining = -1; private static long bonzomaskCooldownMillisRemaining = -1; private static long spiritMaskCooldownMillisRemaining = -1; private static long sprayonatorCooldownMillisRemaining = -1; private static long grappleCooldownMillisRemaining = -1; public static boolean firstLoad = true; public static long firstLoadMillis = 0; private static long lastMillis = 0; public static long pickaxeCooldown = -1; private static long bonzoMaskCooldown = -1; private static long spiritMaskCooldown = -1; private static long sprayonatorCooldown = -1; private static long grappleCooldown = 2000; private static HashSet grappleSetIds = new HashSet() {{ add("BAT_PERSON_BOOTS"); add("BAT_PERSON_LEGGINGS"); add("BAT_PERSON_CHESTPLATE"); add("BAT_PERSON_HELMET"); }}; public static TreeMap blocksClicked = new TreeMap<>(); private static int tickCounter = 0; /** * Class to store the block state at a position, the moment the position is passed */ public static class BlockData { public BlockPos blockPos; public IBlockState blockState; public BlockData(BlockPos pos) { this.blockPos = pos; this.blockState = Minecraft.getMinecraft().theWorld.getBlockState(pos); } } enum Item { PICKAXES, BONZO_MASK, SPIRIT_MASK, SPRAYONATOR } @SubscribeEvent public void tick(TickEvent.ClientTickEvent event) { if (event.phase == TickEvent.Phase.END && NotEnoughUpdates.INSTANCE.hasSkyblockScoreboard()) { if (tickCounter++ >= 20 * 10) { tickCounter = 0; pickaxeCooldown = -1; bonzoMaskCooldown = -1; spiritMaskCooldown = -1; sprayonatorCooldown = -1; } long currentTime = System.currentTimeMillis(); if (firstLoad) { firstLoadMillis = currentTime; firstLoad = false; } Long key; while ((key = blocksClicked.floorKey(currentTime - 1500)) != null) { blocksClicked.remove(key); } long millisDelta = currentTime - lastMillis; lastMillis = currentTime; durabilityOverrideMap.clear(); if (pickaxeUseCooldownMillisRemaining >= 0) { pickaxeUseCooldownMillisRemaining -= millisDelta; } if (treecapitatorCooldownMillisRemaining >= 0) { treecapitatorCooldownMillisRemaining -= millisDelta; } if (bonzomaskCooldownMillisRemaining >= 0) { bonzomaskCooldownMillisRemaining -= millisDelta; } if (spiritMaskCooldownMillisRemaining >= 0) { spiritMaskCooldownMillisRemaining -= millisDelta; } if (sprayonatorCooldownMillisRemaining >= 0) { sprayonatorCooldownMillisRemaining -= millisDelta; } if (grappleCooldownMillisRemaining >= 0) { grappleCooldownMillisRemaining -= millisDelta; } } } @SubscribeEvent public void onWorldLoad(WorldEvent.Load event) { blocksClicked.clear(); if (pickaxeCooldown > 0) pickaxeUseCooldownMillisRemaining = 60 * 1000; pickaxeCooldown = -1; } @SubscribeEvent public void onPlayerInteract(PlayerInteractEvent event) { if (event.action == PlayerInteractEvent.Action.RIGHT_CLICK_AIR || event.action == PlayerInteractEvent.Action.RIGHT_CLICK_BLOCK) { ItemStack held = Minecraft.getMinecraft().thePlayer.getHeldItem(); String internalname = NotEnoughUpdates.INSTANCE.manager.createItemResolutionQuery().withItemStack(held).resolveInternalName(); if (internalname != null) { if (grappleCooldownMillisRemaining < 0 && internalname.equals("GRAPPLING_HOOK") && Minecraft.getMinecraft().thePlayer.fishEntity != null) { grappleCooldownMillisRemaining = getGrappleCooldownWithArmor(); } } } } public static long getTreecapCooldownWithPet() { if (!NotEnoughUpdates.INSTANCE.config.itemOverlays.enableCooldownInItemDurability) { return 0; } PetInfoOverlay.Pet pet = PetInfoOverlay.getCurrentPet(); if (NotEnoughUpdates.INSTANCE.config.itemOverlays.enableMonkeyCheck && pet != null) { if (pet.petLevel != null && pet.petType.equalsIgnoreCase("monkey") && pet.rarity.equals(PetInfoOverlay.Rarity.LEGENDARY) ) { return 2000 - (int) (2000 * (0.005 * pet.petLevel.getCurrentLevel())); } } return 2000; } public static long getGrappleCooldownWithArmor() { if (!NotEnoughUpdates.INSTANCE.config.itemOverlays.enableGrappleOverlay) { return 0; } for (int i = 0; i < 4; i++) { ItemStack armorPiece = Minecraft.getMinecraft().thePlayer.getCurrentArmor(i); String internal = NotEnoughUpdates.INSTANCE.manager.createItemResolutionQuery().withItemStack(armorPiece).resolveInternalName(); if (internal != null) { if (!grappleSetIds.contains(internal)) return grappleCooldown; } else return grappleCooldown; } return 0; } public static void blockClicked(BlockPos pos) { long currentTime = System.currentTimeMillis(); blocksClicked.put(currentTime, new BlockData(pos)); } public static void processBlockChangePacket(S23PacketBlockChange packetIn) { BlockPos pos = packetIn.getBlockPosition(); checkForBlockChange(pos, packetIn.blockState); } public static void checkForBlockChange(BlockPos pos, IBlockState blockState) { Minecraft.getMinecraft().addScheduledTask(() -> { BlockData oldBlockData = null; for (BlockData value : blocksClicked.values()) { if (value.blockPos.equals(pos)) oldBlockData = value; } if (oldBlockData != null) { IBlockState oldState = oldBlockData.blockState; if ((oldState.getBlock() == Blocks.log || oldState.getBlock() == Blocks.log2) && blockState.getBlock() == Blocks.air) { onBlockMined(); } } }); } public static void onBlockMined() { ItemStack held = Minecraft.getMinecraft().thePlayer.getHeldItem(); String internalname = NotEnoughUpdates.INSTANCE.manager.createItemResolutionQuery().withItemStack(held).resolveInternalName(); if (internalname != null) { if (treecapitatorCooldownMillisRemaining < 0 && (internalname.equals("TREECAPITATOR_AXE") || internalname.equals("JUNGLE_AXE"))) { treecapitatorCooldownMillisRemaining = getTreecapCooldownWithPet(); } } } private static boolean isPickaxe(String internalname) { if (internalname == null) return false; if (internalname.endsWith("_PICKAXE")) { return true; } else if (internalname.contains("_DRILL_")) { char lastChar = internalname.charAt(internalname.length() - 1); return lastChar >= '0' && lastChar <= '9'; } else return internalname.equals("GEMSTONE_GAUNTLET") || internalname.equals("PICKONIMBUS") || internalname.equals("DIVAN_DRILL") || internalname.equals("BINGONIMBUS_2000"); } @SubscribeEvent public void onChatMessage(ClientChatReceivedEvent event) { if (pickaxeCooldown != 0 && PICKAXE_ABILITY_ACTIVATION.matcher(event.message.getFormattedText()).matches()) { findCooldownInTooltip(Item.PICKAXES); pickaxeUseCooldownMillisRemaining = pickaxeCooldown * 1000; } if (NotEnoughUpdates.INSTANCE.config.itemOverlays.bonzoAbility && bonzoMaskCooldown != 0 && BONZO_ABILITY_ACTIVATION.matcher(event.message.getFormattedText()).matches()) { findCooldownInTooltip(Item.BONZO_MASK); bonzomaskCooldownMillisRemaining = bonzoMaskCooldown * 1000; } if (NotEnoughUpdates.INSTANCE.config.itemOverlays.spiritAbility && spiritMaskCooldown != 0 && SPIRIT_ABILITY_ACTIVATION.matcher(event.message.getFormattedText()).matches()) { findCooldownInTooltip(Item.SPIRIT_MASK); spiritMaskCooldownMillisRemaining = spiritMaskCooldown * 1000; } if (NotEnoughUpdates.INSTANCE.config.garden.sprayonatorCooldown && sprayonatorCooldown != 0 && SPRAYONATOR_ACTIVATION.matcher(event.message.getFormattedText()).matches()) { findCooldownInTooltip(Item.SPIRIT_MASK); sprayonatorCooldownMillisRemaining = sprayonatorCooldown * 1000; } } private static void findCooldownInTooltip(Item item) { for (ItemStack stack : Minecraft.getMinecraft().thePlayer.inventory.mainInventory) { setSpecificCooldown(stack, item); } // Check helmet slot for items that can also be equipped as a helmet ItemStack stack = Minecraft.getMinecraft().thePlayer.inventory.armorInventory[3]; setSpecificCooldown(stack, item); } private static void setSpecificCooldown(ItemStack stack, Item item) { if (stack != null && stack.hasTagCompound()) { String internalname = NotEnoughUpdates.INSTANCE.manager.createItemResolutionQuery().withItemStack(stack).resolveInternalName(); if (internalname != null) { switch (item) { case PICKAXES: if (isPickaxe(internalname)) pickaxeCooldown = setCooldown(stack); break; case BONZO_MASK: if (internalname.equals("BONZO_MASK") || internalname.equals("STARRED_BONZO_MASK")) bonzoMaskCooldown = setCooldown(stack); break; case SPIRIT_MASK: if (internalname.equals("SPIRIT_MASK") || internalname.equals("STARRED_SPIRIT_MASK")) spiritMaskCooldown = setCooldown(stack); break; case SPRAYONATOR: if (internalname.equals("SPRAYONATOR")) sprayonatorCooldown = setCooldown(stack); break; } } } } private static int setCooldown(ItemStack stack) { for (String line : NotEnoughUpdates.INSTANCE.manager.getLoreFromNBT(stack.getTagCompound())) { Matcher matcher = COOLDOWN_LORE.matcher(line); if (matcher.find()) { try { return Integer.parseInt(matcher.group(1)); } catch (Exception ignored) { } } } return -1; } public static float getDurabilityOverride(ItemStack stack) { if (Minecraft.getMinecraft().theWorld == null) return -1; if (!NotEnoughUpdates.INSTANCE.hasSkyblockScoreboard()) return -1; if (durabilityOverrideMap.containsKey(stack)) { return durabilityOverrideMap.get(stack); } String internalname = NotEnoughUpdates.INSTANCE.manager.createItemResolutionQuery().withItemStack(stack).resolveInternalName(); if (internalname == null) { durabilityOverrideMap.put(stack, -1f); return -1; } // Pickaxes if (isPickaxe(internalname)) { findCooldownInTooltip(Item.PICKAXES); if (NotEnoughUpdates.INSTANCE.config.itemOverlays.pickaxeAbility) { return durabilityOverride(pickaxeUseCooldownMillisRemaining, pickaxeCooldown, stack); } else return -1; } // Treecapitator / Jungle Axe if (internalname.equals("TREECAPITATOR_AXE") || internalname.equals("JUNGLE_AXE")) { if (treecapitatorCooldownMillisRemaining < 0) { durabilityOverrideMap.put(stack, -1f); return -1; } if (treecapitatorCooldownMillisRemaining > getTreecapCooldownWithPet()) { return stack.getItemDamage(); } float durability = treecapitatorCooldownMillisRemaining / (float) getTreecapCooldownWithPet(); durabilityOverrideMap.put(stack, durability); return durability; } // Grappling Hook if (internalname.equals("GRAPPLING_HOOK")) { return grappleCooldownMillisRemaining / 2000f; } // Bonzo Mask if (NotEnoughUpdates.INSTANCE.config.itemOverlays.bonzoAbility && (internalname.equals("BONZO_MASK") || internalname.equals("STARRED_BONZO_MASK"))) { findCooldownInTooltip(Item.BONZO_MASK); return durabilityOverride(bonzomaskCooldownMillisRemaining, bonzoMaskCooldown, stack); } // Spirit Mask if (NotEnoughUpdates.INSTANCE.config.itemOverlays.spiritAbility && (internalname.equals("SPIRIT_MASK") || internalname.equals("STARRED_SPIRIT_MASK"))) { findCooldownInTooltip(Item.SPIRIT_MASK); return durabilityOverride(spiritMaskCooldownMillisRemaining, spiritMaskCooldown, stack); } if (NotEnoughUpdates.INSTANCE.config.garden.sprayonatorCooldown && internalname.equals("SPRAYONATOR")) { findCooldownInTooltip(Item.SPRAYONATOR); return durabilityOverride(sprayonatorCooldownMillisRemaining, sprayonatorCooldown, stack); } durabilityOverrideMap.put(stack, -1f); return -1; } private static float durabilityOverride(float millisRemaining, long cooldown, ItemStack stack) { if (millisRemaining < 0) { durabilityOverrideMap.put(stack, -1f); return -1; } if (millisRemaining > cooldown * 1000) { return stack.getItemDamage(); } float durability = (float) (millisRemaining / (cooldown * 1000.0)); durabilityOverrideMap.put(stack, durability); return durability; } }