Skip to content

Commit

Permalink
Improvement: Pathfind Rendering (hannibal002#2634)
Browse files Browse the repository at this point in the history
Co-authored-by: hannibal2 <[email protected]>
  • Loading branch information
hannibal002 and hannibal002 authored Oct 2, 2024
1 parent 0864f1e commit ab7b326
Show file tree
Hide file tree
Showing 10 changed files with 136 additions and 176 deletions.
180 changes: 68 additions & 112 deletions src/main/java/at/hannibal2/skyhanni/data/IslandGraphs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import at.hannibal2.skyhanni.utils.CollectionUtils.sorted
import at.hannibal2.skyhanni.utils.DelayedRun
import at.hannibal2.skyhanni.utils.GraphUtils
import at.hannibal2.skyhanni.utils.LocationUtils
import at.hannibal2.skyhanni.utils.LocationUtils.canBeSeen
import at.hannibal2.skyhanni.utils.LocationUtils.distanceSqToPlayer
import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer
import at.hannibal2.skyhanni.utils.LorenzColor
Expand All @@ -39,7 +38,6 @@ import net.minecraft.client.Minecraft
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import java.awt.Color
import java.io.File
import kotlin.math.abs
import kotlin.time.Duration.Companion.milliseconds

/**
Expand Down Expand Up @@ -104,18 +102,17 @@ object IslandGraphs {

val existsForThisIsland get() = currentIslandGraph != null

var closedNote: GraphNode? = null
var secondClosedNote: GraphNode? = null
private var pathfindClosestNode: GraphNode? = null
var closestNode: GraphNode? = null
private var secondClosestNode: GraphNode? = null

private var currentTarget: LorenzVec? = null
private var currentTargetNode: GraphNode? = null
private var label = ""
private var distanceViaNodes = 0.0
private var distanceToNextNode = 0.0
private var lastDistance = 0.0
private var totalDistance = 0.0
private var color = Color.WHITE
private var shouldAllowRerouting = false
private var showGoalExact = false
private var onFound: () -> Unit = {}
private var goal: GraphNode? = null
set(value) {
Expand Down Expand Up @@ -215,28 +212,29 @@ object IslandGraphs {

// calling various update functions to make swtiching between deep caverns and glacite tunnels bareable
handleTick()
IslandAreas.noteMoved()
IslandAreas.nodeMoved()
DelayedRun.runDelayed(150.milliseconds) {
IslandAreas.updatePosition()
}
}

private fun reset() {
stop()
closedNote = null
pathfindClosestNode = null
closestNode = null
}

@SubscribeEvent
fun onTick(event: LorenzTickEvent) {
if (!LorenzUtils.inSkyBlock) return
handleTick()
if (event.isMod(2)) {
handleTick()
checkMoved()
}
}

private fun handleTick() {
val prevClosed = closedNote
val prevClosest = pathfindClosestNode

currentTarget?.let {
if (it.distanceToPlayer() < 3) {
Expand All @@ -252,39 +250,46 @@ object IslandGraphs {
val graph = currentIslandGraph ?: return
val sortedNodes = graph.sortedBy { it.position.distanceSqToPlayer() }
val newClosest = sortedNodes.first()
if (closedNote == newClosest) return
if (onCurrentPath()) return

closedNote = newClosest
secondClosedNote = sortedNodes.getOrNull(1)
onNewNote()
hasMoved = false
if (newClosest == prevClosed) return
findNewPath()
if (pathfindClosestNode == newClosest) return
val newPath = !onCurrentPath()

closestNode = newClosest
secondClosestNode = sortedNodes.getOrNull(1)
onNewNode()
if (newClosest == prevClosest) return
if (newPath) {
pathfindClosestNode = closestNode
findNewPath()
}
}

private fun onCurrentPath(): Boolean {
val path = fastestPath ?: return false
val closest = path.nodes.minBy { it.position.distanceSqToPlayer() }
val distance = closest.position.distanceToPlayer()
if (distance > 5) return false

if (distance < 3) {
val index = path.nodes.indexOf(closest)
val newNodes = path.drop(index)
val newGraph = Graph(newNodes)
fastestPath = newGraph
newNodes.getOrNull(1)?.let {
secondClosedNote = it
}
setFastestPath(newGraph to newGraph.totalLenght(), setPath = false)
if (distance > 7) return false

val index = path.nodes.indexOf(closest)
val newNodes = path.drop(index)
val newGraph = Graph(newNodes)
fastestPath = skipIfCloser(newGraph)
newNodes.getOrNull(1)?.let {
secondClosestNode = it
}
setFastestPath(newGraph to newGraph.totalLenght(), setPath = false)
return true
}

private fun skipIfCloser(graph: Graph): Graph = if (graph.nodes.size > 1) {
val hideNearby = if (Minecraft.getMinecraft().thePlayer.onGround) 3 else 5
Graph(graph.nodes.takeLastWhile { it.position.distanceToPlayer() > hideNearby })
} else {
graph
}

private fun findNewPath() {
val goal = IslandGraphs.goal ?: return
val closest = closedNote ?: return
val closest = pathfindClosestNode ?: return

val (path, distance) = GraphUtils.findShortestPathAsGraphWithDistance(closest, goal)
val first = path.firstOrNull()
Expand All @@ -307,8 +312,6 @@ object IslandGraphs {
private fun Graph.totalLenght(): Double = nodes.zipWithNext().sumOf { (a, b) -> a.position.distance(b.position) }

private fun handlePositionChange() {
val secondClosestNode = secondClosedNote ?: return
distanceToNextNode = secondClosestNode.position.distanceToPlayer()
updateChat()
}

Expand All @@ -331,38 +334,34 @@ object IslandGraphs {
}

private fun setFastestPath(path: Pair<Graph, Double>, setPath: Boolean = true) {
// TODO cleanup
val (fastestPath, distance) = path.takeIf { it.first.isNotEmpty() } ?: return
val nodes = fastestPath.nodes.toMutableList()
if (Minecraft.getMinecraft().thePlayer.onGround) {
nodes.add(0, GraphNode(0, LocationUtils.playerLocation()))
}
if (setPath) {
this.fastestPath = Graph(cutByMaxDistance(nodes, 3.0))
this.fastestPath = skipIfCloser(Graph(cutByMaxDistance(nodes, 2.0)))
}

val diff = fastestPath.getOrNull(1)?.let {
fastestPath.first().position.distance(it.position)
} ?: 0.0
this.distanceViaNodes = distance - diff
updateChat()
}

private fun onNewNote() {
private fun onNewNode() {
// TODO create an event
IslandAreas.noteMoved()
IslandAreas.nodeMoved()
if (shouldAllowRerouting) {
tryRerouting()
}
}

private fun tryRerouting() {
val target = currentTargetNode ?: return
val closest = closedNote ?: return
val closest = pathfindClosestNode ?: return
val map = GraphUtils.findAllShortestDistances(closest).distances.filter { it.key.sameNameAndTags(target) }
val newTarget = map.sorted().keys.firstOrNull() ?: return
if (newTarget != target) {
ChatUtils.debug("Rerouting navigation..")
newTarget.pathFind(label, color, onFound, allowRerouting = true, condition)
newTarget.pathFind(label, color, onFound, allowRerouting = true, condition = condition)
}
}

Expand All @@ -372,9 +371,8 @@ object IslandGraphs {
fastestPath = null
currentTargetNode = null
label = ""
distanceToNextNode = 0.0
distanceViaNodes = 0.0
totalDistance = 0.0
lastDistance = 0.0
}

/**
Expand All @@ -396,7 +394,7 @@ object IslandGraphs {
reset()
currentTargetNode = this
shouldAllowRerouting = allowRerouting
pathFind0(location = position, label, color, onFound, showGoalExact = false, condition)
pathFind0(location = position, label, color, onFound, condition)
}

/**
Expand All @@ -406,35 +404,31 @@ object IslandGraphs {
* @param label The name of the naviation goal in chat.
* @param color The color of the lines in world.
* @param onFound The callback that gets fired when the goal is reached.
* @param showGoalExact Wether the exact location should be shown as a waypoint, as well as shwoing a line from last node to the goal location.
* @param condition The pathfinding stops when the condition is no longer valid.
*/
fun pathFind(
location: LorenzVec,
label: String,
color: Color = LorenzColor.WHITE.toColor(),
onFound: () -> Unit = {},
showGoalExact: Boolean = false,
condition: () -> Boolean,
) {
reset()
shouldAllowRerouting = false
pathFind0(location, label, color, onFound, showGoalExact, condition)
pathFind0(location, label, color, onFound, condition)
}

private fun pathFind0(
location: LorenzVec,
label: String,
color: Color = LorenzColor.WHITE.toColor(),
onFound: () -> Unit = {},
showGoalExact: Boolean = false,
condition: () -> Boolean,
) {
currentTarget = location
this.label = label
this.color = color
this.onFound = onFound
this.showGoalExact = showGoalExact
this.condition = condition
val graph = currentIslandGraph ?: return
goal = graph.minBy { it.position.distance(currentTarget!!) }
Expand All @@ -445,9 +439,18 @@ object IslandGraphs {

private fun updateChat() {
if (label == "") return
val finalDistance = distanceViaNodes + distanceToNextNode
if (finalDistance == 0.0) return
val distance = finalDistance.roundTo(1)
val path = fastestPath ?: return
var distance = 0.0
for ((a, b) in path.zipWithNext()) {
distance += a.position.distance(b.position)
}
val distanceToPlayer = path.first().position.distanceToPlayer()
distance += distanceToPlayer
distance = distance.roundTo(1)

if (distance == lastDistance) return
lastDistance = distance
if (distance == 0.0) return
if (totalDistance == 0.0 || distance > totalDistance) {
totalDistance = distance
}
Expand All @@ -470,29 +473,25 @@ object IslandGraphs {
@SubscribeEvent
fun onRenderWorld(event: LorenzRenderWorldEvent) {
if (!LorenzUtils.inSkyBlock) return
var path = fastestPath ?: return

if (path.nodes.size > 1) {
val hideNearby = if (Minecraft.getMinecraft().thePlayer.onGround) 5 else 7
path = Graph(path.nodes.takeLastWhile { it.position.distanceToPlayer() > hideNearby })
}
// graph = skipNodes(graph) ?: graph
val path = fastestPath ?: return

// maybe reuse for debuggin
// for ((a, b) in path.nodes.zipWithNext()) {
// val diff = a.position.distance(b.position)
// event.drawString(a.position, "diff: ${diff.roundTo(1)}")
// }
event.draw3DPathWithWaypoint(
path,
color,
6,
true,
bezierPoint = 2.0,
bezierPoint = 0.6,
textSize = 1.0,
markLastBlock = showGoalExact,
)

if (showGoalExact) {
val targetLocation = currentTarget ?: return
val lastNode = path.nodes.last().position
event.draw3DLine(lastNode.add(0.5, 0.5, 0.5), targetLocation.add(0.5, 0.5, 0.5), color, 4, true)
}
val targetLocation = currentTarget ?: return
val lastNode = path.nodes.lastOrNull()?.position ?: return
event.draw3DLine(lastNode.add(0.5, 0.5, 0.5), targetLocation.add(0.5, 0.5, 0.5), color, 4, true)
}

// TODO move into new utils class
Expand Down Expand Up @@ -524,47 +523,4 @@ object IslandGraphs {

return locations.map { GraphNode(index++, it) }
}

// trying to find a faster node-path, if the future nodes are in line of sight and gratly beneift the current path
private fun skipNodes(graph: Graph): Graph? {
val closedNode = closedNote ?: return null

val playerEyeLocation = LocationUtils.playerEyeLocation()
val playerY = playerEyeLocation.y - 1

val distanceToPlayer = closedNode.position.distanceToPlayer()
val skipNodeDistance = distanceToPlayer > 8
val maxSkipDistance = if (skipNodeDistance) 50.0 else 20.0

val nodes = graph.nodes
val potentialSkip =
nodes.lastOrNull { it.position.canBeSeen(maxSkipDistance, -1.0) && abs(it.position.y - playerY) <= 2 } ?: return null

val angleSkip = if (potentialSkip == nodes.first()) {
false
} else {
val v1 = potentialSkip.position - playerEyeLocation
val v2 = nodes.first().position - playerEyeLocation
val v = v1.angleInRad(v2)
v > 1
}

if (!skipNodeDistance && !angleSkip) return null

val list = mutableListOf<GraphNode>()
list.add(potentialSkip)

var passed = false
for (node in nodes) {
if (passed) {
list.add(node)
} else {
if (node == potentialSkip) {
passed = true
}
}
}

return Graph(list)
}
}
Loading

0 comments on commit ab7b326

Please sign in to comment.