Release Healer Man 0.1.59

This commit is contained in:
phenom committed 2026-09-27 10:16:42 -04:00
1 parent 11934a3979
commit ab1434a3b8
44 files changed
+1242 -103

No files matched your search

+19 -13
View File
@@ -141,7 +141,7 @@ import { rollBossLoot } from "./lootCatalog";
import { bossCardForSource } from "./bossCardCatalog";
import { collectBossCardDrop, emptyBossCardCollection, normalizeBossCardCollection, rollBossCards, type BossCardCollection, type BossCardDrop } from "./bossCards";
import { adaptRunewakerEnemyDamage } from "./runewakerCombatFormula";
import { channelAmountEffects, channelPulseCount } from "./combatChannelAmounts";
import { channelAmountEffects, channelPulseAt, channelPulseCount } from "./combatChannelAmounts";
import { normalizeInventory, type InventoryItem } from "./lootTypes";
import {
PLAYER_EQUIPMENT_OWNER_ID,
@@ -5251,7 +5251,7 @@ export const useCombatStore = create<CombatState>((set, get) => ({
groundPosition,
totalTicks: ability.castMode === "channel" ? channelPulseCount(ability) : 0,
ticksCompleted: 0,
nextTickAt: ability.castMode === "channel" ? now + castTimeMs / channelPulseCount(ability) : 0,
nextTickAt: ability.castMode === "channel" ? channelPulseAt(ability, now, castTimeMs, 0) : 0,
};
const channelCooldowns: Record<string, number> = { ...state.cooldowns };
if (activeCast.mode === "channel" && cooldownMs > 0) {
@@ -5346,7 +5346,7 @@ export const useCombatStore = create<CombatState>((set, get) => ({
}
const castThreatSource = playerThreatSource(state, ability.id);
if (targeting?.shape === "ground" && groundPosition) {
if (targeting?.shape === "ground" && groundPosition && completingCast?.mode !== "channel") {
const periodic = ability.effects.filter((effect) => effect.kind === "dot" || effect.kind === "hot");
const durationMs = periodic.reduce((maximum, effect) => (
Math.max(maximum, effect.ticks * effect.intervalMs)
@@ -9803,7 +9803,9 @@ export const useCombatStore = create<CombatState>((set, get) => ({
: null;
let ticksCompleted = cast.ticksCompleted;
let nextTickAt = cast.nextTickAt;
while (ability && ticksCompleted < cast.totalTicks && nextTickAt <= now) {
while (ability && ticksCompleted < cast.totalTicks && nextTickAt <= now && nextTickAt <= cast.completesAt) {
const channelTargeting = targetingRulesForAbility(ability);
const groundChannel = channelTargeting.shape === "ground";
const target = cast.targetId ? get().mobs[cast.targetId] : null;
const friendlyTarget = cast.targetId
? usePartyStore.getState().members.find((member) => member.id === cast.targetId)
@@ -9812,14 +9814,14 @@ export const useCombatStore = create<CombatState>((set, get) => ({
? getMobPosition(cast.targetId) ?? getPartyRuntimePosition(cast.targetId)
: get().playerPosition;
const channelHostile = ability.target === "hostile" || (ability.target === "any" && Boolean(target));
const distance = cast.targetId
const distance = groundChannel ? 0 : cast.targetId
? channelHostile
? Math.sqrt(mobRuntimeDistanceSquared(cast.targetId, cast.origin))
: Math.sqrt(partyRuntimeDistanceSquared(cast.targetId, cast.origin))
: 0;
const minimumRange = ability ? talentAdjustedRange(ability.range.min, ability, get().talentModifiers) : 0;
const maximumRange = ability ? talentAdjustedRange(ability.range.max, ability, get().talentModifiers) : 0;
const targetUnavailable = channelHostile
const targetUnavailable = groundChannel ? !cast.groundPosition : channelHostile
? !target || target.dead || !targetPosition
: (ability.target === "friendly" || ability.target === "any") && cast.targetId
? !friendlyTarget || friendlyTarget.health <= 0 || !targetPosition
@@ -9856,11 +9858,15 @@ export const useCombatStore = create<CombatState>((set, get) => ({
0,
);
if (damageAmount > 0) {
const channelCenter = channelHostile ? targetPosition ?? cast.origin : get().playerPosition;
const channelTargets = ability.radius === undefined
const channelCenter = groundChannel
? cast.groundPosition!
: channelHostile ? targetPosition ?? cast.origin : get().playerPosition;
const channelRadius = groundChannel ? channelTargeting.radius ?? ability.radius ?? 8 : ability.radius;
const channelTargets = channelRadius === undefined
? (cast.targetId && channelHostile ? [cast.targetId] : [])
: nearbyMobRuntimeIds(channelCenter, talentAdjustedRange(ability.radius, ability, get().talentModifiers))
.filter((id) => !get().mobs[id]?.dead);
: nearbyMobRuntimeIds(channelCenter, talentAdjustedRange(channelRadius, ability, get().talentModifiers))
.filter((id) => !get().mobs[id]?.dead && Boolean(getMobPosition(id)
&& combatHasLineOfSight(channelCenter, getMobPosition(id)!)));
for (const channelTargetId of channelTargets) get().damageMob(
channelTargetId,
talentAdjustedAmount(
@@ -9887,7 +9893,7 @@ export const useCombatStore = create<CombatState>((set, get) => ({
},
);
}
if (healingAmount > 0) {
if (healingAmount > 0 && !(ability.target === "any" && channelHostile)) {
const perTargetHealing = talentAdjustedAmount(
healingAmount,
get().talentModifiers,
@@ -10002,10 +10008,10 @@ export const useCombatStore = create<CombatState>((set, get) => ({
nextTickAt,
get().playerPosition,
cast.targetId,
cast.targetId ? getMobPosition(cast.targetId) : get().playerPosition,
cast.groundPosition ?? (cast.targetId ? getMobPosition(cast.targetId) : get().playerPosition),
);
ticksCompleted += 1;
nextTickAt += (cast.completesAt - cast.startedAt) / cast.totalTicks;
nextTickAt = channelPulseAt(ability, cast.startedAt, cast.originalDurationMs, ticksCompleted);
}
if (get().activeCast?.id !== cast.id) return;
if (ticksCompleted >= cast.totalTicks || now >= cast.completesAt) {