Refactored movement pulse
Fixed bug with Vinesweeper gnomes not checking flags Fixed hunter tracking
This commit is contained in:
@@ -7,12 +7,15 @@ import org.rs09.consts.NPCs
|
|||||||
import core.game.dialogue.DialogueFile
|
import core.game.dialogue.DialogueFile
|
||||||
import core.game.interaction.InteractionListener
|
import core.game.interaction.InteractionListener
|
||||||
import core.game.interaction.IntType
|
import core.game.interaction.IntType
|
||||||
|
import core.game.world.update.flag.context.Animation
|
||||||
import core.tools.END_DIALOGUE
|
import core.tools.END_DIALOGUE
|
||||||
|
import core.api.*
|
||||||
|
|
||||||
class MistagEasterEgg : InteractionListener {
|
class MistagEasterEgg : InteractionListener {
|
||||||
val DIAMOND = Items.DIAMOND_1601
|
val DIAMOND = Items.DIAMOND_1601
|
||||||
val MISTAG = NPCs.MISTAG_2084
|
val MISTAG = NPCs.MISTAG_2084
|
||||||
val ZANIK_RING = 14649
|
val ZANIK_RING = 14649
|
||||||
|
val DRUNK_RENDER = 982
|
||||||
|
|
||||||
override fun defineListeners() {
|
override fun defineListeners() {
|
||||||
onUseWith(IntType.NPC,DIAMOND,MISTAG){ player, _, with ->
|
onUseWith(IntType.NPC,DIAMOND,MISTAG){ player, _, with ->
|
||||||
@@ -30,6 +33,18 @@ class MistagEasterEgg : InteractionListener {
|
|||||||
player.appearance.transformNPC(-1)
|
player.appearance.transformNPC(-1)
|
||||||
return@onUnequip true
|
return@onUnequip true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onEquip(Items.BEER_1917){player, _ ->
|
||||||
|
setAttribute(player, "render-anim-override", DRUNK_RENDER)
|
||||||
|
return@onEquip true
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnequip(Items.BEER_1917){player, _ ->
|
||||||
|
removeAttribute(player, "render-anim-override")
|
||||||
|
player.appearance.setDefaultAnimations()
|
||||||
|
player.appearance.sync()
|
||||||
|
return@onUnequip true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,4 +70,4 @@ class MistagEasterEggDialogue(val hasRing: Boolean): DialogueFile(){
|
|||||||
override fun npc(vararg messages: String?): Component? {
|
override fun npc(vararg messages: String?): Component? {
|
||||||
return super.npc(core.game.dialogue.FacialExpression.OLD_HAPPY,*messages)
|
return super.npc(core.game.dialogue.FacialExpression.OLD_HAPPY,*messages)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ abstract class HunterTracking : OptionHandler(){
|
|||||||
trail.get(currentIndex)
|
trail.get(currentIndex)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return false
|
TrailDefinition(0,TrailType.LINKING,false,Location(0,0,0),Location(0,0,0),Location(0,0,0))
|
||||||
}
|
}
|
||||||
when(option){
|
when(option){
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import core.api.*
|
|||||||
import core.tools.SystemLogger
|
import core.tools.SystemLogger
|
||||||
import core.game.system.command.Privilege
|
import core.game.system.command.Privilege
|
||||||
import core.game.world.GameWorld
|
import core.game.world.GameWorld
|
||||||
|
import core.game.world.map.path.Pathfinder
|
||||||
import core.game.world.repository.Repository
|
import core.game.world.repository.Repository
|
||||||
import core.tools.Log
|
import core.tools.Log
|
||||||
import java.lang.Integer.min
|
import java.lang.Integer.min
|
||||||
@@ -108,24 +109,14 @@ class RevenantController : TickListener, Commands {
|
|||||||
NONE {
|
NONE {
|
||||||
override fun execute(revenantNPC: RevenantNPC) {}
|
override fun execute(revenantNPC: RevenantNPC) {}
|
||||||
},
|
},
|
||||||
INTENTIONAL_IDLE {
|
|
||||||
private val MAX_IDLE_TIME: Int = 50
|
|
||||||
|
|
||||||
override fun execute(revenantNPC: RevenantNPC) {
|
|
||||||
if (taskTimeRemaining[revenantNPC] == 0) currentTask[revenantNPC] = NONE
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun assign(revenantNPC: RevenantNPC) {
|
|
||||||
taskTimeRemaining[revenantNPC] = RandomFunction.random(MAX_IDLE_TIME)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
RANDOM_ROAM {
|
RANDOM_ROAM {
|
||||||
private val MAX_ROAM_TICKS: Int = 250
|
private val MAX_ROAM_TICKS: Int = 250
|
||||||
|
|
||||||
override fun execute(revenantNPC: RevenantNPC) {
|
override fun execute(revenantNPC: RevenantNPC) {
|
||||||
if (!canMove(revenantNPC)) return
|
if (!canMove(revenantNPC)) return
|
||||||
|
|
||||||
revenantNPC.pulseManager.run(object : MovementPulse(revenantNPC, getNextLocation(revenantNPC)) {
|
val nextLoc = getNextLocation(revenantNPC)
|
||||||
|
revenantNPC.pulseManager.run(object : MovementPulse(revenantNPC, nextLoc, Pathfinder.SMART) {
|
||||||
override fun pulse(): Boolean {
|
override fun pulse(): Boolean {
|
||||||
if (taskTimeRemaining[revenantNPC]!! <= 0) currentTask[revenantNPC] = NONE
|
if (taskTimeRemaining[revenantNPC]!! <= 0) currentTask[revenantNPC] = NONE
|
||||||
return true
|
return true
|
||||||
@@ -139,6 +130,7 @@ class RevenantController : TickListener, Commands {
|
|||||||
|
|
||||||
fun canMove(revenantNPC: RevenantNPC) : Boolean {
|
fun canMove(revenantNPC: RevenantNPC) : Boolean {
|
||||||
return !revenantNPC.walkingQueue.isMoving
|
return !revenantNPC.walkingQueue.isMoving
|
||||||
|
&& !revenantNPC.pulseManager.hasPulseRunning()
|
||||||
&& !revenantNPC.properties.combatPulse.isAttacking
|
&& !revenantNPC.properties.combatPulse.isAttacking
|
||||||
&& !revenantNPC.properties.combatPulse.isInCombat
|
&& !revenantNPC.properties.combatPulse.isInCombat
|
||||||
}
|
}
|
||||||
@@ -212,7 +204,7 @@ class RevenantController : TickListener, Commands {
|
|||||||
RandomFunction.random(-pathVariance, pathVariance),
|
RandomFunction.random(-pathVariance, pathVariance),
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
revenantNPC.pulseManager.run(object : MovementPulse(revenantNPC, nextLoc) {
|
revenantNPC.pulseManager.run(object : MovementPulse(revenantNPC, nextLoc, Pathfinder.SMART) {
|
||||||
override fun pulse(): Boolean {
|
override fun pulse(): Boolean {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -235,4 +227,4 @@ class RevenantController : TickListener, Commands {
|
|||||||
abstract fun execute(revenantNPC: RevenantNPC)
|
abstract fun execute(revenantNPC: RevenantNPC)
|
||||||
open fun assign(revenantNPC: RevenantNPC) {}
|
open fun assign(revenantNPC: RevenantNPC) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1099,6 +1099,22 @@ fun forceWalk(entity: Entity, dest: Location, type: String) {
|
|||||||
path.walk(entity)
|
path.walk(entity)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a location truncated to the appropriate pathfinding limit
|
||||||
|
**/
|
||||||
|
fun truncateLoc (mover: Entity, destination: Location) : Pair<Boolean,Location> {
|
||||||
|
val vector = Vector.betweenLocs(mover.location, destination)
|
||||||
|
val normVec = vector.normalized()
|
||||||
|
val mag = vector.magnitude()
|
||||||
|
|
||||||
|
var multiplier = if (mover is NPC) 14.0 else ServerConstants.MAX_PATHFIND_DISTANCE.toDouble()
|
||||||
|
var clampedMultiplier = min(multiplier, mag)
|
||||||
|
|
||||||
|
var truncated = multiplier == clampedMultiplier
|
||||||
|
|
||||||
|
return Pair(truncated, mover.location.transform(normVec * clampedMultiplier))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Force a player to move from the start location to the dest location
|
* Force a player to move from the start location to the dest location
|
||||||
* @param player the player we are moving
|
* @param player the player we are moving
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ class Vector (val x: Double, val y: Double) {
|
|||||||
return -this
|
return -this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun toLocation (plane: Int = 0) : Location {
|
||||||
|
return Location.create(floor(x).toInt(), floor(y).toInt(), plane)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic fun betweenLocs (from: Location, to: Location) : Vector {
|
@JvmStatic fun betweenLocs (from: Location, to: Location) : Vector {
|
||||||
val xDiff = to.x - from.x
|
val xDiff = to.x - from.x
|
||||||
|
|||||||
@@ -15,11 +15,13 @@ import core.net.packet.PacketRepository;
|
|||||||
import core.net.packet.context.PlayerContext;
|
import core.net.packet.context.PlayerContext;
|
||||||
import core.net.packet.out.ClearMinimapFlag;
|
import core.net.packet.out.ClearMinimapFlag;
|
||||||
import kotlin.jvm.functions.Function2;
|
import kotlin.jvm.functions.Function2;
|
||||||
|
import kotlin.Pair;
|
||||||
import core.tools.SystemLogger;
|
import core.tools.SystemLogger;
|
||||||
|
import core.api.utils.Vector;
|
||||||
|
|
||||||
import static core.api.ContentAPIKt.getWorldTicks;
|
import content.region.wilderness.handlers.revenants.RevenantNPC;
|
||||||
import static core.api.ContentAPIKt.log;
|
|
||||||
import core.tools.Log;
|
import static core.api.ContentAPIKt.*;
|
||||||
|
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
|
|
||||||
@@ -84,9 +86,6 @@ public abstract class MovementPulse extends Pulse {
|
|||||||
|
|
||||||
private Location previousLoc;
|
private Location previousLoc;
|
||||||
|
|
||||||
private Location previousMoverLoc;
|
|
||||||
private int previousMoveTime;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new {@code MovementPulse} {@code Object}.
|
* Constructs a new {@code MovementPulse} {@code Object}.
|
||||||
*
|
*
|
||||||
@@ -187,38 +186,27 @@ public abstract class MovementPulse extends Pulse {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean update() {
|
public boolean update() {
|
||||||
|
if (!mover.getViewport().getRegion().isActive())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!validate()) {
|
||||||
|
stop();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
mover.face(null);
|
mover.face(null);
|
||||||
if (mover == null || destination == null || mover.getViewport().getRegion() == null) {
|
updatePath();
|
||||||
|
|
||||||
|
if (tryInteract()) {
|
||||||
stop();
|
stop();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasInactiveNode() || !mover.getViewport().getRegion().isActive()) {
|
return false;
|
||||||
stop();
|
}
|
||||||
return true;
|
|
||||||
}
|
private boolean tryInteract() {
|
||||||
if (!isRunning()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
findPath();
|
|
||||||
Location ml = mover.getLocation();
|
Location ml = mover.getLocation();
|
||||||
|
|
||||||
if (previousMoverLoc == null || !previousMoverLoc.equals(ml)) {
|
|
||||||
previousMoverLoc = Location.create(ml);
|
|
||||||
previousMoveTime = getWorldTicks();
|
|
||||||
}
|
|
||||||
else if (getWorldTicks() - previousMoveTime >= 25) {
|
|
||||||
if (mover instanceof Player) {
|
|
||||||
((Player) mover).getPacketDispatch().sendMessage("I can't reach that.");
|
|
||||||
PacketRepository.send(ClearMinimapFlag.class, new PlayerContext((Player) mover));
|
|
||||||
}
|
|
||||||
log(this.getClass(), Log.FINE, mover.getName() + " was trying to move to " + interactLocation + " from " + ml + " but hasn't changed location in 25 ticks. More info follows:");
|
|
||||||
log(this.getClass(), Log.FINE, " -> Locked? " + mover.getLocks().isMovementLocked());
|
|
||||||
log(this.getClass(), Log.FINE, " -> Has path? " + mover.getWalkingQueue().hasPath());
|
|
||||||
stop();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow being within 1 square of moving entities to interact with them.
|
// Allow being within 1 square of moving entities to interact with them.
|
||||||
int radius = destination instanceof Entity && ((Entity)destination).getWalkingQueue().hasPath() ? 1 : 0;
|
int radius = destination instanceof Entity && ((Entity)destination).getWalkingQueue().hasPath() ? 1 : 0;
|
||||||
if (interactLocation == null)
|
if (interactLocation == null)
|
||||||
@@ -243,6 +231,13 @@ public abstract class MovementPulse extends Pulse {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean validate() {
|
||||||
|
if (mover == null || destination == null || mover.getViewport().getRegion() == null || hasInactiveNode()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return isRunning();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
public void stop() {
|
||||||
super.stop();
|
super.stop();
|
||||||
@@ -255,20 +250,15 @@ public abstract class MovementPulse extends Pulse {
|
|||||||
/**
|
/**
|
||||||
* Finds a path to the destination, if necessary.
|
* Finds a path to the destination, if necessary.
|
||||||
*/
|
*/
|
||||||
public void findPath() {
|
private boolean usingTruncatedPath = false;
|
||||||
|
private boolean isMoveNearSet = false;
|
||||||
|
public void updatePath() {
|
||||||
if (mover instanceof NPC && mover.asNpc().isNeverWalks()) {
|
if (mover instanceof NPC && mover.asNpc().isNeverWalks()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(destination.getLocation() == null){
|
if(destination == null || destination.getLocation() == null){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean inside = isInsideEntity(mover.getLocation());
|
|
||||||
/* This appears to have been a premature optimization that lead to a bug that would cause both entities
|
|
||||||
to completely stop moving mid-combat/mid-follow-dance/etc
|
|
||||||
if (last != null && last.equals(destination.getLocation()) && !inside) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
Location loc = null;
|
Location loc = null;
|
||||||
|
|
||||||
@@ -287,60 +277,35 @@ public abstract class MovementPulse extends Pulse {
|
|||||||
else if (useHandler != null) {
|
else if (useHandler != null) {
|
||||||
loc = useHandler.getDestination((Player) mover, destination);
|
loc = useHandler.getDestination((Player) mover, destination);
|
||||||
}
|
}
|
||||||
else if (inside) {
|
else if (isInsideEntity(mover.getLocation())) {
|
||||||
loc = findBorderLocation();
|
loc = findBorderLocation();
|
||||||
}
|
}
|
||||||
} else if (loc == previousLoc && interactLocation != null && mover.getWalkingQueue().hasPath()) return;
|
|
||||||
|
|
||||||
if (destination == null) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (destination instanceof Entity || interactLocation == null) {
|
if (destination instanceof NPC)
|
||||||
Location ml = mover.getLocation();
|
loc = checkForEntityPathInterrupt(loc != null ? loc : destination.getLocation());
|
||||||
Location dl = destination.getLocation();
|
|
||||||
// Lead the target if they're walking/running, unless they're already within interaction range
|
|
||||||
if(loc != null && destination instanceof Entity && Math.max(Math.abs(ml.getX() - dl.getX()), Math.abs(ml.getY() - dl.getY())) > 1) {
|
|
||||||
WalkingQueue wq = ((Entity)destination).getWalkingQueue();
|
|
||||||
if(wq.hasPath()) {
|
|
||||||
Point[] points = wq.getQueue().toArray(new Point[0]);
|
|
||||||
if(points.length > 0) {
|
|
||||||
Point p = points[0];
|
|
||||||
for(int i=0; i<points.length; i++) {
|
|
||||||
// Target the farthest point along target's planned movement that's within 1 tick's running,
|
|
||||||
// this ensures the player will run to catch up to the target if able.
|
|
||||||
if(Math.max(Math.abs(ml.getX() - points[i].getX()), Math.abs(ml.getY() - points[i].getY())) <= 2) {
|
|
||||||
p = points[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
loc.setX(p.getX());
|
|
||||||
loc.setY(p.getY());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Path path = Pathfinder.find(mover, loc != null ? loc : destination, true, pathfinder);
|
if (interactLocation == null)
|
||||||
loc = destination.getLocation();
|
interactLocation = loc;
|
||||||
near = !path.isSuccessful() || path.isMoveNear();
|
|
||||||
interactLocation = mover.getLocation();
|
if (destination instanceof Entity || interactLocation == null || (!mover.getWalkingQueue().hasPath() && interactLocation.getDistance(mover.getLocation()) > 0) || (usingTruncatedPath && destination.getLocation().getDistance(mover.getLocation()) < 14)) {
|
||||||
boolean canMove = true;
|
if (!checkAllowMovement())
|
||||||
if (destination instanceof Entity) {
|
return;
|
||||||
Entity e = (Entity) destination;
|
|
||||||
Location l = e.getLocation();
|
Path path;
|
||||||
Deque<Point> npcPath = e.getWalkingQueue().getQueue();
|
Pair<Boolean, Location> truncation = truncateLoc(mover, loc != null ? loc : destination.getLocation());
|
||||||
if (e.getWalkingQueue().hasPath() && e.getProperties().getCombatPulse().isRunning() && e.getProperties().getCombatPulse().getVictim() == mover)
|
if (truncation.getFirst()) {
|
||||||
canMove = false;
|
path = Pathfinder.find(mover, truncation.getSecond(), true, pathfinder);
|
||||||
if (!canMove) { //If we normally shouldn't move, but the NPC's pathfinding is not letting them move, then move.
|
usingTruncatedPath = true;
|
||||||
if (npcPath.size() == 1) {
|
} else {
|
||||||
Point pathElement = npcPath.peek();
|
path = Pathfinder.find(mover, loc != null ? loc : destination, true, pathfinder);
|
||||||
if (pathElement.getX() == l.getX() && pathElement.getY() == l.getY())
|
interactLocation = null; //reset interactLocation so the below code can set it to the properly-pathfound last bit of path.
|
||||||
canMove = true;
|
usingTruncatedPath = false;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!path.getPoints().isEmpty() && canMove) {
|
near = !path.isSuccessful() || path.isMoveNear();
|
||||||
|
|
||||||
|
if (!path.getPoints().isEmpty()) {
|
||||||
Point point = path.getPoints().getLast();
|
Point point = path.getPoints().getLast();
|
||||||
interactLocation = Location.create(point.getX(), point.getY(), mover.getLocation().getZ());
|
|
||||||
if (forceRun) {
|
if (forceRun) {
|
||||||
mover.getWalkingQueue().reset(forceRun);
|
mover.getWalkingQueue().reset(forceRun);
|
||||||
} else {
|
} else {
|
||||||
@@ -356,11 +321,88 @@ public abstract class MovementPulse extends Pulse {
|
|||||||
} else {
|
} else {
|
||||||
mover.face(null);
|
mover.face(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (i == size - 1 && interactLocation == null)
|
||||||
|
interactLocation = Location.create(point.getX(), point.getY(), mover.getLocation().getZ());
|
||||||
}
|
}
|
||||||
previousLoc = loc;
|
|
||||||
}
|
}
|
||||||
|
previousLoc = loc;
|
||||||
}
|
}
|
||||||
last = destination.getLocation();
|
last = destination.getLocation();
|
||||||
|
if (mover instanceof Player && mover.getAttribute("draw-intersect", false)) {
|
||||||
|
clearHintIcon((Player) mover);
|
||||||
|
registerHintIcon((Player) mover, interactLocation, 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkAllowMovement() {
|
||||||
|
boolean canMove = true;
|
||||||
|
if (destination instanceof Entity) {
|
||||||
|
Entity e = (Entity) destination;
|
||||||
|
Location l = e.getLocation();
|
||||||
|
Deque<Point> npcPath = e.getWalkingQueue().getQueue();
|
||||||
|
if (e.getWalkingQueue().hasPath() && e.getProperties().getCombatPulse().isRunning() && e.getProperties().getCombatPulse().getVictim() == mover)
|
||||||
|
canMove = false;
|
||||||
|
if (!canMove) { //If we normally shouldn't move, but the NPC's pathfinding is not letting them move, then move.
|
||||||
|
if (npcPath.size() == 1) {
|
||||||
|
Point pathElement = npcPath.peek();
|
||||||
|
if (pathElement.getX() == l.getX() && pathElement.getY() == l.getY())
|
||||||
|
canMove = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return canMove;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Location checkForEntityPathInterrupt(Location loc) {
|
||||||
|
Location ml = mover.getLocation();
|
||||||
|
Location dl = destination.getLocation();
|
||||||
|
// Lead the target if they're walking/running, unless they're already within interaction range
|
||||||
|
if(loc != null && destination instanceof Entity) {
|
||||||
|
WalkingQueue wq = ((Entity)destination).getWalkingQueue();
|
||||||
|
if(wq.hasPath()) {
|
||||||
|
Point[] points = wq.getQueue().toArray(new Point[0]);
|
||||||
|
if(points.length > 0) {
|
||||||
|
Point p = points[0];
|
||||||
|
Point predictiveIntersection = null;
|
||||||
|
for(int i=0; i<points.length; i++) {
|
||||||
|
Location closestBorder = getClosestBorderToPoint (points[i], loc.getZ());
|
||||||
|
|
||||||
|
int moverDist = Math.max(Math.abs(ml.getX() - closestBorder.getX()), Math.abs(ml.getY() - closestBorder.getY()));
|
||||||
|
float movementRatio = moverDist / (float) ((i + 1) / (mover.getWalkingQueue().isRunning() ? 2 : 1));
|
||||||
|
if (predictiveIntersection == null && movementRatio <= 1.0) { //try to predict an intersection point on the path if possible
|
||||||
|
predictiveIntersection = points[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we target the farthest point along target's planned movement that's within 1 tick's running,
|
||||||
|
// this ensures the player will run to catch up to the target if able.
|
||||||
|
if(moverDist <= 2) {
|
||||||
|
p = points[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (predictiveIntersection != null)
|
||||||
|
p = predictiveIntersection;
|
||||||
|
|
||||||
|
Location endLoc = getClosestBorderToPoint(p, loc.getZ());
|
||||||
|
return endLoc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return loc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Location getClosestBorderToPoint (Point p, int plane) {
|
||||||
|
Vector pathDiff = Vector.betweenLocs (destination.getLocation(), Location.create(p.getX(), p.getY(), plane));
|
||||||
|
Location predictedCenterPos = (destination.getMathematicalCenter().plus(pathDiff)).toLocation(plane);
|
||||||
|
Vector toPlayerNormalized = Vector.betweenLocs(predictedCenterPos, mover.getCenterLocation()).normalized();
|
||||||
|
return predictedCenterPos.transform(toPlayerNormalized.times(destination.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Location findBorderLocation() {
|
||||||
|
return findBorderLocation(destination.getLocation());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -368,12 +410,12 @@ public abstract class MovementPulse extends Pulse {
|
|||||||
*
|
*
|
||||||
* @return The location to walk to.
|
* @return The location to walk to.
|
||||||
*/
|
*/
|
||||||
private Location findBorderLocation() {
|
private Location findBorderLocation(Location centerDestLoc) {
|
||||||
int size = destination.size();
|
int size = destination.size();
|
||||||
Location centerDest = destination.getLocation().transform(size >> 1, size >> 1, 0);
|
Location centerDest = centerDestLoc.transform(size >> 1, size >> 1, 0);
|
||||||
Location center = mover.getLocation().transform(mover.size() >> 1, mover.size() >> 1, 0);
|
Location center = mover.getLocation().transform(mover.size() >> 1, mover.size() >> 1, 0);
|
||||||
Direction direction = Direction.getLogicalDirection(centerDest, center);
|
Direction direction = Direction.getLogicalDirection(centerDest, center);
|
||||||
Location delta = Location.getDelta(destination.getLocation(), mover.getLocation());
|
Location delta = Location.getDelta(centerDestLoc, mover.getLocation());
|
||||||
main:
|
main:
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
int amount = 0;
|
int amount = 0;
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ class CombatPulse(
|
|||||||
if (entity == null || victim == null || entity.locks.isMovementLocked) {
|
if (entity == null || victim == null || entity.locks.isMovementLocked) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
movement.findPath()
|
movement.updatePath()
|
||||||
return type == InteractionType.MOVE_INTERACT
|
return type == InteractionType.MOVE_INTERACT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -719,7 +719,7 @@ public class NPC extends Entity {
|
|||||||
if (!pathBoundMovement || movementPath == null || movementPath.length < 1) {
|
if (!pathBoundMovement || movementPath == null || movementPath.length < 1) {
|
||||||
Location returnToSpawnLocation = getProperties().getSpawnLocation().transform(-5 + RandomFunction.random(getWalkRadius()), -5 + RandomFunction.random(getWalkRadius()), 0);
|
Location returnToSpawnLocation = getProperties().getSpawnLocation().transform(-5 + RandomFunction.random(getWalkRadius()), -5 + RandomFunction.random(getWalkRadius()), 0);
|
||||||
int dist = (int) Location.getDistance(location, returnToSpawnLocation);
|
int dist = (int) Location.getDistance(location, returnToSpawnLocation);
|
||||||
int pathLimit = 15;
|
int pathLimit = 14;
|
||||||
if (dist > pathLimit) {
|
if (dist > pathLimit) {
|
||||||
Vector normalizedDir = Vector.betweenLocs(this.location, returnToSpawnLocation).normalized();
|
Vector normalizedDir = Vector.betweenLocs(this.location, returnToSpawnLocation).normalized();
|
||||||
returnToSpawnLocation = this.location.transform (normalizedDir.times(pathLimit));
|
returnToSpawnLocation = this.location.transform (normalizedDir.times(pathLimit));
|
||||||
|
|||||||
@@ -545,6 +545,8 @@ public final class Appearance {
|
|||||||
* @return The render animation id.
|
* @return The render animation id.
|
||||||
*/
|
*/
|
||||||
public int getRenderAnimation() {
|
public int getRenderAnimation() {
|
||||||
|
if (player.getAttribute("render-anim-override") != null)
|
||||||
|
return player.getAttribute("render-anim-override", renderAnimationId);
|
||||||
return renderAnimationId;
|
return renderAnimationId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -272,5 +272,9 @@ class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) {
|
|||||||
define("fmanim", Privilege.ADMIN, "", "") {player, args ->
|
define("fmanim", Privilege.ADMIN, "", "") {player, args ->
|
||||||
setAttribute(player, "fmanim", args[1].toIntOrNull() ?: -1)
|
setAttribute(player, "fmanim", args[1].toIntOrNull() ?: -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define("drawintersect", Privilege.ADMIN, "", "Visualizes the predicted intersection point with an NPC") {player, _ ->
|
||||||
|
setAttribute(player, "draw-intersect", !getAttribute(player, "draw-intersect", false))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class PulseRunner {
|
|||||||
|
|
||||||
val elapsedTime = measure {
|
val elapsedTime = measure {
|
||||||
try {
|
try {
|
||||||
if (!pulse.update()) {
|
if (!pulse.update() && pulse.isRunning) {
|
||||||
pulses.add(pulse)
|
pulses.add(pulse)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -60,4 +60,4 @@ class PulseRunner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,9 +83,7 @@ public final class DumbPathfinder extends Pathfinder {
|
|||||||
Direction last = null;
|
Direction last = null;
|
||||||
for (int i = 0; i < points.size() - 1; i++) {
|
for (int i = 0; i < points.size() - 1; i++) {
|
||||||
Point p = points.get(i);
|
Point p = points.get(i);
|
||||||
if (p.getDirection() != last) {
|
path.getPoints().add(p);
|
||||||
path.getPoints().add(p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
path.getPoints().add(points.get(points.size() - 1));
|
path.getPoints().add(points.get(points.size() - 1));
|
||||||
}
|
}
|
||||||
@@ -426,4 +424,4 @@ public final class DumbPathfinder extends Pathfinder {
|
|||||||
return new Direction[0];
|
return new Direction[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -209,11 +209,9 @@ internal constructor() : Pathfinder() {
|
|||||||
if (++attempts > queueX.size) {
|
if (++attempts > queueX.size) {
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
if (directionFlag != previousDirection) {
|
previousDirection = directionFlag
|
||||||
previousDirection = directionFlag
|
queueX[readPosition] = curX
|
||||||
queueX[readPosition] = curX
|
queueY[readPosition++] = curY
|
||||||
queueY[readPosition++] = curY
|
|
||||||
}
|
|
||||||
if (directionFlag and WEST_FLAG != 0) {
|
if (directionFlag and WEST_FLAG != 0) {
|
||||||
curX++
|
curX++
|
||||||
} else if (directionFlag and EAST_FLAG != 0) {
|
} else if (directionFlag and EAST_FLAG != 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user