try to improve UX for dumbasses

This commit is contained in:
2026-07-22 16:25:28 +02:00
parent 27d80b099d
commit bd4f7f6bc6
3 changed files with 118 additions and 8 deletions
+1 -1
View File
@@ -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. # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
mod_license=All Rights Reserved mod_license=All Rights Reserved
# The mod version. See https://semver.org/ # The mod version. See https://semver.org/
mod_version=1.4.3 mod_version=1.4.5
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # 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. # This should match the base package used for the mod sources.
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html # See https://maven.apache.org/guides/mini/guide-naming-conventions.html
@@ -69,7 +69,8 @@ public class AbilitySlotsScreen extends Screen {
} }
private void drawSlotRow(GuiGraphics gfx, int x, int y, int slot, String label) { private void drawSlotRow(GuiGraphics gfx, int x, int y, int slot, String label) {
gfx.drawString(font, label + ":", x, y + 6, 0xFFFFFF); String slotLabel = label + ":";
gfx.drawString(font, slotLabel, x, y + 6, 0xFFFFFF);
ResourceLocation abilityId = SkilltreeClientState.getAbilityInSlot(slot); ResourceLocation abilityId = SkilltreeClientState.getAbilityInSlot(slot);
String title = "Empty"; String title = "Empty";
ResourceLocation icon = null; ResourceLocation icon = null;
@@ -83,11 +84,12 @@ public class AbilitySlotsScreen extends Screen {
} }
} }
if (icon != null) { if (icon != null) {
gfx.blit(icon, x + 20, y - 2, 0, 0, 20, 20, 20, 20); int iconX = x + font.width(slotLabel) + 6;
int textX = x + 46; gfx.blit(icon, iconX, y - 2, 0, 0, 20, 20, 20, 20);
int textX = iconX + 26;
gfx.drawString(font, fitTitle(title, textX), textX, y + 6, 0xDDDDDD); gfx.drawString(font, fitTitle(title, textX), textX, y + 6, 0xDDDDDD);
} else { } else {
int textX = x + 20; int textX = x + font.width(slotLabel) + 6;
gfx.drawString(font, fitTitle(title, textX), textX, y + 6, 0x888888); gfx.drawString(font, fitTitle(title, textX), textX, y + 6, 0x888888);
} }
} }
@@ -42,6 +42,10 @@ public class SkilltreeScreen extends Screen {
private AbilityNode hoveredAbility = null; private AbilityNode hoveredAbility = null;
private AbilityNode selectedAbility = null; private AbilityNode selectedAbility = null;
private ResourceLocation activeTree = null; private ResourceLocation activeTree = null;
private ResourceLocation highlightedNode = null;
private ResourceLocation highlightedAbility = null;
private boolean highlightSkillPoints = false;
private Component unlockFailureMessage = null;
public SkilltreeScreen() { public SkilltreeScreen() {
super(Component.literal("Ascend Skilltree")); super(Component.literal("Ascend Skilltree"));
@@ -132,6 +136,7 @@ public class SkilltreeScreen extends Screen {
hoveredNode = findNodeAt(mouseX, mouseY); hoveredNode = findNodeAt(mouseX, mouseY);
hoveredAbility = findAbilityAt(mouseX, mouseY); hoveredAbility = findAbilityAt(mouseX, mouseY);
drawGraph(gfx); drawGraph(gfx);
renderUnlockFailure(gfx);
if (hoveredNode != null && selectedNode == null && selectedAbility == null) { if (hoveredNode != null && selectedNode == null && selectedAbility == null) {
renderNodeTooltip(gfx, hoveredNode, mouseX, mouseY); renderNodeTooltip(gfx, hoveredNode, mouseX, mouseY);
} }
@@ -150,7 +155,13 @@ public class SkilltreeScreen extends Screen {
.append(Component.literal(String.valueOf(remaining)).withStyle(ChatFormatting.GOLD)); .append(Component.literal(String.valueOf(remaining)).withStyle(ChatFormatting.GOLD));
int panelWidth = font.width(pointsText) + 16; int panelWidth = font.width(pointsText) + 16;
gfx.fill(7, 6, 7 + panelWidth, 24, 0xAA080B10); gfx.fill(7, 6, 7 + panelWidth, 24, 0xAA080B10);
gfx.fill(7, 6, 9, 24, 0xFFFFC857); gfx.fill(7, 6, 9, 24, highlightSkillPoints ? 0xFFFF4545 : 0xFFFFC857);
if (highlightSkillPoints) {
gfx.fill(5, 4, 7 + panelWidth + 2, 6, 0xFFFF4545);
gfx.fill(5, 24, 7 + panelWidth + 2, 26, 0xFFFF4545);
gfx.fill(5, 4, 7, 26, 0xFFFF4545);
gfx.fill(7 + panelWidth, 4, 7 + panelWidth + 2, 26, 0xFFFF4545);
}
gfx.drawString(font, pointsText, 14, 11, 0xFFFFFF, true); gfx.drawString(font, pointsText, 14, 11, 0xFFFFFF, true);
Component hint = Component.literal("Drag to pan • Scroll to zoom") Component hint = Component.literal("Drag to pan • Scroll to zoom")
.withStyle(ChatFormatting.DARK_GRAY); .withStyle(ChatFormatting.DARK_GRAY);
@@ -203,7 +214,9 @@ public class SkilltreeScreen extends Screen {
int[] pos = worldToScreen(ability.getX(), ability.getY()); int[] pos = worldToScreen(ability.getX(), ability.getY());
boolean unlocked = SkilltreeClientState.isAbilityUnlocked(ability.getId()); boolean unlocked = SkilltreeClientState.isAbilityUnlocked(ability.getId());
int color; int color;
if (ability == selectedAbility) { if (ability.getId().equals(highlightedAbility)) {
color = 0xFFFF4545;
} else if (ability == selectedAbility) {
color = 0xFFAA8833; color = 0xFFAA8833;
} else if (ability == hoveredAbility) { } else if (ability == hoveredAbility) {
color = 0xFF8888AA; color = 0xFF8888AA;
@@ -404,6 +417,12 @@ public class SkilltreeScreen extends Screen {
boolean alreadyUnlocked = SkilltreeClientState.isUnlocked(selectedNode.getId()); boolean alreadyUnlocked = SkilltreeClientState.isUnlocked(selectedNode.getId());
if (!alreadyUnlocked) { if (!alreadyUnlocked) {
addRenderableWidget(Button.builder(Component.literal("Unlock"), btn -> { addRenderableWidget(Button.builder(Component.literal("Unlock"), btn -> {
if (!prepareSkillUnlock(selectedNode)) {
selectedNode = null;
clearWidgets();
rebuildTreeTabs();
return;
}
PacketHandler.CHANNEL.sendToServer( PacketHandler.CHANNEL.sendToServer(
new ServerboundUnlockNodePacket(selectedNode.getId()) new ServerboundUnlockNodePacket(selectedNode.getId())
); );
@@ -433,6 +452,12 @@ public class SkilltreeScreen extends Screen {
boolean unlocked = SkilltreeClientState.isAbilityUnlocked(selectedAbility.getId()); boolean unlocked = SkilltreeClientState.isAbilityUnlocked(selectedAbility.getId());
if (!unlocked) { if (!unlocked) {
addRenderableWidget(Button.builder(Component.literal("Unlock"), btn -> { addRenderableWidget(Button.builder(Component.literal("Unlock"), btn -> {
if (!prepareAbilityUnlock(selectedAbility)) {
selectedAbility = null;
clearWidgets();
rebuildTreeTabs();
return;
}
PacketHandler.CHANNEL.sendToServer( PacketHandler.CHANNEL.sendToServer(
new ServerboundUnlockNodePacket(selectedAbility.getId()) new ServerboundUnlockNodePacket(selectedAbility.getId())
); );
@@ -491,7 +516,9 @@ public class SkilltreeScreen extends Screen {
boolean unlocked = SkilltreeClientState.isUnlocked(node.getId()); boolean unlocked = SkilltreeClientState.isUnlocked(node.getId());
boolean lockedByExclusivity = isLockedByExclusivity(node); boolean lockedByExclusivity = isLockedByExclusivity(node);
int color; int color;
if (lockedByExclusivity) { if (node.getId().equals(highlightedNode)) {
color = 0xFFFF4545;
} else if (lockedByExclusivity) {
color = 0xFFCC3333; color = 0xFFCC3333;
} else if (node == selectedNode) { } else if (node == selectedNode) {
color = 0xFF999933; color = 0xFF999933;
@@ -514,6 +541,87 @@ public class SkilltreeScreen extends Screen {
return color; return color;
} }
private boolean prepareSkillUnlock(SkillNode target) {
clearUnlockHighlights();
for (ResourceLocation parentId : target.getLinks()) {
if (SkilltreeClientState.isUnlocked(parentId)) continue;
SkillNode parent = SkillNodeRegistry.get(parentId);
highlightedNode = parentId;
Component parentName = parent != null ? parent.getTitle() : Component.literal(parentId.toString());
unlockFailureMessage = Component.literal("Unlock ")
.append(parentName.copy().withStyle(ChatFormatting.YELLOW))
.append(Component.literal(" first — it is required for "))
.append(target.getTitle().copy().withStyle(ChatFormatting.WHITE));
return false;
}
if (!meetsClientAttributes(target)) {
highlightedNode = target.getId();
unlockFailureMessage = Component.literal("Attribute requirements are not met for ")
.append(target.getTitle().copy().withStyle(ChatFormatting.YELLOW));
return false;
}
if (SkilltreeClientState.getCurrentSkillPoints() < Math.max(0, target.getCost())) {
highlightSkillPoints = true;
unlockFailureMessage = Component.literal("Not enough skill points to unlock ")
.append(target.getTitle().copy().withStyle(ChatFormatting.YELLOW));
return false;
}
return true;
}
private boolean meetsClientAttributes(SkillNode node) {
if (node.getPrereqAttributes().isEmpty()) return true;
Minecraft minecraft = Minecraft.getInstance();
if (minecraft.player == null) return false;
return minecraft.player.getCapability(PlayerStatsProvider.PLAYER_STATS)
.map(stats -> node.getPrereqAttributes().entrySet().stream()
.allMatch(entry -> stats.getAttributeLevel(entry.getKey()) >= entry.getValue()))
.orElse(false);
}
private boolean prepareAbilityUnlock(AbilityNode ability) {
clearUnlockHighlights();
for (ResourceLocation parentId : ability.getLinks()) {
if (SkilltreeClientState.isUnlocked(parentId)
|| SkilltreeClientState.isAbilityUnlocked(parentId)) continue;
SkillNode parentNode = SkillNodeRegistry.get(parentId);
if (parentNode != null) {
highlightedNode = parentId;
} else {
highlightedAbility = parentId;
}
Component parentName = parentNode != null
? parentNode.getTitle()
: AbilityNodeRegistry.get(parentId) != null
? AbilityNodeRegistry.get(parentId).getTitle()
: Component.literal(parentId.toString());
unlockFailureMessage = Component.literal("Unlock ")
.append(parentName.copy().withStyle(ChatFormatting.YELLOW))
.append(Component.literal(" first — it is required for "))
.append(ability.getTitle().copy().withStyle(ChatFormatting.WHITE));
return false;
}
return true;
}
private void clearUnlockHighlights() {
highlightedNode = null;
highlightedAbility = null;
highlightSkillPoints = false;
unlockFailureMessage = null;
}
private void renderUnlockFailure(GuiGraphics gfx) {
if (unlockFailureMessage == null) return;
int messageWidth = Math.min(width - 40, font.width(unlockFailureMessage) + 20);
int x = (width - messageWidth) / 2;
int y = height - 34;
gfx.fill(x - 2, y - 2, x + messageWidth + 2, y + 18, 0xCC000000);
gfx.fill(x, y, x + messageWidth, y + 16, 0xE0421717);
gfx.fill(x, y, x + 3, y + 16, 0xFFFF4545);
gfx.drawCenteredString(font, unlockFailureMessage, width / 2, y + 4, 0xFFFFFF);
}
private void drawLine(GuiGraphics gfx, int x1, int y1, int x2, int y2, int color) { private void drawLine(GuiGraphics gfx, int x1, int y1, int x2, int y2, int color) {
float dx = x2 - x1; float dx = x2 - x1;
float dy = y2 - y1; float dy = y2 - y1;