Internal refactoring of Router (#241)

This commit is contained in:
David Walker 2022-12-04 00:14:06 -08:00 committed by GitHub
parent 897a1fbc8e
commit 8d793ea271
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 292 additions and 383 deletions

@ -21,6 +21,7 @@ import {
initUnstableCircadianModulator, initUnstableCircadianModulator,
} from "./data/AugmentationCreator"; } from "./data/AugmentationCreator";
import { Router } from "../ui/GameRoot"; import { Router } from "../ui/GameRoot";
import { Page } from "../ui/Router";
import { mergeMultipliers } from "../PersonObjects/Multipliers"; import { mergeMultipliers } from "../PersonObjects/Multipliers";
export function AddToStaticAugmentations(aug: Augmentation): void { export function AddToStaticAugmentations(aug: Augmentation): void {
@ -138,7 +139,7 @@ function installAugmentations(force?: boolean): boolean {
); );
} }
prestigeAugmentation(); prestigeAugmentation();
Router.toTerminal(); Router.toPage(Page.Terminal);
return true; return true;
} }

@ -1,6 +1,7 @@
import React from "react"; import React from "react";
import { FactionNames } from "../../Faction/data/FactionNames"; import { FactionNames } from "../../Faction/data/FactionNames";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { CinematicText } from "../../ui/React/CinematicText"; import { CinematicText } from "../../ui/React/CinematicText";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../ui/React/DialogBox";
@ -31,7 +32,7 @@ export function BladeburnerCinematic(): React.ReactElement {
"investigating and dealing with Synthoid threats.", "investigating and dealing with Synthoid threats.",
]} ]}
onDone={() => { onDone={() => {
Router.toTerminal(); Router.toPage(Page.Terminal);
dialogBoxCreate( dialogBoxCreate(
`Visit the National Security Agency (NSA) to apply for their ${FactionNames.Bladeburners} ` + `Visit the National Security Agency (NSA) to apply for their ${FactionNames.Bladeburners} ` +
"division! You will need 100 of each combat stat before doing this.", "division! You will need 100 of each combat stat before doing this.",

@ -3,6 +3,7 @@ import React, { useState } from "react";
import { Money } from "../../../ui/React/Money"; import { Money } from "../../../ui/React/Money";
import { Modal } from "../../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { Router } from "../../../ui/GameRoot"; import { Router } from "../../../ui/GameRoot";
import { Page } from "../../../ui/Router";
import { Player } from "@player"; import { Player } from "@player";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
@ -33,7 +34,7 @@ export function CreateCorporationModal(props: IProps): React.ReactElement {
Player.loseMoney(150e9, "corporation"); Player.loseMoney(150e9, "corporation");
props.onClose(); props.onClose();
Router.toCorporation(); Router.toPage(Page.Corporation);
} }
function seed(): void { function seed(): void {
@ -44,7 +45,7 @@ export function CreateCorporationModal(props: IProps): React.ReactElement {
Player.startCorporation(name, 500e6); Player.startCorporation(name, 500e6);
props.onClose(); props.onClose();
Router.toCorporation(); Router.toPage(Page.Corporation);
} }
return ( return (

@ -1,6 +1,7 @@
import React from "react"; import React from "react";
import { FactionNames } from "./data/FactionNames"; import { FactionNames } from "./data/FactionNames";
import { Router } from "../ui/GameRoot"; import { Router } from "../ui/GameRoot";
import { Page } from "../ui/Router";
import { Option } from "./ui/Option"; import { Option } from "./ui/Option";
import { Typography } from "@mui/material"; import { Typography } from "@mui/material";
@ -432,7 +433,7 @@ export const FactionInfos: Record<string, FactionInfo> = {
<Option <Option
buttonText={"Open Bladeburner headquarters"} buttonText={"Open Bladeburner headquarters"}
infoText={"You can gain reputation with bladeburner by completing contracts and operations."} infoText={"You can gain reputation with bladeburner by completing contracts and operations."}
onClick={() => Router.toBladeburner()} onClick={() => Router.toPage(Page.Bladeburner)}
/> />
); );
}, },
@ -484,7 +485,7 @@ export const FactionInfos: Record<string, FactionInfo> = {
"Stanek's Gift is a powerful augmentation that powers up the stat you chose to boost." + "Stanek's Gift is a powerful augmentation that powers up the stat you chose to boost." +
"Gaining reputation with the Church of the Machine God can only be done by charging the gift." "Gaining reputation with the Church of the Machine God can only be done by charging the gift."
} }
onClick={() => Router.toStaneksGift()} onClick={() => Router.toPage(Page.StaneksGift)}
/> />
); );
}, },

@ -1,6 +1,7 @@
import React from "react"; import React from "react";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../ui/React/Modal";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { Player } from "@player"; import { Player } from "@player";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
@ -32,7 +33,7 @@ export function CreateGangModal(props: IProps): React.ReactElement {
function createGang(): void { function createGang(): void {
Player.startGang(props.facName, isHacking()); Player.startGang(props.facName, isHacking());
props.onClose(); props.onClose();
Router.toGang(); Router.toPage(Page.Gang);
} }
function onKeyUp(event: React.KeyboardEvent): void { function onKeyUp(event: React.KeyboardEvent): void {

@ -16,6 +16,7 @@ import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { Faction } from "../Faction"; import { Faction } from "../Faction";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { Player } from "@player"; import { Player } from "@player";
import { Typography, Button } from "@mui/material"; import { Typography, Button } from "@mui/material";
@ -64,7 +65,7 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
function startWork(): void { function startWork(): void {
Player.startFocusing(); Player.startFocusing();
Router.toWork(); Router.toPage(Page.Work);
} }
function startFieldWork(faction: Faction): void { function startFieldWork(faction: Faction): void {
@ -113,7 +114,7 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
return ( return (
<> <>
<Button onClick={() => Router.toFactions()}>Back</Button> <Button onClick={() => Router.toPage(Page.Factions)}>Back</Button>
<Typography variant="h4" color="primary"> <Typography variant="h4" color="primary">
{faction.name} {faction.name}
</Typography> </Typography>
@ -171,7 +172,7 @@ export function FactionRoot(props: IProps): React.ReactElement {
<Typography variant="h4" color="primary"> <Typography variant="h4" color="primary">
You have not joined {faction.name} yet! You have not joined {faction.name} yet!
</Typography> </Typography>
<Button onClick={() => Router.toFactions()}>Back to Factions</Button> <Button onClick={() => Router.toPage(Page.Factions)}>Back to Factions</Button>
</> </>
); );
} }

@ -2,6 +2,7 @@ import { Button, Typography, Box, Paper, Tooltip } from "@mui/material";
import React, { useState } from "react"; import React, { useState } from "react";
import { GangConstants } from "../../Gang/data/Constants"; import { GangConstants } from "../../Gang/data/Constants";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { Player } from "@player"; import { Player } from "@player";
import { Faction } from "../Faction"; import { Faction } from "../Faction";
import { CreateGangModal } from "./CreateGangModal"; import { CreateGangModal } from "./CreateGangModal";
@ -51,7 +52,7 @@ export function GangButton({ faction }: IProps): React.ReactElement {
const manageGang = (): void => { const manageGang = (): void => {
// If player already has a gang, just go to the gang UI // If player already has a gang, just go to the gang UI
if (Player.inGang()) { if (Player.inGang()) {
return Router.toGang(); return Router.toPage(Page.Gang);
} }
setGangOpen(true); setGangOpen(true);

@ -22,6 +22,7 @@ import { DeleteGameButton } from "../../ui/React/DeleteGameButton";
import { SnackbarEvents, ToastVariant } from "../../ui/React/Snackbar"; import { SnackbarEvents, ToastVariant } from "../../ui/React/Snackbar";
import { SoftResetButton } from "../../ui/React/SoftResetButton"; import { SoftResetButton } from "../../ui/React/SoftResetButton";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions"; import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { GameOptionsTab } from "../GameOptionsTab"; import { GameOptionsTab } from "../GameOptionsTab";
@ -216,7 +217,7 @@ export const GameOptionsSidebar = (props: IProps): React.ReactElement => {
</Button> </Button>
</Tooltip> </Tooltip>
<Tooltip title="Head to the theme browser to see a collection of prebuilt themes."> <Tooltip title="Head to the theme browser to see a collection of prebuilt themes.">
<Button startIcon={<Palette />} onClick={() => Router.toThemeBrowser()} sx={{ gridArea: "browse" }}> <Button startIcon={<Palette />} onClick={() => Router.toPage(Page.ThemeBrowser)} sx={{ gridArea: "browse" }}>
Theme Browser Theme Browser
</Button> </Button>
</Tooltip> </Tooltip>

@ -2,6 +2,7 @@ import { Button, Container, Paper, Typography } from "@mui/material";
import React, { useState } from "react"; import React, { useState } from "react";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { Player } from "@player"; import { Player } from "@player";
import { BackwardGame } from "./BackwardGame"; import { BackwardGame } from "./BackwardGame";
import { BracketGame } from "./BracketGame"; import { BracketGame } from "./BracketGame";
@ -93,14 +94,14 @@ export function Game(props: IProps): React.ReactElement {
? Player.hp.current ? Player.hp.current
: props.StartingDifficulty * 3 * (Player.hasAugmentation(AugmentationNames.WKSharmonizer, true) ? 0.5 : 1); : props.StartingDifficulty * 3 * (Player.hasAugmentation(AugmentationNames.WKSharmonizer, true) ? 0.5 : 1);
if (Player.takeDamage(damage)) { if (Player.takeDamage(damage)) {
Router.toCity(); Router.toPage(Page.City);
return; return;
} }
setupNextGame(); setupNextGame();
} }
function cancel(): void { function cancel(): void {
Router.toCity(); Router.toPage(Page.City);
return; return;
} }

@ -1,6 +1,7 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Location } from "../../Locations/Location"; import { Location } from "../../Locations/Location";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { calculateDifficulty, calculateReward } from "../formulas/game"; import { calculateDifficulty, calculateReward } from "../formulas/game";
import { Game } from "./Game"; import { Game } from "./Game";
import { Intro } from "./Intro"; import { Intro } from "./Intro";
@ -17,7 +18,7 @@ export function InfiltrationRoot(props: IProps): React.ReactElement {
const reward = calculateReward(startingSecurityLevel); const reward = calculateReward(startingSecurityLevel);
function cancel(): void { function cancel(): void {
Router.toCity(); Router.toPage(Page.City);
} }
return ( return (

@ -4,6 +4,7 @@ import { FactionNames } from "../../Faction/data/FactionNames";
import { inviteToFaction } from "../../Faction/FactionHelpers"; import { inviteToFaction } from "../../Faction/FactionHelpers";
import { Factions } from "../../Faction/Factions"; import { Factions } from "../../Faction/Factions";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { Player } from "@player"; import { Player } from "@player";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { Reputation } from "../../ui/React/Reputation"; import { Reputation } from "../../ui/React/Reputation";
@ -26,7 +27,7 @@ export function Victory(props: IProps): React.ReactElement {
function quitInfiltration(): void { function quitInfiltration(): void {
handleInfiltrators(); handleInfiltrators();
Router.toCity(); Router.toPage(Page.City);
} }
const soa = Factions[FactionNames.ShadowsOfAnarchy]; const soa = Factions[FactionNames.ShadowsOfAnarchy];

@ -14,6 +14,7 @@ import { Settings } from "../../Settings/Settings";
import { Player } from "@player"; import { Player } from "@player";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { LocationType } from "../LocationTypeEnum"; import { LocationType } from "../LocationTypeEnum";
@ -39,9 +40,9 @@ const useStyles = makeStyles((theme: Theme) =>
function toLocation(location: Location): void { function toLocation(location: Location): void {
if (location.name === LocationName.TravelAgency) { if (location.name === LocationName.TravelAgency) {
Router.toTravel(); Router.toPage(Page.Travel);
} else if (location.name === LocationName.WorldStockExchange) { } else if (location.name === LocationName.WorldStockExchange) {
Router.toStockMarket(); Router.toPage(Page.StockMarket);
} else { } else {
Router.toLocation(location); Router.toLocation(location);
} }

@ -21,6 +21,7 @@ import * as posNames from "../../Company/data/companypositionnames";
import { Reputation } from "../../ui/React/Reputation"; import { Reputation } from "../../ui/React/Reputation";
import { Favor } from "../../ui/React/Favor"; import { Favor } from "../../ui/React/Favor";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { Player } from "@player"; import { Player } from "@player";
import { QuitJobModal } from "../../Company/ui/QuitJobModal"; import { QuitJobModal } from "../../Company/ui/QuitJobModal";
import { CompanyWork } from "../../Work/CompanyWork"; import { CompanyWork } from "../../Work/CompanyWork";
@ -177,7 +178,7 @@ export function CompanyLocation(props: IProps): React.ReactElement {
}), }),
); );
Player.startFocusing(); Player.startFocusing();
Router.toWork(); Router.toPage(Page.Work);
} }
} }

@ -28,6 +28,7 @@ import { GetServer } from "../../Server/AllServers";
import { CorruptableText } from "../../ui/React/CorruptableText"; import { CorruptableText } from "../../ui/React/CorruptableText";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { serverMetadata } from "../../Server/data/servers"; import { serverMetadata } from "../../Server/data/servers";
import { Tooltip } from "@mui/material"; import { Tooltip } from "@mui/material";
@ -90,7 +91,7 @@ export function GenericLocation({ loc }: IProps): React.ReactElement {
return ( return (
<> <>
<Button onClick={() => Router.toCity()}>Return to World</Button> <Button onClick={() => Router.toPage(Page.City)}>Return to World</Button>
<Typography variant="h4" sx={{ mt: 1 }}> <Typography variant="h4" sx={{ mt: 1 }}>
{backdoorInstalled && !Settings.DisableTextEffects ? ( {backdoorInstalled && !Settings.DisableTextEffects ? (
<Tooltip title={`Backdoor installed on ${loc.name}.`}> <Tooltip title={`Backdoor installed on ${loc.name}.`}>

@ -12,6 +12,7 @@ import { Player } from "@player";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { Box } from "@mui/material"; import { Box } from "@mui/material";
import { ClassWork, Classes } from "../../Work/ClassWork"; import { ClassWork, Classes } from "../../Work/ClassWork";
import { calculateCost } from "../../Work/Formulas"; import { calculateCost } from "../../Work/Formulas";
@ -31,7 +32,7 @@ export function GymLocation(props: IProps): React.ReactElement {
}), }),
); );
Player.startFocusing(); Player.startFocusing();
Router.toWork(); Router.toPage(Page.Work);
} }
const cost = calculateCost(Classes[GymType.strength], props.loc); const cost = calculateCost(Classes[GymType.strength], props.loc);

@ -11,6 +11,7 @@ import { Crimes } from "../../Crime/Crimes";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { Player } from "@player"; import { Player } from "@player";
import { Box } from "@mui/material"; import { Box } from "@mui/material";
import { Crime } from "../../Crime/Crime"; import { Crime } from "../../Crime/Crime";
@ -27,7 +28,7 @@ export function SlumsLocation(): React.ReactElement {
function doCrime(e: React.MouseEvent<HTMLElement>, crime: Crime) { function doCrime(e: React.MouseEvent<HTMLElement>, crime: Crime) {
if (!e.isTrusted) return; if (!e.isTrusted) return;
crime.commit(); crime.commit();
Router.toWork(); Router.toPage(Page.Work);
Player.focus = true; Player.focus = true;
} }

@ -22,6 +22,7 @@ import { Factions } from "../../Faction/Factions";
import { joinFaction } from "../../Faction/FactionHelpers"; import { joinFaction } from "../../Faction/FactionHelpers";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { Player } from "@player"; import { Player } from "@player";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../ui/React/DialogBox";
@ -48,7 +49,7 @@ export function SpecialLocation(props: IProps): React.ReactElement {
function handleBladeburner(): void { function handleBladeburner(): void {
if (Player.bladeburner) { if (Player.bladeburner) {
// Enter Bladeburner division // Enter Bladeburner division
Router.toBladeburner(); Router.toPage(Page.Bladeburner);
} else if ( } else if (
Player.skills.strength >= 100 && Player.skills.strength >= 100 &&
Player.skills.defense >= 100 && Player.skills.defense >= 100 &&
@ -72,7 +73,7 @@ export function SpecialLocation(props: IProps): React.ReactElement {
/** Click handler for Resleeving button at New Tokyo VitaLife */ /** Click handler for Resleeving button at New Tokyo VitaLife */
function handleGrafting(): void { function handleGrafting(): void {
Router.toGrafting(); Router.toPage(Page.Grafting);
} }
function renderBladeburner(): React.ReactElement { function renderBladeburner(): React.ReactElement {
@ -173,11 +174,11 @@ export function SpecialLocation(props: IProps): React.ReactElement {
applyAugmentation({ name: AugmentationNames.StaneksGift1, level: 1 }); applyAugmentation({ name: AugmentationNames.StaneksGift1, level: 1 });
} }
Router.toStaneksGift(); Router.toPage(Page.StaneksGift);
} }
function renderCotMG(): React.ReactElement { function renderCotMG(): React.ReactElement {
const toStanek = <Button onClick={() => Router.toStaneksGift()}>Open Stanek's Gift</Button>; const toStanek = <Button onClick={() => Router.toPage(Page.StaneksGift)}>Open Stanek's Gift</Button>;
// prettier-ignore // prettier-ignore
const symbol = <Typography sx={{ lineHeight: '1em', whiteSpace: 'pre' }}> const symbol = <Typography sx={{ lineHeight: '1em', whiteSpace: 'pre' }}>
{" `` "}<br /> {" `` "}<br />

@ -11,6 +11,7 @@ import { TravelConfirmationModal } from "./TravelConfirmationModal";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { Player } from "@player"; import { Player } from "@player";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
@ -30,7 +31,7 @@ function travel(to: CityName): void {
Player.loseMoney(cost, "other"); Player.loseMoney(cost, "other");
Player.travel(to); Player.travel(to);
dialogBoxCreate(`You are now in ${to}!`); dialogBoxCreate(`You are now in ${to}!`);
Router.toCity(); Router.toPage(Page.City);
} }
export function TravelAgencyRoot(): React.ReactElement { export function TravelAgencyRoot(): React.ReactElement {

@ -11,6 +11,7 @@ import { Location } from "../Location";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { Player } from "@player"; import { Player } from "@player";
import { Box } from "@mui/material"; import { Box } from "@mui/material";
@ -32,7 +33,7 @@ export function UniversityLocation(props: IProps): React.ReactElement {
}), }),
); );
Player.startFocusing(); Player.startFocusing();
Router.toWork(); Router.toPage(Page.Work);
} }
const dataStructuresCost = calculateCost(Classes[UniversityClassType.dataStructures], props.loc); const dataStructuresCost = calculateCost(Classes[UniversityClassType.dataStructures], props.loc);

@ -7,6 +7,7 @@ import { getGraftingAvailableAugs, calculateGraftingTimeWithBonus } from "../Per
import { Player as player } from "../Player"; import { Player as player } from "../Player";
import { Grafting as IGrafting } from "../ScriptEditor/NetscriptDefinitions"; import { Grafting as IGrafting } from "../ScriptEditor/NetscriptDefinitions";
import { Router } from "../ui/GameRoot"; import { Router } from "../ui/GameRoot";
import { Page } from "../ui/Router";
import { GraftingWork } from "../Work/GraftingWork"; import { GraftingWork } from "../Work/GraftingWork";
import { helpers } from "../Netscript/NetscriptHelpers"; import { helpers } from "../Netscript/NetscriptHelpers";
@ -83,10 +84,10 @@ export function NetscriptGrafting(): InternalAPI<IGrafting> {
if (focus) { if (focus) {
player.startFocusing(); player.startFocusing();
Router.toWork(); Router.toPage(Page.Work);
} else if (wasFocusing) { } else if (wasFocusing) {
player.stopFocusing(); player.stopFocusing();
Router.toTerminal(); Router.toPage(Page.Terminal);
} }
helpers.log(ctx, () => `Began grafting Augmentation ${augName}.`); helpers.log(ctx, () => `Began grafting Augmentation ${augName}.`);

@ -240,9 +240,9 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
return false; return false;
} }
if (location.name === LocationName.TravelAgency) { if (location.name === LocationName.TravelAgency) {
Router.toTravel(); Router.toPage(Page.Travel);
} else if (location.name === LocationName.WorldStockExchange) { } else if (location.name === LocationName.WorldStockExchange) {
Router.toStockMarket(); Router.toPage(Page.StockMarket);
} else { } else {
Router.toLocation(location); Router.toLocation(location);
} }
@ -307,10 +307,10 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
); );
if (focus) { if (focus) {
Player.startFocusing(); Player.startFocusing();
Router.toWork(); Router.toPage(Page.Work);
} else if (wasFocusing) { } else if (wasFocusing) {
Player.stopFocusing(); Player.stopFocusing();
Router.toTerminal(); Router.toPage(Page.Terminal);
} }
helpers.log(ctx, () => `Started ${classType} at ${universityName}`); helpers.log(ctx, () => `Started ${classType} at ${universityName}`);
return true; return true;
@ -393,10 +393,10 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
Player.startWork(new ClassWork({ classType, location: Player.location, singularity: true })); Player.startWork(new ClassWork({ classType, location: Player.location, singularity: true }));
if (focus) { if (focus) {
Player.startFocusing(); Player.startFocusing();
Router.toWork(); Router.toPage(Page.Work);
} else if (wasFocusing) { } else if (wasFocusing) {
Player.stopFocusing(); Player.stopFocusing();
Router.toTerminal(); Router.toPage(Page.Terminal);
} }
helpers.log(ctx, () => `Started training ${classType} at ${gymName}`); helpers.log(ctx, () => `Started training ${classType} at ${gymName}`);
return true; return true;
@ -594,11 +594,11 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
if (!Player.focus && focus) { if (!Player.focus && focus) {
Player.startFocusing(); Player.startFocusing();
Router.toWork(); Router.toPage(Page.Work);
return true; return true;
} else if (Player.focus && !focus) { } else if (Player.focus && !focus) {
Player.stopFocusing(); Player.stopFocusing();
Router.toTerminal(); Router.toPage(Page.Terminal);
return true; return true;
} }
return false; return false;
@ -723,10 +723,10 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
); );
if (focus) { if (focus) {
Player.startFocusing(); Player.startFocusing();
Router.toWork(); Router.toPage(Page.Work);
} else if (wasFocused) { } else if (wasFocused) {
Player.stopFocusing(); Player.stopFocusing();
Router.toTerminal(); Router.toPage(Page.Terminal);
} }
helpers.log(ctx, () => `Began working at '${companyName}' with position '${companyPositionName}'`); helpers.log(ctx, () => `Began working at '${companyName}' with position '${companyPositionName}'`);
return true; return true;
@ -889,10 +889,10 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
); );
if (focus) { if (focus) {
Player.startFocusing(); Player.startFocusing();
Router.toWork(); Router.toPage(Page.Work);
} else if (wasFocusing) { } else if (wasFocusing) {
Player.stopFocusing(); Player.stopFocusing();
Router.toTerminal(); Router.toPage(Page.Terminal);
} }
helpers.log(ctx, () => `Started carrying out hacking contracts for '${faction.name}'`); helpers.log(ctx, () => `Started carrying out hacking contracts for '${faction.name}'`);
return true; return true;
@ -912,10 +912,10 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
); );
if (focus) { if (focus) {
Player.startFocusing(); Player.startFocusing();
Router.toWork(); Router.toPage(Page.Work);
} else if (wasFocusing) { } else if (wasFocusing) {
Player.stopFocusing(); Player.stopFocusing();
Router.toTerminal(); Router.toPage(Page.Terminal);
} }
helpers.log(ctx, () => `Started carrying out field missions for '${faction.name}'`); helpers.log(ctx, () => `Started carrying out field missions for '${faction.name}'`);
return true; return true;
@ -935,10 +935,10 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
); );
if (focus) { if (focus) {
Player.startFocusing(); Player.startFocusing();
Router.toWork(); Router.toPage(Page.Work);
} else if (wasFocusing) { } else if (wasFocusing) {
Player.stopFocusing(); Player.stopFocusing();
Router.toTerminal(); Router.toPage(Page.Terminal);
} }
helpers.log(ctx, () => `Started carrying out security work for '${faction.name}'`); helpers.log(ctx, () => `Started carrying out security work for '${faction.name}'`);
return true; return true;
@ -1054,10 +1054,10 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
); );
if (focus) { if (focus) {
Player.startFocusing(); Player.startFocusing();
Router.toWork(); Router.toPage(Page.Work);
} else if (wasFocusing) { } else if (wasFocusing) {
Player.stopFocusing(); Player.stopFocusing();
Router.toTerminal(); Router.toPage(Page.Terminal);
} }
helpers.log(ctx, () => `Began creating program: '${programName}'`); helpers.log(ctx, () => `Began creating program: '${programName}'`);
return true; return true;
@ -1079,10 +1079,10 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
const crimeTime = crime.commit(1, ctx.workerScript); const crimeTime = crime.commit(1, ctx.workerScript);
if (focus) { if (focus) {
Player.startFocusing(); Player.startFocusing();
Router.toWork(); Router.toPage(Page.Work);
} else if (wasFocusing) { } else if (wasFocusing) {
Player.stopFocusing(); Player.stopFocusing();
Router.toTerminal(); Router.toPage(Page.Terminal);
} }
return crimeTime; return crimeTime;
}, },

@ -12,6 +12,7 @@ import { Locations } from "../../../Locations/Locations";
import { PurchaseAugmentationsOrderSetting } from "../../../Settings/SettingEnums"; import { PurchaseAugmentationsOrderSetting } from "../../../Settings/SettingEnums";
import { Settings } from "../../../Settings/Settings"; import { Settings } from "../../../Settings/Settings";
import { Router } from "../../../ui/GameRoot"; import { Router } from "../../../ui/GameRoot";
import { Page } from "../../../ui/Router";
import { ConfirmationModal } from "../../../ui/React/ConfirmationModal"; import { ConfirmationModal } from "../../../ui/React/ConfirmationModal";
import { Money } from "../../../ui/React/Money"; import { Money } from "../../../ui/React/Money";
import { convertTimeMsToTimeElapsedString, formatNumber } from "../../../utils/StringHelperFunctions"; import { convertTimeMsToTimeElapsedString, formatNumber } from "../../../utils/StringHelperFunctions";
@ -158,7 +159,7 @@ export const GraftingRoot = (): React.ReactElement => {
}), }),
); );
Player.startFocusing(); Player.startFocusing();
Router.toWork(); Router.toPage(Page.Work);
}} }}
confirmationText={ confirmationText={
<> <>

@ -5,6 +5,7 @@ import { Box, Typography, Button, Container, Paper } from "@mui/material";
import { Check, Lock, Create } from "@mui/icons-material"; import { Check, Lock, Create } from "@mui/icons-material";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { Player } from "@player"; import { Player } from "@player";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
@ -99,7 +100,7 @@ export function ProgramsRoot(): React.ReactElement {
if (!event.isTrusted) return; if (!event.isTrusted) return;
Player.startWork(new CreateProgramWork({ singularity: false, programName: program.name })); Player.startWork(new CreateProgramWork({ singularity: false, programName: program.name }));
Player.startFocusing(); Player.startFocusing();
Router.toWork(); Router.toPage(Page.Work);
}} }}
> >
Create program Create program

@ -7,6 +7,7 @@ import { SourceFiles } from "./SourceFile/SourceFiles";
import { dialogBoxCreate } from "./ui/React/DialogBox"; import { dialogBoxCreate } from "./ui/React/DialogBox";
import { Router } from "./ui/GameRoot"; import { Router } from "./ui/GameRoot";
import { Page } from "./ui/Router";
function giveSourceFile(bitNodeNumber: number): void { function giveSourceFile(bitNodeNumber: number): void {
const sourceFileKey = "SourceFile" + bitNodeNumber.toString(); const sourceFileKey = "SourceFile" + bitNodeNumber.toString();
@ -68,9 +69,9 @@ export function enterBitNode(flume: boolean, destroyedBitNode: number, newBitNod
Player.bitNodeN = newBitNode; Player.bitNodeN = newBitNode;
if (newBitNode === 6) { if (newBitNode === 6) {
Router.toBladeburnerCinematic(); Router.toPage(Page.BladeburnerCinematic);
} else { } else {
Router.toTerminal(); Router.toPage(Page.Terminal);
} }
prestigeSourceFile(flume); prestigeSourceFile(flume);
} }

@ -9,6 +9,7 @@ import { Options } from "./Options";
import { isValidFilePath } from "../../Terminal/DirectoryHelpers"; import { isValidFilePath } from "../../Terminal/DirectoryHelpers";
import { Player } from "@player"; import { Player } from "@player";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { isScriptFilename } from "../../Script/isScriptFilename"; import { isScriptFilename } from "../../Script/isScriptFilename";
import { Script } from "../../Script/Script"; import { Script } from "../../Script/Script";
@ -154,7 +155,7 @@ export function Root(props: IProps): React.ReactElement {
//Ctrl + b //Ctrl + b
if (event.code == "KeyB" && (event.ctrlKey || event.metaKey)) { if (event.code == "KeyB" && (event.ctrlKey || event.metaKey)) {
event.preventDefault(); event.preventDefault();
Router.toTerminal(); Router.toPage(Page.Terminal);
} }
// CTRL/CMD + S // CTRL/CMD + S
@ -181,12 +182,12 @@ export function Root(props: IProps): React.ReactElement {
save(); save();
}); });
MonacoVim.VimMode.Vim.defineEx("quit", "q", function () { MonacoVim.VimMode.Vim.defineEx("quit", "q", function () {
Router.toTerminal(); Router.toPage(Page.Terminal);
}); });
const saveNQuit = (): void => { const saveNQuit = (): void => {
save(); save();
Router.toTerminal(); Router.toPage(Page.Terminal);
}; };
// "wqriteandquit" & "xriteandquit" are not typos, prefix must be found in full string // "wqriteandquit" & "xriteandquit" are not typos, prefix must be found in full string
MonacoVim.VimMode.Vim.defineEx("wqriteandquit", "wq", saveNQuit); MonacoVim.VimMode.Vim.defineEx("wqriteandquit", "wq", saveNQuit);
@ -465,7 +466,7 @@ export function Root(props: IProps): React.ReactElement {
if (scriptToSave.fileName == server.scripts[i].filename) { if (scriptToSave.fileName == server.scripts[i].filename) {
server.scripts[i].saveScript(scriptToSave.fileName, scriptToSave.code, Player.currentServer, server.scripts); server.scripts[i].saveScript(scriptToSave.fileName, scriptToSave.code, Player.currentServer, server.scripts);
if (Settings.SaveGameOnFileSave) saveObject.saveGame(); if (Settings.SaveGameOnFileSave) saveObject.saveGame();
Router.toTerminal(); Router.toPage(Page.Terminal);
return; return;
} }
} }
@ -479,7 +480,7 @@ export function Root(props: IProps): React.ReactElement {
if (server.textFiles[i].fn === scriptToSave.fileName) { if (server.textFiles[i].fn === scriptToSave.fileName) {
server.textFiles[i].write(scriptToSave.code); server.textFiles[i].write(scriptToSave.code);
if (Settings.SaveGameOnFileSave) saveObject.saveGame(); if (Settings.SaveGameOnFileSave) saveObject.saveGame();
Router.toTerminal(); Router.toPage(Page.Terminal);
return; return;
} }
} }
@ -491,7 +492,7 @@ export function Root(props: IProps): React.ReactElement {
} }
if (Settings.SaveGameOnFileSave) saveObject.saveGame(); if (Settings.SaveGameOnFileSave) saveObject.saveGame();
Router.toTerminal(); Router.toPage(Page.Terminal);
} }
function save(): void { function save(): void {
@ -646,7 +647,7 @@ export function Root(props: IProps): React.ReactElement {
openScripts.splice(index, 1); openScripts.splice(index, 1);
if (openScripts.length === 0) { if (openScripts.length === 0) {
currentScript = null; currentScript = null;
Router.toTerminal(); Router.toPage(Page.Terminal);
return; return;
} }
@ -900,7 +901,7 @@ export function Root(props: IProps): React.ReactElement {
{ram} {ram}
</Button> </Button>
<Button onClick={save}>Save (Ctrl/Cmd + s)</Button> <Button onClick={save}>Save (Ctrl/Cmd + s)</Button>
<Button sx={{ mx: 1 }} onClick={Router.toTerminal}> <Button sx={{ mx: 1 }} onClick={() => Router.toPage(Page.Terminal)}>
Terminal (Ctrl/Cmd + b) Terminal (Ctrl/Cmd + b)
</Button> </Button>
<Typography> <Typography>

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react"; import React, { useCallback, useState, useEffect } from "react";
import { KEYCODE } from "../../utils/helpers/keyCodes"; import { KEYCODE } from "../../utils/helpers/keyCodes";
import clsx from "clsx"; import clsx from "clsx";
import { styled, Theme, CSSObject } from "@mui/material/styles"; import { styled, Theme, CSSObject } from "@mui/material/styles";
@ -46,7 +46,7 @@ import ExpandLessIcon from "@mui/icons-material/ExpandLess";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router"; import { Page, SimplePage } from "../../ui/Router";
import { Player } from "@player"; import { Player } from "@player";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { iTutorialSteps, iTutorialNextStep, ITutorial } from "../../InteractiveTutorial"; import { iTutorialSteps, iTutorialNextStep, ITutorial } from "../../InteractiveTutorial";
@ -105,8 +105,6 @@ const useStyles = makeStyles((theme: Theme) =>
interface IProps { interface IProps {
page: Page; page: Page;
opened: boolean;
onToggled: (newValue: boolean) => void;
} }
export function SidebarRoot(props: IProps): React.ReactElement { export function SidebarRoot(props: IProps): React.ReactElement {
@ -125,19 +123,28 @@ export function SidebarRoot(props: IProps): React.ReactElement {
const [worldOpen, setWorldOpen] = useState(true); const [worldOpen, setWorldOpen] = useState(true);
const [helpOpen, setHelpOpen] = useState(true); const [helpOpen, setHelpOpen] = useState(true);
const flashTerminal = let flash: Page | null = null;
ITutorial.currStep === iTutorialSteps.CharacterGoToTerminalPage || switch (ITutorial.currStep) {
ITutorial.currStep === iTutorialSteps.ActiveScriptsPage; case iTutorialSteps.CharacterGoToTerminalPage:
case iTutorialSteps.ActiveScriptsPage:
const flashStats = ITutorial.currStep === iTutorialSteps.GoToCharacterPage; flash = Page.Terminal;
break;
const flashActiveScripts = ITutorial.currStep === iTutorialSteps.TerminalGoToActiveScriptsPage; case iTutorialSteps.GoToCharacterPage:
flash = Page.Stats;
const flashHacknet = ITutorial.currStep === iTutorialSteps.GoToHacknetNodesPage; break;
case iTutorialSteps.TerminalGoToActiveScriptsPage:
const flashCity = ITutorial.currStep === iTutorialSteps.HacknetNodesGoToWorldPage; flash = Page.ActiveScripts;
break;
const flashTutorial = ITutorial.currStep === iTutorialSteps.WorldDescription; case iTutorialSteps.GoToHacknetNodesPage:
flash = Page.Hacknet;
break;
case iTutorialSteps.HacknetNodesGoToWorldPage:
flash = Page.City;
break;
case iTutorialSteps.WorldDescription:
flash = Page.Tutorial;
break;
}
const augmentationCount = Player.queuedAugmentations.length; const augmentationCount = Player.queuedAugmentations.length;
const invitationsCount = Player.factionInvitations.filter((f) => !InvitationsSeen.includes(f)).length; const invitationsCount = Player.factionInvitations.filter((f) => !InvitationsSeen.includes(f)).length;
@ -162,99 +169,6 @@ export function SidebarRoot(props: IProps): React.ReactElement {
const canBladeburner = !!Player.bladeburner; const canBladeburner = !!Player.bladeburner;
const canStaneksGift = Player.augmentations.some((aug) => aug.name === AugmentationNames.StaneksGift1); const canStaneksGift = Player.augmentations.some((aug) => aug.name === AugmentationNames.StaneksGift1);
function clickTerminal(): void {
Router.toTerminal();
if (flashTerminal) iTutorialNextStep();
}
function clickScriptEditor(): void {
Router.toScriptEditor();
}
function clickStats(): void {
Router.toStats();
if (flashStats) iTutorialNextStep();
}
function clickActiveScripts(): void {
Router.toActiveScripts();
if (flashActiveScripts) iTutorialNextStep();
}
function clickCreateProgram(): void {
Router.toCreateProgram();
}
function clickStaneksGift(): void {
Router.toStaneksGift();
}
function clickFactions(): void {
Router.toFactions();
}
function clickAugmentations(): void {
Router.toAugmentations();
}
function clickSleeves(): void {
Router.toSleeves();
}
function clickHacknet(): void {
Router.toHacknetNodes();
if (flashHacknet) iTutorialNextStep();
}
function clickCity(): void {
Router.toCity();
if (flashCity) iTutorialNextStep();
}
function clickTravel(): void {
Router.toTravel();
}
function clickJob(): void {
Router.toJob(Locations[Object.keys(Player.jobs)[0]]);
}
function clickStockMarket(): void {
Router.toStockMarket();
}
function clickBladeburner(): void {
Router.toBladeburner();
}
function clickCorp(): void {
Router.toCorporation();
}
function clickGang(): void {
Router.toGang();
}
function clickTutorial(): void {
Router.toTutorial();
if (flashTutorial) iTutorialNextStep();
}
function clickMilestones(): void {
Router.toMilestones();
}
function clickOptions(): void {
Router.toGameOptions();
}
function clickDev(): void {
Router.toDevMenu();
}
function clickAchievements(): void {
Router.toAchievements();
}
useEffect(() => { useEffect(() => {
// Shortcuts to navigate through the game // Shortcuts to navigate through the game
// Alt-t - Terminal // Alt-t - Terminal
@ -277,53 +191,53 @@ export function SidebarRoot(props: IProps): React.ReactElement {
if ((Player.currentWork && Player.focus) || Router.page() === Page.BitVerse) return; if ((Player.currentWork && Player.focus) || Router.page() === Page.BitVerse) return;
if (event.code === KEYCODE.T && event.altKey) { if (event.code === KEYCODE.T && event.altKey) {
event.preventDefault(); event.preventDefault();
clickTerminal(); clickPage(Page.Terminal);
} else if (event.code === KEYCODE.C && event.altKey) { } else if (event.code === KEYCODE.C && event.altKey) {
event.preventDefault(); event.preventDefault();
clickStats(); clickPage(Page.Stats);
} else if (event.code === KEYCODE.E && event.altKey) { } else if (event.code === KEYCODE.E && event.altKey) {
event.preventDefault(); event.preventDefault();
clickScriptEditor(); clickPage(Page.ScriptEditor);
} else if (event.code === KEYCODE.S && event.altKey) { } else if (event.code === KEYCODE.S && event.altKey) {
event.preventDefault(); event.preventDefault();
clickActiveScripts(); clickPage(Page.ActiveScripts);
} else if (event.code === KEYCODE.H && event.altKey) { } else if (event.code === KEYCODE.H && event.altKey) {
event.preventDefault(); event.preventDefault();
clickHacknet(); clickPage(Page.Hacknet);
} else if (event.code === KEYCODE.W && event.altKey) { } else if (event.code === KEYCODE.W && event.altKey) {
event.preventDefault(); event.preventDefault();
clickCity(); clickPage(Page.City);
} else if (event.code === KEYCODE.J && event.altKey && !event.ctrlKey && !event.metaKey && canJob) { } else if (event.code === KEYCODE.J && event.altKey && !event.ctrlKey && !event.metaKey && canJob) {
// ctrl/cmd + alt + j is shortcut to open Chrome dev tools // ctrl/cmd + alt + j is shortcut to open Chrome dev tools
event.preventDefault(); event.preventDefault();
clickJob(); clickPage(Page.Job);
} else if (event.code === KEYCODE.R && event.altKey) { } else if (event.code === KEYCODE.R && event.altKey) {
event.preventDefault(); event.preventDefault();
clickTravel(); clickPage(Page.Travel);
} else if (event.code === KEYCODE.P && event.altKey) { } else if (event.code === KEYCODE.P && event.altKey) {
event.preventDefault(); event.preventDefault();
clickCreateProgram(); clickPage(Page.CreateProgram);
} else if (event.code === KEYCODE.F && event.altKey) { } else if (event.code === KEYCODE.F && event.altKey) {
if (props.page == Page.Terminal && Settings.EnableBashHotkeys) { if (props.page == Page.Terminal && Settings.EnableBashHotkeys) {
return; return;
} }
event.preventDefault(); event.preventDefault();
clickFactions(); clickPage(Page.Factions);
} else if (event.code === KEYCODE.A && event.altKey) { } else if (event.code === KEYCODE.A && event.altKey) {
event.preventDefault(); event.preventDefault();
clickAugmentations(); clickPage(Page.Augmentations);
} else if (event.code === KEYCODE.U && event.altKey) { } else if (event.code === KEYCODE.U && event.altKey) {
event.preventDefault(); event.preventDefault();
clickTutorial(); clickPage(Page.Tutorial);
} else if (event.code === KEYCODE.O && event.altKey) { } else if (event.code === KEYCODE.O && event.altKey) {
event.preventDefault(); event.preventDefault();
clickOptions(); clickPage(Page.Options);
} else if (event.code === KEYCODE.B && event.altKey && Player.bladeburner) { } else if (event.code === KEYCODE.B && event.altKey && Player.bladeburner) {
event.preventDefault(); event.preventDefault();
clickBladeburner(); clickPage(Page.Bladeburner);
} else if (event.code === KEYCODE.G && event.altKey && Player.gang) { } else if (event.code === KEYCODE.G && event.altKey && Player.gang) {
event.preventDefault(); event.preventDefault();
clickGang(); clickPage(Page.Gang);
} }
} }
@ -331,13 +245,31 @@ export function SidebarRoot(props: IProps): React.ReactElement {
return () => document.removeEventListener("keydown", handleShortcuts); return () => document.removeEventListener("keydown", handleShortcuts);
}, []); }, []);
const classes = useStyles(); const clickPage = useCallback(
const [open, setOpen] = useState(props.opened); (page: Page) => {
const toggleDrawer = (): void => setOpen((old) => !old); if (page === Page.Job) {
Router.toJob(Locations[Object.keys(Player.jobs)[0]]);
} else if (page == Page.ScriptEditor) {
Router.toScriptEditor();
} else if ((Object.values(SimplePage) as Page[]).includes(page)) {
Router.toPage(page as SimplePage);
} else {
throw new Error("Can't handle click on Page " + page);
}
if (flash === page) {
iTutorialNextStep();
}
},
[flash],
);
useEffect(() => { const classes = useStyles();
props.onToggled(open); const [open, setOpen] = useState(Settings.IsSidebarOpened);
}, [open]); const toggleDrawer = (): void =>
setOpen((old) => {
Settings.IsSidebarOpened = !old;
return !old;
});
return ( return (
<Drawer open={open} anchor="left" variant="permanent"> <Drawer open={open} anchor="left" variant="permanent">
@ -373,17 +305,19 @@ export function SidebarRoot(props: IProps): React.ReactElement {
className={clsx({ className={clsx({
[classes.active]: props.page === Page.Terminal, [classes.active]: props.page === Page.Terminal,
})} })}
onClick={clickTerminal} onClick={() => clickPage(Page.Terminal)}
> >
<ListItemIcon> <ListItemIcon>
<Tooltip title={!open ? "Terminal" : ""}> <Tooltip title={!open ? "Terminal" : ""}>
<LastPageIcon <LastPageIcon
color={flashTerminal ? "error" : props.page !== Page.Terminal ? "secondary" : "primary"} color={flash === Page.Terminal ? "error" : props.page !== Page.Terminal ? "secondary" : "primary"}
/> />
</Tooltip> </Tooltip>
</ListItemIcon> </ListItemIcon>
<ListItemText> <ListItemText>
<Typography color={flashTerminal ? "error" : props.page !== Page.Terminal ? "secondary" : "primary"}> <Typography
color={flash === Page.Terminal ? "error" : props.page !== Page.Terminal ? "secondary" : "primary"}
>
Terminal Terminal
</Typography> </Typography>
</ListItemText> </ListItemText>
@ -395,7 +329,7 @@ export function SidebarRoot(props: IProps): React.ReactElement {
className={clsx({ className={clsx({
[classes.active]: props.page === Page.ScriptEditor, [classes.active]: props.page === Page.ScriptEditor,
})} })}
onClick={clickScriptEditor} onClick={() => clickPage(Page.ScriptEditor)}
> >
<ListItemIcon> <ListItemIcon>
<Tooltip title={!open ? "Script Editor" : ""}> <Tooltip title={!open ? "Script Editor" : ""}>
@ -415,18 +349,26 @@ export function SidebarRoot(props: IProps): React.ReactElement {
className={clsx({ className={clsx({
[classes.active]: props.page === Page.ActiveScripts, [classes.active]: props.page === Page.ActiveScripts,
})} })}
onClick={clickActiveScripts} onClick={() => clickPage(Page.ActiveScripts)}
> >
<ListItemIcon> <ListItemIcon>
<Tooltip title={!open ? "Active Scripts" : ""}> <Tooltip title={!open ? "Active Scripts" : ""}>
<StorageIcon <StorageIcon
color={flashActiveScripts ? "error" : props.page !== Page.ActiveScripts ? "secondary" : "primary"} color={
flash === Page.ActiveScripts
? "error"
: props.page !== Page.ActiveScripts
? "secondary"
: "primary"
}
/> />
</Tooltip> </Tooltip>
</ListItemIcon> </ListItemIcon>
<ListItemText> <ListItemText>
<Typography <Typography
color={flashActiveScripts ? "error" : props.page !== Page.ActiveScripts ? "secondary" : "primary"} color={
flash === Page.ActiveScripts ? "error" : props.page !== Page.ActiveScripts ? "secondary" : "primary"
}
> >
Active Scripts Active Scripts
</Typography> </Typography>
@ -438,7 +380,7 @@ export function SidebarRoot(props: IProps): React.ReactElement {
className={clsx({ className={clsx({
[classes.active]: props.page === Page.CreateProgram, [classes.active]: props.page === Page.CreateProgram,
})} })}
onClick={clickCreateProgram} onClick={() => clickPage(Page.CreateProgram)}
> >
<ListItemIcon> <ListItemIcon>
<Badge badgeContent={programCount > 0 ? programCount : undefined} color="error"> <Badge badgeContent={programCount > 0 ? programCount : undefined} color="error">
@ -460,7 +402,7 @@ export function SidebarRoot(props: IProps): React.ReactElement {
className={clsx({ className={clsx({
[classes.active]: props.page === Page.StaneksGift, [classes.active]: props.page === Page.StaneksGift,
})} })}
onClick={clickStaneksGift} onClick={() => clickPage(Page.StaneksGift)}
> >
<ListItemIcon> <ListItemIcon>
<Tooltip title={!open ? "Stanek's Gift" : ""}> <Tooltip title={!open ? "Stanek's Gift" : ""}>
@ -494,15 +436,17 @@ export function SidebarRoot(props: IProps): React.ReactElement {
className={clsx({ className={clsx({
[classes.active]: props.page === Page.Stats, [classes.active]: props.page === Page.Stats,
})} })}
onClick={clickStats} onClick={() => clickPage(Page.Stats)}
> >
<ListItemIcon> <ListItemIcon>
<Tooltip title={!open ? "Stats" : ""}> <Tooltip title={!open ? "Stats" : ""}>
<EqualizerIcon color={flashStats ? "error" : props.page !== Page.Stats ? "secondary" : "primary"} /> <EqualizerIcon
color={flash === Page.Stats ? "error" : props.page !== Page.Stats ? "secondary" : "primary"}
/>
</Tooltip> </Tooltip>
</ListItemIcon> </ListItemIcon>
<ListItemText> <ListItemText>
<Typography color={flashStats ? "error" : props.page !== Page.Stats ? "secondary" : "primary"}> <Typography color={flash === Page.Stats ? "error" : props.page !== Page.Stats ? "secondary" : "primary"}>
Stats Stats
</Typography> </Typography>
</ListItemText> </ListItemText>
@ -515,7 +459,7 @@ export function SidebarRoot(props: IProps): React.ReactElement {
className={clsx({ className={clsx({
[classes.active]: [Page.Factions, Page.Faction].includes(props.page), [classes.active]: [Page.Factions, Page.Faction].includes(props.page),
})} })}
onClick={clickFactions} onClick={() => clickPage(Page.Factions)}
> >
<ListItemIcon> <ListItemIcon>
<Badge badgeContent={invitationsCount !== 0 ? invitationsCount : undefined} color="error"> <Badge badgeContent={invitationsCount !== 0 ? invitationsCount : undefined} color="error">
@ -541,7 +485,7 @@ export function SidebarRoot(props: IProps): React.ReactElement {
className={clsx({ className={clsx({
[classes.active]: props.page === Page.Augmentations, [classes.active]: props.page === Page.Augmentations,
})} })}
onClick={clickAugmentations} onClick={() => clickPage(Page.Augmentations)}
> >
<ListItemIcon> <ListItemIcon>
<Badge badgeContent={augmentationCount !== 0 ? augmentationCount : undefined} color="error"> <Badge badgeContent={augmentationCount !== 0 ? augmentationCount : undefined} color="error">
@ -566,17 +510,19 @@ export function SidebarRoot(props: IProps): React.ReactElement {
className={clsx({ className={clsx({
[classes.active]: props.page === Page.Hacknet, [classes.active]: props.page === Page.Hacknet,
})} })}
onClick={clickHacknet} onClick={() => clickPage(Page.Hacknet)}
> >
<ListItemIcon> <ListItemIcon>
<Tooltip title={!open ? "Hacknet" : ""}> <Tooltip title={!open ? "Hacknet" : ""}>
<AccountTreeIcon <AccountTreeIcon
color={flashHacknet ? "error" : props.page !== Page.Hacknet ? "secondary" : "primary"} color={flash === Page.Hacknet ? "error" : props.page !== Page.Hacknet ? "secondary" : "primary"}
/> />
</Tooltip> </Tooltip>
</ListItemIcon> </ListItemIcon>
<ListItemText> <ListItemText>
<Typography color={flashHacknet ? "error" : props.page !== Page.Hacknet ? "secondary" : "primary"}> <Typography
color={flash === Page.Hacknet ? "error" : props.page !== Page.Hacknet ? "secondary" : "primary"}
>
Hacknet Hacknet
</Typography> </Typography>
</ListItemText> </ListItemText>
@ -589,7 +535,7 @@ export function SidebarRoot(props: IProps): React.ReactElement {
className={clsx({ className={clsx({
[classes.active]: props.page === Page.Sleeves, [classes.active]: props.page === Page.Sleeves,
})} })}
onClick={clickSleeves} onClick={() => clickPage(Page.Sleeves)}
> >
<ListItemIcon> <ListItemIcon>
<Tooltip title={!open ? "Sleeves" : ""}> <Tooltip title={!open ? "Sleeves" : ""}>
@ -621,15 +567,17 @@ export function SidebarRoot(props: IProps): React.ReactElement {
[classes.active]: [classes.active]:
props.page === Page.City || props.page === Page.Grafting || props.page === Page.Location, props.page === Page.City || props.page === Page.Grafting || props.page === Page.Location,
})} })}
onClick={clickCity} onClick={() => clickPage(Page.City)}
> >
<ListItemIcon> <ListItemIcon>
<Tooltip title={!open ? "City" : ""}> <Tooltip title={!open ? "City" : ""}>
<LocationCityIcon color={flashCity ? "error" : props.page !== Page.City ? "secondary" : "primary"} /> <LocationCityIcon
color={flash === Page.City ? "error" : props.page !== Page.City ? "secondary" : "primary"}
/>
</Tooltip> </Tooltip>
</ListItemIcon> </ListItemIcon>
<ListItemText> <ListItemText>
<Typography color={flashCity ? "error" : props.page !== Page.City ? "secondary" : "primary"}> <Typography color={flash === Page.City ? "error" : props.page !== Page.City ? "secondary" : "primary"}>
City City
</Typography> </Typography>
</ListItemText> </ListItemText>
@ -640,7 +588,7 @@ export function SidebarRoot(props: IProps): React.ReactElement {
className={clsx({ className={clsx({
[classes.active]: props.page === Page.Travel, [classes.active]: props.page === Page.Travel,
})} })}
onClick={clickTravel} onClick={() => clickPage(Page.Travel)}
> >
<ListItemIcon> <ListItemIcon>
<Tooltip title={!open ? "Travel" : ""}> <Tooltip title={!open ? "Travel" : ""}>
@ -659,7 +607,7 @@ export function SidebarRoot(props: IProps): React.ReactElement {
className={clsx({ className={clsx({
[classes.active]: props.page === Page.Job, [classes.active]: props.page === Page.Job,
})} })}
onClick={clickJob} onClick={() => clickPage(Page.Job)}
> >
<ListItemIcon> <ListItemIcon>
<Tooltip title={!open ? "Job" : ""}> <Tooltip title={!open ? "Job" : ""}>
@ -679,7 +627,7 @@ export function SidebarRoot(props: IProps): React.ReactElement {
className={clsx({ className={clsx({
[classes.active]: props.page === Page.StockMarket, [classes.active]: props.page === Page.StockMarket,
})} })}
onClick={clickStockMarket} onClick={() => clickPage(Page.StockMarket)}
> >
<ListItemIcon> <ListItemIcon>
<Tooltip title={!open ? "Stock Market" : ""}> <Tooltip title={!open ? "Stock Market" : ""}>
@ -699,7 +647,7 @@ export function SidebarRoot(props: IProps): React.ReactElement {
className={clsx({ className={clsx({
[classes.active]: props.page === Page.Bladeburner, [classes.active]: props.page === Page.Bladeburner,
})} })}
onClick={clickBladeburner} onClick={() => clickPage(Page.Bladeburner)}
> >
<ListItemIcon> <ListItemIcon>
<Tooltip title={!open ? "Bladeburner" : ""}> <Tooltip title={!open ? "Bladeburner" : ""}>
@ -719,7 +667,7 @@ export function SidebarRoot(props: IProps): React.ReactElement {
className={clsx({ className={clsx({
[classes.active]: props.page === Page.Corporation, [classes.active]: props.page === Page.Corporation,
})} })}
onClick={clickCorp} onClick={() => clickPage(Page.Corporation)}
> >
<ListItemIcon> <ListItemIcon>
<Tooltip title={!open ? "Corp" : ""}> <Tooltip title={!open ? "Corp" : ""}>
@ -739,7 +687,7 @@ export function SidebarRoot(props: IProps): React.ReactElement {
className={clsx({ className={clsx({
[classes.active]: props.page === Page.Gang, [classes.active]: props.page === Page.Gang,
})} })}
onClick={clickGang} onClick={() => clickPage(Page.Gang)}
> >
<ListItemIcon> <ListItemIcon>
<Tooltip title={!open ? "Gang" : ""}> <Tooltip title={!open ? "Gang" : ""}>
@ -770,7 +718,7 @@ export function SidebarRoot(props: IProps): React.ReactElement {
className={clsx({ className={clsx({
[classes.active]: props.page === Page.Milestones, [classes.active]: props.page === Page.Milestones,
})} })}
onClick={clickMilestones} onClick={() => clickPage(Page.Milestones)}
> >
<ListItemIcon> <ListItemIcon>
<Tooltip title={!open ? "Milestones" : ""}> <Tooltip title={!open ? "Milestones" : ""}>
@ -787,15 +735,19 @@ export function SidebarRoot(props: IProps): React.ReactElement {
className={clsx({ className={clsx({
[classes.active]: props.page === Page.Tutorial, [classes.active]: props.page === Page.Tutorial,
})} })}
onClick={clickTutorial} onClick={() => clickPage(Page.Tutorial)}
> >
<ListItemIcon> <ListItemIcon>
<Tooltip title={!open ? "Tutorial" : ""}> <Tooltip title={!open ? "Tutorial" : ""}>
<HelpIcon color={flashTutorial ? "error" : props.page !== Page.Tutorial ? "secondary" : "primary"} /> <HelpIcon
color={flash === Page.Tutorial ? "error" : props.page !== Page.Tutorial ? "secondary" : "primary"}
/>
</Tooltip> </Tooltip>
</ListItemIcon> </ListItemIcon>
<ListItemText> <ListItemText>
<Typography color={flashTutorial ? "error" : props.page !== Page.Tutorial ? "secondary" : "primary"}> <Typography
color={flash === Page.Tutorial ? "error" : props.page !== Page.Tutorial ? "secondary" : "primary"}
>
Tutorial Tutorial
</Typography> </Typography>
</ListItemText> </ListItemText>
@ -806,7 +758,7 @@ export function SidebarRoot(props: IProps): React.ReactElement {
className={clsx({ className={clsx({
[classes.active]: props.page === Page.Achievements, [classes.active]: props.page === Page.Achievements,
})} })}
onClick={clickAchievements} onClick={() => clickPage(Page.Achievements)}
> >
<ListItemIcon> <ListItemIcon>
<Tooltip title={!open ? "Achievements" : ""}> <Tooltip title={!open ? "Achievements" : ""}>
@ -823,7 +775,7 @@ export function SidebarRoot(props: IProps): React.ReactElement {
className={clsx({ className={clsx({
[classes.active]: props.page === Page.Options, [classes.active]: props.page === Page.Options,
})} })}
onClick={clickOptions} onClick={() => clickPage(Page.Options)}
> >
<ListItemIcon> <ListItemIcon>
<Tooltip title={!open ? "Options" : ""}> <Tooltip title={!open ? "Options" : ""}>
@ -842,7 +794,7 @@ export function SidebarRoot(props: IProps): React.ReactElement {
className={clsx({ className={clsx({
[classes.active]: props.page === Page.DevMenu, [classes.active]: props.page === Page.DevMenu,
})} })}
onClick={clickDev} onClick={() => clickPage(Page.DevMenu)}
> >
<ListItemIcon> <ListItemIcon>
<Tooltip title={!open ? "Dev" : ""}> <Tooltip title={!open ? "Dev" : ""}>

@ -16,6 +16,7 @@ import { Settings } from "../../Settings/Settings";
import { defaultTheme } from "../Themes"; import { defaultTheme } from "../Themes";
import { UserInterfaceTheme } from "../../ScriptEditor/NetscriptDefinitions"; import { UserInterfaceTheme } from "../../ScriptEditor/NetscriptDefinitions";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { ThemeCollaborate } from "./ThemeCollaborate"; import { ThemeCollaborate } from "./ThemeCollaborate";
interface IProps { interface IProps {
@ -380,7 +381,7 @@ export function ThemeEditorModal(props: IProps): React.ReactElement {
</Button> </Button>
</Tooltip> </Tooltip>
<Tooltip title="Move over to the theme browser's page to use one of our predefined themes."> <Tooltip title="Move over to the theme browser's page to use one of our predefined themes.">
<Button startIcon={<PaletteSharpIcon />} onClick={() => Router.toThemeBrowser()}> <Button startIcon={<PaletteSharpIcon />} onClick={() => Router.toPage(Page.ThemeBrowser)}>
See more themes See more themes
</Button> </Button>
</Tooltip> </Tooltip>

@ -411,7 +411,7 @@ function warnAutosaveDisabled(): void {
// We don't want this warning to show up on certain pages. // We don't want this warning to show up on certain pages.
// When in recovery or importing we want to keep autosave disabled. // When in recovery or importing we want to keep autosave disabled.
const ignoredPages = [Page.Recovery, Page.ImportSave]; const ignoredPages = [Page.Recovery as Page, Page.ImportSave];
if (ignoredPages.includes(Router.page())) return; if (ignoredPages.includes(Router.page())) return;
const warningToast = ( const warningToast = (

@ -23,7 +23,7 @@ import createStyles from "@mui/styles/createStyles";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { Page, IRouter, ScriptEditorRouteOptions } from "./Router"; import { Page, SimplePage, IRouter, ScriptEditorRouteOptions } from "./Router";
import { Overview } from "./React/Overview"; import { Overview } from "./React/Overview";
import { SidebarRoot } from "../Sidebar/ui/SidebarRoot"; import { SidebarRoot } from "../Sidebar/ui/SidebarRoot";
import { AugmentationsRoot } from "../Augmentation/ui/AugmentationsRoot"; import { AugmentationsRoot } from "../Augmentation/ui/AugmentationsRoot";
@ -67,7 +67,6 @@ import { calculateAchievements } from "../Achievements/Achievements";
import { RecoveryMode, RecoveryRoot } from "./React/RecoveryRoot"; import { RecoveryMode, RecoveryRoot } from "./React/RecoveryRoot";
import { AchievementsRoot } from "../Achievements/AchievementsRoot"; import { AchievementsRoot } from "../Achievements/AchievementsRoot";
import { ErrorBoundary } from "./ErrorBoundary"; import { ErrorBoundary } from "./ErrorBoundary";
import { Settings } from "../Settings/Settings";
import { ThemeBrowser } from "../Themes/ui/ThemeBrowser"; import { ThemeBrowser } from "../Themes/ui/ThemeBrowser";
import { ImportSaveRoot } from "./React/ImportSaveRoot"; import { ImportSaveRoot } from "./React/ImportSaveRoot";
import { BypassWrapper } from "./React/BypassWrapper"; import { BypassWrapper } from "./React/BypassWrapper";
@ -105,36 +104,15 @@ export let Router: IRouter = {
throw new Error("Router called before initialization"); throw new Error("Router called before initialization");
}, },
allowRouting: uninitialized, allowRouting: uninitialized,
toActiveScripts: uninitialized, toPage: () => {
toAugmentations: uninitialized, throw new Error("Router called before initialization");
},
toBitVerse: uninitialized, toBitVerse: uninitialized,
toBladeburner: uninitialized,
toStats: uninitialized,
toCity: uninitialized,
toCorporation: uninitialized,
toCreateProgram: uninitialized,
toDevMenu: uninitialized,
toFaction: uninitialized, toFaction: uninitialized,
toFactions: uninitialized,
toGameOptions: uninitialized,
toGang: uninitialized,
toHacknetNodes: uninitialized,
toInfiltration: uninitialized, toInfiltration: uninitialized,
toJob: uninitialized, toJob: uninitialized,
toMilestones: uninitialized,
toGrafting: uninitialized,
toScriptEditor: uninitialized, toScriptEditor: uninitialized,
toSleeves: uninitialized,
toStockMarket: uninitialized,
toTerminal: uninitialized,
toTravel: uninitialized,
toTutorial: uninitialized,
toWork: uninitialized,
toBladeburnerCinematic: uninitialized,
toLocation: uninitialized, toLocation: uninitialized,
toStaneksGift: uninitialized,
toAchievements: uninitialized,
toThemeBrowser: uninitialized,
toImportSave: uninitialized, toImportSave: uninitialized,
}; };
@ -164,7 +142,6 @@ export function GameRoot(): React.ReactElement {
const [cinematicText, setCinematicText] = useState(""); const [cinematicText, setCinematicText] = useState("");
const [errorBoundaryKey, setErrorBoundaryKey] = useState<number>(0); const [errorBoundaryKey, setErrorBoundaryKey] = useState<number>(0);
const [sidebarOpened, setSideBarOpened] = useState(Settings.IsSidebarOpened);
const [importString, setImportString] = useState<string>(undefined as unknown as string); const [importString, setImportString] = useState<string>(undefined as unknown as string);
const [importAutomatic, setImportAutomatic] = useState<boolean>(false); const [importAutomatic, setImportAutomatic] = useState<boolean>(false);
@ -196,24 +173,23 @@ export function GameRoot(): React.ReactElement {
isInitialized: true, isInitialized: true,
page: () => page, page: () => page,
allowRouting: (value: boolean) => setAllowRoutingCalls(value), allowRouting: (value: boolean) => setAllowRoutingCalls(value),
toActiveScripts: () => setPage(Page.ActiveScripts), toPage: (page: SimplePage) => {
toAugmentations: () => setPage(Page.Augmentations), switch (page) {
toBladeburner: () => setPage(Page.Bladeburner), case Page.Travel:
toStats: () => setPage(Page.Stats), Player.gotoLocation(LocationName.TravelAgency);
toCorporation: () => setPage(Page.Corporation), break;
toCreateProgram: () => setPage(Page.CreateProgram), case Page.BladeburnerCinematic:
toDevMenu: () => setPage(Page.DevMenu), setPage(page);
setCinematicText(cinematicText);
return;
}
setPage(page);
},
toFaction: (faction: Faction, augPage = false) => { toFaction: (faction: Faction, augPage = false) => {
setAugPage(augPage); setAugPage(augPage);
setPage(Page.Faction); setPage(Page.Faction);
if (faction) setFaction(faction); if (faction) setFaction(faction);
}, },
toFactions: () => setPage(Page.Factions),
toGameOptions: () => setPage(Page.Options),
toGang: () => setPage(Page.Gang),
toHacknetNodes: () => setPage(Page.Hacknet),
toMilestones: () => setPage(Page.Milestones),
toGrafting: () => setPage(Page.Grafting),
toScriptEditor: (files: Record<string, string>, options?: ScriptEditorRouteOptions) => { toScriptEditor: (files: Record<string, string>, options?: ScriptEditorRouteOptions) => {
setEditorOptions({ setEditorOptions({
files, files,
@ -221,21 +197,10 @@ export function GameRoot(): React.ReactElement {
}); });
setPage(Page.ScriptEditor); setPage(Page.ScriptEditor);
}, },
toSleeves: () => setPage(Page.Sleeves),
toStockMarket: () => setPage(Page.StockMarket),
toTerminal: () => setPage(Page.Terminal),
toTutorial: () => setPage(Page.Tutorial),
toJob: (location: Location) => { toJob: (location: Location) => {
setLocation(location); setLocation(location);
setPage(Page.Job); setPage(Page.Job);
}, },
toCity: () => {
setPage(Page.City);
},
toTravel: () => {
Player.gotoLocation(LocationName.TravelAgency);
setPage(Page.Travel);
},
toBitVerse: (flume: boolean, quick: boolean) => { toBitVerse: (flume: boolean, quick: boolean) => {
setFlume(flume); setFlume(flume);
setQuick(quick); setQuick(quick);
@ -246,24 +211,10 @@ export function GameRoot(): React.ReactElement {
setLocation(location); setLocation(location);
setPage(Page.Infiltration); setPage(Page.Infiltration);
}, },
toWork: () => setPage(Page.Work),
toBladeburnerCinematic: () => {
setPage(Page.BladeburnerCinematic);
setCinematicText(cinematicText);
},
toLocation: (location: Location) => { toLocation: (location: Location) => {
setLocation(location); setLocation(location);
setPage(Page.Location); setPage(Page.Location);
}, },
toStaneksGift: () => {
setPage(Page.StaneksGift);
},
toAchievements: () => {
setPage(Page.Achievements);
},
toThemeBrowser: () => {
setPage(Page.ThemeBrowser);
},
toImportSave: (base64save: string, automatic = false) => { toImportSave: (base64save: string, automatic = false) => {
setImportString(base64save); setImportString(base64save);
setImportAutomatic(automatic); setImportAutomatic(automatic);
@ -298,7 +249,7 @@ export function GameRoot(): React.ReactElement {
dialogBoxCreate("Soft Reset!"); dialogBoxCreate("Soft Reset!");
installAugmentations(true); installAugmentations(true);
resetErrorBoundary(); resetErrorBoundary();
Router.toTerminal(); Router.toPage(Page.Terminal);
} }
let mainPage = <Typography>Cannot load</Typography>; let mainPage = <Typography>Cannot load</Typography>;
@ -385,7 +336,7 @@ export function GameRoot(): React.ReactElement {
<TutorialRoot <TutorialRoot
reactivateTutorial={() => { reactivateTutorial={() => {
prestigeAugmentation(); prestigeAugmentation();
Router.toTerminal(); Router.toPage(Page.Terminal);
iTutorialStart(); iTutorialStart();
}} }}
/> />
@ -489,14 +440,7 @@ export function GameRoot(): React.ReactElement {
</Overview> </Overview>
{withSidebar ? ( {withSidebar ? (
<Box display="flex" flexDirection="row" width="100%"> <Box display="flex" flexDirection="row" width="100%">
<SidebarRoot <SidebarRoot page={page} />
page={page}
opened={sidebarOpened}
onToggled={(isOpened) => {
setSideBarOpened(isOpened);
Settings.IsSidebarOpened = isOpened;
}}
/>
<Box className={classes.root}>{mainPage}</Box> <Box className={classes.root}>{mainPage}</Box>
</Box> </Box>
) : ( ) : (

@ -21,6 +21,7 @@ import ClearAllIcon from "@mui/icons-material/ClearAll";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { Router } from "../GameRoot"; import { Router } from "../GameRoot";
import { Page } from "../Router";
import { Player } from "@player"; import { Player } from "@player";
import { StatsProgressOverviewCell } from "./StatsProgressBar"; import { StatsProgressOverviewCell } from "./StatsProgressBar";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
@ -140,7 +141,7 @@ function WorkInProgressOverview({
function Work(): React.ReactElement { function Work(): React.ReactElement {
const onClickFocus = (): void => { const onClickFocus = (): void => {
Player.startFocusing(); Player.startFocusing();
Router.toWork(); Router.toPage(Page.Work);
}; };
if (Player.currentWork === null || Player.focus) return <></>; if (Player.currentWork === null || Player.focus) return <></>;

@ -32,6 +32,7 @@ import { numeralWrapper } from "../numeralFormat";
import { ConfirmationModal } from "./ConfirmationModal"; import { ConfirmationModal } from "./ConfirmationModal";
import { pushImportResult } from "../../Electron"; import { pushImportResult } from "../../Electron";
import { Router } from "../GameRoot"; import { Router } from "../GameRoot";
import { Page } from "../Router";
const useStyles = makeStyles((theme: Theme) => const useStyles = makeStyles((theme: Theme) =>
createStyles({ createStyles({
@ -136,7 +137,7 @@ export function ImportSaveRoot(props: IProps): JSX.Element {
}, []); }, []);
useEffect(() => { useEffect(() => {
if (headback) Router.toTerminal(); if (headback) Router.toPage(Page.Terminal);
}, [headback]); }, [headback]);
useEffect(() => { useEffect(() => {

@ -4,6 +4,7 @@ import { Typography, Link, Button, ButtonGroup, Tooltip, Box, Paper, TextField }
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { load } from "../../db"; import { load } from "../../db";
import { Router } from "../GameRoot"; import { Router } from "../GameRoot";
import { Page } from "../Router";
import { download } from "../../SaveObject"; import { download } from "../../SaveObject";
import { IErrorData, newIssueUrl } from "../../utils/ErrorHelper"; import { IErrorData, newIssueUrl } from "../../utils/ErrorHelper";
import { DeleteGameButton } from "./DeleteGameButton"; import { DeleteGameButton } from "./DeleteGameButton";
@ -28,7 +29,7 @@ export function RecoveryRoot({ softReset, errorData, resetError }: IProps): Reac
function recover(): void { function recover(): void {
if (resetError) resetError(); if (resetError) resetError();
RecoveryMode = false; RecoveryMode = false;
Router.toTerminal(); Router.toPage(Page.Terminal);
} }
Settings.AutosaveInterval = 0; Settings.AutosaveInterval = 0;

@ -3,87 +3,71 @@ import { Location } from "../Locations/Location";
/** /**
* The full-screen page the player is currently be on. * The full-screen page the player is currently be on.
* These pages are mutually exclusive. * These are "simple" pages that don't require any extra parameters to
* transition to. You can use setPage() with these.
*/ */
export enum Page { export enum SimplePage {
ActiveScripts, ActiveScripts = "Active Scripts",
Augmentations, Augmentations = "Augmentations",
BitVerse, Bladeburner = "Bladeburner",
Bladeburner, City = "City",
City, Corporation = "Corporation",
Corporation, CreateProgram = "Create Program",
CreateProgram, DevMenu = "Dev",
ScriptEditor, Factions = "Factions",
DevMenu, Gang = "Gang",
Faction, Hacknet = "Hacknet",
Factions, Milestones = "Milestones",
Gang, Options = "Options",
Hacknet, Grafting = "Grafting",
Infiltration, Sleeves = "Sleeves",
Job, Stats = "Stats",
Milestones, StockMarket = "Stock Market",
Options, Terminal = "Terminal",
Grafting, Travel = "Travel",
Sleeves, Tutorial = "Tutorial",
Stats, Work = "Work",
StockMarket, BladeburnerCinematic = "Bladeburner Cinematic",
Terminal, Loading = "Loading",
Travel, StaneksGift = "Staneks Gift",
Tutorial, Recovery = "Recovery",
Work, Achievements = "Achievements",
BladeburnerCinematic, ThemeBrowser = "Theme Browser",
Location,
Loading,
StaneksGift,
Recovery,
Achievements,
ThemeBrowser,
ImportSave,
} }
/**
* "Complex" pages that need a custom transition function.
*/
export enum ComplexPage {
BitVerse = "BitVerse",
Faction = "Faction",
Infiltration = "Infiltration",
Job = "Job",
ScriptEditor = "Script Editor",
Location = "Location",
ImportSave = "Import Save",
}
// Using the same name as both type and object to mimic enum-like behavior.
// See https://stackoverflow.com/a/71255520/202091
export type Page = SimplePage | ComplexPage;
export const Page = { ...SimplePage, ...ComplexPage };
export interface ScriptEditorRouteOptions { export interface ScriptEditorRouteOptions {
vim: boolean; vim: boolean;
} }
/** The router keeps track of player navigation/routing within the game. */ /** The router keeps track of player navigation/routing within the game. */
export interface IRouter { export interface IRouter {
// toCinematicText(): void;
// toInfiltration(): void;
// toMission(): void;
// toRedPill(): void;
// toworkInProgress(): void;
isInitialized: boolean; isInitialized: boolean;
page(): Page; page(): Page;
allowRouting(value: boolean): void; allowRouting(value: boolean): void;
toActiveScripts(): void; toPage(page: SimplePage): void;
toAugmentations(): void;
toBitVerse(flume: boolean, quick: boolean): void; toBitVerse(flume: boolean, quick: boolean): void;
toBladeburner(): void;
toStats(): void;
toCity(): void; // travel ? city ?
toCorporation(): void;
toCreateProgram(): void;
toDevMenu(): void;
toFaction(faction: Faction, augPage?: boolean): void; // faction name toFaction(faction: Faction, augPage?: boolean): void; // faction name
toFactions(): void;
toGameOptions(): void;
toGang(): void;
toHacknetNodes(): void;
toInfiltration(location: Location): void; toInfiltration(location: Location): void;
toJob(location: Location): void; toJob(location: Location): void;
toMilestones(): void;
toGrafting(): void;
toScriptEditor(files?: Record<string, string>, options?: ScriptEditorRouteOptions): void; toScriptEditor(files?: Record<string, string>, options?: ScriptEditorRouteOptions): void;
toSleeves(): void;
toStockMarket(): void;
toTerminal(): void;
toTravel(): void;
toTutorial(): void;
toWork(): void;
toBladeburnerCinematic(): void;
toLocation(location: Location): void; toLocation(location: Location): void;
toStaneksGift(): void;
toAchievements(): void;
toThemeBrowser(): void;
toImportSave(base64Save: string, automatic?: boolean): void; toImportSave(base64Save: string, automatic?: boolean): void;
} }

@ -11,6 +11,7 @@ import { Settings } from "../Settings/Settings";
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions"; import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
import { Player } from "@player"; import { Player } from "@player";
import { Router } from "./GameRoot"; import { Router } from "./GameRoot";
import { Page } from "./Router";
import { numeralWrapper } from "./numeralFormat"; import { numeralWrapper } from "./numeralFormat";
import { Money } from "./React/Money"; import { Money } from "./React/Money";
import { MoneyRate } from "./React/MoneyRate"; import { MoneyRate } from "./React/MoneyRate";
@ -211,7 +212,7 @@ export function WorkInProgressRoot(): React.ReactElement {
}; };
if (Player.currentWork === null) { if (Player.currentWork === null) {
setTimeout(() => Router.toTerminal()); setTimeout(() => Router.toPage(Page.Terminal));
return <></>; return <></>;
} }
@ -227,7 +228,7 @@ export function WorkInProgressRoot(): React.ReactElement {
Player.finishWork(true); Player.finishWork(true);
}, },
unfocus: () => { unfocus: () => {
Router.toCity(); Router.toPage(Page.City);
Player.stopFocusing(); Player.stopFocusing();
}, },
}, },
@ -256,11 +257,11 @@ export function WorkInProgressRoot(): React.ReactElement {
const classWork = Player.currentWork; const classWork = Player.currentWork;
function cancel(): void { function cancel(): void {
Player.finishWork(true); Player.finishWork(true);
Router.toCity(); Router.toPage(Page.City);
} }
function unfocus(): void { function unfocus(): void {
Router.toCity(); Router.toPage(Page.City);
Player.stopFocusing(); Player.stopFocusing();
} }
@ -303,10 +304,10 @@ export function WorkInProgressRoot(): React.ReactElement {
const create = Player.currentWork; const create = Player.currentWork;
function cancel(): void { function cancel(): void {
Player.finishWork(true); Player.finishWork(true);
Router.toTerminal(); Router.toPage(Page.Terminal);
} }
function unfocus(): void { function unfocus(): void {
Router.toTerminal(); Router.toPage(Page.Terminal);
Player.stopFocusing(); Player.stopFocusing();
} }
@ -337,10 +338,10 @@ export function WorkInProgressRoot(): React.ReactElement {
const graft = Player.currentWork; const graft = Player.currentWork;
function cancel(): void { function cancel(): void {
Player.finishWork(true); Player.finishWork(true);
Router.toTerminal(); Router.toPage(Page.Terminal);
} }
function unfocus(): void { function unfocus(): void {
Router.toTerminal(); Router.toPage(Page.Terminal);
Player.stopFocusing(); Player.stopFocusing();
} }
@ -374,7 +375,7 @@ export function WorkInProgressRoot(): React.ReactElement {
if (!faction) { if (!faction) {
workInfo = { workInfo = {
buttons: { buttons: {
cancel: () => Router.toFactions(), cancel: () => Router.toPage(Page.Factions),
}, },
title: title:
`You have not joined ${Player.currentWork.factionName || "(Faction not found)"} at this time,` + `You have not joined ${Player.currentWork.factionName || "(Faction not found)"} at this time,` +
@ -432,7 +433,7 @@ export function WorkInProgressRoot(): React.ReactElement {
if (comp) { if (comp) {
workInfo = { workInfo = {
buttons: { buttons: {
cancel: () => Router.toTerminal(), cancel: () => Router.toPage(Page.Terminal),
}, },
title: title:
`You cannot work for ${Player.currentWork.companyName || "(Company not found)"} at this time,` + `You cannot work for ${Player.currentWork.companyName || "(Company not found)"} at this time,` +

@ -89,7 +89,7 @@ export function getErrorForDisplay(error: Error, errorInfo?: React.ErrorInfo, pa
`lang=${metadata.features.language} cookiesEnabled=${metadata.features.cookiesEnabled.toString()}` + `lang=${metadata.features.language} cookiesEnabled=${metadata.features.cookiesEnabled.toString()}` +
` doNotTrack=${metadata.features.doNotTrack ?? "null"} indexedDb=${metadata.features.indexedDb.toString()}`; ` doNotTrack=${metadata.features.doNotTrack ?? "null"} indexedDb=${metadata.features.indexedDb.toString()}`;
const title = `${metadata.error.name}: ${metadata.error.message}${metadata.page && ` (at "${Page[metadata.page]}")`}`; const title = `${metadata.error.name}: ${metadata.error.message} (at "${metadata.page}")`;
const body = ` const body = `
## ${title} ## ${title}
@ -104,7 +104,7 @@ Please fill this information with details if relevant.
### Environment ### Environment
* Error: ${metadata.error?.toString() ?? "n/a"} * Error: ${metadata.error?.toString() ?? "n/a"}
* Page: ${metadata.page ? Page[metadata.page] : "n/a"} * Page: ${metadata.page ?? "n/a"}
* Version: ${metadata.version.toDisplay()} * Version: ${metadata.version.toDisplay()}
* Environment: ${GameEnv[metadata.environment]} * Environment: ${GameEnv[metadata.environment]}
* Platform: ${Platform[metadata.platform]} * Platform: ${Platform[metadata.platform]}