104 lines
3.3 KiB
TypeScript
104 lines
3.3 KiB
TypeScript
import { afterEach, describe, expect, it } from "vitest";
|
|
import type { AbilityDefinition } from "./abilityCatalog";
|
|
import {
|
|
applyAbilityBalanceTuning,
|
|
currentBalanceTuningPolicy,
|
|
loadBalanceTuningPolicy,
|
|
normalizeBalanceTuningPolicy,
|
|
resetBalanceTuningCache,
|
|
saveBalanceTuningPolicy,
|
|
} from "./balanceTuning";
|
|
|
|
const ability: AbilityDefinition = {
|
|
id: "test-fireball",
|
|
classId: "mage",
|
|
dbcSpellId: 1,
|
|
name: "Test Fireball",
|
|
unlockLevel: 1,
|
|
icon: "",
|
|
sigil: "TF",
|
|
description: "Test",
|
|
target: "hostile",
|
|
range: { min: 0, max: 30 },
|
|
radius: 8,
|
|
castMode: "cast",
|
|
castTimeMs: 2_000,
|
|
cooldownMs: 10_000,
|
|
gcdMs: 1_500,
|
|
cost: { resource: "mana", amount: 20 },
|
|
effects: [
|
|
{ kind: "damage", coefficient: 2, basePoints: 100 },
|
|
{ kind: "dot", coefficient: 0.5, ticks: 4, intervalMs: 1_000 },
|
|
{ kind: "utility", action: "class-script", effectType: 1, auraType: 0, miscValue: 0, triggerSpellId: 0, durationMs: -1 },
|
|
],
|
|
};
|
|
|
|
function memoryStorage(initial: string | null = null) {
|
|
let value = initial;
|
|
return {
|
|
getItem: () => value,
|
|
setItem: (_key: string, next: string) => { value = next; },
|
|
removeItem: () => { value = null; },
|
|
value: () => value,
|
|
};
|
|
}
|
|
|
|
describe("local ability and talent balance tuning", () => {
|
|
afterEach(() => resetBalanceTuningCache());
|
|
|
|
it("normalizes unsafe values and drops default-only overrides", () => {
|
|
expect(normalizeBalanceTuningPolicy({
|
|
abilityOverrides: {
|
|
default: { cooldownPercent: 100 },
|
|
tuned: { cooldownPercent: -50, damagePercent: 9_999 },
|
|
},
|
|
talentOverrides: {
|
|
talent: { effectScalePercent: 150, modifiersPerRank: { "critical-chance-percent": 2.5 } },
|
|
},
|
|
})).toEqual({
|
|
schemaVersion: 1,
|
|
abilityOverrides: { tuned: { cooldownPercent: 0, damagePercent: 1_000 } },
|
|
talentOverrides: {
|
|
talent: { effectScalePercent: 150, modifiersPerRank: { "critical-chance-percent": 2.5 } },
|
|
},
|
|
});
|
|
});
|
|
|
|
it("applies cooldown, cast, cost, damage, duration, and radius tuning", () => {
|
|
const tuned = applyAbilityBalanceTuning(ability, normalizeBalanceTuningPolicy({
|
|
abilityOverrides: {
|
|
[ability.id]: {
|
|
cooldownPercent: 50,
|
|
castTimePercent: 0,
|
|
resourceCostPercent: 75,
|
|
damagePercent: 125,
|
|
durationPercent: 150,
|
|
radiusPercent: 200,
|
|
},
|
|
},
|
|
}));
|
|
expect(tuned).toMatchObject({
|
|
castMode: "instant",
|
|
castTimeMs: 0,
|
|
cooldownMs: 5_000,
|
|
cost: { amount: 15 },
|
|
radius: 16,
|
|
});
|
|
expect(tuned.effects[0]).toMatchObject({ coefficient: 2.5, basePoints: 125 });
|
|
expect(tuned.effects[1]).toMatchObject({ coefficient: 0.625, ticks: 6 });
|
|
expect(tuned.effects[2]).toMatchObject({ durationMs: -1 });
|
|
});
|
|
|
|
it("persists valid policy data and falls back after corrupt storage", () => {
|
|
const storage = memoryStorage();
|
|
saveBalanceTuningPolicy({ abilityOverrides: { x: { cooldownPercent: 80 } } }, storage);
|
|
expect(JSON.parse(storage.value()!)).toMatchObject({ abilityOverrides: { x: { cooldownPercent: 80 } } });
|
|
expect(currentBalanceTuningPolicy().abilityOverrides.x.cooldownPercent).toBe(80);
|
|
expect(loadBalanceTuningPolicy(memoryStorage("not-json"))).toEqual({
|
|
schemaVersion: 1,
|
|
abilityOverrides: {},
|
|
talentOverrides: {},
|
|
});
|
|
});
|
|
});
|