Release Healer Man 0.1.37
This commit is contained in:
1 parent
03a4e3f51c
commit
361734bc41
41 files changed
+1714
-110
No files matched your search
+62
-57
@@ -2175,10 +2175,7 @@ function executeTriggeredAuraProcsInWork(
|
||||
const maximum = work.maxResourcePools[resource] ?? defaultResourceMaximum(resource);
|
||||
work.resourcePools[resource] = clamp((work.resourcePools[resource] ?? 0) + action.amount, 0, maximum);
|
||||
} else if (action.kind === "heal" && actionTarget === PLAYER_AURA_ENTITY_ID) {
|
||||
const healed = Math.min(Math.max(0, action.amount), work.maxHealth - work.health);
|
||||
work.health += healed;
|
||||
work.progression.health = work.health;
|
||||
work.totalHealing += healed;
|
||||
work.totalHealing += healPlayerInWork(work, action.amount);
|
||||
} else if (action.kind === "heal" && actionTarget && !work.mobs[actionTarget]) {
|
||||
work.totalHealing += healCompanionActorInWork(work, actionTarget, Math.max(0, action.amount));
|
||||
} else if (action.kind === "damage" && actionTarget && work.mobs[actionTarget]) {
|
||||
@@ -2379,7 +2376,8 @@ function grantMobExperience(
|
||||
work.secondaryClassId,
|
||||
work.auras,
|
||||
);
|
||||
progression.health = progression.maxHealth;
|
||||
// A lingering DoT may grant experience after death, but leveling is not resurrection.
|
||||
progression.health = work.health > 0 ? progression.maxHealth : 0;
|
||||
work.health = progression.health;
|
||||
work.maxHealth = progression.maxHealth;
|
||||
work.maxResource = talentAdjustedMaxResource(
|
||||
@@ -2877,10 +2875,7 @@ function executeTriggeredSpellInWork(
|
||||
"taken",
|
||||
));
|
||||
if (recipient === PLAYER_AURA_ENTITY_ID) {
|
||||
const healed = Math.min(amount, work.maxHealth - work.health);
|
||||
work.health += healed;
|
||||
work.progression.health = work.health;
|
||||
work.totalHealing += healed;
|
||||
work.totalHealing += healPlayerInWork(work, amount);
|
||||
} else {
|
||||
const healed = healCompanionActorInWork(work, recipient, amount);
|
||||
work.totalHealing += healed;
|
||||
@@ -3188,6 +3183,18 @@ function applyAuraHealthTransitionInWork(
|
||||
mirrorCompanionActor(nextActor);
|
||||
}
|
||||
|
||||
/** Ordinary healing never transitions a corpse back to a living actor. */
|
||||
function healPlayerInWork(work: MutableCastWork, amount: number): number {
|
||||
const requested = Math.max(0, Math.round(Number.isFinite(amount) ? amount : 0));
|
||||
if (work.health <= 0 || requested <= 0) return 0;
|
||||
const healed = Math.min(requested, Math.max(0, work.maxHealth - work.health));
|
||||
work.health += healed;
|
||||
work.progression.health = work.health;
|
||||
const actor = work.actors[PLAYER_AURA_ENTITY_ID];
|
||||
if (actor) work.actors[PLAYER_AURA_ENTITY_ID] = { ...actor, health: work.health, alive: true };
|
||||
return healed;
|
||||
}
|
||||
|
||||
function healCompanionActorInWork(work: MutableCastWork, id: string, amount: number): number {
|
||||
const actor = ensureCompanionActorInWork(work, id);
|
||||
const requested = Math.max(0, Math.round(Number.isFinite(amount) ? amount : 0));
|
||||
@@ -4700,7 +4707,7 @@ export const useCombatStore = create<CombatState>((set, get) => ({
|
||||
? Math.min(resolution.rawAmount, friendlySummon.maxHealth - friendlySummon.health)
|
||||
: friendlyOnline
|
||||
? Math.min(resolution.rawAmount, friendlyOnline.maxHealth - friendlyOnline.health)
|
||||
: resolution.effectiveAmount;
|
||||
: healPlayerInWork(work, resolution.rawAmount);
|
||||
if (friendlySummon && healed > 0) {
|
||||
work.summons = work.summons.map((summon) => summon.id === healingTargetId
|
||||
? { ...summon, health: summon.health + healed }
|
||||
@@ -4710,9 +4717,6 @@ export const useCombatStore = create<CombatState>((set, get) => ({
|
||||
} else if (friendlyOnline && healed > 0) {
|
||||
const actor = work.actors[healingTargetId] ?? friendlyOnline;
|
||||
work.actors[healingTargetId] = { ...actor, health: actor.health + healed, alive: true };
|
||||
} else if (!friendlyTarget) {
|
||||
work.health += healed;
|
||||
work.progression.health = work.health;
|
||||
}
|
||||
recordCombatEventInWork(work, {
|
||||
occurredAt: now,
|
||||
@@ -4744,10 +4748,7 @@ export const useCombatStore = create<CombatState>((set, get) => ({
|
||||
&& aura.definition.utilityTags?.includes("beacon-of-light")
|
||||
));
|
||||
if (beacon?.targetId === PLAYER_AURA_ENTITY_ID) {
|
||||
const beaconHealing = Math.min(directHealing, work.maxHealth - work.health);
|
||||
work.health += beaconHealing;
|
||||
work.progression.health = work.health;
|
||||
work.totalHealing += beaconHealing;
|
||||
work.totalHealing += healPlayerInWork(work, directHealing);
|
||||
} else if (beacon) {
|
||||
work.totalHealing += healCompanionActorInWork(work, beacon.targetId, directHealing);
|
||||
}
|
||||
@@ -5331,10 +5332,7 @@ export const useCombatStore = create<CombatState>((set, get) => ({
|
||||
work.resource = clamp(work.resource + restored, 0, work.maxResource);
|
||||
work.resourcePools[activeResourceType] = work.resource;
|
||||
} else if (/food|refreshment|health|firestone/i.test(ability.name)) {
|
||||
const healed = Math.min(Math.max(1, Math.round(work.maxHealth * 0.25)), work.maxHealth - work.health);
|
||||
work.health += healed;
|
||||
work.progression.health = work.health;
|
||||
work.totalHealing += healed;
|
||||
work.totalHealing += healPlayerInWork(work, Math.max(1, Math.round(work.maxHealth * 0.25)));
|
||||
}
|
||||
} else if (effect.action === "portal") {
|
||||
summonSequence += 1;
|
||||
@@ -5363,13 +5361,7 @@ export const useCombatStore = create<CombatState>((set, get) => ({
|
||||
aura.targetId === PLAYER_AURA_ENTITY_ID
|
||||
&& aura.definition.utilityTags?.includes("rom-revival-charge")
|
||||
));
|
||||
const restored = Math.min(
|
||||
work.maxHealth - work.health,
|
||||
Math.round(work.maxHealth * 0.05 * (revival?.stacks ?? 0)),
|
||||
);
|
||||
work.health += restored;
|
||||
work.progression.health = work.health;
|
||||
work.totalHealing += restored;
|
||||
work.totalHealing += healPlayerInWork(work, Math.round(work.maxHealth * 0.05 * (revival?.stacks ?? 0)));
|
||||
if (revival) work.auras = work.auras.filter((aura) => aura.instanceId !== revival.instanceId);
|
||||
}
|
||||
}
|
||||
@@ -5412,9 +5404,8 @@ export const useCombatStore = create<CombatState>((set, get) => ({
|
||||
|
||||
// Judgement of Light restores 2% health when the marked target is struck.
|
||||
if (work.totalDamage > 0 && targetId && work.mobs[targetId]?.statuses.some((entry) => entry.kind === "marked")) {
|
||||
const healed = Math.min(talentAdjustedAmount(Math.round(work.maxHealth * 0.02), modifiers, "healing-percent", ability), work.maxHealth - work.health);
|
||||
work.health += healed;
|
||||
work.progression.health = work.health;
|
||||
const healed = healPlayerInWork(work,
|
||||
talentAdjustedAmount(Math.round(work.maxHealth * 0.02), modifiers, "healing-percent", ability));
|
||||
work.totalHealing += healed;
|
||||
addHealingThreatInWork(work, castThreatSource, healed);
|
||||
}
|
||||
@@ -5874,10 +5865,15 @@ export const useCombatStore = create<CombatState>((set, get) => ({
|
||||
if (id === PLAYER_AURA_ENTITY_ID) {
|
||||
const restored = state.health > 0 ? 0 : Math.max(1, Math.round(state.maxHealth * fraction));
|
||||
if (restored <= 0) return 0;
|
||||
const playerActor = state.actors[id];
|
||||
set({
|
||||
health: restored,
|
||||
controlledUntil: 0,
|
||||
controlMechanic: null,
|
||||
playerAnimationEvent: nextAnimationEvent("ability-cancel"),
|
||||
actors: playerActor ? { ...state.actors, [id]: {
|
||||
...playerActor, health: restored, alive: true, controlledUntil: 0, cast: null,
|
||||
} } : state.actors,
|
||||
feedback: nextFeedback("heal", `Resurrected with ${restored} health.`, { amount: restored, abilityId }),
|
||||
combatEvents: recordCombatEvent(state.combatEvents, {
|
||||
occurredAt: now,
|
||||
@@ -6101,7 +6097,12 @@ export const useCombatStore = create<CombatState>((set, get) => ({
|
||||
const state = get();
|
||||
if (!state.actors[sourceActorId]) return false;
|
||||
if (effect.kind === "dot" && (!state.mobs[targetActorId] || state.mobs[targetActorId].dead)) return false;
|
||||
if (effect.kind === "hot" && !state.actors[targetActorId]) return false;
|
||||
if (effect.kind === "hot") {
|
||||
const targetHealth = targetActorId === PLAYER_AURA_ENTITY_ID ? state.health
|
||||
: state.summons.find((summon) => summon.id === targetActorId)?.health
|
||||
?? state.actors[targetActorId]?.health ?? 0;
|
||||
if (!state.actors[targetActorId] || targetHealth <= 0) return false;
|
||||
}
|
||||
const sourceActor = state.actors[sourceActorId];
|
||||
const sourceMember = usePartyStore.getState().members.find((member) => member.id === sourceActorId);
|
||||
const amount = sourceMember
|
||||
@@ -6402,11 +6403,15 @@ export const useCombatStore = create<CombatState>((set, get) => ({
|
||||
const restored = Math.max(1, Math.round(
|
||||
state.maxHealth * Math.max(0.01, Math.min(1, percentMaxHealth)),
|
||||
));
|
||||
const playerActor = state.actors[PLAYER_AURA_ENTITY_ID];
|
||||
set({
|
||||
health: restored,
|
||||
controlledUntil: 0,
|
||||
controlMechanic: null,
|
||||
playerAnimationEvent: nextAnimationEvent("ability-cancel"),
|
||||
actors: playerActor ? { ...state.actors, [PLAYER_AURA_ENTITY_ID]: {
|
||||
...playerActor, health: restored, alive: true, controlledUntil: 0, cast: null,
|
||||
} } : state.actors,
|
||||
feedback: nextFeedback("heal", `Resurrected with ${restored} health.`, { amount: restored }),
|
||||
});
|
||||
return restored;
|
||||
@@ -7578,7 +7583,7 @@ export const useCombatStore = create<CombatState>((set, get) => ({
|
||||
combatEvents: [...state.combatEvents],
|
||||
actors: { ...state.actors },
|
||||
cooldowns: { ...state.cooldowns },
|
||||
health: Math.min(
|
||||
health: state.health <= 0 ? 0 : Math.min(
|
||||
state.maxHealth,
|
||||
state.health + resolveAuraStatValue(
|
||||
state.gearStats.healthPer5 / 5 + state.gearStats.spirit * 0.01,
|
||||
@@ -7707,10 +7712,24 @@ export const useCombatStore = create<CombatState>((set, get) => ({
|
||||
}
|
||||
}
|
||||
|
||||
const timedEffectTargetAlive = (effect: TimedCombatEffect): boolean => {
|
||||
if (effect.kind === "dot") {
|
||||
const mob = effect.targetId ? work.mobs[effect.targetId] : null;
|
||||
return Boolean(mob && !mob.dead && mob.health > 0);
|
||||
}
|
||||
const targetId = effect.targetId ?? PLAYER_AURA_ENTITY_ID;
|
||||
if (targetId === PLAYER_AURA_ENTITY_ID) return work.health > 0;
|
||||
const summon = work.summons.find((candidate) => candidate.id === targetId);
|
||||
if (summon) return summon.health > 0;
|
||||
const actor = ensureCompanionActorInWork(work, targetId) ?? work.actors[targetId];
|
||||
return Boolean(actor?.alive && actor.health > 0);
|
||||
};
|
||||
for (const effect of state.effects) {
|
||||
// Check before due ticks/procs, not only after applying their health changes.
|
||||
if (!timedEffectTargetAlive(effect)) continue;
|
||||
let remainingTicks = effect.remainingTicks;
|
||||
let nextTickAt = effect.nextTickAt;
|
||||
while (remainingTicks > 0 && nextTickAt <= now) {
|
||||
while (remainingTicks > 0 && nextTickAt <= now && timedEffectTargetAlive(effect)) {
|
||||
const ability = abilityById(effect.sourceAbilityId);
|
||||
const baseAmount = effect.fixedAmount ?? (ability
|
||||
? equipmentAdjustedCombatAmount(
|
||||
@@ -7764,10 +7783,10 @@ export const useCombatStore = create<CombatState>((set, get) => ({
|
||||
"taken",
|
||||
));
|
||||
if (effect.targetId) {
|
||||
const target = work.actors[effect.targetId]
|
||||
?? ensureCompanionActorInWork(work, effect.targetId)
|
||||
?? (work.summons.some((summon) => summon.id === effect.targetId)
|
||||
? combatActorFromSummon(work.summons.find((summon) => summon.id === effect.targetId)!, progression.level, work.auras)
|
||||
const target = ensureCompanionActorInWork(work, hotTargetId)
|
||||
?? work.actors[hotTargetId]
|
||||
?? (work.summons.some((summon) => summon.id === hotTargetId)
|
||||
? combatActorFromSummon(work.summons.find((summon) => summon.id === hotTargetId)!, progression.level, work.auras)
|
||||
: null);
|
||||
if (target?.alive) partyHotTicks.push({
|
||||
memberId: target.id,
|
||||
@@ -7776,9 +7795,7 @@ export const useCombatStore = create<CombatState>((set, get) => ({
|
||||
sourceAbilityId: effect.sourceAbilityId,
|
||||
});
|
||||
} else {
|
||||
const healed = Math.min(modifiedHealing, work.maxHealth - work.health);
|
||||
work.health += healed;
|
||||
work.progression.health = work.health;
|
||||
const healed = healPlayerInWork(work, modifiedHealing);
|
||||
work.totalHealing += healed;
|
||||
addHealingThreatInWork(
|
||||
work,
|
||||
@@ -7823,12 +7840,7 @@ export const useCombatStore = create<CombatState>((set, get) => ({
|
||||
remainingTicks -= 1;
|
||||
nextTickAt += effect.intervalMs;
|
||||
}
|
||||
const targetAlive = effect.kind === "dot"
|
||||
? Boolean(effect.targetId && !work.mobs[effect.targetId]?.dead)
|
||||
: !effect.targetId || usePartyStore.getState().members.some(
|
||||
(member) => member.id === effect.targetId && member.health > 0,
|
||||
);
|
||||
if (remainingTicks > 0 && targetAlive) {
|
||||
if (remainingTicks > 0 && timedEffectTargetAlive(effect)) {
|
||||
work.effects.push(
|
||||
remainingTicks === effect.remainingTicks && nextTickAt === effect.nextTickAt
|
||||
? effect
|
||||
@@ -7884,13 +7896,10 @@ export const useCombatStore = create<CombatState>((set, get) => ({
|
||||
|| Math.sqrt(distanceSquared(area.position, position)) > area.radius
|
||||
|| !combatHasLineOfSight(area.position, position)) continue;
|
||||
const member = partyMembers.find((candidate) => candidate.id === friendlyId);
|
||||
if (member ? member.health <= 0 : work.health <= 0) continue;
|
||||
const healed = member
|
||||
? healCompanionActorInWork(work, friendlyId, amount)
|
||||
: Math.min(amount, work.maxHealth - work.health);
|
||||
if (!member) {
|
||||
work.health += healed;
|
||||
work.progression.health = work.health;
|
||||
}
|
||||
: healPlayerInWork(work, amount);
|
||||
work.totalHealing += healed;
|
||||
recordCombatEventInWork(work, {
|
||||
occurredAt: nextTickAt,
|
||||
@@ -7941,12 +7950,8 @@ export const useCombatStore = create<CombatState>((set, get) => ({
|
||||
const target = injured[0];
|
||||
if (target) {
|
||||
const healed = target.id === PLAYER_AURA_ENTITY_ID
|
||||
? Math.min(summon.attackDamage, work.maxHealth - work.health)
|
||||
? healPlayerInWork(work, summon.attackDamage)
|
||||
: healCompanionActorInWork(work, target.id, summon.attackDamage);
|
||||
if (target.id === PLAYER_AURA_ENTITY_ID) {
|
||||
work.health += healed;
|
||||
work.progression.health = work.health;
|
||||
}
|
||||
work.totalHealing += healed;
|
||||
addHealingThreatInWork(work, { actorId: summon.ownerActorId, role: "healer", abilityId: summon.sourceAbilityId }, healed);
|
||||
recordCombatEventInWork(work, {
|
||||
|
||||
Reference in new issue
Block a user