From dd15eb93271e2d97641752b5305cbdd88a474dfd Mon Sep 17 00:00:00 2001 From: fretuks Date: Tue, 25 Nov 2025 22:23:14 +0100 Subject: [PATCH] Implement detection for resetting points and add remaining skillpoints in tree screen --- gradle.properties | 2 +- .../capability/PlayerSkillData.java | 17 +++++ .../client/SkilltreeClientState.java | 9 +++ .../skillengine/client/SkilltreeScreen.java | 5 ++ .../compat/AscendResetDetector.java | 63 +++++++++++++++++++ .../events/SkillEnginePlayerEvents.java | 2 +- .../network/ClientboundSyncSkillsPacket.java | 13 ++-- .../skillengine/network/PacketHandler.java | 11 ++++ .../network/ServerboundUnlockNodePacket.java | 2 - 9 files changed, 116 insertions(+), 8 deletions(-) create mode 100644 src/main/java/net/fretux/skillengine/compat/AscendResetDetector.java diff --git a/gradle.properties b/gradle.properties index abfe87f..3f279d9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -48,7 +48,7 @@ mod_name=Fretux's Skill Engine # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=All Rights Reserved # The mod version. See https://semver.org/ -mod_version=1.0.0 +mod_version=1.1.0 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html diff --git a/src/main/java/net/fretux/skillengine/capability/PlayerSkillData.java b/src/main/java/net/fretux/skillengine/capability/PlayerSkillData.java index dc20d91..005e8af 100644 --- a/src/main/java/net/fretux/skillengine/capability/PlayerSkillData.java +++ b/src/main/java/net/fretux/skillengine/capability/PlayerSkillData.java @@ -1,6 +1,7 @@ package net.fretux.skillengine.capability; import net.fretux.skillengine.skilltree.SkillNode; +import net.fretux.skillengine.skilltree.SkillNodeRegistry; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.Tag; @@ -81,4 +82,20 @@ public class PlayerSkillData { public Set getUnlockedNodes() { return unlockedNodes; } + + public int getTotalSkillCost() { + int total = 0; + for (ResourceLocation id : unlockedNodes) { + SkillNode node = SkillNodeRegistry.get(id); + if (node != null) { + total += node.getCost(); + } + } + return total; + } + + public void clearAllNodes() { + unlockedNodes.clear(); + activeTags.clear(); + } } \ No newline at end of file diff --git a/src/main/java/net/fretux/skillengine/client/SkilltreeClientState.java b/src/main/java/net/fretux/skillengine/client/SkilltreeClientState.java index cd40bf3..eb03466 100644 --- a/src/main/java/net/fretux/skillengine/client/SkilltreeClientState.java +++ b/src/main/java/net/fretux/skillengine/client/SkilltreeClientState.java @@ -12,6 +12,7 @@ import java.util.Set; public class SkilltreeClientState { private static final Set unlocked = new HashSet<>(); + private static int currentSkillPoints = 0; public static void unlockNode(ResourceLocation id) { SkillEngine.LOGGER.info("[CLIENT] Unlocking node: {}", id); @@ -41,4 +42,12 @@ public class SkilltreeClientState { unlocked.clear(); unlocked.addAll(ids); } + + public static void setCurrentSkillPoints(int points) { + currentSkillPoints = points; + } + + public static int getCurrentSkillPoints() { + return currentSkillPoints; + } } \ No newline at end of file diff --git a/src/main/java/net/fretux/skillengine/client/SkilltreeScreen.java b/src/main/java/net/fretux/skillengine/client/SkilltreeScreen.java index 036eb89..a516622 100644 --- a/src/main/java/net/fretux/skillengine/client/SkilltreeScreen.java +++ b/src/main/java/net/fretux/skillengine/client/SkilltreeScreen.java @@ -98,6 +98,11 @@ public class SkilltreeScreen extends Screen { @Override public void render(GuiGraphics gfx, int mouseX, int mouseY, float partialTicks) { this.renderBackground(gfx); + int remaining = SkilltreeClientState.getCurrentSkillPoints(); + Component pointsText = Component.literal(remaining + " skillpoints remaining") + .withStyle(ChatFormatting.YELLOW); + gfx.drawString(font, pointsText, 10, 10, 0xFFFFFF); + hoveredNode = findNodeAt(mouseX, mouseY); drawGraph(gfx); if (hoveredNode != null && selectedNode == null) { diff --git a/src/main/java/net/fretux/skillengine/compat/AscendResetDetector.java b/src/main/java/net/fretux/skillengine/compat/AscendResetDetector.java new file mode 100644 index 0000000..35ada5c --- /dev/null +++ b/src/main/java/net/fretux/skillengine/compat/AscendResetDetector.java @@ -0,0 +1,63 @@ +package net.fretux.skillengine.compat; + +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.fretux.skillengine.network.PacketHandler; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +import java.util.*; + +@Mod.EventBusSubscriber(modid = SkillEngine.MODID) +public class AscendResetDetector { + private static final String[] ASCEND_ATTRIBUTES = new String[]{ + "strength", "agility", "fortitude", "intelligence", + "willpower", "charisma", + "light_scaling", "medium_scaling", "heavy_scaling", "magic_scaling" + }; + private static final Map LAST_ATTRIBUTE_SUM = new HashMap<>(); + private static final Map LAST_UNSPENT = new HashMap<>(); + + @SubscribeEvent + public static void onTick(TickEvent.PlayerTickEvent event) { + if (event.phase != TickEvent.Phase.END) return; + Player player = event.player; + if (player.level().isClientSide) return; + player.getCapability(PlayerStatsProvider.PLAYER_STATS).ifPresent(ascend -> { + UUID id = player.getUUID(); + int currentSum = 0; + for (String a : ASCEND_ATTRIBUTES) { + currentSum += ascend.getAttributeLevel(a); + } + int lastSum = LAST_ATTRIBUTE_SUM.getOrDefault(id, currentSum); + LAST_ATTRIBUTE_SUM.put(id, currentSum); + int currentUnspent = ascend.getUnspentPoints(); + int lastUnspent = LAST_UNSPENT.getOrDefault(id, currentUnspent); + LAST_UNSPENT.put(id, currentUnspent); + boolean attributesDropped = lastSum > currentSum; + boolean largeDrop = (lastSum - currentSum) >= 5; + boolean unspentJump = currentUnspent > lastUnspent + 3; + if (attributesDropped && largeDrop && unspentJump) { + SkillEngine.LOGGER.info("[SkillEngine] Detected Ascend attribute reset. Resetting skilltree."); + player.getCapability(SkillEngineCapabilities.PLAYER_SKILLS).ifPresent(skillData -> { + resetSkillEngine(skillData, player); + }); + } + }); + } + + private static void resetSkillEngine(PlayerSkillData data, Player player) { + int refund = data.getTotalSkillCost(); + data.addSkillPoints(refund); + data.clearAllNodes(); + SkillEngine.LOGGER.info("[SkillEngine] Skilltree fully reset. Refunded {} skill points.", refund); + if (player instanceof ServerPlayer serverPlayer) { + PacketHandler.syncSkillsTo(serverPlayer); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/fretux/skillengine/events/SkillEnginePlayerEvents.java b/src/main/java/net/fretux/skillengine/events/SkillEnginePlayerEvents.java index d1f40df..4a99d94 100644 --- a/src/main/java/net/fretux/skillengine/events/SkillEnginePlayerEvents.java +++ b/src/main/java/net/fretux/skillengine/events/SkillEnginePlayerEvents.java @@ -17,7 +17,7 @@ public class SkillEnginePlayerEvents { player.getCapability(SkillEngineCapabilities.PLAYER_SKILLS).ifPresent(data -> { PacketHandler.CHANNEL.send( PacketDistributor.PLAYER.with(() -> player), - new ClientboundSyncSkillsPacket(data.getUnlockedNodes()) + new ClientboundSyncSkillsPacket(data.getUnlockedNodes(), data.getSkillPoints()) ); }); } diff --git a/src/main/java/net/fretux/skillengine/network/ClientboundSyncSkillsPacket.java b/src/main/java/net/fretux/skillengine/network/ClientboundSyncSkillsPacket.java index 2a6b94e..6e73be8 100644 --- a/src/main/java/net/fretux/skillengine/network/ClientboundSyncSkillsPacket.java +++ b/src/main/java/net/fretux/skillengine/network/ClientboundSyncSkillsPacket.java @@ -12,9 +12,11 @@ import java.util.function.Supplier; public class ClientboundSyncSkillsPacket { private final Set unlocked; + private final int skillPoints; - public ClientboundSyncSkillsPacket(Set unlocked) { + public ClientboundSyncSkillsPacket(Set unlocked, int skillPoints) { this.unlocked = unlocked; + this.skillPoints = skillPoints; } public static void encode(ClientboundSyncSkillsPacket msg, FriendlyByteBuf buf) { @@ -22,6 +24,7 @@ public class ClientboundSyncSkillsPacket { for (ResourceLocation id : msg.unlocked) { buf.writeResourceLocation(id); } + buf.writeInt(msg.skillPoints); } public static ClientboundSyncSkillsPacket decode(FriendlyByteBuf buf) { @@ -30,13 +33,15 @@ public class ClientboundSyncSkillsPacket { for (int i = 0; i < size; i++) { result.add(buf.readResourceLocation()); } - return new ClientboundSyncSkillsPacket(result); + int points = buf.readInt(); + return new ClientboundSyncSkillsPacket(result, points); } - + public static void handle(ClientboundSyncSkillsPacket msg, Supplier ctx) { ctx.get().enqueueWork(() -> { SkilltreeClientState.setUnlocked(msg.unlocked); + SkilltreeClientState.setCurrentSkillPoints(msg.skillPoints); }); ctx.get().setPacketHandled(true); } -} +} \ No newline at end of file diff --git a/src/main/java/net/fretux/skillengine/network/PacketHandler.java b/src/main/java/net/fretux/skillengine/network/PacketHandler.java index 9409c1c..9846b02 100644 --- a/src/main/java/net/fretux/skillengine/network/PacketHandler.java +++ b/src/main/java/net/fretux/skillengine/network/PacketHandler.java @@ -1,9 +1,12 @@ package net.fretux.skillengine.network; +import net.fretux.skillengine.capability.SkillEngineCapabilities; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; import net.minecraftforge.network.NetworkDirection; import net.minecraftforge.network.NetworkRegistry; import net.fretux.skillengine.SkillEngine; +import net.minecraftforge.network.PacketDistributor; import net.minecraftforge.network.simple.SimpleChannel; public class PacketHandler { @@ -35,4 +38,12 @@ public class PacketHandler { .consumerMainThread(ClientboundSyncSkillsPacket::handle) .add(); } + + public static void syncSkillsTo(ServerPlayer player) { + player.getCapability(SkillEngineCapabilities.PLAYER_SKILLS).ifPresent(data -> { + ClientboundSyncSkillsPacket packet = + new ClientboundSyncSkillsPacket(data.getUnlockedNodes(), data.getSkillPoints()); + CHANNEL.send(PacketDistributor.PLAYER.with(() -> player), packet); + }); + } } diff --git a/src/main/java/net/fretux/skillengine/network/ServerboundUnlockNodePacket.java b/src/main/java/net/fretux/skillengine/network/ServerboundUnlockNodePacket.java index 6ab5539..a358bd6 100644 --- a/src/main/java/net/fretux/skillengine/network/ServerboundUnlockNodePacket.java +++ b/src/main/java/net/fretux/skillengine/network/ServerboundUnlockNodePacket.java @@ -36,13 +36,11 @@ public class ServerboundUnlockNodePacket { SkillEngine.LOGGER.warn("Unlock packet received but sender was null"); return; } - SkillNode node = SkillNodeRegistry.get(msg.nodeId); if (node == null) { SkillEngine.LOGGER.warn("Unlock packet for unknown node {}", msg.nodeId); return; } - player.getCapability(SkillEngineCapabilities.PLAYER_SKILLS).ifPresent(data -> { SkillEngine.LOGGER.info("Received unlock request for {} from {} (points={})", msg.nodeId, player.getGameProfile().getName(), data.getSkillPoints());