Skip to content

Commit

Permalink
Corpse profit tracker (#1152)
Browse files Browse the repository at this point in the history
* Corpse profit tracker initial commit

* A bunch of stuff

- Extract `CorpseLoot` and `Reward` inner classes into separate classes
- Use translatables where it makes sense
- Add config option to toggle corpse profit tracker (can't believe I forgot this on corpse tracker as well)
- Some minor command adjustments and fixes to both `PowderMiningTracker` and `CorpseProfitTracker`
- Refactored `CorpseProfitHistoryScreen` into `CorpseProfitScreen`. The button that switched from one to the other is now a view switch button that changes the displayed list, and functionality of both screens is combined in one.
- Add hover/click events to the profit text sent when a corpse is looted that opens the screen

* Round the chat message so it's more copy-friendly

* Fix keys' total price not being multiplied by key amount

* Fix `pricePerUnit` display of items to match keys

This way, they don't display the price per unit when the amount is 1.
  • Loading branch information
Emirlol authored Feb 27, 2025
1 parent fc47877 commit e2659c8
Show file tree
Hide file tree
Showing 14 changed files with 1,289 additions and 171 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
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;
Expand Down Expand Up @@ -122,12 +122,23 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig
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
Expand Down Expand Up @@ -287,6 +298,14 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig
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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ public static class Glacite {

@SerialEntry
public boolean autoShareCorpses = false;

@SerialEntry
public boolean enableCorpseProfitTracker = true;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -63,7 +63,7 @@ protected void init() {
public void saveFilters() {
SkyblockerConfigManager.get().mining.crystalHollows.powderTrackerFilter = filters;
SkyblockerConfigManager.save();
PowderMiningTracker.recalculateAll();
PowderMiningTracker.INSTANCE.recalculateAll();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -19,15 +18,13 @@
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;
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;
Expand All @@ -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() {
Expand All @@ -78,9 +71,9 @@ public static void init() {
.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;
})
)
Expand Down Expand Up @@ -250,50 +243,6 @@ private static void parseCords(Text text) {
}
}

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;
/**
Expand Down
71 changes: 71 additions & 0 deletions src/main/java/de/hysky/skyblocker/skyblock/dwarven/CorpseType.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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
}
}
Loading

0 comments on commit e2659c8

Please sign in to comment.