Release Healer Man 0.1.6
This commit is contained in:
1 parent
55cfd43d66
commit
b12fb84859
96 files changed
+10874
-609
No files matched your search
@@ -0,0 +1,89 @@
|
||||
import {
|
||||
createDeathKnightRunes,
|
||||
type CombatResourcePoolState,
|
||||
type DeathKnightRuneState,
|
||||
type DeathKnightRuneType,
|
||||
} from "./combatActors";
|
||||
|
||||
export const MANA_FIVE_SECOND_RULE_MS = 5_000;
|
||||
export const DEATH_KNIGHT_RUNE_RECHARGE_MS = 10_000;
|
||||
|
||||
export interface CombatResourceAdvanceRules {
|
||||
readonly inCombat: boolean;
|
||||
readonly actorLevel: number;
|
||||
readonly now: number;
|
||||
readonly deltaSeconds: number;
|
||||
readonly manaSpiritRegenerationPerSecond?: number;
|
||||
}
|
||||
export function advanceCombatResourcePool(
|
||||
pool: CombatResourcePoolState,
|
||||
rules: CombatResourceAdvanceRules,
|
||||
): CombatResourcePoolState {
|
||||
const delta = Math.max(0, Math.min(1, Number.isFinite(rules.deltaSeconds) ? rules.deltaSeconds : 0));
|
||||
let rate = pool.regenerationPerSecond;
|
||||
if (pool.id === "mana" && rules.now - pool.lastSpentAt < MANA_FIVE_SECOND_RULE_MS) {
|
||||
rate = Math.max(0, rate - Math.max(0, rules.manaSpiritRegenerationPerSecond ?? 0));
|
||||
}
|
||||
if ((pool.id === "rage" || pool.id === "runic-power") && !rules.inCombat) {
|
||||
rate = pool.id === "rage" ? -3 : -2.5;
|
||||
}
|
||||
return Object.freeze({
|
||||
...pool,
|
||||
current: Math.max(0, Math.min(pool.maximum, pool.current + rate * delta)),
|
||||
});
|
||||
}
|
||||
|
||||
export interface RuneCost {
|
||||
readonly blood?: number;
|
||||
readonly frost?: number;
|
||||
readonly unholy?: number;
|
||||
}
|
||||
|
||||
export function availableDeathKnightRunes(
|
||||
runes: readonly DeathKnightRuneState[],
|
||||
now: number,
|
||||
): readonly DeathKnightRuneState[] {
|
||||
return runes.filter((rune) => rune.readyAt <= now);
|
||||
}
|
||||
|
||||
function runeMatches(rune: DeathKnightRuneState, type: Exclude<DeathKnightRuneType, "death">): boolean {
|
||||
return rune.type === type || rune.type === "death";
|
||||
}
|
||||
|
||||
export function spendDeathKnightRunes(
|
||||
runes: readonly DeathKnightRuneState[] | undefined,
|
||||
cost: RuneCost,
|
||||
now: number,
|
||||
rechargeMs = DEATH_KNIGHT_RUNE_RECHARGE_MS,
|
||||
): readonly DeathKnightRuneState[] | null {
|
||||
const current = runes?.length === 6 ? [...runes] : [...createDeathKnightRunes(now)];
|
||||
const spent = new Set<number>();
|
||||
for (const type of ["blood", "frost", "unholy"] as const) {
|
||||
const count = Math.max(0, Math.trunc(cost[type] ?? 0));
|
||||
for (let index = 0; index < count; index += 1) {
|
||||
const candidate = current.find((rune) => (
|
||||
!spent.has(rune.slot) && rune.readyAt <= now && runeMatches(rune, type)
|
||||
));
|
||||
if (!candidate) return null;
|
||||
spent.add(candidate.slot);
|
||||
}
|
||||
}
|
||||
return Object.freeze(current.map((rune) => spent.has(rune.slot)
|
||||
? Object.freeze({ ...rune, readyAt: now + Math.max(1, rechargeMs) })
|
||||
: rune));
|
||||
}
|
||||
|
||||
export function activateDeathKnightRunes(
|
||||
runes: readonly DeathKnightRuneState[],
|
||||
count: number,
|
||||
): readonly DeathKnightRuneState[] {
|
||||
let remaining = Math.max(0, Math.trunc(count));
|
||||
return Object.freeze([...runes]
|
||||
.sort((left, right) => right.readyAt - left.readyAt || left.slot - right.slot)
|
||||
.map((rune) => {
|
||||
if (remaining <= 0 || rune.readyAt <= 0) return rune;
|
||||
remaining -= 1;
|
||||
return Object.freeze({ ...rune, readyAt: 0 });
|
||||
})
|
||||
.sort((left, right) => left.slot - right.slot));
|
||||
}
|
||||
Reference in new issue
Block a user