diff --git a/src/Augmentation/AugmentationHelpers.tsx b/src/Augmentation/AugmentationHelpers.tsx
index 80f709a1f..2959dfd78 100644
--- a/src/Augmentation/AugmentationHelpers.tsx
+++ b/src/Augmentation/AugmentationHelpers.tsx
@@ -13,6 +13,7 @@ import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
import { dialogBoxCreate } from "../ui/React/DialogBox";
import { clearObject } from "../utils/helpers/clearObject";
+import { Router } from "../ui/GameRoot";
import { WHRNG } from "../Casino/RNG";
@@ -2582,6 +2583,8 @@ function installAugmentations(): boolean {
augmentationList += aug.name + level + "
";
}
Player.queuedAugmentations = [];
+ Router.clearHistory();
+
dialogBoxCreate(
"You slowly drift to sleep as scientists put you under in order " +
"to install the following Augmentations:
" +
diff --git a/src/Prestige.ts b/src/Prestige.ts
index 971ad08c7..505f2c7df 100755
--- a/src/Prestige.ts
+++ b/src/Prestige.ts
@@ -306,5 +306,7 @@ export function prestigeSourceFile(flume: boolean): void {
// Gain int exp
if (SourceFileFlags[5] !== 0 && !flume) Player.gainIntelligenceExp(300);
+ Router.clearHistory();
+
resetPidCounter();
}
diff --git a/src/ui/GameRoot.tsx b/src/ui/GameRoot.tsx
index aa9f3cb77..bb9354274 100644
--- a/src/ui/GameRoot.tsx
+++ b/src/ui/GameRoot.tsx
@@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react";
-
+import { cloneDeep } from "lodash";
import { IPlayer } from "../PersonObjects/IPlayer";
import { IEngine } from "../IEngine";
import { ITerminal } from "../Terminal/ITerminal";
@@ -86,6 +86,11 @@ interface IProps {
engine: IEngine;
}
+interface PageHistoryEntry {
+ page: Page;
+ args: any[];
+}
+
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
@@ -104,6 +109,15 @@ export let Router: IRouter = {
page: () => {
throw new Error("Router called before initialization");
},
+ previousPage: () => {
+ throw new Error("Router called before initialization");
+ },
+ clearHistory: () => {
+ throw new Error("Router called before initialization");
+ },
+ toPreviousPage: (): boolean => {
+ throw new Error("Router called before initialization");
+ },
toActiveScripts: () => {
throw new Error("Router called before initialization");
},
@@ -202,7 +216,9 @@ function determineStartPage(player: IPlayer): Page {
export function GameRoot({ player, engine, terminal }: IProps): React.ReactElement {
const classes = useStyles();
const [{ files, vim }, setEditorOptions] = useState({ files: {}, vim: false });
- const [page, setPage] = useState(determineStartPage(player));
+ const startPage = determineStartPage(player);
+ const [page, setPage] = useState(startPage);
+ const [pageHistory, setPageHistory] = useState([{ page: startPage, args: [] }]);
const setRerender = useState(0)[1];
const [faction, setFaction] = useState(
player.currentWorkFactionName ? Factions[player.currentWorkFactionName] : (undefined as unknown as Faction),
@@ -233,70 +249,131 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
setTimeout(() => htmlLocation.reload(), 2000);
}
+ function setCurrentPage(page: Page, ...args: any): void {
+ const history = [
+ { page, args: cloneDeep(args) },
+ ...pageHistory
+ ].slice(0, 20);
+ setPageHistory(history)
+ setPage(page)
+ }
+
+ function goBack(fallback: (...args: any[]) => void): void {
+ const [ , previousPage ] = pageHistory;
+ if (previousPage) {
+ const handler = pageToRouterMap[previousPage?.page];
+ handler(...previousPage.args);
+ } else {
+ if (fallback) fallback();
+ }
+ const [ , ...history] = pageHistory;
+ setPageHistory(cloneDeep(history));
+ }
+
+ const pageToRouterMap: { [key: number] : (...args: any[]) => void } = {
+ [Page.ActiveScripts]: Router.toActiveScripts,
+ [Page.Augmentations]: Router.toAugmentations,
+ [Page.Bladeburner]: Router.toBladeburner,
+ [Page.Stats]: Router.toStats,
+ [Page.Corporation]: Router.toCorporation,
+ [Page.CreateProgram]: Router.toCreateProgram,
+ [Page.DevMenu]: Router.toDevMenu,
+ [Page.Faction]: Router.toFaction,
+ [Page.Factions]: Router.toFactions,
+ [Page.Options]: Router.toGameOptions,
+ [Page.Gang]: Router.toGang,
+ [Page.Hacknet]: Router.toHacknetNodes,
+ [Page.Milestones]: Router.toMilestones,
+ [Page.Resleeves]: Router.toResleeves,
+ [Page.ScriptEditor]: Router.toScriptEditor,
+ [Page.Sleeves]: Router.toSleeves,
+ [Page.StockMarket]: Router.toStockMarket,
+ [Page.Terminal]: Router.toTerminal,
+ [Page.Tutorial]: Router.toTutorial,
+ [Page.Job]: Router.toJob,
+ [Page.City]: Router.toCity,
+ [Page.Travel]: Router.toTravel,
+ [Page.BitVerse]: Router.toBitVerse,
+ [Page.Infiltration]: Router.toInfiltration,
+ [Page.Work]: Router.toWork,
+ [Page.BladeburnerCinematic]: Router.toBladeburnerCinematic,
+ [Page.Location]: Router.toLocation,
+ [Page.StaneksGift]: Router.toStaneksGift,
+ [Page.Achievements]: Router.toAchievements,
+ }
+
Router = {
page: () => page,
- toActiveScripts: () => setPage(Page.ActiveScripts),
- toAugmentations: () => setPage(Page.Augmentations),
- toBladeburner: () => setPage(Page.Bladeburner),
- toStats: () => setPage(Page.Stats),
- toCorporation: () => setPage(Page.Corporation),
- toCreateProgram: () => setPage(Page.CreateProgram),
- toDevMenu: () => setPage(Page.DevMenu),
+ previousPage: () => {
+ const [ , previousPage] = pageHistory;
+ return previousPage?.page ?? -1;
+ },
+ clearHistory: () => setPageHistory([]),
+ toPreviousPage: goBack,
+ toActiveScripts: () => setCurrentPage(Page.ActiveScripts),
+ toAugmentations: () => setCurrentPage(Page.Augmentations),
+ toBladeburner: () => setCurrentPage(Page.Bladeburner),
+ toStats: () => setCurrentPage(Page.Stats),
+ toCorporation: () => setCurrentPage(Page.Corporation),
+ toCreateProgram: () => setCurrentPage(Page.CreateProgram),
+ toDevMenu: () => setCurrentPage(Page.DevMenu),
toFaction: (faction?: Faction) => {
- setPage(Page.Faction);
+ setCurrentPage(Page.Faction, 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),
- toResleeves: () => setPage(Page.Resleeves),
+ toFactions: () => setCurrentPage(Page.Factions),
+ toGameOptions: () => setCurrentPage(Page.Options),
+ toGang: () => setCurrentPage(Page.Gang),
+ toHacknetNodes: () => setCurrentPage(Page.Hacknet),
+ toMilestones: () => setCurrentPage(Page.Milestones),
+ toResleeves: () => setCurrentPage(Page.Resleeves),
toScriptEditor: (files: Record, options?: ScriptEditorRouteOptions) => {
setEditorOptions({
files,
vim: !!options?.vim,
});
- setPage(Page.ScriptEditor);
+ setCurrentPage(Page.ScriptEditor, files, options);
},
- toSleeves: () => setPage(Page.Sleeves),
- toStockMarket: () => setPage(Page.StockMarket),
- toTerminal: () => setPage(Page.Terminal),
- toTutorial: () => setPage(Page.Tutorial),
+ toSleeves: () => setCurrentPage(Page.Sleeves),
+ toStockMarket: () => setCurrentPage(Page.StockMarket),
+ toTerminal: () => setCurrentPage(Page.Terminal),
+ toTutorial: () => setCurrentPage(Page.Tutorial),
toJob: () => {
setLocation(Locations[player.companyName]);
- setPage(Page.Job);
+ setCurrentPage(Page.Job);
},
toCity: () => {
- setPage(Page.City);
+ setCurrentPage(Page.City);
},
toTravel: () => {
player.gotoLocation(LocationName.TravelAgency);
- setPage(Page.Travel);
+ setCurrentPage(Page.Travel);
},
toBitVerse: (flume: boolean, quick: boolean) => {
setFlume(flume);
setQuick(quick);
- setPage(Page.BitVerse);
+ setCurrentPage(Page.BitVerse, flume, quick);
},
toInfiltration: (location: Location) => {
setLocation(location);
- setPage(Page.Infiltration);
+ setCurrentPage(Page.Infiltration, location);
+ },
+ toWork: () => {
+ setCurrentPage(Page.Work);
},
- toWork: () => setPage(Page.Work),
toBladeburnerCinematic: () => {
- setPage(Page.BladeburnerCinematic);
+ setCurrentPage(Page.BladeburnerCinematic);
setCinematicText(cinematicText);
},
toLocation: (location: Location) => {
setLocation(location);
- setPage(Page.Location);
+ setCurrentPage(Page.Location, location);
},
toStaneksGift: () => {
- setPage(Page.StaneksGift);
+ setCurrentPage(Page.StaneksGift);
},
toAchievements: () => {
- setPage(Page.Achievements);
+ setCurrentPage(Page.Achievements);
},
};
@@ -499,7 +576,11 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
{!ITutorial.isRunning ? (
- saveObject.saveGame()} killScripts={killAllScripts} />
+ saveObject.saveGame()}
+ killScripts={killAllScripts}
+ router={Router}
+ allowBackButton={withSidebar} />
) : (
)}
diff --git a/src/ui/React/CharacterOverview.tsx b/src/ui/React/CharacterOverview.tsx
index 885157235..d3ea22166 100644
--- a/src/ui/React/CharacterOverview.tsx
+++ b/src/ui/React/CharacterOverview.tsx
@@ -16,16 +16,21 @@ import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import IconButton from "@mui/material/IconButton";
import SaveIcon from "@mui/icons-material/Save";
+import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import ClearAllIcon from "@mui/icons-material/ClearAll";
import { Settings } from "../../Settings/Settings";
import { use } from "../Context";
import { StatsProgressOverviewCell } from "./StatsProgressBar";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
+import { IRouter, Page } from "../Router";
+import { Box, Tooltip } from "@mui/material";
interface IProps {
save: () => void;
killScripts: () => void;
+ router: IRouter;
+ allowBackButton: boolean;
}
function Intelligence(): React.ReactElement {
@@ -205,7 +210,7 @@ const useStyles = makeStyles((theme: Theme) =>
export { useStyles as characterOverviewStyles };
-export function CharacterOverview({ save, killScripts }: IProps): React.ReactElement {
+export function CharacterOverview({ save, killScripts, router, allowBackButton }: IProps): React.ReactElement {
const [killOpen, setKillOpen] = useState(false);
const player = use.Player();
@@ -244,6 +249,9 @@ export function CharacterOverview({ save, killScripts }: IProps): React.ReactEle
player.charisma_mult * BitNodeMultipliers.CharismaLevelMultiplier,
);
+ const previousPageName = router.previousPage() < 0
+ ? '' : Page[router.previousPage() ?? 0].replace(/([a-z])([A-Z])/g, '$1 $2');
+
return (
<>
@@ -418,21 +426,33 @@ export function CharacterOverview({ save, killScripts }: IProps): React.ReactEle
-
-
-
-
-
-
-
-
- setKillOpen(true)}>
-
-
-
-
+
+
+
+
+
+
+
+ {allowBackButton && (
+ router.toPreviousPage()}>
+
+
+
+
+ )}
+
+
+ setKillOpen(true)}>
+
+
+
+
+
+
setKillOpen(false)} killScripts={killScripts} />
>
);
diff --git a/src/ui/Router.ts b/src/ui/Router.ts
index 4056a44ac..8bf86f704 100644
--- a/src/ui/Router.ts
+++ b/src/ui/Router.ts
@@ -53,6 +53,9 @@ export interface IRouter {
// toRedPill(): void;
// toworkInProgress(): void;
page(): Page;
+ previousPage(): Page;
+ clearHistory(): void;
+ toPreviousPage(fallback?: (...args: any[]) => void): void;
toActiveScripts(): void;
toAugmentations(): void;
toBitVerse(flume: boolean, quick: boolean): void;
diff --git a/src/ui/WorkInProgressRoot.tsx b/src/ui/WorkInProgressRoot.tsx
index d43843c85..535629deb 100644
--- a/src/ui/WorkInProgressRoot.tsx
+++ b/src/ui/WorkInProgressRoot.tsx
@@ -36,12 +36,12 @@ export function WorkInProgressRoot(): React.ReactElement {
const faction = Factions[player.currentWorkFactionName];
if (player.workType == CONSTANTS.WorkTypeFaction) {
function cancel(): void {
- router.toFaction(faction);
player.finishFactionWork(true);
+ router.toPreviousPage(() => router.toFaction(faction));
}
function unfocus(): void {
- router.toFaction(faction);
player.stopFocusing();
+ router.toPreviousPage(() => router.toFaction(faction));
}
return (
@@ -120,13 +120,12 @@ export function WorkInProgressRoot(): React.ReactElement {
if (player.className !== "") {
function cancel(): void {
player.finishClass(true);
- router.toCity();
+ router.toPreviousPage(() => router.toCity());
}
function unfocus(): void {
- router.toFaction(faction);
- router.toCity();
player.stopFocusing();
+ router.toPreviousPage(() => router.toCity());
}
let stopText = "";
@@ -212,11 +211,11 @@ export function WorkInProgressRoot(): React.ReactElement {
function cancel(): void {
player.finishWork(true);
- router.toJob();
+ router.toPreviousPage(() => router.toJob());
}
function unfocus(): void {
player.stopFocusing();
- router.toJob();
+ router.toPreviousPage(() => router.toJob());
}
const position = player.jobs[player.companyName];
@@ -304,11 +303,11 @@ export function WorkInProgressRoot(): React.ReactElement {
if (player.workType == CONSTANTS.WorkTypeCompanyPartTime) {
function cancel(): void {
player.finishWorkPartTime(true);
- router.toJob();
+ router.toPreviousPage(() => router.toJob());
}
function unfocus(): void {
player.stopFocusing();
- router.toJob();
+ router.toPreviousPage(() => router.toJob());
}
const comp = Companies[player.companyName];
let companyRep = 0;
@@ -440,11 +439,11 @@ export function WorkInProgressRoot(): React.ReactElement {
if (player.createProgramName !== "") {
function cancel(): void {
player.finishCreateProgramWork(true);
- router.toTerminal();
+ router.toPreviousPage(() => router.toTerminal());
}
function unfocus(): void {
- router.toTerminal();
player.stopFocusing();
+ router.toPreviousPage(() => router.toTerminal());
}
return (