Update Healer Man server
This commit is contained in:
1 parent
6114a2295f
commit
69809f7228
593 files changed
+903643
-40774
No files matched your search
@@ -5,9 +5,16 @@ import {
|
||||
} from "../abilityCatalog";
|
||||
import { aiHintsForAbility, canAbilityCastWhileMoving, targetingRulesForAbility } from "../abilityRules";
|
||||
import { PLAYER_AGGRO_ID } from "../aggro";
|
||||
import { combatNow } from "../combatClock";
|
||||
import { enemyControlPreventsActions } from "../enemyAttacks";
|
||||
import type { CastFailureReason, CombatState } from "../combatStore";
|
||||
import { equipmentAdjustedCombatAmount, resolvedAbilityCosts, useCombatStore } from "../combatStore";
|
||||
import { isCoaBarbarianDecapitate } from "../coaBarbarianExecuteRuntime";
|
||||
import { coaBarbarianDecapitateWeaponStrike, coaNormalizedMeleeWeaponSpeed } from "../coaBarbarianWeaponDamageRuntime";
|
||||
import { coaGuardianFormationAbilityForCast } from "../coaGuardianFormationRuntime";
|
||||
import { projectCoaGuardianParagonAbility } from "../coaGuardianParagonRuntime";
|
||||
import { PLAYER_EQUIPMENT_OWNER_ID } from "../equipment";
|
||||
import { resolvedEquipmentLoadout } from "../heirloomEquipment";
|
||||
import { getMobPosition } from "../mobRuntimeRegistry";
|
||||
import { getPartyRuntimePosition } from "../partyRuntimeRegistry";
|
||||
import { healingAbilityCanAidOthers, healingAbilityHasPendingHot, healingAbilityIsArea } from "../healingAbilityRules";
|
||||
@@ -79,7 +86,21 @@ function learnedAbility(ability: AbilityDefinition, combat: CombatState): Abilit
|
||||
const ranked = learnedAbilityForBuild(ability, combat.level, combat.talentRanks);
|
||||
if (!ranked) return null;
|
||||
if (ranked.effects.every((effect) => effect.kind === "unsupported" || effect.kind === "scripted")) return null;
|
||||
return ranked;
|
||||
if (combat.classId === "guardian" && combat.gearRole === "tank" && ranked.dbcSpellId === 803130) return null;
|
||||
if (isCoaBarbarianDecapitate(ranked)) {
|
||||
const mainHand = resolvedEquipmentLoadout(combat.equipment, combat.inventory, {
|
||||
ownerId: PLAYER_EQUIPMENT_OWNER_ID, ownerKind: "player", classId: combat.classId,
|
||||
secondaryClassId: combat.secondaryClassId, specializationId: combat.specializationId,
|
||||
role: combat.gearRole, level: combat.level,
|
||||
})["main-hand"];
|
||||
return coaBarbarianDecapitateWeaponStrike(ranked, combat.gearStats.meleeWeapon,
|
||||
mainHand ? coaNormalizedMeleeWeaponSpeed(mainHand) : null);
|
||||
}
|
||||
return projectCoaGuardianParagonAbility(coaGuardianFormationAbilityForCast(ranked), {
|
||||
id: PLAYER_AGGRO_ID, classId: combat.classId, talentRanks: combat.talentRanks,
|
||||
role: combat.gearRole, specializationId: combat.specializationId,
|
||||
health: combat.health, maxHealth: combat.maxHealth,
|
||||
}, combat.auras, combatNow());
|
||||
}
|
||||
|
||||
function isAreaAbility(ability: AbilityDefinition): boolean {
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { PartyCastTelemetry } from "./partyCastTelemetry";
|
||||
|
||||
describe("Balance Lab companion cast telemetry", () => {
|
||||
it("counts completed damaging casts from party releases", () => {
|
||||
const telemetry = new PartyCastTelemetry();
|
||||
|
||||
expect(telemetry.record({
|
||||
actorGroup: "party",
|
||||
sourceActorId: "party-ranger",
|
||||
abilityId: "ranger-power-shot",
|
||||
phase: "cast-start",
|
||||
})).toBe(false);
|
||||
expect(telemetry.record({
|
||||
actorGroup: "party",
|
||||
sourceActorId: "party-ranger",
|
||||
abilityId: "ranger-power-shot",
|
||||
phase: "release",
|
||||
})).toBe(true);
|
||||
});
|
||||
|
||||
it("counts one cast for a channel with several release pulses", () => {
|
||||
const telemetry = new PartyCastTelemetry();
|
||||
const cast = {
|
||||
actorGroup: "party",
|
||||
sourceActorId: "party-healer",
|
||||
abilityId: "channel-heal",
|
||||
};
|
||||
|
||||
expect(telemetry.record({ ...cast, phase: "cast-start" })).toBe(false);
|
||||
expect(telemetry.record({ ...cast, phase: "release" })).toBe(true);
|
||||
expect(telemetry.record({ ...cast, phase: "release" })).toBe(false);
|
||||
});
|
||||
|
||||
it("counts instant releases and ignores canceled or non-party actions", () => {
|
||||
const telemetry = new PartyCastTelemetry();
|
||||
const cast = {
|
||||
actorGroup: "party",
|
||||
sourceActorId: "party-pyromancer",
|
||||
abilityId: "instant-blast",
|
||||
};
|
||||
|
||||
expect(telemetry.record({ ...cast, phase: "release", instantCast: true })).toBe(true);
|
||||
expect(telemetry.record({ ...cast, phase: "cast-start" })).toBe(false);
|
||||
expect(telemetry.record({ ...cast, phase: "cast-cancel" })).toBe(false);
|
||||
expect(telemetry.record({ ...cast, phase: "release" })).toBe(false);
|
||||
expect(telemetry.record({ ...cast, actorGroup: "player", phase: "release", instantCast: true })).toBe(false);
|
||||
expect(telemetry.record({
|
||||
...cast,
|
||||
abilityId: "party-auto-attack",
|
||||
phase: "release",
|
||||
instantCast: true,
|
||||
})).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
export interface PartyCastPresentationEvent {
|
||||
readonly actorGroup: string;
|
||||
readonly sourceActorId: string;
|
||||
readonly abilityId?: string;
|
||||
readonly phase: string;
|
||||
readonly instantCast?: boolean;
|
||||
}
|
||||
|
||||
/** Tracks one completed companion cast across multi-pulse channel releases. */
|
||||
export class PartyCastTelemetry {
|
||||
private readonly startedCasts = new Map<string, boolean>();
|
||||
|
||||
record(event: PartyCastPresentationEvent): boolean {
|
||||
if (event.actorGroup !== "party" || !event.abilityId || event.abilityId === "party-auto-attack") return false;
|
||||
const key = `${event.sourceActorId}\u0000${event.abilityId}`;
|
||||
if (event.phase === "cast-start") {
|
||||
this.startedCasts.set(key, false);
|
||||
return false;
|
||||
}
|
||||
if (event.phase === "cast-cancel") {
|
||||
this.startedCasts.delete(key);
|
||||
return false;
|
||||
}
|
||||
if (event.phase !== "release") return false;
|
||||
if (event.instantCast) return true;
|
||||
if (!this.startedCasts.has(key) || this.startedCasts.get(key)) return false;
|
||||
this.startedCasts.set(key, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user