From d0345f39230e4f07f061b6c82a381c863e9787d8 Mon Sep 17 00:00:00 2001 From: Thunderblade73 <85900443+Thunderblade73@users.noreply.github.com> Date: Tue, 7 May 2024 22:43:17 +0200 Subject: Feature: Tunnels maps (#1546) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../hannibal2/skyhanni/data/EntityMovementData.kt | 23 +++ .../java/at/hannibal2/skyhanni/data/model/Graph.kt | 180 +++++++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 src/main/java/at/hannibal2/skyhanni/data/model/Graph.kt (limited to 'src/main/java/at/hannibal2/skyhanni/data') diff --git a/src/main/java/at/hannibal2/skyhanni/data/EntityMovementData.kt b/src/main/java/at/hannibal2/skyhanni/data/EntityMovementData.kt index ae5a03878..97039022a 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/EntityMovementData.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/EntityMovementData.kt @@ -1,11 +1,16 @@ package at.hannibal2.skyhanni.data import at.hannibal2.skyhanni.events.EntityMoveEvent +import at.hannibal2.skyhanni.events.LorenzChatEvent import at.hannibal2.skyhanni.events.LorenzTickEvent +import at.hannibal2.skyhanni.events.LorenzWarpEvent import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent +import at.hannibal2.skyhanni.utils.DelayedRun import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzVec +import at.hannibal2.skyhanni.utils.StringUtils.matches import at.hannibal2.skyhanni.utils.getLorenzVec +import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern import net.minecraft.client.Minecraft import net.minecraft.entity.Entity import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @@ -41,6 +46,24 @@ class EntityMovementData { } } + private val warpingPattern by RepoPattern.pattern( + "warping", + "§7Warping...|" + + "§7Warping you to your SkyBlock island...|" + + "§7Warping using transfer token...|" + + "§7Finding player...|" + + "§7Sending a visit request..." + ) + + @SubscribeEvent + fun onChat(event: LorenzChatEvent) { + if (!LorenzUtils.inSkyBlock) return + if (!warpingPattern.matches(event.message)) return + DelayedRun.runNextTick { + LorenzWarpEvent().postAndCatch() + } + } + @SubscribeEvent fun onWorldChange(event: LorenzWorldChangeEvent) { entityLocation.clear() diff --git a/src/main/java/at/hannibal2/skyhanni/data/model/Graph.kt b/src/main/java/at/hannibal2/skyhanni/data/model/Graph.kt new file mode 100644 index 000000000..b6e6f7b8e --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/data/model/Graph.kt @@ -0,0 +1,180 @@ +package at.hannibal2.skyhanni.data.model + +import at.hannibal2.skyhanni.config.ConfigManager.Companion.registerTypeAdapter +import at.hannibal2.skyhanni.utils.LorenzVec +import at.hannibal2.skyhanni.utils.fromJson +import com.google.gson.GsonBuilder +import com.google.gson.JsonElement +import com.google.gson.annotations.Expose +import java.util.PriorityQueue + +@JvmInline +value class Graph( + @Expose + val graph: List, +) : List { + override val size + get() = graph.size + + override fun contains(element: GraphNode) = graph.contains(element) + + override fun containsAll(elements: Collection) = graph.containsAll(elements) + + override fun get(index: Int) = graph.get(index) + + override fun isEmpty() = graph.isEmpty() + + override fun indexOf(element: GraphNode) = graph.indexOf(element) + + override fun iterator(): Iterator = graph.iterator() + override fun listIterator() = graph.listIterator() + + override fun listIterator(index: Int) = graph.listIterator(index) + + override fun subList(fromIndex: Int, toIndex: Int) = graph.subList(fromIndex, toIndex) + + override fun lastIndexOf(element: GraphNode) = graph.lastIndexOf(element) + + companion object { + val gson = GsonBuilder().setPrettyPrinting() + /* ConfigManager.createBaseGsonBuilder() */.registerTypeAdapter({ out, value -> + out.beginObject() + value.forEach { + out.name(it.id.toString()).beginObject() + out.name("Position").value(with(it.position) { "$x:$y:$z" }) + if (it.name != null) { + out.name("Name").value(it.name) + } + out.name("Neighbours") + out.beginObject() + it.neighbours.forEach { (node, weight) -> + val id = node.id.toString() + out.name(id).value(weight) + } + out.endObject() + out.endObject() + } + out.endObject() + }, { reader -> + reader.beginObject() + val list = mutableListOf() + val neigbourMap = mutableMapOf>>() + while (reader.hasNext()) { + val id = reader.nextName().toInt() + reader.beginObject() + var position: LorenzVec? = null + var name: String? = null + var neighbors = mutableListOf>() + while (reader.hasNext()) { + when (reader.nextName()) { + "Position" -> { + position = reader.nextString().split(":").let { parts -> + LorenzVec(parts[0].toDouble(), parts[1].toDouble(), parts[2].toDouble()) + } + } + + "Neighbours" -> { + reader.beginObject() + while (reader.hasNext()) { + val nId = reader.nextName().toInt() + val distance = reader.nextDouble() + neighbors.add(nId to distance) + } + reader.endObject() + } + + "Name" -> { + name = reader.nextString() + } + + } + } + val node = GraphNode(id, position!!, name) + list.add(node) + neigbourMap[node] = neighbors + reader.endObject() + } + neigbourMap.forEach { (node, edge) -> + node.neighbours = edge.associate { (id, distance) -> + list.first { it.id == id } to distance + } + } + reader.endObject() + Graph(list) + }).create() + + fun fromJson(json: String): Graph = gson.fromJson(json) + fun fromJson(json: JsonElement): Graph = gson.fromJson(json) + } +} + +class GraphNode(val id: Int, val position: LorenzVec, val name: String? = null) { + + /** Keys are the neighbours and value the edge weight (e.g. Distance) */ + lateinit var neighbours: Map + + override fun hashCode(): Int { + return id + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as GraphNode + + if (id != other.id) return false + + return true + } +} + +fun Graph.findShortestPathAsGraph(start: GraphNode, end: GraphNode): Graph = + this.findShortestPathAsGraphWithDistance(start, end).first + +fun Graph.findShortestPathAsGraphWithDistance(start: GraphNode, end: GraphNode): Pair { + val distances = mutableMapOf() + val previous = mutableMapOf() + val visited = mutableSetOf() + val queue = PriorityQueue(compareBy { distances.getOrDefault(it, Double.MAX_VALUE) }) + + distances[start] = 0.0 + queue.add(start) + + while (queue.isNotEmpty()) { + val current = queue.poll() + if (current == end) break + + visited.add(current) + + current.neighbours.forEach { (neighbour, weight) -> + if (neighbour !in visited) { + val newDistance = distances.getValue(current) + weight + if (newDistance < distances.getOrDefault(neighbour, Double.MAX_VALUE)) { + distances[neighbour] = newDistance + previous[neighbour] = current + queue.add(neighbour) + } + } + } + } + + return Graph(buildList { + var current = end + while (current != start) { + add(current) + current = previous[current] ?: return Graph(emptyList()) to 0.0 + } + add(start) + }.reversed()) to distances[end]!! +} + +fun Graph.findShortestPath(start: GraphNode, end: GraphNode): List = + this.findShortestPathAsGraph(start, end).toPositionsList() + +fun Graph.findShortestDistance(start: GraphNode, end: GraphNode): Double = + this.findShortestPathAsGraphWithDistance(start, end).second + +fun Graph.toPositionsList() = this.map { it.position } + +fun Graph.toJson(): String = Graph.gson.toJson(this) -- cgit