Release Healer Man 0.1.36

This commit is contained in:
phenom committed 2026-09-12 19:16:01 -04:00
1 parent 40d9f346d8
commit 03a4e3f51c
22 files changed
+1389 -153

No files matched your search

+43 -34
View File
@@ -27,7 +27,7 @@ import {
sampleMobAnimation,
} from "../game/mobAnimation";
import type { MobAnimationState } from "../game/mobAnimation";
import { useCombatStore } from "../game/combatStore";
import { useCombatStore, type MobCombatState } from "../game/combatStore";
import { PRIMARY_MOUSE_BUTTON } from "../game/inputManager";
import { TimedEffectStrip } from "../ui/TimedEffectStrip";
import { AuraStrip } from "../ui/AuraStrip";
@@ -76,9 +76,10 @@ import {
resolveMobAnimationInterval,
} from "./mobAnimationCadence";
import { useGameGLTF } from "./useGameGLTF";
import { onlineSynchronizedMobTransform, requestOnlineInteraction } from "../game/onlineSessionRuntime";
import { onlineSessionOwnsWorldSimulation, onlineSynchronizedMobTransform, requestOnlineInteraction } from "../game/onlineSessionRuntime";
import { isMobAggroRangeVisible } from "./mobAggroRangeVisibility";
import { DungeonOccludedHtml } from "./DungeonOccludedHtml";
import { useMobNavigation } from "./mobNavigation";
const MOVEMENT_LOCKING_MOB_STATUSES: ReadonlySet<string> = new Set([
"stun", "root", "sleep", "knockdown", "freeze", "charm", "confuse",
@@ -119,8 +120,32 @@ function mobTargetPosition(
playerPosition: MobVector3,
): MobVector3 {
if (!targetActorId || targetActorId === PLAYER_AGGRO_ID) return playerPosition;
const summon = useCombatStore.getState().summons.find((candidate) => candidate.id === targetActorId);
if (summon) return summon.position;
return getPartyRuntimePosition(targetActorId) ?? playerPosition;
}
function navigateMob(
navigator: ReturnType<typeof useMobNavigation>, mob: MobCombatState,
position: MutableMobVector3, target: MobVector3, home: MobVector3,
maximumStep: number, active: boolean, nowMs: number, controlled = false,
): void {
const game = useGameStore.getState();
const hostileRanges = mob.attacks.filter((attack) => attack.target === "primary"
|| attack.target === "random-party" || attack.target === "nearby-party").map((attack) => attack.range);
const result = navigator.step({
id: mob.id, targetId: mob.targetActorId, position, target, home,
phase: mob.combatPhase, health: mob.health, engaged: mob.engaged,
active: active && onlineSessionOwnsWorldSimulation() && !game.paused && !game.mapOpen && !game.companionOpen,
casting: mob.activeCast !== null, controlled,
attackRange: hostileRanges.length ? Math.max(...hostileRanges) : mob.attackRange,
leashRange: mob.leashRange, maximumStep, nowMs,
});
if (result === "reset" || result === "home") {
useCombatStore.getState().resetMobForNavigation(mob.id,
result === "home" || Math.hypot(position[0] - home[0], position[1] - home[1], position[2] - home[2]) <= 0.85, nowMs);
}
}
const PROXIMITY_CHECK_INTERVAL = 0.3;
const AGGRO_RANGE_VISIBILITY_CHECK_INTERVAL = 0.25;
let preparedCreatureTemplates = new WeakMap<Object3D, Object3D>();
@@ -771,23 +796,6 @@ function isPlayerOrPartyNear(points: readonly MobVector3[], maxDistance: number)
});
}
function stepToward(
current: MutableMobVector3,
target: MobVector3,
maximumStep: number,
): number {
const dx = target[0] - current[0];
const dy = target[1] - current[1];
const dz = target[2] - current[2];
const distance = Math.hypot(dx, dy, dz);
if (distance <= 0.001) return 0;
const step = Math.min(Math.max(0, maximumStep), distance);
current[0] += dx / distance * step;
current[1] += dy / distance * step;
current[2] += dz / distance * step;
return distance;
}
/**
* Activates content when the player or an autonomous party member approaches.
* The stores are read
@@ -1159,6 +1167,7 @@ function RoamingPack({
useOriginalModels,
allowedRuntimeIds,
}: RoamingPackProps) {
const navigator = useMobNavigation();
const path = useMemo(() => createLoopedPath(definition.waypoints), [definition.waypoints]);
const distanceRef = useRef(definition.startDistance ?? 0);
const { scratchPositions, scratchForwards, chasePositions } = useMemo(() => {
@@ -1277,7 +1286,7 @@ function RoamingPack({
const maximumStep = Math.min(delta, 0.05)
* mob!.moveSpeed
* Math.max(0.1, 1 - slow);
if (!movementLocked && feared) {
if (active && !movementLocked && feared) {
stepFearedMobAway(
chasePosition,
targetPosition,
@@ -1286,19 +1295,16 @@ function RoamingPack({
mob!.leashRange,
chasePosition,
);
} else if (!movementLocked && mob!.combatPhase === "chasing") {
stepToward(
chasePosition,
targetPosition,
maximumStep,
);
}
navigateMob(navigator, mob!, chasePosition, targetPosition,
mob!.homePosition ?? patrolPosition, maximumStep, active, now, movementLocked || feared);
position = chasePosition;
const facingSign = feared && !movementLocked ? -1 : 1;
forward[0] = (targetPosition[0] - position[0]) * facingSign;
forward[2] = (targetPosition[2] - position[2]) * facingSign;
} else if (mob?.combatPhase === "returning") {
stepToward(chasePosition, patrolPosition, Math.min(delta, 0.05) * mob.moveSpeed * 1.25);
navigateMob(navigator, mob, chasePosition, targetPosition,
mob.homePosition ?? patrolPosition, Math.min(delta, 0.05) * mob.moveSpeed * 1.25, active, now);
position = chasePosition;
} else {
chasePosition[0] = patrolPosition[0];
@@ -1360,6 +1366,7 @@ interface StaticPopulationProps {
}
function StaticPopulationMember({ spawn, entities, active, showLabels, useOriginalModels }: StaticPopulationProps) {
const navigator = useMobNavigation();
const entity = entities[spawn.entityId];
const groupRef = useRef<Group>(null);
const positionRef = useRef<MutableMobVector3>([...spawn.position]);
@@ -1398,7 +1405,7 @@ function StaticPopulationMember({ spawn, entities, active, showLabels, useOrigin
feared = mob.statuses.some((status) => status.endsAt > now && status.kind === "fear");
const slow = mob.statuses.find((status) => status.endsAt > now && status.kind === "slow")?.magnitude ?? 0;
const maximumStep = Math.min(delta, 0.05) * mob.moveSpeed * Math.max(0.1, 1 - slow);
if (!movementLocked && feared) {
if (active && !movementLocked && feared) {
stepFearedMobAway(
position,
targetPosition,
@@ -1407,11 +1414,12 @@ function StaticPopulationMember({ spawn, entities, active, showLabels, useOrigin
mob.leashRange,
position,
);
} else if (!movementLocked && mob.combatPhase === "chasing") {
stepToward(position, targetPosition, maximumStep);
}
navigateMob(navigator, mob, position, targetPosition, mob.homePosition ?? spawn.position,
maximumStep, active, now, movementLocked || feared);
} else if (mob?.combatPhase === "returning") {
stepToward(position, spawn.position, Math.min(delta, 0.05) * mob.moveSpeed * 1.25);
navigateMob(navigator, mob, position, targetPosition, mob.homePosition ?? spawn.position,
Math.min(delta, 0.05) * mob.moveSpeed * 1.25, active, now);
} else {
position[0] = spawn.position[0];
position[1] = spawn.position[1];
@@ -1500,6 +1508,7 @@ function SummonedPopulationMember({
readonly active: boolean;
readonly useOriginalModels: boolean;
}) {
const navigator = useMobNavigation();
const groupRef = useRef<Group>(null);
const initial = getMobRuntimeTransform(instanceId)?.position ?? [0, 0, 0];
const positionRef = useRef<MutableMobVector3>([...initial]);
@@ -1515,7 +1524,7 @@ function SummonedPopulationMember({
const feared = mob.statuses.some((status) => status.endsAt > now && status.kind === "fear");
const slow = mob.statuses.find((status) => status.endsAt > now && status.kind === "slow")?.magnitude ?? 0;
const maximumStep = Math.min(delta, 0.05) * mob.moveSpeed * Math.max(0.1, 1 - slow);
if (!movementLocked && feared) {
if (active && !movementLocked && feared) {
stepFearedMobAway(
position,
targetPosition,
@@ -1524,9 +1533,9 @@ function SummonedPopulationMember({
mob.leashRange,
position,
);
} else if (!movementLocked && mob.combatPhase === "chasing") {
stepToward(position, targetPosition, maximumStep);
}
navigateMob(navigator, mob, position, targetPosition, mob.homePosition ?? initial,
maximumStep, active, now, movementLocked || feared);
const facingSign = feared && !movementLocked ? -1 : 1;
const yaw = Math.atan2(
(targetPosition[0] - position[0]) * facingSign,