aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2025-07-31 17:36:29 +0200
committerLinnea Gräf <nea@nea.moe>2025-07-31 18:09:41 +0200
commit31511ecd012c5dbe8cc2c8a7245851392cdf6f58 (patch)
treef25c434e5ed7e50bef259554bc4506b5f632ecd9 /src
parent93dfb15f25dcbe857e99d1a1fcbbc2205f06eef6 (diff)
downloadSkyblocker-31511ecd012c5dbe8cc2c8a7245851392cdf6f58.tar.gz
Skyblocker-31511ecd012c5dbe8cc2c8a7245851392cdf6f58.tar.bz2
Skyblocker-31511ecd012c5dbe8cc2c8a7245851392cdf6f58.zip
feat: Skills page
Diffstat (limited to 'src')
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ApiProfile.java1
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/PlayerData.java53
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ProfileMember.java2
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/ErrorPage.java4
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/LoadingPage.java36
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/ProfileViewerPage.java2
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/ProfileViewerScreenRework.java223
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/pages/MainPage.java40
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/pages/SkillsPage.java122
9 files changed, 309 insertions, 174 deletions
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ApiProfile.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ApiProfile.java
index 1d0be925..e0cb96c7 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ApiProfile.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ApiProfile.java
@@ -14,7 +14,6 @@ public class ApiProfile {
@SerializedName("cute_name")
public String cuteName;
public Events events = new Events();
- public PlayerData playerData = new PlayerData();
public Bestiary bestiary = new Bestiary();
// TODO: mining_core (which is broken right now)
public boolean selected = false;
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/PlayerData.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/PlayerData.java
index f0c67e3c..188354c8 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/PlayerData.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/PlayerData.java
@@ -1,6 +1,9 @@
package de.hysky.skyblocker.skyblock.profileviewer.model;
import com.google.gson.annotations.SerializedName;
+import de.hysky.skyblocker.skyblock.profileviewer.utils.LevelFinder;
+import de.hysky.skyblocker.skyblock.tabhud.util.Ico;
+import net.minecraft.item.ItemStack;
import java.util.List;
import java.util.Map;
@@ -8,7 +11,7 @@ import java.util.Set;
public class PlayerData {
@SerializedName("visited_zones")
- public List<String> visitedZones = List.of();
+ public Set<String> visitedZones = Set.of();
/**
* Seems to be a second timestamp with some sort of offset.
*/
@@ -65,6 +68,7 @@ public class PlayerData {
public double highestCriticalDamage;
@SerializedName("items_fished")
public ItemsFished itemsFished = new ItemsFished();
+ public Map<String, Double> experience = Map.of();
public static class ItemsFished {
public int total;
@@ -82,6 +86,53 @@ public class PlayerData {
return craftedMinions.contains(String.format("%s_%d", minionType, tier));
}
+ public double getSkillExperience(Skill skill) {
+ return experience.getOrDefault("SKILL_" + skill.name(), 0.0);
+ }
+
+ public LevelFinder.LevelInfo getSkillLevel(Skill skill) {
+ return LevelFinder.getLevelInfo(skill.levelFinderOverride, (long) getSkillExperience(skill));
+ }
+
+ public enum Skill {
+ FISHING("Fishing", Ico.FISH_ROD),
+ ALCHEMY("Alchemy", Ico.BREWING_STAND),
+ // DUNGEONEERING,
+ RUNECRAFTING("Runecrafting", Ico.MAGMA_CREAM, "Runecraft"),
+ MINING("Mining", Ico.STONE_PICKAXE),
+ FARMING("Farming", Ico.GOLDEN_HOE),
+ ENCHANTING("Enchanting", Ico.ENCHANTING_TABLE),
+ TAMING("Taming", Ico.BONE),
+ FORAGING("Foraging", Ico.JUNGLE_SAPLING),
+ SOCIAL("Social", Ico.EMERALD, "Social"),
+ CARPENTRY("Carpentry", Ico.CRAFTING_TABLE),
+ COMBAT("Combat", Ico.STONE_SWORD),
+ ;
+ private final String name;
+ private final ItemStack itemStack;
+ private final String levelFinderOverride;
+
+
+ Skill(String name, ItemStack itemStack) {
+ this(name, itemStack, "GenericSkill");
+ }
+
+ Skill(String name, ItemStack itemStack, String override) {
+ this.name = name;
+ this.itemStack = itemStack;
+ this.levelFinderOverride = override;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public ItemStack getIcon() {
+ return itemStack;
+ }
+ }
+
+
/**
* Gets the highest contiguously unlocked minion tier. It is still possible to craft a higher tier minion by trading with other players.
*
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ProfileMember.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ProfileMember.java
index d2a163c7..208e0a52 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ProfileMember.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ProfileMember.java
@@ -30,6 +30,8 @@ public class ProfileMember {
public ItemData itemData = new ItemData();
@SerializedName("winter_player_data")
public WinterPlayerData winterPlayerData = new WinterPlayerData();
+ @SerializedName("player_data")
+ public PlayerData playerData = new PlayerData();
public Leveling leveling = new Leveling();
public AccessoryBagStorage accessoryBagStorage = new AccessoryBagStorage();
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/ErrorPage.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/ErrorPage.java
index 934a4a4e..eca3a0d9 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/ErrorPage.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/ErrorPage.java
@@ -27,8 +27,8 @@ public record ErrorPage(ProfileLoadState.Error error) implements ProfileViewerPa
@Override
public @NotNull List<ProfileViewerWidget.Instance> getWidgets() {
return List.of(
- ProfileViewerWidget.widget(ProfileViewerScreenRework.GUI_WIDTH / 2, 8, TextWidget.centered(Text.of("Error!"))),
- ProfileViewerWidget.widget(ProfileViewerScreenRework.GUI_WIDTH / 2, 19, TextWidget.centered(Text.of(error.message())))
+ ProfileViewerWidget.widget(ProfileViewerScreenRework.PAGE_WIDTH / 2, 8, TextWidget.centered(Text.of("Error!"))),
+ ProfileViewerWidget.widget(ProfileViewerScreenRework.PAGE_WIDTH / 2, 19, TextWidget.centered(Text.of(error.message())))
);
}
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/LoadingPage.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/LoadingPage.java
index d2b523b4..4d326277 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/LoadingPage.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/LoadingPage.java
@@ -7,25 +7,25 @@ import net.minecraft.text.Text;
import java.util.List;
public class LoadingPage implements ProfileViewerPage {
- @Override
- public int getSortIndex() {
- return 0;
- }
+ @Override
+ public int getSortIndex() {
+ return 0;
+ }
- @Override
- public ItemStack getIcon() {
- return Ico.CLOCK;
- }
+ @Override
+ public ItemStack getIcon() {
+ return Ico.CLOCK;
+ }
- @Override
- public String getName() {
- return "Loading...";
- }
+ @Override
+ public String getName() {
+ return "Loading...";
+ }
- @Override
- public List<ProfileViewerWidget.Instance> getWidgets() {
- return List.of(
- ProfileViewerWidget.widget(ProfileViewerScreenRework.GUI_WIDTH / 2, 8, TextWidget.centered(Text.of("Loading profile...")))
- );
- }
+ @Override
+ public List<ProfileViewerWidget.Instance> getWidgets() {
+ return List.of(
+ ProfileViewerWidget.widget(ProfileViewerScreenRework.PAGE_WIDTH / 2, 8, TextWidget.centered(Text.of("Loading profile...")))
+ );
+ }
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/ProfileViewerPage.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/ProfileViewerPage.java
index 184f6660..c68201a8 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/ProfileViewerPage.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/ProfileViewerPage.java
@@ -15,7 +15,7 @@ public interface ProfileViewerPage extends Comparable<ProfileViewerPage> {
return Integer.compare(this.getSortIndex(), o.getSortIndex());
}
- static ProfileViewerWidget.Instance widget(int x, int y, ProfileViewerWidget widget) {
+ default ProfileViewerWidget.Instance widget(int x, int y, ProfileViewerWidget widget) {
return ProfileViewerWidget.widget(x, y, widget);
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/ProfileViewerScreenRework.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/ProfileViewerScreenRework.java
index 4158fcf4..c66b5eeb 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/ProfileViewerScreenRework.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/ProfileViewerScreenRework.java
@@ -25,115 +25,116 @@ import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
public class ProfileViewerScreenRework extends Screen {
- public static final Gson GSON = new GsonBuilder()
- .registerTypeAdapter(UUID.class, new UUIDTypeAdapter())
- .create();
- public static final List<Function<ProfileLoadState.SuccessfulLoad, ProfileViewerPage>> PAGE_CONSTRUCTORS =
- new ArrayList<>();
-
- public ProfileViewerScreenRework() {
- super(Text.of("SkyBlocker Profile Viewer"));
- displayLoadedProfile(new ProfileLoadState.Loading());
- }
-
- public static Screen forPlayer(String username) {
- var screen = new ProfileViewerScreenRework();
- screen.loadProfilesFromPlayer(username);
- return screen;
- }
-
- //<editor-fold desc="Loading and state management">
- private CompletableFuture<ProfileLoadState> reload;
- private ProfileLoadState currentLoadState;
- private List<ProfileViewerPage> pages;
- private List<ProfileViewerNavButton> buttons;
- private List<ProfileViewerWidget.Instance> widgets;
- private int selectedIndex = 0;
-
-
- public ProfileLoadState getCurrentLoadState() {
- return currentLoadState;
- }
-
- public void displayLoadedProfile(ProfileLoadState profileLoadState) {
- this.currentLoadState = profileLoadState;
- this.pages = switch (profileLoadState) {
- case ProfileLoadState.Error error -> List.of(new ErrorPage(error));
- case ProfileLoadState.SuccessfulLoad successfulLoad ->
- PAGE_CONSTRUCTORS.stream().sorted().map(it -> it.apply(successfulLoad)).toList();
- case ProfileLoadState.Loading ignored -> List.of(new LoadingPage());
- };
- this.buttons = new ArrayList<>();
- for (int i = 0; i < pages.size(); i++) {
- var page = pages.get(i);
- buttons.add(new ProfileViewerNavButton(ignored -> setSelectedPage(selectedIndex), page.getName(), page.getIcon(), i, false));
- }
- setSelectedPage(0);
- }
-
- public int getSelectedIndex() {
- return selectedIndex;
- }
-
- public ProfileViewerPage getSelectedPage() {
- return pages.get(selectedIndex);
- }
-
- public void setSelectedPage(int index) {
- this.selectedIndex = index;
- for (int i = 0; i < buttons.size(); i++) {
- buttons.get(i).setToggled(i == selectedIndex);
- }
- widgets = pages.get(selectedIndex).getWidgets();
- }
-
- public CompletableFuture<ProfileLoadState> loadProfilesFromPlayer(String name) {
- if (reload != null) {
- reload.cancel(true);
- }
- this.displayLoadedProfile(new ProfileLoadState.Loading());
- return reload = ProfileUtils.fetchFullProfile(name)
- .thenApplyAsync(jsonObject -> GSON.fromJson(jsonObject, ApiProfileResponse.class))
- .thenApplyAsync(apiProfileResponse -> apiProfileResponse
- .profiles
- .stream()
- .max(Comparator.comparing(it -> it.selected))
- .<ProfileLoadState>map(selectedProfile -> {
- var uuid = UndashedUuid.fromStringLenient(ApiUtils.name2Uuid(name));
- return new ProfileLoadState.SuccessfulLoad(
- selectedProfile,
- uuid,
- selectedProfile.members.get(uuid)
- );
- })
- .orElseGet(() -> new ProfileLoadState.Error("No profile found")))
- .exceptionally(ex -> new ProfileLoadState.Error(ex.getMessage()))
- .thenApplyAsync(load -> {
- displayLoadedProfile(load);
- return load;
- }, MinecraftClient.getInstance());
- }
- //</editor-fold>
-
- private static final Identifier TEXTURE = Identifier.of(SkyblockerMod.NAMESPACE, "textures/gui/profile_viewer/base_plate.png");
- public static final int GUI_WIDTH = 322;
- public static final int GUI_HEIGHT = 180;
-
- @Override
- public void render(DrawContext context, int mouseX, int mouseY, float deltaTicks) {
- super.render(context, mouseX, mouseY, deltaTicks);
- int rootX = width / 2 - GUI_WIDTH / 2;
- int rootY = height / 2 - GUI_HEIGHT / 2 + 5;
-
- context.drawTexture(RenderPipelines.GUI_TEXTURED, TEXTURE, rootX, rootY, 0, 0, GUI_WIDTH, GUI_HEIGHT, GUI_WIDTH, GUI_HEIGHT);
- for (var button : buttons) {
- button.setX(rootX + button.getIndex() * 28 + 4);
- button.setY(rootY - 28);
- button.render(context, mouseX, mouseY, deltaTicks);
- }
-
- for (var widget : widgets) {
- widget.render(context, rootX, rootY, mouseX, mouseY, deltaTicks);
- }
- }
+ public static final Gson GSON = new GsonBuilder()
+ .registerTypeAdapter(UUID.class, new UUIDTypeAdapter())
+ .create();
+ public static final List<Function<ProfileLoadState.SuccessfulLoad, ProfileViewerPage>> PAGE_CONSTRUCTORS =
+ new ArrayList<>();
+
+ public ProfileViewerScreenRework() {
+ super(Text.of("SkyBlocker Profile Viewer"));
+ displayLoadedProfile(new ProfileLoadState.Loading());
+ }
+
+ public static Screen forPlayer(String username) {
+ var screen = new ProfileViewerScreenRework();
+ screen.loadProfilesFromPlayer(username);
+ return screen;
+ }
+
+ //<editor-fold desc="Loading and state management">
+ private CompletableFuture<ProfileLoadState> reload;
+ private ProfileLoadState currentLoadState;
+ private List<ProfileViewerPage> pages;
+ private List<ProfileViewerNavButton> buttons;
+ private List<ProfileViewerWidget.Instance> widgets;
+ private int selectedIndex = 0;
+
+
+ public ProfileLoadState getCurrentLoadState() {
+ return currentLoadState;
+ }
+
+ public void displayLoadedProfile(ProfileLoadState profileLoadState) {
+ this.currentLoadState = profileLoadState;
+ this.pages = switch (profileLoadState) {
+ case ProfileLoadState.Error error -> List.of(new ErrorPage(error));
+ case ProfileLoadState.SuccessfulLoad successfulLoad -> PAGE_CONSTRUCTORS.stream().sorted().map(it -> it.apply(successfulLoad)).toList();
+ case ProfileLoadState.Loading ignored -> List.of(new LoadingPage());
+ };
+ this.buttons = new ArrayList<>();
+ for (int i = 0; i < pages.size(); i++) {
+ var page = pages.get(i);
+ buttons.add(new ProfileViewerNavButton(ignored -> setSelectedPage(selectedIndex), page.getName(), page.getIcon(), i, false));
+ }
+ setSelectedPage(0);
+ }
+
+ public int getSelectedIndex() {
+ return selectedIndex;
+ }
+
+ public ProfileViewerPage getSelectedPage() {
+ return pages.get(selectedIndex);
+ }
+
+ public void setSelectedPage(int index) {
+ this.selectedIndex = index;
+ for (int i = 0; i < buttons.size(); i++) {
+ buttons.get(i).setToggled(i == selectedIndex);
+ }
+ widgets = pages.get(selectedIndex).getWidgets();
+ }
+
+ public CompletableFuture<ProfileLoadState> loadProfilesFromPlayer(String name) {
+ if (reload != null) {
+ reload.cancel(true);
+ }
+ this.displayLoadedProfile(new ProfileLoadState.Loading());
+ return reload = ProfileUtils.fetchFullProfile(name)
+ .thenApplyAsync(jsonObject -> GSON.fromJson(jsonObject, ApiProfileResponse.class))
+ .thenApplyAsync(apiProfileResponse -> apiProfileResponse
+ .profiles
+ .stream()
+ .max(Comparator.comparing(it -> it.selected))
+ .<ProfileLoadState>map(selectedProfile -> {
+ var uuid = UndashedUuid.fromStringLenient(ApiUtils.name2Uuid(name));
+ return new ProfileLoadState.SuccessfulLoad(
+ selectedProfile,
+ uuid,
+ selectedProfile.members.get(uuid)
+ );
+ })
+ .orElseGet(() -> new ProfileLoadState.Error("No profile found")))
+ .exceptionally(ex -> new ProfileLoadState.Error(ex.getMessage()))
+ .thenApplyAsync(load -> {
+ displayLoadedProfile(load);
+ return load;
+ }, MinecraftClient.getInstance());
+ }
+ //</editor-fold>
+
+ private static final Identifier TEXTURE = Identifier.of(SkyblockerMod.NAMESPACE, "textures/gui/profile_viewer/base_plate.png");
+ private static final int GUI_WIDTH = 322;
+ public static final int PAGE_WIDTH = GUI_WIDTH - 10;
+ private static final int GUI_HEIGHT = 180;
+ public static final int PAGE_HEIGHT = GUI_HEIGHT - 10;
+
+ @Override
+ public void render(DrawContext context, int mouseX, int mouseY, float deltaTicks) {
+ super.render(context, mouseX, mouseY, deltaTicks);
+ int rootX = width / 2 - GUI_WIDTH / 2;
+ int rootY = height / 2 - GUI_HEIGHT / 2 + 5;
+
+ context.drawTexture(RenderPipelines.GUI_TEXTURED, TEXTURE, rootX, rootY, 0, 0, GUI_WIDTH, GUI_HEIGHT, GUI_WIDTH, GUI_HEIGHT);
+ for (var button : buttons) {
+ button.setX(rootX + button.getIndex() * 28 + 4);
+ button.setY(rootY - 28);
+ button.render(context, mouseX, mouseY, deltaTicks);
+ }
+
+ for (var widget : widgets) {
+ widget.render(context, rootX + 5, rootY + 5, mouseX, mouseY, deltaTicks);
+ }
+ }
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/pages/MainPage.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/pages/MainPage.java
deleted file mode 100644
index 5387373c..00000000
--- a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/pages/MainPage.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package de.hysky.skyblocker.skyblock.profileviewer.rework.pages;
-
-import de.hysky.skyblocker.annotations.Init;
-import de.hysky.skyblocker.skyblock.profileviewer.rework.*;
-import de.hysky.skyblocker.skyblock.tabhud.util.Ico;
-import net.minecraft.item.ItemStack;
-import net.minecraft.text.Text;
-
-import java.util.List;
-
-public class MainPage implements ProfileViewerPage {
- public MainPage(ProfileLoadState.SuccessfulLoad load) {}
-
- @Init
- public static void init() {
- ProfileViewerScreenRework.PAGE_CONSTRUCTORS.add(MainPage::new);
- }
-
- @Override
- public int getSortIndex() {
- return 0;
- }
-
- @Override
- public ItemStack getIcon() {
- return Ico.IRON_SWORD;
- }
-
- @Override
- public String getName() {
- return "Skills";
- }
-
- @Override
- public List<ProfileViewerWidget.Instance> getWidgets() {
- return List.of(
- ProfileViewerWidget.widget(ProfileViewerScreenRework.GUI_WIDTH / 2, 8, TextWidget.centered(Text.of("Skills")))
- );
- }
-}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/pages/SkillsPage.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/pages/SkillsPage.java
new file mode 100644
index 00000000..bb57cd1b
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/rework/pages/SkillsPage.java
@@ -0,0 +1,122 @@
+package de.hysky.skyblocker.skyblock.profileviewer.rework.pages;
+
+import de.hysky.skyblocker.SkyblockerMod;
+import de.hysky.skyblocker.annotations.Init;
+import de.hysky.skyblocker.skyblock.profileviewer.model.PlayerData;
+import de.hysky.skyblocker.skyblock.profileviewer.rework.*;
+import de.hysky.skyblocker.skyblock.profileviewer.utils.LevelFinder;
+import de.hysky.skyblocker.skyblock.tabhud.util.Ico;
+import de.hysky.skyblocker.utils.Formatters;
+import de.hysky.skyblocker.utils.NEURepoManager;
+import de.hysky.skyblocker.utils.render.HudHelper;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.font.TextRenderer;
+import net.minecraft.client.gl.RenderPipelines;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.item.ItemStack;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import net.minecraft.util.Identifier;
+
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.OptionalInt;
+
+public class SkillsPage implements ProfileViewerPage {
+ private static final Identifier ICON_DATA_TEXTURE = Identifier.of(SkyblockerMod.NAMESPACE, "textures/gui/profile_viewer/icon_data_widget.png");
+ private static final Identifier BAR_FILL = Identifier.of(SkyblockerMod.NAMESPACE, "bars/bar_fill");
+ private static final Identifier BAR_BACK = Identifier.of(SkyblockerMod.NAMESPACE, "bars/bar_back");
+
+ record SkillWidget(
+ PlayerData.Skill skill,
+ LevelFinder.LevelInfo levelInfo,
+ OptionalInt softSkillCap
+ ) implements ProfileViewerWidget {
+ static TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
+
+ @Override
+ public void render(DrawContext drawContext,
+ int x, int y, int mouseX, int mouseY, float deltaTicks) {
+ drawContext.drawTexture(RenderPipelines.GUI_TEXTURED, ICON_DATA_TEXTURE, x, y, 0, 0, 109, 26, 109, 26);
+ drawContext.drawItem(skill.getIcon(), x + 3, y + 4);
+ drawContext.drawText(textRenderer, skill.getName() + " " + levelInfo.level, x + 31, y + 4, -1, false);
+ Color fillColor = Color.GREEN;
+ var skillCap = NEURepoManager.getConstants().getLeveling().getMaximumLevels().get(skill.name().toLowerCase(Locale.ROOT));
+ if (softSkillCap.isPresent() && levelInfo.level > softSkillCap.getAsInt())
+ fillColor = Color.YELLOW;
+ if (levelInfo.level >= skillCap)
+ fillColor = Color.MAGENTA;
+ drawContext.drawGuiTexture(RenderPipelines.GUI_TEXTURED, BAR_BACK, x + 30, y + 14, 75, 6);
+ HudHelper.renderNineSliceColored(drawContext, BAR_FILL, x + 30, y + 14, (int) (75 * levelInfo.fill), 6, fillColor);
+ // TODO: add helper for hover selection
+
+ if (mouseX > x + 30 && mouseX < x + 105 && mouseY > y + 14 && mouseY < y + 21) {
+ List<Text> tooltipText = new ArrayList<>();
+ tooltipText.add(Text.literal(skill.getName()).formatted(Formatting.GREEN));
+ tooltipText.add(Text.literal("XP: " + Formatters.INTEGER_NUMBERS.format(levelInfo.xp)).formatted(Formatting.GOLD));
+ if (levelInfo.level < skillCap) {
+ tooltipText.add(Text.literal("XP till " + (levelInfo.level + 1) + ": " + Formatters.INTEGER_NUMBERS.format(levelInfo.nextLevelXP - levelInfo.levelXP)).formatted(Formatting.GRAY));
+ }
+ drawContext.drawTooltip(textRenderer, tooltipText, mouseX, mouseY);
+ }
+ }
+ }
+
+ List<ProfileViewerWidget.Instance> widgets = new ArrayList<>();
+
+ public SkillsPage(ProfileLoadState.SuccessfulLoad load) {
+ var playerData = load.member().playerData;
+ List<SkillWidget> skills = new ArrayList<>();
+ skills.add(new SkillWidget(PlayerData.Skill.COMBAT, playerData.getSkillLevel(PlayerData.Skill.COMBAT), OptionalInt.empty()));
+ skills.add(new SkillWidget(PlayerData.Skill.MINING, playerData.getSkillLevel(PlayerData.Skill.MINING), OptionalInt.empty()));
+ skills.add(new SkillWidget(PlayerData.Skill.FARMING, playerData.getSkillLevel(PlayerData.Skill.FARMING), OptionalInt.of(50)));
+ skills.add(new SkillWidget(PlayerData.Skill.FORAGING, playerData.getSkillLevel(PlayerData.Skill.FORAGING), OptionalInt.of(50)));
+ skills.add(new SkillWidget(PlayerData.Skill.FISHING, playerData.getSkillLevel(PlayerData.Skill.FISHING), OptionalInt.empty()));
+ skills.add(new SkillWidget(PlayerData.Skill.ENCHANTING, playerData.getSkillLevel(PlayerData.Skill.ENCHANTING), OptionalInt.empty()));
+ skills.add(new SkillWidget(PlayerData.Skill.ALCHEMY, playerData.getSkillLevel(PlayerData.Skill.ALCHEMY), OptionalInt.empty()));
+ skills.add(new SkillWidget(PlayerData.Skill.TAMING, playerData.getSkillLevel(PlayerData.Skill.TAMING), OptionalInt.of(50)));
+ skills.add(new SkillWidget(PlayerData.Skill.CARPENTRY, playerData.getSkillLevel(PlayerData.Skill.CARPENTRY), OptionalInt.empty()));
+// skills.add(new SkillWidget(PlayerData.Skill., playerData.getSkillLevel(PlayerData.Skill.COMBAT), OptionalInt.empty()));
+ skills.add(new SkillWidget(PlayerData.Skill.RUNECRAFTING, playerData.getSkillLevel(PlayerData.Skill.RUNECRAFTING), OptionalInt.empty()));
+ skills.add(new SkillWidget(PlayerData.Skill.SOCIAL, playerData.getSkillLevel(PlayerData.Skill.SOCIAL), OptionalInt.empty())); // TODO: cross player member
+
+
+ int i = 0;
+ for (var skill : skills) {
+ int x = i < 6 ? 88 : 88 + 113;
+ int y = (i % 6) * (2 + 26);
+ i++;
+ widgets.add(widget(
+ x, y, skill
+ ));
+ }
+ }
+
+ @Init
+ public static void init() {
+ ProfileViewerScreenRework.PAGE_CONSTRUCTORS.add(SkillsPage::new);
+ }
+
+ @Override
+ public int getSortIndex() {
+ return 0;
+ }
+
+ @Override
+ public ItemStack getIcon() {
+ return Ico.IRON_SWORD;
+ }
+
+ @Override
+ public String getName() {
+ return "Skills";
+ }
+
+ @Override
+ public List<ProfileViewerWidget.Instance> getWidgets() {
+ // TODO: add player widget to the left only on this page.
+ return widgets;
+ }
+}