bitburner-src/src/ui/GameRoot.tsx

420 lines
13 KiB
TypeScript
Raw Normal View History

2021-09-20 05:29:02 +02:00
import React, { useState, useEffect } from "react";
import { createStyles, makeStyles } from "@mui/styles";
import { Box, Typography } from "@mui/material";
import { Theme } from "@mui/material/styles";
import { Player } from "@player";
2021-09-17 08:04:44 +02:00
import { installAugmentations } from "../Augmentation/AugmentationHelpers";
2021-09-21 22:49:38 +02:00
import { saveObject } from "../SaveObject";
2021-09-17 08:04:44 +02:00
import { onExport } from "../ExportBonus";
import { LocationName } from "@enums";
2021-12-14 00:44:52 +01:00
import { ITutorial, iTutorialStart } from "../InteractiveTutorial";
2021-09-19 06:46:39 +02:00
import { InteractiveTutorialRoot } from "./InteractiveTutorial/InteractiveTutorialRoot";
import { ITutorialEvents } from "./InteractiveTutorial/ITutorialEvents";
2021-09-18 10:01:07 +02:00
2021-09-17 08:04:44 +02:00
import { prestigeAugmentation } from "../Prestige";
2021-09-25 20:42:57 +02:00
import { dialogBoxCreate } from "./React/DialogBox";
2021-10-07 22:04:04 +02:00
import { GetAllServers } from "../Server/AllServers";
2022-08-21 01:14:54 +02:00
import { StockMarket } from "../StockMarket/StockMarket";
2021-09-17 08:04:44 +02:00
import { Page, PageWithContext, IRouter, ComplexPage, PageContext } from "./Router";
2021-09-19 06:46:39 +02:00
import { Overview } from "./React/Overview";
2021-09-17 08:04:44 +02:00
import { SidebarRoot } from "../Sidebar/ui/SidebarRoot";
2021-09-18 01:43:08 +02:00
import { AugmentationsRoot } from "../Augmentation/ui/AugmentationsRoot";
2021-09-17 08:04:44 +02:00
import { DevMenuRoot } from "../DevMenu";
2021-09-18 01:43:08 +02:00
import { BladeburnerRoot } from "../Bladeburner/ui/BladeburnerRoot";
import { GangRoot } from "../Gang/ui/GangRoot";
2021-09-17 08:04:44 +02:00
import { CorporationRoot } from "../Corporation/ui/CorporationRoot";
2021-09-18 01:43:08 +02:00
import { InfiltrationRoot } from "../Infiltration/ui/InfiltrationRoot";
2022-03-19 05:22:21 +01:00
import { GraftingRoot } from "../PersonObjects/Grafting/ui/GraftingRoot";
2021-09-18 01:43:08 +02:00
import { WorkInProgressRoot } from "./WorkInProgressRoot";
2022-04-17 19:51:14 +02:00
import { GameOptionsRoot } from "../GameOptions/ui/GameOptionsRoot";
2021-09-17 08:04:44 +02:00
import { SleeveRoot } from "../PersonObjects/Sleeve/ui/SleeveRoot";
import { HacknetRoot } from "../Hacknet/ui/HacknetRoot";
2021-09-18 10:01:07 +02:00
import { GenericLocation } from "../Locations/ui/GenericLocation";
import { LocationCity } from "../Locations/ui/City";
2021-09-17 08:04:44 +02:00
import { ProgramsRoot } from "../Programs/ui/ProgramsRoot";
2023-06-03 19:55:25 +02:00
import { ScriptEditorRoot } from "../ScriptEditor/ui/ScriptEditorRoot";
2021-09-17 08:04:44 +02:00
import { MilestonesRoot } from "../Milestones/ui/MilestonesRoot";
import { TerminalRoot } from "../Terminal/ui/TerminalRoot";
2023-07-12 23:10:52 +02:00
import { DocumentationRoot } from "../Documentation/ui/DocumentationRoot";
2022-03-06 05:05:55 +01:00
import { ActiveScriptsRoot } from "./ActiveScripts/ActiveScriptsRoot";
2021-09-17 08:04:44 +02:00
import { FactionsRoot } from "../Faction/ui/FactionsRoot";
import { FactionRoot } from "../Faction/ui/FactionRoot";
import { AugmentationsPage as FactionAugmentations } from "../Faction/ui/AugmentationsPage";
2021-09-18 18:13:20 +02:00
import { CharacterStats } from "./CharacterStats";
2021-09-17 08:04:44 +02:00
import { TravelAgencyRoot } from "../Locations/ui/TravelAgencyRoot";
2021-09-17 08:31:19 +02:00
import { StockMarketRoot } from "../StockMarket/ui/StockMarketRoot";
2021-09-18 01:43:08 +02:00
import { BitverseRoot } from "../BitNode/ui/BitverseRoot";
2021-09-25 23:21:50 +02:00
import { StaneksGiftRoot } from "../CotMG/ui/StaneksGiftRoot";
import { staneksGift } from "../CotMG/Helper";
2021-09-18 01:43:08 +02:00
import { CharacterOverview } from "./React/CharacterOverview";
import { BladeburnerCinematic } from "../Bladeburner/ui/BladeburnerCinematic";
2021-09-17 08:04:44 +02:00
import { workerScripts } from "../Netscript/WorkerScripts";
import { Unclickable } from "../Exploits/Unclickable";
2021-10-13 23:25:58 +02:00
import { Snackbar, SnackbarProvider } from "./React/Snackbar";
2021-10-01 07:00:50 +02:00
import { LogBoxManager } from "./React/LogBoxManager";
import { AlertManager } from "./React/AlertManager";
2021-10-02 04:53:23 +02:00
import { PromptManager } from "./React/PromptManager";
2021-10-01 19:08:37 +02:00
import { InvitationModal } from "../Faction/ui/InvitationModal";
import { calculateAchievements } from "../Achievements/Achievements";
2021-09-17 08:04:44 +02:00
2021-11-02 22:28:19 +01:00
import { RecoveryMode, RecoveryRoot } from "./React/RecoveryRoot";
import { AchievementsRoot } from "../Achievements/AchievementsRoot";
import { ErrorBoundary } from "./ErrorBoundary";
import { ThemeBrowser } from "../Themes/ui/ThemeBrowser";
import { ImportSave } from "./React/ImportSave";
import { BypassWrapper } from "./React/BypassWrapper";
2021-09-17 08:04:44 +02:00
2022-03-31 18:04:06 +02:00
import { Apr1 } from "./Apr1";
2022-07-26 21:09:11 +02:00
import { V2Modal } from "../utils/V2Modal";
import { MathJaxContext } from "better-react-mathjax";
import { useRerender } from "./React/hooks";
2023-07-12 23:10:52 +02:00
import { HistoryProvider } from "./React/Documentation";
import { GoRoot } from "../Go/GoRoot";
2021-10-26 21:50:55 +02:00
const htmlLocation = location;
const useStyles = makeStyles(
(theme: Theme) =>
createStyles({
root: {
"-ms-overflow-style": "none" /* for Internet Explorer, Edge */,
"scrollbar-width": "none" /* for Firefox */,
margin: theme.spacing(0),
flexGrow: 1,
padding: "8px",
minHeight: "100vh",
boxSizing: "border-box",
width: "1px",
},
}),
{ name: "GameRoot" },
2021-09-17 08:04:44 +02:00
);
2022-07-15 07:51:30 +02:00
const uninitialized = (): void => {
2022-03-11 21:19:10 +01:00
throw new Error("Router called before initialization");
};
const MAX_PAGES_IN_HISTORY = 10;
2021-09-18 01:43:08 +02:00
export let Router: IRouter = {
isInitialized: false,
2022-07-15 07:51:30 +02:00
page: () => {
throw new Error("Router called before initialization");
},
2022-03-11 21:19:10 +01:00
allowRouting: uninitialized,
2022-12-04 09:14:06 +01:00
toPage: () => {
throw new Error("Router called before initialization");
},
back: () => {
throw new Error("Router called before initialization");
},
2021-09-18 01:43:08 +02:00
};
function determineStartPage() {
2021-11-02 22:28:19 +01:00
if (RecoveryMode) return Page.Recovery;
2022-08-29 08:41:17 +02:00
if (Player.currentWork !== null) return Page.Work;
2021-09-18 01:43:08 +02:00
return Page.Terminal;
}
2022-08-29 08:41:17 +02:00
export function GameRoot(): React.ReactElement {
2021-09-17 08:04:44 +02:00
const classes = useStyles();
const [pages, setPages] = useState<PageWithContext[]>(() => [{ page: determineStartPage() }]);
const pageWithContext = pages[0];
2021-09-18 01:43:08 +02:00
const setNextPage = (pageWithContext: PageWithContext) =>
setPages((prev) => {
const next = [pageWithContext, ...prev];
next.length = Math.min(next.length, MAX_PAGES_IN_HISTORY);
return next;
});
const rerender = useRerender();
const [errorBoundaryKey, setErrorBoundaryKey] = useState<number>(0);
const [allowRoutingCalls, setAllowRoutingCalls] = useState(true);
function resetErrorBoundary(): void {
2022-01-16 00:13:35 +01:00
setErrorBoundaryKey(errorBoundaryKey + 1);
}
2021-09-18 01:43:08 +02:00
2021-09-19 06:46:39 +02:00
useEffect(() => {
return ITutorialEvents.subscribe(rerender);
}, [rerender]);
2021-09-19 06:46:39 +02:00
2021-10-26 21:50:55 +02:00
function killAllScripts(): void {
for (const server of GetAllServers()) {
server.runningScriptMap.clear();
2021-10-26 21:50:55 +02:00
}
saveObject.saveGame();
setTimeout(() => htmlLocation.reload(), 2000);
}
function attemptedForbiddenRouting(name: string) {
console.error(`Routing is currently disabled - Attempted router.${name}()`);
}
2021-09-18 01:43:08 +02:00
Router = {
isInitialized: true,
page: () => pageWithContext.page,
allowRouting: (value: boolean) => setAllowRoutingCalls(value),
toPage: (page: Page, context?: PageContext<ComplexPage>) => {
if (!allowRoutingCalls) return attemptedForbiddenRouting("toPage");
2022-12-04 09:14:06 +01:00
switch (page) {
case Page.Travel:
Player.gotoLocation(LocationName.TravelAgency);
break;
case Page.BitVerse:
calculateAchievements();
break;
2022-12-04 09:14:06 +01:00
}
setNextPage({ page, ...context } as PageWithContext);
2022-12-04 09:14:06 +01:00
},
back: () => {
if (!allowRoutingCalls) return attemptedForbiddenRouting("back");
setPages((pages) => pages.slice(1));
},
2021-09-17 08:04:44 +02:00
};
2021-09-17 08:58:02 +02:00
useEffect(() => {
if (pageWithContext.page !== Page.Terminal) window.scrollTo(0, 0);
2021-09-17 08:58:02 +02:00
});
function softReset(): void {
dialogBoxCreate("Soft Reset!");
installAugmentations(true);
resetErrorBoundary();
2022-12-04 09:14:06 +01:00
Router.toPage(Page.Terminal);
}
let mainPage = <Typography>Cannot load</Typography>;
let withSidebar = true;
let withPopups = true;
let bypassGame = false;
switch (pageWithContext.page) {
case Page.Recovery: {
2022-09-06 15:07:12 +02:00
mainPage = <RecoveryRoot softReset={softReset} />;
withSidebar = false;
withPopups = false;
bypassGame = true;
break;
}
case Page.BitVerse: {
mainPage = <BitverseRoot flume={pageWithContext.flume} quick={pageWithContext.quick} />;
withSidebar = false;
withPopups = false;
break;
}
case Page.Infiltration: {
mainPage = <InfiltrationRoot location={pageWithContext.location} />;
withSidebar = false;
withPopups = false;
break;
}
case Page.BladeburnerCinematic: {
mainPage = <BladeburnerCinematic />;
withSidebar = false;
withPopups = false;
break;
}
case Page.Work: {
mainPage = <WorkInProgressRoot />;
withSidebar = false;
break;
}
case Page.Terminal: {
2022-09-06 15:07:12 +02:00
mainPage = <TerminalRoot />;
break;
}
case Page.Sleeves: {
mainPage = <SleeveRoot />;
break;
}
case Page.StaneksGift: {
mainPage = <StaneksGiftRoot staneksGift={staneksGift} />;
break;
}
case Page.Stats: {
mainPage = <CharacterStats />;
break;
}
case Page.ScriptEditor: {
mainPage = (
<ScriptEditorRoot
files={pageWithContext.files ?? new Map()}
hostname={Player.getCurrentServer().hostname}
vim={!!pageWithContext.options?.vim}
/>
);
break;
}
case Page.ActiveScripts: {
mainPage = <ActiveScriptsRoot workerScripts={workerScripts} />;
break;
}
case Page.Hacknet: {
2022-09-06 15:07:12 +02:00
mainPage = <HacknetRoot />;
break;
}
case Page.CreateProgram: {
mainPage = <ProgramsRoot />;
break;
}
case Page.Factions: {
2022-09-06 15:07:12 +02:00
mainPage = <FactionsRoot />;
break;
}
case Page.Faction: {
mainPage = <FactionRoot faction={pageWithContext.faction} />;
break;
}
case Page.FactionAugmentations: {
mainPage = <FactionAugmentations faction={pageWithContext.faction} />;
break;
}
case Page.Milestones: {
2022-09-06 15:07:12 +02:00
mainPage = <MilestonesRoot />;
break;
}
2023-07-12 23:10:52 +02:00
case Page.Documentation: {
mainPage = <DocumentationRoot />;
break;
}
case Page.DevMenu: {
2022-09-06 15:07:12 +02:00
mainPage = <DevMenuRoot />;
break;
}
case Page.Gang: {
mainPage = <GangRoot />;
break;
}
case Page.Corporation: {
mainPage = <CorporationRoot />;
break;
}
case Page.Bladeburner: {
mainPage = <BladeburnerRoot />;
break;
}
2022-03-19 05:22:21 +01:00
case Page.Grafting: {
mainPage = <GraftingRoot />;
break;
}
case Page.Travel: {
2022-09-06 15:07:12 +02:00
mainPage = <TravelAgencyRoot />;
break;
}
case Page.StockMarket: {
2022-09-06 15:07:12 +02:00
mainPage = <StockMarketRoot stockMarket={StockMarket} />;
break;
}
case Page.City: {
mainPage = <LocationCity />;
break;
}
case Page.Job:
case Page.Location: {
mainPage = <GenericLocation loc={pageWithContext.location} />;
break;
}
case Page.Options: {
mainPage = (
<GameOptionsRoot
save={() => saveObject.saveGame()}
export={() => {
// Apply the export bonus before saving the game
2022-09-06 15:07:12 +02:00
onExport();
saveObject.exportGame();
}}
forceKill={killAllScripts}
softReset={softReset}
2023-07-12 23:10:52 +02:00
reactivateTutorial={() => {
prestigeAugmentation();
Router.toPage(Page.Terminal);
iTutorialStart();
}}
/>
);
break;
}
case Page.Augmentations: {
mainPage = (
<AugmentationsRoot
exportGameFn={() => {
// Apply the export bonus before saving the game
2022-09-06 15:07:12 +02:00
onExport();
saveObject.exportGame();
}}
installAugmentationsFn={() => {
installAugmentations();
}}
/>
);
break;
}
case Page.Go: {
mainPage = <GoRoot />;
break;
}
case Page.Achievements: {
mainPage = <AchievementsRoot />;
break;
}
case Page.ThemeBrowser: {
2022-09-06 15:07:12 +02:00
mainPage = <ThemeBrowser />;
break;
}
case Page.ImportSave: {
mainPage = <ImportSave importString={pageWithContext.base64Save} automatic={!!pageWithContext.automatic} />;
withSidebar = false;
withPopups = false;
bypassGame = true;
}
}
2021-09-17 08:04:44 +02:00
return (
<MathJaxContext version={3} src={"dist/ext/MathJax-3.2.2/es5/tex-chtml.js"}>
2022-09-13 00:05:48 +02:00
<ErrorBoundary key={errorBoundaryKey} softReset={softReset}>
<BypassWrapper content={bypassGame ? mainPage : null}>
2023-07-12 23:10:52 +02:00
<HistoryProvider>
<SnackbarProvider>
<Overview mode={ITutorial.isRunning ? "tutorial" : "overview"}>
{(parentOpen) =>
!ITutorial.isRunning ? (
<CharacterOverview
parentOpen={parentOpen}
save={() => saveObject.saveGame()}
killScripts={killAllScripts}
/>
) : (
<InteractiveTutorialRoot />
)
}
</Overview>
{withSidebar ? (
<Box display="flex" flexDirection="row" width="100%">
<SidebarRoot page={pageWithContext.page} />
<Box className={classes.root}>{mainPage}</Box>
</Box>
) : (
2022-09-13 00:05:48 +02:00
<Box className={classes.root}>{mainPage}</Box>
2023-07-12 23:10:52 +02:00
)}
<Unclickable />
<LogBoxManager hidden={!withPopups} />
<AlertManager hidden={!withPopups} />
<PromptManager hidden={!withPopups} />
<InvitationModal hidden={!withPopups} />
<Snackbar hidden={!withPopups} />
2023-07-12 23:10:52 +02:00
<Apr1 />
</SnackbarProvider>
</HistoryProvider>
2022-09-13 00:05:48 +02:00
</BypassWrapper>
</ErrorBoundary>
<V2Modal />
</MathJaxContext>
2021-09-17 08:04:44 +02:00
);
}