forked from phenom/healer-man
Release Healer Man 0.1.32
This commit is contained in:
1 parent
d3d6aaebdb
commit
6dcb1034e5
102 files changed
+10772
-788
No files matched your search
@@ -1,4 +1,4 @@
|
||||
import { lazy, Suspense, useState, type CSSProperties } from "react";
|
||||
import { useState, type CSSProperties } from "react";
|
||||
import { classById, FACTIONS, raceById } from "../app/characterCatalog";
|
||||
import type { CharacterProfile } from "../app/types";
|
||||
import { useShellStore } from "../app/shellStore";
|
||||
@@ -28,14 +28,8 @@ import { ActionBar, type ActionBarItem, type ActionBarItems } from "./ActionBar"
|
||||
import { DungeonMapGraphic } from "./DungeonMapGraphic";
|
||||
import { ExperienceBar } from "./ExperienceBar";
|
||||
import { BrandMark, FrontSurface } from "./FrontSurface";
|
||||
import {
|
||||
getLoadedCompanionManastormPanel,
|
||||
loadCompanionManastormPanel,
|
||||
} from "./gameplayResources";
|
||||
|
||||
const CompanionManastormPanel = lazy(() => loadCompanionManastormPanel().then((module) => ({
|
||||
default: module.CompanionManastormPanel,
|
||||
})));
|
||||
import { ShelvedCompanionPanel } from "@shelvedCompanionPanel";
|
||||
import { SHELVED_MODES_ENABLED } from "../app/releaseFeatures";
|
||||
|
||||
function safePercent(current: number, maximum: number): number {
|
||||
if (!Number.isFinite(current) || !Number.isFinite(maximum) || maximum <= 0) return 0;
|
||||
@@ -158,7 +152,6 @@ export function CompanionPanel({ snapshot, send, preferControllerBinds = false }
|
||||
}) {
|
||||
const selected = snapshot.characters.find((character) => character.id === snapshot.selectedCharacterId) ?? null;
|
||||
const active = snapshot.activeCharacter ?? selected;
|
||||
const PreloadedCompanionManastormPanel = getLoadedCompanionManastormPanel();
|
||||
|
||||
if (snapshot.phase === "login") {
|
||||
return (
|
||||
@@ -289,7 +282,7 @@ export function CompanionPanel({ snapshot, send, preferControllerBinds = false }
|
||||
<footer className="companion-actions">
|
||||
<button type="button" onClick={() => send({ name: "returnToCharacters" })}>Change character</button>
|
||||
<button type="button" disabled={!selected} onClick={() => send({ name: "openDungeons" })}>Dungeons</button>
|
||||
<button type="button" disabled={!selected} onClick={() => send({ name: "openManastorms" })}>Manastorms</button>
|
||||
{SHELVED_MODES_ENABLED ? <button type="button" disabled={!selected} onClick={() => send({ name: "openManastorms" })}>Manastorms</button> : null}
|
||||
</footer>
|
||||
</FrontSurface>
|
||||
);
|
||||
@@ -300,13 +293,7 @@ export function CompanionPanel({ snapshot, send, preferControllerBinds = false }
|
||||
}
|
||||
|
||||
if (snapshot.phase === "manastorms") {
|
||||
return PreloadedCompanionManastormPanel ? (
|
||||
<PreloadedCompanionManastormPanel selected={selected} send={send} />
|
||||
) : (
|
||||
<Suspense fallback={<FrontSurface bottom className="companion-panel" ariaLabel="Loading Manastorm companion screen"><span>Loading Manastorm data…</span></FrontSurface>}>
|
||||
<CompanionManastormPanel selected={selected} send={send} />
|
||||
</Suspense>
|
||||
);
|
||||
return <ShelvedCompanionPanel selected={selected} send={send} />;
|
||||
}
|
||||
|
||||
const race = active ? raceById(active.raceId) : null;
|
||||
|
||||
@@ -6,6 +6,14 @@ const content = vi.hoisted(() => ({
|
||||
installed: true,
|
||||
update: true,
|
||||
bundled: false,
|
||||
packs: [{
|
||||
id: "characters:test",
|
||||
label: "Test Characters",
|
||||
bytes: 1024,
|
||||
fileCount: 1,
|
||||
bundledOnAndroid: false,
|
||||
dependencies: [],
|
||||
}],
|
||||
install: vi.fn(async () => undefined),
|
||||
remove: vi.fn(async () => undefined),
|
||||
}));
|
||||
@@ -29,14 +37,7 @@ vi.mock("../content/useContentManager", () => ({
|
||||
checking: false,
|
||||
manifest: {
|
||||
version: "content-test",
|
||||
packs: [{
|
||||
id: "characters:test",
|
||||
label: "Test Characters",
|
||||
bytes: 1024,
|
||||
fileCount: 1,
|
||||
bundledOnAndroid: false,
|
||||
dependencies: [],
|
||||
}],
|
||||
packs: content.packs,
|
||||
},
|
||||
bundledManifest: null,
|
||||
installingPackId: null,
|
||||
@@ -61,6 +62,10 @@ describe("browser Downloads actions", () => {
|
||||
content.installed = true;
|
||||
content.update = true;
|
||||
content.bundled = false;
|
||||
content.packs = [{
|
||||
id: "characters:test", label: "Test Characters", bytes: 1024,
|
||||
fileCount: 1, bundledOnAndroid: false, dependencies: [],
|
||||
}];
|
||||
});
|
||||
|
||||
it("offers an update action when an installed browser pack has changed", () => {
|
||||
@@ -92,4 +97,17 @@ describe("browser Downloads actions", () => {
|
||||
expect(markup).toContain("Included — always available");
|
||||
expect(markup).not.toContain(">Remove</button>");
|
||||
});
|
||||
|
||||
it("hides shelved and internal dependency packs from Downloads", () => {
|
||||
content.packs = [
|
||||
...content.packs,
|
||||
{ id: "core:arenas", label: "Rated Arenas", bytes: 1, fileCount: 1, bundledOnAndroid: false, dependencies: [] },
|
||||
{ id: "manastorm:deadmines", label: "Deadmines Manastorm", bytes: 1, fileCount: 1, bundledOnAndroid: false, dependencies: [] },
|
||||
];
|
||||
const markup = renderToStaticMarkup(createElement(ContentManagerScreen, { onClose: () => undefined }));
|
||||
|
||||
expect(markup).toContain("Test Characters");
|
||||
expect(markup).not.toContain("Rated Arenas");
|
||||
expect(markup).not.toContain("Deadmines Manastorm");
|
||||
});
|
||||
});
|
||||
@@ -12,6 +12,7 @@ import { useContentManagerSnapshot } from "../content/useContentManager";
|
||||
import { DualDisplayFrame } from "../components/DualDisplayFrame";
|
||||
import { LazyLocalCompanionPanel } from "./LazyLocalCompanionPanel";
|
||||
import { BrandMark, FrontSurface } from "./FrontSurface";
|
||||
import contentPolicy from "../../shared/content-policy.json";
|
||||
|
||||
function formatBytes(bytes: number): string {
|
||||
if (bytes >= 1024 ** 3) return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
||||
@@ -23,7 +24,10 @@ function formatBytes(bytes: number): string {
|
||||
export function ContentManagerScreen({ onClose }: { readonly onClose: () => void }) {
|
||||
const state = useContentManagerSnapshot();
|
||||
const native = Capacitor.isNativePlatform();
|
||||
const packs = useMemo(() => state.manifest?.packs ?? state.bundledManifest?.packs ?? [], [state.bundledManifest, state.manifest]);
|
||||
const packs = useMemo(() => (state.manifest?.packs ?? state.bundledManifest?.packs ?? []).filter((pack) => (
|
||||
!contentPolicy.hiddenContentPackIds.includes(pack.id)
|
||||
&& !contentPolicy.hiddenContentPackPrefixes.some((prefix) => pack.id.startsWith(prefix))
|
||||
)), [state.bundledManifest, state.manifest]);
|
||||
const progress = state.totalBytes > 0 ? Math.min(1, state.downloadedBytes / state.totalBytes) : 0;
|
||||
|
||||
return (
|
||||
|
||||
+2
-2
@@ -30,7 +30,7 @@ import { EncounterTimeline } from "./EncounterTimeline";
|
||||
import { PartyFrames } from "./PartyFrames";
|
||||
import { TimedEffectStrip } from "./TimedEffectStrip";
|
||||
import { AuraStrip } from "./AuraStrip";
|
||||
import { ManastormStatus } from "./ManastormStatus";
|
||||
import { ShelvedHud } from "@shelvedHud";
|
||||
import { ThreatMeterWidget } from "./ThreatMeterWidget";
|
||||
import { BossLootWindow } from "./BossLootWindow";
|
||||
import { EditableHudElement } from "./EditableHudElement";
|
||||
@@ -363,7 +363,7 @@ export function Hud() {
|
||||
|
||||
return (
|
||||
<div className="hud" style={{ "--game-ui-scale": settings.uiScale } as CSSProperties}>
|
||||
<ManastormStatus />
|
||||
<ShelvedHud />
|
||||
<BossLootWindow />
|
||||
<EditableHudElement id="completion">{dungeonComplete && health > 0 ? (
|
||||
<DungeonCompletePrompt dungeonTitle={dungeon.title} onFinish={finishDungeon} />
|
||||
|
||||
+11
-13
@@ -8,8 +8,8 @@ import { LazyLocalCompanionPanel } from "./LazyLocalCompanionPanel";
|
||||
import { ControllerButton } from "./ControllerButton";
|
||||
import { ContentManagerScreen } from "./ContentManagerScreen";
|
||||
import { BrandMark, ControllerLegend, FrontSurface } from "./FrontSurface";
|
||||
import { isGameMasterSession } from "../game/manastormAdminConfig";
|
||||
import { OnlineGroupPanel } from "./OnlineGroupPanel";
|
||||
import { sessionCanUseShelvedGmTools, SHELVED_MODES_ENABLED } from "../app/releaseFeatures";
|
||||
|
||||
const GAME_MODES = [
|
||||
{
|
||||
@@ -19,20 +19,19 @@ const GAME_MODES = [
|
||||
description: "Choose an expedition and lead your party into the depths.",
|
||||
available: true,
|
||||
},
|
||||
{
|
||||
id: "manastorms",
|
||||
...(SHELVED_MODES_ENABLED ? [{
|
||||
id: "manastorms" as const,
|
||||
number: "02",
|
||||
name: "Manastorms",
|
||||
description: "Chain random boss encounters as the storm grows stronger and more rewarding.",
|
||||
available: true,
|
||||
},
|
||||
{
|
||||
id: "arenas",
|
||||
}, {
|
||||
id: "arenas" as const,
|
||||
number: "03",
|
||||
name: "Arenas",
|
||||
description: "Rated 2v2 and 3v3 combat against players or rating-matched opponents.",
|
||||
available: true,
|
||||
},
|
||||
}] : []),
|
||||
{
|
||||
id: "trials",
|
||||
number: "04",
|
||||
@@ -77,7 +76,7 @@ export function MainMenuScreen() {
|
||||
const openGmAdmin = useShellStore((state) => state.openGmAdmin);
|
||||
const character = activeCharacter ?? rosterCharacter;
|
||||
const isRom = character?.categoryId === "rom";
|
||||
const isGm = isGameMasterSession(session);
|
||||
const isGm = sessionCanUseShelvedGmTools(session);
|
||||
|
||||
const actions = useMemo<MenuAction[]>(() => [
|
||||
{
|
||||
@@ -85,20 +84,19 @@ export function MainMenuScreen() {
|
||||
run: openDungeons,
|
||||
neighbors: { right: "mode-manastorms", down: "back-to-roster" },
|
||||
},
|
||||
{
|
||||
...(SHELVED_MODES_ENABLED ? [{
|
||||
id: "mode-manastorms",
|
||||
run: openManastorms,
|
||||
neighbors: { left: "mode-dungeons", down: "back-to-roster" },
|
||||
},
|
||||
{
|
||||
}, {
|
||||
id: "mode-arenas",
|
||||
run: openArenas,
|
||||
neighbors: { left: "mode-manastorms", down: "back-to-roster" },
|
||||
},
|
||||
}] : []),
|
||||
{
|
||||
id: "mode-downloads",
|
||||
run: () => setContentManagerOpen(true),
|
||||
neighbors: { left: "mode-manastorms", down: "back-to-roster" },
|
||||
neighbors: { left: SHELVED_MODES_ENABLED ? "mode-manastorms" : "mode-dungeons", down: "back-to-roster" },
|
||||
},
|
||||
{
|
||||
id: "back-to-roster",
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useShellStore } from "../app/shellStore";
|
||||
import { useGameStore } from "../game/store";
|
||||
import { useMenuController, type MenuAction } from "../input/useMenuController";
|
||||
import { ControllerButton } from "./ControllerButton";
|
||||
import { isGameMasterSession } from "../game/manastormAdminConfig";
|
||||
import { sessionCanUseShelvedGmTools } from "../app/releaseFeatures";
|
||||
import { useInputBindingLabels } from "../input/useInputBindingLabels";
|
||||
import { INPUT_ACTION_BY_ID } from "../input/keybinds";
|
||||
|
||||
@@ -33,7 +33,7 @@ function PauseDialog() {
|
||||
abandonManastormRun();
|
||||
};
|
||||
const skillMenuLabel = activeCharacter?.categoryId === "rom" ? "Skills" : "Talents";
|
||||
const isGm = isGameMasterSession(session);
|
||||
const isGm = sessionCanUseShelvedGmTools(session);
|
||||
|
||||
const actions = useMemo<MenuAction[]>(() => [
|
||||
{ id: "pause-resume", run: resume },
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { CharacterProfile } from "../app/types";
|
||||
import type { CompanionCommand } from "../platform/dualScreenSync";
|
||||
|
||||
export function ShelvedCompanionPanel(_props: {
|
||||
readonly selected: CharacterProfile | null;
|
||||
readonly send: (command: CompanionCommand) => void;
|
||||
}) {
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { lazy, Suspense } from "react";
|
||||
import type { CharacterProfile } from "../app/types";
|
||||
import type { CompanionCommand } from "../platform/dualScreenSync";
|
||||
import { FrontSurface } from "./FrontSurface";
|
||||
|
||||
const CompanionManastormPanel = lazy(() => import("./CompanionManastormPanel").then((module) => ({
|
||||
default: module.CompanionManastormPanel,
|
||||
})));
|
||||
|
||||
export function ShelvedCompanionPanel({
|
||||
selected,
|
||||
send,
|
||||
}: {
|
||||
readonly selected: CharacterProfile | null;
|
||||
readonly send: (command: CompanionCommand) => void;
|
||||
}) {
|
||||
return (
|
||||
<Suspense fallback={<FrontSurface bottom className="companion-panel" ariaLabel="Loading Manastorm companion screen"><span>Loading Manastorm data…</span></FrontSurface>}>
|
||||
<CompanionManastormPanel selected={selected} send={send} />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export function ShelvedHud() {
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { ManastormStatus } from "./ManastormStatus";
|
||||
|
||||
export function ShelvedHud() {
|
||||
return <ManastormStatus />;
|
||||
}
|
||||
@@ -21,6 +21,10 @@ const companionPanelSource = readFileSync(
|
||||
new URL("./CompanionPanel.tsx", import.meta.url),
|
||||
"utf8",
|
||||
);
|
||||
const shelvedCompanionSource = readFileSync(
|
||||
new URL("./ShelvedCompanionPanel.enabled.tsx", import.meta.url),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
describe("gameplay panel loading integration", () => {
|
||||
it("loads talent resources behind the main-menu catalog gate, not during gameplay", () => {
|
||||
@@ -42,9 +46,10 @@ describe("gameplay panel loading integration", () => {
|
||||
expect(gameplayResourcesSource).toContain('import("../GameRuntime")');
|
||||
expect(gameplayResourcesSource).toContain('import("./PartyManagementPanel")');
|
||||
expect(gameplayResourcesSource).toContain('import("./BossGuidePanel")');
|
||||
expect(gameplayResourcesSource).toContain('import("./CompanionManastormPanel")');
|
||||
expect(gameplayResourcesSource).not.toContain('import("./CompanionManastormPanel")');
|
||||
expect(shelvedCompanionSource).toContain('import("./CompanionManastormPanel")');
|
||||
expect(gameplayPanelsSource).toContain("<PreloadedPartyManagementPanel />");
|
||||
expect(gameplayPanelsSource).toContain("<PreloadedBossGuidePanel />");
|
||||
expect(companionPanelSource).toContain("<PreloadedCompanionManastormPanel");
|
||||
expect(companionPanelSource).toContain("<ShelvedCompanionPanel");
|
||||
});
|
||||
});
|
||||
@@ -4,8 +4,6 @@ let partyManagementPanelModule: Promise<typeof import("./PartyManagementPanel")>
|
||||
let loadedPartyManagementPanel: typeof import("./PartyManagementPanel")["PartyManagementPanel"] | null = null;
|
||||
let bossGuidePanelModule: Promise<typeof import("./BossGuidePanel")> | null = null;
|
||||
let loadedBossGuidePanel: typeof import("./BossGuidePanel")["BossGuidePanel"] | null = null;
|
||||
let companionManastormPanelModule: Promise<typeof import("./CompanionManastormPanel")> | null = null;
|
||||
let loadedCompanionManastormPanel: typeof import("./CompanionManastormPanel")["CompanionManastormPanel"] | null = null;
|
||||
|
||||
export function loadGameRuntime() {
|
||||
if (!gameRuntimeModule) {
|
||||
@@ -64,31 +62,11 @@ export function getLoadedBossGuidePanel() {
|
||||
return loadedBossGuidePanel;
|
||||
}
|
||||
|
||||
export function loadCompanionManastormPanel() {
|
||||
if (!companionManastormPanelModule) {
|
||||
companionManastormPanelModule = import("./CompanionManastormPanel")
|
||||
.then((module) => {
|
||||
loadedCompanionManastormPanel = module.CompanionManastormPanel;
|
||||
return module;
|
||||
})
|
||||
.catch((error: unknown) => {
|
||||
companionManastormPanelModule = null;
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
return companionManastormPanelModule;
|
||||
}
|
||||
|
||||
export function getLoadedCompanionManastormPanel() {
|
||||
return loadedCompanionManastormPanel;
|
||||
}
|
||||
|
||||
export async function preloadSharedGameplayResources(originalMediaEnabled: boolean): Promise<void> {
|
||||
await Promise.all([
|
||||
loadGameRuntime(),
|
||||
loadPartyManagementPanel(),
|
||||
loadBossGuidePanel(),
|
||||
loadCompanionManastormPanel(),
|
||||
]);
|
||||
if (!originalMediaEnabled) return;
|
||||
const { ensureCombatMediaPack } = await import("../game/combatMediaPack");
|
||||
|
||||
Reference in new issue
Block a user