forked from phenom/healer-man
Update class mechanics, stat rulesets, and CoA reference catalogs
This commit is contained in:
1 parent
b16821e92e
commit
39458d5de6
221 files changed
+2584962
-568
No files matched your search
@@ -6,6 +6,7 @@ import { installManastormShellRuntime } from "./manastormShellRuntime";
|
||||
import { SHELVED_MODES_ENABLED } from "./releaseFeatures";
|
||||
import { DUNGEON_MANIFEST } from "../game/dungeonManifest";
|
||||
import { useGameStore } from "../game/store";
|
||||
import { useCombatStore } from "../game/combatStore";
|
||||
import { useManastormStore } from "../game/manastormStore";
|
||||
import { createEmptyManastormProgress, recordManastormCheckpoint } from "../game/manastormProgress";
|
||||
import {
|
||||
@@ -21,6 +22,8 @@ import {
|
||||
resolveBossAdminEntry,
|
||||
} from "../game/manastormAdminConfig";
|
||||
import { useManastormAdminStore } from "../game/manastormAdminStore";
|
||||
import { createCharacter, listCharacters, type StorageLike } from "./accountRepository";
|
||||
import { useOnlineGroupStore } from "./onlineGroupStore";
|
||||
|
||||
installDungeonShellRuntime();
|
||||
installManastormShellRuntime();
|
||||
@@ -62,6 +65,20 @@ function readyBossForMode(modeId: number) {
|
||||
const readyLevelingBoss = readyBossForMode(0);
|
||||
const readyEndgameBoss = readyBossForMode(2);
|
||||
if (!readyLevelingBoss || !readyEndgameBoss) throw new Error("Shell tests require runnable Manastorm bosses for both modes.");
|
||||
class MemoryStorage implements StorageLike {
|
||||
private values = new Map<string, string>();
|
||||
getItem(key: string) { return this.values.get(key) ?? null; }
|
||||
setItem(key: string, value: string) { this.values.set(key, value); }
|
||||
removeItem(key: string) { this.values.delete(key); }
|
||||
}
|
||||
class FailingWriteStorage extends MemoryStorage {
|
||||
failWrites = false;
|
||||
setItem(key: string, value: string) {
|
||||
if (this.failWrites) throw new Error("Storage quota exceeded.");
|
||||
super.setItem(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
const readyTestConfig = {
|
||||
...DEFAULT_MANASTORM_ADMIN_CONFIG,
|
||||
bosses: {
|
||||
@@ -84,9 +101,11 @@ function prepareSelectedCharacter(): void {
|
||||
describe("shell store", () => {
|
||||
afterEach(() => {
|
||||
vi.unstubAllEnvs();
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
useOnlineGroupStore.getState().reset();
|
||||
useShellStore.setState({
|
||||
phase: "login",
|
||||
session: null,
|
||||
@@ -108,6 +127,91 @@ describe("shell store", () => {
|
||||
expect(useShellStore.getState().notice).toMatch(/select or create/i);
|
||||
});
|
||||
|
||||
it("saves a Hunter's selected family to the active character and local roster", () => {
|
||||
const storage = new MemoryStorage();
|
||||
vi.stubGlobal("window", { localStorage: storage });
|
||||
const hunter = createCharacter({
|
||||
ownerId: "offline-roster",
|
||||
name: "Turtlekeeper",
|
||||
categoryId: "wow",
|
||||
raceId: "human",
|
||||
classId: "hunter",
|
||||
specializationId: "hunter:marksmanship",
|
||||
gender: "female",
|
||||
appearance: createDefaultAppearance(),
|
||||
}, storage).character!;
|
||||
useShellStore.setState({
|
||||
session: { kind: "offline", ownerId: hunter.ownerId, displayName: "Offline" },
|
||||
characters: [hunter],
|
||||
activeCharacter: hunter,
|
||||
});
|
||||
|
||||
expect(useShellStore.getState().saveActiveHunterPetFamily("turtle")).toBe(true);
|
||||
expect(useShellStore.getState().activeCharacter?.hunterPetFamily).toBe("turtle");
|
||||
expect(listCharacters(hunter.ownerId, storage)[0]?.hunterPetFamily).toBe("turtle");
|
||||
expect(useShellStore.getState().saveActiveHunterPetFamily("cat")).toBe(true);
|
||||
expect(useShellStore.getState().activeCharacter?.hunterPetFamily).toBe("cat");
|
||||
expect(listCharacters(hunter.ownerId, storage)[0]?.hunterPetFamily).toBe("cat");
|
||||
});
|
||||
|
||||
it("switches the selected RoM stat profile only from the main menu and reports storage failure", () => {
|
||||
const storage = new FailingWriteStorage();
|
||||
vi.stubGlobal("window", { localStorage: storage });
|
||||
const romCharacter = createCharacter({
|
||||
ownerId: "offline-roster",
|
||||
name: "Runekeeper",
|
||||
categoryId: "rom",
|
||||
raceId: "rom-human",
|
||||
classId: "rom-priest",
|
||||
gender: "female",
|
||||
appearance: createDefaultAppearance(),
|
||||
}, storage).character!;
|
||||
useShellStore.setState({
|
||||
phase: "main-menu",
|
||||
session: { kind: "offline", ownerId: romCharacter.ownerId, displayName: "Offline" },
|
||||
characters: [romCharacter],
|
||||
selectedCharacterId: romCharacter.id,
|
||||
activeCharacter: null,
|
||||
});
|
||||
|
||||
expect(useShellStore.getState().setSelectedCharacterStatRuleset("wow")).toBe(true);
|
||||
expect(useShellStore.getState().characters[0]?.statRuleset).toBe("wow");
|
||||
expect(listCharacters(romCharacter.ownerId, storage)[0]?.statRuleset).toBe("wow");
|
||||
|
||||
storage.failWrites = true;
|
||||
expect(useShellStore.getState().setSelectedCharacterStatRuleset("rom")).toBe(false);
|
||||
expect(useShellStore.getState().characters[0]?.statRuleset).toBe("wow");
|
||||
expect(useShellStore.getState().notice).toMatch(/could not be saved/i);
|
||||
|
||||
storage.failWrites = false;
|
||||
const previousMobs = useCombatStore.getState().mobs;
|
||||
try {
|
||||
useCombatStore.setState({ mobs: { "live-fight": { engaged: true, dead: false } as never } });
|
||||
expect(useShellStore.getState().setSelectedCharacterStatRuleset("rom")).toBe(false);
|
||||
expect(useShellStore.getState().notice).toMatch(/combat is active/i);
|
||||
expect(listCharacters(romCharacter.ownerId, storage)[0]?.statRuleset).toBe("wow");
|
||||
} finally {
|
||||
useCombatStore.setState({ mobs: previousMobs });
|
||||
}
|
||||
useShellStore.setState({ activeCharacter: romCharacter });
|
||||
expect(useShellStore.getState().setSelectedCharacterStatRuleset("rom")).toBe(false);
|
||||
useShellStore.setState({ activeCharacter: null });
|
||||
useOnlineGroupStore.setState({ snapshot: {
|
||||
group: { activity: { id: "live-run" } } as never,
|
||||
invitations: [],
|
||||
} });
|
||||
useShellStore.setState({ phase: "main-menu", notice: "" });
|
||||
expect(useShellStore.getState().setSelectedCharacterStatRuleset("rom")).toBe(false);
|
||||
expect(useShellStore.getState().characters[0]?.statRuleset).toBe("wow");
|
||||
expect(useShellStore.getState().notice).toMatch(/live online group activity/i);
|
||||
|
||||
useOnlineGroupStore.getState().reset();
|
||||
useShellStore.setState({ phase: "game", notice: "" });
|
||||
expect(useShellStore.getState().setSelectedCharacterStatRuleset("rom")).toBe(false);
|
||||
expect(useShellStore.getState().characters[0]?.statRuleset).toBe("wow");
|
||||
expect(listCharacters(romCharacter.ownerId, storage)[0]?.statRuleset).toBe("wow");
|
||||
});
|
||||
|
||||
it("routes a legacy WoW character through the mandatory specialization picker", () => {
|
||||
prepareSelectedCharacter();
|
||||
useShellStore.setState({ characters: [{ ...character, specializationId: null }] });
|
||||
|
||||
Reference in new issue
Block a user