aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/at/hannibal2')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/dev/GraphConfig.java12
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/rift/EnigmaSoulConfig.java8
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/IslandGraphs.kt8
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt32
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/rift/everywhere/EnigmaSoulWaypoints.kt33
-rw-r--r--src/main/java/at/hannibal2/skyhanni/test/GraphEditor.kt207
-rw-r--r--src/main/java/at/hannibal2/skyhanni/test/GraphNodeEditor.kt178
-rw-r--r--src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt1
8 files changed, 284 insertions, 195 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/dev/GraphConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/dev/GraphConfig.java
index 9eed8233c..acf9ac67d 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/dev/GraphConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/dev/GraphConfig.java
@@ -4,6 +4,7 @@ import at.hannibal2.skyhanni.config.core.config.Position;
import com.google.gson.annotations.Expose;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorKeybind;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider;
import io.github.notenoughupdates.moulconfig.annotations.ConfigLink;
import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
import org.lwjgl.input.Keyboard;
@@ -100,6 +101,17 @@ public class GraphConfig {
public Position namedNodesList = new Position(20, 20);
@Expose
+ @ConfigOption(
+ name = "Max Node Distance",
+ desc = "Only render nodes below this distance to the player.")
+ @ConfigEditorSlider(
+ minValue = 10,
+ maxValue = 500,
+ minStep = 10
+ )
+ public int maxNodeDistance = 50;
+
+ @Expose
@ConfigOption(name = "Shows Stats", desc = "Show funny extra statistics on save. May lag the game a bit.")
@ConfigEditorBoolean
public boolean showsStats = true;
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/rift/EnigmaSoulConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/rift/EnigmaSoulConfig.java
index 96c721539..f64a42506 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/rift/EnigmaSoulConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/rift/EnigmaSoulConfig.java
@@ -15,6 +15,11 @@ public class EnigmaSoulConfig {
@FeatureToggle
public boolean enabled = true;
+ @Expose
+ @ConfigOption(name = "Show Path Finder", desc = "Additionally show a pathfind to the Enigma Soul.")
+ @ConfigEditorBoolean
+ public boolean showPathFinder = true;
+
@ConfigOption(
name = "§aRift Guide",
desc = "Type §e/riftguide §7in chat or navigate through the SkyBlock Menu to open the §aRift Guide§7. " +
@@ -26,6 +31,5 @@ public class EnigmaSoulConfig {
@Expose
@ConfigOption(name = "Color", desc = "Color of the Enigma Souls.")
@ConfigEditorColour
- public String color = "0:120:13:49:255";
-
+ public String color = "0:245:219:27:198";
}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/IslandGraphs.kt b/src/main/java/at/hannibal2/skyhanni/data/IslandGraphs.kt
index 02a065abe..0b173db46 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/IslandGraphs.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/IslandGraphs.kt
@@ -40,6 +40,7 @@ import kotlin.math.abs
* jump pads between servers
* ring of love/romeo juliet quest
* death location
+ * % of island discvovered (every node was most closest node at least once)
* hub:
* 12 starter NPC's
* diana
@@ -49,6 +50,8 @@ import kotlin.math.abs
* enigma souls
* eyes
* big quests
+ * montezuma souls
+ * blood effigies
* spider:
* relicts + throw spot
* dwarven mines:
@@ -56,7 +59,7 @@ import kotlin.math.abs
* commssion areas
* events: raffle, goblin slayer, donpieresso
* deep
- * path to the bottom
+ * path to the bottom (Rhys NPC)
* end
* golem spawn
* dragon death spot
@@ -65,12 +68,15 @@ import kotlin.math.abs
* area mini bosses
* daily quests
* intro tutorials with elle
+ * fishing spots
*
* graph todo:
* fix rename not using tick but input event we have (+ create the input event in the first place)
* toggle distance to node by node path lengh, instead of eye of sight lenght
* press test button again to enable "true test mode", with graph math and hiding other stuff
* option to compare two graphs, and store multiple graphs in the edit mode in paralell
+ *
+ * mineshaft + corpse + ladder spot
*/
@SkyHanniModule
diff --git a/src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt b/src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt
index a73498013..e1fa3317a 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt
@@ -1,8 +1,15 @@
package at.hannibal2.skyhanni.data.model
+import at.hannibal2.skyhanni.data.IslandType
import at.hannibal2.skyhanni.utils.LorenzColor
-enum class GraphNodeTag(val internalName: String?, val color: LorenzColor, val cleanName: String, val description: String) {
+enum class GraphNodeTag(
+ val internalName: String,
+ val color: LorenzColor,
+ val cleanName: String,
+ val description: String,
+ val onlyIsland: IslandType? = null,
+) {
DEV("dev", LorenzColor.WHITE, "Dev", "Intentionally marked as dev."), // E.g. Spawn points, todos, etc
// Everywhere
@@ -16,31 +23,40 @@ enum class GraphNodeTag(val internalName: String?, val color: LorenzColor, val c
ROMEO("romeo", LorenzColor.WHITE, "Romeo & Juliette Quest", "Blocks related to the Romeo and Juliette/Ring of Love quest line."),
RACE("race", LorenzColor.WHITE, "Race Start/Stop", "A race start or stop point."),
SLAYER("slayer", LorenzColor.WHITE, "Slayer", "A Slayer area"),
+ HOPPITY("hoppity", LorenzColor.AQUA, "Hoppity Egg", "An egg location in Hoppity's Hunt."),
// hoppity
// Hub
- HUB_12_STARTER("starter_npc", LorenzColor.WHITE, "Starter NPC", "One of the 12 starter NPC's you need to talk to."),
+ HUB_12_STARTER(
+ "starter_npc", LorenzColor.WHITE, "Starter NPC", "One of the 12 starter NPC's you need to talk to.",
+ onlyIsland = IslandType.HUB,
+ ),
// diana
// Farming Islands: Pelts
- FARMING_CROP("farming_crop", LorenzColor.WHITE, "Farming Crop", "A spot where you can break crops on farming islands."),
+// FARMING_CROP("farming_crop", LorenzColor.WHITE, "Farming Crop", "A spot where you can break crops on farming islands."),
// Rift
- RIFT_ENIGMA("rift_enigma", LorenzColor.DARK_PURPLE, "Enigma Soul", "Enigma Souls in the rift."),
- RIFT_EYE("rift_eye", LorenzColor.DARK_RED, "Eye", "An Eye in the rift to teleport to."),
+ RIFT_ENIGMA("rift_enigma", LorenzColor.DARK_PURPLE, "Enigma Soul", "Enigma Souls in the rift.", onlyIsland = IslandType.THE_RIFT),
+ RIFT_EYE("rift_eye", LorenzColor.DARK_RED, "Rift Eye", "An Eye in the rift to teleport to.", onlyIsland = IslandType.THE_RIFT),
+ RIFT_MONTEZUMA("rift_montezuma", LorenzColor.GRAY, "Montezuma Soul Piece", "A piece of the Montezuma Soul.", onlyIsland = IslandType.THE_RIFT),
+ RIFT_EFFIGY("rift_effigy", LorenzColor.RED, "Blood Effigies", "Locations of the Blood Effigies.", onlyIsland = IslandType.THE_RIFT),
// Spider's Den
- SPIDER_RELIC("SPIDER_RELIC", LorenzColor.DARK_PURPLE, "Relic", "An relic in the Spider's Den."),
+ SPIDER_RELIC("SPIDER_RELIC", LorenzColor.DARK_PURPLE, "Spider's Relic", "An relic in the Spider's Den.", onlyIsland = IslandType.SPIDER_DEN),
// Dwarven Mines
- MINES_EMISSARY("mines_emissary", LorenzColor.GOLD, "Emissary", "A Emissary from the king."),
+ MINES_EMISSARY("mines_emissary", LorenzColor.GOLD, "Mines Emissary", "A Emissary from the king.", onlyIsland = IslandType.DWARVEN_MINES),
// commission areas
+ // Crimson Isles
+ CRIMSON_MINIBOSS("crimson_miniboss", LorenzColor.RED, "Crimson Miniboss", "Mini bosses in Crimson Isle.", onlyIsland = IslandType.CRIMSON_ISLE),
+
;
val displayName: String = color.getChatColor() + cleanName
companion object {
- fun byId(internalName: String?): GraphNodeTag? = values().firstOrNull { it.internalName == internalName }
+ fun byId(internalName: String?): GraphNodeTag? = entries.firstOrNull { it.internalName == internalName }
}
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/rift/everywhere/EnigmaSoulWaypoints.kt b/src/main/java/at/hannibal2/skyhanni/features/rift/everywhere/EnigmaSoulWaypoints.kt
index 67bf84aa9..d91b4008d 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/rift/everywhere/EnigmaSoulWaypoints.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/rift/everywhere/EnigmaSoulWaypoints.kt
@@ -1,5 +1,6 @@
package at.hannibal2.skyhanni.features.rift.everywhere
+import at.hannibal2.skyhanni.data.IslandGraphs
import at.hannibal2.skyhanni.data.jsonobjects.repo.EnigmaSoulsJson
import at.hannibal2.skyhanni.events.GuiContainerEvent
import at.hannibal2.skyhanni.events.InventoryCloseEvent
@@ -11,6 +12,7 @@ import at.hannibal2.skyhanni.events.render.gui.ReplaceItemEvent
import at.hannibal2.skyhanni.features.rift.RiftAPI
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.ChatUtils
+import at.hannibal2.skyhanni.utils.ColorUtils.toChromaColor
import at.hannibal2.skyhanni.utils.InventoryUtils.getAllItems
import at.hannibal2.skyhanni.utils.ItemUtils.getLore
import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer
@@ -46,7 +48,7 @@ object EnigmaSoulWaypoints {
"§5Toggle Missing",
"§7Click here to toggle",
"§7the waypoints for each",
- "§7missing souls on this page"
+ "§7missing souls on this page",
)
}
@@ -97,19 +99,26 @@ object EnigmaSoulWaypoints {
}
if (event.slot?.stack == null) return
+
val split = event.slot.stack.displayName.split("Enigma: ")
- if (split.size == 2) {
- event.makePickblock()
- val name = split.last()
- if (soulLocations.contains(name)) {
- if (!trackedSouls.contains(name)) {
- ChatUtils.chat("§5Tracking the $name Enigma Soul!", prefixColor = "§5")
- trackedSouls.add(name)
- } else {
- trackedSouls.remove(name)
- ChatUtils.chat("§5No longer tracking the $name Enigma Soul!", prefixColor = "§5")
+ if (split.size != 2) return
+
+ event.makePickblock()
+ val name = split.last()
+ if (!soulLocations.contains(name)) return
+
+ if (!trackedSouls.contains(name)) {
+ ChatUtils.chat("§5Tracking the $name Enigma Soul!", prefixColor = "§5")
+ if (config.showPathFinder) {
+ soulLocations[name]?.let {
+ IslandGraphs.pathFind(it, config.color.toChromaColor(), condition = { config.showPathFinder })
}
}
+ trackedSouls.add(name)
+ } else {
+ trackedSouls.remove(name)
+ ChatUtils.chat("§5No longer tracking the $name Enigma Soul!", prefixColor = "§5")
+ IslandGraphs.stop()
}
}
@@ -138,7 +147,7 @@ object EnigmaSoulWaypoints {
if (!isEnabled()) return
for (soul in trackedSouls) {
soulLocations[soul]?.let {
- event.drawWaypointFilled(it, LorenzColor.DARK_PURPLE.toColor(), seeThroughBlocks = true, beacon = true)
+ event.drawWaypointFilled(it, config.color.toChromaColor(), seeThroughBlocks = true, beacon = true)
event.drawDynamicText(it.add(y = 1), "§5${soul.removeSuffix(" Soul")} Soul", 1.5)
}
}
diff --git a/src/main/java/at/hannibal2/skyhanni/test/GraphEditor.kt b/src/main/java/at/hannibal2/skyhanni/test/GraphEditor.kt
index 81a85283e..d4038162f 100644
--- a/src/main/java/at/hannibal2/skyhanni/test/GraphEditor.kt
+++ b/src/main/java/at/hannibal2/skyhanni/test/GraphEditor.kt
@@ -14,13 +14,12 @@ import at.hannibal2.skyhanni.events.LorenzTickEvent
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.test.command.ErrorManager
import at.hannibal2.skyhanni.utils.ChatUtils
-import at.hannibal2.skyhanni.utils.CollectionUtils.addSearchString
-import at.hannibal2.skyhanni.utils.CollectionUtils.addString
import at.hannibal2.skyhanni.utils.ColorUtils
import at.hannibal2.skyhanni.utils.KeyboardManager
import at.hannibal2.skyhanni.utils.KeyboardManager.isKeyClicked
import at.hannibal2.skyhanni.utils.KeyboardManager.isKeyHeld
import at.hannibal2.skyhanni.utils.LocationUtils
+import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer
import at.hannibal2.skyhanni.utils.LocationUtils.playerLocation
import at.hannibal2.skyhanni.utils.LorenzColor
import at.hannibal2.skyhanni.utils.LorenzUtils
@@ -30,37 +29,27 @@ import at.hannibal2.skyhanni.utils.OSUtils
import at.hannibal2.skyhanni.utils.RenderUtils.draw3DLine_nea
import at.hannibal2.skyhanni.utils.RenderUtils.drawDynamicText
import at.hannibal2.skyhanni.utils.RenderUtils.drawWaypointFilled
-import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables
import at.hannibal2.skyhanni.utils.RenderUtils.renderStrings
-import at.hannibal2.skyhanni.utils.SimpleTimeMark
-import at.hannibal2.skyhanni.utils.renderables.Renderable
-import at.hannibal2.skyhanni.utils.renderables.ScrollValue
-import at.hannibal2.skyhanni.utils.renderables.Searchable
-import at.hannibal2.skyhanni.utils.renderables.buildSearchableScrollable
-import at.hannibal2.skyhanni.utils.renderables.toSearchable
import kotlinx.coroutines.runBlocking
import net.minecraft.client.settings.KeyBinding
import net.minecraftforge.fml.common.eventhandler.EventPriority
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable
import java.awt.Color
-import kotlin.math.sqrt
-import kotlin.time.Duration.Companion.milliseconds
-import kotlin.time.Duration.Companion.seconds
@SkyHanniModule
object GraphEditor {
- private val config get() = SkyHanniMod.feature.dev.devTool.graph
+ val config get() = SkyHanniMod.feature.dev.devTool.graph
- private fun isEnabled() = config != null && config.enabled
+ fun isEnabled() = config != null && config.enabled
private var id = 0
- private val nodes = mutableListOf<GraphingNode>()
+ val nodes = mutableListOf<GraphingNode>()
private val edges = mutableListOf<GraphingEdge>()
- private var activeNode: GraphingNode? = null
+ var activeNode: GraphingNode? = null
set(value) {
field = value
selectedEdge = findEdgeBetweenActiveAndClosed()
@@ -106,25 +95,25 @@ object GraphEditor {
private val edgeDijkstraColor = LorenzColor.DARK_BLUE.addOpacity(150)
private val edgeSelectedColor = LorenzColor.DARK_RED.addOpacity(150)
- private val scrollValue = ScrollValue()
- private val textInput = TextInput()
- private var nodesDisplay = emptyList<Searchable>()
- var lastUpdate = SimpleTimeMark.farPast()
-
@SubscribeEvent(priority = EventPriority.HIGHEST)
fun onRender(event: LorenzRenderWorldEvent) {
if (!isEnabled()) return
nodes.forEach { event.drawNode(it) }
edges.forEach { event.drawEdge(it) }
- ghostPosition?.let {
- event.drawWaypointFilled(
- it,
- if (activeNode == null) Color.RED else Color.GRAY,
- seeThroughBlocks = seeThroughBlocks,
- minimumAlpha = 0.2f,
- inverseAlphaScale = true,
- )
- }
+ drawGhostPosition(event)
+ }
+
+ private fun drawGhostPosition(event: LorenzRenderWorldEvent) {
+ val ghostPosition = ghostPosition ?: return
+ if (ghostPosition.distanceToPlayer() >= config.maxNodeDistance) return
+
+ event.drawWaypointFilled(
+ ghostPosition,
+ if (activeNode == null) Color.RED else Color.GRAY,
+ seeThroughBlocks = seeThroughBlocks,
+ minimumAlpha = 0.2f,
+ inverseAlphaScale = true,
+ )
}
@SubscribeEvent
@@ -188,136 +177,6 @@ object GraphEditor {
dissolvePossible = edges.count { it.isInEdge(activeNode) } == 2
}
- @SubscribeEvent
- fun onGuiRender(event: GuiRenderEvent) {
- if (!isEnabled()) return
-
-
- config.namedNodesList.renderRenderables(
- buildList {
- val list = getNodeNames()
- val size = list.size
- addString("§eGraph Nodes: $size")
- val height = (size * 10).coerceAtMost(250)
- if (list.isNotEmpty()) {
- add(list.buildSearchableScrollable(height, textInput, scrollValue, velocity = 10.0))
- }
- },
- posLabel = "Graph Nodes List",
- )
- }
-
- private fun getNodeNames(): List<Searchable> {
- if (lastUpdate.passedSince() > 250.milliseconds) {
- updateNodeNames()
- }
- return nodesDisplay
- }
-
- private fun updateNodeNames() {
- lastUpdate = SimpleTimeMark.now()
- nodesDisplay = drawNodeNames()
- }
-
- private fun updateTagView(node: GraphingNode) {
- lastUpdate = SimpleTimeMark.now() + 60.seconds
- nodesDisplay = drawTagNames(node)
- }
-
- private fun drawTagNames(node: GraphingNode): List<Searchable> = buildList {
- addSearchString("§eChange tag for node '${node.name}§e'")
- addSearchString("")
-
- for (tag in GraphNodeTag.entries) {
- val state = if (tag in node.tags) "§aYES" else "§cNO"
- val name = state + " §r" + tag.displayName
- add(createTagName(name, tag, node))
- }
- addSearchString("")
- add(
- Renderable.clickAndHover(
- "§cGo Back!",
- tips = listOf("§eClick to go back to the node list!"),
- onClick = {
- updateNodeNames()
- },
- ).toSearchable(),
- )
- }
-
- private fun createTagName(
- name: String,
- tag: GraphNodeTag,
- node: GraphingNode,
- ) = Renderable.clickAndHover(
- name,
- tips = listOf(
- "Tag ${tag.name}",
- "§7${tag.description}",
- "",
- "§eClick to set tag for ${node.name} to ${tag.name}!",
- ),
- onClick = {
- if (tag in node.tags) {
- node.tags.remove(tag)
- } else {
- node.tags.add(tag)
- }
- updateTagView(node)
- },
- ).toSearchable(name)
-
- private fun drawNodeNames(): List<Searchable> = buildList {
- for ((node, distance: Double) in nodes.map { it to it.position.distanceSqToPlayer() }.sortedBy { it.second }) {
- val name = node.name?.takeIf { !it.isBlank() } ?: continue
- val color = if (node == activeNode) "§a" else "§7"
- val distanceFormat = sqrt(distance).toInt().addSeparators()
- val tagText = node.tags.let {
- if (it.isEmpty()) {
- " §cNo tag§r"
- } else {
- val text = node.tags.map { it.internalName }.joinToString(", ")
- " §f($text)"
- }
- }
-
- val text = "${color}Node §r$name$tagText §7[$distanceFormat]"
- add(createNodeTextLine(text, name, node))
- }
- }
-
- private fun MutableList<Searchable>.createNodeTextLine(
- text: String,
- name: String,
- node: GraphingNode,
- ): Searchable = Renderable.clickAndHover(
- text,
- tips = buildList {
- add("Node '$name'")
- add("")
-
- if (node.tags.isNotEmpty()) {
- add("Tags: ")
- for (tag in node.tags) {
- add(" §8- §r${tag.displayName}")
- }
- add("")
- }
-
- add("§eClick to select/deselect this node!")
- add("§eControl-Click to edit the tags for this node!")
-
- },
- onClick = {
- if (KeyboardManager.isModifierKeyDown()) {
- updateTagView(node)
- } else {
- activeNode = node
- updateNodeNames()
- }
- },
- ).toSearchable(name)
-
private fun feedBackInTutorial(text: String) {
if (inTutorialMode) {
ChatUtils.chat(text)
@@ -333,6 +192,7 @@ object GraphEditor {
}
private fun LorenzRenderWorldEvent.drawNode(node: GraphingNode) {
+ if (node.position.distanceToPlayer() > config.maxNodeDistance) return
this.drawWaypointFilled(
node.position,
node.getNodeColor(),
@@ -368,17 +228,20 @@ object GraphEditor {
)
}
- private fun LorenzRenderWorldEvent.drawEdge(edge: GraphingEdge) = this.draw3DLine_nea(
- edge.node1.position.add(0.5, 0.5, 0.5),
- edge.node2.position.add(0.5, 0.5, 0.5),
- when {
- selectedEdge == edge -> edgeSelectedColor
- edge in highlightedEdges -> edgeDijkstraColor
- else -> edgeColor
- },
- 7,
- !seeThroughBlocks,
- )
+ private fun LorenzRenderWorldEvent.drawEdge(edge: GraphingEdge) {
+ if (edge.node1.position.distanceToPlayer() > config.maxNodeDistance) return
+ this.draw3DLine_nea(
+ edge.node1.position.add(0.5, 0.5, 0.5),
+ edge.node2.position.add(0.5, 0.5, 0.5),
+ when {
+ selectedEdge == edge -> edgeSelectedColor
+ edge in highlightedEdges -> edgeDijkstraColor
+ else -> edgeColor
+ },
+ 7,
+ !seeThroughBlocks,
+ )
+ }
private fun GraphingNode.getNodeColor() = when (this) {
activeNode -> if (this == closedNode) ColorUtils.blendRGB(activeColor, closedColor, 0.5) else activeColor
@@ -723,7 +586,7 @@ object GraphEditor {
}
// The node object the graph editor is working with
-private class GraphingNode(
+class GraphingNode(
val id: Int,
var position: LorenzVec,
var name: String? = null,
diff --git a/src/main/java/at/hannibal2/skyhanni/test/GraphNodeEditor.kt b/src/main/java/at/hannibal2/skyhanni/test/GraphNodeEditor.kt
new file mode 100644
index 000000000..f541b16e3
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/test/GraphNodeEditor.kt
@@ -0,0 +1,178 @@
+package at.hannibal2.skyhanni.test
+
+import at.hannibal2.skyhanni.data.model.GraphNodeTag
+import at.hannibal2.skyhanni.data.model.TextInput
+import at.hannibal2.skyhanni.events.GuiRenderEvent
+import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
+import at.hannibal2.skyhanni.test.GraphEditor.distanceSqToPlayer
+import at.hannibal2.skyhanni.utils.CollectionUtils.addString
+import at.hannibal2.skyhanni.utils.KeyboardManager
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
+import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables
+import at.hannibal2.skyhanni.utils.SimpleTimeMark
+import at.hannibal2.skyhanni.utils.renderables.Renderable
+import at.hannibal2.skyhanni.utils.renderables.ScrollValue
+import at.hannibal2.skyhanni.utils.renderables.Searchable
+import at.hannibal2.skyhanni.utils.renderables.buildSearchableScrollable
+import at.hannibal2.skyhanni.utils.renderables.toSearchable
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import kotlin.math.sqrt
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
+
+@SkyHanniModule
+object GraphNodeEditor {
+
+ private val scrollValueNodes = ScrollValue()
+ private val scrollValueTags = ScrollValue()
+ private val textInput = TextInput()
+ private var nodesDisplay = emptyList<Renderable>()
+ private var lastUpdate = SimpleTimeMark.farPast()
+
+ @SubscribeEvent
+ fun onGuiRender(event: GuiRenderEvent) {
+ if (!isEnabled()) return
+
+
+ config.namedNodesList.renderRenderables(
+ getNodeNames(),
+ posLabel = "Graph Nodes List",
+ )
+ }
+
+ private fun getNodeNames(): List<Renderable> {
+ if (lastUpdate.passedSince() > 250.milliseconds) {
+ updateNodeNames()
+ }
+ return nodesDisplay
+ }
+
+ private fun updateNodeNames() {
+ lastUpdate = SimpleTimeMark.now()
+ nodesDisplay = buildList {
+ val list = drawNodeNames()
+ val size = list.size
+ addString("§eGraph Nodes: $size")
+ val height = (size * 10).coerceAtMost(250)
+ if (list.isNotEmpty()) {
+ add(list.buildSearchableScrollable(height, textInput, scrollValueNodes, velocity = 10.0))
+ }
+ }
+ }
+
+ private fun updateTagView(node: GraphingNode) {
+ lastUpdate = SimpleTimeMark.now() + 60.seconds
+ nodesDisplay = buildList {
+ val list = drawTagNames(node)
+ val size = list.size
+ addString("§eGraph Nodes: $size")
+ val height = (size * 10).coerceAtMost(250)
+ if (list.isNotEmpty()) {
+ add(Renderable.scrollList(list, height, scrollValueTags, velocity = 10.0))
+ }
+ }
+ }
+
+ private fun drawTagNames(node: GraphingNode): List<Renderable> = buildList {
+ addString("§eChange tag for node '${node.name}§e'")
+ addString("")
+
+ for (tag in GraphNodeTag.entries.filter { it in node.tags || checkIsland(it) }) {
+ val state = if (tag in node.tags) "§aYES" else "§cNO"
+ val name = state + " §r" + tag.displayName
+ add(createTagName(name, tag, node))
+ }
+ addString("")
+ add(
+ Renderable.clickAndHover(
+ "§cGo Back!",
+ tips = listOf("§eClick to go back to the node list!"),
+ onClick = {
+ updateNodeNames()
+ },
+ ),
+ )
+ }
+
+ private fun checkIsland(tag: GraphNodeTag): Boolean = tag.onlyIsland?.let {
+ it == LorenzUtils.skyBlockIsland
+ } ?: true
+
+ private fun createTagName(
+ name: String,
+ tag: GraphNodeTag,
+ node: GraphingNode,
+ ) = Renderable.clickAndHover(
+ name,
+ tips = listOf(
+ "Tag ${tag.name}",
+ "§7${tag.description}",
+ "",
+ "§eClick to set tag for ${node.name} to ${tag.name}!",
+ ),
+ onClick = {
+ if (tag in node.tags) {
+ node.tags.remove(tag)
+ } else {
+ node.tags.add(tag)
+ }
+ updateTagView(node)
+ },
+ )
+
+ private fun drawNodeNames(): List<Searchable> = buildList {
+ for ((node, distance: Double) in GraphEditor.nodes.map { it to it.position.distanceSqToPlayer() }.sortedBy { it.second }) {
+ val name = node.name?.takeIf { !it.isBlank() } ?: continue
+ val color = if (node == GraphEditor.activeNode) "§a" else "§7"
+ val distanceFormat = sqrt(distance).toInt().addSeparators()
+ val tagText = node.tags.let { tags ->
+ if (tags.isEmpty()) {
+ " §cNo tag§r"
+ } else {
+ val text = node.tags.joinToString(", ") { it.internalName }
+ " §f($text)"
+ }
+ }
+
+ val text = "${color}Node §r$name$tagText §7[$distanceFormat]"
+ add(createNodeTextLine(text, name, node))
+ }
+ }
+
+ private fun createNodeTextLine(
+ text: String,
+ name: String,
+ node: GraphingNode,
+ ): Searchable = Renderable.clickAndHover(
+ text,
+ tips = buildList {
+ add("Node '$name'")
+ add("")
+
+ if (node.tags.isNotEmpty()) {
+ add("Tags: ")
+ for (tag in node.tags) {
+ add(" §8- §r${tag.displayName}")
+ }
+ add("")
+ }
+
+ add("§eClick to select/deselect this node!")
+ add("§eControl-Click to edit the tags for this node!")
+
+ },
+ onClick = {
+ if (KeyboardManager.isModifierKeyDown()) {
+ updateTagView(node)
+ } else {
+ GraphEditor.activeNode = node
+ updateNodeNames()
+ }
+ },
+ ).toSearchable(name)
+
+ fun isEnabled() = GraphEditor.isEnabled()
+ private val config get() = GraphEditor.config
+
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt
index 9e95df881..5b7333189 100644
--- a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt
+++ b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt
@@ -116,6 +116,7 @@ object SkyHanniDebugsAndTests {
if (args.isEmpty()) {
testLocation = null
ChatUtils.chat("reset test waypoint")
+ IslandGraphs.stop()
}
val x = args[0].toDouble()