/*
* 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;
}
}