diff --git a/data/plugins/entity/mob/walk-to/plugin.xml b/data/plugins/entity/mob/walk-to/plugin.xml
new file mode 100644
index 00000000..676560e8
--- /dev/null
+++ b/data/plugins/entity/mob/walk-to/plugin.xml
@@ -0,0 +1,16 @@
+
+
+ mob-walk-to
+ 0.1
+ Mob path generation
+ Adds a mixin for making mobs walk to another entity.
+
+ Gary Tierney
+
+
+
+
+
+ mob-extension
+
+
\ No newline at end of file
diff --git a/data/plugins/entity/mob/walk-to/walk_to.rb b/data/plugins/entity/mob/walk-to/walk_to.rb
new file mode 100644
index 00000000..6381172f
--- /dev/null
+++ b/data/plugins/entity/mob/walk-to/walk_to.rb
@@ -0,0 +1,83 @@
+java_import 'org.apollo.game.model.entity.path.AStarPathfindingAlgorithm'
+java_import 'org.apollo.game.model.entity.path.EuclideanHeuristic'
+java_import 'org.apollo.game.model.entity.path.SimplePathfindingAlgorithm'
+java_import 'org.apollo.game.model.entity.obj.GameObject'
+java_import 'org.apollo.game.model.entity.Mob'
+java_import 'org.apollo.game.model.entity.Player'
+java_import 'org.apollo.game.model.entity.EntityType'
+java_import 'org.apollo.game.model.Direction'
+
+##
+# A pathfinder used for simple following (i.e.: An NPC attacking a player).
+SIMPLE_PATH_FINDER = SimplePathfindingAlgorithm.new($world.collision_manager)
+
+##
+# A pathfinder used for precise following (i.e.: A player attacking another player).
+FOLLOW_PATH_FINDER = AStarPathfindingAlgorithm.new($world.collision_manager, EuclideanHeuristic.new)
+
+##
+# The directions that we use as a random offset when not walking to the facing direction.
+OFFSET_DIRECTIONS = Direction::NESW.to_a
+
+module WalkToMobExtension
+ def walk_to(entity, front: false, behind: false)
+ if [front, behind].count { |option| option == true } > 1
+ fail 'Can only specify one of "front" or "behind"'
+ end
+
+ position = self.position
+ target_position = entity.position
+ target_distance = position.get_distance(target_position)
+ target_offset = walk_to_offset(entity)
+ target_offset_direction = OFFSET_DIRECTIONS.sample
+ target_facing_direction = walk_to_facing_direction(entity)
+
+ unless target_facing_direction.nil?
+ if front
+ target_offset_direction = target_facing_direction
+ elsif behind
+ target_offset_direction = target_facing_direction.opposite
+ end
+ end
+
+ target_offset_position = target_position.step(target_offset, target_offset_direction)
+ return if target_offset_position.eql?(position)
+
+ pathfinder = FOLLOW_PATH_FINDER
+ path = pathfinder.find(position, target_offset_position)
+ path.each { |tile| self.walking_queue.add_step(tile) }
+ end
+end
+
+MobExtension::register(WalkToMobExtension)
+
+private
+
+##
+# Gets the number of tiles away from an entity's actual origin that we can
+# walk to.
+def walk_to_offset(entity)
+ case entity.entity_type
+ when EntityType::DYNAMIC_OBJECT, EntityType::STATIC_OBJECT
+ return max(entity.definition.width, entity.definition.length) + 1
+ when EntityType::NPC
+ return entity.definition.size + 1
+ when EntityType::PLAYER
+ return 1
+ else
+ fail "walk_to_offset called with invalid entity type: #{type.to_s}"
+ end
+end
+
+##
+# Gets the direction that an entity is facing, so a mob can walk up infront of them.
+def walk_to_facing_direction(entity)
+ case entity.entity_type
+ when EntityType::DYNAMIC_OBJECT, EntityType::STATIC_OBJECT
+ return Direction::WNES[entity.orientation]
+ when EntityType::NPC, EntityType::PLAYER
+ return entity.last_direction
+ else
+ fail "walk_to_offset called with invalid entity type: #{type.to_s}"
+ end
+end
\ No newline at end of file
diff --git a/game/src/main/org/apollo/game/model/entity/Mob.java b/game/src/main/org/apollo/game/model/entity/Mob.java
index 0c06a050..82cdba96 100644
--- a/game/src/main/org/apollo/game/model/entity/Mob.java
+++ b/game/src/main/org/apollo/game/model/entity/Mob.java
@@ -103,6 +103,11 @@ public abstract class Mob extends Entity {
*/
private Direction firstDirection = Direction.NONE;
+ /**
+ * The last facing direction of this mob.
+ */
+ private Direction lastDirection = Direction.NORTH;
+
/**
* This mob's second movement direction.
*/
@@ -196,8 +201,8 @@ public abstract class Mob extends Entity {
*/
public final Direction[] getDirections() {
if (firstDirection != Direction.NONE) {
- return secondDirection == Direction.NONE ? new Direction[]{ firstDirection }
- : new Direction[]{ firstDirection, secondDirection };
+ return secondDirection == Direction.NONE ? new Direction[]{firstDirection}
+ : new Direction[]{firstDirection, secondDirection};
}
return Direction.EMPTY_DIRECTION_ARRAY;
@@ -268,6 +273,15 @@ public abstract class Mob extends Entity {
return inventory;
}
+ /**
+ * Gets the last facing direction of this mob.
+ *
+ * @return The last direction this mob was facing.
+ */
+ public Direction getLastDirection() {
+ return lastDirection;
+ }
+
/**
* Gets this mob's local npc {@link List}.
*
@@ -425,6 +439,15 @@ public abstract class Mob extends Entity {
blockSet.add(SynchronizationBlock.createInteractingMobBlock(mob.getInteractionIndex()));
}
+ /**
+ * Set the last direction this mob was facing.
+ *
+ * @param lastDirection The direction to set.
+ */
+ public void setLastDirection(Direction lastDirection) {
+ this.lastDirection = lastDirection;
+ }
+
/**
* Sets the {@link Position} of this mob.
*
@@ -470,7 +493,7 @@ public abstract class Mob extends Entity {
* Gets the number of tiles this mob occupies.
*
* @return The number of tiles this mob occupies.
- */
+ */
public int size() {
return definition.map(NpcDefinition::getSize).orElse(1);
}
@@ -548,4 +571,5 @@ public abstract class Mob extends Entity {
world.schedule(new SkillNormalizationTask(this));
}
+
}
\ No newline at end of file
diff --git a/game/src/main/org/apollo/game/model/entity/WalkingQueue.java b/game/src/main/org/apollo/game/model/entity/WalkingQueue.java
index f2feeac7..7db2579d 100644
--- a/game/src/main/org/apollo/game/model/entity/WalkingQueue.java
+++ b/game/src/main/org/apollo/game/model/entity/WalkingQueue.java
@@ -146,6 +146,7 @@ public final class WalkingQueue {
} else {
previousPoints.add(next);
position = new Position(next.getX(), next.getY(), height);
+ mob.setLastDirection(firstDirection);
if (running) {
next = points.poll();
@@ -158,6 +159,7 @@ public final class WalkingQueue {
} else {
previousPoints.add(next);
position = new Position(next.getX(), next.getY(), height);
+ mob.setLastDirection(secondDirection);
}
}
}