/*
* Copyright (C) 2022-2023 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.overlays;
import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
import io.github.moulberry.notenoughupdates.core.config.Position;
import io.github.moulberry.notenoughupdates.core.util.lerp.LerpUtils;
import io.github.moulberry.notenoughupdates.util.Utils;
import io.github.moulberry.notenoughupdates.util.XPInformation;
import lombok.var;
import net.minecraft.client.Minecraft;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumChatFormatting;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Supplier;
public class CombatSkillOverlay
extends TextOverlay { //Im sure there is a much better way to do this besides making another class ¯\_(ツ)_/¯
private long lastUpdate = -1;
private int killLast = -1;
private int kill = -1;
private int championTier = -1;
private String championTierAmount = "1";
private int championXp = -1;
private int championXpLast = -1;
private final LinkedList killQueue = new LinkedList<>();
private boolean hasToxophilite = false;
private XPInformation.SkillInfo skillInfo = null;
private XPInformation.SkillInfo skillInfoLast = null;
private float lastTotalXp = -1;
private boolean isKilling = false;
private final LinkedList xpGainQueue = new LinkedList<>();
private float xpGainHourLast = -1;
private float xpGainHour = -1;
private int xpGainTimer = 0;
private final String skillType = "Combat";
public CombatSkillOverlay(
Position position,
Supplier> dummyStrings,
Supplier styleSupplier
) {
super(position, dummyStrings, styleSupplier);
}
private float interp(float now, float last) {
float interp = now;
if (last >= 0 && last != now) {
float factor = (System.currentTimeMillis() - lastUpdate) / 1000f;
factor = LerpUtils.clampZeroOne(factor);
interp = last + (now - last) * factor;
}
return interp;
}
@Override
public boolean isEnabled() {
return NotEnoughUpdates.INSTANCE.config.skillOverlays.combatSkillOverlay;
}
@Override
public void update() {
if (!isEnabled()) {
kill = -1;
championXp = -1;
overlayStrings = null;
return;
}
lastUpdate = System.currentTimeMillis();
killLast = kill;
championXpLast = championXp;
xpGainHourLast = xpGainHour;
kill = -1;
championXp = -1;
if (Minecraft.getMinecraft().thePlayer == null) return;
ItemStack stack = Minecraft.getMinecraft().thePlayer.getHeldItem();
if (stack != null && stack.hasTagCompound()) {
NBTTagCompound tag = stack.getTagCompound();
if (tag.hasKey("ExtraAttributes", 10)) {
NBTTagCompound ea = tag.getCompoundTag("ExtraAttributes");
if (ea.hasKey("stats_book", 99)) {
kill = ea.getInteger("stats_book");
killQueue.add(0, kill);
}
hasToxophilite = false;
if (ea.hasKey("champion_combat_xp", 99)) {
championXp = (int) ea.getDouble("champion_combat_xp");
} else if (ea.hasKey("toxophilite_combat_xp", 99)) {
championXp = (int) ea.getDouble("toxophilite_combat_xp");
hasToxophilite = true;
}
}
}
if (championXp < 50000) {
championTier = 1;
} else if (championXp < 100000) {
championTier = 2;
} else if (championXp < 250000) {
championTier = 3;
} else if (championXp < 500000) {
championTier = 4;
} else if (championXp < 1000000) {
championTier = 5;
} else if (championXp < 1500000) {
championTier = 6;
} else if (championXp < 2000000) {
championTier = 7;
} else if (championXp < 2500000) {
championTier = 8;
} else if (championXp < 3000000) {
championTier = 9;
} else if (championXp > 3000000) {
championTier = 10;
}
switch (championTier) {
case 1:
championTierAmount = "50,000";
break;
case 2:
championTierAmount = "100,000";
break;
case 3:
championTierAmount = "250,000";
break;
case 4:
championTierAmount = "500,000";
break;
case 5:
championTierAmount = "1,000,000";
break;
case 6:
championTierAmount = "1,500,000";
break;
case 7:
championTierAmount = "2,000,000";
break;
case 8:
championTierAmount = "2,500,000";
break;
case 9:
championTierAmount = "3,000,000";
break;
case 10:
championTierAmount = "Maxed";
break;
}
skillInfoLast = skillInfo;
var s = NotEnoughUpdates.INSTANCE.config.skillOverlays.combatText;
skillInfo = XPInformation.getInstance().getSkillInfo(
skillType,
s.contains(1) || s.contains(2) || s.contains(3) || s.contains(4)
);
if (skillInfo != null) {
float totalXp = (float) skillInfo.totalXp;
if (lastTotalXp > 0) {
float delta = totalXp - lastTotalXp;
if (delta > 0 && delta < 1000) {
xpGainTimer = NotEnoughUpdates.INSTANCE.config.skillOverlays.combatPauseTimer;
xpGainQueue.add(0, delta);
while (xpGainQueue.size() > 30) {
xpGainQueue.removeLast();
}
float totalGain = 0;
for (float f : xpGainQueue) totalGain += f;
xpGainHour = totalGain * (60 * 60) / xpGainQueue.size();
isKilling = true;
} else if (xpGainTimer > 0) {
xpGainTimer--;
xpGainQueue.add(0, 0f);
while (xpGainQueue.size() > 30) {
xpGainQueue.removeLast();
}
float totalGain = 0;
for (float f : xpGainQueue) totalGain += f;
xpGainHour = totalGain * (60 * 60) / xpGainQueue.size();
isKilling = true;
} else if (delta <= 0) {
isKilling = false;
}
}
lastTotalXp = totalXp;
}
while (killQueue.size() >= 4) {
killQueue.removeLast();
}
if (kill != -1 || championXp != -1) {
overlayStrings = new ArrayList<>();
} else {
overlayStrings = null;
}
}
@Override
public void updateFrequent() {
super.updateFrequent();
if ((kill < 0 && championXp < 0) && !NotEnoughUpdates.INSTANCE.config.skillOverlays.alwaysShowCombatOverlay) {
overlayStrings = null;
} else {
HashMap lineMap = new HashMap<>();
overlayStrings = new ArrayList<>();
NumberFormat format = NumberFormat.getIntegerInstance();
if (kill >= 0) {
int counterInterp = (int) interp(kill, killLast);
lineMap.put(0, EnumChatFormatting.AQUA + "Kills: " + EnumChatFormatting.YELLOW + format.format(counterInterp));
}
String enchantText = "Champion: ";
if (hasToxophilite) {
enchantText = "Toxophilite: ";
}
if (championTier <= 9 && championXp >= 0) {
int counterInterp = (int) interp(championXp, championXpLast);
lineMap.put(
6,
EnumChatFormatting.AQUA + enchantText + EnumChatFormatting.YELLOW + format.format(counterInterp) + "/" +
championTierAmount
);
}
if (championTier == 10) {
int counterInterp = (int) interp(championXp, championXpLast);
lineMap.put(
6,
EnumChatFormatting.AQUA + enchantText + EnumChatFormatting.YELLOW + format.format(counterInterp) + " " +
EnumChatFormatting.RED + championTierAmount
);
}
float xpInterp = xpGainHour;
if (xpGainHourLast == xpGainHour && xpGainHour <= 0) {
lineMap.put(4, EnumChatFormatting.AQUA + "XP/h: " + EnumChatFormatting.YELLOW + "N/A");
} else {
xpInterp = interp(xpGainHour, xpGainHourLast);
lineMap.put(4, EnumChatFormatting.AQUA + "XP/h: " + EnumChatFormatting.YELLOW +
format.format(xpInterp) + (isKilling ? "" : EnumChatFormatting.RED + " (PAUSED)"));
}
if (skillInfo != null && skillInfo.level < 60) {
StringBuilder levelStr = new StringBuilder(EnumChatFormatting.AQUA + "Combat" + ": ");
levelStr.append(EnumChatFormatting.YELLOW)
.append(skillInfo.level)
.append(EnumChatFormatting.GRAY)
.append(" [");
float progress = (float) (skillInfo.currentXp / skillInfo.currentXpMax);
if (skillInfoLast != null && skillInfo.currentXpMax == skillInfoLast.currentXpMax) {
progress = interp(progress, (float) (skillInfoLast.currentXp / skillInfoLast.currentXpMax));
}
float lines = 25;
for (int i = 0; i < lines; i++) {
if (i / lines < progress) {
levelStr.append(EnumChatFormatting.YELLOW);
} else {
levelStr.append(EnumChatFormatting.DARK_GRAY);
}
levelStr.append('|');
}
levelStr.append(EnumChatFormatting.GRAY)
.append("] ")
.append(EnumChatFormatting.YELLOW)
.append((int) (progress * 100))
.append("%");
int current = (int) skillInfo.currentXp;
if (skillInfoLast != null && skillInfo.currentXpMax == skillInfoLast.currentXpMax) {
current = (int) interp(current, (float) skillInfoLast.currentXp);
}
int remaining = (int) (skillInfo.currentXpMax - skillInfo.currentXp);
if (skillInfoLast != null && skillInfo.currentXpMax == skillInfoLast.currentXpMax) {
remaining = (int) interp(remaining, (int) (skillInfoLast.currentXpMax - skillInfoLast.currentXp));
}
lineMap.put(1, levelStr.toString());
lineMap.put(2, EnumChatFormatting.AQUA + "Current XP: " + EnumChatFormatting.YELLOW + format.format(current));
if (remaining < 0) {
lineMap.put(3, EnumChatFormatting.AQUA + "Remaining XP: " + EnumChatFormatting.YELLOW + "MAXED!");
lineMap.put(5, EnumChatFormatting.AQUA + "ETA: " + EnumChatFormatting.YELLOW + "MAXED!");
} else {
lineMap.put(
3,
EnumChatFormatting.AQUA + "Remaining XP: " + EnumChatFormatting.YELLOW + format.format(remaining)
);
if (xpGainHour < 1000) {
lineMap.put(5, EnumChatFormatting.AQUA + "ETA: " + EnumChatFormatting.YELLOW + "N/A");
} else {
lineMap.put(
5,
EnumChatFormatting.AQUA + "ETA: " + EnumChatFormatting.YELLOW +
Utils.prettyTime((long) (remaining) * 1000 * 60 * 60 / (long) xpInterp)
);
}
}
}
if (skillInfo != null && skillInfo.level == 60) {
int current = (int) skillInfo.currentXp;
if (skillInfoLast != null && skillInfo.currentXpMax == skillInfoLast.currentXpMax) {
current = (int) interp(current,(float) skillInfoLast.currentXp);
}
lineMap.put(
1,
EnumChatFormatting.AQUA + "Combat: " + EnumChatFormatting.YELLOW + "60 " + EnumChatFormatting.RED + "(Maxed)"
);
lineMap.put(2, EnumChatFormatting.AQUA + "Current XP: " + EnumChatFormatting.YELLOW + format.format(current));
}
for (int strIndex : NotEnoughUpdates.INSTANCE.config.skillOverlays.combatText) {
if (lineMap.get(strIndex) != null) {
overlayStrings.add(lineMap.get(strIndex));
}
}
if (overlayStrings != null && overlayStrings.isEmpty()) overlayStrings = null;
}
}
}