package net.fretux.skillengine.skilltree; import net.fretux.skillengine.api.PrerequisiteProviderRegistry; import net.fretux.skillengine.capability.PlayerSkillData; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; public class SkillLogic { public static boolean canUnlock(PlayerSkillData data, Player player, SkillNode node) { return getUnlockPlan(data, player, node).isPresent(); } public static Optional> getUnlockPlan(PlayerSkillData data, Player player, SkillNode node) { if (data == null || player == null || node == null || data.isUnlocked(node.getId())) { return Optional.empty(); } List plan = new ArrayList<>(); Set planned = new HashSet<>(); Set visiting = new HashSet<>(); if (!collectUnlockPlan(data, player, node, plan, planned, visiting)) { return Optional.empty(); } int totalCost = 0; for (SkillNode plannedNode : plan) { totalCost += plannedNode.getCost(); } if (data.getSkillPoints() < totalCost) { return Optional.empty(); } return Optional.of(List.copyOf(plan)); } public static boolean meetsAscendPrerequisites(Player player, SkillNode node) { if (node.getPrereqAttributes().isEmpty()) return true; if (!(player instanceof ServerPlayer serverPlayer)) return true; for (Map.Entry req : node.getPrereqAttributes().entrySet()) { if (!PrerequisiteProviderRegistry.meets(serverPlayer, node, req.getKey(), req.getValue())) { return false; } } return true; } private static boolean collectUnlockPlan(PlayerSkillData data, Player player, SkillNode node, List plan, Set planned, Set visiting) { ResourceLocation id = node.getId(); if (data.isUnlocked(id) || planned.contains(id)) { return true; } if (!visiting.add(id)) { return false; } if (!meetsAscendPrerequisites(player, node) || conflictsWithUnlocked(data, node)) { visiting.remove(id); return false; } for (ResourceLocation parentId : node.getLinks()) { if (data.isUnlocked(parentId)) { continue; } SkillNode parent = SkillNodeRegistry.get(parentId); if (parent == null || !collectUnlockPlan(data, player, parent, plan, planned, visiting)) { visiting.remove(id); return false; } } if (conflictsWithPlanned(node, planned)) { visiting.remove(id); return false; } visiting.remove(id); planned.add(id); plan.add(node); return true; } private static boolean conflictsWithUnlocked(PlayerSkillData data, SkillNode node) { for (ResourceLocation excl : node.getExclusiveWith()) { if (data.isUnlocked(excl)) { return true; } } return false; } private static boolean conflictsWithPlanned(SkillNode node, Set planned) { for (ResourceLocation excl : node.getExclusiveWith()) { if (planned.contains(excl)) { return true; } } return false; } }