Changed most of the doors in Draynor Manor to not autowalk

Implemented Draynor Manor chair NPCs (The ones that follow you around the house)
This commit is contained in:
Michael Veit 2025-08-18 11:08:56 +00:00 committed by Ryan
parent faf2222f4c
commit 89edf78e54
4 changed files with 208 additions and 0 deletions

View file

@ -1439,6 +1439,12 @@
"fence": "false",
"metal": "false"
},
{
"id": "11470",
"replaceId": "11471",
"fence": "false",
"metal": "false"
},
{
"id": "11483",
"replaceId": "11708",

View file

@ -6991,6 +6991,10 @@
"npc_id": "3288",
"loc_data": "{2987,3446,0,1,7}-{3034,3437,0,1,0}-"
},
{
"npc_id": "3293",
"loc_data": "{3113,3369,1,0,0}-{3122,3353,0,0,3}-{3124,3360,0,0,2}-{3115,3362,0,0,1}-"
},
{
"npc_id": "3294",
"loc_data": "{3014,3339,0,1,2}-"

View file

@ -0,0 +1,141 @@
package content.region.misthalin.draynor.handlers
import core.api.hasLineOfSight
import core.game.interaction.MovementPulse
import core.game.node.entity.Entity
import core.game.node.entity.impl.PulseType
import core.game.node.entity.npc.AbstractNPC
import core.game.node.entity.player.Player
import core.game.world.map.Location
import core.game.world.map.RegionManager
import core.game.world.map.path.Pathfinder
import core.plugin.Initializable
/**
* Represents the Draynor Manor Chair NPC, these are the chairs that follow the player
* around the manor.
*
* @author Broseki
*/
private const val DRAYNOR_MANOR_CHAIR_NPC_ID = 3293
private const val FOLLOWING_DISTANCE = 5
private const val STOP_FOLLOWING_DISTANCE = 30
@Initializable
class DraynorManorChairNPC(id: Int = DRAYNOR_MANOR_CHAIR_NPC_ID, location: Location? = null) :
AbstractNPC(id, location) {
// The player this NPC is currently following, null if nobody is being followed
private var following: Player? = null
override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC {
return DraynorManorChairNPC(id, location)
}
override fun handleTickActions() {
super.handleTickActions()
val closestPlayer = findClosestPlayer()
// If there is a player nearby, follow them
if (closestPlayer != null && following != closestPlayer) {
stopFollowing()
following = closestPlayer
follow(closestPlayer)
following?.let { face(it) }
} else {
// If we didn't find a player within `FOLLOWING_DISTANCE`
// but we are following a player, check that they are still
// within `STOP_FOLLOWING_DISTANCE`, if they are gone, stop
// trying to follow them.
following?.let { player ->
if (findDistanceToPlayer(player) > STOP_FOLLOWING_DISTANCE
|| !player.isActive
|| player.isInvisible) {
stopFollowing()
} else {
if (!pulseManager.hasPulseRunning()) {
follow(player)
}
face(player)
}
}
}
}
/**
* Stops following `following`
*/
fun stopFollowing() {
following = null
resetWalk()
pulseManager.clear(PulseType.STANDARD)
}
/**
* Finds the closest player to the current entity within `FOLLOWING_DISTANCE`
* that is currently in the chair's line of sight.
*
* @return The Player object representing the closest player, or null if there are no players nearby.
*/
fun findClosestPlayer(): Player? {
val players = RegionManager.getLocalPlayers(this, FOLLOWING_DISTANCE)
if (players.isEmpty()) {
return null
}
var closestPlayer: Player? = null
var closestDistance = Double.MAX_VALUE
for (player in players) {
// Make sure the chair does not try to start
// following a player in another room
if (!hasLineOfSight(this, player)) {
continue
}
// If the player is invisible, don't follow them
if (player.isInvisible) {
continue
}
val distance = findDistanceToPlayer(player)
if (distance < closestDistance) {
closestDistance = distance
closestPlayer = player
}
}
return closestPlayer
}
/**
* Calculates the distance between the current entity and the specified player.
*
* @param player The player whose distance from the current entity is to be calculated.
* @return The distance between the current entity and the specified player as a Double value.
*/
fun findDistanceToPlayer(player: Player): Double {
return this.location.getDistance(player.location)
}
/**
* Triggers the current entity to follow the specified player using a movement pulse.
*
* @param player The player that the entity should follow.
*/
fun follow(player: Player) {
pulseManager.run((object : MovementPulse(this, player, Pathfinder.DUMB) {
override fun pulse(): Boolean {
return false
}
}), PulseType.STANDARD)
}
override fun getIds(): IntArray {
return intArrayOf(DRAYNOR_MANOR_CHAIR_NPC_ID)
}
override fun shouldPreventStacking(mover: Entity?): Boolean {
return mover is DraynorManorChairNPC
}
}

View file

@ -0,0 +1,57 @@
package content.region.misthalin.draynor.handlers
import core.game.node.entity.Entity
import core.game.world.map.Location
import core.game.world.map.RegionManager.getLocalNpcs
import core.game.world.map.path.Pathfinder
import core.game.world.map.zone.MapZone
import core.game.world.map.zone.ZoneBorders
import core.game.world.map.zone.ZoneBuilder
import core.plugin.Initializable
import core.plugin.Plugin
/**
* Represents the interior of Draynor Manor.
*
* @author Broseki
*/
@Initializable
class DraynorManorHouseZone : MapZone("Draynor Manor House", true), Plugin<Any?> {
override fun newInstance(arg: Any?): Plugin<Any?> {
ZoneBuilder.configure(this)
return this
}
override fun move(e: Entity, loc: Location?, destination: Location): Boolean {
for (n in getLocalNpcs(e, 5)) {
if (n.isInvisible() || n === e) {
continue
}
if (n.shouldPreventStacking(e)) {
val s1 = e.size()
val s2 = n.size()
val x = destination.getX()
val y = destination.getY()
val l = n.getLocation()
if (Pathfinder.isStandingIn(x, y, s1, s1, l.getX(), l.getY(), s2, s2)) {
return false
}
}
}
return true
}
override fun enter(entity: Entity?): Boolean {
return super.enter(entity)
}
override fun fireEvent(identifier: String?, vararg args: Any?): Any? {
return null
}
override fun configure() {
register(ZoneBorders(3097, 3373, 3119, 3364))
register(ZoneBorders(3090, 3363, 3126, 3354))
}
}