Add toPreviousPage in router

- Allows the WorkInProgress cancel & unfocus to go back to the previous
page instead of a default one.
- Change layout of overview buttons
- Add a back button in the overview, only visible in pages with a
sidebar
- Clear the history on augmentation install & on prestige
This commit is contained in:
Martin Fournier 2022-01-11 06:51:27 -05:00
parent f742782e4a
commit b0bc3236fd
6 changed files with 165 additions and 57 deletions

@ -13,6 +13,7 @@ import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import { clearObject } from "../utils/helpers/clearObject"; import { clearObject } from "../utils/helpers/clearObject";
import { Router } from "../ui/GameRoot";
import { WHRNG } from "../Casino/RNG"; import { WHRNG } from "../Casino/RNG";
@ -2582,6 +2583,8 @@ function installAugmentations(): boolean {
augmentationList += aug.name + level + "<br>"; augmentationList += aug.name + level + "<br>";
} }
Player.queuedAugmentations = []; Player.queuedAugmentations = [];
Router.clearHistory();
dialogBoxCreate( dialogBoxCreate(
"You slowly drift to sleep as scientists put you under in order " + "You slowly drift to sleep as scientists put you under in order " +
"to install the following Augmentations:<br>" + "to install the following Augmentations:<br>" +

@ -306,5 +306,7 @@ export function prestigeSourceFile(flume: boolean): void {
// Gain int exp // Gain int exp
if (SourceFileFlags[5] !== 0 && !flume) Player.gainIntelligenceExp(300); if (SourceFileFlags[5] !== 0 && !flume) Player.gainIntelligenceExp(300);
Router.clearHistory();
resetPidCounter(); resetPidCounter();
} }

@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { cloneDeep } from "lodash";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { IEngine } from "../IEngine"; import { IEngine } from "../IEngine";
import { ITerminal } from "../Terminal/ITerminal"; import { ITerminal } from "../Terminal/ITerminal";
@ -86,6 +86,11 @@ interface IProps {
engine: IEngine; engine: IEngine;
} }
interface PageHistoryEntry {
page: Page;
args: any[];
}
const useStyles = makeStyles((theme: Theme) => const useStyles = makeStyles((theme: Theme) =>
createStyles({ createStyles({
root: { root: {
@ -104,6 +109,15 @@ export let Router: IRouter = {
page: () => { page: () => {
throw new Error("Router called before initialization"); 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: () => { toActiveScripts: () => {
throw new Error("Router called before initialization"); 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 { export function GameRoot({ player, engine, terminal }: IProps): React.ReactElement {
const classes = useStyles(); const classes = useStyles();
const [{ files, vim }, setEditorOptions] = useState({ files: {}, vim: false }); 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<PageHistoryEntry[]>([{ page: startPage, args: [] }]);
const setRerender = useState(0)[1]; const setRerender = useState(0)[1];
const [faction, setFaction] = useState<Faction>( const [faction, setFaction] = useState<Faction>(
player.currentWorkFactionName ? Factions[player.currentWorkFactionName] : (undefined as unknown as Faction), 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); 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 = { Router = {
page: () => page, page: () => page,
toActiveScripts: () => setPage(Page.ActiveScripts), previousPage: () => {
toAugmentations: () => setPage(Page.Augmentations), const [ , previousPage] = pageHistory;
toBladeburner: () => setPage(Page.Bladeburner), return previousPage?.page ?? -1;
toStats: () => setPage(Page.Stats), },
toCorporation: () => setPage(Page.Corporation), clearHistory: () => setPageHistory([]),
toCreateProgram: () => setPage(Page.CreateProgram), toPreviousPage: goBack,
toDevMenu: () => setPage(Page.DevMenu), 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) => { toFaction: (faction?: Faction) => {
setPage(Page.Faction); setCurrentPage(Page.Faction, faction);
if (faction) setFaction(faction); if (faction) setFaction(faction);
}, },
toFactions: () => setPage(Page.Factions), toFactions: () => setCurrentPage(Page.Factions),
toGameOptions: () => setPage(Page.Options), toGameOptions: () => setCurrentPage(Page.Options),
toGang: () => setPage(Page.Gang), toGang: () => setCurrentPage(Page.Gang),
toHacknetNodes: () => setPage(Page.Hacknet), toHacknetNodes: () => setCurrentPage(Page.Hacknet),
toMilestones: () => setPage(Page.Milestones), toMilestones: () => setCurrentPage(Page.Milestones),
toResleeves: () => setPage(Page.Resleeves), toResleeves: () => setCurrentPage(Page.Resleeves),
toScriptEditor: (files: Record<string, string>, options?: ScriptEditorRouteOptions) => { toScriptEditor: (files: Record<string, string>, options?: ScriptEditorRouteOptions) => {
setEditorOptions({ setEditorOptions({
files, files,
vim: !!options?.vim, vim: !!options?.vim,
}); });
setPage(Page.ScriptEditor); setCurrentPage(Page.ScriptEditor, files, options);
}, },
toSleeves: () => setPage(Page.Sleeves), toSleeves: () => setCurrentPage(Page.Sleeves),
toStockMarket: () => setPage(Page.StockMarket), toStockMarket: () => setCurrentPage(Page.StockMarket),
toTerminal: () => setPage(Page.Terminal), toTerminal: () => setCurrentPage(Page.Terminal),
toTutorial: () => setPage(Page.Tutorial), toTutorial: () => setCurrentPage(Page.Tutorial),
toJob: () => { toJob: () => {
setLocation(Locations[player.companyName]); setLocation(Locations[player.companyName]);
setPage(Page.Job); setCurrentPage(Page.Job);
}, },
toCity: () => { toCity: () => {
setPage(Page.City); setCurrentPage(Page.City);
}, },
toTravel: () => { toTravel: () => {
player.gotoLocation(LocationName.TravelAgency); player.gotoLocation(LocationName.TravelAgency);
setPage(Page.Travel); setCurrentPage(Page.Travel);
}, },
toBitVerse: (flume: boolean, quick: boolean) => { toBitVerse: (flume: boolean, quick: boolean) => {
setFlume(flume); setFlume(flume);
setQuick(quick); setQuick(quick);
setPage(Page.BitVerse); setCurrentPage(Page.BitVerse, flume, quick);
}, },
toInfiltration: (location: Location) => { toInfiltration: (location: Location) => {
setLocation(location); setLocation(location);
setPage(Page.Infiltration); setCurrentPage(Page.Infiltration, location);
},
toWork: () => {
setCurrentPage(Page.Work);
}, },
toWork: () => setPage(Page.Work),
toBladeburnerCinematic: () => { toBladeburnerCinematic: () => {
setPage(Page.BladeburnerCinematic); setCurrentPage(Page.BladeburnerCinematic);
setCinematicText(cinematicText); setCinematicText(cinematicText);
}, },
toLocation: (location: Location) => { toLocation: (location: Location) => {
setLocation(location); setLocation(location);
setPage(Page.Location); setCurrentPage(Page.Location, location);
}, },
toStaneksGift: () => { toStaneksGift: () => {
setPage(Page.StaneksGift); setCurrentPage(Page.StaneksGift);
}, },
toAchievements: () => { toAchievements: () => {
setPage(Page.Achievements); setCurrentPage(Page.Achievements);
}, },
}; };
@ -499,7 +576,11 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
<SnackbarProvider> <SnackbarProvider>
<Overview mode={ITutorial.isRunning ? "tutorial" : "overview"}> <Overview mode={ITutorial.isRunning ? "tutorial" : "overview"}>
{!ITutorial.isRunning ? ( {!ITutorial.isRunning ? (
<CharacterOverview save={() => saveObject.saveGame()} killScripts={killAllScripts} /> <CharacterOverview
save={() => saveObject.saveGame()}
killScripts={killAllScripts}
router={Router}
allowBackButton={withSidebar} />
) : ( ) : (
<InteractiveTutorialRoot /> <InteractiveTutorialRoot />
)} )}

@ -16,16 +16,21 @@ import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import IconButton from "@mui/material/IconButton"; import IconButton from "@mui/material/IconButton";
import SaveIcon from "@mui/icons-material/Save"; import SaveIcon from "@mui/icons-material/Save";
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import ClearAllIcon from "@mui/icons-material/ClearAll"; import ClearAllIcon from "@mui/icons-material/ClearAll";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { use } from "../Context"; import { use } from "../Context";
import { StatsProgressOverviewCell } from "./StatsProgressBar"; import { StatsProgressOverviewCell } from "./StatsProgressBar";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { IRouter, Page } from "../Router";
import { Box, Tooltip } from "@mui/material";
interface IProps { interface IProps {
save: () => void; save: () => void;
killScripts: () => void; killScripts: () => void;
router: IRouter;
allowBackButton: boolean;
} }
function Intelligence(): React.ReactElement { function Intelligence(): React.ReactElement {
@ -205,7 +210,7 @@ const useStyles = makeStyles((theme: Theme) =>
export { useStyles as characterOverviewStyles }; 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 [killOpen, setKillOpen] = useState(false);
const player = use.Player(); const player = use.Player();
@ -244,6 +249,9 @@ export function CharacterOverview({ save, killScripts }: IProps): React.ReactEle
player.charisma_mult * BitNodeMultipliers.CharismaLevelMultiplier, player.charisma_mult * BitNodeMultipliers.CharismaLevelMultiplier,
); );
const previousPageName = router.previousPage() < 0
? '' : Page[router.previousPage() ?? 0].replace(/([a-z])([A-Z])/g, '$1 $2');
return ( return (
<> <>
<Table sx={{ display: "block", m: 1 }}> <Table sx={{ display: "block", m: 1 }}>
@ -418,21 +426,33 @@ export function CharacterOverview({ save, killScripts }: IProps): React.ReactEle
</TableRow> </TableRow>
<Work /> <Work />
<Bladeburner /> <Bladeburner />
<TableRow>
<TableCell align="center" classes={{ root: classes.cellNone }}>
<IconButton onClick={save}>
<SaveIcon color={Settings.AutosaveInterval !== 0 ? "primary" : "error"} />
</IconButton>
</TableCell>
<TableCell align="center" classes={{ root: classes.cellNone }}>
<IconButton onClick={() => setKillOpen(true)}>
<ClearAllIcon color="error" />
</IconButton>
</TableCell>
</TableRow>
</TableBody> </TableBody>
</Table> </Table>
<Box sx={{ display: 'flex', borderTop: `1px solid ${Settings.theme.welllight}` }}>
<Box sx={{ display: 'flex', flex: 1, justifyContent: 'flex-start', alignItems: 'center' }}>
<IconButton onClick={save}>
<Tooltip title="Save game">
<SaveIcon color={Settings.AutosaveInterval !== 0 ? "primary" : "error"} />
</Tooltip>
</IconButton>
{allowBackButton && (
<IconButton
disabled={!previousPageName}
onClick={() => router.toPreviousPage()}>
<Tooltip title={previousPageName ? `Go back to "${previousPageName}"` : ''}>
<ArrowBackIcon />
</Tooltip>
</IconButton>
)}
</Box>
<Box sx={{ display: 'flex', flex: 1, justifyContent: 'flex-end', alignItems: 'center' }}>
<IconButton onClick={() => setKillOpen(true)}>
<Tooltip title="Kill all running scripts">
<ClearAllIcon color="error" />
</Tooltip>
</IconButton>
</Box>
</Box>
<KillScriptsModal open={killOpen} onClose={() => setKillOpen(false)} killScripts={killScripts} /> <KillScriptsModal open={killOpen} onClose={() => setKillOpen(false)} killScripts={killScripts} />
</> </>
); );

@ -53,6 +53,9 @@ export interface IRouter {
// toRedPill(): void; // toRedPill(): void;
// toworkInProgress(): void; // toworkInProgress(): void;
page(): Page; page(): Page;
previousPage(): Page;
clearHistory(): void;
toPreviousPage(fallback?: (...args: any[]) => void): void;
toActiveScripts(): void; toActiveScripts(): void;
toAugmentations(): void; toAugmentations(): void;
toBitVerse(flume: boolean, quick: boolean): void; toBitVerse(flume: boolean, quick: boolean): void;

@ -36,12 +36,12 @@ export function WorkInProgressRoot(): React.ReactElement {
const faction = Factions[player.currentWorkFactionName]; const faction = Factions[player.currentWorkFactionName];
if (player.workType == CONSTANTS.WorkTypeFaction) { if (player.workType == CONSTANTS.WorkTypeFaction) {
function cancel(): void { function cancel(): void {
router.toFaction(faction);
player.finishFactionWork(true); player.finishFactionWork(true);
router.toPreviousPage(() => router.toFaction(faction));
} }
function unfocus(): void { function unfocus(): void {
router.toFaction(faction);
player.stopFocusing(); player.stopFocusing();
router.toPreviousPage(() => router.toFaction(faction));
} }
return ( return (
<Grid container direction="column" justifyContent="center" alignItems="center" style={{ minHeight: "100vh" }}> <Grid container direction="column" justifyContent="center" alignItems="center" style={{ minHeight: "100vh" }}>
@ -120,13 +120,12 @@ export function WorkInProgressRoot(): React.ReactElement {
if (player.className !== "") { if (player.className !== "") {
function cancel(): void { function cancel(): void {
player.finishClass(true); player.finishClass(true);
router.toCity(); router.toPreviousPage(() => router.toCity());
} }
function unfocus(): void { function unfocus(): void {
router.toFaction(faction);
router.toCity();
player.stopFocusing(); player.stopFocusing();
router.toPreviousPage(() => router.toCity());
} }
let stopText = ""; let stopText = "";
@ -212,11 +211,11 @@ export function WorkInProgressRoot(): React.ReactElement {
function cancel(): void { function cancel(): void {
player.finishWork(true); player.finishWork(true);
router.toJob(); router.toPreviousPage(() => router.toJob());
} }
function unfocus(): void { function unfocus(): void {
player.stopFocusing(); player.stopFocusing();
router.toJob(); router.toPreviousPage(() => router.toJob());
} }
const position = player.jobs[player.companyName]; const position = player.jobs[player.companyName];
@ -304,11 +303,11 @@ export function WorkInProgressRoot(): React.ReactElement {
if (player.workType == CONSTANTS.WorkTypeCompanyPartTime) { if (player.workType == CONSTANTS.WorkTypeCompanyPartTime) {
function cancel(): void { function cancel(): void {
player.finishWorkPartTime(true); player.finishWorkPartTime(true);
router.toJob(); router.toPreviousPage(() => router.toJob());
} }
function unfocus(): void { function unfocus(): void {
player.stopFocusing(); player.stopFocusing();
router.toJob(); router.toPreviousPage(() => router.toJob());
} }
const comp = Companies[player.companyName]; const comp = Companies[player.companyName];
let companyRep = 0; let companyRep = 0;
@ -440,11 +439,11 @@ export function WorkInProgressRoot(): React.ReactElement {
if (player.createProgramName !== "") { if (player.createProgramName !== "") {
function cancel(): void { function cancel(): void {
player.finishCreateProgramWork(true); player.finishCreateProgramWork(true);
router.toTerminal(); router.toPreviousPage(() => router.toTerminal());
} }
function unfocus(): void { function unfocus(): void {
router.toTerminal();
player.stopFocusing(); player.stopFocusing();
router.toPreviousPage(() => router.toTerminal());
} }
return ( return (
<Grid container direction="column" justifyContent="center" alignItems="center" style={{ minHeight: "100vh" }}> <Grid container direction="column" justifyContent="center" alignItems="center" style={{ minHeight: "100vh" }}>