import { useEffect, useRef } from "react"; import { useShellStore } from "../app/shellStore"; import { useCombatStore } from "./combatStore"; import { resetPartyRuntimeClock } from "./partyRuntime"; import { usePartyStore } from "./partyStore"; import { useGameStore } from "./store"; import { useManastormStore } from "./manastormStore"; import { onlineGroupAiPartySize, useOnlineGroupStore } from "../app/onlineGroupStore"; import { useDungeonSimulationStore } from "./dungeonSimulationStore"; import { useCompanionSpecReadinessStore } from "./companionSpecReadinessStore"; import { useCompanionPartyPreferences } from "./companionPartyPreferences"; import { RANDOMIZED_BITE_SIZED_STOCKADES_DUNGEON_ID } from "./dungeonContentSource"; /** Owns party lifecycle; the combat interval itself remains in CombatBridge. */ export function PartyBridge() { const character = useShellStore((state) => state.activeCharacter); const playerRole = useShellStore((state) => state.activeDungeonRole); const gameMode = useGameStore((state) => state.gameMode); const manastormPartySize = useManastormStore((state) => state.partySize); const onlineActivity = useOnlineGroupStore((state) => state.activeActivity); const onlineGroup = useOnlineGroupStore((state) => state.snapshot.group); const level = useCombatStore((state) => state.level); const simulationMode = useDungeonSimulationStore((state) => state.sessionMode); const simulationSeed = useDungeonSimulationStore((state) => state.seed); const resetRevision = useGameStore((state) => state.resetRevision); const readinessPolicy = useCompanionSpecReadinessStore((state) => state.config); const readinessPolicyRevision = useCompanionSpecReadinessStore((state) => state.revision); const deviceIncludeBetaSpecs = useCompanionPartyPreferences((state) => state.includeBetaSpecs); const mountedResetRevision = useRef(resetRevision); useEffect(() => { if (!character) return; const combat = useCombatStore.getState(); const party = usePartyStore.getState(); const requestedPartySize = gameMode === "manastorm" ? manastormPartySize : 5; const aiPartySize = onlineActivity && onlineActivity.type === (gameMode === "manastorm" ? "manastorm" : "dungeon") ? onlineGroupAiPartySize(onlineActivity.partySize) : requestedPartySize; const sharedOnlineParty = onlineActivity && onlineGroup?.activity?.id === onlineActivity.id ? onlineGroup.members : []; const randomizedReadyOnly = onlineActivity?.type === "dungeon" && onlineActivity.contentId === RANDOMIZED_BITE_SIZED_STOCKADES_DUNGEON_ID; const companionOptions = { readinessPolicy, readinessPolicyRevision: onlineActivity?.companionReadinessRevision ?? readinessPolicyRevision, includeBetaSpecs: randomizedReadyOnly ? false : onlineActivity?.includeBetaSpecs ?? deviceIncludeBetaSpecs, excludedPlayerSpecializationId: character.specializationId, excludedPlayerSpecializationIds: sharedOnlineParty.flatMap((member) => ( member.character?.specializationId ? [member.character.specializationId] : [] )), allowedCompanionSpecializationIds: onlineActivity?.companionSpecializationIds, occupiedHumanRoles: sharedOnlineParty.flatMap((member) => member.role ? [member.role] : []), }; if (simulationMode === "play") { party.initializeParty( onlineActivity?.id ?? character.id, combat.level, combat.health, playerRole, aiPartySize, randomizedReadyOnly ? "wow" : character.categoryId ?? "wow", companionOptions, ); } else { party.initializeSpectatorParty(simulationSeed, combat.level, character.categoryId ?? "wow", companionOptions); } usePartyStore.getState().syncEquipment(combat.equipment, combat.inventory); combat.synchronizeActors(); combat.applyDefaultPartyBuffs(); combat.applyPrecombatPreparation(); resetPartyRuntimeClock(); }, [character?.id, deviceIncludeBetaSpecs, gameMode, manastormPartySize, onlineActivity?.id, onlineActivity?.includeBetaSpecs, onlineGroup?.updatedAt, playerRole, readinessPolicy, readinessPolicyRevision, simulationMode, simulationSeed]); useEffect(() => { const party = usePartyStore.getState(); const combat = useCombatStore.getState(); party.syncLevel(level); usePartyStore.getState().syncEquipment(combat.equipment, combat.inventory); combat.synchronizeActors(); combat.applyDefaultPartyBuffs(); combat.applyPrecombatPreparation(); }, [level]); useEffect(() => { if (mountedResetRevision.current === resetRevision) return; mountedResetRevision.current = resetRevision; const combat = useCombatStore.getState(); usePartyStore.getState().resetCombat(combat.level, combat.health); usePartyStore.getState().syncEquipment(combat.equipment, combat.inventory); combat.synchronizeActors(); combat.applyDefaultPartyBuffs(); combat.applyPrecombatPreparation(); resetPartyRuntimeClock(); }, [resetRevision]); useEffect(() => useCombatStore.subscribe((state, previous) => { if (state.selectedTargetId && state.selectedTargetId !== previous.selectedTargetId) { usePartyStore.getState().clearSelection(); } }), []); return null; }