Update Healer Man server
This commit is contained in:
1 parent
6114a2295f
commit
69809f7228
593 files changed
+903643
-40774
No files matched your search
@@ -0,0 +1,65 @@
|
||||
import type { ActiveAura } from "./combatAuras";
|
||||
import { combatHasLineOfSight, combatNavigationGraph, combatSpatialActor, projectCombatGroundPoint } from "./combatSpatial";
|
||||
import { createEnemyFearMovementRuntime, resolveEnemyFearActorAuras } from "./enemyFearMovementRuntime";
|
||||
import { stepEnemyFearTowardDestination, type EnemyFearPosition } from "./enemyFearMovement";
|
||||
import { enemyTriggeredShoutMovementSpeedMultiplier } from "./enemyTriggeredShouts";
|
||||
import { findNearestPartyNavigationSurface, findPartyNavigationPath } from "./partyNavigation";
|
||||
|
||||
const runtime = createEnemyFearMovementRuntime();
|
||||
const distance = (a: EnemyFearPosition, b: EnemyFearPosition) => Math.hypot(b[0] - a[0], b[1] - a[1], b[2] - a[2]);
|
||||
|
||||
/** A fear-only position step; callers always suppress summon attacks and casts while it applies. */
|
||||
export function sourceFearSummonPosition(input: {
|
||||
readonly id: string;
|
||||
readonly position: EnemyFearPosition;
|
||||
readonly auras: readonly ActiveAura[];
|
||||
readonly now: number;
|
||||
readonly deltaSeconds: number;
|
||||
readonly movementMultiplier: number;
|
||||
}): EnemyFearPosition | null {
|
||||
const control = resolveEnemyFearActorAuras(input.auras, input.id, input.now);
|
||||
if (!control.fearAura) { runtime.clearActor(input.id); return null; }
|
||||
// Existing generic control remains authoritative, even if its timer is shorter than source fear.
|
||||
const otherMovementControl = input.auras.some((aura) => aura.targetId === input.id
|
||||
&& (aura.expiresAt === null || aura.expiresAt > input.now)
|
||||
&& aura.definition.id.startsWith("enemy-control:")
|
||||
&& !aura.definition.id.startsWith("enemy-control:source-spell-")
|
||||
&& !/:slow$|:interrupt$|:silence$/.test(aura.definition.id));
|
||||
if (otherMovementControl) { runtime.clearActor(input.id); return input.position; }
|
||||
const graph = combatNavigationGraph();
|
||||
if (!graph) { runtime.clearActor(input.id); return input.position; }
|
||||
const path = (current: EnemyFearPosition, goal: EnemyFearPosition) => findPartyNavigationPath(graph, current, goal, {
|
||||
maxSnapDistance: 0.75,
|
||||
routeSegmentAllowed: combatHasLineOfSight,
|
||||
});
|
||||
const plan = runtime.plan({
|
||||
actorId: input.id, fearAura: control.fearAura, stunEndsAtMs: control.stunEndsAtMs,
|
||||
nowMs: input.now, currentPosition: input.position, baseMovementSpeed: 4 * input.movementMultiplier,
|
||||
sourceSpeedMultiplier: enemyTriggeredShoutMovementSpeedMultiplier(input.auras, input.id, input.now),
|
||||
worldUnitsPerYard: 1,
|
||||
getSourcePosition: (id) => combatSpatialActor(id)?.position ?? null,
|
||||
}, {
|
||||
projectDestination: (requested, current) => {
|
||||
const projected = projectCombatGroundPoint(requested, distance(current, requested), current);
|
||||
if (!projected || distance(requested, projected) > 1.25) return null;
|
||||
return findNearestPartyNavigationSurface(graph, projected, 0.75)?.position ?? null;
|
||||
},
|
||||
hasLineOfSight: combatHasLineOfSight,
|
||||
pathLength: (current, goal) => {
|
||||
const route = path(current, goal);
|
||||
return route ? route.reduce((length, point, index) => length + (index ? distance(route[index - 1], point) : 0), 0) : null;
|
||||
},
|
||||
});
|
||||
if (plan.kind !== "move") return input.position;
|
||||
const route = path(input.position, plan.destination);
|
||||
if (!route) { runtime.clearActor(input.id); return input.position; }
|
||||
const waypoint = route.find((point) => distance(input.position, point) > 0.05);
|
||||
if (!waypoint) return input.position;
|
||||
const maximumStep = plan.movementSpeed * Math.max(0, Math.min(1, input.deltaSeconds));
|
||||
const requested = stepEnemyFearTowardDestination(input.position, waypoint, maximumStep);
|
||||
const projected = projectCombatGroundPoint(requested, maximumStep, input.position);
|
||||
if (!projected || distance(input.position, projected) > maximumStep + 0.05
|
||||
|| !combatHasLineOfSight(input.position, projected)
|
||||
|| !findNearestPartyNavigationSurface(graph, projected, 0.15)) return input.position;
|
||||
return projected;
|
||||
}
|
||||
Reference in new issue
Block a user