Update Healer Man server
This commit is contained in:
1 parent
69809f7228
commit
ff20ab8779
101 files changed
+16713
-945
No files matched your search
+62
-20
@@ -1,3 +1,4 @@
|
||||
import { healerManaRecoveryAmount } from "./healerManaRecovery";
|
||||
import { nativeDisarmedMainHandAutoAttackProfile, nativeTrainingAutoAttackProfile } from "./romNativeCombatAmount";
|
||||
import { romNativePlayerHitProfile } from "./romNativeCombatResolution";
|
||||
import { classifyEnemyNpcSpellBlockPhase } from "./enemyBlocking";
|
||||
@@ -8,7 +9,11 @@ import {
|
||||
enemyDisarmAbilityWeaponChannel,
|
||||
enemyDisarmBlocksAbilityCast,
|
||||
enemyDisarmRequirementCandidates,
|
||||
enemySpellHasMatchingEquippedItem,
|
||||
} from "./enemyDisarmRuntime";
|
||||
import { WOW335_HUNTER_DETERRENCE_ABILITY_ID, wow335DeterrenceAbilityContract,
|
||||
wow335DeterrenceActivePackage } from "./wow335DeterrenceRuntime";
|
||||
import { wow335HunterPreventionTypeForAbility } from "./wow335AbilityCatalog";
|
||||
import type { EnemyDisarmWeaponChannel } from "./enemyWeaponDebuffs";
|
||||
import {
|
||||
abilitiesForClass,
|
||||
@@ -36,6 +41,7 @@ import {
|
||||
auraAdjustedCharacterStats,
|
||||
effectiveActorAuras,
|
||||
equipmentAdjustedCombatAmount,
|
||||
refreshHealerManaRecovery,
|
||||
refreshPriestPainAndSufferingDot,
|
||||
stableCombatUnit,
|
||||
useCombatStore,
|
||||
@@ -189,7 +195,7 @@ import {
|
||||
} from "./partyAbilityAi";
|
||||
import { minimumHostileTargetsForAbility } from "./combatRotation";
|
||||
import { emitRotationDecision } from "./rotationTelemetry";
|
||||
import { imminentCooldownMechanic } from "./companionCooldownPolicy";
|
||||
import { imminentCooldownMechanic, currentCompanionCooldownPolicy } from "./companionCooldownPolicy";
|
||||
import { normalizeActorResourcePool, type DeathKnightRuneState } from "./combatActors";
|
||||
import { enemyControlPreventsActions } from "./enemyAttacks";
|
||||
import { enemyControlAbilityCanEscapeAuras } from "./enemyControlImmunityPurge";
|
||||
@@ -2290,6 +2296,9 @@ function maybeProcPartyWarriorUnbridledWrath(
|
||||
|
||||
/** Weapon swings continue during the GCD and supply Rage for paid attacks. */
|
||||
function advancePartyWeaponAttack(member: PartyMember, targetId: string, now: number): PartyMember {
|
||||
if (useCombatStore.getState().auras.some((aura) => aura.targetId === member.id
|
||||
&& (aura.expiresAt === null || aura.expiresAt > now)
|
||||
&& aura.definition.utilityTags?.includes("pacified"))) return member;
|
||||
const hasMaelstromWeaponTalent = member.classId === "shaman"
|
||||
&& (member.talentRanks["talent-2057"] ?? 0) > 0;
|
||||
if (partyUsesRangedAttacks(member) && baseClassFor(member.classId) !== "hunter"
|
||||
@@ -2605,6 +2614,10 @@ export function partyAbilityTargetIsValid(
|
||||
castCanMove = false,
|
||||
): boolean {
|
||||
const combat = useCombatStore.getState();
|
||||
if (wow335HunterPreventionTypeForAbility(ability) === 2 && combat.auras.some((aura) => (
|
||||
aura.targetId === member.id && (aura.expiresAt === null || aura.expiresAt > now)
|
||||
&& aura.definition.utilityTags?.includes("pacified")
|
||||
))) return false;
|
||||
if (ability.wowWeaponRequirements) {
|
||||
const loadout = resolvedEquipmentLoadout(combat.equipment, combat.inventory, {
|
||||
ownerId: member.id,
|
||||
@@ -2615,7 +2628,10 @@ export function partyAbilityTargetIsValid(
|
||||
role: member.role,
|
||||
level: member.level,
|
||||
});
|
||||
if (enemyDisarmBlocksAbilityCast(
|
||||
const equippedItems = enemyDisarmRequirementCandidates(loadout);
|
||||
if ((ability.id === WOW335_HUNTER_DETERRENCE_ABILITY_ID
|
||||
&& !enemySpellHasMatchingEquippedItem(ability.wowWeaponRequirements, equippedItems))
|
||||
|| enemyDisarmBlocksAbilityCast(
|
||||
combat.auras,
|
||||
member.id,
|
||||
now,
|
||||
@@ -2625,7 +2641,7 @@ export function partyAbilityTargetIsValid(
|
||||
passive: ability.passive,
|
||||
createsAura: ability.effects.some((effect) => effect.kind === "apply-aura"),
|
||||
},
|
||||
enemyDisarmRequirementCandidates(loadout),
|
||||
equippedItems,
|
||||
)) return false;
|
||||
}
|
||||
if (!coaWitchDoctorCanBrewIngredient(ability, combat.auras, member.id, member.talentRanks, now)) return false;
|
||||
@@ -5192,6 +5208,7 @@ function membersDiffer(left: readonly PartyMember[], right: readonly PartyMember
|
||||
* tank can redirect that tick's pressure before the death/respawn boundary.
|
||||
*/
|
||||
export function advanceDungeonPartyCombat(now = Date.now()): void {
|
||||
refreshHealerManaRecovery(now);
|
||||
const combat = useCombatStore.getState();
|
||||
const party = usePartyStore.getState();
|
||||
if (!party.members.length) return;
|
||||
@@ -5321,6 +5338,10 @@ export function advanceDungeonPartyCombat(now = Date.now()): void {
|
||||
manaSpiritRegenerationPerSecond: type === "mana" ? member.gearStats.spirit * 0.02 : 0,
|
||||
}).current;
|
||||
}
|
||||
if (living && member.resourcePools.mana !== undefined && nativeManaMaximum > 0) {
|
||||
resourcePools.mana = Math.min(nativeManaMaximum, (resourcePools.mana ?? 0)
|
||||
+ healerManaRecoveryAmount(combat.auras, member.id, nativeManaMaximum, deltaSeconds));
|
||||
}
|
||||
const angerManagement = member.classId === "warrior"
|
||||
? wow335WarriorAngerManagementProfile(
|
||||
member.talentRanks[WOW335_WARRIOR_ANGER_MANAGEMENT_TALENT_ID] ?? 0,
|
||||
@@ -5402,22 +5423,18 @@ export function advanceDungeonPartyCombat(now = Date.now()): void {
|
||||
&& enemyControlPreventsActions(member.controlMechanic)
|
||||
) {
|
||||
const controlCombat = useCombatStore.getState();
|
||||
const berserkerRageBase = member.classId === "warrior"
|
||||
? abilitiesForCharacter(member.classId, member.secondaryClassId)
|
||||
.find((ability) => ability.dbcSpellId === 18499)
|
||||
: undefined;
|
||||
const berserkerRage = berserkerRageBase
|
||||
? learnedAbilityForBuild(berserkerRageBase, member.level, member.talentRanks)
|
||||
: null;
|
||||
const mechanic = member.controlMechanic;
|
||||
if (berserkerRage && !member.activeCast && (mechanic === "fear" || mechanic === "stun")
|
||||
&& enemyControlAbilityCanEscapeAuras(
|
||||
berserkerRage, mechanic, controlCombat.auras, member.id, now,
|
||||
)
|
||||
&& partyAbilityTargetIsValid(member, berserkerRage, member.id, undefined, now)) {
|
||||
const committed = commitPartyAbilityUseWithRogueCooldown(member, berserkerRage, now);
|
||||
const escapeAbility = !member.activeCast && (mechanic === "fear" || mechanic === "stun" || mechanic === "confuse")
|
||||
? abilitiesForCharacter(member.classId, member.secondaryClassId)
|
||||
.map((ability) => learnedAbilityForBuild(ability, member.level, member.talentRanks))
|
||||
.find((ability) => ability && enemyControlAbilityCanEscapeAuras(
|
||||
ability, mechanic, controlCombat.auras, member.id, now,
|
||||
) && partyAbilityTargetIsValid(member, ability, member.id, undefined, now))
|
||||
: undefined;
|
||||
if (escapeAbility) {
|
||||
const committed = commitPartyAbilityUseWithRogueCooldown(member, escapeAbility, now);
|
||||
if (committed) {
|
||||
const execution = executePartyAbility(committed, berserkerRage, member.id, now, nextMembers);
|
||||
const execution = executePartyAbility(committed, escapeAbility, member.id, now, nextMembers);
|
||||
const partyStateAfterGrant = usePartyStore.getState().members.find((candidate) => candidate.id === member.id);
|
||||
const actorAfterGrant = useCombatStore.getState().actors[member.id];
|
||||
const controlledUntil = actorAfterGrant?.controlledUntil
|
||||
@@ -5429,8 +5446,9 @@ export function advanceDungeonPartyCombat(now = Date.now()): void {
|
||||
...execution.member,
|
||||
controlledUntil,
|
||||
controlMechanic: controlledUntil > now ? controlMechanic : null,
|
||||
status: targetId ? member.role === "tank" ? "guarding" : "attacking" : "ready",
|
||||
nextActionAt: now + Math.max(250, berserkerRage.castTimeMs, berserkerRage.gcdMs),
|
||||
status: controlledUntil > now && enemyControlPreventsActions(controlMechanic)
|
||||
? "holding" : targetId ? member.role === "tank" ? "guarding" : "attacking" : "ready",
|
||||
nextActionAt: now + Math.max(250, escapeAbility.castTimeMs, escapeAbility.gcdMs),
|
||||
};
|
||||
continue;
|
||||
}
|
||||
@@ -5440,7 +5458,31 @@ export function advanceDungeonPartyCombat(now = Date.now()): void {
|
||||
: { ...member, status: "holding" };
|
||||
continue;
|
||||
}
|
||||
if (combat.auras.some((aura) => aura.targetId === member.id && aura.definition.utilityTags?.includes("pacified"))) {
|
||||
const hunterDeterrence = member.classId === "hunter"
|
||||
? abilitiesForCharacter(member.classId, member.secondaryClassId)
|
||||
.map((ability) => learnedAbilityForBuild(ability, member.level, member.talentRanks))
|
||||
.find((ability): ability is AbilityDefinition => Boolean(ability
|
||||
&& ability.id === WOW335_HUNTER_DETERRENCE_ABILITY_ID && wow335DeterrenceAbilityContract(ability)))
|
||||
: undefined;
|
||||
const activeHunterDeterrence = hunterDeterrence && wow335DeterrenceActivePackage({
|
||||
ability: hunterDeterrence, auras: combat.auras, defenderId: member.id, nowMs: now,
|
||||
});
|
||||
if (!member.activeCast && hunterDeterrence && !activeHunterDeterrence
|
||||
&& Object.values(combat.mobs).some((mob) => !mob.dead && mob.engaged && mob.targetActorId === member.id
|
||||
&& (member.health / Math.max(1, member.maxHealth)
|
||||
< currentCompanionCooldownPolicy().generic.defensiveHealthPercent / 100
|
||||
|| Boolean(imminentCooldownMechanic(mob, member.role, now))))
|
||||
&& partyAbilityIsReady(member, hunterDeterrence, now)
|
||||
&& partyAbilityTargetIsValid(member, hunterDeterrence, member.id, undefined, now)) {
|
||||
const committed = commitPartyAbilityUseWithRogueCooldown(member, hunterDeterrence, now);
|
||||
if (committed) {
|
||||
const execution = executePartyAbility(committed, hunterDeterrence, member.id, now, nextMembers);
|
||||
nextMembers[index] = { ...execution.member, status: "holding", nextActionAt: now + 250 };
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!activeHunterDeterrence && combat.auras.some((aura) => aura.targetId === member.id
|
||||
&& (aura.expiresAt === null || aura.expiresAt > now) && aura.definition.utilityTags?.includes("pacified"))) {
|
||||
nextMembers[index] = member.status === "holding" ? member : { ...member, status: "holding" };
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user