import { PlayerCtor, ProjectileType, RpgCommonPlayer, Direction, MovementStrategy, MovementOptions } from '@rpgjs/common'; import { RpgMap } from '../rooms/map'; import { RpgPlayer } from './Player'; export type CallbackTileMove = (player: RpgPlayer, map: RpgMap) => Direction[]; export type CallbackTurnMove = (player: RpgPlayer, map: RpgMap) => string; export type MoveRouteCallback = (player: RpgPlayer, map: RpgMap) => string | Direction | Direction[] | Promise | undefined; export type MoveRoute = string | Promise | Direction | Direction[] | MoveRouteCallback; export type Routes = MoveRoute[]; export type { MovementOptions }; /** * Options for moveRoutes method */ export interface MoveRoutesOptions { /** * Callback function called when the player gets stuck (cannot move towards target) * * This callback is triggered when the player is trying to move but cannot make progress * towards the target position, typically due to obstacles or collisions. * * @param player - The player instance that is stuck * @param target - The target position the player was trying to reach * @param currentPosition - The current position of the player * @returns If true, the route will continue; if false, the route will be cancelled * * @example * ```ts * await player.moveRoutes([Move.right()], { * onStuck: (player, target, currentPos) => { * console.log('Player is stuck!'); * return false; // Cancel the route * } * }); * ``` */ onStuck?: (player: RpgPlayer, target: { x: number; y: number; }, currentPosition: { x: number; y: number; }) => boolean | void; /** * Time in milliseconds to wait before considering the player stuck (default: 500ms) * * The player must be unable to make progress for this duration before onStuck is called. */ stuckTimeout?: number; /** * Minimum distance change in pixels to consider movement progress (default: 1 pixel) * * If the player moves less than this distance over the stuckTimeout period, they are considered stuck. */ stuckThreshold?: number; /** * Multiplier applied to the player's frequency delay between route segments. * * The default keeps legacy route timing. Lower values are useful for generated * routes that need smoother visual motion, such as Studio random movement. */ frequencyRatio?: number; } export declare enum Frequency { Lowest = 600, Lower = 400, Low = 200, High = 100, Higher = 50, Highest = 25, None = 0 } export declare enum Speed { Slowest = 0.2, Slower = 0.5, Slow = 1, Normal = 3, Fast = 5, Faster = 7, Fastest = 10 } declare class MoveList { private static perlinNoise; private static randomCounter; private static callCounter; private static playerMoveStates; private static readonly STUCK_THRESHOLD; private getRandomDirectionIndex; /** * Clears the movement state for a specific player * * Should be called when a player changes map or is destroyed to prevent * memory leaks and stale stuck detection data. * * @param playerId - The ID of the player to clear state for * * @example * ```ts * // Clear state when player leaves map * Move.clearPlayerState(player.id); * ``` */ static clearPlayerState(playerId: string): void; /** * Clears all player movement states * * Useful for cleanup during server shutdown or when resetting game state. * * @example * ```ts * // Clear all states on server shutdown * Move.clearAllPlayerStates(); * ``` */ static clearAllPlayerStates(): void; repeatMove(direction: Direction, repeat: number): Direction[]; private repeatTileMove; right(repeat?: number): Direction[]; left(repeat?: number): Direction[]; up(repeat?: number): Direction[]; down(repeat?: number): Direction[]; wait(sec: number): Promise; random(repeat?: number): Direction[]; tileRight(repeat?: number): CallbackTileMove; tileLeft(repeat?: number): CallbackTileMove; tileUp(repeat?: number): CallbackTileMove; tileDown(repeat?: number): CallbackTileMove; tileRandom(repeat?: number): CallbackTileMove; private _awayFromPlayerDirection; private _towardPlayerDirection; private _awayFromPlayer; towardPlayer(player: RpgPlayer, repeat?: number): CallbackTileMove; tileTowardPlayer(player: RpgPlayer, repeat?: number): CallbackTileMove; awayFromPlayer(player: RpgPlayer, repeat?: number): CallbackTileMove; tileAwayFromPlayer(player: RpgPlayer, repeat?: number): CallbackTileMove; turnLeft(): string; turnRight(): string; turnUp(): string; turnDown(): string; turnRandom(): string; turnAwayFromPlayer(otherPlayer: RpgPlayer): CallbackTurnMove; turnTowardPlayer(otherPlayer: RpgPlayer): CallbackTurnMove; } export declare const Move: MoveList; /** * Move Manager mixin * * Adds comprehensive movement management capabilities to a player class. * Provides access to all available movement strategies and utility methods * for common movement patterns. * * ## Features * - **Strategy Management**: Add, remove, and query movement strategies * - **Predefined Movements**: Quick access to common movement patterns * - **Composite Movements**: Combine multiple strategies * - **Physics Integration**: Seamless integration with the deterministic @rpgjs/physic engine * * ## Available Movement Strategies * - `LinearMove`: Constant velocity movement * - `Dash`: Quick burst movement * - `Knockback`: Push effect with decay * - `PathFollow`: Follow waypoint sequences * - `Oscillate`: Back-and-forth patterns * - `SeekAvoid`: AI target seeking with local obstacle avoidance * - `LinearRepulsion`: Smoother obstacle avoidance * - `IceMovement`: Slippery surface physics * - `ProjectileMovement`: Ballistic trajectories * - `CompositeMovement`: Combine multiple strategies * * @param Base - The base class to extend * @returns A new class with comprehensive movement management capabilities * * @example * ```ts * // Basic usage * class MyPlayer extends WithMoveManager(RpgCommonPlayer) { * onInput(direction: { x: number, y: number }) { * // Apply dash movement on input * this.dash(direction, 8, 200); * } * * onIceTerrain() { * // Switch to ice physics * this.clearMovements(); * this.applyIceMovement({ x: 1, y: 0 }, 4); * } * * createPatrol() { * // Create patrol path * const waypoints = [ * { x: 100, y: 100 }, * { x: 300, y: 100 }, * { x: 300, y: 300 } * ]; * this.followPath(waypoints, 2, true); * } * } * ``` */ /** * Move Manager Mixin * * Provides comprehensive movement management capabilities to any class. This mixin handles * various types of movement including target seeking, physics-based movement, route following, * and advanced movement strategies like dashing, knockback, and projectile movement. * * @param Base - The base class to extend with movement management * @returns Extended class with movement management methods * * @example * ```ts * class MyPlayer extends WithMoveManager(BasePlayer) { * constructor() { * super(); * this.frequency = Frequency.High; * } * } * * const player = new MyPlayer(); * player.moveTo({ x: 100, y: 100 }); * player.dash({ x: 1, y: 0 }, 8, 200); * ``` */ export declare function WithMoveManager(Base: TBase): PlayerCtor; /** * Interface for Move Manager functionality * * Provides comprehensive movement management capabilities including target seeking, * physics-based movement, route following, and advanced movement strategies. * This interface defines the public API of the MoveManager mixin. */ export interface IMoveManager { /** * Whether the player passes through other players * * When `true`, the player can walk through other player entities without collision. * This is useful for busy areas where players shouldn't block each other. * * @default true * * @example * ```ts * // Disable player-to-player collision * player.throughOtherPlayer = true; * * // Enable player-to-player collision * player.throughOtherPlayer = false; * ``` */ throughOtherPlayer: boolean; /** * Whether the player goes through all characters (players and events) * * When `true`, the player can walk through all character entities (both players and events) * without collision. Walls and obstacles still block movement. * This takes precedence over `throughOtherPlayer` and `throughEvent`. * * @default false * * @example * ```ts * // Enable ghost mode - pass through all characters * player.through = true; * * // Disable ghost mode * player.through = false; * ``` */ through: boolean; /** * Whether the player passes through events (NPCs, objects) * * When `true`, the player can walk through event entities without collision. * This is useful for NPCs that shouldn't block player movement. * * @default false * * @example * ```ts * // Allow passing through events * player.throughEvent = true; * * // Block passage through events * player.throughEvent = false; * ``` */ throughEvent: boolean; /** Frequency for movement timing (milliseconds between movements) */ frequency: number; /** Whether direction changes are locked (prevents automatic direction changes) */ directionFixed: boolean; /** Whether animation changes are locked (prevents automatic animation changes) */ animationFixed: boolean; /** * Add a custom movement strategy to this entity * * Returns a Promise that resolves when the movement completes. * * @param strategy - The movement strategy to add * @param options - Optional callbacks for movement lifecycle events * @returns Promise that resolves when the movement completes */ addMovement(strategy: MovementStrategy, options?: MovementOptions): Promise; /** * Remove a specific movement strategy from this entity * * @param strategy - The strategy instance to remove * @returns True if the strategy was found and removed */ removeMovement(strategy: MovementStrategy): boolean; /** * Remove all active movement strategies from this entity */ clearMovements(): void; /** * Check if this entity has any active movement strategies * * @returns True if entity has active movements */ hasActiveMovements(): boolean; /** * Get all active movement strategies for this entity * * @returns Array of active movement strategies */ getActiveMovements(): MovementStrategy[]; /** * Move toward a target player or position using local obstacle avoidance * * @param target - Target player or position to move toward */ moveTo(target: RpgCommonPlayer | { x: number; y: number; }): void; /** * Stop the current moveTo behavior */ stopMoveTo(): void; /** * Perform a dash movement in the specified direction * * The total speed is calculated by adding the player's base speed to the additional speed. * Returns a Promise that resolves when the dash completes. * * @param direction - Normalized direction vector * @param additionalSpeed - Extra speed added on top of base speed (default: 4) * @param duration - Duration in milliseconds (default: 200) * @param options - Optional callbacks for movement lifecycle events * @returns Promise that resolves when the dash completes */ dash(direction: { x: number; y: number; }, additionalSpeed?: number, duration?: number, options?: MovementOptions): Promise; /** * Apply knockback effect in the specified direction * * The force is scaled by the player's base speed for consistent behavior. * Returns a Promise that resolves when the knockback completes. * * @param direction - Normalized direction vector * @param force - Force multiplier applied to base speed (default: 5) * @param duration - Duration in milliseconds (default: 300) * @param options - Optional callbacks for movement lifecycle events * @returns Promise that resolves when the knockback completes */ knockback(direction: { x: number; y: number; }, force?: number, duration?: number, options?: MovementOptions): Promise; /** * Follow a sequence of waypoints * * Speed is calculated from the player's base speed multiplied by the speedMultiplier. * * @param waypoints - Array of x,y positions to follow * @param speedMultiplier - Multiplier applied to base speed (default: 0.5) * @param loop - Whether to loop back to start (default: false) */ followPath(waypoints: Array<{ x: number; y: number; }>, speedMultiplier?: number, loop?: boolean): void; /** * Apply oscillating movement pattern * * @param direction - Primary oscillation axis (normalized) * @param amplitude - Maximum distance from center (default: 50) * @param period - Time for complete cycle in ms (default: 2000) */ oscillate(direction: { x: number; y: number; }, amplitude?: number, period?: number): void; /** * Apply ice movement physics * * Max speed is calculated from the player's base speed multiplied by the speedFactor. * * @param direction - Target movement direction * @param speedFactor - Factor multiplied with base speed for max speed (default: 1.0) */ applyIceMovement(direction: { x: number; y: number; }, speedFactor?: number): void; /** * Shoot a projectile in the specified direction * * Speed is calculated from the player's base speed multiplied by the speedFactor. * * @param type - Type of projectile trajectory * @param direction - Normalized direction vector * @param speedFactor - Factor multiplied with base speed (default: 50) */ shootProjectile(type: ProjectileType, direction: { x: number; y: number; }, speedFactor?: number): void; /** * Give an itinerary to follow using movement strategies * * @param routes - Array of movement instructions to execute * @param options - Optional configuration including onStuck callback * @returns Promise that resolves when all routes are completed */ moveRoutes(routes: Routes, options?: MoveRoutesOptions): Promise; /** * Give a path that repeats itself in a loop to a character * * @param routes - Array of movement instructions to repeat infinitely */ infiniteMoveRoute(routes: Routes, options?: MoveRoutesOptions): void; /** * Stop an infinite movement * * @param force - Forces the stop of the infinite movement immediately */ breakRoutes(force?: boolean): void; /** * Replay an infinite movement */ replayRoutes(): void; }