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

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
}
}