Release Healer Man 0.1.46

This commit is contained in:
phenom committed 2026-09-16 14:30:58 -04:00
1 parent f82daf1c13
commit 9ff574ac0b
73 files changed
+33486 -174

No files matched your search

+45 -6
View File
@@ -64,6 +64,7 @@ import {
isPositionInFacingArc,
isSourceBehindTarget,
} from "./combatSpatial";
import { useDungeonSimulationStore } from "./dungeonSimulationStore";
const OUT_OF_COMBAT_REGEN_FRACTION = 0.06;
const ATTACK_ACQUISITION_RANGE = 30;
@@ -128,6 +129,16 @@ export function activePartyBossObjectives(): readonly PartyBossObjective[] {
}] : [];
}
const game = useGameStore.getState();
const simulation = useDungeonSimulationStore.getState();
if (
simulation.sessionMode === "spectate-live"
&& simulation.mode === "navigation-only"
&& simulation.objectiveId
) {
const objective = requireDungeonDefinition(game.activeDungeonId).objectiveOrder
.find((candidate) => candidate.id === simulation.objectiveId);
return objective ? [objective] : [];
}
const completed = new Set(activeDungeonCompletedObjectiveIds(game.activeDungeonId));
const mobs = useCombatStore.getState().mobs;
const objectives = requireDungeonDefinition(game.activeDungeonId).objectiveOrder
@@ -143,6 +154,10 @@ export function activePartyBossObjectives(): readonly PartyBossObjective[] {
));
}
function simulatedPartyExcludesPlayer(): boolean {
return useDungeonSimulationStore.getState().sessionMode !== "play";
}
export function resetPartyRuntimeClock(): void {
lastAdvanceAt = 0;
nextRegenAt = 0;
@@ -799,7 +814,10 @@ function partyHealingTargets(
): readonly string[] {
const combat = useCombatStore.getState();
const targeting = targetingRulesForAbility(ability);
const living = [PLAYER_AGGRO_ID, ...members.map((member) => member.id)].filter((id) => combat.actors[id]?.alive);
const living = [
...(simulatedPartyExcludesPlayer() ? [] : [PLAYER_AGGRO_ID]),
...members.map((member) => member.id),
].filter((id) => combat.actors[id]?.alive);
const healthFraction = (id: string) => {
const actor = combat.actors[id];
return actor ? actor.health / Math.max(1, actor.maxHealth) : 1;
@@ -850,7 +868,7 @@ function choosePartySupportIntent(
): PartySupportIntent | null {
const combat = useCombatStore.getState();
const fallenTargetId = [
...(combat.health <= 0 ? [PLAYER_AGGRO_ID] : []),
...(!simulatedPartyExcludesPlayer() && combat.health <= 0 ? [PLAYER_AGGRO_ID] : []),
...members
.filter((candidate) => candidate.health <= 0)
.sort((left, right) => (
@@ -866,7 +884,7 @@ function choosePartySupportIntent(
}
if (member.role !== "healer") return null;
const dispelTargetId = [
...(combat.health > 0 ? [PLAYER_AGGRO_ID] : []),
...(!simulatedPartyExcludesPlayer() && combat.health > 0 ? [PLAYER_AGGRO_ID] : []),
...members.filter((candidate) => candidate.health > 0).map((candidate) => candidate.id),
].find((id) => combat.auras.some((aura) => (
aura.targetId === id
@@ -880,7 +898,7 @@ function choosePartySupportIntent(
}
}
const injured = [
...(combat.health > 0 && combat.health < combat.maxHealth
...(!simulatedPartyExcludesPlayer() && combat.health > 0 && combat.health < combat.maxHealth
? [{ id: PLAYER_AGGRO_ID, health: combat.health, maxHealth: combat.maxHealth, role: "healer" as const }]
: []),
...members.filter((candidate) => candidate.health > 0 && candidate.health < candidate.maxHealth),
@@ -1024,7 +1042,10 @@ function executePartyAbility(
}
} else if (effect.kind === "apply-aura") {
const recipients = effect.scope === "party"
? [PLAYER_AGGRO_ID, ...members.filter((candidate) => candidate.health > 0).map((candidate) => candidate.id)]
? [
...(simulatedPartyExcludesPlayer() ? [] : [PLAYER_AGGRO_ID]),
...members.filter((candidate) => candidate.health > 0).map((candidate) => candidate.id),
]
: effect.recipient === "caster"
? [member.id]
: combat.mobs[targetId] ? hostileTargets : friendlyTargets;
@@ -1036,7 +1057,7 @@ function executePartyAbility(
}
} else if (effect.kind === "dispel") {
const recipients = effect.scope === "party"
? [PLAYER_AGGRO_ID, ...members.map((candidate) => candidate.id)]
? [...(simulatedPartyExcludesPlayer() ? [] : [PLAYER_AGGRO_ID]), ...members.map((candidate) => candidate.id)]
: [targetId];
for (const recipientId of recipients) {
effectiveAmount += useCombatStore.getState().dispelActor(recipientId, effect.categories, effect.maxCount);
@@ -1134,6 +1155,24 @@ export function advanceDungeonPartyCombat(now = Date.now()): void {
const authoritativeMembers: readonly PartyMember[] = party.members.map((member) => (
memberFromCompanionActor(member, combat.actors[member.id])
));
const simulation = useDungeonSimulationStore.getState();
if (simulation.sessionMode === "spectate-live" && simulation.mode === "navigation-only") {
const members = authoritativeMembers.map((member) => ({
...member,
status: member.health > 0 ? "advancing" as const : "down" as const,
activeCast: null,
activeCastTargetId: null,
nextActionAt: 0,
}));
party.commitRuntime({
members,
activeMobId: null,
objectiveBossId: simulation.objectiveId,
memberTargetIds: {},
});
useCombatStore.getState().commitCompanionActors(members);
return;
}
let members: readonly PartyMember[] = authoritativeMembers;
const partyOrigin = partyRuntimeCenter(
members.filter((member) => !partyRuntimeIsRejoining(member.id)).map((member) => member.id),