Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 175 additions & 0 deletions src/main/java/net/asodev/islandutils/mixins/ItemBarMixin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package net.asodev.islandutils.mixins;

import net.asodev.islandutils.modules.cosmetics.CosmeticChroma;
import net.asodev.islandutils.options.IslandOptions;
import net.asodev.islandutils.util.BarInfo;
import net.asodev.islandutils.util.Utils;
import net.minecraft.network.chat.Component;
import net.minecraft.util.ARGB;
import net.minecraft.world.item.ItemStack;
import org.apache.commons.lang3.math.Fraction;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import java.util.ArrayList;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Mixin(ItemStack.class)
public class ItemBarMixin {
@Unique
private static final Pattern PROGRESS_REGEX = Pattern.compile(".*\\n[\\uE001\\uE269\\uE26C\\uE266]* (\\d+)%.*");
@Unique
private static final String PROGRESS_COSMETIC_LABEL = "Left-Click to Equip\n";
@Unique
private static final String PROGRESS_COSMETIC_LABEL_2 = "Left-Click to Unequip\n";
@Unique
private static final String PROGRESS_BADGE_PINNED_LABEL = "Shift-Click to Unpin";
@Unique
private static final String PROGRESS_BADGE_LOCKED_LABEL = "\nUnlock this slot by reaching the below\namount of Skill Trophies.\n";
@Unique
private static final String PROGRESS_QUEST_LABEL = "\nComplete any of the below tasks to\nfinish the quest.\n";
@Unique
private static final String PROGRESS_TOOL_USES_LABEL = "\nUses Remaining: ";
@Unique
private static final String PROGRESS_TOOL_REPAIRABLE_LABEL = "\nThis item is out of uses! You can Repair\nit to restore all its uses, you can only\ndo this once per item.\n";
@Unique
private static final String PROGRESS_TOOL_BROKEN_LABEL = "\nThis item is out of uses! You've already\nrepaired it once, so cannot do so\nagain.";

@Unique
private static final Pattern REPUTATION_REGEX = Pattern.compile(".*\\nRoyal Donations: (\\d+)/(\\d+)\\n.*");

@Unique
private static final String CHROMA_LABEL = "\nChromas Unlocked:\n";
@Unique
private static final Pattern CHROMA_REGEX = Pattern.compile(".*\\n([\\uE02A\\uE02E\\uE02F\\uE02C\\uE02D\\uE02B]{5}) - (\\d+)\\uE328\\n.*");

@Unique
private static final int PROGRESS_BAR_COLOR = ARGB.colorFromFloat(1.0F, 0.33F, 1.0F, 0.33F);
@Unique
private static final int REPAIRABLE_BAR_COLOR = ARGB.colorFromFloat(1.0F, 1.0F, 0.33F, 0.33F);

@Unique
private static final int REPUTATION_BAR_COLOR = ARGB.colorFromFloat(1.0F, 0.66F, 0.33F, 1.0F);

@Unique
private static final int CHROMA_BAR_COLOR_SPEED_MS = 2000;

@Unique
private Optional<BarInfo> getProgressBar(String lore) {
if (!IslandOptions.getMisc().isShowProgressBar()) return Optional.empty();

var isCosmetic = lore.contains(PROGRESS_COSMETIC_LABEL) || lore.contains(PROGRESS_COSMETIC_LABEL_2);
var isProfileBadge = lore.contains(PROGRESS_BADGE_PINNED_LABEL) || lore.contains(PROGRESS_BADGE_LOCKED_LABEL);
if (isCosmetic || isProfileBadge) return Optional.empty();

var isBrokenTool = lore.contains(PROGRESS_TOOL_BROKEN_LABEL);
if (isBrokenTool) return Optional.of(new BarInfo(Fraction.ZERO, PROGRESS_BAR_COLOR));

var isRepairableTool = lore.contains(PROGRESS_TOOL_REPAIRABLE_LABEL);
if (isRepairableTool) return Optional.of(new BarInfo(Fraction.getFraction(1, 100), REPAIRABLE_BAR_COLOR));

var matcher = PROGRESS_REGEX.matcher(lore);
var progresses = new ArrayList<Fraction>();
while (matcher.find()) {
var percentage = Integer.parseInt(matcher.group(1));
var fraction = Fraction.getFraction(percentage, 100);
progresses.add(fraction);
}
if (progresses.isEmpty()) return Optional.empty();

var isQuest = lore.contains(PROGRESS_QUEST_LABEL);
var fraction = isQuest ? progresses.stream().max(Fraction::compareTo) : progresses.stream().findFirst();

var isNonBrokenTool = lore.contains(PROGRESS_TOOL_USES_LABEL);

return fraction.map(f -> new BarInfo(f, PROGRESS_BAR_COLOR));
}

@Unique
private Optional<BarInfo> getReputationBar(String lore) {
if (!IslandOptions.getCosmetics().isShowReputationBar()) return Optional.empty();

var matcher = REPUTATION_REGEX.matcher(lore);
if (!matcher.find()) return Optional.empty();

var level = Integer.parseInt(matcher.group(1));
var max = Integer.parseInt(matcher.group(2));

try {
var fraction = Fraction.getFraction(level, max);
return Optional.of(new BarInfo(fraction, REPUTATION_BAR_COLOR));
} catch (ArithmeticException e) {
return Optional.empty();
}
}

@Unique
private Optional<BarInfo> getChromaBar(String lore) {
if (!IslandOptions.getCosmetics().isShowChromaBar()) return Optional.empty();

if (!lore.contains(CHROMA_LABEL)) return Optional.empty();

var matcher = CHROMA_REGEX.matcher(lore);
if (!matcher.find()) return Optional.empty();
var chromaChars = Optional.of(matcher.group(1).chars().mapToObj(c -> (char) c).toList());

return chromaChars.map(charList ->
charList.stream()
.map(CosmeticChroma::fromChar)
.filter(Optional::isPresent)
.map(Optional::get)
.toList()
).map(chromas -> {
var fraction = CosmeticChroma.toFraction(chromas);

var color = 0;
if (!chromas.isEmpty()) {
var currentChromaIdx = (int) ((System.currentTimeMillis() / CHROMA_BAR_COLOR_SPEED_MS) % chromas.size());
var nextChromaIdx = (currentChromaIdx + 1) % chromas.size();

var currentChromaColor = chromas.get(currentChromaIdx).toColor();
var nextChromaColor = chromas.get(nextChromaIdx).toColor();

var interpolatedProgress = (float) (System.currentTimeMillis() % CHROMA_BAR_COLOR_SPEED_MS) / CHROMA_BAR_COLOR_SPEED_MS;
color = ARGB.lerp(interpolatedProgress, currentChromaColor, nextChromaColor);
}

return new BarInfo(fraction, color);
});
}

@Unique
private Optional<BarInfo> getItemBar() {
var itemStack = (ItemStack)(Object)this;
var lore = Utils.getLores(itemStack);
if (lore == null) return Optional.empty();

var loreStr = lore.stream().map(Component::getString).collect(Collectors.joining("\n"));

return this.getChromaBar(loreStr)
.or(() -> this.getReputationBar(loreStr))
.or(() -> this.getProgressBar(loreStr));
}

@Inject(method = "isBarVisible", at = @At("RETURN"), cancellable = true)
private void injectBarVisibility(CallbackInfoReturnable<Boolean> cir) {
if (this.getItemBar().isPresent()) {
cir.setReturnValue(true);
}
}

@Inject(method = "getBarWidth", at = @At("RETURN"), cancellable = true)
private void injectBarWidth(CallbackInfoReturnable<Integer> cir) {
this.getItemBar().ifPresent(barInfo -> cir.setReturnValue(barInfo.getBarWidth()));
}

@Inject(method = "getBarColor", at = @At("RETURN"), cancellable = true)
private void injectBarColor(CallbackInfoReturnable<Integer> cir) {
this.getItemBar().ifPresent(barInfo -> cir.setReturnValue(barInfo.color()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package net.asodev.islandutils.modules.cosmetics;

import net.minecraft.util.ARGB;
import org.apache.commons.lang3.math.Fraction;

import java.util.List;
import java.util.Optional;

public enum CosmeticChroma {
MECHANICAL,
NATURAL,
OCEANIC,
MAGICAL,
GRAYSCALE;

public static final int COUNT = 5;

public static Optional<CosmeticChroma> fromChar(char c) {
return Optional.ofNullable(switch (c) {
case '\uE02E' -> MECHANICAL;
case '\uE02F' -> NATURAL;
case '\uE02C' -> OCEANIC;
case '\uE02D' -> MAGICAL;
case '\uE02B' -> GRAYSCALE;
default -> null;
});
}

public int toColor() {
return switch (this) {
case MECHANICAL -> ARGB.colorFromFloat(1.0f, 1.0f, 0.33f, 0.0f);
case NATURAL -> ARGB.colorFromFloat(1.0f, 0.0f, 0.66f, 0.33f);
case OCEANIC -> ARGB.colorFromFloat(1.0f, 0.0f, 0.5f, 1.0f);
case MAGICAL -> ARGB.colorFromFloat(1.0f, 0.5f, 0.0f, 1.0f);
case GRAYSCALE -> ARGB.colorFromFloat(1.0f, 0.5f, 0.5f, 0.5f);
};
}

public static Fraction toFraction(List<CosmeticChroma> chromas) {
return Fraction.getFraction(chromas.size(), COUNT);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public class CosmeticsOptions implements OptionsCategory {
boolean showPlayerPreview = true;
boolean showOnHover = true;
boolean showOnOnlyCosmeticMenus = true;
boolean showReputationBar = true;
boolean showChromaBar = true;

public boolean isShowPlayerPreview() {
return showPlayerPreview;
Expand All @@ -26,6 +28,14 @@ public boolean isShowOnOnlyCosmeticMenus() {
return showOnOnlyCosmeticMenus;
}

public boolean isShowReputationBar() {
return showReputationBar;
}

public boolean isShowChromaBar() {
return showChromaBar;
}

@Override
public ConfigCategory getCategory() {
Option<Boolean> showPreviewOption = Option.<Boolean>createBuilder()
Expand All @@ -38,16 +48,28 @@ public ConfigCategory getCategory() {
.controller(TickBoxControllerBuilder::create)
.binding(defaults.showOnHover, () -> showOnHover, value -> this.showOnHover = value)
.build();
Option<Boolean> showInOnlyCosmeticMenu = Option.<Boolean>createBuilder()
Option<Boolean> showOnlyCosmeticMenusOption = Option.<Boolean>createBuilder()
.name(Component.translatable("text.autoconfig.islandutils.option.showOnOnlyCosmeticMenus"))
.controller(TickBoxControllerBuilder::create)
.binding(defaults.showOnOnlyCosmeticMenus, () -> showOnOnlyCosmeticMenus, value -> this.showOnOnlyCosmeticMenus = value)
.build();
Option<Boolean> showReputationBarOption = Option.<Boolean>createBuilder()
.name(Component.translatable("text.autoconfig.islandutils.option.showReputationBar"))
.controller(TickBoxControllerBuilder::create)
.binding(defaults.showReputationBar, () -> showReputationBar, value -> this.showReputationBar = value)
.build();
Option<Boolean> showChromaBarOption = Option.<Boolean>createBuilder()
.name(Component.translatable("text.autoconfig.islandutils.option.showChromaBar"))
.controller(TickBoxControllerBuilder::create)
.binding(defaults.showChromaBar, () -> showChromaBar, value -> this.showChromaBar = value)
.build();
return ConfigCategory.createBuilder()
.name(Component.literal("Cosmetics"))
.option(showPreviewOption)
.option(showHoverOption)
.option(showInOnlyCosmeticMenu)
.option(showOnlyCosmeticMenusOption)
.option(showReputationBarOption)
.option(showChromaBarOption)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class MiscOptions implements OptionsCategory {
boolean silverPreview = true;
boolean channelSwitchers = true;
boolean showFishingUpgradeIcon = true;
boolean showProgressBar = true;
boolean enableConfigButton = true;
boolean debugMode = false;

Expand All @@ -44,6 +45,9 @@ public boolean showChannelSwitchers() {
public boolean isShowFishingUpgradeIcon() {
return showFishingUpgradeIcon;
}
public boolean isShowProgressBar() {
return showProgressBar;
}

public boolean isDebugMode() {
return debugMode && !Utils.isLunarClient();
Expand Down Expand Up @@ -83,7 +87,6 @@ public ConfigCategory getCategory() {
.build();
Option<Boolean> fishingUpgradeOption = Option.<Boolean>createBuilder()
.name(Component.translatable("text.autoconfig.islandutils.option.showFishingUpgradeIcon"))
.description(OptionDescription.of(Component.translatable("text.autoconfig.islandutils.option.showFishingUpgradeIcon.@Tooltip")))
.controller(TickBoxControllerBuilder::create)
.binding(defaults.showFishingUpgradeIcon, () -> showFishingUpgradeIcon, value -> this.showFishingUpgradeIcon = value)
.build();
Expand All @@ -93,6 +96,12 @@ public ConfigCategory getCategory() {
.controller(TickBoxControllerBuilder::create)
.binding(defaults.enableConfigButton, () -> enableConfigButton, value -> this.enableConfigButton = value)
.build();
Option<Boolean> progressBarOption = Option.<Boolean>createBuilder()
.name(Component.translatable("text.autoconfig.islandutils.option.showProgressBar"))
.description(OptionDescription.of(Component.translatable("text.autoconfig.islandutils.option.showProgressBar.@Tooltip")))
.controller(TickBoxControllerBuilder::create)
.binding(defaults.showProgressBar, () -> showProgressBar, value -> this.showProgressBar = value)
.build();
Option<Boolean> debugOption = Option.<Boolean>createBuilder()
.name(Component.translatable("text.autoconfig.islandutils.option.debugMode"))
.description(OptionDescription.of(Component.translatable("text.autoconfig.islandutils.option.debugMode.@Tooltip")))
Expand All @@ -114,6 +123,7 @@ public ConfigCategory getCategory() {
.option(channelsOption)
.option(fishingUpgradeOption)
.option(buttonOption)
.option(progressBarOption)
.group(OptionGroup.createBuilder()
.name(Component.literal("Debug Options"))
.collapsed(true)
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/net/asodev/islandutils/util/BarInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package net.asodev.islandutils.util;

import net.minecraft.util.Mth;
import org.apache.commons.lang3.math.Fraction;

public record BarInfo(Fraction fraction, int color) {
public int getBarWidth() {
if (fraction.compareTo(Fraction.ZERO) > 0) {
return Math.min(1 + Mth.mulAndTruncate(fraction, 12), 13);
} else {
return Math.min(Mth.mulAndTruncate(fraction, 13), 13);
}
}
}
4 changes: 4 additions & 0 deletions src/main/resources/assets/island/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
"text.autoconfig.islandutils.option.showPlayerPreview": "Show Cosmetic Previews",
"text.autoconfig.islandutils.option.showOnHover": "Preview cosmetics on hover",
"text.autoconfig.islandutils.option.showOnOnlyCosmeticMenus": "Show cosmetic preview only in Cosmetic Menus",
"text.autoconfig.islandutils.option.showReputationBar": "Show reputation bar",
"text.autoconfig.islandutils.option.showChromaBar": "Show chroma bar",

"text.autoconfig.islandutils.category.discord": "Discord Presence",
"text.autoconfig.islandutils.option.discordPresence": "Enable Discord Presence",
Expand Down Expand Up @@ -74,6 +76,8 @@
"text.autoconfig.islandutils.option.silverPreview.@Tooltip": "Shows all the items you'll earn in the Scavenging menu",
"text.autoconfig.islandutils.option.channelSwitchers": "Show chat channel buttons",
"text.autoconfig.islandutils.option.channelSwitchers.@Tooltip": "Shows the chat channel switcher buttons above the chat box",
"text.autoconfig.islandutils.option.showProgressBar": "Show progress bars",
"text.autoconfig.islandutils.option.showProgressBar.@Tooltip": "Adds an progress bar to inventories for menu items with progress in their tooltip, such as quests or fishing islands",
"text.autoconfig.islandutils.option.hideSliders": "Hide Sound Sliders if not Online",
"text.autoconfig.islandutils.option.hideSliders.@Tooltip": "Hides the Music Sliders if you're not on MCC Island",
"text.autoconfig.islandutils.option.showFishingUpgradeIcon": "Show fishing upgrade icons",
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/islandutils.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"cosmetics.ChestScreenMixin",
"cosmetics.FontLoaderMixin",
"cosmetics.PreviewTutorialMixin",
"ItemBarMixin",
"discord.ConnectionMixin",
"discord.ScoreDisplayMixin",
"resources.PackRepositoryMixin",
Expand Down
Loading