diff --git a/src/plugins/commands/follow-command.js b/src/plugins/commands/follow-command.js index 2bc39d13e..663b3318d 100644 --- a/src/plugins/commands/follow-command.js +++ b/src/plugins/commands/follow-command.js @@ -1,9 +1,8 @@ import { ActionType } from '../plugin'; import { world } from '../../game-server'; -import { followActor } from '../../world/actor/player/action/follow-action'; module.exports = { type: ActionType.COMMAND, commands: 'follow', - action: details => followActor(details.player, world.npcList[0]) + action: details => details.player.follow(world.npcList[0]) }; diff --git a/src/plugins/player/follow-player-plugin.js b/src/plugins/player/follow-player-plugin.js index 5c17b1539..a298934a8 100644 --- a/src/plugins/player/follow-player-plugin.js +++ b/src/plugins/player/follow-player-plugin.js @@ -1,8 +1,7 @@ import { ActionType } from '../plugin'; -import { followActor } from '../../world/actor/player/action/follow-action'; module.exports = { type: ActionType.PLAYER_ACTION, options: 'follow', - action: details => followActor(details.player, details.otherPlayer) + action: details => details.player.follow(details.otherPlayer) }; diff --git a/src/world/actor/actor.ts b/src/world/actor/actor.ts index 029540788..51d57e391 100644 --- a/src/world/actor/actor.ts +++ b/src/world/actor/actor.ts @@ -10,30 +10,31 @@ import { CombatAction } from '@server/world/actor/player/action/combat-action'; import { Pathfinding } from '@server/world/actor/pathfinding'; import { Subject } from 'rxjs'; import { ActionCancelType } from '@server/world/actor/player/action/action'; +import { filter, take } from 'rxjs/operators'; /** * Handles an actor within the game world. */ export abstract class Actor { + public readonly updateFlags: UpdateFlags; + public readonly skills: Skills; + public readonly metadata: { [key: string]: any } = {}; + public readonly actionsCancelled: Subject; + public readonly movementEvent: Subject; + public pathfinding: Pathfinding; + public lastMovementPosition: Position; + private readonly _walkingQueue: WalkingQueue; + private readonly _inventory: ItemContainer; + private readonly _bank: ItemContainer; private _position: Position; private _lastMapRegionUpdatePosition: Position; private _worldIndex: number; - public readonly updateFlags: UpdateFlags; - private readonly _walkingQueue: WalkingQueue; private _walkDirection: number; private _runDirection: number; private _faceDirection: number; - private readonly _inventory: ItemContainer; - private readonly _bank: ItemContainer; - public readonly skills: Skills; private _busy: boolean; - public readonly metadata: { [key: string]: any } = {}; private _combatActions: CombatAction[]; - public pathfinding: Pathfinding; - public lastMovementPosition: Position; - public readonly actionsCancelled: Subject; - public readonly movementEvent: Subject; protected constructor() { this.updateFlags = new UpdateFlags(); @@ -52,7 +53,80 @@ export abstract class Actor { } public damage(amount: number, damageType: DamageType = DamageType.DAMAGE): void { + } + + public async moveBehind(target: Actor): Promise { + const distance = Math.floor(this.position.distanceBetween(target.position)); + if(distance > 16) { + this.clearFaceActor(); + return false; + } + + let ignoreDestination = true; + let desiredPosition = target.position; + if(target.lastMovementPosition) { + desiredPosition = target.lastMovementPosition; + ignoreDestination = false; + } + + await this.pathfinding.walkTo(desiredPosition, { + pathingSearchRadius: distance + 2, + ignoreDestination + }); + + return true; + } + + public follow(target: Actor): void { + this.face(target, false, false, false); + + this.moveBehind(target); + const subscription = target.movementEvent.subscribe(() => this.moveBehind(target)); + + this.actionsCancelled.pipe( + filter(type => type !== 'pathing-movement'), + take(1) + ).subscribe(() => { + subscription.unsubscribe(); + this.face(null); + }); + } + + public async walkTo(target: Actor): Promise { + const distance = Math.floor(this.position.distanceBetween(target.position)); + if(distance > 16) { + this.clearFaceActor(); + this.metadata.faceActorClearedByWalking = true; + return false; + } + + if(distance <= 1) { + return false; + } + + const desiredPosition = target.position; + + await this.pathfinding.walkTo(desiredPosition, { + pathingSearchRadius: distance + 2, + ignoreDestination: true + }); + + return true; + } + + public tail(target: Actor): void { + this.face(target, false, false, false); + + this.walkTo(target); + const subscription = target.movementEvent.subscribe(() => this.walkTo(target)); + this.actionsCancelled.pipe( + filter(type => type !== 'pathing-movement'), + take(1) + ).subscribe(() => { + subscription.unsubscribe(); + this.face(null); + }); } public face(face: Position | Actor | null, clearWalkingQueue: boolean = true, autoClear: boolean = true, clearedByWalking: boolean = true): void { diff --git a/src/world/actor/player/action/follow-action.ts b/src/world/actor/player/action/follow-action.ts deleted file mode 100644 index dae7c516f..000000000 --- a/src/world/actor/player/action/follow-action.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Actor } from '@server/world/actor/actor'; - - -export const walkToActor = async (actor: Actor, target: Actor): Promise => { - const distance = Math.floor(target.position.distanceBetween(actor.position)); - if(distance > 16) { - actor.clearFaceActor(); - actor.metadata.faceActorClearedByWalking = true; - throw new Error(`Distance too great!`); - } - - let ignoreDestination = true; - let desiredPosition = target.position; - if(target.lastMovementPosition) { - desiredPosition = target.lastMovementPosition; - ignoreDestination = false; - } - - await actor.pathfinding.walkTo(desiredPosition, { - pathingSearchRadius: distance + 2, - ignoreDestination - }); - - return Promise.resolve(true); -}; - -export const followActor = (follower: Actor, following: Actor): void => { - follower.face(following, false, false, false); - - walkToActor(follower, following); - - const subscription = following.movementEvent.subscribe(() => { - walkToActor(follower, following); - }); - const actionCancelled = follower.actionsCancelled.subscribe(type => { - if(type !== 'pathing-movement') { - subscription.unsubscribe(); - actionCancelled.unsubscribe(); - follower.face(null); - } - }); -};