Release Healer Man 0.1.22
This commit is contained in:
1 parent
f6f2375145
commit
7f6001723a
144 files changed
+54107
-690
No files matched your search
@@ -4,8 +4,13 @@ import { installManastormShellRuntime } from "../app/manastormShellRuntime";
|
||||
import { selectedCharacter, useShellStore } from "../app/shellStore";
|
||||
import { preloadAvatarModels } from "../avatar/avatarPreloader";
|
||||
import { DualDisplayFrame } from "../components/DualDisplayFrame";
|
||||
import { contentPackIsUsable, installContentPack } from "../content/contentManager";
|
||||
import { useContentManagerSnapshot } from "../content/useContentManager";
|
||||
import { MANASTORM_CATALOG_SUMMARY } from "../game/manastormCatalogSummary";
|
||||
import { GENERATED_MANASTORM_STAGE_ASSET_PACKAGE_COUNT } from "../game/manastormAssetRegistry";
|
||||
import {
|
||||
GENERATED_MANASTORM_STAGE_ASSET_PACKAGE_COUNT,
|
||||
generatedManastormContentPackId,
|
||||
} from "../game/manastormAssetRegistry";
|
||||
import { INSTALLED_MANASTORM_RUNTIME_CATALOG } from "../game/manastormInstalledCatalog";
|
||||
import { effectiveManastormCatalog } from "../game/manastormAdminConfig";
|
||||
import { useManastormAdminStore } from "../game/manastormAdminStore";
|
||||
@@ -46,6 +51,7 @@ const ROLE_DETAILS: Readonly<Record<PartyRole, { readonly sigil: string; readonl
|
||||
};
|
||||
|
||||
export function ManastormSelectScreen() {
|
||||
const contentState = useContentManagerSnapshot();
|
||||
const activeCharacter = useShellStore((state) => state.activeCharacter);
|
||||
const rosterCharacter = useShellStore(selectedCharacter);
|
||||
const enterManastorm = useShellStore((state) => state.enterManastorm);
|
||||
@@ -80,9 +86,20 @@ export function ManastormSelectScreen() {
|
||||
);
|
||||
const modeEnabled = adminConfig.global.enabled
|
||||
&& (modeId === 2 ? adminConfig.global.endgameEnabled : adminConfig.global.levelingEnabled);
|
||||
const hasReadyBosses = Boolean(effectiveCatalog.stages?.some((stage) => (
|
||||
const eligibleStages = useMemo(() => effectiveCatalog.stages?.filter((stage) => (
|
||||
!stage.modeIds?.length || stage.modeIds.includes(modeId)
|
||||
)));
|
||||
)) ?? [], [effectiveCatalog, modeId]);
|
||||
const hasReadyBosses = eligibleStages.length > 0;
|
||||
const requiredContentPackIds = useMemo(() => Object.freeze([...new Set(eligibleStages.flatMap((stage) => {
|
||||
if (stage.assetPackageId) {
|
||||
const packId = generatedManastormContentPackId(stage.assetPackageId);
|
||||
return packId ? [packId] : [];
|
||||
}
|
||||
return stage.dungeonId ? [`dungeon:${stage.dungeonId}`] : [];
|
||||
}))].sort()), [eligibleStages]);
|
||||
const missingContentPackIds = import.meta.env.DEV
|
||||
? []
|
||||
: requiredContentPackIds.filter((packId) => !contentPackIsUsable(packId));
|
||||
const canEnter = modeEnabled && hasReadyBosses;
|
||||
const modeProgress = manastormModeProgress(progress, modeId);
|
||||
const modeLabel = modeId === 2 ? "End-game" : "Leveling";
|
||||
@@ -124,6 +141,9 @@ export function ManastormSelectScreen() {
|
||||
);
|
||||
const persistedSpellIds = progress.loadout.filter((id) => availableSpellIds.has(id));
|
||||
const [selectedSpellIds, setSelectedSpellIds] = useState<readonly string[]>(persistedSpellIds);
|
||||
const [pendingDownloadRole, setPendingDownloadRole] = useState<PartyRole | null>(null);
|
||||
const [downloading, setDownloading] = useState(false);
|
||||
const [downloadError, setDownloadError] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedSpellIds(persistedSpellIds);
|
||||
@@ -155,7 +175,7 @@ export function ManastormSelectScreen() {
|
||||
});
|
||||
};
|
||||
|
||||
const enterAsRole = async (role: PartyRole) => {
|
||||
const continueAsRole = async (role: PartyRole) => {
|
||||
if (!canEnter || !character) return;
|
||||
if (!onlineGroup || !session || session.kind !== "account") {
|
||||
enterManastorm(role, partySize, startingLevel);
|
||||
@@ -174,7 +194,42 @@ export function ManastormSelectScreen() {
|
||||
});
|
||||
};
|
||||
|
||||
const actions = useMemo<MenuAction[]>(() => [
|
||||
const enterAsRole = async (role: PartyRole) => {
|
||||
if (!canEnter || !character) return;
|
||||
if (missingContentPackIds.length > 0) {
|
||||
setDownloadError("");
|
||||
setPendingDownloadRole(role);
|
||||
return;
|
||||
}
|
||||
await continueAsRole(role);
|
||||
};
|
||||
|
||||
const confirmDownload = async () => {
|
||||
if (!pendingDownloadRole || downloading) return;
|
||||
setDownloading(true);
|
||||
setDownloadError("");
|
||||
try {
|
||||
for (const packId of requiredContentPackIds) {
|
||||
if (!contentPackIsUsable(packId)) await installContentPack(packId);
|
||||
}
|
||||
const stillMissing = requiredContentPackIds.filter((packId) => !contentPackIsUsable(packId));
|
||||
if (stillMissing.length > 0) {
|
||||
throw new Error("One or more downloaded Manastorm maps could not be verified on this device.");
|
||||
}
|
||||
const role = pendingDownloadRole;
|
||||
setPendingDownloadRole(null);
|
||||
await continueAsRole(role);
|
||||
} catch (error) {
|
||||
setDownloadError(error instanceof Error ? error.message : "The Manastorm download failed.");
|
||||
} finally {
|
||||
setDownloading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const actions = useMemo<MenuAction[]>(() => pendingDownloadRole ? [
|
||||
{ id: "manastorm-download-cancel", run: () => setPendingDownloadRole(null), enabled: !downloading },
|
||||
{ id: "manastorm-download-confirm", run: () => void confirmDownload(), enabled: !downloading },
|
||||
] : [
|
||||
...MANASTORM_PARTY_SIZES.map((size) => ({
|
||||
id: `manastorm-party-${size}`,
|
||||
run: () => setPartySize(size),
|
||||
@@ -196,10 +251,13 @@ export function ManastormSelectScreen() {
|
||||
availableRoles,
|
||||
canEnter,
|
||||
checkpoint,
|
||||
contentState.installedRevision,
|
||||
groupHumanCount,
|
||||
fillWithAi,
|
||||
onlineGroup,
|
||||
effectivePartySize,
|
||||
downloading,
|
||||
pendingDownloadRole,
|
||||
returnToMainMenu,
|
||||
selectedSpellIds,
|
||||
spellLoadoutModel,
|
||||
@@ -207,7 +265,9 @@ export function ManastormSelectScreen() {
|
||||
]);
|
||||
const controller = useMenuController(actions, {
|
||||
initialId: "manastorm-party-5",
|
||||
onBack: returnToMainMenu,
|
||||
onBack: pendingDownloadRole
|
||||
? () => { if (!downloading) setPendingDownloadRole(null); }
|
||||
: returnToMainMenu,
|
||||
});
|
||||
|
||||
const race = character ? raceById(character.raceId) : null;
|
||||
@@ -246,6 +306,11 @@ export function ManastormSelectScreen() {
|
||||
: "No bosses are currently marked Ready for this Manastorm mode."}
|
||||
</p>
|
||||
) : null}
|
||||
{canEnter && missingContentPackIds.length > 0 ? (
|
||||
<p className="front-notice" role="status">
|
||||
{missingContentPackIds.length} Manastorm map {missingContentPackIds.length === 1 ? "pack is" : "packs are"} not on this device. Choose a role to download them and continue.
|
||||
</p>
|
||||
) : null}
|
||||
<dl>
|
||||
<div><dt>Catalog maps</dt><dd>{MANASTORM_CATALOG_SUMMARY.maps}</dd></div>
|
||||
<div><dt>Streamed maps</dt><dd>{GENERATED_MANASTORM_STAGE_ASSET_PACKAGE_COUNT}</dd></div>
|
||||
@@ -412,6 +477,48 @@ export function ManastormSelectScreen() {
|
||||
</section>
|
||||
</main>
|
||||
<ControllerLegend back />
|
||||
{pendingDownloadRole && (
|
||||
<div className="front-modal-backdrop">
|
||||
<section className="front-confirm" role="alertdialog" aria-modal="true" aria-labelledby="manastorm-download-title">
|
||||
<p className="front-kicker">Download required</p>
|
||||
<h2 id="manastorm-download-title">Download {modeLabel} Manastorm maps?</h2>
|
||||
<p>
|
||||
This mode can draw {missingContentPackIds.length} map {missingContentPackIds.length === 1 ? "pack" : "packs"} that {missingContentPackIds.length === 1 ? "is" : "are"} not on this device. Download and verify the required files now, then enter the storm?
|
||||
</p>
|
||||
{downloading && (
|
||||
<p role="status">
|
||||
Downloading and verifying files
|
||||
{contentState.totalBytes > 0 ? ` — ${Math.round((contentState.downloadedBytes / contentState.totalBytes) * 100)}%` : "..."}
|
||||
</p>
|
||||
)}
|
||||
{downloadError && <p className="front-notice" role="alert">{downloadError}</p>}
|
||||
<div>
|
||||
<ControllerButton
|
||||
controlId="manastorm-download-cancel"
|
||||
selectedId={controller.selectedId}
|
||||
select={controller.select}
|
||||
className="front-button"
|
||||
type="button"
|
||||
disabled={downloading}
|
||||
onClick={() => setPendingDownloadRole(null)}
|
||||
>
|
||||
Not now
|
||||
</ControllerButton>
|
||||
<ControllerButton
|
||||
controlId="manastorm-download-confirm"
|
||||
selectedId={controller.selectedId}
|
||||
select={controller.select}
|
||||
className="front-button front-button--primary"
|
||||
type="button"
|
||||
disabled={downloading}
|
||||
onClick={() => void confirmDownload()}
|
||||
>
|
||||
{downloading ? "Downloading..." : downloadError ? "Try again" : "Download and continue"}
|
||||
</ControllerButton>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
)}
|
||||
</FrontSurface>
|
||||
}
|
||||
bottom={<LazyLocalCompanionPanel />}
|
||||
|
||||
Reference in new issue
Block a user