forked from phenom/healer-man
Release Healer Man 0.1.54
This commit is contained in:
1 parent
d6976e6ef6
commit
2dbb0acfc1
26 files changed
+1893
-912
No files matched your search
@@ -8,6 +8,7 @@ import {
|
||||
import { aiHintsForAbility, runeCostForAbility } from "./abilityRules";
|
||||
import {
|
||||
evaluateCombatRotation,
|
||||
rotationProfileFor,
|
||||
type RotationDecision,
|
||||
type RotationFriendlyTarget,
|
||||
} from "./combatRotation";
|
||||
@@ -66,6 +67,42 @@ function learnedAbilities(member: PartyMember): readonly AbilityDefinition[] {
|
||||
.filter((ability): ability is AbilityDefinition => ability !== null);
|
||||
}
|
||||
|
||||
function isComboFinisher(ability: AbilityDefinition): boolean {
|
||||
return ability.effects.some((effect) => effect.kind === "finisher-damage"
|
||||
|| (effect.kind === "combo" && effect.amount < 0));
|
||||
}
|
||||
|
||||
const RESERVED_SIGNATURE_ACTIONS = new Set(["Mortal Strike"]);
|
||||
|
||||
function shouldReserveForHigherPriority(
|
||||
member: PartyMember,
|
||||
learned: readonly AbilityDefinition[],
|
||||
decision: RotationDecision,
|
||||
now: number,
|
||||
): boolean {
|
||||
const rotation = rotationProfileFor(member.classId, member.specializationId, member.specialization);
|
||||
const decisionIndex = rotation?.abilityPriority.indexOf(decision.ability.name) ?? -1;
|
||||
if (!rotation || decisionIndex <= 0) return false;
|
||||
return rotation.abilityPriority.slice(0, decisionIndex).some((name) => {
|
||||
const candidate = learned.find((ability) => ability.name === name);
|
||||
if (!candidate || candidate.passive || isComboFinisher(candidate)) return false;
|
||||
if (!RESERVED_SIGNATURE_ACTIONS.has(candidate.name)) return false;
|
||||
if ((member.cooldowns[candidate.id] ?? 0) > now) return false;
|
||||
if ((member.schoolLockedUntil[candidate.castRules?.school ?? "physical"] ?? 0) > now) return false;
|
||||
const categories = aiHintsForAbility(candidate).categories;
|
||||
if (!categories.includes("damage") || categories.includes("execute")) return false;
|
||||
const blockedCosts = abilityCosts(member, candidate).filter((cost) => (
|
||||
cost.amount > currentResource(member, cost.resource)
|
||||
));
|
||||
if (!blockedCosts.length) return false;
|
||||
return blockedCosts.some((cost) => !decision.ability.effects.some((effect) => (
|
||||
effect.kind === "resource"
|
||||
&& (effect.resource ?? member.resourceType) === cost.resource
|
||||
&& effect.amount > 0
|
||||
)));
|
||||
});
|
||||
}
|
||||
|
||||
export function choosePartyRotationDecision(
|
||||
member: PartyMember,
|
||||
context: PartyAbilityContext,
|
||||
@@ -85,7 +122,7 @@ export function choosePartyRotationDecision(
|
||||
.map((resource) => [resource, companionResourceMaximum(member, resource)]),
|
||||
);
|
||||
const learned = learnedAbilities(member);
|
||||
return evaluateCombatRotation({
|
||||
const decision = evaluateCombatRotation({
|
||||
now: context.now,
|
||||
classId: member.classId,
|
||||
secondaryClassId: member.secondaryClassId,
|
||||
@@ -119,6 +156,9 @@ export function choosePartyRotationDecision(
|
||||
hasActivePet: context.hasActivePet,
|
||||
canUseAbility: context.canUseAbility,
|
||||
});
|
||||
return decision && shouldReserveForHigherPriority(member, learned, decision, context.now)
|
||||
? null
|
||||
: decision;
|
||||
}
|
||||
|
||||
export function choosePartyAbility(
|
||||
|
||||
Reference in new issue
Block a user