From 17fce2fd17c024824875ddf62fbda71e89a7aa5c Mon Sep 17 00:00:00 2001 From: fretuks Date: Tue, 25 Nov 2025 09:53:20 +0100 Subject: [PATCH] Implement all required compat with Ascend --- .../net/fretux/skillengine/SkillEngine.java | 3 - .../skillengine/client/SkilltreeScreen.java | 96 +++++++++++++++---- .../compat/AscendCompatHandler.java | 2 - .../network/ServerboundUnlockNodePacket.java | 2 +- .../skillengine/skilltree/SkillLogic.java | 17 +++- .../skillengine/skilltree/SkillNode.java | 58 +++++++++-- .../skilltree/SkillNodeLoader.java | 21 ++-- .../skillengine/skillnodes/arcane_spark.json | 10 -- .../skillengine/skillnodes/endurance.json | 10 -- .../skillengine/skillnodes/resilience.json | 10 -- .../data/skillengine/skillnodes/speed.json | 10 -- .../skillengine/skillnodes/strength_1.json | 10 +- .../data/skillengine/skillnodes/wanderer.json | 6 +- .../data/skillengine/skillnodes/welcome.json | 9 +- 14 files changed, 174 insertions(+), 90 deletions(-) delete mode 100644 src/main/resources/data/skillengine/skillnodes/arcane_spark.json delete mode 100644 src/main/resources/data/skillengine/skillnodes/endurance.json delete mode 100644 src/main/resources/data/skillengine/skillnodes/resilience.json delete mode 100644 src/main/resources/data/skillengine/skillnodes/speed.json diff --git a/src/main/java/net/fretux/skillengine/SkillEngine.java b/src/main/java/net/fretux/skillengine/SkillEngine.java index deba577..cac962a 100644 --- a/src/main/java/net/fretux/skillengine/SkillEngine.java +++ b/src/main/java/net/fretux/skillengine/SkillEngine.java @@ -1,12 +1,9 @@ package net.fretux.skillengine; import com.mojang.logging.LogUtils; -import net.fretux.skillengine.capability.SkillEngineCapabilities; import net.fretux.skillengine.network.PacketHandler; -import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import org.slf4j.Logger; diff --git a/src/main/java/net/fretux/skillengine/client/SkilltreeScreen.java b/src/main/java/net/fretux/skillengine/client/SkilltreeScreen.java index 5e41e12..036eb89 100644 --- a/src/main/java/net/fretux/skillengine/client/SkilltreeScreen.java +++ b/src/main/java/net/fretux/skillengine/client/SkilltreeScreen.java @@ -1,18 +1,25 @@ package net.fretux.skillengine.client; import com.mojang.math.Axis; -import net.fretux.skillengine.SkillEngine; +import net.fretux.ascend.player.PlayerStatsProvider; import net.fretux.skillengine.network.PacketHandler; import net.fretux.skillengine.network.ServerboundUnlockNodePacket; import net.fretux.skillengine.skilltree.SkillNode; import net.fretux.skillengine.skilltree.SkillNodeRegistry; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.FormattedText; import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.FormattedCharSequence; import net.minecraft.util.Mth; +import java.util.ArrayList; +import java.util.List; + public class SkilltreeScreen extends Screen { private float zoom = 1.0f; @@ -69,17 +76,14 @@ public class SkilltreeScreen extends Screen { @Override public boolean mouseDragged(double mouseX, double mouseY, int button, double dx, double dy) { - // Don't pan while overlay is open if (selectedNode != null) { return super.mouseDragged(mouseX, mouseY, button, dx, dy); } - if (dragging && button == 0) { panX += dx; panY += dy; return true; } - return super.mouseDragged(mouseX, mouseY, button, dx, dy); } @@ -97,13 +101,42 @@ public class SkilltreeScreen extends Screen { hoveredNode = findNodeAt(mouseX, mouseY); drawGraph(gfx); if (hoveredNode != null && selectedNode == null) { - gfx.renderTooltip(font, hoveredNode.getTitle(), mouseX, mouseY); + List tooltip = new ArrayList<>(); + tooltip.add(hoveredNode.getTitle()); + String desc = hoveredNode.getDescription().getString(); + List wrapped = font.getSplitter().splitLines( + desc, + 220, + hoveredNode.getDescription().getStyle() + ); + for (FormattedText ft : wrapped) { + tooltip.add(Component.literal(ft.getString()).withStyle(ChatFormatting.GRAY)); + } + tooltip.add(Component.literal("Cost: " + hoveredNode.getCost()) + .withStyle(ChatFormatting.DARK_AQUA)); + if (!hoveredNode.getPrereqAttributes().isEmpty()) { + tooltip.add(Component.literal("Requirements:") + .withStyle(ChatFormatting.GOLD)); + Minecraft.getInstance().player.getCapability(PlayerStatsProvider.PLAYER_STATS) + .ifPresent(stats -> { + hoveredNode.getPrereqAttributes().forEach((attr, required) -> { + int current = stats.getAttributeLevel(attr); + boolean ok = current >= required; + tooltip.add( + Component.literal(" - " + attr + ": " + current + "/" + required) + .withStyle(ok ? ChatFormatting.GREEN : ChatFormatting.RED) + ); + }); + }); + } + gfx.renderComponentTooltip(font, tooltip, mouseX, mouseY); } if (selectedNode != null) { renderOverlay(gfx, selectedNode); } super.render(gfx, mouseX, mouseY, partialTicks); } + private void drawGraph(GuiGraphics gfx) { for (SkillNode node : SkillNodeRegistry.all()) { int[] p1 = worldToScreen(node.getX(), node.getY()); @@ -123,24 +156,27 @@ public class SkilltreeScreen extends Screen { int[] pos = worldToScreen(node.getX(), node.getY()); int r = 12; boolean unlocked = SkilltreeClientState.isUnlocked(node.getId()); - int color = unlocked - ? 0xFF44CC44 - : 0xFF444444; + int color = unlocked ? 0xFF44CC44 : 0xFF444444; if (node == hoveredNode) color = 0xFF777777; if (node == selectedNode) color = 0xFF999933; gfx.fill(pos[0] - r, pos[1] - r, pos[0] + r, pos[1] + r, color); - gfx.drawString(font, - "", - pos[0] + r + 2, - pos[1] - r, - 0xFFFFFFFF); + ResourceLocation icon = node.getIcons(); + if (icon != null) { + int size = 16; + gfx.blit(icon, + pos[0] - size / 2, + pos[1] - size / 2, + 0, 0, + size, size, + size, size); + } } } private int[] worldToScreen(float wx, float wy) { double sx = width / 2.0 + panX + wx * zoom; double sy = height / 2.0 + panY + wy * zoom; - return new int[] { (int) sx, (int) sy }; + return new int[]{(int) sx, (int) sy}; } private SkillNode findNodeAt(double mouseX, double mouseY) { @@ -157,16 +193,42 @@ public class SkilltreeScreen extends Screen { private void renderOverlay(GuiGraphics gfx, SkillNode node) { int w = 240; - int h = 160; + int h = 180; int x = (width - w) / 2; int y = (height - h) / 2; gfx.fill(0, 0, width, height, 0x88000000); gfx.fill(x, y, x + w, y + h, 0xFF222222); gfx.drawCenteredString(font, node.getTitle(), x + w / 2, y + 10, 0xFFFFFF); gfx.drawWordWrap(font, node.getDescription(), x + 10, y + 30, w - 20, 0xDDDDDD); + final int[] textY = {y + 95}; gfx.drawString(font, - Component.literal("Cost: " + node.getCost() + " skill points"), - x + 10, y + h - 45, 0xAAAAAA); + Component.literal("Cost: " + node.getCost() + " skill points") + .withStyle(ChatFormatting.AQUA), + x + 10, textY[0], + 0xAAAAAA); + textY[0] += 15; + if (!node.getPrereqAttributes().isEmpty()) { + gfx.drawString(font, + Component.literal("Requirements:") + .withStyle(ChatFormatting.GOLD), + x + 10, textY[0], + 0xFFFFFF); + textY[0] += 12; + Minecraft.getInstance().player.getCapability(PlayerStatsProvider.PLAYER_STATS) + .ifPresent(stats -> { + for (var entry : node.getPrereqAttributes().entrySet()) { + String attr = entry.getKey(); + int required = entry.getValue(); + int current = stats.getAttributeLevel(attr); + boolean ok = current >= required; + Component line = Component.literal( + " - " + attr + ": " + current + "/" + required + ).withStyle(ok ? ChatFormatting.GREEN : ChatFormatting.RED); + gfx.drawString(font, line, x + 10, textY[0], 0xFFFFFF); + textY[0] += 12; + } + }); + } } private void rebuildOverlayButtons() { diff --git a/src/main/java/net/fretux/skillengine/compat/AscendCompatHandler.java b/src/main/java/net/fretux/skillengine/compat/AscendCompatHandler.java index e5504a4..d8d7c35 100644 --- a/src/main/java/net/fretux/skillengine/compat/AscendCompatHandler.java +++ b/src/main/java/net/fretux/skillengine/compat/AscendCompatHandler.java @@ -1,9 +1,7 @@ package net.fretux.skillengine.compat; -import net.fretux.ascend.player.PlayerStats; import net.fretux.ascend.player.PlayerStatsProvider; import net.fretux.skillengine.SkillEngine; -import net.fretux.skillengine.capability.PlayerSkillData; import net.fretux.skillengine.capability.SkillEngineCapabilities; import net.minecraft.world.entity.player.Player; import net.minecraftforge.event.TickEvent; diff --git a/src/main/java/net/fretux/skillengine/network/ServerboundUnlockNodePacket.java b/src/main/java/net/fretux/skillengine/network/ServerboundUnlockNodePacket.java index 2502bf4..6ab5539 100644 --- a/src/main/java/net/fretux/skillengine/network/ServerboundUnlockNodePacket.java +++ b/src/main/java/net/fretux/skillengine/network/ServerboundUnlockNodePacket.java @@ -47,7 +47,7 @@ public class ServerboundUnlockNodePacket { SkillEngine.LOGGER.info("Received unlock request for {} from {} (points={})", msg.nodeId, player.getGameProfile().getName(), data.getSkillPoints()); - if (SkillLogic.canUnlock(data, node)) { + if (SkillLogic.canUnlock(data, player, node)) { data.unlockNode(node); SkillEngine.LOGGER.info("Node {} unlocked for {}", msg.nodeId, player.getGameProfile().getName()); diff --git a/src/main/java/net/fretux/skillengine/skilltree/SkillLogic.java b/src/main/java/net/fretux/skillengine/skilltree/SkillLogic.java index ae0b238..5807b44 100644 --- a/src/main/java/net/fretux/skillengine/skilltree/SkillLogic.java +++ b/src/main/java/net/fretux/skillengine/skilltree/SkillLogic.java @@ -1,17 +1,32 @@ package net.fretux.skillengine.skilltree; +import net.fretux.ascend.player.PlayerStatsProvider; import net.fretux.skillengine.capability.PlayerSkillData; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Player; + +import java.util.Map; public class SkillLogic { - public static boolean canUnlock(PlayerSkillData data, SkillNode node) { + public static boolean canUnlock(PlayerSkillData data, Player player, SkillNode node) { if (data.isUnlocked(node.getId())) return false; if (data.getSkillPoints() < node.getCost()) return false; if (node.isRoot()) return true; + if (!meetsAscendPrerequisites(player, node)) return false; for (ResourceLocation neighborId : node.getLinks()) { if (data.isUnlocked(neighborId)) return true; } return false; } + public static boolean meetsAscendPrerequisites(Player player, SkillNode node) { + return player.getCapability(PlayerStatsProvider.PLAYER_STATS).map(stats -> { + for (Map.Entry req : node.getPrereqAttributes().entrySet()) { + int current = stats.getAttributeLevel(req.getKey()); + if (current < req.getValue()) return false; + } + return true; + }).orElse(false); + } + } diff --git a/src/main/java/net/fretux/skillengine/skilltree/SkillNode.java b/src/main/java/net/fretux/skillengine/skilltree/SkillNode.java index 634dd3c..eb0570a 100644 --- a/src/main/java/net/fretux/skillengine/skilltree/SkillNode.java +++ b/src/main/java/net/fretux/skillengine/skilltree/SkillNode.java @@ -4,6 +4,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import java.util.List; +import java.util.Map; public class SkillNode { @@ -15,6 +16,8 @@ public class SkillNode { private final float y; private final List links; private final List tags; + private final ResourceLocation icons; + private final Map prereqAttributes; public SkillNode(ResourceLocation id, Component title, @@ -22,7 +25,9 @@ public class SkillNode { int cost, float x, float y, List links, - List tags) { + List tags, + ResourceLocation icons, + Map prereqAttributes) { this.id = id; this.title = title; @@ -32,18 +37,51 @@ public class SkillNode { this.y = y; this.links = List.copyOf(links); this.tags = List.copyOf(tags); + this.icons = icons; + this.prereqAttributes = Map.copyOf(prereqAttributes); } - public ResourceLocation getId() { return id; } - public Component getTitle() { return title; } - public Component getDescription() { return description; } - public int getCost() { return cost; } - public float getX() { return x; } - public float getY() { return y; } - public List getLinks() { return links; } - public List getTags() { return tags; } + public ResourceLocation getId() { + return id; + } + + public Component getTitle() { + return title; + } + + public Component getDescription() { + return description; + } + + public int getCost() { + return cost; + } + + public float getX() { + return x; + } + + public float getY() { + return y; + } + + public List getLinks() { + return links; + } + + public List getTags() { + return tags; + } + + public ResourceLocation getIcons() { + return icons; + } + + public Map getPrereqAttributes() { + return prereqAttributes; + } public boolean isRoot() { - return links.isEmpty(); // or a dedicated flag if you want later + return links.isEmpty(); } } \ No newline at end of file diff --git a/src/main/java/net/fretux/skillengine/skilltree/SkillNodeLoader.java b/src/main/java/net/fretux/skillengine/skilltree/SkillNodeLoader.java index 6c00492..55236bb 100644 --- a/src/main/java/net/fretux/skillengine/skilltree/SkillNodeLoader.java +++ b/src/main/java/net/fretux/skillengine/skilltree/SkillNodeLoader.java @@ -12,6 +12,7 @@ import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -33,29 +34,30 @@ public class SkillNodeLoader extends SimpleJsonResourceReloadListener { protected void apply(Map jsons, ResourceManager resourceManager, ProfilerFiller profiler) { - SkillEngine.LOGGER.info("Loading skill nodes..."); SkillNodeRegistry.clear(); - jsons.forEach((id, element) -> { JsonObject obj = element.getAsJsonObject(); - String title = obj.get("title").getAsString(); String description = obj.get("description").getAsString(); int cost = obj.get("cost").getAsInt(); - JsonObject pos = obj.getAsJsonObject("position"); float x = pos.get("x").getAsFloat(); float y = pos.get("y").getAsFloat(); - List links = new ArrayList<>(); obj.getAsJsonArray("links").forEach(e -> links.add(new ResourceLocation(e.getAsString()))); - List tags = new ArrayList<>(); obj.getAsJsonArray("tags").forEach(e -> tags.add(new ResourceLocation(e.getAsString()))); - + ResourceLocation icons = new ResourceLocation(obj.get("icons").getAsString()); + Map prereqAttributes = new HashMap<>(); + if (obj.has("prerequisites")) { + JsonObject prereqObj = obj.getAsJsonObject("prerequisites"); + for (Map.Entry entry : prereqObj.entrySet()) { + prereqAttributes.put(entry.getKey(), entry.getValue().getAsInt()); + } + } SkillNode node = new SkillNode( id, Component.literal(title), @@ -63,11 +65,12 @@ public class SkillNodeLoader extends SimpleJsonResourceReloadListener { cost, x, y, links, - tags + tags, + icons, + prereqAttributes ); SkillNodeRegistry.put(node); }); - SkillEngine.LOGGER.info("Loaded {} skill nodes", SkillNodeRegistry.all().size()); } } diff --git a/src/main/resources/data/skillengine/skillnodes/arcane_spark.json b/src/main/resources/data/skillengine/skillnodes/arcane_spark.json deleted file mode 100644 index 35bd379..0000000 --- a/src/main/resources/data/skillengine/skillnodes/arcane_spark.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "title": "Arcane Spark", - "description": "A faint magical glow flickers.\nGrants a minor magic tag.", - "cost": 1, - "position": { "x": 150.0, "y": -50.0 }, - "links": ["skillengine:welcome"], - "tags": [ - "skillengine:magic_1" - ] -} \ No newline at end of file diff --git a/src/main/resources/data/skillengine/skillnodes/endurance.json b/src/main/resources/data/skillengine/skillnodes/endurance.json deleted file mode 100644 index ea5c830..0000000 --- a/src/main/resources/data/skillengine/skillnodes/endurance.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "title": "Endurance Training", - "description": "Improves stamina regeneration slightly.", - "cost": 1, - "position": { "x": -300.0, "y": -50.0 }, - "links": ["skillengine:strength_1"], - "tags": [ - "skillengine:endurance" - ] -} \ No newline at end of file diff --git a/src/main/resources/data/skillengine/skillnodes/resilience.json b/src/main/resources/data/skillengine/skillnodes/resilience.json deleted file mode 100644 index e4cafe9..0000000 --- a/src/main/resources/data/skillengine/skillnodes/resilience.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "title": "Path of Resilience", - "description": "Your endurance increases.\nA defensive option.", - "cost": 2, - "position": { "x": -100.0, "y": 300.0 }, - "links": ["skillengine:wanderer"], - "tags": [ - "skillengine:resilience" - ] -} \ No newline at end of file diff --git a/src/main/resources/data/skillengine/skillnodes/speed.json b/src/main/resources/data/skillengine/skillnodes/speed.json deleted file mode 100644 index 29fd1d2..0000000 --- a/src/main/resources/data/skillengine/skillnodes/speed.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "title": "Path of Speed", - "description": "Your body learns to move quicker.", - "cost": 2, - "position": { "x": 100.0, "y": 300.0 }, - "links": ["skillengine:wanderer"], - "tags": [ - "skillengine:speed" - ] -} diff --git a/src/main/resources/data/skillengine/skillnodes/strength_1.json b/src/main/resources/data/skillengine/skillnodes/strength_1.json index fbb9403..c02552c 100644 --- a/src/main/resources/data/skillengine/skillnodes/strength_1.json +++ b/src/main/resources/data/skillengine/skillnodes/strength_1.json @@ -1,10 +1,12 @@ { "title": "Basic Strength", - "description": "You feel your muscles tighten.\nGrants +1 Strength tag.", + "description": "You feel your muscles tighten.\nGrants +1 Strength.", "cost": 1, "position": { "x": -150.0, "y": -50.0 }, "links": ["skillengine:welcome"], - "tags": [ - "skillengine:strength_1" - ] + "tags": ["skillengine:strength_1"], + "icons": "minecraft:textures/item/iron_sword.png", + "prerequisites": { + "strength": 20 + } } \ No newline at end of file diff --git a/src/main/resources/data/skillengine/skillnodes/wanderer.json b/src/main/resources/data/skillengine/skillnodes/wanderer.json index 6d37721..9de2449 100644 --- a/src/main/resources/data/skillengine/skillnodes/wanderer.json +++ b/src/main/resources/data/skillengine/skillnodes/wanderer.json @@ -6,5 +6,9 @@ "links": ["skillengine:welcome"], "tags": [ "skillengine:wanderer" - ] + ], + "icons": "minecraft:textures/item/leather_boots.png", + "prerequisites": { + "agility": 20 + } } \ No newline at end of file diff --git a/src/main/resources/data/skillengine/skillnodes/welcome.json b/src/main/resources/data/skillengine/skillnodes/welcome.json index 1d70a65..8a17e46 100644 --- a/src/main/resources/data/skillengine/skillnodes/welcome.json +++ b/src/main/resources/data/skillengine/skillnodes/welcome.json @@ -2,10 +2,15 @@ "title": "Welcome to Ascend", "description": "This is your first skill node.\nClick Unlock to test the system.", "cost": 0, - "position": { "x": 0.0, "y": 0.0 }, + "position": { + "x": 0.0, + "y": 0.0 + }, "links": [], "tags": [ "skillengine:welcome", "skillengine:starter" - ] + ], + "icons": "minecraft:textures/item/book.png", + "prerequisites": {} } \ No newline at end of file