Implement mutual exclusives

This commit is contained in:
2025-12-04 20:46:39 +01:00
parent 9ff5d9f828
commit d0755c893f
5 changed files with 37 additions and 5 deletions
@@ -59,6 +59,14 @@ public class SkilltreeScreen extends Screen {
if (button == 0) { if (button == 0) {
SkillNode clicked = findNodeAt(mouseX, mouseY); SkillNode clicked = findNodeAt(mouseX, mouseY);
if (clicked != null) { if (clicked != null) {
boolean lockedByExclusivity = clicked.getExclusiveWith().stream()
.anyMatch(SkilltreeClientState::isUnlocked);
if (lockedByExclusivity) {
Minecraft.getInstance().player.playSound(
net.minecraft.sounds.SoundEvents.VILLAGER_NO, 1f, 1f
);
return true;
}
if (SkilltreeClientState.isUnlocked(clicked.getId())) { if (SkilltreeClientState.isUnlocked(clicked.getId())) {
return true; return true;
} }
@@ -70,7 +78,6 @@ public class SkilltreeScreen extends Screen {
lastMouseX = mouseX; lastMouseX = mouseX;
lastMouseY = mouseY; lastMouseY = mouseY;
} }
return super.mouseClicked(mouseX, mouseY, button); return super.mouseClicked(mouseX, mouseY, button);
} }
@@ -162,6 +169,11 @@ public class SkilltreeScreen extends Screen {
int r = 12; int r = 12;
boolean unlocked = SkilltreeClientState.isUnlocked(node.getId()); boolean unlocked = SkilltreeClientState.isUnlocked(node.getId());
int color = unlocked ? 0xFF44CC44 : 0xFF444444; int color = unlocked ? 0xFF44CC44 : 0xFF444444;
boolean lockedByExclusivity = node.getExclusiveWith().stream()
.anyMatch(SkilltreeClientState::isUnlocked);
if (lockedByExclusivity) {
color = 0xFFCC3333;
}
if (node == hoveredNode) color = 0xFF777777; if (node == hoveredNode) color = 0xFF777777;
if (node == selectedNode) color = 0xFF999933; if (node == selectedNode) color = 0xFF999933;
gfx.fill(pos[0] - r, pos[1] - r, pos[0] + r, pos[1] + r, color); gfx.fill(pos[0] - r, pos[1] - r, pos[0] + r, pos[1] + r, color);
@@ -197,6 +209,9 @@ public class SkilltreeScreen extends Screen {
} }
private void renderOverlay(GuiGraphics gfx, SkillNode node) { private void renderOverlay(GuiGraphics gfx, SkillNode node) {
boolean lockedByExclusivity = node.getExclusiveWith().stream()
.anyMatch(SkilltreeClientState::isUnlocked);
if (lockedByExclusivity) return;
int w = 240; int w = 240;
int h = 180; int h = 180;
int x = (width - w) / 2; int x = (width - w) / 2;
@@ -48,8 +48,6 @@ public class ServerboundUnlockNodePacket {
if (SkillLogic.canUnlock(data, player, node)) { if (SkillLogic.canUnlock(data, player, node)) {
data.unlockNode(node); data.unlockNode(node);
SkillEngine.LOGGER.info("Node {} unlocked for {}", msg.nodeId, player.getGameProfile().getName()); SkillEngine.LOGGER.info("Node {} unlocked for {}", msg.nodeId, player.getGameProfile().getName());
// Send client an immediate update about the unlocked node and the new remaining points
PacketHandler.CHANNEL.send( PacketHandler.CHANNEL.send(
PacketDistributor.PLAYER.with(() -> player), PacketDistributor.PLAYER.with(() -> player),
new ClientboundNodeUnlockedPacket(node.getId(), data.getSkillPoints()) new ClientboundNodeUnlockedPacket(node.getId(), data.getSkillPoints())
@@ -17,6 +17,11 @@ public class SkillLogic {
for (ResourceLocation neighborId : node.getLinks()) { for (ResourceLocation neighborId : node.getLinks()) {
if (data.isUnlocked(neighborId)) return true; if (data.isUnlocked(neighborId)) return true;
} }
for (ResourceLocation excl : node.getExclusiveWith()) {
if (data.isUnlocked(excl)) {
return false;
}
}
return false; return false;
} }
public static boolean meetsAscendPrerequisites(Player player, SkillNode node) { public static boolean meetsAscendPrerequisites(Player player, SkillNode node) {
@@ -18,6 +18,7 @@ public class SkillNode {
private final List<ResourceLocation> tags; private final List<ResourceLocation> tags;
private final ResourceLocation icons; private final ResourceLocation icons;
private final Map<String, Integer> prereqAttributes; private final Map<String, Integer> prereqAttributes;
private final List<ResourceLocation> exclusiveWith;
public SkillNode(ResourceLocation id, public SkillNode(ResourceLocation id,
Component title, Component title,
@@ -27,7 +28,8 @@ public class SkillNode {
List<ResourceLocation> links, List<ResourceLocation> links,
List<ResourceLocation> tags, List<ResourceLocation> tags,
ResourceLocation icons, ResourceLocation icons,
Map<String, Integer> prereqAttributes) { Map<String, Integer> prereqAttributes,
List<ResourceLocation> exclusiveWith){
this.id = id; this.id = id;
this.title = title; this.title = title;
@@ -39,6 +41,7 @@ public class SkillNode {
this.tags = List.copyOf(tags); this.tags = List.copyOf(tags);
this.icons = icons; this.icons = icons;
this.prereqAttributes = Map.copyOf(prereqAttributes); this.prereqAttributes = Map.copyOf(prereqAttributes);
this.exclusiveWith = List.copyOf(exclusiveWith);
} }
public ResourceLocation getId() { public ResourceLocation getId() {
@@ -81,6 +84,10 @@ public class SkillNode {
return prereqAttributes; return prereqAttributes;
} }
public List<ResourceLocation> getExclusiveWith() {
return exclusiveWith;
}
public boolean isRoot() { public boolean isRoot() {
return links.isEmpty(); return links.isEmpty();
} }
@@ -61,6 +61,11 @@ public class SkillNodeLoader extends SimpleJsonResourceReloadListener {
prereqAttributes.put(entry.getKey(), entry.getValue().getAsInt()); prereqAttributes.put(entry.getKey(), entry.getValue().getAsInt());
} }
} }
List<ResourceLocation> exclusiveWith = new ArrayList<>();
if (obj.has("exclusive_with")) {
obj.getAsJsonArray("exclusive_with").forEach(e ->
exclusiveWith.add(new ResourceLocation(e.getAsString())));
}
SkillNode node = new SkillNode( SkillNode node = new SkillNode(
nodeId, nodeId,
Component.literal(title), Component.literal(title),
@@ -70,8 +75,10 @@ public class SkillNodeLoader extends SimpleJsonResourceReloadListener {
links, links,
tags, tags,
icons, icons,
prereqAttributes prereqAttributes,
exclusiveWith
); );
SkillNodeRegistry.put(node); SkillNodeRegistry.put(node);
}); });
SkillEngine.LOGGER.info("Loaded {} skill nodes", SkillNodeRegistry.all().size()); SkillEngine.LOGGER.info("Loaded {} skill nodes", SkillNodeRegistry.all().size());