Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | 3x 3x 136x 4x 1x 3x 3x 3x 3x 3x 3x 3x 3x 3x 2x 135x | /**
* Acceleration Utilities for Movement System
*
* Pure functions for calculating acceleration-based running speeds.
* Extracted for testability and reusability across training and combat screens.
*
* @module systems/movement/helpers/accelerationUtils
* @category Movement
* @korean 가속 유틸리티
*/
/**
* Step distance thresholds for foot laterality alternation
* 발 측면성 교대를 위한 걸음 거리 임계값
*/
export const STEP_DISTANCE_THRESHOLDS = {
/**
* Average step length when walking (meters)
* 걷기 시 평균 걸음 길이
*/
WALK: 0.7,
/**
* Average step length when running (meters)
* 달리기 시 평균 걸음 길이
*/
RUN: 1.0,
} as const;
/**
* Constants for acceleration-based running (defaults for non-archetype usage)
*/
export const ACCELERATION_CONSTANTS = {
/** Default walking speed in m/s (when no archetype speed provided) */
DEFAULT_WALK_SPEED: 6.0,
/** Default running speed in m/s (when no archetype speed provided) */
DEFAULT_RUN_SPEED: 10.0,
/** Time to reach running speed in seconds */
TIME_TO_RUN: 1.5,
/** Threshold for considering direction "same" (cos(45°) = √2/2) */
DIRECTION_THRESHOLD: Math.cos(Math.PI / 4),
/** Running threshold as percentage of max speed (0-1) */
RUN_THRESHOLD_PERCENT: 0.9,
/** Epsilon for speed change detection (m/s) */
SPEED_CHANGE_EPSILON: 0.05,
} as const;
/**
* Calculate running threshold speed
* @param runSpeed - Maximum running speed (from archetype or default)
* @returns Speed at which movement is considered running (m/s)
*/
export function calculateRunThreshold(
runSpeed: number = ACCELERATION_CONSTANTS.DEFAULT_RUN_SPEED
): number {
return runSpeed * ACCELERATION_CONSTANTS.RUN_THRESHOLD_PERCENT;
}
/**
* Check if two directions are consistent (within threshold angle)
* @param currentDir Current direction vector
* @param lastDir Previous direction vector
* @returns True if directions are within 45° of each other
*/
export function isDirectionConsistent(
currentDir: { x: number; y: number },
lastDir: { x: number; y: number }
): boolean {
// If last direction is zero, consider any movement as consistent
if (lastDir.x === 0 && lastDir.y === 0) {
return true;
}
// Calculate dot product
const dot = currentDir.x * lastDir.x + currentDir.y * lastDir.y;
const magCurrent = Math.sqrt(currentDir.x ** 2 + currentDir.y ** 2);
const magLast = Math.sqrt(lastDir.x ** 2 + lastDir.y ** 2);
Iif (magCurrent === 0 || magLast === 0) {
return false;
}
// Clamp cosAngle to [-1, 1] to handle floating-point edge cases
const cosAngleRaw = dot / (magCurrent * magLast);
const cosAngle = Math.max(-1, Math.min(1, cosAngleRaw));
// Use >= to include exactly 45° as consistent (not trigger reset)
return cosAngle >= ACCELERATION_CONSTANTS.DIRECTION_THRESHOLD;
}
/**
* Calculate acceleration-based speed
* @param movementTime Accumulated movement time in same direction (seconds)
* @param walkSpeed - Walking speed in m/s (from archetype or default)
* @param runSpeed - Running speed in m/s (from archetype or default)
* @returns Interpolated speed between walk and run (m/s)
*/
export function calculateAcceleratedSpeed(
movementTime: number,
walkSpeed: number = ACCELERATION_CONSTANTS.DEFAULT_WALK_SPEED,
runSpeed: number = ACCELERATION_CONSTANTS.DEFAULT_RUN_SPEED
): number {
const progress = Math.min(movementTime / ACCELERATION_CONSTANTS.TIME_TO_RUN, 1.0);
return walkSpeed + (runSpeed - walkSpeed) * progress;
}
/**
* Check if speed change is meaningful (exceeds epsilon)
* @param oldSpeed Previous speed (m/s)
* @param newSpeed New speed (m/s)
* @returns True if change exceeds threshold
*/
export function isSpeedChangeMeaningful(oldSpeed: number, newSpeed: number): boolean {
return Math.abs(newSpeed - oldSpeed) >= ACCELERATION_CONSTANTS.SPEED_CHANGE_EPSILON;
}
/**
* Determine if player is running based on current speed
* @param speed Current speed (m/s)
* @param runSpeed - Maximum running speed (from archetype or default)
* @returns True if speed exceeds running threshold
*/
export function isRunningSpeed(
speed: number,
runSpeed: number = ACCELERATION_CONSTANTS.DEFAULT_RUN_SPEED
): boolean {
return speed >= calculateRunThreshold(runSpeed);
}
|