Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion data/area/misthalin/lumbridge/lumbridge.npc-spawns.toml
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ spawns = [
{ id = "shop_assistant_lumbridge", x = 3214, y = 3243 },
{ id = "bob", x = 3228, y = 3203 },
{ id = "banker_3", x = 3208, y = 3222, level = 2, direction = "SOUTH" },
{ id = "hans", x = 3223, y = 3225 },
{ id = "hans", x = 3219, y = 3222 },
{ id = "giant_spider", x = 3249, y = 3249 },
{ id = "giant_spider", x = 3241, y = 3241 },
{ id = "giant_spider", x = 3250, y = 3239 },
Expand Down
16 changes: 16 additions & 0 deletions data/area/misthalin/lumbridge/lumbridge.patrols.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[hans]
defaultmode = "patrol"
patrol0 = "0_50_50_19_22,10"
patrol1 = "0_50_50_21_22,0"
patrol2 = "0_50_50_21_12,0"
patrol3 = "0_50_50_20_12,0"
patrol4 = "0_50_50_18_10,0"
patrol5 = "0_50_50_18_8,0"
patrol6 = "0_50_50_17_8,0"
patrol7 = "0_50_50_14_5,0"
patrol8 = "0_50_50_2_5,0"
patrol9 = "0_50_50_2_32,0"
patrol10 = "0_50_50_3_33,0"
patrol11 = "0_50_50_7_33,0"
patrol12 = "0_50_50_10_30,0"
patrol13 = "0_50_50_19_30,0"
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ class PatrolDefinitions {
}
points.add(Tile(x, y, level) to delay)
}
else -> throw IllegalArgumentException("Unexpected key: '$key' ${exception()}")
"defaultmode" -> string()
else -> if (key.startsWith("patrol")) {
points.add(parseLegacyPatrolPoint(string()))
} else {
throw IllegalArgumentException("Unexpected key: '$key' ${exception()}")
}
}
}
definitions[stringId] = PatrolDefinition(stringId = stringId, waypoints = points)
Expand All @@ -54,4 +59,17 @@ class PatrolDefinitions {
}
return this
}

private fun parseLegacyPatrolPoint(value: String): Pair<Tile, Int> {
val parts = value.split(",", limit = 2)
val coords = parts[0].split("_")
require(coords.size == 5) { "Unexpected patrol point value: '$value'" }
val level = coords[0].toInt()
val regionX = coords[1].toInt()
val regionY = coords[2].toInt()
val localX = coords[3].toInt()
val localY = coords[4].toInt()
val delay = parts.getOrNull(1)?.toIntOrNull() ?: 0
return Tile(regionX * 64 + localX, regionY * 64 + localY, level) to delay
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,46 @@ import content.entity.player.dialogue.Neutral
import content.entity.player.dialogue.type.choice
import content.entity.player.dialogue.type.npc
import world.gregs.voidps.engine.Script
import world.gregs.voidps.engine.client.ui.dialogue
import world.gregs.voidps.engine.data.definition.PatrolDefinitions
import world.gregs.voidps.engine.entity.character.mode.Patrol
import world.gregs.voidps.engine.entity.character.mode.Retreat
import world.gregs.voidps.engine.entity.character.npc.NPC
import world.gregs.voidps.engine.entity.character.player.Player
import world.gregs.voidps.engine.queue.queue

class Hans : Script {
class Hans(
private val patrols: PatrolDefinitions,
) : Script {

init {
npcSpawn("hans") {
val patrol = patrols.get("hans")
if (patrol.waypoints.isNotEmpty()) {
set("hans_patrol_index", 0)
mode = Patrol(this, patrol.waypoints)
}
}

npcMoved("hans") {
if (mode is Patrol) {
set("hans_patrol_index", get("patrol_index", 0))
}
}

npcOperate("Talk-to", "hans") { (target) ->
var resumeFromNearest = false
var resumeDelay = 10
scheduleResume(this, target, resumeDelay) { resumeFromNearest }
npc<Neutral>("Hello. What are you doing here?")
choice {
option<Neutral>("I'm looking for whoever is in charge of this place.") {
npc<Neutral>("Who, the Duke? He's in his study, on the first floor.")
}
option<EvilLaugh>("I have come to kill everyone in this castle!") {
resumeFromNearest = true
resumeDelay = 15
scheduleResume(this, target, resumeDelay) { resumeFromNearest }
target.say("Help! Help!")
target.mode = Retreat(target, this)
}
Expand All @@ -27,4 +55,34 @@ class Hans : Script {
}
}
}

private fun scheduleResume(player: Player, target: NPC, delay: Int, nearest: () -> Boolean) {
target.queue.clear("hans_resume_patrol")
target.queue("hans_resume_patrol", delay) {
if (player.dialogue != null) {
scheduleResume(player, target, 1, nearest)
return@queue
}
resumePatrol(target, nearest = nearest())
}
}

private fun resumePatrol(target: NPC, nearest: Boolean = false) {
val patrol = patrols.get("hans")
if (patrol.waypoints.isEmpty()) {
return
}
val size = patrol.waypoints.size
val baseIndex = if (nearest) {
patrol.waypoints.withIndex()
.minByOrNull { target.tile.distanceTo(it.value.first) }
?.index ?: 0
} else {
target["hans_patrol_index", 0].mod(size)
}
val resumeIndex = if (patrol.waypoints[baseIndex].first == target.tile) (baseIndex + 1).mod(size) else baseIndex
target.mode = Patrol(target, patrol.waypoints)
target.set("patrol_index", resumeIndex)
target.steps.clear()
}
}