diff options
author | hannibal2 <24389977+hannibal002@users.noreply.github.com> | 2024-09-17 10:28:38 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-17 10:28:38 +0200 |
commit | 1dcc43a3f37591912f086299c298df4e0e471bd9 (patch) | |
tree | 1f1682f7ad74848a7569d0e237fb482f73cdf68a /src/main/java/at/hannibal2/skyhanni | |
parent | d1bb73a6826b4c56694a47b5811980ec4cdc90af (diff) | |
download | skyhanni-1dcc43a3f37591912f086299c298df4e0e471bd9.tar.gz skyhanni-1dcc43a3f37591912f086299c298df4e0e471bd9.tar.bz2 skyhanni-1dcc43a3f37591912f086299c298df4e0e471bd9.zip |
Improvement: More Graph Things (#2515)
Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com>
Diffstat (limited to 'src/main/java/at/hannibal2/skyhanni')
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() |