import { useEffect, useRef, useState, useSyncExternalStore } from "react";
import { useFrame } from "@react-three/fiber";
import { DoubleSide, Group } from "three";
import { subscribeContentManager } from "../content/contentManager";
import { combatMediaPackReady } from "../game/combatMediaPack";
import { type CombatSummon, useCombatStore } from "../game/combatStore";
import { getMobPosition } from "../game/mobRuntimeRegistry";
import { OriginalWowModel } from "./OriginalWowModel";
import { getPreparedShadowfiendMedia, preloadShadowfiendMedia } from "./shadowfiendMedia";
const SHADOWFIEND_CREATURE_ID = 19668;
function ShadowfiendFallback() {
return
{[-1, 1].map((side) =>
)}
{[-1, 1].map((side) =>
)}
;
}
function ShadowfiendMember({ summon, active }: { readonly summon: CombatSummon; readonly active: boolean }) {
const [media, setMedia] = useState(getPreparedShadowfiendMedia);
const mediaReady = useSyncExternalStore(subscribeContentManager, combatMediaPackReady, () => false);
const group = useRef(null);
const playback = useRef({ elapsedMs: 0, decaying: false });
useEffect(() => {
if (!mediaReady) {
setMedia(null);
return;
}
let cancelled = false;
void preloadShadowfiendMedia().then((ready) => {
if (!cancelled) setMedia(ready);
}).catch(() => { if (!cancelled) setMedia(null); });
return () => {
cancelled = true;
};
}, [mediaReady]);
useFrame((_, delta) => {
if (!group.current) return;
const live = useCombatStore.getState().summons.find((candidate) => candidate.id === summon.id);
if (!live) return;
const goal = live.position;
const blend = Math.min(1, delta * 9);
group.current.position.x += (goal[0] - group.current.position.x) * blend;
group.current.position.y += (goal[1] + 0.13 - group.current.position.y) * blend;
group.current.position.z += (goal[2] - group.current.position.z) * blend;
const target = live.targetId ? getMobPosition(live.targetId) : null;
if (target) group.current.rotation.y = Math.atan2(goal[2] - target[2], target[0] - goal[0]);
if (active) playback.current.elapsedMs += Math.min(delta, 0.1) * 1_000;
});
return
{media
?
: }
;
}
/** Player and companion combat summons live outside the static dungeon population. */
export function CombatSummonPopulation({ active }: { readonly active: boolean }) {
const summons = useCombatStore((state) => state.summons);
return
{summons.filter((summon) => summon.creatureId === SHADOWFIEND_CREATURE_ID && summon.health > 0)
.map((summon) => )}
;
}