You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
265 lines
5.6 KiB
Kotlin
265 lines
5.6 KiB
Kotlin
package net.idylls.pathos
|
|
|
|
import java.awt.Graphics2D
|
|
import java.awt.Dimension
|
|
import java.awt.Color
|
|
import java.awt.BasicStroke
|
|
import javax.inject.Inject
|
|
|
|
import net.runelite.api.Client
|
|
import net.runelite.api.Perspective
|
|
import net.runelite.api.Point
|
|
|
|
import net.runelite.client.ui.overlay.Overlay
|
|
import net.runelite.client.ui.overlay.OverlayLayer
|
|
import net.runelite.client.ui.overlay.OverlayPosition
|
|
import net.runelite.client.ui.overlay.OverlayPriority
|
|
import net.runelite.client.ui.overlay.OverlayUtil
|
|
import net.runelite.api.coords.LocalPoint
|
|
|
|
import kotlin.math.min
|
|
|
|
/**
|
|
* Draws an overlay based on the Old School RuneScape pathfinding
|
|
* algorithm. More information can be found here:
|
|
* https://oldschool.runescape.wiki/w/Pathfinding
|
|
*/
|
|
|
|
const val SCENE_WIDTH = 128
|
|
const val SCENE_BUF_LEN = SCENE_WIDTH * SCENE_WIDTH
|
|
|
|
|
|
class Overlay
|
|
@Inject constructor(
|
|
val client: Client,
|
|
val config: PathosConfig,
|
|
): net.runelite.client.ui.overlay.Overlay() {
|
|
init {
|
|
this.position = OverlayPosition.DYNAMIC
|
|
this.layer = OverlayLayer.ABOVE_SCENE
|
|
this.priority = OverlayPriority.MED
|
|
}
|
|
|
|
override public fun render(gfx: Graphics2D): Dimension? {
|
|
val player = client.getLocalPlayer()
|
|
if (player == null) {
|
|
return null
|
|
}
|
|
val z = client.plane
|
|
|
|
val collisions = client.collisionMaps
|
|
if (collisions == null) {
|
|
return null
|
|
}
|
|
|
|
val _playerTrueTile = LocalPoint.fromWorld(client, player.worldLocation)
|
|
if (_playerTrueTile == null) {
|
|
return null
|
|
}
|
|
val playerTrueTile = ScenePoint(_playerTrueTile.sceneX, _playerTrueTile.sceneY)
|
|
|
|
val planeCollisions = collisions[z]
|
|
val collisionFlags = planeCollisions.flags
|
|
val tiles = client.scene.tiles[z]
|
|
|
|
val _hoveredTile = client.selectedSceneTile
|
|
val hoveredTarget = getHoveredTarget(client)
|
|
|
|
val hoveredPathTiles = if (
|
|
config.highlightHoveredPath()
|
|
&& hoveredTarget != null
|
|
) {
|
|
val requestedTiles = hoveredTarget.getRequestedTiles()
|
|
|
|
findShortestPathAll(
|
|
playerTrueTile,
|
|
requestedTiles,
|
|
collisionFlags,
|
|
tiles,
|
|
)
|
|
} else {
|
|
null
|
|
}
|
|
|
|
val destinationPoint = client.getLocalDestinationLocation()
|
|
val destinationPathTiles = if (
|
|
config.highlightDestinationPath()
|
|
&& destinationPoint != null
|
|
) {
|
|
val destinationTile = ScenePoint(
|
|
destinationPoint.sceneX,
|
|
destinationPoint.sceneY,
|
|
)
|
|
|
|
findPath(
|
|
playerTrueTile,
|
|
destinationTile,
|
|
collisionFlags,
|
|
tiles,
|
|
)
|
|
} else {
|
|
null
|
|
}
|
|
|
|
val overlappingTiles = when (config.highlightOverlapping()) {
|
|
true -> {
|
|
if (
|
|
(destinationPathTiles == null)
|
|
|| (hoveredPathTiles == null)
|
|
) {
|
|
null
|
|
} else {
|
|
destinationPathTiles
|
|
.toSet()
|
|
.intersect(hoveredPathTiles.toSet())
|
|
}
|
|
}
|
|
false -> null
|
|
}
|
|
|
|
if (this.isRunning()) {
|
|
doPaint(
|
|
gfx,
|
|
destinationPathTiles?.let({ runningPath(it) }),
|
|
hoveredPathTiles?.let({ runningPath(it) }),
|
|
overlappingTiles,
|
|
)
|
|
} else {
|
|
doPaint(
|
|
gfx,
|
|
destinationPathTiles,
|
|
hoveredPathTiles,
|
|
overlappingTiles,
|
|
)
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
fun doPaint(
|
|
gfx: Graphics2D,
|
|
destinationPathTiles: Iterable<ScenePoint>?,
|
|
hoveredPathTiles: Iterable<ScenePoint>?,
|
|
overlappingTiles: Set<ScenePoint>?,
|
|
) {
|
|
if (overlappingTiles != null) {
|
|
val ot = destinationPathTiles!!.filter({t ->
|
|
overlappingTiles.contains(t)
|
|
})
|
|
val dpt = destinationPathTiles.filter({t ->
|
|
!overlappingTiles.contains(t)
|
|
})
|
|
val hpt = hoveredPathTiles!!.filter({t ->
|
|
!overlappingTiles.contains(t)
|
|
})
|
|
|
|
paintTiles(
|
|
gfx,
|
|
ot,
|
|
config.highlightOverlappingBorder(),
|
|
config.highlightOverlappingBorderWidth(),
|
|
)
|
|
paintTiles(
|
|
gfx,
|
|
dpt,
|
|
config.highlightDestinationBorder(),
|
|
config.highlightDestinationBorderWidth(),
|
|
)
|
|
paintTiles(
|
|
gfx,
|
|
hpt,
|
|
config.highlightHoveredBorder(),
|
|
config.highlightHoveredBorderWidth(),
|
|
)
|
|
|
|
if (config.drawTickDistance()) {
|
|
paintTicks(gfx, ot)
|
|
paintTicks(gfx, hpt, ot.size)
|
|
paintTicks(gfx, dpt, ot.size)
|
|
}
|
|
} else {
|
|
if (destinationPathTiles != null) {
|
|
paintTiles(
|
|
gfx,
|
|
destinationPathTiles,
|
|
config.highlightDestinationBorder(),
|
|
config.highlightDestinationBorderWidth(),
|
|
)
|
|
|
|
if (config.drawTickDistance()) {
|
|
paintTicks(gfx, destinationPathTiles)
|
|
}
|
|
}
|
|
|
|
if (hoveredPathTiles != null) {
|
|
paintTiles(
|
|
gfx,
|
|
hoveredPathTiles,
|
|
config.highlightHoveredBorder(),
|
|
config.highlightHoveredBorderWidth(),
|
|
)
|
|
|
|
if (config.drawTickDistance()) {
|
|
paintTicks(gfx, hoveredPathTiles)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fun paintTiles(
|
|
gfx: Graphics2D,
|
|
tiles: Iterable<ScenePoint>,
|
|
color: Color,
|
|
borderWidth: Double,
|
|
) {
|
|
for (tile in tiles) {
|
|
renderTile(gfx, tile, color, borderWidth)
|
|
}
|
|
}
|
|
|
|
fun renderTile(
|
|
gfx: Graphics2D,
|
|
tile: ScenePoint,
|
|
borderColor: Color,
|
|
borderWidth: Double,
|
|
) {
|
|
val local = LocalPoint.fromScene(tile.getX(), tile.getY())
|
|
val poly = Perspective.getCanvasTilePoly(client, local)
|
|
|
|
OverlayUtil.renderPolygon(gfx, poly, borderColor, Color(0, 0, 0, 0), BasicStroke(borderWidth.toFloat()))
|
|
}
|
|
|
|
fun paintTicks(
|
|
gfx: Graphics2D,
|
|
tiles: Iterable<ScenePoint>,
|
|
offset: Int = 0,
|
|
) {
|
|
for (tile in tiles.withIndex()) {
|
|
renderTick(gfx, tile.value, tile.index + offset)
|
|
}
|
|
}
|
|
|
|
fun renderTick(
|
|
gfx: Graphics2D,
|
|
tile: ScenePoint,
|
|
tick: Int,
|
|
) {
|
|
val lp = LocalPoint.fromScene(tile.getX(), tile.getY())
|
|
val str = "${tick}"
|
|
val loc = Perspective.getCanvasTextLocation(client, gfx, lp, str, 0)
|
|
if (loc == null) {
|
|
return
|
|
}
|
|
|
|
OverlayUtil.renderTextLocation(gfx, loc, str, Color(255, 255, 255))
|
|
}
|
|
|
|
fun isRunning(): Boolean {
|
|
val willRun = client.getVarpValue(173) == 1
|
|
// TODO: handle ctrl-clicks
|
|
// TODO: handle other conditions
|
|
|
|
return willRun
|
|
}
|
|
}
|