71 lines
2.8 KiB
TypeScript
71 lines
2.8 KiB
TypeScript
import { useEffect } from "react";
|
|
import { useShellStore } from "../app/shellStore";
|
|
import {
|
|
dispatchDungeonEncounterSignal,
|
|
registerDungeonEncounterEffectHandler,
|
|
} from "./dungeonEncounterRuntime";
|
|
import { LOOT_CATALOG, rollBossLoot } from "./lootCatalog";
|
|
import { updatePartyRuntimePosition } from "./partyRuntimeRegistry";
|
|
import { usePartyStore } from "./partyStore";
|
|
import { useCombatStore } from "./combatStore";
|
|
import { useGameStore } from "./store";
|
|
import type { DungeonEncounterEffectEnvelope } from "./dungeonEncounterTypes";
|
|
import { publishOnlinePartyTeleport } from "./onlineSessionRuntime";
|
|
import { dungeonDefinitionById } from "./dungeonRegistry";
|
|
import { removeMobPosition } from "./mobRuntimeRegistry";
|
|
import { dungeonSimulationNow } from "./dungeonSimulationStore";
|
|
|
|
function grantEncounterLoot(lootTableId: string, now: number): boolean {
|
|
const table = LOOT_CATALOG.bosses.find((candidate) => candidate.lootTableId === lootTableId);
|
|
if (!table) return false;
|
|
const combat = useCombatStore.getState();
|
|
const drops = rollBossLoot(table.bossKey, combat.level, { now });
|
|
if (!drops.length) return true;
|
|
const inventory = [...combat.inventory, ...drops];
|
|
useCombatStore.setState({ inventory });
|
|
useShellStore.getState().saveActiveInventory(inventory);
|
|
return true;
|
|
}
|
|
|
|
/** Applies platform effects emitted by the renderer-neutral encounter reducer. */
|
|
export function DungeonEncounterEffectBridge() {
|
|
useEffect(() => registerDungeonEncounterEffectHandler((envelope) => {
|
|
applyDungeonEncounterEffect(envelope, dungeonSimulationNow());
|
|
}), []);
|
|
return null;
|
|
}
|
|
|
|
export function applyDungeonEncounterEffect(
|
|
{ dungeonId, effect }: DungeonEncounterEffectEnvelope,
|
|
now = dungeonSimulationNow(),
|
|
): void {
|
|
if (effect.kind === "signal") {
|
|
dispatchDungeonEncounterSignal(effect.signalId, now);
|
|
return;
|
|
}
|
|
if (effect.kind === "party-teleport") {
|
|
const game = useGameStore.getState();
|
|
game.relocatePlayer({ footPosition: effect.destination, yaw: game.cameraYaw }, true);
|
|
for (const member of usePartyStore.getState().members) {
|
|
updatePartyRuntimePosition(member.id, ...effect.destination);
|
|
}
|
|
publishOnlinePartyTeleport(effect.destination, effect.actionId, now);
|
|
return;
|
|
}
|
|
if (effect.kind === "despawn-group") {
|
|
const group = dungeonDefinitionById(dungeonId)?.encounterSpawnGroups?.find(
|
|
(candidate) => candidate.id === effect.spawnGroupId,
|
|
);
|
|
if (!group) return;
|
|
const combat = useCombatStore.getState();
|
|
for (const spawn of group.spawns) {
|
|
combat.removeMob(spawn.id);
|
|
removeMobPosition(spawn.id);
|
|
}
|
|
return;
|
|
}
|
|
if (effect.kind === "loot-grant" && !grantEncounterLoot(effect.lootTableId, now)) {
|
|
useShellStore.getState().setNotice(`Loot table ${effect.lootTableId} is not packaged for this encounter.`);
|
|
}
|
|
}
|