mirror of
https://gitlab.com/2009scape/2009scape.git
synced 2025-12-10 10:20:41 -07:00
Refactored movement pulse
Fixed bug with Vinesweeper gnomes not checking flags Fixed hunter tracking
This commit is contained in:
parent
da33c89a70
commit
06e8279f2a
13 changed files with 192 additions and 121 deletions
|
|
@ -7,12 +7,15 @@ import org.rs09.consts.NPCs
|
|||
import core.game.dialogue.DialogueFile
|
||||
import core.game.interaction.InteractionListener
|
||||
import core.game.interaction.IntType
|
||||
import core.game.world.update.flag.context.Animation
|
||||
import core.tools.END_DIALOGUE
|
||||
import core.api.*
|
||||
|
||||
class MistagEasterEgg : InteractionListener {
|
||||
val DIAMOND = Items.DIAMOND_1601
|
||||
val MISTAG = NPCs.MISTAG_2084
|
||||
val ZANIK_RING = 14649
|
||||
val DRUNK_RENDER = 982
|
||||
|
||||
override fun defineListeners() {
|
||||
onUseWith(IntType.NPC,DIAMOND,MISTAG){ player, _, with ->
|
||||
|
|
@ -30,6 +33,18 @@ class MistagEasterEgg : InteractionListener {
|
|||
player.appearance.transformNPC(-1)
|
||||
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? {
|
||||
return super.npc(core.game.dialogue.FacialExpression.OLD_HAPPY,*messages)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ abstract class HunterTracking : OptionHandler(){
|
|||
trail.get(currentIndex)
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
TrailDefinition(0,TrailType.LINKING,false,Location(0,0,0),Location(0,0,0),Location(0,0,0))
|
||||
}
|
||||
when(option){
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import core.api.*
|
|||
import core.tools.SystemLogger
|
||||
import core.game.system.command.Privilege
|
||||
import core.game.world.GameWorld
|
||||
import core.game.world.map.path.Pathfinder
|
||||
import core.game.world.repository.Repository
|
||||
import core.tools.Log
|
||||
import java.lang.Integer.min
|
||||
|
|
@ -108,24 +109,14 @@ class RevenantController : TickListener, Commands {
|
|||
NONE {
|
||||
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 {
|
||||
private val MAX_ROAM_TICKS: Int = 250
|
||||
|
||||
override fun execute(revenantNPC: RevenantNPC) {
|
||||
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 {
|
||||
if (taskTimeRemaining[revenantNPC]!! <= 0) currentTask[revenantNPC] = NONE
|
||||
return true
|
||||
|
|
@ -139,6 +130,7 @@ class RevenantController : TickListener, Commands {
|
|||
|
||||
fun canMove(revenantNPC: RevenantNPC) : Boolean {
|
||||
return !revenantNPC.walkingQueue.isMoving
|
||||
&& !revenantNPC.pulseManager.hasPulseRunning()
|
||||
&& !revenantNPC.properties.combatPulse.isAttacking
|
||||
&& !revenantNPC.properties.combatPulse.isInCombat
|
||||
}
|
||||
|
|
@ -212,7 +204,7 @@ class RevenantController : TickListener, Commands {
|
|||
RandomFunction.random(-pathVariance, pathVariance),
|
||||
0
|
||||
)
|
||||
revenantNPC.pulseManager.run(object : MovementPulse(revenantNPC, nextLoc) {
|
||||
revenantNPC.pulseManager.run(object : MovementPulse(revenantNPC, nextLoc, Pathfinder.SMART) {
|
||||
override fun pulse(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
|
@ -235,4 +227,4 @@ class RevenantController : TickListener, Commands {
|
|||
abstract fun execute(revenantNPC: RevenantNPC)
|
||||
open fun assign(revenantNPC: RevenantNPC) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1099,6 +1099,22 @@ fun forceWalk(entity: Entity, dest: Location, type: String) {
|
|||
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
|
||||
* @param player the player we are moving
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@ class Vector (val x: Double, val y: Double) {
|
|||
return -this
|
||||
}
|
||||
|
||||
fun toLocation (plane: Int = 0) : Location {
|
||||
return Location.create(floor(x).toInt(), floor(y).toInt(), plane)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic fun betweenLocs (from: Location, to: Location) : Vector {
|
||||
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.out.ClearMinimapFlag;
|
||||
import kotlin.jvm.functions.Function2;
|
||||
import kotlin.Pair;
|
||||
import core.tools.SystemLogger;
|
||||
import core.api.utils.Vector;
|
||||
|
||||
import static core.api.ContentAPIKt.getWorldTicks;
|
||||
import static core.api.ContentAPIKt.log;
|
||||
import core.tools.Log;
|
||||
import content.region.wilderness.handlers.revenants.RevenantNPC;
|
||||
|
||||
import static core.api.ContentAPIKt.*;
|
||||
|
||||
import java.util.Deque;
|
||||
|
||||
|
|
@ -84,9 +86,6 @@ public abstract class MovementPulse extends Pulse {
|
|||
|
||||
private Location previousLoc;
|
||||
|
||||
private Location previousMoverLoc;
|
||||
private int previousMoveTime;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code MovementPulse} {@code Object}.
|
||||
*
|
||||
|
|
@ -187,38 +186,27 @@ public abstract class MovementPulse extends Pulse {
|
|||
|
||||
@Override
|
||||
public boolean update() {
|
||||
if (!mover.getViewport().getRegion().isActive())
|
||||
return false;
|
||||
|
||||
if (!validate()) {
|
||||
stop();
|
||||
return true;
|
||||
}
|
||||
|
||||
mover.face(null);
|
||||
if (mover == null || destination == null || mover.getViewport().getRegion() == null) {
|
||||
updatePath();
|
||||
|
||||
if (tryInteract()) {
|
||||
stop();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hasInactiveNode() || !mover.getViewport().getRegion().isActive()) {
|
||||
stop();
|
||||
return true;
|
||||
}
|
||||
if (!isRunning()) {
|
||||
return true;
|
||||
}
|
||||
findPath();
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean tryInteract() {
|
||||
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.
|
||||
int radius = destination instanceof Entity && ((Entity)destination).getWalkingQueue().hasPath() ? 1 : 0;
|
||||
if (interactLocation == null)
|
||||
|
|
@ -243,6 +231,13 @@ public abstract class MovementPulse extends Pulse {
|
|||
return false;
|
||||
}
|
||||
|
||||
private boolean validate() {
|
||||
if (mover == null || destination == null || mover.getViewport().getRegion() == null || hasInactiveNode()) {
|
||||
return false;
|
||||
}
|
||||
return isRunning();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop();
|
||||
|
|
@ -255,20 +250,15 @@ public abstract class MovementPulse extends Pulse {
|
|||
/**
|
||||
* 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()) {
|
||||
return;
|
||||
}
|
||||
if(destination.getLocation() == null){
|
||||
if(destination == null || destination.getLocation() == null){
|
||||
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;
|
||||
|
||||
|
|
@ -287,60 +277,35 @@ public abstract class MovementPulse extends Pulse {
|
|||
else if (useHandler != null) {
|
||||
loc = useHandler.getDestination((Player) mover, destination);
|
||||
}
|
||||
else if (inside) {
|
||||
else if (isInsideEntity(mover.getLocation())) {
|
||||
loc = findBorderLocation();
|
||||
}
|
||||
} else if (loc == previousLoc && interactLocation != null && mover.getWalkingQueue().hasPath()) return;
|
||||
|
||||
if (destination == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (destination instanceof Entity || interactLocation == null) {
|
||||
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 && 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (destination instanceof NPC)
|
||||
loc = checkForEntityPathInterrupt(loc != null ? loc : destination.getLocation());
|
||||
|
||||
Path path = Pathfinder.find(mover, loc != null ? loc : destination, true, pathfinder);
|
||||
loc = destination.getLocation();
|
||||
near = !path.isSuccessful() || path.isMoveNear();
|
||||
interactLocation = mover.getLocation();
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (interactLocation == null)
|
||||
interactLocation = loc;
|
||||
|
||||
if (destination instanceof Entity || interactLocation == null || (!mover.getWalkingQueue().hasPath() && interactLocation.getDistance(mover.getLocation()) > 0) || (usingTruncatedPath && destination.getLocation().getDistance(mover.getLocation()) < 14)) {
|
||||
if (!checkAllowMovement())
|
||||
return;
|
||||
|
||||
Path path;
|
||||
Pair<Boolean, Location> truncation = truncateLoc(mover, loc != null ? loc : destination.getLocation());
|
||||
if (truncation.getFirst()) {
|
||||
path = Pathfinder.find(mover, truncation.getSecond(), true, pathfinder);
|
||||
usingTruncatedPath = true;
|
||||
} else {
|
||||
path = Pathfinder.find(mover, loc != null ? loc : destination, true, pathfinder);
|
||||
interactLocation = null; //reset interactLocation so the below code can set it to the properly-pathfound last bit of path.
|
||||
usingTruncatedPath = false;
|
||||
}
|
||||
if (!path.getPoints().isEmpty() && canMove) {
|
||||
near = !path.isSuccessful() || path.isMoveNear();
|
||||
|
||||
if (!path.getPoints().isEmpty()) {
|
||||
Point point = path.getPoints().getLast();
|
||||
interactLocation = Location.create(point.getX(), point.getY(), mover.getLocation().getZ());
|
||||
if (forceRun) {
|
||||
mover.getWalkingQueue().reset(forceRun);
|
||||
} else {
|
||||
|
|
@ -356,11 +321,88 @@ public abstract class MovementPulse extends Pulse {
|
|||
} else {
|
||||
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();
|
||||
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.
|
||||
*/
|
||||
private Location findBorderLocation() {
|
||||
private Location findBorderLocation(Location centerDestLoc) {
|
||||
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);
|
||||
Direction direction = Direction.getLogicalDirection(centerDest, center);
|
||||
Location delta = Location.getDelta(destination.getLocation(), mover.getLocation());
|
||||
Location delta = Location.getDelta(centerDestLoc, mover.getLocation());
|
||||
main:
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int amount = 0;
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ class CombatPulse(
|
|||
if (entity == null || victim == null || entity.locks.isMovementLocked) {
|
||||
return false
|
||||
}
|
||||
movement.findPath()
|
||||
movement.updatePath()
|
||||
return type == InteractionType.MOVE_INTERACT
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -719,7 +719,7 @@ public class NPC extends Entity {
|
|||
if (!pathBoundMovement || movementPath == null || movementPath.length < 1) {
|
||||
Location returnToSpawnLocation = getProperties().getSpawnLocation().transform(-5 + RandomFunction.random(getWalkRadius()), -5 + RandomFunction.random(getWalkRadius()), 0);
|
||||
int dist = (int) Location.getDistance(location, returnToSpawnLocation);
|
||||
int pathLimit = 15;
|
||||
int pathLimit = 14;
|
||||
if (dist > pathLimit) {
|
||||
Vector normalizedDir = Vector.betweenLocs(this.location, returnToSpawnLocation).normalized();
|
||||
returnToSpawnLocation = this.location.transform (normalizedDir.times(pathLimit));
|
||||
|
|
|
|||
|
|
@ -545,6 +545,8 @@ public final class Appearance {
|
|||
* @return The render animation id.
|
||||
*/
|
||||
public int getRenderAnimation() {
|
||||
if (player.getAttribute("render-anim-override") != null)
|
||||
return player.getAttribute("render-anim-override", renderAnimationId);
|
||||
return renderAnimationId;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -272,5 +272,9 @@ class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) {
|
|||
define("fmanim", Privilege.ADMIN, "", "") {player, args ->
|
||||
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 {
|
||||
try {
|
||||
if (!pulse.update()) {
|
||||
if (!pulse.update() && pulse.isRunning) {
|
||||
pulses.add(pulse)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
|
@ -60,4 +60,4 @@ class PulseRunner {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,9 +83,7 @@ public final class DumbPathfinder extends Pathfinder {
|
|||
Direction last = null;
|
||||
for (int i = 0; i < points.size() - 1; 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));
|
||||
}
|
||||
|
|
@ -426,4 +424,4 @@ public final class DumbPathfinder extends Pathfinder {
|
|||
return new Direction[0];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -209,11 +209,9 @@ internal constructor() : Pathfinder() {
|
|||
if (++attempts > queueX.size) {
|
||||
return path
|
||||
}
|
||||
if (directionFlag != previousDirection) {
|
||||
previousDirection = directionFlag
|
||||
queueX[readPosition] = curX
|
||||
queueY[readPosition++] = curY
|
||||
}
|
||||
previousDirection = directionFlag
|
||||
queueX[readPosition] = curX
|
||||
queueY[readPosition++] = curY
|
||||
if (directionFlag and WEST_FLAG != 0) {
|
||||
curX++
|
||||
} else if (directionFlag and EAST_FLAG != 0) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue