Release Healer Man 0.1.46
This commit is contained in:
1 parent
f82daf1c13
commit
9ff574ac0b
73 files changed
+33486
-174
No files matched your search
@@ -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),
|
||||
|
||||
Reference in new issue
Block a user