mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-10-23 18:23:15 +02:00
855a4e622d
Adds a channel to communicate between the main process & the renderer process, so that the game can easily ship data back to the main process. It uses the Electron contextBridge & ipcRenderer/ipcMain. Connects those events to various save functions. Adds triggered events on game save, game load, and imported game. Adds way for the Electron app to ask for certain actions or data. Hook handlers to disable automatic restore Allows to temporarily disable restore when the game just did an import or deleted a save game. Prevents looping screens.
338 lines
12 KiB
TypeScript
338 lines
12 KiB
TypeScript
import React, { useEffect, useState } from "react";
|
|
|
|
import {
|
|
Paper,
|
|
Table,
|
|
TableHead,
|
|
TableRow,
|
|
TableBody,
|
|
TableContainer,
|
|
TableCell,
|
|
Typography,
|
|
Tooltip,
|
|
Box,
|
|
Button,
|
|
ButtonGroup,
|
|
} from "@mui/material";
|
|
|
|
import makeStyles from "@mui/styles/makeStyles";
|
|
import createStyles from "@mui/styles/createStyles";
|
|
import { Theme } from "@mui/material/styles";
|
|
|
|
import ThumbUpAlt from "@mui/icons-material/ThumbUpAlt";
|
|
import ThumbDownAlt from "@mui/icons-material/ThumbDownAlt";
|
|
import DirectionsRunIcon from "@mui/icons-material/DirectionsRun";
|
|
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
|
import WarningIcon from "@mui/icons-material/Warning";
|
|
|
|
import { ImportData, saveObject } from "../../SaveObject";
|
|
import { Settings } from "../../Settings/Settings";
|
|
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
|
import { numeralWrapper } from "../numeralFormat";
|
|
import { ConfirmationModal } from "./ConfirmationModal";
|
|
import { pushImportResult } from "../../Electron";
|
|
|
|
const useStyles = makeStyles((theme: Theme) =>
|
|
createStyles({
|
|
root: {
|
|
padding: theme.spacing(2),
|
|
maxWidth: "1000px",
|
|
|
|
"& .MuiTable-root": {
|
|
"& .MuiTableCell-root": {
|
|
borderBottom: `1px solid ${Settings.theme.welllight}`,
|
|
},
|
|
|
|
"& .MuiTableHead-root .MuiTableRow-root": {
|
|
backgroundColor: Settings.theme.backgroundsecondary,
|
|
|
|
"& .MuiTableCell-root": {
|
|
color: Settings.theme.primary,
|
|
fontWeight: "bold",
|
|
},
|
|
},
|
|
|
|
"& .MuiTableBody-root": {
|
|
"& .MuiTableRow-root:nth-of-type(odd)": {
|
|
backgroundColor: Settings.theme.well,
|
|
|
|
"& .MuiTableCell-root": {
|
|
color: Settings.theme.primarylight,
|
|
},
|
|
},
|
|
"& .MuiTableRow-root:nth-of-type(even)": {
|
|
backgroundColor: Settings.theme.backgroundsecondary,
|
|
|
|
"& .MuiTableCell-root": {
|
|
color: Settings.theme.primarylight,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
);
|
|
|
|
function ComparisonIcon({ isBetter }: { isBetter: boolean }): JSX.Element {
|
|
if (isBetter) {
|
|
return (
|
|
<Tooltip
|
|
title={
|
|
<>
|
|
Imported value is <b>larger</b>!
|
|
</>
|
|
}
|
|
>
|
|
<ThumbUpAlt color="success" />
|
|
</Tooltip>
|
|
);
|
|
} else {
|
|
return (
|
|
<Tooltip
|
|
title={
|
|
<>
|
|
Imported value is <b>smaller</b>!
|
|
</>
|
|
}
|
|
>
|
|
<ThumbDownAlt color="error" />
|
|
</Tooltip>
|
|
);
|
|
}
|
|
}
|
|
|
|
export interface ImportSaveProps {
|
|
importString: string;
|
|
automatic: boolean;
|
|
onReturning: () => void;
|
|
}
|
|
|
|
let initialAutosave = 0;
|
|
|
|
export function ImportSaveRoot({ importString, automatic, onReturning }: ImportSaveProps): JSX.Element {
|
|
const classes = useStyles();
|
|
const [importData, setImportData] = useState<ImportData | undefined>();
|
|
const [currentData, setCurrentData] = useState<ImportData | undefined>();
|
|
const [importModalOpen, setImportModalOpen] = useState(false);
|
|
|
|
function handleGoBack(): void {
|
|
Settings.AutosaveInterval = initialAutosave;
|
|
pushImportResult(false);
|
|
onReturning();
|
|
}
|
|
|
|
async function handleImport(): Promise<void> {
|
|
await saveObject.importGame(importString, true);
|
|
pushImportResult(true);
|
|
}
|
|
|
|
useEffect(() => {
|
|
// We want to disable autosave while we're in this mode
|
|
initialAutosave = Settings.AutosaveInterval;
|
|
Settings.AutosaveInterval = 0;
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
async function fetchData(): Promise<void> {
|
|
const dataBeingImported = await saveObject.getImportDataFromString(importString);
|
|
const dataCurrentlyInGame = await saveObject.getImportDataFromString(saveObject.getSaveString(true));
|
|
|
|
setImportData(dataBeingImported);
|
|
setCurrentData(dataCurrentlyInGame);
|
|
|
|
return Promise.resolve();
|
|
}
|
|
if (importString) fetchData();
|
|
}, [importString]);
|
|
|
|
if (!importData || !currentData) return <></>;
|
|
return (
|
|
<Box className={classes.root}>
|
|
<Typography variant="h4" sx={{ mb: 2 }}>
|
|
Import Save Comparison
|
|
</Typography>
|
|
{automatic && (
|
|
<Typography sx={{ mb: 2 }}>
|
|
We've found a <b>NEWER save</b> that you may want to use instead.
|
|
</Typography>
|
|
)}
|
|
<Typography variant="body1" sx={{ mb: 2 }}>
|
|
Your current game's data is on the left and the data that will be imported is on the right.
|
|
<br />
|
|
Please double check everything is fine before proceeding!
|
|
</Typography>
|
|
<TableContainer color="secondary" component={Paper}>
|
|
<Table>
|
|
<TableHead>
|
|
<TableRow>
|
|
<TableCell></TableCell>
|
|
<TableCell>Current Game</TableCell>
|
|
<TableCell>Being Imported</TableCell>
|
|
<TableCell></TableCell>
|
|
</TableRow>
|
|
</TableHead>
|
|
|
|
<TableBody>
|
|
<TableRow>
|
|
<TableCell>Game Identifier</TableCell>
|
|
<TableCell>{currentData.playerData?.identifier ?? "n/a"}</TableCell>
|
|
<TableCell>{importData.playerData?.identifier ?? "n/a"}</TableCell>
|
|
<TableCell>
|
|
{importData.playerData?.identifier !== currentData.playerData?.identifier && (
|
|
<Tooltip title="These are two different games!">
|
|
<WarningIcon color="warning" />
|
|
</Tooltip>
|
|
)}
|
|
</TableCell>
|
|
</TableRow>
|
|
<TableRow>
|
|
<TableCell>Playtime</TableCell>
|
|
<TableCell>{convertTimeMsToTimeElapsedString(currentData.playerData?.totalPlaytime ?? 0)}</TableCell>
|
|
<TableCell>{convertTimeMsToTimeElapsedString(importData.playerData?.totalPlaytime ?? 0)}</TableCell>
|
|
<TableCell>
|
|
{importData.playerData?.totalPlaytime !== currentData.playerData?.totalPlaytime && (
|
|
<ComparisonIcon
|
|
isBetter={
|
|
(importData.playerData?.totalPlaytime ?? 0) > (currentData.playerData?.totalPlaytime ?? 0)
|
|
}
|
|
/>
|
|
)}
|
|
</TableCell>
|
|
</TableRow>
|
|
|
|
<TableRow>
|
|
<TableCell>Saved On</TableCell>
|
|
<TableCell>{new Date(currentData.playerData?.lastSave ?? 0).toLocaleString()}</TableCell>
|
|
<TableCell>{new Date(importData.playerData?.lastSave ?? 0).toLocaleString()}</TableCell>
|
|
<TableCell>
|
|
{importData.playerData?.lastSave !== currentData.playerData?.lastSave && (
|
|
<ComparisonIcon
|
|
isBetter={(importData.playerData?.lastSave ?? 0) > (currentData.playerData?.lastSave ?? 0)}
|
|
/>
|
|
)}
|
|
</TableCell>
|
|
</TableRow>
|
|
|
|
<TableRow>
|
|
<TableCell>Money</TableCell>
|
|
<TableCell>{numeralWrapper.formatMoney(currentData.playerData?.money ?? 0)}</TableCell>
|
|
<TableCell>{numeralWrapper.formatMoney(importData.playerData?.money ?? 0)}</TableCell>
|
|
<TableCell>
|
|
{importData.playerData?.money !== currentData.playerData?.money && (
|
|
<ComparisonIcon
|
|
isBetter={(importData.playerData?.money ?? 0) > (currentData.playerData?.money ?? 0)}
|
|
/>
|
|
)}
|
|
</TableCell>
|
|
</TableRow>
|
|
|
|
<TableRow>
|
|
<TableCell>Hacking</TableCell>
|
|
<TableCell>{numeralWrapper.formatSkill(currentData.playerData?.hacking ?? 0)}</TableCell>
|
|
<TableCell>{numeralWrapper.formatSkill(importData.playerData?.hacking ?? 0)}</TableCell>
|
|
<TableCell>
|
|
{importData.playerData?.hacking !== currentData.playerData?.hacking && (
|
|
<ComparisonIcon
|
|
isBetter={(importData.playerData?.hacking ?? 0) > (currentData.playerData?.hacking ?? 0)}
|
|
/>
|
|
)}
|
|
</TableCell>
|
|
</TableRow>
|
|
|
|
<TableRow>
|
|
<TableCell>Augmentations</TableCell>
|
|
<TableCell>{currentData.playerData?.augmentations}</TableCell>
|
|
<TableCell>{importData.playerData?.augmentations}</TableCell>
|
|
<TableCell>
|
|
{importData.playerData?.augmentations !== currentData.playerData?.augmentations && (
|
|
<ComparisonIcon
|
|
isBetter={
|
|
(importData.playerData?.augmentations ?? 0) > (currentData.playerData?.augmentations ?? 0)
|
|
}
|
|
/>
|
|
)}
|
|
</TableCell>
|
|
</TableRow>
|
|
|
|
<TableRow>
|
|
<TableCell>Factions</TableCell>
|
|
<TableCell>{currentData.playerData?.factions}</TableCell>
|
|
<TableCell>{importData.playerData?.factions}</TableCell>
|
|
<TableCell>
|
|
{importData.playerData?.factions !== currentData.playerData?.factions && (
|
|
<ComparisonIcon
|
|
isBetter={(importData.playerData?.factions ?? 0) > (currentData.playerData?.factions ?? 0)}
|
|
/>
|
|
)}
|
|
</TableCell>
|
|
</TableRow>
|
|
<TableRow>
|
|
<TableCell>Achievements</TableCell>
|
|
<TableCell>{currentData.playerData?.achievements}</TableCell>
|
|
<TableCell>{importData.playerData?.achievements}</TableCell>
|
|
<TableCell>
|
|
{importData.playerData?.achievements !== currentData.playerData?.achievements && (
|
|
<ComparisonIcon
|
|
isBetter={(importData.playerData?.achievements ?? 0) > (currentData.playerData?.achievements ?? 0)}
|
|
/>
|
|
)}
|
|
</TableCell>
|
|
</TableRow>
|
|
|
|
<TableRow>
|
|
<TableCell>Source Files</TableCell>
|
|
<TableCell>{currentData.playerData?.sourceFiles}</TableCell>
|
|
<TableCell>{importData.playerData?.sourceFiles}</TableCell>
|
|
<TableCell>
|
|
{importData.playerData?.sourceFiles !== currentData.playerData?.sourceFiles && (
|
|
<ComparisonIcon
|
|
isBetter={(importData.playerData?.sourceFiles ?? 0) > (currentData.playerData?.sourceFiles ?? 0)}
|
|
/>
|
|
)}
|
|
</TableCell>
|
|
</TableRow>
|
|
|
|
<TableRow>
|
|
<TableCell>BitNode</TableCell>
|
|
<TableCell>
|
|
{currentData.playerData?.bitNode}-{currentData.playerData?.bitNodeLevel}
|
|
</TableCell>
|
|
<TableCell>
|
|
{importData.playerData?.bitNode}-{importData.playerData?.bitNodeLevel}
|
|
</TableCell>
|
|
<TableCell></TableCell>
|
|
</TableRow>
|
|
</TableBody>
|
|
</Table>
|
|
</TableContainer>
|
|
|
|
<Box sx={{ display: "flex", justifyContent: "flex-end" }}>
|
|
<ButtonGroup>
|
|
<Button onClick={handleGoBack} sx={{ my: 2 }} startIcon={<ArrowBackIcon />} color="secondary">
|
|
Take me back!
|
|
</Button>
|
|
<Button
|
|
onClick={() => setImportModalOpen(true)}
|
|
sx={{ my: 2 }}
|
|
startIcon={<DirectionsRunIcon />}
|
|
color="warning"
|
|
>
|
|
Proceed with import
|
|
</Button>
|
|
</ButtonGroup>
|
|
<ConfirmationModal
|
|
open={importModalOpen}
|
|
onClose={() => setImportModalOpen(false)}
|
|
onConfirm={handleImport}
|
|
confirmationText={
|
|
<>
|
|
Importing new save game data will <strong>completely wipe</strong> the current game data!
|
|
<br />
|
|
</>
|
|
}
|
|
/>
|
|
</Box>
|
|
</Box>
|
|
);
|
|
}
|