aboutsummaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/de/hysky/skyblocker/config/categories/MiningCategory.java33
-rw-r--r--src/main/java/de/hysky/skyblocker/config/configs/MiningConfig.java3
-rw-r--r--src/main/java/de/hysky/skyblocker/config/screens/powdertracker/PowderFilterConfigScreen.java4
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dwarven/CorpseFinder.java57
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dwarven/CorpseType.java71
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dwarven/profittrackers/AbstractProfitTracker.java26
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dwarven/profittrackers/PowderMiningTracker.java (renamed from src/main/java/de/hysky/skyblocker/skyblock/dwarven/PowderMiningTracker.java)224
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dwarven/profittrackers/corpse/CorpseList.java251
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dwarven/profittrackers/corpse/CorpseLoot.java82
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dwarven/profittrackers/corpse/CorpseProfitScreen.java103
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dwarven/profittrackers/corpse/CorpseProfitTracker.java279
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dwarven/profittrackers/corpse/Reward.java46
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dwarven/profittrackers/corpse/RewardList.java256
13 files changed, 1266 insertions, 169 deletions
diff --git a/src/main/java/de/hysky/skyblocker/config/categories/MiningCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/MiningCategory.java
index 3eed81da..4b7d198d 100644
--- a/src/main/java/de/hysky/skyblocker/config/categories/MiningCategory.java
+++ b/src/main/java/de/hysky/skyblocker/config/categories/MiningCategory.java
@@ -6,7 +6,7 @@ import de.hysky.skyblocker.config.configs.MiningConfig;
import de.hysky.skyblocker.config.screens.powdertracker.PowderFilterConfigScreen;
import de.hysky.skyblocker.skyblock.dwarven.CrystalsHudWidget;
import de.hysky.skyblocker.skyblock.dwarven.CarpetHighlighter;
-import de.hysky.skyblocker.skyblock.dwarven.PowderMiningTracker;
+import de.hysky.skyblocker.skyblock.dwarven.profittrackers.PowderMiningTracker;
import de.hysky.skyblocker.skyblock.tabhud.widget.CommsWidget;
import dev.isxander.yacl3.api.*;
import dev.isxander.yacl3.api.controller.ColorControllerBuilder;
@@ -122,12 +122,23 @@ public class MiningCategory {
newValue -> config.mining.crystalHollows.chestHighlightColor = newValue)
.controller(v -> ColorControllerBuilder.create(v).allowAlpha(true))
.build())
- .option(ButtonOption.createBuilder()
- .name(Text.translatable("skyblocker.config.mining.crystalHollows.powderTrackerFilter"))
- .description(OptionDescription.of(Text.translatable("skyblocker.config.mining.crystalHollows.powderTrackerFilter.@Tooltip")))
- .text(Text.translatable("text.skyblocker.open"))
- .action((screen, opt) -> MinecraftClient.getInstance().setScreen(new PowderFilterConfigScreen(screen, new ObjectImmutableList<>(PowderMiningTracker.getName2IdMap().keySet()))))
- .build())
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("skyblocker.config.mining.crystalHollows.enablePowderTracker"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.mining.crystalHollows.enablePowderTracker.@Tooltip")))
+ .binding(defaults.mining.crystalHollows.enablePowderTracker,
+ () -> config.mining.crystalHollows.enablePowderTracker,
+ newValue -> {
+ config.mining.crystalHollows.enablePowderTracker = newValue;
+ if (newValue) PowderMiningTracker.INSTANCE.recalculateAll();
+ })
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .option(ButtonOption.createBuilder()
+ .name(Text.translatable("skyblocker.config.mining.crystalHollows.powderTrackerFilter"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.mining.crystalHollows.powderTrackerFilter.@Tooltip")))
+ .text(Text.translatable("text.skyblocker.open"))
+ .action((screen, opt) -> MinecraftClient.getInstance().setScreen(new PowderFilterConfigScreen(screen, new ObjectImmutableList<>(PowderMiningTracker.getName2IdMap().keySet()))))
+ .build())
.build())
//Crystal Hollows Map
@@ -287,6 +298,14 @@ public class MiningCategory {
newValue -> config.mining.glacite.autoShareCorpses = newValue)
.controller(ConfigUtils::createBooleanController)
.build())
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("skyblocker.config.mining.glacite.enableCorpseProfitTracker"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.mining.glacite.enableCorpseProfitTracker.@Tooltip")))
+ .binding(defaults.mining.glacite.enableCorpseProfitTracker,
+ () -> config.mining.glacite.enableCorpseProfitTracker,
+ newValue -> config.mining.glacite.enableCorpseProfitTracker = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
.build())
.build();
}
diff --git a/src/main/java/de/hysky/skyblocker/config/configs/MiningConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/MiningConfig.java
index 34c59429..33ed1b05 100644
--- a/src/main/java/de/hysky/skyblocker/config/configs/MiningConfig.java
+++ b/src/main/java/de/hysky/skyblocker/config/configs/MiningConfig.java
@@ -173,6 +173,9 @@ public class MiningConfig {
@SerialEntry
public boolean autoShareCorpses = false;
+
+ @SerialEntry
+ public boolean enableCorpseProfitTracker = true;
}
/**
diff --git a/src/main/java/de/hysky/skyblocker/config/screens/powdertracker/PowderFilterConfigScreen.java b/src/main/java/de/hysky/skyblocker/config/screens/powdertracker/PowderFilterConfigScreen.java
index fbd2668a..a5c90a2a 100644
--- a/src/main/java/de/hysky/skyblocker/config/screens/powdertracker/PowderFilterConfigScreen.java
+++ b/src/main/java/de/hysky/skyblocker/config/screens/powdertracker/PowderFilterConfigScreen.java
@@ -1,7 +1,7 @@
package de.hysky.skyblocker.config.screens.powdertracker;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
-import de.hysky.skyblocker.skyblock.dwarven.PowderMiningTracker;
+import de.hysky.skyblocker.skyblock.dwarven.profittrackers.PowderMiningTracker;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
@@ -63,7 +63,7 @@ public class PowderFilterConfigScreen extends Screen {
public void saveFilters() {
SkyblockerConfigManager.get().mining.crystalHollows.powderTrackerFilter = filters;
SkyblockerConfigManager.save();
- PowderMiningTracker.recalculateAll();
+ PowderMiningTracker.INSTANCE.recalculateAll();
}
@Override
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CorpseFinder.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CorpseFinder.java
index f3dd9aab..3f4ec90a 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CorpseFinder.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CorpseFinder.java
@@ -1,13 +1,12 @@
package de.hysky.skyblocker.skyblock.dwarven;
import com.mojang.brigadier.Command;
-import com.mojang.brigadier.context.CommandContext;
-import com.mojang.serialization.Codec;
import de.hysky.skyblocker.SkyblockerMod;
import de.hysky.skyblocker.annotations.Init;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.debug.Debug;
import de.hysky.skyblocker.events.SkyblockEvents;
+import de.hysky.skyblocker.skyblock.dwarven.CorpseType.CorpseTypeArgumentType;
import de.hysky.skyblocker.utils.*;
import de.hysky.skyblocker.utils.command.argumenttypes.blockpos.ClientBlockPosArgumentType;
import de.hysky.skyblocker.utils.scheduler.MessageScheduler;
@@ -19,7 +18,6 @@ import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
import net.minecraft.client.MinecraftClient;
-import net.minecraft.command.argument.EnumArgumentType;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.decoration.ArmorStandEntity;
@@ -27,7 +25,6 @@ import net.minecraft.text.ClickEvent;
import net.minecraft.text.HoverEvent;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
-import net.minecraft.util.StringIdentifiable;
import net.minecraft.util.Util;
import net.minecraft.util.math.BlockPos;
import org.apache.commons.lang3.EnumUtils;
@@ -49,10 +46,6 @@ public class CorpseFinder {
private static final String PREFIX = "[Skyblocker Corpse Finder] ";
private static final Logger LOGGER = LoggerFactory.getLogger(CorpseFinder.class);
private static final Map<CorpseType, List<Corpse>> corpsesByType = new EnumMap<>(CorpseType.class);
- private static final String LAPIS_HELMET = "LAPIS_ARMOR_HELMET";
- private static final String UMBER_HELMET = "ARMOR_OF_YOG_HELMET";
- private static final String TUNGSTEN_HELMET = "MINERAL_HELMET";
- private static final String VANGUARD_HELMET = "VANGUARD_HELMET";
@Init
public static void init() {
@@ -78,9 +71,9 @@ public class CorpseFinder {
.then(literal("corpseHelper")
.then(literal("shareLocation")
.then(argument("blockPos", ClientBlockPosArgumentType.blockPos())
- .then(argument("corpseType", CorpseType.CorpseTypeArgumentType.corpseType())
+ .then(argument("corpseType", CorpseTypeArgumentType.corpseType())
.executes(context -> {
- shareLocation(ClientBlockPosArgumentType.getBlockPos(context, "blockPos"), CorpseType.CorpseTypeArgumentType.getCorpseType(context, "corpseType"));
+ shareLocation(ClientBlockPosArgumentType.getBlockPos(context, "blockPos"), CorpseTypeArgumentType.getCorpseType(context, "corpseType"));
return Command.SINGLE_SUCCESS;
})
)
@@ -250,50 +243,6 @@ public class CorpseFinder {
}
}
- enum CorpseType implements StringIdentifiable {
- LAPIS(LAPIS_HELMET, Formatting.BLUE), // dark blue looks bad and these two never exist in same shaft
- UMBER(UMBER_HELMET, Formatting.RED),
- TUNGSTEN(TUNGSTEN_HELMET, Formatting.GRAY),
- VANGUARD(VANGUARD_HELMET, Formatting.BLUE),
- UNKNOWN("UNKNOWN", Formatting.YELLOW);
- private static final Codec<CorpseType> CODEC = StringIdentifiable.createCodec(CorpseType::values);
- private final String helmetItemId;
- private final Formatting color;
-
- CorpseType(String helmetItemId, Formatting color) {
- this.helmetItemId = helmetItemId;
- this.color = color;
- }
-
- static CorpseType fromHelmetItemId(String helmetItemId) {
- for (CorpseType value : values()) {
- if (value.helmetItemId.equals(helmetItemId)) {
- return value;
- }
- }
- return UNKNOWN;
- }
-
- @Override
- public String asString() {
- return name().toLowerCase();
- }
-
- static class CorpseTypeArgumentType extends EnumArgumentType<CorpseType> {
- protected CorpseTypeArgumentType() {
- super(CODEC, CorpseType::values);
- }
-
- static CorpseTypeArgumentType corpseType() {
- return new CorpseTypeArgumentType();
- }
-
- static <S> CorpseType getCorpseType(CommandContext<S> context, String name) {
- return context.getArgument(name, CorpseType.class);
- }
- }
- }
-
static class Corpse {
private final ArmorStandEntity entity;
/**
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CorpseType.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CorpseType.java
new file mode 100644
index 00000000..b48ff153
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CorpseType.java
@@ -0,0 +1,71 @@
+package de.hysky.skyblocker.skyblock.dwarven;
+
+import com.mojang.brigadier.context.CommandContext;
+import com.mojang.serialization.Codec;
+import de.hysky.skyblocker.utils.ItemUtils;
+import net.minecraft.command.argument.EnumArgumentType;
+import net.minecraft.util.Formatting;
+import net.minecraft.util.StringIdentifiable;
+
+public enum CorpseType implements StringIdentifiable {
+ LAPIS("LAPIS_ARMOR_HELMET", null, Formatting.BLUE), // dark blue looks bad and these two never exist in same shaft
+ UMBER("ARMOR_OF_YOG_HELMET", "UMBER_KEY", Formatting.GOLD),
+ TUNGSTEN("MINERAL_HELMET", "TUNGSTEN_KEY", Formatting.GRAY),
+ VANGUARD("VANGUARD_HELMET", "SKELETON_KEY", Formatting.AQUA),
+ UNKNOWN("UNKNOWN", null, Formatting.RED);
+
+ public static final Codec<CorpseType> CODEC = StringIdentifiable.createCodec(CorpseType::values);
+ public final String helmetItemId;
+ public final String keyItemId;
+ public final Formatting color;
+
+ CorpseType(String helmetItemId, String keyItemId, Formatting color) {
+ this.helmetItemId = helmetItemId;
+ this.keyItemId = keyItemId;
+ this.color = color;
+ }
+
+ static CorpseType fromHelmetItemId(String helmetItemId) {
+ for (CorpseType value : values()) {
+ if (value.helmetItemId.equals(helmetItemId)) {
+ return value;
+ }
+ }
+ return UNKNOWN;
+ }
+
+ @Override
+ public String asString() {
+ return name().toLowerCase();
+ }
+
+ /**
+ * @return the price of the key item for this corpse type
+ * @throws IllegalStateException when there's no price found for the key item, or when the corpse type is UNKNOWN
+ */
+ public double getKeyPrice() throws IllegalStateException {
+ return switch (this) {
+ case UNKNOWN -> throw new IllegalStateException("There's no key or key price for the UNKNOWN corpse type!");
+ case LAPIS -> 0; // Lapis corpses don't need a key
+ default -> {
+ var result = ItemUtils.getItemPrice(keyItemId);
+ if (!result.rightBoolean()) throw new IllegalStateException("No price found for key item `" + keyItemId + "`!");
+ yield result.leftDouble();
+ }
+ };
+ }
+
+ public static class CorpseTypeArgumentType extends EnumArgumentType<CorpseType> {
+ protected CorpseTypeArgumentType() {
+ super(CODEC, CorpseType::values);
+ }
+
+ static CorpseTypeArgumentType corpseType() {
+ return new CorpseTypeArgumentType();
+ }
+
+ static <S> CorpseType getCorpseType(CommandContext<S> context, String name) {
+ return context.getArgument(name, CorpseType.class);
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/profittrackers/AbstractProfitTracker.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/profittrackers/AbstractProfitTracker.java
new file mode 100644
index 00000000..172764e6
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/profittrackers/AbstractProfitTracker.java
@@ -0,0 +1,26 @@
+package de.hysky.skyblocker.skyblock.dwarven.profittrackers;
+
+import de.hysky.skyblocker.SkyblockerMod;
+
+import java.nio.file.Path;
+import java.util.regex.Pattern;
+
+/**
+ * Abstract class for profit trackers that use the chat messages.
+ * <br>
+ * There isn't meant to be much inheritance from this class, it's more of a util class that provides some common methods.
+ */
+public abstract class AbstractProfitTracker {
+ private static final String REWARD_TRACKERS_DIR = "reward-trackers";
+ protected static final Pattern REWARD_PATTERN = Pattern.compile(" {4}(.*?) ?x?([\\d,]*)");
+ protected static final Pattern HOTM_XP_PATTERN = Pattern.compile(" {4}\\+[\\d,]+ HOTM Experience");
+ protected static final Pattern GEMSTONE_SYMBOLS = Pattern.compile("[α☘☠✎✧❁❂❈❤⸕] ");
+
+ protected static String replaceGemstoneSymbols(String reward) {
+ return GEMSTONE_SYMBOLS.matcher(reward).replaceAll("");
+ }
+
+ protected Path getRewardFilePath(String fileName) {
+ return SkyblockerMod.CONFIG_DIR.resolve(REWARD_TRACKERS_DIR).resolve(fileName); // 2 resolve calls to avoid the need for a possibly confusing / placement
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/PowderMiningTracker.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/profittrackers/PowderMiningTracker.java
index 121422d5..10b9d776 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/PowderMiningTracker.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/profittrackers/PowderMiningTracker.java
@@ -1,5 +1,6 @@
-package de.hysky.skyblocker.skyblock.dwarven;
+package de.hysky.skyblocker.skyblock.dwarven.profittrackers;
+import com.mojang.brigadier.Command;
import com.mojang.serialization.Codec;
import de.hysky.skyblocker.SkyblockerMod;
import de.hysky.skyblocker.annotations.Init;
@@ -9,13 +10,11 @@ import de.hysky.skyblocker.events.HudRenderEvents;
import de.hysky.skyblocker.events.ItemPriceUpdateEvent;
import de.hysky.skyblocker.events.SkyblockEvents;
import de.hysky.skyblocker.skyblock.itemlist.ItemRepository;
-import de.hysky.skyblocker.utils.CodecUtils;
-import de.hysky.skyblocker.utils.ItemUtils;
-import de.hysky.skyblocker.utils.Location;
-import de.hysky.skyblocker.utils.Utils;
+import de.hysky.skyblocker.utils.*;
import de.hysky.skyblocker.utils.profile.ProfiledData;
import it.unimi.dsi.fastutil.doubles.DoubleBooleanPair;
import it.unimi.dsi.fastutil.objects.*;
+import it.unimi.dsi.fastutil.objects.Object2IntMap.Entry;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
@@ -30,105 +29,125 @@ import org.jetbrains.annotations.Unmodifiable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.nio.file.Path;
import java.text.NumberFormat;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
-public class PowderMiningTracker {
+public final class PowderMiningTracker extends AbstractProfitTracker {
+ public static final PowderMiningTracker INSTANCE = new PowderMiningTracker();
private static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Powder Mining Tracker");
- private static final Pattern GEMSTONE_SYMBOLS = Pattern.compile("[α☘☠✎✧❁❂❈❤⸕] ");
- private static final Pattern REWARD_PATTERN = Pattern.compile(" {4}(.*?) ?x?([\\d,]*)");
private static final Codec<Object2IntMap<String>> REWARDS_CODEC = CodecUtils.object2IntMapCodec(Codec.STRING);
private static final Object2ObjectArrayMap<String, String> NAME2ID_MAP = new Object2ObjectArrayMap<>(50);
- // This constructor takes in a comparator that is triggered to decide where to add the element in the tree map
- // This causes it to be sorted at all times. This is for rendering them in a sort of easy-to-read manner.
- private static final Object2IntAVLTreeMap<Text> SHOWN_REWARDS = new Object2IntAVLTreeMap<>(Comparator.<Text>comparingInt(text -> comparePriority(text.getString())).thenComparing(Text::getString));
-
- /**
- * Holds the total reward maps for all accounts and profiles. {@link #currentProfileRewards} is a subset of this map, updated on profile change.
- */
- private static final ProfiledData<Object2IntMap<String>> ALL_REWARDS = new ProfiledData<>(getRewardFilePath(), REWARDS_CODEC);
-
/**
* <p>
* Holds the total amount of each reward obtained for the current profile.
- * If any items are filtered out, they are still added to this map but not to the {@link #SHOWN_REWARDS} map.
- * Once the filter is changed, the {@link #SHOWN_REWARDS} map is cleared and recalculated based on this map.
+ * If any items are filtered out, they are still added to this map but not to the {@link #shownRewards} map.
+ * Once the filter is changed, the {@link #shownRewards} map is cleared and recalculated based on this map.
* </p>
* <p>This is similar to how {@link ChatHud#messages} and {@link ChatHud#visibleMessages} behave.</p>
*
* @implNote This is a map of item IDs to the amount of that item obtained.
*/
@SuppressWarnings("JavadocReference")
- private static Object2IntMap<String> currentProfileRewards = new Object2IntOpenHashMap<>();
- private static boolean insideChestMessage = false;
- private static double profit = 0;
+ private Object2IntMap<String> currentProfileRewards = new Object2IntOpenHashMap<>();
+
+ // This constructor takes in a comparator that is triggered to decide where to add the element in the tree map
+ // This causes it to be sorted at all times. This is for rendering them in a sort of easy-to-read manner.
+ private final Object2IntAVLTreeMap<Text> shownRewards = new Object2IntAVLTreeMap<>(Comparator.<Text>comparingInt(text -> comparePriority(text.getString())).thenComparing(Text::getString));
+
+ /**
+ * Holds the total reward maps for all accounts and profiles. {@link #currentProfileRewards} is a subset of this map, updated on profile change.
+ */
+ private final ProfiledData<Object2IntMap<String>> allRewards = new ProfiledData<>(getRewardFilePath("powder-mining.json"), REWARDS_CODEC);
+ private boolean insideChestMessage = false;
+ private double profit = 0;
+
+ private PowderMiningTracker() {} // Singleton
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
- private static boolean isEnabled() {
+ public boolean isEnabled() {
return SkyblockerConfigManager.get().mining.crystalHollows.enablePowderTracker;
}
@Init
public static void init() {
- ChatEvents.RECEIVE_STRING.register(PowderMiningTracker::onChatMessage);
+ ChatEvents.RECEIVE_STRING.register(INSTANCE::onChatMessage);
HudRenderEvents.AFTER_MAIN_HUD.register(PowderMiningTracker::render);
-
- ItemPriceUpdateEvent.ON_PRICE_UPDATE.register(() -> {
- if (isEnabled()) recalculatePrices();
- });
-
- ALL_REWARDS.init();
-
- SkyblockEvents.PROFILE_CHANGE.register(PowderMiningTracker::onProfileChange);
- SkyblockEvents.PROFILE_INIT.register(PowderMiningTracker::onProfileInit);
-
- //TODO: Sort out proper commands for this
- ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(
- literal(SkyblockerMod.NAMESPACE)
- .then(
- literal("clearrewards")
- .executes(context -> {
- SHOWN_REWARDS.clear();
- currentProfileRewards.clear();
- profit = 0;
- return 1;
- })
+ ItemPriceUpdateEvent.ON_PRICE_UPDATE.register(INSTANCE::onPriceUpdate);
+
+ INSTANCE.allRewards.init();
+
+ // @formatter:off // Don't you hate it when your format style for chained method calls makes a chain like this incredibly ugly?
+ ClientCommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> dispatcher.register(
+ literal(SkyblockerMod.NAMESPACE)
+ .then(literal("rewardTrackers")
+ .then(literal("powderMining")
+ .then(literal("list")
+ .executes(ctx -> {
+ if (INSTANCE.currentProfileRewards.isEmpty()) {
+ ctx.getSource().sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.powderTracker.emptyHistory").formatted(Formatting.RED)));
+ return Command.SINGLE_SUCCESS;
+ } else if (INSTANCE.shownRewards.isEmpty()) {
+ ctx.getSource().sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.powderTracker.rewardsFilteredOut").formatted(Formatting.RED)));
+ return Command.SINGLE_SUCCESS;
+ }
+
+ for (Entry<Text> entry : INSTANCE.shownRewards.object2IntEntrySet()) {
+ ctx.getSource().sendFeedback(
+ Text.empty()
+ .append(entry.getKey())
+ .append(Text.literal(": ").formatted(Formatting.GRAY))
+ .append(Text.literal(String.valueOf(entry.getIntValue()))));
+ }
+ ctx.getSource().sendFeedback(Text.translatable("skyblocker.powderTracker.profit", NumberFormat.getInstance().format(INSTANCE.profit)).formatted(Formatting.GOLD));
+ return Command.SINGLE_SUCCESS;
+ })
)
- .then(
- literal("listrewards")
- .executes(context -> {
- var set = SHOWN_REWARDS.object2IntEntrySet();
- for (Object2IntMap.Entry<Text> entry : set) {
- MinecraftClient.getInstance().inGameHud.getChatHud().addMessage(entry.getKey().copy().append(" ").append(Text.of(String.valueOf(entry.getIntValue()))));
- }
- return 1;
- })
+ .then(literal("reset")
+ .executes(ctx -> {
+ INSTANCE.currentProfileRewards.clear();
+ INSTANCE.allRewards.save();
+ ctx.getSource().sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.powderTracker.historyReset").formatted(Formatting.GREEN)));
+ return Command.SINGLE_SUCCESS;
+ })
)
- ));
+ )
+ )
+ )); // @formatter:on
+
+ SkyblockEvents.PROFILE_CHANGE.register(INSTANCE::onProfileChange);
+ SkyblockEvents.PROFILE_INIT.register(INSTANCE::onProfileInit);
+ }
+
+ private void onProfileChange(String prevProfileId, String newProfileId) {
+ onProfileInit(newProfileId);
+ }
+
+ private void onProfileInit(String profileId) {
+ if (!isEnabled()) return;
+ currentProfileRewards = allRewards.computeIfAbsent(Object2IntArrayMap::new);
+ recalculateAll();
}
- private static void onChatMessage(String text) {
- if (Utils.getLocation() != Location.CRYSTAL_HOLLOWS || !isEnabled()) return;
+ private void onChatMessage(String message) {
+ if (Utils.getLocation() != Location.CRYSTAL_HOLLOWS || !INSTANCE.isEnabled()) return;
// Reward messages end with a separator like so
- if (insideChestMessage && text.equals("▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬")) {
+ if (insideChestMessage && message.equals("▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬")) {
insideChestMessage = false;
return;
}
- if (!insideChestMessage && (text.equals(" CHEST LOCKPICKED ") || (SkyblockerConfigManager.get().mining.crystalHollows.countNaturalChestsInTracker && text.equals(" LOOT CHEST COLLECTED ")))) {
+ if (!insideChestMessage && (message.equals(" CHEST LOCKPICKED ") || (SkyblockerConfigManager.get().mining.crystalHollows.countNaturalChestsInTracker && message.equals(" LOOT CHEST COLLECTED ")))) {
insideChestMessage = true;
return;
}
if (!insideChestMessage) return;
- Matcher matcher = REWARD_PATTERN.matcher(text);
+ Matcher matcher = REWARD_PATTERN.matcher(message);
if (!matcher.matches()) return;
String itemName = matcher.group(1);
int amount = NumberUtils.toInt(matcher.group(2).replace(",", ""), 1);
@@ -142,35 +161,25 @@ public class PowderMiningTracker {
calculateProfitForItem(itemId, amount);
}
- private static void onProfileChange(String prevProfileId, String newProfileId) {
- onProfileInit(newProfileId);
- }
-
- private static void onProfileInit(String profileId) {
- if (!isEnabled()) return;
- currentProfileRewards = ALL_REWARDS.computeIfAbsent(Object2IntArrayMap::new);
- recalculateAll();
- }
-
- private static void incrementReward(String itemName, String itemId, int amount) {
+ private void incrementReward(String itemName, String itemId, int amount) {
currentProfileRewards.mergeInt(itemId, amount, Integer::sum);
- if (SkyblockerConfigManager.get().mining.crystalHollows.powderTrackerFilter.contains(itemName)) return;
- if (itemId.equals("GEMSTONE_POWDER")) {
- SHOWN_REWARDS.merge(Text.literal("Gemstone Powder").formatted(Formatting.LIGHT_PURPLE), amount, Integer::sum);
- } else {
- ItemStack stack = ItemRepository.getItemStack(itemId);
- if (stack == null) {
- LOGGER.warn("Item stack for id `{}` is null! This might be caused by failed item repository downloads.", itemId);
- return;
+ if (!SkyblockerConfigManager.get().mining.crystalHollows.powderTrackerFilter.contains(itemName)) {
+ if (itemId.equals("GEMSTONE_POWDER")) {
+ shownRewards.merge(Text.literal("Gemstone Powder").formatted(Formatting.LIGHT_PURPLE), amount, Integer::sum);
+ } else {
+ ItemStack stack = ItemRepository.getItemStack(itemId);
+ if (stack == null) {
+ LOGGER.warn("Item stack for id `{}` is null! This might be caused by failed item repository downloads.", itemId);
+ return;
+ }
+ shownRewards.merge(stack.getName(), amount, Integer::sum);
}
- SHOWN_REWARDS.merge(stack.getName(), amount, Integer::sum);
}
}
- private static int comparePriority(String string) {
- string = GEMSTONE_SYMBOLS.matcher(string).replaceAll(""); // Removes the gemstone symbol from the string to make it easier to compare
+ private static int comparePriority(String itemName) {
// Puts gemstone powder at the top of the list, then gold and diamond essence, then gemstones by ascending rarity and then whatever else.
- return switch (string) {
+ return switch (replaceGemstoneSymbols(itemName)) {
case "Gemstone Powder" -> 1;
case "Gold Essence" -> 2;
case "Diamond Essence" -> 3;
@@ -182,10 +191,14 @@ public class PowderMiningTracker {
};
}
+ private void onPriceUpdate() {
+ if (isEnabled()) recalculatePrices();
+ }
+
/**
* Normally, the price is calculated on a per-reward basis as they are obtained. This is what this method does.
*/
- private static void calculateProfitForItem(String itemId, int amount) {
+ private void calculateProfitForItem(String itemId, int amount) {
DoubleBooleanPair price = ItemUtils.getItemPrice(itemId);
if (price.rightBoolean()) profit += price.leftDouble() * amount;
}
@@ -193,35 +206,36 @@ public class PowderMiningTracker {
/**
* When the bz/ah prices are updated, this method recalculates the profit for all rewards at once.
*/
- private static void recalculatePrices() {
+ private void recalculatePrices() {
profit = 0;
- ObjectSortedSet<Object2IntMap.Entry<Text>> set = SHOWN_REWARDS.object2IntEntrySet();
- for (Object2IntMap.Entry<Text> entry : set) {
+ ObjectSortedSet<Entry<Text>> set = shownRewards.object2IntEntrySet();
+ for (Entry<Text> entry : set) {
calculateProfitForItem(getItemId(entry.getKey().getString()), entry.getIntValue());
}
}
/**
- * Resets the shown rewards and profit to 0 and recalculates rewards for the current profile based on the config filter.
+ * <p>Resets the shown rewards and profit to 0 and recalculates rewards for the current profile based on the config filter.</p>
+ * <p>This is also called from the config when the feature is enabled, as the periodic recalculation doesn't happen when the feature is disabled.</p>
*/
- public static void recalculateAll() {
- SHOWN_REWARDS.clear();
- ObjectSet<Object2IntMap.Entry<String>> set = currentProfileRewards.object2IntEntrySet();
+ public void recalculateAll() {
+ shownRewards.clear();
+ ObjectSet<Entry<String>> set = currentProfileRewards.object2IntEntrySet();
// The filters are actually item names so that they would look nice and not need a lot of mapping under the screen code
// Here they are converted to item IDs for comparison
- List<String> filters = SkyblockerConfigManager.get().mining.crystalHollows.powderTrackerFilter.stream().map(PowderMiningTracker::getItemId).toList();
- for (Object2IntMap.Entry<String> entry : set) {
+ List<String> filters = SkyblockerConfigManager.get().mining.crystalHollows.powderTrackerFilter.stream().map(INSTANCE::getItemId).toList();
+ for (Entry<String> entry : set) {
if (filters.contains(entry.getKey())) continue;
if (entry.getKey().equals("GEMSTONE_POWDER")) {
- SHOWN_REWARDS.put(Text.literal("Gemstone Powder").formatted(Formatting.LIGHT_PURPLE), entry.getIntValue());
+ shownRewards.put(Text.literal("Gemstone Powder").formatted(Formatting.LIGHT_PURPLE), entry.getIntValue());
} else {
ItemStack stack = ItemRepository.getItemStack(entry.getKey());
if (stack == null) {
LOGGER.warn("Item stack for id `{}` is null! This might be caused by failed item repository downloads.", entry.getKey());
continue;
}
- SHOWN_REWARDS.put(stack.getName(), entry.getIntValue());
+ shownRewards.put(stack.getName(), entry.getIntValue());
}
}
recalculatePrices();
@@ -232,6 +246,7 @@ public class PowderMiningTracker {
return Object2ObjectMaps.unmodifiable(NAME2ID_MAP);
}
+ // TODO: Perhaps make a little something in the skyblocker-assets repo for this in case it needs updating in the future
static {
NAME2ID_MAP.put("Gemstone Powder", "GEMSTONE_POWDER"); // Not an actual item, but since we're using IDs for mapping to colored text we need to have this here
@@ -297,23 +312,20 @@ public class PowderMiningTracker {
}
@NotNull
- private static String getItemId(String itemName) {
+ private String getItemId(String itemName) {
return NAME2ID_MAP.getOrDefault(itemName, "");
}
- private static Path getRewardFilePath() {
- return SkyblockerMod.CONFIG_DIR.resolve("reward-trackers/powder-mining.json");
- }
-
+ // TODO: Make this a hud widget without the background (optional), needs to be moveable
private static void render(DrawContext context, RenderTickCounter tickCounter) {
- if (Utils.getLocation() != Location.CRYSTAL_HOLLOWS || !isEnabled()) return;
+ if (Utils.getLocation() != Location.CRYSTAL_HOLLOWS || !INSTANCE.isEnabled()) return;
int y = MinecraftClient.getInstance().getWindow().getScaledHeight() / 2 - 100;
- var set = SHOWN_REWARDS.object2IntEntrySet();
- for (Object2IntMap.Entry<Text> entry : set) {
+ var set = INSTANCE.shownRewards.object2IntEntrySet();
+ for (Entry<Text> entry : set) {
context.drawTextWithShadow(MinecraftClient.getInstance().textRenderer, entry.getKey(), 5, y, 0xFFFFFF);
context.drawTextWithShadow(MinecraftClient.getInstance().textRenderer, Text.of(NumberFormat.getInstance().format(entry.getIntValue())), 10 + MinecraftClient.getInstance().textRenderer.getWidth(entry.getKey()), y, 0xFFFFFF);
y += 10;
}
- if (!set.isEmpty()) context.drawTextWithShadow(MinecraftClient.getInstance().textRenderer, Text.literal("Gain: " + NumberFormat.getInstance().format(profit) + " coins").formatted(Formatting.GOLD), 5, y + 10, 0xFFFFFF);
+ context.drawTextWithShadow(MinecraftClient.getInstance().textRenderer, Text.translatable("skyblocker.powderTracker.profit", NumberFormat.getInstance().format(INSTANCE.profit)).formatted(Formatting.GOLD), 5, y + 10, 0xFFFFFF);
}
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/profittrackers/corpse/CorpseList.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/profittrackers/corpse/CorpseList.java
new file mode 100644
index 00000000..5e4eaec5
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/profittrackers/corpse/CorpseList.java
@@ -0,0 +1,251 @@
+package de.hysky.skyblocker.skyblock.dwarven.profittrackers.corpse;
+
+import de.hysky.skyblocker.skyblock.dwarven.CorpseType;
+import de.hysky.skyblocker.skyblock.itemlist.ItemRepository;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.Element;
+import net.minecraft.client.gui.Selectable;
+import net.minecraft.client.gui.widget.ClickableWidget;
+import net.minecraft.client.gui.widget.ElementListWidget;
+import net.minecraft.client.gui.widget.TextWidget;
+import net.minecraft.item.ItemStack;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import org.apache.commons.text.WordUtils;
+import org.jetbrains.annotations.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.text.NumberFormat;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFo