Release Healer Man 0.1.14

This commit is contained in:
phenom committed 2026-09-05 11:40:50 -04:00
1 parent 67d7a8ebe7
commit 7b41b102fa
147 files changed
+169892 -1342

No files matched your search

+100
View File
@@ -39,6 +39,7 @@ import {
} from "../game/partyRuntimeRegistry";
import { PLAYER_AGGRO_ID } from "../game/aggro";
import {
getMobRuntimeTransform,
registerMobRuntime,
updateMobRuntimePosition,
} from "../game/mobRuntimeRegistry";
@@ -1450,6 +1451,100 @@ function StaticPopulationCluster({
);
}
function SummonedPopulationMember({
instanceId,
entity,
active,
useOriginalModels,
}: {
readonly instanceId: string;
readonly entity: PopulationEntityDefinition;
readonly active: boolean;
readonly useOriginalModels: boolean;
}) {
const groupRef = useRef<Group>(null);
const initial = getMobRuntimeTransform(instanceId)?.position ?? [0, 0, 0];
const positionRef = useRef<MutableMobVector3>([...initial]);
useFrame((_, delta) => {
const mob = useCombatStore.getState().mobs[instanceId];
if (!mob || mob.dead || mob.despawned) return;
const player = useGameStore.getState().playerPosition;
const targetPosition = mobTargetPosition(mob.targetActorId, player);
const position = positionRef.current;
const now = Date.now();
const movementLocked = mob.statuses.some((status) => status.endsAt > now
&& MOVEMENT_LOCKING_MOB_STATUSES.has(status.kind));
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) {
stepFearedMobAway(
position,
targetPosition,
mob.homePosition ?? initial,
maximumStep * 1.1,
mob.leashRange,
position,
);
} else if (!movementLocked && mob.combatPhase === "chasing") {
stepToward(position, targetPosition, maximumStep);
}
const facingSign = feared && !movementLocked ? -1 : 1;
const yaw = Math.atan2(
(targetPosition[0] - position[0]) * facingSign,
(targetPosition[2] - position[2]) * facingSign,
);
if (groupRef.current) {
groupRef.current.position.set(position[0], position[1], position[2]);
groupRef.current.rotation.y = yaw;
}
updateMobRuntimePosition(instanceId, position[0], position[1], position[2], yaw);
});
return (
<group ref={groupRef} position={initial} name={`summoned-mob-${instanceId}`}>
<ProxyMob
definition={entity}
instanceId={instanceId}
active={active}
moving
showLabel={false}
useOriginalModels={useOriginalModels}
/>
</group>
);
}
function SummonedEnemyPopulation({
entities,
active,
useOriginalModels,
}: {
readonly entities: PopulationDefinitionMap;
readonly active: boolean;
readonly useOriginalModels: boolean;
}) {
const mobs = useCombatStore((state) => state.mobs);
const summons = useMemo(() => Object.values(mobs).flatMap((mob) => {
if (!mob.summonedBy || mob.serverEntry === null || mob.despawned) return [];
const entity = entities[`entry-${mob.serverEntry}`]
?? Object.values(entities).find((candidate) => candidate.combat?.serverEntry === mob.serverEntry);
return entity ? [{ instanceId: mob.id, entity }] : [];
}), [entities, mobs]);
return (
<group name="summoned-enemy-population">
{summons.map(({ instanceId, entity }) => (
<SummonedPopulationMember
key={instanceId}
instanceId={instanceId}
entity={entity}
active={active}
useOriginalModels={useOriginalModels}
/>
))}
</group>
);
}
export interface MobPopulationProps {
readonly entities: PopulationDefinitionMap;
readonly roamingPacks: readonly MobPackDefinition[];
@@ -1512,6 +1607,11 @@ export function MobPopulation({
useOriginalModels={useOriginalModels}
/>
))}
<SummonedEnemyPopulation
entities={entities}
active={active}
useOriginalModels={useOriginalModels}
/>
</group>
);
}