mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-22 15:43:49 +01:00
commit
997f20aa68
@ -1,3 +1,3 @@
|
|||||||
last 4 versions
|
last 8 versions
|
||||||
not dead
|
not dead
|
||||||
not ie <= 11
|
not ie <= 11
|
||||||
|
@ -353,6 +353,7 @@ module.exports = {
|
|||||||
"no-useless-constructor": [
|
"no-useless-constructor": [
|
||||||
"off", // Valid for typescript due to property ctor shorthand
|
"off", // Valid for typescript due to property ctor shorthand
|
||||||
],
|
],
|
||||||
|
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
|
||||||
"@typescript-eslint/ban-ts-comment": "off",
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
"@typescript-eslint/ban-ts-ignore": "off",
|
"@typescript-eslint/ban-ts-ignore": "off",
|
||||||
"@typescript-eslint/camelcase": "off",
|
"@typescript-eslint/camelcase": "off",
|
||||||
|
11
dist/bitburner.d.ts
vendored
11
dist/bitburner.d.ts
vendored
@ -2101,7 +2101,7 @@ export declare interface Hacknet {
|
|||||||
* // NS1:
|
* // NS1:
|
||||||
* var upgradeName = "Sell for Corporation Funds";
|
* var upgradeName = "Sell for Corporation Funds";
|
||||||
* if (hacknet.numHashes() > hacknet.hashCost(upgradeName)) {
|
* if (hacknet.numHashes() > hacknet.hashCost(upgradeName)) {
|
||||||
* hacknet.spendHashes(upgName);
|
* hacknet.spendHashes(upgradeName);
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
* @example
|
* @example
|
||||||
@ -2109,7 +2109,7 @@ export declare interface Hacknet {
|
|||||||
* // NS2:
|
* // NS2:
|
||||||
* const upgradeName = "Sell for Corporation Funds";
|
* const upgradeName = "Sell for Corporation Funds";
|
||||||
* if (ns.hacknet.numHashes() > ns.hacknet.hashCost(upgradeName)) {
|
* if (ns.hacknet.numHashes() > ns.hacknet.hashCost(upgradeName)) {
|
||||||
* ns.hacknet.spendHashes(upgName);
|
* ns.hacknet.spendHashes(upgradeName);
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
* @param upgName - Name of the upgrade of Hacknet Node.
|
* @param upgName - Name of the upgrade of Hacknet Node.
|
||||||
@ -2825,9 +2825,10 @@ export declare interface NS extends Singularity {
|
|||||||
* Returns the security increase that would occur if a hack with this many threads happened.
|
* Returns the security increase that would occur if a hack with this many threads happened.
|
||||||
*
|
*
|
||||||
* @param threads - Amount of threads that will be used.
|
* @param threads - Amount of threads that will be used.
|
||||||
|
* @param hostname - Hostname of the target server. The number of threads is limited to the number needed to hack the servers maximum amount of money.
|
||||||
* @returns The security increase.
|
* @returns The security increase.
|
||||||
*/
|
*/
|
||||||
hackAnalyzeSecurity(threads: number): number;
|
hackAnalyzeSecurity(threads: number, hostname?: string): number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the chance of successfully hacking a server.
|
* Get the chance of successfully hacking a server.
|
||||||
@ -4671,8 +4672,10 @@ export declare interface Office {
|
|||||||
maxMor: number;
|
maxMor: number;
|
||||||
/** Name of all the employees */
|
/** Name of all the employees */
|
||||||
employees: string[];
|
employees: string[];
|
||||||
/** Positions of the employees */
|
/** Production of the employees */
|
||||||
employeeProd: EmployeeJobs;
|
employeeProd: EmployeeJobs;
|
||||||
|
/** Positions of the employees */
|
||||||
|
employeeJobs: EmployeeJobs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
4
dist/main.bundle.js
vendored
4
dist/main.bundle.js
vendored
File diff suppressed because one or more lines are too long
2
dist/main.bundle.js.map
vendored
2
dist/main.bundle.js.map
vendored
File diff suppressed because one or more lines are too long
42
dist/vendor.bundle.js
vendored
42
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
2
dist/vendor.bundle.js.map
vendored
2
dist/vendor.bundle.js.map
vendored
File diff suppressed because one or more lines are too long
@ -73,5 +73,5 @@
|
|||||||
<link rel="shortcut icon" href="favicon.ico"></head>
|
<link rel="shortcut icon" href="favicon.ico"></head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"/>
|
<div id="root"/>
|
||||||
<script type="text/javascript" src="dist/vendor.bundle.js"></script><script type="text/javascript" src="main.bundle.js"></script></body>
|
<script type="text/javascript" src="dist/vendor.bundle.js"></script><script type="text/javascript" src="dist/main.bundle.js"></script></body>
|
||||||
</html>
|
</html>
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
26
package-lock.json
generated
26
package-lock.json
generated
@ -5965,13 +5965,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001265",
|
"version": "1.0.30001328",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001265.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001328.tgz",
|
||||||
"integrity": "sha512-YzBnspggWV5hep1m9Z6sZVLOt7vrju8xWooFAgN6BA5qvy98qPAPb7vNUzypFaoh2pb3vlfzbDO8tB57UPGbtw==",
|
"integrity": "sha512-Ue55jHkR/s4r00FLNiX+hGMMuwml/QGqqzVeMQ5thUewznU2EdULFvI3JR7JJid6OrjJNfFvHY2G2dIjmRaDDQ==",
|
||||||
"funding": {
|
"funding": [
|
||||||
"type": "opencollective",
|
{
|
||||||
"url": "https://opencollective.com/browserslist"
|
"type": "opencollective",
|
||||||
}
|
"url": "https://opencollective.com/browserslist"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tidelift",
|
||||||
|
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"node_modules/caseless": {
|
"node_modules/caseless": {
|
||||||
"version": "0.12.0",
|
"version": "0.12.0",
|
||||||
@ -26994,9 +27000,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"caniuse-lite": {
|
"caniuse-lite": {
|
||||||
"version": "1.0.30001265",
|
"version": "1.0.30001328",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001265.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001328.tgz",
|
||||||
"integrity": "sha512-YzBnspggWV5hep1m9Z6sZVLOt7vrju8xWooFAgN6BA5qvy98qPAPb7vNUzypFaoh2pb3vlfzbDO8tB57UPGbtw=="
|
"integrity": "sha512-Ue55jHkR/s4r00FLNiX+hGMMuwml/QGqqzVeMQ5thUewznU2EdULFvI3JR7JJid6OrjJNfFvHY2G2dIjmRaDDQ=="
|
||||||
},
|
},
|
||||||
"caseless": {
|
"caseless": {
|
||||||
"version": "0.12.0",
|
"version": "0.12.0",
|
||||||
|
@ -115,15 +115,15 @@
|
|||||||
"start:container": "webpack-dev-server --progress --env.devServer --mode development --env.runInContainer",
|
"start:container": "webpack-dev-server --progress --env.devServer --mode development --env.runInContainer",
|
||||||
"build": "webpack --mode production",
|
"build": "webpack --mode production",
|
||||||
"build:dev": "webpack --mode development",
|
"build:dev": "webpack --mode development",
|
||||||
"lint": "eslint --fix . --ext js,jsx,ts,tsx",
|
"lint": "eslint --fix --ext js,jsx,ts,tsx --max-warnings 0 .",
|
||||||
"lint:report": "eslint --ext js,jsx,ts,tsx .",
|
"lint:report": "eslint --ext js,jsx,ts,tsx --max-warnings 0 .",
|
||||||
"preinstall": "node ./tools/engines-check/engines-check.js",
|
"preinstall": "node ./tools/engines-check/engines-check.js",
|
||||||
"postinstall": "cd electron && npm install",
|
"postinstall": "cd electron && npm install",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
"watch": "webpack --watch --mode production",
|
"watch": "webpack --watch --mode production",
|
||||||
"watch:dev": "webpack --watch --mode development",
|
"watch:dev": "webpack --watch --mode development",
|
||||||
"electron": "sh ./package.sh",
|
"electron": "sh ./tools/package-electron.sh",
|
||||||
"electron:packager": "electron-packager .package bitburner --all --out .build --overwrite --icon .package/icon.png --no-prune",
|
"electron:packager": "electron-packager .package bitburner --all --out .build --overwrite --icon .package/icon.png --no-prune",
|
||||||
"electron:packager-all": "electron-packager .package bitburner --all --out .build --overwrite --icon .package/icon.png",
|
"electron:packager-all": "electron-packager .package bitburner --all --out .build --overwrite --icon .package/icon.png",
|
||||||
"electron:packager-win": "electron-packager .package bitburner --platform win32 --arch x64 --out .build --overwrite --icon .package/icon.png",
|
"electron:packager-win": "electron-packager .package bitburner --platform win32 --arch x64 --out .build --overwrite --icon .package/icon.png",
|
||||||
|
31
package.sh
31
package.sh
@ -1,31 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# Clear out any files remaining from old builds
|
|
||||||
rm -rf .package
|
|
||||||
|
|
||||||
mkdir -p .package/dist/src/ThirdParty || true
|
|
||||||
mkdir -p .package/src/ThirdParty || true
|
|
||||||
mkdir -p .package/node_modules || true
|
|
||||||
|
|
||||||
cp index.html .package
|
|
||||||
cp -r electron/* .package
|
|
||||||
cp -r dist/ext .package/dist
|
|
||||||
cp -r dist/icons .package/dist
|
|
||||||
cp -r dist/images .package/dist
|
|
||||||
|
|
||||||
# The js files.
|
|
||||||
cp dist/vendor.bundle.js .package/dist/vendor.bundle.js
|
|
||||||
cp main.bundle.js .package/main.bundle.js
|
|
||||||
|
|
||||||
# Source maps
|
|
||||||
cp dist/vendor.bundle.js.map .package/dist/vendor.bundle.js.map
|
|
||||||
cp main.bundle.js.map .package/main.bundle.js.map
|
|
||||||
|
|
||||||
# Install electron sub-dependencies
|
|
||||||
cd electron
|
|
||||||
npm install
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
BUILD_PLATFORM="${1:-"all"}"
|
|
||||||
# And finally build the app.
|
|
||||||
npm run electron:packager-$BUILD_PLATFORM
|
|
@ -21,6 +21,7 @@ import {
|
|||||||
initNeuroFluxGovernor,
|
initNeuroFluxGovernor,
|
||||||
initUnstableCircadianModulator,
|
initUnstableCircadianModulator,
|
||||||
} from "./AugmentationCreator";
|
} from "./AugmentationCreator";
|
||||||
|
import { Router } from "../ui/GameRoot";
|
||||||
|
|
||||||
export function AddToAugmentations(aug: Augmentation): void {
|
export function AddToAugmentations(aug: Augmentation): void {
|
||||||
const name = aug.name;
|
const name = aug.name;
|
||||||
@ -194,6 +195,7 @@ function installAugmentations(force?: boolean): boolean {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
prestigeAugmentation();
|
prestigeAugmentation();
|
||||||
|
Router.toTerminal();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import React, { useState, useEffect } from "react";
|
|||||||
import { InstalledAugmentations } from "./InstalledAugmentations";
|
import { InstalledAugmentations } from "./InstalledAugmentations";
|
||||||
import { PlayerMultipliers } from "./PlayerMultipliers";
|
import { PlayerMultipliers } from "./PlayerMultipliers";
|
||||||
import { PurchasedAugmentations } from "./PurchasedAugmentations";
|
import { PurchasedAugmentations } from "./PurchasedAugmentations";
|
||||||
import { SourceFiles } from "./SourceFiles";
|
import { SourceFilesElement } from "./SourceFiles";
|
||||||
|
|
||||||
import { canGetBonus } from "../../ExportBonus";
|
import { canGetBonus } from "../../ExportBonus";
|
||||||
import { use } from "../../ui/Context";
|
import { use } from "../../ui/Context";
|
||||||
@ -16,8 +16,55 @@ import Typography from "@mui/material/Typography";
|
|||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
import Tooltip from "@mui/material/Tooltip";
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
|
import Paper from "@mui/material/Paper";
|
||||||
|
import Container from "@mui/material/Container";
|
||||||
import { Settings } from "../../Settings/Settings";
|
import { Settings } from "../../Settings/Settings";
|
||||||
import { ConfirmationModal } from "../../ui/React/ConfirmationModal";
|
import { ConfirmationModal } from "../../ui/React/ConfirmationModal";
|
||||||
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import { AugmentationNames } from "../data/AugmentationNames";
|
||||||
|
import { Augmentations } from "../Augmentations";
|
||||||
|
import { CONSTANTS } from "../../Constants";
|
||||||
|
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||||
|
import { Info } from "@mui/icons-material";
|
||||||
|
|
||||||
|
interface NFGDisplayProps {
|
||||||
|
player: IPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NeuroFluxDisplay = ({ player }: NFGDisplayProps): React.ReactElement => {
|
||||||
|
const level = player.augmentations.find((e) => e.name === AugmentationNames.NeuroFluxGovernor)?.level ?? 0;
|
||||||
|
|
||||||
|
return level > 0 ? (
|
||||||
|
<Paper sx={{ p: 1 }}>
|
||||||
|
<Typography variant="h5" color={Settings.theme.info}>
|
||||||
|
NeuroFlux Governor - Level {level}
|
||||||
|
</Typography>
|
||||||
|
<Typography color={Settings.theme.info}>{Augmentations[AugmentationNames.NeuroFluxGovernor].stats}</Typography>
|
||||||
|
</Paper>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface EntropyDisplayProps {
|
||||||
|
player: IPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EntropyDisplay = ({ player }: EntropyDisplayProps): React.ReactElement => {
|
||||||
|
return player.entropy > 0 ? (
|
||||||
|
<Paper sx={{ p: 1 }}>
|
||||||
|
<Typography variant="h5" color={Settings.theme.error}>
|
||||||
|
Entropy Virus - Level {player.entropy}
|
||||||
|
</Typography>
|
||||||
|
<Typography color={Settings.theme.error}>
|
||||||
|
<b>All multipliers decreased by:</b> {formatNumber((1 - CONSTANTS.EntropyEffect ** player.entropy) * 100, 3)}%
|
||||||
|
(multiplicative)
|
||||||
|
</Typography>
|
||||||
|
</Paper>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
exportGameFn: () => void;
|
exportGameFn: () => void;
|
||||||
@ -55,81 +102,113 @@ export function AugmentationsRoot(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Container disableGutters maxWidth="lg" sx={{ mx: 0 }}>
|
||||||
<Typography variant="h4">Augmentations</Typography>
|
<Typography variant="h4">Augmentations</Typography>
|
||||||
<Box mx={2}>
|
<Box sx={{ mb: 1 }}>
|
||||||
<Typography>
|
<Paper sx={{ p: 1 }}>
|
||||||
Below is a list of all Augmentations you have purchased but not yet installed. Click the button below to
|
<Typography variant="h5" color="primary" sx={{ display: "flex", alignItems: "center", flexWrap: "wrap" }}>
|
||||||
install them.
|
Purchased Augmentations
|
||||||
</Typography>
|
<Tooltip
|
||||||
<Typography>WARNING: Installing your Augmentations resets most of your progress, including:</Typography>
|
title={
|
||||||
<br />
|
<>
|
||||||
<Typography>- Stats/Skill levels and Experience</Typography>
|
<Typography>
|
||||||
<Typography>- Money</Typography>
|
Below is a list of all Augmentations you have purchased but not yet installed. Click the button
|
||||||
<Typography>- Scripts on every computer but your home computer</Typography>
|
below to install them.
|
||||||
<Typography>- Purchased servers</Typography>
|
</Typography>
|
||||||
<Typography>- Hacknet Nodes</Typography>
|
<Typography>
|
||||||
<Typography>- Faction/Company reputation</Typography>
|
WARNING: Installing your Augmentations resets most of your progress, including:
|
||||||
<Typography>- Stocks</Typography>
|
</Typography>
|
||||||
<br />
|
<br />
|
||||||
<Typography>
|
<Typography>- Stats/Skill levels and Experience</Typography>
|
||||||
Installing Augmentations lets you start over with the perks and benefits granted by all of the Augmentations
|
<Typography>- Money</Typography>
|
||||||
you have ever installed. Also, you will keep any scripts and RAM/Core upgrades on your home computer (but you
|
<Typography>- Scripts on every computer but your home computer</Typography>
|
||||||
will lose all programs besides NUKE.exe)
|
<Typography>- Purchased servers</Typography>
|
||||||
</Typography>
|
<Typography>- Hacknet Nodes</Typography>
|
||||||
|
<Typography>- Faction/Company reputation</Typography>
|
||||||
|
<Typography>- Stocks</Typography>
|
||||||
|
<br />
|
||||||
|
<Typography>
|
||||||
|
Installing Augmentations lets you start over with the perks and benefits granted by all of the
|
||||||
|
Augmentations you have ever installed. Also, you will keep any scripts and RAM/Core upgrades on your
|
||||||
|
home computer (but you will lose all programs besides NUKE.exe)
|
||||||
|
</Typography>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Info sx={{ ml: 1, mb: 0.5 }} color="info" />
|
||||||
|
</Tooltip>
|
||||||
|
</Typography>
|
||||||
|
<ConfirmationModal
|
||||||
|
open={installOpen}
|
||||||
|
onClose={() => setInstallOpen(false)}
|
||||||
|
onConfirm={props.installAugmentationsFn}
|
||||||
|
confirmationText={
|
||||||
|
<>
|
||||||
|
Installing will reset
|
||||||
|
<br />
|
||||||
|
<br />- money
|
||||||
|
<br />- skill / experience
|
||||||
|
<br />- every server except home
|
||||||
|
<br />- factions and reputation
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
You will keep:
|
||||||
|
<br />
|
||||||
|
<br />- All scripts on home
|
||||||
|
<br />- home ram and cores
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
It is recommended to install several Augmentations at once. Preferably everything from any faction of
|
||||||
|
your choosing.
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Box sx={{ display: "grid", width: "100%", gridTemplateColumns: "1fr 1fr" }}>
|
||||||
|
<Tooltip title={<Typography>'I never asked for this'</Typography>}>
|
||||||
|
<span>
|
||||||
|
<Button sx={{ width: "100%" }} disabled={player.queuedAugmentations.length === 0} onClick={doInstall}>
|
||||||
|
Install Augmentations
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title={<Typography>It's always a good idea to backup/export your save!</Typography>}>
|
||||||
|
<Button sx={{ width: "100%" }} onClick={doExport} color="error">
|
||||||
|
Backup Save {exportBonusStr()}
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
|
{player.queuedAugmentations.length > 0 ? (
|
||||||
|
<Box sx={{ display: "grid", gridTemplateColumns: "1fr 3fr" }}>
|
||||||
|
<PurchasedAugmentations />
|
||||||
|
<PlayerMultipliers />
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
|
<Paper sx={{ p: 1 }}>
|
||||||
|
<Typography>No Augmentations have been purchased yet</Typography>
|
||||||
|
</Paper>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<Typography variant="h4" color="primary">
|
|
||||||
Purchased Augmentations
|
<Box
|
||||||
</Typography>
|
sx={{
|
||||||
<Box mx={2}>
|
my: 1,
|
||||||
<Tooltip title={<Typography>'I never asked for this'</Typography>}>
|
display: "grid",
|
||||||
<span>
|
gridTemplateColumns: `repeat(${
|
||||||
<Button disabled={player.queuedAugmentations.length === 0} onClick={doInstall}>
|
+!!((player.augmentations.find((e) => e.name === AugmentationNames.NeuroFluxGovernor)?.level ?? 0) > 0) +
|
||||||
Install Augmentations
|
+!!(player.entropy > 0)
|
||||||
</Button>
|
}, 1fr)`,
|
||||||
</span>
|
gap: 1,
|
||||||
</Tooltip>
|
}}
|
||||||
<ConfirmationModal
|
>
|
||||||
open={installOpen}
|
<NeuroFluxDisplay player={player} />
|
||||||
onClose={() => setInstallOpen(false)}
|
<EntropyDisplay player={player} />
|
||||||
onConfirm={props.installAugmentationsFn}
|
|
||||||
confirmationText={
|
|
||||||
<>
|
|
||||||
Installing will reset
|
|
||||||
<br />
|
|
||||||
<br />- money
|
|
||||||
<br />- skill / experience
|
|
||||||
<br />- every server except home
|
|
||||||
<br />- factions and reputation
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
You will keep:
|
|
||||||
<br />
|
|
||||||
<br />- All scripts on home
|
|
||||||
<br />- home ram and cores
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
It is recommended to install several Augmentations at once. Preferably everything from any faction of your
|
|
||||||
choosing.
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Tooltip title={<Typography>It's always a good idea to backup/export your save!</Typography>}>
|
|
||||||
<Button sx={{ mx: 2 }} onClick={doExport} color="error">
|
|
||||||
Backup Save {exportBonusStr()}
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
|
||||||
<PurchasedAugmentations />
|
|
||||||
</Box>
|
</Box>
|
||||||
<Typography variant="h4">Installed Augmentations</Typography>
|
|
||||||
<Box mx={2}>
|
<Box>
|
||||||
<Typography>
|
|
||||||
List of all Augmentations that have been installed. You have gained the effects of these.
|
|
||||||
</Typography>
|
|
||||||
<InstalledAugmentations />
|
<InstalledAugmentations />
|
||||||
</Box>
|
</Box>
|
||||||
<PlayerMultipliers />
|
<SourceFilesElement />
|
||||||
<SourceFiles />
|
</Container>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -5,28 +5,23 @@
|
|||||||
* It also contains 'configuration' buttons that allow you to change how the
|
* It also contains 'configuration' buttons that allow you to change how the
|
||||||
* Augs/SF's are displayed
|
* Augs/SF's are displayed
|
||||||
*/
|
*/
|
||||||
|
import { Box, ListItemButton, Paper, Typography } from "@mui/material";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
import List from "@mui/material/List";
|
||||||
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
|
||||||
import { AugmentationAccordion } from "../../ui/React/AugmentationAccordion";
|
|
||||||
import { Augmentations } from "../Augmentations";
|
|
||||||
import { AugmentationNames } from "../data/AugmentationNames";
|
|
||||||
|
|
||||||
import { Settings } from "../../Settings/Settings";
|
import { Settings } from "../../Settings/Settings";
|
||||||
import { use } from "../../ui/Context";
|
import { use } from "../../ui/Context";
|
||||||
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
|
import { Augmentations } from "../Augmentations";
|
||||||
import Button from "@mui/material/Button";
|
import { AugmentationNames } from "../data/AugmentationNames";
|
||||||
import Tooltip from "@mui/material/Tooltip";
|
|
||||||
import List from "@mui/material/List";
|
|
||||||
import { ExpandLess, ExpandMore } from "@mui/icons-material";
|
|
||||||
import { Box, Paper, ListItemButton, ListItemText, Typography, Collapse } from "@mui/material";
|
|
||||||
import { CONSTANTS } from "../../Constants";
|
|
||||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
|
||||||
|
|
||||||
export function InstalledAugmentations(): React.ReactElement {
|
export function InstalledAugmentations(): React.ReactElement {
|
||||||
const setRerender = useState(true)[1];
|
const setRerender = useState(true)[1];
|
||||||
const player = use.Player();
|
const player = use.Player();
|
||||||
|
const sourceAugs = player.augmentations.slice().filter((aug) => aug.name !== AugmentationNames.NeuroFluxGovernor);
|
||||||
|
|
||||||
const sourceAugs = player.augmentations.slice();
|
const [selectedAug, setSelectedAug] = useState(sourceAugs[0]);
|
||||||
|
|
||||||
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
|
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
|
||||||
sourceAugs.sort((aug1, aug2) => {
|
sourceAugs.sort((aug1, aug2) => {
|
||||||
@ -49,59 +44,60 @@ export function InstalledAugmentations(): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Box sx={{ width: "100%" }}>
|
||||||
<Tooltip title={"Sorts the Augmentations alphabetically in numeral order"}>
|
<Paper sx={{ p: 1 }}>
|
||||||
<Button onClick={sortInOrder}>Sort in Order</Button>
|
<Typography variant="h5">Installed Augmentations</Typography>
|
||||||
</Tooltip>
|
<Box sx={{ display: "grid", gridTemplateColumns: "1fr 1fr" }}>
|
||||||
<Tooltip title={"Sorts the Augmentations based on when you acquired them (same as default)"}>
|
<Tooltip title={"Sorts the Augmentations alphabetically in numeral order"}>
|
||||||
<Button sx={{ mx: 2 }} onClick={sortByAcquirementTime}>
|
<Button sx={{ width: "100%" }} onClick={sortInOrder}>
|
||||||
Sort by Acquirement Time
|
Sort in Order
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<List dense>
|
<Tooltip title={"Sorts the Augmentations based on when you acquired them (same as default)"}>
|
||||||
{player.entropy > 0 &&
|
<Button sx={{ width: "100%" }} onClick={sortByAcquirementTime}>
|
||||||
(() => {
|
Sort by Time of Acquirement
|
||||||
const [open, setOpen] = useState(false);
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
return (
|
</Box>
|
||||||
<Box component={Paper}>
|
</Paper>
|
||||||
<ListItemButton onClick={() => setOpen((old) => !old)}>
|
{sourceAugs.length > 0 ? (
|
||||||
<ListItemText
|
<Paper sx={{ display: "grid", gridTemplateColumns: "1fr 3fr" }}>
|
||||||
primary={
|
<Box>
|
||||||
<Typography color={Settings.theme.hp} style={{ whiteSpace: "pre-wrap" }}>
|
<List sx={{ height: 400, overflowY: "scroll", borderRight: `1px solid ${Settings.theme.welllight}` }}>
|
||||||
Entropy Virus - Level {player.entropy}
|
{sourceAugs.map((k, i) => (
|
||||||
</Typography>
|
<ListItemButton key={i + 1} onClick={() => setSelectedAug(k)} selected={selectedAug === k}>
|
||||||
}
|
<Typography>{k.name}</Typography>
|
||||||
/>
|
|
||||||
{open ? (
|
|
||||||
<ExpandLess sx={{ color: Settings.theme.hp }} />
|
|
||||||
) : (
|
|
||||||
<ExpandMore sx={{ color: Settings.theme.hp }} />
|
|
||||||
)}
|
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
<Collapse in={open} unmountOnExit>
|
))}
|
||||||
<Box m={4}>
|
</List>
|
||||||
<Typography color={Settings.theme.hp}>
|
</Box>
|
||||||
<b>All multipliers decreased by:</b>{" "}
|
<Box sx={{ m: 1 }}>
|
||||||
{formatNumber((1 - CONSTANTS.EntropyEffect ** player.entropy) * 100, 3)}% (multiplicative)
|
<Typography variant="h6" sx={{ display: "flex", alignItems: "center", flexWrap: "wrap" }}>
|
||||||
</Typography>
|
{selectedAug.name}
|
||||||
</Box>
|
</Typography>
|
||||||
</Collapse>
|
<Typography sx={{ maxHeight: 350, overflowY: "scroll" }}>
|
||||||
</Box>
|
{(() => {
|
||||||
);
|
const aug = Augmentations[selectedAug.name];
|
||||||
})()}
|
|
||||||
|
|
||||||
{sourceAugs.map((e) => {
|
const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info;
|
||||||
const aug = Augmentations[e.name];
|
const tooltip = (
|
||||||
|
<>
|
||||||
let level = null;
|
{info}
|
||||||
if (e.name === AugmentationNames.NeuroFluxGovernor) {
|
<br />
|
||||||
level = e.level;
|
<br />
|
||||||
}
|
{aug.stats}
|
||||||
|
</>
|
||||||
return <AugmentationAccordion key={aug.name} aug={aug} level={level} />;
|
);
|
||||||
})}
|
return tooltip;
|
||||||
</List>
|
})()}
|
||||||
</>
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
|
) : (
|
||||||
|
<Paper sx={{ p: 1 }}>
|
||||||
|
<Typography>No Augmentations have been installed yet</Typography>
|
||||||
|
</Paper>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,22 @@
|
|||||||
/**
|
/**
|
||||||
* React component for displaying the player's multipliers on the Augmentation UI page
|
* React component for displaying the player's multipliers on the Augmentation UI page
|
||||||
*/
|
*/
|
||||||
|
import { DoubleArrow } from "@mui/icons-material";
|
||||||
|
import { List, ListItem, ListItemText, Paper, Typography } from "@mui/material";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||||
import { Player } from "../../Player";
|
import { Player } from "../../Player";
|
||||||
|
import { Settings } from "../../Settings/Settings";
|
||||||
|
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
|
||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
import { Augmentations } from "../Augmentations";
|
import { Augmentations } from "../Augmentations";
|
||||||
import { Table, TableCell } from "../../ui/React/Table";
|
|
||||||
import TableBody from "@mui/material/TableBody";
|
|
||||||
import TableRow from "@mui/material/TableRow";
|
|
||||||
import Typography from "@mui/material/Typography";
|
|
||||||
import Box from "@mui/material/Box";
|
|
||||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
|
||||||
|
|
||||||
function calculateAugmentedStats(): any {
|
interface IAugmentedStats {
|
||||||
const augP: any = {};
|
[index: string]: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateAugmentedStats(): IAugmentedStats {
|
||||||
|
const augP: IAugmentedStats = {};
|
||||||
for (const aug of Player.queuedAugmentations) {
|
for (const aug of Player.queuedAugmentations) {
|
||||||
const augObj = Augmentations[aug.name];
|
const augObj = Augmentations[aug.name];
|
||||||
for (const mult of Object.keys(augObj.mults)) {
|
for (const mult of Object.keys(augObj.mults)) {
|
||||||
@ -25,268 +27,249 @@ function calculateAugmentedStats(): any {
|
|||||||
return augP;
|
return augP;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Improvements({ r, m }: { r: number; m: number }): React.ReactElement {
|
interface IBitNodeModifiedStatsProps {
|
||||||
if (r) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<TableCell key="2">
|
|
||||||
<Typography> {"=>"} </Typography>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell key="3">
|
|
||||||
<Typography>
|
|
||||||
{numeralWrapper.formatPercentage(r)} <BN5Stat base={r} mult={m} />
|
|
||||||
</Typography>
|
|
||||||
</TableCell>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IBN5StatsProps {
|
|
||||||
base: number;
|
base: number;
|
||||||
mult: number;
|
mult: number;
|
||||||
|
color: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function BN5Stat(props: IBN5StatsProps): React.ReactElement {
|
function BitNodeModifiedStats(props: IBitNodeModifiedStatsProps): React.ReactElement {
|
||||||
if (props.mult === 1) return <></>;
|
// If player doesn't have SF5 or if the property isn't affected by BitNode mults
|
||||||
return <>({numeralWrapper.formatPercentage(props.base * props.mult)})</>;
|
if (props.mult === 1 || SourceFileFlags[5] === 0)
|
||||||
}
|
return <Typography color={props.color}>{numeralWrapper.formatPercentage(props.base)}</Typography>;
|
||||||
|
|
||||||
function MultiplierTable({ rows }: { rows: [string, number, number, number][] }): React.ReactElement {
|
|
||||||
return (
|
return (
|
||||||
<Table size="small" padding="none">
|
<Typography color={props.color}>
|
||||||
<TableBody>
|
<span style={{ opacity: 0.5 }}>{numeralWrapper.formatPercentage(props.base)}</span>{" "}
|
||||||
{rows.map((r: any) => (
|
{numeralWrapper.formatPercentage(props.base * props.mult)}
|
||||||
<TableRow key={r[0]}>
|
</Typography>
|
||||||
<TableCell key="0">
|
|
||||||
<Typography noWrap>{r[0]} multiplier: </Typography>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell key="1" style={{ textAlign: "right" }}>
|
|
||||||
<Typography noWrap>
|
|
||||||
{numeralWrapper.formatPercentage(r[1])} <BN5Stat base={r[1]} mult={r[3]} />
|
|
||||||
</Typography>
|
|
||||||
</TableCell>
|
|
||||||
<Improvements r={r[2]} m={r[3]} />
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MultiplierListItemData = [
|
||||||
|
multiplier: string,
|
||||||
|
currentValue: number,
|
||||||
|
augmentedValue: number,
|
||||||
|
bitNodeMultiplier: number,
|
||||||
|
color: string,
|
||||||
|
];
|
||||||
|
|
||||||
|
interface IMultiplierListProps {
|
||||||
|
rows: MultiplierListItemData[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function MultiplierList(props: IMultiplierListProps): React.ReactElement {
|
||||||
|
const listItems = props.rows
|
||||||
|
.map((data) => {
|
||||||
|
const [multiplier, currentValue, augmentedValue, bitNodeMultiplier, color] = data;
|
||||||
|
|
||||||
|
if (!isNaN(augmentedValue)) {
|
||||||
|
return (
|
||||||
|
<ListItem key={multiplier} disableGutters sx={{ py: 0 }}>
|
||||||
|
<ListItemText
|
||||||
|
sx={{ my: 0.1 }}
|
||||||
|
primary={
|
||||||
|
<Typography color={color}>
|
||||||
|
<b>{multiplier}</b>
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
secondary={
|
||||||
|
<span style={{ display: "flex", alignItems: "center", flexWrap: "wrap" }}>
|
||||||
|
<BitNodeModifiedStats base={currentValue} mult={bitNodeMultiplier} color={color} />
|
||||||
|
<DoubleArrow fontSize="small" color="success" sx={{ mb: 0.5, mx: 1 }} />
|
||||||
|
<BitNodeModifiedStats base={augmentedValue} mult={bitNodeMultiplier} color={Settings.theme.success} />
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
disableTypography
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
})
|
||||||
|
.filter((i) => i !== undefined);
|
||||||
|
|
||||||
|
return listItems.length > 0 ? <List disablePadding>{listItems}</List> : <></>;
|
||||||
|
}
|
||||||
|
|
||||||
export function PlayerMultipliers(): React.ReactElement {
|
export function PlayerMultipliers(): React.ReactElement {
|
||||||
const mults = calculateAugmentedStats();
|
const mults = calculateAugmentedStats();
|
||||||
|
|
||||||
function BladeburnerMults(): React.ReactElement {
|
// Column data is a bit janky, so it's set up here to allow for
|
||||||
if (!Player.canAccessBladeburner()) return <></>;
|
// easier logic in setting up the layout
|
||||||
return (
|
const leftColData: MultiplierListItemData[] = [
|
||||||
<>
|
...[
|
||||||
<MultiplierTable
|
["Hacking Chance ", Player.hacking_chance_mult, Player.hacking_chance_mult * mults.hacking_chance_mult, 1],
|
||||||
rows={[
|
["Hacking Speed ", Player.hacking_speed_mult, Player.hacking_speed_mult * mults.hacking_speed_mult, 1],
|
||||||
[
|
["Hacking Money ", Player.hacking_money_mult, Player.hacking_money_mult * mults.hacking_money_mult, 1],
|
||||||
"Bladeburner Success Chance",
|
["Hacking Growth ", Player.hacking_grow_mult, Player.hacking_grow_mult * mults.hacking_grow_mult, 1],
|
||||||
Player.bladeburner_success_chance_mult,
|
[
|
||||||
Player.bladeburner_success_chance_mult * mults.bladeburner_success_chance_mult,
|
"Hacking Level ",
|
||||||
1,
|
Player.hacking_mult,
|
||||||
],
|
Player.hacking_mult * mults.hacking_mult,
|
||||||
[
|
BitNodeMultipliers.HackingLevelMultiplier,
|
||||||
"Bladeburner Max Stamina",
|
],
|
||||||
Player.bladeburner_max_stamina_mult,
|
[
|
||||||
Player.bladeburner_max_stamina_mult * mults.bladeburner_max_stamina_mult,
|
"Hacking Experience ",
|
||||||
1,
|
Player.hacking_exp_mult,
|
||||||
],
|
Player.hacking_exp_mult * mults.hacking_exp_mult,
|
||||||
[
|
BitNodeMultipliers.HackExpGain,
|
||||||
"Bladeburner Stamina Gain",
|
],
|
||||||
Player.bladeburner_stamina_gain_mult,
|
].map((data): MultiplierListItemData => (data as any).concat([Settings.theme.hack])),
|
||||||
Player.bladeburner_stamina_gain_mult * mults.bladeburner_stamina_gain_mult,
|
...[
|
||||||
1,
|
[
|
||||||
],
|
"Strength Level ",
|
||||||
[
|
Player.strength_mult,
|
||||||
"Bladeburner Field Analysis",
|
Player.strength_mult * mults.strength_mult,
|
||||||
Player.bladeburner_analysis_mult,
|
BitNodeMultipliers.StrengthLevelMultiplier,
|
||||||
Player.bladeburner_analysis_mult * mults.bladeburner_analysis_mult,
|
],
|
||||||
1,
|
["Strength Experience ", Player.strength_exp_mult, Player.strength_exp_mult * mults.strength_exp_mult, 1],
|
||||||
],
|
[
|
||||||
]}
|
"Defense Level ",
|
||||||
/>
|
Player.defense_mult,
|
||||||
<br />
|
Player.defense_mult * mults.defense_mult,
|
||||||
</>
|
BitNodeMultipliers.DefenseLevelMultiplier,
|
||||||
|
],
|
||||||
|
["Defense Experience ", Player.defense_exp_mult, Player.defense_exp_mult * mults.defense_exp_mult, 1],
|
||||||
|
[
|
||||||
|
"Dexterity Level ",
|
||||||
|
Player.dexterity_mult,
|
||||||
|
Player.dexterity_mult * mults.dexterity_mult,
|
||||||
|
BitNodeMultipliers.DexterityLevelMultiplier,
|
||||||
|
],
|
||||||
|
["Dexterity Experience ", Player.dexterity_exp_mult, Player.dexterity_exp_mult * mults.dexterity_exp_mult, 1],
|
||||||
|
[
|
||||||
|
"Agility Level ",
|
||||||
|
Player.agility_mult,
|
||||||
|
Player.agility_mult * mults.agility_mult,
|
||||||
|
BitNodeMultipliers.AgilityLevelMultiplier,
|
||||||
|
],
|
||||||
|
["Agility Experience ", Player.agility_exp_mult, Player.agility_exp_mult * mults.agility_exp_mult, 1],
|
||||||
|
].map((data): MultiplierListItemData => (data as any).concat([Settings.theme.combat])),
|
||||||
|
[
|
||||||
|
"Charisma Level ",
|
||||||
|
Player.charisma_mult,
|
||||||
|
Player.charisma_mult * mults.charisma_mult,
|
||||||
|
BitNodeMultipliers.CharismaLevelMultiplier,
|
||||||
|
Settings.theme.cha,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Charisma Experience ",
|
||||||
|
Player.charisma_exp_mult,
|
||||||
|
Player.charisma_exp_mult * mults.charisma_exp_mult,
|
||||||
|
1,
|
||||||
|
Settings.theme.cha,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
const rightColData: MultiplierListItemData[] = [
|
||||||
|
...[
|
||||||
|
[
|
||||||
|
"Hacknet Node production ",
|
||||||
|
Player.hacknet_node_money_mult,
|
||||||
|
Player.hacknet_node_money_mult * mults.hacknet_node_money_mult,
|
||||||
|
BitNodeMultipliers.HacknetNodeMoney,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Hacknet Node purchase cost ",
|
||||||
|
Player.hacknet_node_purchase_cost_mult,
|
||||||
|
Player.hacknet_node_purchase_cost_mult * mults.hacknet_node_purchase_cost_mult,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Hacknet Node RAM upgrade cost ",
|
||||||
|
Player.hacknet_node_ram_cost_mult,
|
||||||
|
Player.hacknet_node_ram_cost_mult * mults.hacknet_node_ram_cost_mult,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Hacknet Node Core purchase cost ",
|
||||||
|
Player.hacknet_node_core_cost_mult,
|
||||||
|
Player.hacknet_node_core_cost_mult * mults.hacknet_node_core_cost_mult,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Hacknet Node level upgrade cost ",
|
||||||
|
Player.hacknet_node_level_cost_mult,
|
||||||
|
Player.hacknet_node_level_cost_mult * mults.hacknet_node_level_cost_mult,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
["Company reputation gain ", Player.company_rep_mult, Player.company_rep_mult * mults.company_rep_mult, 1],
|
||||||
|
[
|
||||||
|
"Faction reputation gain ",
|
||||||
|
Player.faction_rep_mult,
|
||||||
|
Player.faction_rep_mult * mults.faction_rep_mult,
|
||||||
|
BitNodeMultipliers.FactionWorkRepGain,
|
||||||
|
],
|
||||||
|
].map((data): MultiplierListItemData => (data as any).concat([Settings.theme.primary])),
|
||||||
|
[
|
||||||
|
"Salary ",
|
||||||
|
Player.work_money_mult,
|
||||||
|
Player.work_money_mult * mults.work_money_mult,
|
||||||
|
BitNodeMultipliers.CompanyWorkMoney,
|
||||||
|
Settings.theme.money,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Crime success ",
|
||||||
|
Player.crime_success_mult,
|
||||||
|
Player.crime_success_mult * mults.crime_success_mult,
|
||||||
|
1,
|
||||||
|
Settings.theme.combat,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Crime money ",
|
||||||
|
Player.crime_money_mult,
|
||||||
|
Player.crime_money_mult * mults.crime_money_mult,
|
||||||
|
BitNodeMultipliers.CrimeMoney,
|
||||||
|
Settings.theme.money,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
if (Player.canAccessBladeburner()) {
|
||||||
|
rightColData.push(
|
||||||
|
...[
|
||||||
|
[
|
||||||
|
"Bladeburner Success Chance",
|
||||||
|
Player.bladeburner_success_chance_mult,
|
||||||
|
Player.bladeburner_success_chance_mult * mults.bladeburner_success_chance_mult,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Bladeburner Max Stamina",
|
||||||
|
Player.bladeburner_max_stamina_mult,
|
||||||
|
Player.bladeburner_max_stamina_mult * mults.bladeburner_max_stamina_mult,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Bladeburner Stamina Gain",
|
||||||
|
Player.bladeburner_stamina_gain_mult,
|
||||||
|
Player.bladeburner_stamina_gain_mult * mults.bladeburner_stamina_gain_mult,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Bladeburner Field Analysis",
|
||||||
|
Player.bladeburner_analysis_mult,
|
||||||
|
Player.bladeburner_analysis_mult * mults.bladeburner_analysis_mult,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
].map((data): MultiplierListItemData => (data as any).concat([Settings.theme.primary])),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasLeftImprovements = +!!(leftColData.filter((item) => item[2] !== 0).length > 0),
|
||||||
|
hasRightImprovements = +!!(rightColData.filter((item) => item[2] !== 0).length > 0);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Paper
|
||||||
<Typography variant="h4">Multipliers</Typography>
|
sx={{
|
||||||
<Box mx={2}>
|
p: 1,
|
||||||
<MultiplierTable
|
maxHeight: 400,
|
||||||
rows={[
|
overflowY: "scroll",
|
||||||
["Hacking Chance ", Player.hacking_chance_mult, Player.hacking_chance_mult * mults.hacking_chance_mult, 1],
|
display: "grid",
|
||||||
["Hacking Speed ", Player.hacking_speed_mult, Player.hacking_speed_mult * mults.hacking_speed_mult, 1],
|
gridTemplateColumns: `repeat(${hasLeftImprovements + hasRightImprovements}, 1fr)`,
|
||||||
["Hacking Money ", Player.hacking_money_mult, Player.hacking_money_mult * mults.hacking_money_mult, 1],
|
}}
|
||||||
["Hacking Growth ", Player.hacking_grow_mult, Player.hacking_grow_mult * mults.hacking_grow_mult, 1],
|
>
|
||||||
]}
|
<MultiplierList rows={leftColData} />
|
||||||
/>
|
<MultiplierList rows={rightColData} />
|
||||||
<br />
|
</Paper>
|
||||||
|
|
||||||
<MultiplierTable
|
|
||||||
rows={[
|
|
||||||
[
|
|
||||||
"Hacking Level ",
|
|
||||||
Player.hacking_mult,
|
|
||||||
Player.hacking_mult * mults.hacking_mult,
|
|
||||||
BitNodeMultipliers.HackingLevelMultiplier,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"Hacking Experience ",
|
|
||||||
Player.hacking_exp_mult,
|
|
||||||
Player.hacking_exp_mult * mults.hacking_exp_mult,
|
|
||||||
BitNodeMultipliers.HackExpGain,
|
|
||||||
],
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<MultiplierTable
|
|
||||||
rows={[
|
|
||||||
[
|
|
||||||
"Strength Level ",
|
|
||||||
Player.strength_mult,
|
|
||||||
Player.strength_mult * mults.strength_mult,
|
|
||||||
BitNodeMultipliers.StrengthLevelMultiplier,
|
|
||||||
],
|
|
||||||
["Strength Experience ", Player.strength_exp_mult, Player.strength_exp_mult * mults.strength_exp_mult, 1],
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<MultiplierTable
|
|
||||||
rows={[
|
|
||||||
[
|
|
||||||
"Defense Level ",
|
|
||||||
Player.defense_mult,
|
|
||||||
Player.defense_mult * mults.defense_mult,
|
|
||||||
BitNodeMultipliers.DefenseLevelMultiplier,
|
|
||||||
],
|
|
||||||
["Defense Experience ", Player.defense_exp_mult, Player.defense_exp_mult * mults.defense_exp_mult, 1],
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<MultiplierTable
|
|
||||||
rows={[
|
|
||||||
[
|
|
||||||
"Dexterity Level ",
|
|
||||||
Player.dexterity_mult,
|
|
||||||
Player.dexterity_mult * mults.dexterity_mult,
|
|
||||||
BitNodeMultipliers.DexterityLevelMultiplier,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"Dexterity Experience ",
|
|
||||||
Player.dexterity_exp_mult,
|
|
||||||
Player.dexterity_exp_mult * mults.dexterity_exp_mult,
|
|
||||||
1,
|
|
||||||
],
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<MultiplierTable
|
|
||||||
rows={[
|
|
||||||
[
|
|
||||||
"Agility Level ",
|
|
||||||
Player.agility_mult,
|
|
||||||
Player.agility_mult * mults.agility_mult,
|
|
||||||
BitNodeMultipliers.AgilityLevelMultiplier,
|
|
||||||
],
|
|
||||||
["Agility Experience ", Player.agility_exp_mult, Player.agility_exp_mult * mults.agility_exp_mult, 1],
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<MultiplierTable
|
|
||||||
rows={[
|
|
||||||
[
|
|
||||||
"Charisma Level ",
|
|
||||||
Player.charisma_mult,
|
|
||||||
Player.charisma_mult * mults.charisma_mult,
|
|
||||||
BitNodeMultipliers.CharismaLevelMultiplier,
|
|
||||||
],
|
|
||||||
["Charisma Experience ", Player.charisma_exp_mult, Player.charisma_exp_mult * mults.charisma_exp_mult, 1],
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<MultiplierTable
|
|
||||||
rows={[
|
|
||||||
[
|
|
||||||
"Hacknet Node production ",
|
|
||||||
Player.hacknet_node_money_mult,
|
|
||||||
Player.hacknet_node_money_mult * mults.hacknet_node_money_mult,
|
|
||||||
BitNodeMultipliers.HacknetNodeMoney,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"Hacknet Node purchase cost ",
|
|
||||||
Player.hacknet_node_purchase_cost_mult,
|
|
||||||
Player.hacknet_node_purchase_cost_mult * mults.hacknet_node_purchase_cost_mult,
|
|
||||||
1,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"Hacknet Node RAM upgrade cost ",
|
|
||||||
Player.hacknet_node_ram_cost_mult,
|
|
||||||
Player.hacknet_node_ram_cost_mult * mults.hacknet_node_ram_cost_mult,
|
|
||||||
1,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"Hacknet Node Core purchase cost ",
|
|
||||||
Player.hacknet_node_core_cost_mult,
|
|
||||||
Player.hacknet_node_core_cost_mult * mults.hacknet_node_core_cost_mult,
|
|
||||||
1,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"Hacknet Node level upgrade cost ",
|
|
||||||
Player.hacknet_node_level_cost_mult,
|
|
||||||
Player.hacknet_node_level_cost_mult * mults.hacknet_node_level_cost_mult,
|
|
||||||
1,
|
|
||||||
],
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<MultiplierTable
|
|
||||||
rows={[
|
|
||||||
["Company reputation gain ", Player.company_rep_mult, Player.company_rep_mult * mults.company_rep_mult, 1],
|
|
||||||
[
|
|
||||||
"Faction reputation gain ",
|
|
||||||
Player.faction_rep_mult,
|
|
||||||
Player.faction_rep_mult * mults.faction_rep_mult,
|
|
||||||
BitNodeMultipliers.FactionWorkRepGain,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"Salary ",
|
|
||||||
Player.work_money_mult,
|
|
||||||
Player.work_money_mult * mults.work_money_mult,
|
|
||||||
BitNodeMultipliers.CompanyWorkMoney,
|
|
||||||
],
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<MultiplierTable
|
|
||||||
rows={[
|
|
||||||
["Crime success ", Player.crime_success_mult, Player.crime_success_mult * mults.crime_success_mult, 1],
|
|
||||||
[
|
|
||||||
"Crime money ",
|
|
||||||
Player.crime_money_mult,
|
|
||||||
Player.crime_money_mult * mults.crime_money_mult,
|
|
||||||
BitNodeMultipliers.CrimeMoney,
|
|
||||||
],
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<BladeburnerMults />
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,11 @@
|
|||||||
* React component for displaying all of the player's purchased (but not installed)
|
* React component for displaying all of the player's purchased (but not installed)
|
||||||
* Augmentations on the Augmentations UI.
|
* Augmentations on the Augmentations UI.
|
||||||
*/
|
*/
|
||||||
|
import { List, ListItemText, Paper, Tooltip, Typography } from "@mui/material";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import { Player } from "../../Player";
|
||||||
import { Augmentations } from "../Augmentations";
|
import { Augmentations } from "../Augmentations";
|
||||||
import { AugmentationNames } from "../data/AugmentationNames";
|
import { AugmentationNames } from "../data/AugmentationNames";
|
||||||
import { Player } from "../../Player";
|
|
||||||
|
|
||||||
import { AugmentationAccordion } from "../../ui/React/AugmentationAccordion";
|
|
||||||
import List from "@mui/material/List";
|
|
||||||
|
|
||||||
export function PurchasedAugmentations(): React.ReactElement {
|
export function PurchasedAugmentations(): React.ReactElement {
|
||||||
const augs: React.ReactElement[] = [];
|
const augs: React.ReactElement[] = [];
|
||||||
@ -23,14 +20,48 @@ export function PurchasedAugmentations(): React.ReactElement {
|
|||||||
}
|
}
|
||||||
for (let i = 0; i < Player.queuedAugmentations.length; i++) {
|
for (let i = 0; i < Player.queuedAugmentations.length; i++) {
|
||||||
const ownedAug = Player.queuedAugmentations[i];
|
const ownedAug = Player.queuedAugmentations[i];
|
||||||
|
let displayName = ownedAug.name;
|
||||||
|
|
||||||
if (ownedAug.name === AugmentationNames.NeuroFluxGovernor && i !== nfgIndex) continue;
|
if (ownedAug.name === AugmentationNames.NeuroFluxGovernor && i !== nfgIndex) continue;
|
||||||
const aug = Augmentations[ownedAug.name];
|
const aug = Augmentations[ownedAug.name];
|
||||||
|
|
||||||
let level = null;
|
let level = null;
|
||||||
if (ownedAug.name === AugmentationNames.NeuroFluxGovernor) {
|
if (ownedAug.name === AugmentationNames.NeuroFluxGovernor) {
|
||||||
level = ownedAug.level;
|
level = ownedAug.level;
|
||||||
|
displayName += ` - Level ${level}`;
|
||||||
}
|
}
|
||||||
augs.push(<AugmentationAccordion key={aug.name} aug={aug} level={level} />);
|
|
||||||
|
augs.push(
|
||||||
|
<Tooltip
|
||||||
|
title={
|
||||||
|
<Typography>
|
||||||
|
{(() => {
|
||||||
|
const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info;
|
||||||
|
const tooltip = (
|
||||||
|
<>
|
||||||
|
{info}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{aug.stats}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
return tooltip;
|
||||||
|
})()}
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
enterNextDelay={500}
|
||||||
|
key={displayName}
|
||||||
|
>
|
||||||
|
<ListItemText sx={{ px: 2, py: 1 }} primary={displayName} />
|
||||||
|
</Tooltip>,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <List dense>{augs}</List>;
|
return (
|
||||||
|
<Paper sx={{ py: 1, maxHeight: 400, overflowY: "scroll" }}>
|
||||||
|
<List sx={{ height: 400, overflowY: "scroll" }} disablePadding>
|
||||||
|
{augs}
|
||||||
|
</List>
|
||||||
|
</Paper>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,159 @@
|
|||||||
import React from "react";
|
import { ListItemButton, ListItemText, Paper } from "@mui/material";
|
||||||
import { SourceFileMinus1 } from "./SourceFileMinus1";
|
|
||||||
import { OwnedSourceFiles } from "./OwnedSourceFiles";
|
|
||||||
import List from "@mui/material/List";
|
|
||||||
|
|
||||||
import Typography from "@mui/material/Typography";
|
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
|
import List from "@mui/material/List";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { Exploit, ExploitName } from "../../Exploits/Exploit";
|
||||||
|
import { Player } from "../../Player";
|
||||||
|
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
|
||||||
|
import { Settings } from "../../Settings/Settings";
|
||||||
|
import { SourceFile } from "../../SourceFile/SourceFile";
|
||||||
|
import { SourceFiles } from "../../SourceFile/SourceFiles";
|
||||||
|
|
||||||
|
interface SfMinus1 {
|
||||||
|
info: React.ReactElement;
|
||||||
|
n: number;
|
||||||
|
name: string;
|
||||||
|
lvl: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const safeGetSf = (sfNum: number): SourceFile | SfMinus1 | null => {
|
||||||
|
if (sfNum === -1) {
|
||||||
|
const sfMinus1: SfMinus1 = {
|
||||||
|
info: (
|
||||||
|
<>
|
||||||
|
This Source-File can only be acquired with obscure knowledge of the game, javascript, and the web ecosystem.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
It increases all of the player's multipliers by 0.1%
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
You have found the following exploits:
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{Player.exploits.map((c: Exploit) => (
|
||||||
|
<React.Fragment key={c}>
|
||||||
|
* {ExploitName(c)}
|
||||||
|
<br />
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
lvl: Player.exploits.length,
|
||||||
|
n: -1,
|
||||||
|
name: "Source-File -1: Exploits in the BitNodes",
|
||||||
|
};
|
||||||
|
return sfMinus1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const srcFileKey = "SourceFile" + sfNum;
|
||||||
|
const sfObj = SourceFiles[srcFileKey];
|
||||||
|
if (sfObj == null) {
|
||||||
|
console.error(`Invalid source file number: ${sfNum}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return sfObj;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getMaxLevel = (sfObj: SourceFile | SfMinus1): string | number => {
|
||||||
|
let maxLevel;
|
||||||
|
switch (sfObj.n) {
|
||||||
|
case 12:
|
||||||
|
maxLevel = "∞";
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
maxLevel = Object.keys(Exploit).length;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
maxLevel = "3";
|
||||||
|
}
|
||||||
|
return maxLevel;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function SourceFilesElement(): React.ReactElement {
|
||||||
|
const sourceSfs = Player.sourceFiles.slice();
|
||||||
|
const exploits = Player.exploits;
|
||||||
|
// Create a fake SF for -1, if "owned"
|
||||||
|
if (exploits.length > 0) {
|
||||||
|
sourceSfs.unshift({
|
||||||
|
n: -1,
|
||||||
|
lvl: exploits.length,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
|
||||||
|
sourceSfs.sort((sf1, sf2) => {
|
||||||
|
return sf1.n - sf2.n;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sourceSfs.length === 0) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [selectedSf, setSelectedSf] = useState(sourceSfs[0]);
|
||||||
|
|
||||||
export function SourceFiles(): React.ReactElement {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Box sx={{ width: "100%", mt: 1 }}>
|
||||||
<Typography variant="h4">Source Files</Typography>
|
<Paper sx={{ p: 1 }}>
|
||||||
<Box mx={2}>
|
<Typography variant="h5">Source Files</Typography>
|
||||||
<List dense>
|
</Paper>
|
||||||
<SourceFileMinus1 />
|
<Paper sx={{ display: "grid", gridTemplateColumns: "1fr 3fr" }}>
|
||||||
<OwnedSourceFiles />
|
<Box>
|
||||||
</List>
|
<List
|
||||||
</Box>
|
sx={{ height: 400, overflowY: "scroll", borderRight: `1px solid ${Settings.theme.welllight}` }}
|
||||||
</>
|
disablePadding
|
||||||
|
>
|
||||||
|
{sourceSfs.map((e, i) => {
|
||||||
|
const sfObj = safeGetSf(e.n);
|
||||||
|
if (!sfObj) return;
|
||||||
|
|
||||||
|
const maxLevel = getMaxLevel(sfObj);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ListItemButton
|
||||||
|
key={i + 1}
|
||||||
|
onClick={() => setSelectedSf(e)}
|
||||||
|
selected={selectedSf.n === e.n}
|
||||||
|
sx={{ py: 0 }}
|
||||||
|
>
|
||||||
|
<ListItemText
|
||||||
|
disableTypography
|
||||||
|
primary={<Typography>{sfObj.name}</Typography>}
|
||||||
|
secondary={
|
||||||
|
<Typography>
|
||||||
|
Level {e.lvl} / {maxLevel}
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</ListItemButton>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</List>
|
||||||
|
</Box>
|
||||||
|
<Box sx={{ m: 1 }}>
|
||||||
|
<Typography variant="h6" sx={{ display: "flex", alignItems: "center", flexWrap: "wrap" }}>
|
||||||
|
{safeGetSf(selectedSf.n)?.name}
|
||||||
|
</Typography>
|
||||||
|
<Typography sx={{ maxHeight: 350, overflowY: "scroll" }}>
|
||||||
|
{(() => {
|
||||||
|
const sfObj = safeGetSf(selectedSf.n);
|
||||||
|
if (!sfObj) return;
|
||||||
|
|
||||||
|
const maxLevel = getMaxLevel(sfObj);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
Level {selectedSf.lvl} / {maxLevel}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{sfObj.info}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,16 @@ export class OfficeSpace {
|
|||||||
[EmployeePositions.RandD]: 0,
|
[EmployeePositions.RandD]: 0,
|
||||||
total: 0,
|
total: 0,
|
||||||
};
|
};
|
||||||
|
employeeJobs: { [key: string]: number } = {
|
||||||
|
[EmployeePositions.Operations]: 0,
|
||||||
|
[EmployeePositions.Engineer]: 0,
|
||||||
|
[EmployeePositions.Business]: 0,
|
||||||
|
[EmployeePositions.Management]: 0,
|
||||||
|
[EmployeePositions.RandD]: 0,
|
||||||
|
[EmployeePositions.Training]: 0,
|
||||||
|
[EmployeePositions.Unassigned]: 0,
|
||||||
|
total: 0,
|
||||||
|
};
|
||||||
|
|
||||||
constructor(params: IParams = {}) {
|
constructor(params: IParams = {}) {
|
||||||
this.loc = params.loc ? params.loc : "";
|
this.loc = params.loc ? params.loc : "";
|
||||||
@ -48,6 +58,8 @@ export class OfficeSpace {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.calculateTotalEmployees();
|
||||||
|
|
||||||
// Process Office properties
|
// Process Office properties
|
||||||
this.maxEne = 100;
|
this.maxEne = 100;
|
||||||
this.maxHap = 100;
|
this.maxHap = 100;
|
||||||
@ -101,6 +113,19 @@ export class OfficeSpace {
|
|||||||
return salaryPaid;
|
return salaryPaid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
calculateTotalEmployees(): void {
|
||||||
|
//Reset
|
||||||
|
for (const name of Object.keys(this.employeeJobs)) {
|
||||||
|
this.employeeJobs[name] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < this.employees.length; ++i) {
|
||||||
|
const employee = this.employees[i];
|
||||||
|
this.employeeJobs[employee.pos]++;
|
||||||
|
}
|
||||||
|
this.employeeJobs.total = this.employees.length;
|
||||||
|
}
|
||||||
|
|
||||||
calculateEmployeeProductivity(corporation: ICorporation, industry: IIndustry): void {
|
calculateEmployeeProductivity(corporation: ICorporation, industry: IIndustry): void {
|
||||||
//Reset
|
//Reset
|
||||||
for (const name of Object.keys(this.employeeProd)) {
|
for (const name of Object.keys(this.employeeProd)) {
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
|
import { Clear, ExpandMore, Reply, ReplyAll } from "@mui/icons-material";
|
||||||
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionDetails,
|
||||||
|
AccordionSummary,
|
||||||
|
Button,
|
||||||
|
IconButton,
|
||||||
|
MenuItem,
|
||||||
|
Select,
|
||||||
|
SelectChangeEvent,
|
||||||
|
Typography,
|
||||||
|
} from "@mui/material";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
import Accordion from "@mui/material/Accordion";
|
|
||||||
import AccordionSummary from "@mui/material/AccordionSummary";
|
|
||||||
import AccordionDetails from "@mui/material/AccordionDetails";
|
|
||||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
|
||||||
|
|
||||||
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
|
||||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||||
import Typography from "@mui/material/Typography";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
import MenuItem from "@mui/material/MenuItem";
|
|
||||||
import IconButton from "@mui/material/IconButton";
|
|
||||||
import ReplyAllIcon from "@mui/icons-material/ReplyAll";
|
|
||||||
import ReplyIcon from "@mui/icons-material/Reply";
|
|
||||||
import ClearIcon from "@mui/icons-material/Clear";
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
player: IPlayer;
|
player: IPlayer;
|
||||||
@ -39,50 +38,46 @@ export function Augmentations(props: IProps): React.ReactElement {
|
|||||||
props.player.augmentations = [];
|
props.player.augmentations = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clearQueuedAugs(): void {
|
||||||
|
props.player.queuedAugmentations = [];
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Accordion TransitionProps={{ unmountOnExit: true }}>
|
<Accordion TransitionProps={{ unmountOnExit: true }}>
|
||||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
<AccordionSummary expandIcon={<ExpandMore />}>
|
||||||
<Typography>Augmentations</Typography>
|
<Typography>Augmentations</Typography>
|
||||||
</AccordionSummary>
|
</AccordionSummary>
|
||||||
<AccordionDetails>
|
<AccordionDetails>
|
||||||
<table>
|
<Select
|
||||||
<tbody>
|
onChange={setAugmentationDropdown}
|
||||||
<tr>
|
value={augmentation}
|
||||||
<td>
|
startAdornment={
|
||||||
<Typography>Aug:</Typography>
|
<>
|
||||||
</td>
|
<IconButton onClick={queueAllAugs} size="large">
|
||||||
<td>
|
<ReplyAll />
|
||||||
<Select
|
</IconButton>
|
||||||
onChange={setAugmentationDropdown}
|
<IconButton onClick={queueAug} size="large">
|
||||||
value={augmentation}
|
<Reply />
|
||||||
startAdornment={
|
</IconButton>
|
||||||
<>
|
</>
|
||||||
<IconButton onClick={queueAllAugs} size="large">
|
}
|
||||||
<ReplyAllIcon />
|
endAdornment={
|
||||||
</IconButton>
|
<>
|
||||||
<IconButton onClick={queueAug} size="large">
|
<IconButton onClick={clearAugs} size="large">
|
||||||
<ReplyIcon />
|
<Clear />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
endAdornment={
|
>
|
||||||
<>
|
{Object.values(AugmentationNames).map((aug) => (
|
||||||
<IconButton onClick={clearAugs} size="large">
|
<MenuItem key={aug} value={aug}>
|
||||||
<ClearIcon />
|
{aug}
|
||||||
</IconButton>
|
</MenuItem>
|
||||||
</>
|
))}
|
||||||
}
|
</Select>
|
||||||
>
|
<Button sx={{ display: "block" }} onClick={clearQueuedAugs}>
|
||||||
{Object.values(AugmentationNames).map((aug) => (
|
Clear Queued Augmentations
|
||||||
<MenuItem key={aug} value={aug}>
|
</Button>
|
||||||
{aug}
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</AccordionDetails>
|
</AccordionDetails>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
);
|
);
|
||||||
|
@ -81,7 +81,6 @@ export function hasAugmentationPrereqs(aug: Augmentation): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = false): string {
|
export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = false): string {
|
||||||
const factionInfo = fac.getInfo();
|
|
||||||
const hasPrereqs = hasAugmentationPrereqs(aug);
|
const hasPrereqs = hasAugmentationPrereqs(aug);
|
||||||
if (!hasPrereqs) {
|
if (!hasPrereqs) {
|
||||||
const txt = `You must first purchase or install ${aug.prereqs.join(",")} before you can purchase this one.`;
|
const txt = `You must first purchase or install ${aug.prereqs.join(",")} before you can purchase this one.`;
|
||||||
@ -90,7 +89,7 @@ export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = fal
|
|||||||
} else {
|
} else {
|
||||||
dialogBoxCreate(txt);
|
dialogBoxCreate(txt);
|
||||||
}
|
}
|
||||||
} else if (aug.baseCost !== 0 && Player.money < aug.baseCost * factionInfo.augmentationPriceMult) {
|
} else if (aug.baseCost !== 0 && Player.money < aug.baseCost) {
|
||||||
const txt = "You don't have enough money to purchase " + aug.name;
|
const txt = "You don't have enough money to purchase " + aug.name;
|
||||||
if (sing) {
|
if (sing) {
|
||||||
return txt;
|
return txt;
|
||||||
@ -102,14 +101,14 @@ export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = fal
|
|||||||
return txt;
|
return txt;
|
||||||
}
|
}
|
||||||
dialogBoxCreate(txt);
|
dialogBoxCreate(txt);
|
||||||
} else if (aug.baseCost === 0 || Player.money >= aug.baseCost * factionInfo.augmentationPriceMult) {
|
} else if (aug.baseCost === 0 || Player.money >= aug.baseCost) {
|
||||||
const queuedAugmentation = new PlayerOwnedAugmentation(aug.name);
|
const queuedAugmentation = new PlayerOwnedAugmentation(aug.name);
|
||||||
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
|
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
|
||||||
queuedAugmentation.level = getNextNeurofluxLevel();
|
queuedAugmentation.level = getNextNeurofluxLevel();
|
||||||
}
|
}
|
||||||
Player.queuedAugmentations.push(queuedAugmentation);
|
Player.queuedAugmentations.push(queuedAugmentation);
|
||||||
|
|
||||||
Player.loseMoney(aug.baseCost * factionInfo.augmentationPriceMult, "augmentations");
|
Player.loseMoney(aug.baseCost, "augmentations");
|
||||||
|
|
||||||
// If you just purchased Neuroflux Governor, recalculate the cost
|
// If you just purchased Neuroflux Governor, recalculate the cost
|
||||||
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
|
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
|
||||||
|
@ -2,20 +2,20 @@ import React from "react";
|
|||||||
import { IMap } from "../types";
|
import { IMap } from "../types";
|
||||||
import { FactionNames } from "./data/FactionNames";
|
import { FactionNames } from "./data/FactionNames";
|
||||||
|
|
||||||
|
interface FactionInfoParams {
|
||||||
|
infoText?: JSX.Element;
|
||||||
|
enemies?: string[];
|
||||||
|
offerHackingWork?: boolean;
|
||||||
|
offerFieldWork?: boolean;
|
||||||
|
offerSecurityWork?: boolean;
|
||||||
|
special?: boolean;
|
||||||
|
keepOnInstall?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains the "information" property for all the Factions, which is just a description of each faction
|
* Contains the "information" property for all the Factions, which is just a description of each faction
|
||||||
*/
|
*/
|
||||||
export class FactionInfo {
|
export class FactionInfo {
|
||||||
/**
|
|
||||||
* The multiplier to apply to augmentation base purchase price.
|
|
||||||
*/
|
|
||||||
augmentationPriceMult: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The multiplier to apply to augmentation reputation base requirement.
|
|
||||||
*/
|
|
||||||
augmentationRepRequirementMult: number;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The names of all other factions considered to be enemies to this faction.
|
* The names of all other factions considered to be enemies to this faction.
|
||||||
*/
|
*/
|
||||||
@ -31,11 +31,6 @@ export class FactionInfo {
|
|||||||
*/
|
*/
|
||||||
offerFieldWork: boolean;
|
offerFieldWork: boolean;
|
||||||
|
|
||||||
/**
|
|
||||||
* A flag indicating if the faction supports hacking missions to earn reputation.
|
|
||||||
*/
|
|
||||||
offerHackingMission: boolean;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A flag indicating if the faction supports hacking work to earn reputation.
|
* A flag indicating if the faction supports hacking work to earn reputation.
|
||||||
*/
|
*/
|
||||||
@ -56,32 +51,19 @@ export class FactionInfo {
|
|||||||
*/
|
*/
|
||||||
special: boolean;
|
special: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(params: FactionInfoParams) {
|
||||||
infoText: JSX.Element,
|
this.infoText = params.infoText ?? <></>;
|
||||||
enemies: string[],
|
this.enemies = params.enemies ?? [];
|
||||||
offerHackingMission: boolean,
|
this.offerHackingWork = params.offerHackingWork ?? false;
|
||||||
offerHackingWork: boolean,
|
this.offerFieldWork = params.offerFieldWork ?? false;
|
||||||
offerFieldWork: boolean,
|
this.offerSecurityWork = params.offerSecurityWork ?? false;
|
||||||
offerSecurityWork: boolean,
|
|
||||||
special: boolean,
|
|
||||||
keep: boolean,
|
|
||||||
) {
|
|
||||||
this.infoText = infoText;
|
|
||||||
this.enemies = enemies;
|
|
||||||
this.offerHackingMission = offerHackingMission;
|
|
||||||
this.offerHackingWork = offerHackingWork;
|
|
||||||
this.offerFieldWork = offerFieldWork;
|
|
||||||
this.offerSecurityWork = offerSecurityWork;
|
|
||||||
|
|
||||||
// These are always all 1 for now.
|
this.keep = params.keepOnInstall ?? false;
|
||||||
this.augmentationPriceMult = 1;
|
this.special = params.special ?? false;
|
||||||
this.augmentationRepRequirementMult = 1;
|
|
||||||
this.keep = keep;
|
|
||||||
this.special = special;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
offersWork(): boolean {
|
offersWork(): boolean {
|
||||||
return this.offerFieldWork || this.offerHackingMission || this.offerHackingWork || this.offerSecurityWork;
|
return this.offerFieldWork || this.offerHackingWork || this.offerSecurityWork;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,35 +73,25 @@ export class FactionInfo {
|
|||||||
// tslint:disable-next-line:variable-name
|
// tslint:disable-next-line:variable-name
|
||||||
export const FactionInfos: IMap<FactionInfo> = {
|
export const FactionInfos: IMap<FactionInfo> = {
|
||||||
// Endgame
|
// Endgame
|
||||||
[FactionNames.Illuminati]: new FactionInfo(
|
[FactionNames.Illuminati]: new FactionInfo({
|
||||||
(
|
infoText: (
|
||||||
<>
|
<>
|
||||||
Humanity never changes. No matter how civilized society becomes, it will eventually fall back into chaos. And
|
Humanity never changes. No matter how civilized society becomes, it will eventually fall back into chaos. And
|
||||||
from this chaos, we are the invisible hand that guides them to order.{" "}
|
from this chaos, we are the invisible hand that guides them to order.{" "}
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
[],
|
offerHackingWork: true,
|
||||||
true,
|
offerFieldWork: true,
|
||||||
true,
|
}),
|
||||||
true,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
|
|
||||||
[FactionNames.Daedalus]: new FactionInfo(
|
[FactionNames.Daedalus]: new FactionInfo({
|
||||||
<>Yesterday we obeyed kings and bent our necks to emperors. Today we kneel only to truth.</>,
|
infoText: <>Yesterday we obeyed kings and bent our necks to emperors. Today we kneel only to truth.</>,
|
||||||
[],
|
offerHackingWork: true,
|
||||||
true,
|
offerFieldWork: true,
|
||||||
true,
|
}),
|
||||||
true,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
|
|
||||||
[FactionNames.TheCovenant]: new FactionInfo(
|
[FactionNames.TheCovenant]: new FactionInfo({
|
||||||
(
|
infoText: (
|
||||||
<>
|
<>
|
||||||
Surrender yourself. Give up your empty individuality to become part of something great, something eternal.
|
Surrender yourself. Give up your empty individuality to become part of something great, something eternal.
|
||||||
Become a slave. Submit your mind, body, and soul. Only then can you set yourself free.
|
Become a slave. Submit your mind, body, and soul. Only then can you set yourself free.
|
||||||
@ -128,35 +100,27 @@ export const FactionInfos: IMap<FactionInfo> = {
|
|||||||
Only then can you discover immortality.
|
Only then can you discover immortality.
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
[],
|
offerHackingWork: true,
|
||||||
true,
|
offerFieldWork: true,
|
||||||
true,
|
}),
|
||||||
true,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
|
|
||||||
// Megacorporations, each forms its own faction
|
// Megacorporations, each forms its own faction
|
||||||
[FactionNames.ECorp]: new FactionInfo(
|
[FactionNames.ECorp]: new FactionInfo({
|
||||||
(
|
infoText: (
|
||||||
<>
|
<>
|
||||||
{FactionNames.ECorp}'s mission is simple: to connect the world of today with the technology of tomorrow. With
|
{FactionNames.ECorp}'s mission is simple: to connect the world of today with the technology of tomorrow. With
|
||||||
our wide range of Internet-related software and commercial hardware, {FactionNames.ECorp} makes the world's
|
our wide range of Internet-related software and commercial hardware, {FactionNames.ECorp} makes the world's
|
||||||
information universally accessible.
|
information universally accessible.
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
[],
|
offerHackingWork: true,
|
||||||
true,
|
offerFieldWork: true,
|
||||||
true,
|
offerSecurityWork: true,
|
||||||
true,
|
keepOnInstall: true,
|
||||||
true,
|
}),
|
||||||
false,
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
|
|
||||||
[FactionNames.MegaCorp]: new FactionInfo(
|
[FactionNames.MegaCorp]: new FactionInfo({
|
||||||
(
|
infoText: (
|
||||||
<>
|
<>
|
||||||
{FactionNames.MegaCorp} does what no other dares to do. We imagine. We create. We invent. We create what others
|
{FactionNames.MegaCorp} does what no other dares to do. We imagine. We create. We invent. We create what others
|
||||||
have never even dreamed of. Our work fills the world's needs for food, water, power, and transportation on an
|
have never even dreamed of. Our work fills the world's needs for food, water, power, and transportation on an
|
||||||
@ -167,17 +131,14 @@ export const FactionInfos: IMap<FactionInfo> = {
|
|||||||
the world.
|
the world.
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
[],
|
offerHackingWork: true,
|
||||||
true,
|
offerFieldWork: true,
|
||||||
true,
|
offerSecurityWork: true,
|
||||||
true,
|
keepOnInstall: true,
|
||||||
true,
|
}),
|
||||||
false,
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
|
|
||||||
[FactionNames.BachmanAssociates]: new FactionInfo(
|
[FactionNames.BachmanAssociates]: new FactionInfo({
|
||||||
(
|
infoText: (
|
||||||
<>
|
<>
|
||||||
Where Law and Business meet - thats where we are.
|
Where Law and Business meet - thats where we are.
|
||||||
<br />
|
<br />
|
||||||
@ -185,112 +146,87 @@ export const FactionInfos: IMap<FactionInfo> = {
|
|||||||
Legal Insight - Business Instinct - Innovative Experience.
|
Legal Insight - Business Instinct - Innovative Experience.
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
[],
|
offerHackingWork: true,
|
||||||
true,
|
offerFieldWork: true,
|
||||||
true,
|
offerSecurityWork: true,
|
||||||
true,
|
keepOnInstall: true,
|
||||||
true,
|
}),
|
||||||
false,
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
|
|
||||||
[FactionNames.BladeIndustries]: new FactionInfo(
|
[FactionNames.BladeIndustries]: new FactionInfo({
|
||||||
<>Augmentation is Salvation.</>,
|
infoText: <>Augmentation is Salvation.</>,
|
||||||
[],
|
offerHackingWork: true,
|
||||||
true,
|
offerFieldWork: true,
|
||||||
true,
|
offerSecurityWork: true,
|
||||||
true,
|
keepOnInstall: true,
|
||||||
true,
|
}),
|
||||||
false,
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
|
|
||||||
[FactionNames.NWO]: new FactionInfo(
|
[FactionNames.NWO]: new FactionInfo({
|
||||||
(
|
infoText: (
|
||||||
<>
|
<>
|
||||||
Humans don't truly desire freedom. They want to be observed, understood, and judged. They want to be given
|
Humans don't truly desire freedom. They want to be observed, understood, and judged. They want to be given
|
||||||
purpose and direction in life. That is why they created God. And that is why they created civilization - not
|
purpose and direction in life. That is why they created God. And that is why they created civilization - not
|
||||||
because of willingness, but because of a need to be incorporated into higher orders of structure and meaning.
|
because of willingness, but because of a need to be incorporated into higher orders of structure and meaning.
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
[],
|
offerHackingWork: true,
|
||||||
true,
|
offerFieldWork: true,
|
||||||
true,
|
offerSecurityWork: true,
|
||||||
true,
|
keepOnInstall: true,
|
||||||
true,
|
}),
|
||||||
false,
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
|
|
||||||
[FactionNames.ClarkeIncorporated]: new FactionInfo(
|
[FactionNames.ClarkeIncorporated]: new FactionInfo({
|
||||||
<>The Power of the Genome - Unlocked.</>,
|
infoText: <>The Power of the Genome - Unlocked.</>,
|
||||||
[],
|
offerHackingWork: true,
|
||||||
true,
|
offerFieldWork: true,
|
||||||
true,
|
offerSecurityWork: true,
|
||||||
true,
|
keepOnInstall: true,
|
||||||
true,
|
}),
|
||||||
false,
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
|
|
||||||
[FactionNames.OmniTekIncorporated]: new FactionInfo(
|
[FactionNames.OmniTekIncorporated]: new FactionInfo({
|
||||||
<>Simply put, our mission is to design and build robots that make a difference.</>,
|
infoText: <>Simply put, our mission is to design and build robots that make a difference.</>,
|
||||||
[],
|
offerHackingWork: true,
|
||||||
true,
|
offerFieldWork: true,
|
||||||
true,
|
offerSecurityWork: true,
|
||||||
true,
|
keepOnInstall: true,
|
||||||
true,
|
}),
|
||||||
false,
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
|
|
||||||
[FactionNames.FourSigma]: new FactionInfo(
|
[FactionNames.FourSigma]: new FactionInfo({
|
||||||
(
|
infoText: (
|
||||||
<>
|
<>
|
||||||
The scientific method is the best way to approach investing. Big strategies backed up with big data. Driven by
|
The scientific method is the best way to approach investing. Big strategies backed up with big data. Driven by
|
||||||
deep learning and innovative ideas. And improved by iteration. That's {FactionNames.FourSigma}.
|
deep learning and innovative ideas. And improved by iteration. That's {FactionNames.FourSigma}.
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
[],
|
offerHackingWork: true,
|
||||||
true,
|
offerFieldWork: true,
|
||||||
true,
|
offerSecurityWork: true,
|
||||||
true,
|
keepOnInstall: true,
|
||||||
true,
|
}),
|
||||||
false,
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
|
|
||||||
[FactionNames.KuaiGongInternational]: new FactionInfo(
|
[FactionNames.KuaiGongInternational]: new FactionInfo({
|
||||||
<>Dream big. Work hard. Make history.</>,
|
infoText: <>Dream big. Work hard. Make history.</>,
|
||||||
[],
|
offerHackingWork: true,
|
||||||
true,
|
offerFieldWork: true,
|
||||||
true,
|
offerSecurityWork: true,
|
||||||
true,
|
keepOnInstall: true,
|
||||||
true,
|
}),
|
||||||
false,
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
|
|
||||||
// Other Corporations
|
// Other Corporations
|
||||||
[FactionNames.FulcrumSecretTechnologies]: new FactionInfo(
|
[FactionNames.FulcrumSecretTechnologies]: new FactionInfo({
|
||||||
(
|
infoText: (
|
||||||
<>
|
<>
|
||||||
The human organism has an innate desire to worship. That is why they created gods. If there were no gods, it
|
The human organism has an innate desire to worship. That is why they created gods. If there were no gods, it
|
||||||
would be necessary to create them. And now we can.
|
would be necessary to create them. And now we can.
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
[],
|
offerHackingWork: true,
|
||||||
true,
|
offerSecurityWork: true,
|
||||||
true,
|
keepOnInstall: true,
|
||||||
false,
|
}),
|
||||||
true,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
|
|
||||||
// Hacker groups
|
// Hacker groups
|
||||||
[FactionNames.BitRunners]: new FactionInfo(
|
[FactionNames.BitRunners]: new FactionInfo({
|
||||||
(
|
infoText: (
|
||||||
<>
|
<>
|
||||||
Our entire lives are controlled by bits. All of our actions, our thoughts, our personal information. It's all
|
Our entire lives are controlled by bits. All of our actions, our thoughts, our personal information. It's all
|
||||||
transformed into bits, stored in bits, communicated through bits. It’s impossible for any person to move, to
|
transformed into bits, stored in bits, communicated through bits. It’s impossible for any person to move, to
|
||||||
@ -302,17 +238,11 @@ export const FactionInfos: IMap<FactionInfo> = {
|
|||||||
Those who run the bits, run the world.
|
Those who run the bits, run the world.
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
[],
|
offerHackingWork: true,
|
||||||
true,
|
}),
|
||||||
true,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
|
|
||||||
[FactionNames.TheBlackHand]: new FactionInfo(
|
[FactionNames.TheBlackHand]: new FactionInfo({
|
||||||
(
|
infoText: (
|
||||||
<>
|
<>
|
||||||
The world, so afraid of strong government, now has no government. Only power - Digital power. Financial power.
|
The world, so afraid of strong government, now has no government. Only power - Digital power. Financial power.
|
||||||
Technological power. And those at the top rule with an invisible hand. They built a society where the rich get
|
Technological power. And those at the top rule with an invisible hand. They built a society where the rich get
|
||||||
@ -322,17 +252,13 @@ export const FactionInfos: IMap<FactionInfo> = {
|
|||||||
So much pain. So many lives. Their darkness must end.
|
So much pain. So many lives. Their darkness must end.
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
[],
|
offerHackingWork: true,
|
||||||
true,
|
offerFieldWork: true,
|
||||||
true,
|
}),
|
||||||
true,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
[FactionNames.NiteSec]: new FactionInfo(<>
|
[FactionNames.NiteSec]: new FactionInfo({
|
||||||
|
infoText:(<>
|
||||||
{" __..__ "}<br />
|
{" __..__ "}<br />
|
||||||
{" _.nITESECNIt. "}<br />
|
{" _.nITESECNIt. "}<br />
|
||||||
{" .-'NITESECNITESEc. "}<br />
|
{" .-'NITESECNITESEc. "}<br />
|
||||||
@ -367,105 +293,87 @@ export const FactionInfos: IMap<FactionInfo> = {
|
|||||||
{" / .d/$/$; , ; "}<br />
|
{" / .d/$/$; , ; "}<br />
|
||||||
{" d .dNITESEC $ | "}<br />
|
{" d .dNITESEC $ | "}<br />
|
||||||
{" :bp.__.gNITESEC/$ :$ ; "}<br />
|
{" :bp.__.gNITESEC/$ :$ ; "}<br />
|
||||||
{" NITESECNITESECNIT /$b : "}<br /></>,
|
{" NITESECNITESECNIT /$b : "}<br /></>),
|
||||||
[],
|
offerHackingWork: true,
|
||||||
true,
|
offerFieldWork: false,
|
||||||
true,
|
offerSecurityWork: false,
|
||||||
false,
|
special: false,
|
||||||
false,
|
keepOnInstall: false,
|
||||||
false,
|
}),
|
||||||
false,
|
|
||||||
),
|
|
||||||
|
|
||||||
// City factions, essentially governments
|
// City factions, essentially governments
|
||||||
[FactionNames.Aevum]: new FactionInfo(
|
[FactionNames.Aevum]: new FactionInfo({
|
||||||
<>The Silicon City.</>,
|
infoText: <>The Silicon City.</>,
|
||||||
[FactionNames.Chongqing, FactionNames.NewTokyo, FactionNames.Ishima, FactionNames.Volhaven],
|
enemies: [FactionNames.Chongqing, FactionNames.NewTokyo, FactionNames.Ishima, FactionNames.Volhaven],
|
||||||
true,
|
offerHackingWork: true,
|
||||||
true,
|
offerFieldWork: true,
|
||||||
true,
|
offerSecurityWork: true,
|
||||||
true,
|
}),
|
||||||
false,
|
[FactionNames.Chongqing]: new FactionInfo({
|
||||||
false,
|
infoText: <>Serve the People.</>,
|
||||||
),
|
enemies: [FactionNames.Sector12, FactionNames.Aevum, FactionNames.Volhaven],
|
||||||
[FactionNames.Chongqing]: new FactionInfo(
|
offerHackingWork: true,
|
||||||
<>Serve the People.</>,
|
offerFieldWork: true,
|
||||||
[FactionNames.Sector12, FactionNames.Aevum, FactionNames.Volhaven],
|
offerSecurityWork: true,
|
||||||
true,
|
}),
|
||||||
true,
|
[FactionNames.Ishima]: new FactionInfo({
|
||||||
true,
|
infoText: <>The East Asian Order of the Future.</>,
|
||||||
true,
|
enemies: [FactionNames.Sector12, FactionNames.Aevum, FactionNames.Volhaven],
|
||||||
false,
|
offerHackingWork: true,
|
||||||
false,
|
offerFieldWork: true,
|
||||||
),
|
offerSecurityWork: true,
|
||||||
[FactionNames.Ishima]: new FactionInfo(
|
}),
|
||||||
<>The East Asian Order of the Future.</>,
|
[FactionNames.NewTokyo]: new FactionInfo({
|
||||||
[FactionNames.Sector12, FactionNames.Aevum, FactionNames.Volhaven],
|
infoText: <>Asia's World City.</>,
|
||||||
true,
|
enemies: [FactionNames.Sector12, FactionNames.Aevum, FactionNames.Volhaven],
|
||||||
true,
|
offerHackingWork: true,
|
||||||
true,
|
offerFieldWork: true,
|
||||||
true,
|
offerSecurityWork: true,
|
||||||
false,
|
}),
|
||||||
false,
|
[FactionNames.Sector12]: new FactionInfo({
|
||||||
),
|
infoText: <>The City of the Future.</>,
|
||||||
[FactionNames.NewTokyo]: new FactionInfo(
|
enemies: [FactionNames.Chongqing, FactionNames.NewTokyo, FactionNames.Ishima, FactionNames.Volhaven],
|
||||||
<>Asia's World City.</>,
|
offerHackingWork: true,
|
||||||
[FactionNames.Sector12, FactionNames.Aevum, FactionNames.Volhaven],
|
offerFieldWork: true,
|
||||||
true,
|
offerSecurityWork: true,
|
||||||
true,
|
}),
|
||||||
true,
|
[FactionNames.Volhaven]: new FactionInfo({
|
||||||
true,
|
infoText: <>Benefit, Honor, and Glory.</>,
|
||||||
false,
|
enemies: [
|
||||||
false,
|
FactionNames.Chongqing,
|
||||||
),
|
FactionNames.Sector12,
|
||||||
[FactionNames.Sector12]: new FactionInfo(
|
FactionNames.NewTokyo,
|
||||||
<>The City of the Future.</>,
|
FactionNames.Aevum,
|
||||||
[FactionNames.Chongqing, FactionNames.NewTokyo, FactionNames.Ishima, FactionNames.Volhaven],
|
FactionNames.Ishima,
|
||||||
true,
|
],
|
||||||
true,
|
offerHackingWork: true,
|
||||||
true,
|
offerFieldWork: true,
|
||||||
true,
|
offerSecurityWork: true,
|
||||||
false,
|
}),
|
||||||
false,
|
|
||||||
),
|
|
||||||
[FactionNames.Volhaven]: new FactionInfo(
|
|
||||||
<>Benefit, Honor, and Glory.</>,
|
|
||||||
[FactionNames.Chongqing, FactionNames.Sector12, FactionNames.NewTokyo, FactionNames.Aevum, FactionNames.Ishima],
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
|
|
||||||
// Criminal Organizations/Gangs
|
// Criminal Organizations/Gangs
|
||||||
[FactionNames.SpeakersForTheDead]: new FactionInfo(
|
[FactionNames.SpeakersForTheDead]: new FactionInfo({
|
||||||
<>It is better to reign in Hell than to serve in Heaven.</>,
|
infoText: <>It is better to reign in Hell than to serve in Heaven.</>,
|
||||||
[],
|
offerHackingWork: true,
|
||||||
true,
|
offerFieldWork: true,
|
||||||
true,
|
offerSecurityWork: true,
|
||||||
true,
|
}),
|
||||||
true,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
|
|
||||||
[FactionNames.TheDarkArmy]: new FactionInfo(
|
[FactionNames.TheDarkArmy]: new FactionInfo({
|
||||||
<>The World doesn't care about right or wrong. It only cares about power.</>,
|
infoText: <>The World doesn't care about right or wrong. It only cares about power.</>,
|
||||||
[],
|
offerHackingWork: true,
|
||||||
true,
|
offerFieldWork: true,
|
||||||
true,
|
}),
|
||||||
true,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
|
|
||||||
[FactionNames.TheSyndicate]: new FactionInfo(<>Honor holds you back.</>, [], true, true, true, true, false, false),
|
[FactionNames.TheSyndicate]: new FactionInfo({
|
||||||
|
infoText: <>Honor holds you back.</>,
|
||||||
|
offerHackingWork: true,
|
||||||
|
offerFieldWork: true,
|
||||||
|
offerSecurityWork: true,
|
||||||
|
}),
|
||||||
|
|
||||||
[FactionNames.Silhouette]: new FactionInfo(
|
[FactionNames.Silhouette]: new FactionInfo({
|
||||||
(
|
infoText: (
|
||||||
<>
|
<>
|
||||||
Corporations have filled the void of power left behind by the collapse of Western government. The issue is
|
Corporations have filled the void of power left behind by the collapse of Western government. The issue is
|
||||||
they've become so big that you don't know who they're working for. And if you're employed at one of these
|
they've become so big that you don't know who they're working for. And if you're employed at one of these
|
||||||
@ -475,100 +383,66 @@ export const FactionInfos: IMap<FactionInfo> = {
|
|||||||
That's terror. Terror, fear, and corruption. All born into the system, all propagated by the system.
|
That's terror. Terror, fear, and corruption. All born into the system, all propagated by the system.
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
[],
|
offerHackingWork: true,
|
||||||
true,
|
offerFieldWork: true,
|
||||||
true,
|
}),
|
||||||
true,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
|
|
||||||
[FactionNames.Tetrads]: new FactionInfo(
|
[FactionNames.Tetrads]: new FactionInfo({
|
||||||
<>Following the mandate of Heaven and carrying out the way.</>,
|
infoText: <>Following the mandate of Heaven and carrying out the way.</>,
|
||||||
[],
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
|
|
||||||
[FactionNames.SlumSnakes]: new FactionInfo(
|
offerFieldWork: true,
|
||||||
<>{FactionNames.SlumSnakes} rule!</>,
|
offerSecurityWork: true,
|
||||||
[],
|
}),
|
||||||
false,
|
|
||||||
false,
|
[FactionNames.SlumSnakes]: new FactionInfo({
|
||||||
true,
|
infoText: <>{FactionNames.SlumSnakes} rule!</>,
|
||||||
true,
|
|
||||||
false,
|
offerFieldWork: true,
|
||||||
false,
|
offerSecurityWork: true,
|
||||||
),
|
}),
|
||||||
|
|
||||||
// Earlygame factions - factions the player will prestige with early on that don't belong in other categories.
|
// Earlygame factions - factions the player will prestige with early on that don't belong in other categories.
|
||||||
[FactionNames.Netburners]: new FactionInfo(
|
[FactionNames.Netburners]: new FactionInfo({
|
||||||
<>{"~~//*>H4CK||3T 8URN3R5**>?>\\~~"}</>,
|
infoText: <>{"~~//*>H4CK||3T 8URN3R5**>?>\\~~"}</>,
|
||||||
[],
|
offerHackingWork: true,
|
||||||
true,
|
}),
|
||||||
true,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
|
|
||||||
[FactionNames.TianDiHui]: new FactionInfo(
|
[FactionNames.TianDiHui]: new FactionInfo({
|
||||||
<>Obey Heaven and work righteously.</>,
|
infoText: <>Obey Heaven and work righteously.</>,
|
||||||
[],
|
offerHackingWork: true,
|
||||||
true,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
|
|
||||||
[FactionNames.CyberSec]: new FactionInfo(
|
offerSecurityWork: true,
|
||||||
(
|
}),
|
||||||
|
|
||||||
|
[FactionNames.CyberSec]: new FactionInfo({
|
||||||
|
infoText: (
|
||||||
<>
|
<>
|
||||||
The Internet is the first thing that was built that we don't fully understand, the largest experiment in anarchy
|
The Internet is the first thing that was built that we don't fully understand, the largest experiment in anarchy
|
||||||
that we have ever had. And as the world becomes increasingly dominated by it, society approaches the brink of
|
that we have ever had. And as the world becomes increasingly dominated by it, society approaches the brink of
|
||||||
total chaos. We serve only to protect society, to protect humanity, to protect the world from imminent collapse.
|
total chaos. We serve only to protect society, to protect humanity, to protect the world from imminent collapse.
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
[],
|
offerHackingWork: true,
|
||||||
true,
|
}),
|
||||||
true,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
|
|
||||||
// Special Factions
|
// Special Factions
|
||||||
[FactionNames.Bladeburners]: new FactionInfo(
|
[FactionNames.Bladeburners]: new FactionInfo({
|
||||||
(
|
infoText: (
|
||||||
<>
|
<>
|
||||||
It's too bad they won't live. But then again, who does?
|
It's too bad they won't live. But then again, who does?
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
Note that for this faction, reputation can only be gained through {FactionNames.Bladeburners} actions.
|
Note that for this faction, reputation can only be gained through {FactionNames.Bladeburners} actions.{" "}
|
||||||
Completing {FactionNames.Bladeburners}
|
Completing {FactionNames.Bladeburners} contracts/operations will increase your reputation.
|
||||||
contracts/operations will increase your reputation.
|
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
[],
|
|
||||||
false,
|
special: true,
|
||||||
false,
|
}),
|
||||||
false,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
[FactionNames.ChurchOfTheMachineGod]: new FactionInfo(<>
|
[FactionNames.ChurchOfTheMachineGod]: new FactionInfo({
|
||||||
|
infoText:(<>
|
||||||
{" `` "}<br />
|
{" `` "}<br />
|
||||||
{" -odmmNmds: "}<br />
|
{" -odmmNmds: "}<br />
|
||||||
{" `hNmo:..-omNh. "}<br />
|
{" `hNmo:..-omNh. "}<br />
|
||||||
@ -600,13 +474,11 @@ export const FactionInfos: IMap<FactionInfo> = {
|
|||||||
Many cultures predict an end to humanity in the near future, a final
|
Many cultures predict an end to humanity in the near future, a final
|
||||||
Armageddon that will end the world; but we disagree.
|
Armageddon that will end the world; but we disagree.
|
||||||
<br /><br />Note that for this faction, reputation can
|
<br /><br />Note that for this faction, reputation can
|
||||||
only be gained by charging Stanek's gift.</>,
|
only be gained by charging Stanek's gift.</>),
|
||||||
[],
|
offerHackingWork: false,
|
||||||
false,
|
offerFieldWork: false,
|
||||||
false,
|
offerSecurityWork: false,
|
||||||
false,
|
special: true,
|
||||||
false,
|
keepOnInstall: true,
|
||||||
true,
|
}),
|
||||||
true,
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
@ -78,10 +78,10 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
|
|||||||
const augs = getAugs();
|
const augs = getAugs();
|
||||||
function canBuy(augName: string): boolean {
|
function canBuy(augName: string): boolean {
|
||||||
const aug = Augmentations[augName];
|
const aug = Augmentations[augName];
|
||||||
const repCost = aug.baseRepRequirement * props.faction.getInfo().augmentationRepRequirementMult;
|
const repCost = aug.baseRepRequirement;
|
||||||
const hasReq = props.faction.playerReputation >= repCost;
|
const hasReq = props.faction.playerReputation >= repCost;
|
||||||
const hasRep = hasAugmentationPrereqs(aug);
|
const hasRep = hasAugmentationPrereqs(aug);
|
||||||
const hasCost = aug.baseCost !== 0 && player.money > aug.baseCost * props.faction.getInfo().augmentationPriceMult;
|
const hasCost = aug.baseCost !== 0 && player.money > aug.baseCost;
|
||||||
return hasCost && hasReq && hasRep;
|
return hasCost && hasReq && hasRep;
|
||||||
}
|
}
|
||||||
const buy = augs.filter(canBuy).sort((augName1, augName2) => {
|
const buy = augs.filter(canBuy).sort((augName1, augName2) => {
|
||||||
|
@ -20,7 +20,6 @@ interface IProps {
|
|||||||
|
|
||||||
export function PurchaseAugmentationModal(props: IProps): React.ReactElement {
|
export function PurchaseAugmentationModal(props: IProps): React.ReactElement {
|
||||||
const player = use.Player();
|
const player = use.Player();
|
||||||
const factionInfo = props.faction.getInfo();
|
|
||||||
|
|
||||||
function buy(): void {
|
function buy(): void {
|
||||||
if (!isRepeatableAug(props.aug) && player.hasAugmentation(props.aug)) {
|
if (!isRepeatableAug(props.aug) && player.hasAugmentation(props.aug)) {
|
||||||
@ -43,7 +42,7 @@ export function PurchaseAugmentationModal(props: IProps): React.ReactElement {
|
|||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
Would you like to purchase the {props.aug.name} Augmentation for
|
Would you like to purchase the {props.aug.name} Augmentation for
|
||||||
<Money money={props.aug.baseCost * factionInfo.augmentationPriceMult} />?
|
<Money money={props.aug.baseCost} />?
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
</Typography>
|
</Typography>
|
||||||
|
@ -84,11 +84,11 @@ export function PurchaseableAugmentation(props: IProps): React.ReactElement {
|
|||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const moneyCost = aug.baseCost * props.faction.getInfo().augmentationPriceMult;
|
const moneyCost = aug.baseCost;
|
||||||
const repCost = aug.baseRepRequirement * props.faction.getInfo().augmentationRepRequirementMult;
|
const repCost = aug.baseRepRequirement;
|
||||||
const hasReq = hasAugmentationPrereqs(aug);
|
const hasReq = hasAugmentationPrereqs(aug);
|
||||||
const hasRep = props.faction.playerReputation >= repCost;
|
const hasRep = props.faction.playerReputation >= repCost;
|
||||||
const hasCost = aug.baseCost === 0 || props.p.money > aug.baseCost * props.faction.getInfo().augmentationPriceMult;
|
const hasCost = aug.baseCost === 0 || props.p.money > aug.baseCost;
|
||||||
|
|
||||||
// Determine UI properties
|
// Determine UI properties
|
||||||
const color: "error" | "primary" = !hasReq || !hasRep || !hasCost ? "error" : "primary";
|
const color: "error" | "primary" = !hasReq || !hasRep || !hasCost ? "error" : "primary";
|
||||||
|
@ -174,10 +174,11 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
|||||||
applyAugmentation({ name: AugmentationNames.StaneksGift1, level: 1 });
|
applyAugmentation({ name: AugmentationNames.StaneksGift1, level: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
router.toFaction(faction);
|
router.toStaneksGift();
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderCotMG(): React.ReactElement {
|
function renderCotMG(): React.ReactElement {
|
||||||
|
const toStanek = <Button onClick={() => router.toStaneksGift()}>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 />
|
||||||
@ -218,6 +219,9 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
|||||||
seems. Curious, Just how much of a machine's soul do you house in that body?
|
seems. Curious, Just how much of a machine's soul do you house in that body?
|
||||||
</i>
|
</i>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
<br />
|
||||||
|
{toStanek}
|
||||||
|
<br />
|
||||||
{symbol}
|
{symbol}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -232,6 +236,9 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
|||||||
mastery of the gift clearly demonstrates that. My hopes are climbing by the day for you.
|
mastery of the gift clearly demonstrates that. My hopes are climbing by the day for you.
|
||||||
</i>
|
</i>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
<br />
|
||||||
|
{toStanek}
|
||||||
|
<br />
|
||||||
{symbol}
|
{symbol}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -242,6 +249,9 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
|||||||
<Typography>
|
<Typography>
|
||||||
<i>Allison "Mother" Stanek: Welcome back my child!</i>
|
<i>Allison "Mother" Stanek: Welcome back my child!</i>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
<br />
|
||||||
|
{toStanek}
|
||||||
|
<br />
|
||||||
{symbol}
|
{symbol}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -15,6 +15,7 @@ export const RamCostConstants: IMap<number> = {
|
|||||||
ScriptWeakenRamCost: 0.15,
|
ScriptWeakenRamCost: 0.15,
|
||||||
ScriptWeakenAnalyzeRamCost: 1,
|
ScriptWeakenAnalyzeRamCost: 1,
|
||||||
ScriptScanRamCost: 0.2,
|
ScriptScanRamCost: 0.2,
|
||||||
|
ScriptRecentScriptsRamCost: 0.2,
|
||||||
ScriptPortProgramRamCost: 0.05,
|
ScriptPortProgramRamCost: 0.05,
|
||||||
ScriptRunRamCost: 1.0,
|
ScriptRunRamCost: 1.0,
|
||||||
ScriptExecRamCost: 1.3,
|
ScriptExecRamCost: 1.3,
|
||||||
@ -140,6 +141,7 @@ export const RamCosts: IMap<any> = {
|
|||||||
scp: RamCostConstants.ScriptScpRamCost,
|
scp: RamCostConstants.ScriptScpRamCost,
|
||||||
ls: RamCostConstants.ScriptScanRamCost,
|
ls: RamCostConstants.ScriptScanRamCost,
|
||||||
ps: RamCostConstants.ScriptScanRamCost,
|
ps: RamCostConstants.ScriptScanRamCost,
|
||||||
|
getRecentScripts: RamCostConstants.ScriptRecentScriptsRamCost,
|
||||||
hasRootAccess: RamCostConstants.ScriptHasRootAccessRamCost,
|
hasRootAccess: RamCostConstants.ScriptHasRootAccessRamCost,
|
||||||
getIp: RamCostConstants.ScriptGetHostnameRamCost,
|
getIp: RamCostConstants.ScriptGetHostnameRamCost,
|
||||||
getHostname: RamCostConstants.ScriptGetHostnameRamCost,
|
getHostname: RamCostConstants.ScriptGetHostnameRamCost,
|
||||||
|
@ -1,27 +1,24 @@
|
|||||||
import { RunningScript } from "src/Script/RunningScript";
|
import { RunningScript } from "src/Script/RunningScript";
|
||||||
|
import { Settings } from "../Settings/Settings";
|
||||||
import { WorkerScript } from "./WorkerScript";
|
import { WorkerScript } from "./WorkerScript";
|
||||||
|
|
||||||
export const recentScripts: RecentScript[] = [];
|
export const recentScripts: RecentScript[] = [];
|
||||||
|
|
||||||
export function AddRecentScript(workerScript: WorkerScript): void {
|
export function AddRecentScript(workerScript: WorkerScript): void {
|
||||||
if (recentScripts.find((r) => r.pid === workerScript.pid)) return;
|
if (recentScripts.find((r) => r.runningScript.pid === workerScript.pid)) return;
|
||||||
recentScripts.unshift({
|
|
||||||
filename: workerScript.name,
|
|
||||||
args: workerScript.args,
|
|
||||||
pid: workerScript.pid,
|
|
||||||
timestamp: new Date(),
|
|
||||||
|
|
||||||
|
const killedTime = new Date();
|
||||||
|
recentScripts.unshift({
|
||||||
|
timeOfDeath: killedTime,
|
||||||
runningScript: workerScript.scriptRef,
|
runningScript: workerScript.scriptRef,
|
||||||
});
|
});
|
||||||
while (recentScripts.length > 50) {
|
|
||||||
|
while (recentScripts.length > Settings.MaxRecentScriptsCapacity) {
|
||||||
recentScripts.pop();
|
recentScripts.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RecentScript {
|
export interface RecentScript {
|
||||||
filename: string;
|
timeOfDeath: Date;
|
||||||
args: string[];
|
|
||||||
pid: number;
|
|
||||||
timestamp: Date;
|
|
||||||
runningScript: RunningScript;
|
runningScript: RunningScript;
|
||||||
}
|
}
|
||||||
|
37
src/Netscript/ScriptDeath.ts
Normal file
37
src/Netscript/ScriptDeath.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { WorkerScript } from "./WorkerScript";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Script death marker.
|
||||||
|
*
|
||||||
|
* IMPORTANT: the game engine should not base any of it's decisions on the data
|
||||||
|
* carried in a ScriptDeath instance.
|
||||||
|
*
|
||||||
|
* This is because ScriptDeath instances are thrown through player code when a
|
||||||
|
* script is killed. Which grants the player access to the class and the ability
|
||||||
|
* to construct new instances with arbitrary data.
|
||||||
|
*/
|
||||||
|
export class ScriptDeath {
|
||||||
|
/** Process ID number. */
|
||||||
|
pid: number;
|
||||||
|
|
||||||
|
/** Filename of the script. */
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/** IP Address on which the script was running */
|
||||||
|
hostname: string;
|
||||||
|
|
||||||
|
/** Status message in case of script error. */
|
||||||
|
errorMessage = "";
|
||||||
|
|
||||||
|
constructor(ws: WorkerScript) {
|
||||||
|
this.pid = ws.pid;
|
||||||
|
this.name = ws.name;
|
||||||
|
this.hostname = ws.hostname;
|
||||||
|
this.errorMessage = ws.errorMessage;
|
||||||
|
|
||||||
|
Object.freeze(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.freeze(ScriptDeath);
|
||||||
|
Object.freeze(ScriptDeath.prototype);
|
@ -60,7 +60,7 @@ export class WorkerScript {
|
|||||||
env: Environment;
|
env: Environment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Status message in case of script error. Currently unused I think
|
* Status message in case of script error.
|
||||||
*/
|
*/
|
||||||
errorMessage = "";
|
errorMessage = "";
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
* Stops an actively-running script (represented by a WorkerScript object)
|
* Stops an actively-running script (represented by a WorkerScript object)
|
||||||
* and removes it from the global pool of active scripts.
|
* and removes it from the global pool of active scripts.
|
||||||
*/
|
*/
|
||||||
|
import { ScriptDeath } from "./ScriptDeath";
|
||||||
import { WorkerScript } from "./WorkerScript";
|
import { WorkerScript } from "./WorkerScript";
|
||||||
import { workerScripts } from "./WorkerScripts";
|
import { workerScripts } from "./WorkerScripts";
|
||||||
import { WorkerScriptStartStopEventEmitter } from "./WorkerScriptStartStopEventEmitter";
|
import { WorkerScriptStartStopEventEmitter } from "./WorkerScriptStartStopEventEmitter";
|
||||||
@ -139,7 +140,7 @@ function killNetscriptDelay(workerScript: WorkerScript): void {
|
|||||||
if (workerScript.delay) {
|
if (workerScript.delay) {
|
||||||
clearTimeout(workerScript.delay);
|
clearTimeout(workerScript.delay);
|
||||||
if (workerScript.delayReject) {
|
if (workerScript.delayReject) {
|
||||||
workerScript.delayReject(workerScript);
|
workerScript.delayReject(new ScriptDeath(workerScript));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,20 @@
|
|||||||
import { isString } from "./utils/helpers/isString";
|
import { isString } from "./utils/helpers/isString";
|
||||||
import { GetServer } from "./Server/AllServers";
|
import { GetServer } from "./Server/AllServers";
|
||||||
|
import { ScriptDeath } from "./Netscript/ScriptDeath";
|
||||||
import { WorkerScript } from "./Netscript/WorkerScript";
|
import { WorkerScript } from "./Netscript/WorkerScript";
|
||||||
|
|
||||||
export function netscriptDelay(time: number, workerScript: WorkerScript): Promise<void> {
|
export function netscriptDelay(time: number, workerScript: WorkerScript): Promise<void> {
|
||||||
|
// Cancel any pre-existing netscriptDelay'ed function call
|
||||||
|
// TODO: the rejection almost certainly ends up in the uncaught rejection handler.
|
||||||
|
// Maybe reject with a stack-trace'd error message?
|
||||||
if (workerScript.delayReject) workerScript.delayReject();
|
if (workerScript.delayReject) workerScript.delayReject();
|
||||||
|
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
workerScript.delay = window.setTimeout(() => {
|
workerScript.delay = window.setTimeout(() => {
|
||||||
workerScript.delay = null;
|
workerScript.delay = null;
|
||||||
workerScript.delayReject = undefined;
|
workerScript.delayReject = undefined;
|
||||||
|
|
||||||
if (workerScript.env.stopFlag) reject(workerScript);
|
if (workerScript.env.stopFlag) reject(new ScriptDeath(workerScript));
|
||||||
else resolve();
|
else resolve();
|
||||||
}, time);
|
}, time);
|
||||||
workerScript.delayReject = reject;
|
workerScript.delayReject = reject;
|
||||||
|
@ -79,6 +79,8 @@ import {
|
|||||||
Gang as IGang,
|
Gang as IGang,
|
||||||
Bladeburner as IBladeburner,
|
Bladeburner as IBladeburner,
|
||||||
Stanek as IStanek,
|
Stanek as IStanek,
|
||||||
|
RunningScript as IRunningScript,
|
||||||
|
RecentScript as IRecentScript,
|
||||||
SourceFileLvl,
|
SourceFileLvl,
|
||||||
BasicHGWOptions,
|
BasicHGWOptions,
|
||||||
ProcessInfo,
|
ProcessInfo,
|
||||||
@ -98,6 +100,7 @@ import { SnackbarEvents } from "./ui/React/Snackbar";
|
|||||||
import { Flags } from "./NetscriptFunctions/Flags";
|
import { Flags } from "./NetscriptFunctions/Flags";
|
||||||
import { calculateIntelligenceBonus } from "./PersonObjects/formulas/intelligence";
|
import { calculateIntelligenceBonus } from "./PersonObjects/formulas/intelligence";
|
||||||
import { CalculateShareMult, StartSharing } from "./NetworkShare/Share";
|
import { CalculateShareMult, StartSharing } from "./NetworkShare/Share";
|
||||||
|
import { recentScripts } from "./Netscript/RecentScripts";
|
||||||
import { CityName } from "./Locations/data/CityNames";
|
import { CityName } from "./Locations/data/CityNames";
|
||||||
import { wrapAPI } from "./Netscript/APIWrapper";
|
import { wrapAPI } from "./Netscript/APIWrapper";
|
||||||
|
|
||||||
@ -213,6 +216,32 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitizes a `RunningScript` to remove sensitive information, making it suitable for
|
||||||
|
* return through an NS function.
|
||||||
|
* @see NS.getRecentScripts
|
||||||
|
* @see NS.getRunningScript
|
||||||
|
* @param runningScript Existing, internal RunningScript
|
||||||
|
* @returns A sanitized, NS-facing copy of the RunningScript
|
||||||
|
*/
|
||||||
|
const createPublicRunningScript = function (runningScript: RunningScript): IRunningScript {
|
||||||
|
return {
|
||||||
|
args: runningScript.args.slice(),
|
||||||
|
filename: runningScript.filename,
|
||||||
|
logs: runningScript.logs.slice(),
|
||||||
|
offlineExpGained: runningScript.offlineExpGained,
|
||||||
|
offlineMoneyMade: runningScript.offlineMoneyMade,
|
||||||
|
offlineRunningTime: runningScript.offlineRunningTime,
|
||||||
|
onlineExpGained: runningScript.onlineExpGained,
|
||||||
|
onlineMoneyMade: runningScript.onlineMoneyMade,
|
||||||
|
onlineRunningTime: runningScript.onlineRunningTime,
|
||||||
|
pid: runningScript.pid,
|
||||||
|
ramUsage: runningScript.ramUsage,
|
||||||
|
server: runningScript.server,
|
||||||
|
threads: runningScript.threads,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function for getting the error log message when the user specifies
|
* Helper function for getting the error log message when the user specifies
|
||||||
* a nonexistent running script
|
* a nonexistent running script
|
||||||
@ -585,9 +614,25 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
|||||||
|
|
||||||
return calculatePercentMoneyHacked(server, Player);
|
return calculatePercentMoneyHacked(server, Player);
|
||||||
},
|
},
|
||||||
hackAnalyzeSecurity: function (_threads: unknown): number {
|
hackAnalyzeSecurity: function (_threads: unknown, _hostname?: unknown): number {
|
||||||
updateDynamicRam("hackAnalyzeSecurity", getRamCost(Player, "hackAnalyzeSecurity"));
|
updateDynamicRam("hackAnalyzeSecurity", getRamCost(Player, "hackAnalyzeSecurity"));
|
||||||
const threads = helper.number("hackAnalyzeSecurity", "threads", _threads);
|
let threads = helper.number("hackAnalyzeSecurity", "threads", _threads);
|
||||||
|
if (_hostname) {
|
||||||
|
const hostname = helper.string("hackAnalyzeSecurity", "hostname", _hostname);
|
||||||
|
const server = safeGetServer(hostname, "hackAnalyze");
|
||||||
|
if (!(server instanceof Server)) {
|
||||||
|
workerScript.log("hackAnalyzeSecurity", () => "Cannot be executed on this server.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const percentHacked = calculatePercentMoneyHacked(server, Player);
|
||||||
|
|
||||||
|
if (percentHacked > 0) {
|
||||||
|
// thread count is limited to the maximum number of threads needed
|
||||||
|
threads = Math.ceil(1 / percentHacked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return CONSTANTS.ServerFortifyAmount * threads;
|
return CONSTANTS.ServerFortifyAmount * threads;
|
||||||
},
|
},
|
||||||
hackAnalyzeChance: function (_hostname: unknown): number {
|
hackAnalyzeChance: function (_hostname: unknown): number {
|
||||||
@ -1415,6 +1460,13 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
|||||||
allFiles.sort();
|
allFiles.sort();
|
||||||
return allFiles;
|
return allFiles;
|
||||||
},
|
},
|
||||||
|
getRecentScripts: function (): IRecentScript[] {
|
||||||
|
updateDynamicRam("getRecentScripts", getRamCost(Player, "getRecentScripts"));
|
||||||
|
return recentScripts.map((rs) => ({
|
||||||
|
timeOfDeath: rs.timeOfDeath,
|
||||||
|
...createPublicRunningScript(rs.runningScript),
|
||||||
|
}));
|
||||||
|
},
|
||||||
ps: function (_hostname: unknown = workerScript.hostname): ProcessInfo[] {
|
ps: function (_hostname: unknown = workerScript.hostname): ProcessInfo[] {
|
||||||
updateDynamicRam("ps", getRamCost(Player, "ps"));
|
updateDynamicRam("ps", getRamCost(Player, "ps"));
|
||||||
const hostname = helper.string("ps", "hostname", _hostname);
|
const hostname = helper.string("ps", "hostname", _hostname);
|
||||||
@ -2130,21 +2182,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
|||||||
runningScript = getRunningScript(fn, hostname, "getRunningScript", args);
|
runningScript = getRunningScript(fn, hostname, "getRunningScript", args);
|
||||||
}
|
}
|
||||||
if (runningScript === null) return null;
|
if (runningScript === null) return null;
|
||||||
return {
|
return createPublicRunningScript(runningScript);
|
||||||
args: runningScript.args.slice(),
|
|
||||||
filename: runningScript.filename,
|
|
||||||
logs: runningScript.logs.slice(),
|
|
||||||
offlineExpGained: runningScript.offlineExpGained,
|
|
||||||
offlineMoneyMade: runningScript.offlineMoneyMade,
|
|
||||||
offlineRunningTime: runningScript.offlineRunningTime,
|
|
||||||
onlineExpGained: runningScript.onlineExpGained,
|
|
||||||
onlineMoneyMade: runningScript.onlineMoneyMade,
|
|
||||||
onlineRunningTime: runningScript.onlineRunningTime,
|
|
||||||
pid: runningScript.pid,
|
|
||||||
ramUsage: runningScript.ramUsage,
|
|
||||||
server: runningScript.server,
|
|
||||||
threads: runningScript.threads,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
getHackTime: function (_hostname: unknown = workerScript.hostname): number {
|
getHackTime: function (_hostname: unknown = workerScript.hostname): number {
|
||||||
updateDynamicRam("getHackTime", getRamCost(Player, "getHackTime"));
|
updateDynamicRam("getHackTime", getRamCost(Player, "getHackTime"));
|
||||||
|
@ -637,9 +637,6 @@ export function NetscriptCorporation(
|
|||||||
const office = getOffice(divisionName, cityName);
|
const office = getOffice(divisionName, cityName);
|
||||||
if (!Object.values(EmployeePositions).includes(job)) throw new Error(`'${job}' is not a valid job.`);
|
if (!Object.values(EmployeePositions).includes(job)) throw new Error(`'${job}' is not a valid job.`);
|
||||||
return netscriptDelay(1000, workerScript).then(function () {
|
return netscriptDelay(1000, workerScript).then(function () {
|
||||||
if (workerScript.env.stopFlag) {
|
|
||||||
return Promise.reject(workerScript);
|
|
||||||
}
|
|
||||||
return Promise.resolve(office.setEmployeeToJob(job, amount));
|
return Promise.resolve(office.setEmployeeToJob(job, amount));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -753,6 +750,15 @@ export function NetscriptCorporation(
|
|||||||
"Research & Development": office.employeeProd[EmployeePositions.RandD],
|
"Research & Development": office.employeeProd[EmployeePositions.RandD],
|
||||||
Training: office.employeeProd[EmployeePositions.Training],
|
Training: office.employeeProd[EmployeePositions.Training],
|
||||||
},
|
},
|
||||||
|
employeeJobs: {
|
||||||
|
Operations: office.employeeJobs[EmployeePositions.Operations],
|
||||||
|
Engineer: office.employeeJobs[EmployeePositions.Engineer],
|
||||||
|
Business: office.employeeJobs[EmployeePositions.Business],
|
||||||
|
Management: office.employeeJobs[EmployeePositions.Management],
|
||||||
|
"Research & Development": office.employeeJobs[EmployeePositions.RandD],
|
||||||
|
Training: office.employeeJobs[EmployeePositions.Training],
|
||||||
|
Unassigned: office.employeeJobs[EmployeePositions.Unassigned],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
getEmployee: function (_divisionName: unknown, _cityName: unknown, _employeeName: unknown): NSEmployee {
|
getEmployee: function (_divisionName: unknown, _cityName: unknown, _employeeName: unknown): NSEmployee {
|
||||||
|
@ -219,7 +219,7 @@ export function NetscriptSingularity(
|
|||||||
workerScript.running = false;
|
workerScript.running = false;
|
||||||
killWorkerScript(workerScript);
|
killWorkerScript(workerScript);
|
||||||
},
|
},
|
||||||
installAugmentations: function (_cbScript: unknown): boolean {
|
installAugmentations: function (_cbScript: unknown = ""): boolean {
|
||||||
updateRam("installAugmentations");
|
updateRam("installAugmentations");
|
||||||
const cbScript = helper.string("installAugmentations", "cbScript", _cbScript);
|
const cbScript = helper.string("installAugmentations", "cbScript", _cbScript);
|
||||||
helper.checkSingularityAccess("installAugmentations");
|
helper.checkSingularityAccess("installAugmentations");
|
||||||
|
@ -12,7 +12,7 @@ import {
|
|||||||
Stanek as IStanek,
|
Stanek as IStanek,
|
||||||
} from "../ScriptEditor/NetscriptDefinitions";
|
} from "../ScriptEditor/NetscriptDefinitions";
|
||||||
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
||||||
import { NetscriptContext, InternalAPI } from "src/Netscript/APIWrapper";
|
import { NetscriptContext, InternalAPI } from "../Netscript/APIWrapper";
|
||||||
|
|
||||||
export function NetscriptStanek(
|
export function NetscriptStanek(
|
||||||
player: IPlayer,
|
player: IPlayer,
|
||||||
@ -26,12 +26,12 @@ export function NetscriptStanek(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
giftWidth: (_ctx: NetscriptContext) =>
|
giftWidth: () =>
|
||||||
function (): number {
|
function (): number {
|
||||||
checkStanekAPIAccess("giftWidth");
|
checkStanekAPIAccess("giftWidth");
|
||||||
return staneksGift.width();
|
return staneksGift.width();
|
||||||
},
|
},
|
||||||
giftHeight: (_ctx: NetscriptContext) =>
|
giftHeight: () =>
|
||||||
function (): number {
|
function (): number {
|
||||||
checkStanekAPIAccess("giftHeight");
|
checkStanekAPIAccess("giftHeight");
|
||||||
return staneksGift.height();
|
return staneksGift.height();
|
||||||
|
@ -202,7 +202,7 @@ function _getScriptUrls(script: Script, scripts: Script[], seen: Script[]): Scri
|
|||||||
|
|
||||||
// We automatically define a print function() in the NetscriptJS module so that
|
// We automatically define a print function() in the NetscriptJS module so that
|
||||||
// accidental calls to window.print() do not bring up the "print screen" dialog
|
// accidental calls to window.print() do not bring up the "print screen" dialog
|
||||||
transformedCode += `\n\nfunction print() {throw new Error("Invalid call to window.print(). Did you mean to use Netscript's print()?");}`;
|
transformedCode += `\n\nfunction print() {throw new Error("Invalid call to window.print(). Did you mean to use Netscript's print()?");}\n//# sourceURL=${script.server}/${script.filename}`;
|
||||||
|
|
||||||
const blob = URL.createObjectURL(makeScriptBlob(transformedCode));
|
const blob = URL.createObjectURL(makeScriptBlob(transformedCode));
|
||||||
// Push the blob URL onto the top of the stack.
|
// Push the blob URL onto the top of the stack.
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
* that allows for scripts to run
|
* that allows for scripts to run
|
||||||
*/
|
*/
|
||||||
import { killWorkerScript } from "./Netscript/killWorkerScript";
|
import { killWorkerScript } from "./Netscript/killWorkerScript";
|
||||||
|
import { ScriptDeath } from "./Netscript/ScriptDeath";
|
||||||
import { WorkerScript } from "./Netscript/WorkerScript";
|
import { WorkerScript } from "./Netscript/WorkerScript";
|
||||||
import { workerScripts } from "./Netscript/WorkerScripts";
|
import { workerScripts } from "./Netscript/WorkerScripts";
|
||||||
import { WorkerScriptStartStopEventEmitter } from "./Netscript/WorkerScriptStartStopEventEmitter";
|
import { WorkerScriptStartStopEventEmitter } from "./Netscript/WorkerScriptStartStopEventEmitter";
|
||||||
@ -59,7 +60,7 @@ export function prestigeWorkerScripts(): void {
|
|||||||
// JS script promises need a little massaging to have the same guarantees as netscript
|
// JS script promises need a little massaging to have the same guarantees as netscript
|
||||||
// promises. This does said massaging and kicks the script off. It returns a promise
|
// promises. This does said massaging and kicks the script off. It returns a promise
|
||||||
// that resolves or rejects when the corresponding worker script is done.
|
// that resolves or rejects when the corresponding worker script is done.
|
||||||
function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Promise<WorkerScript> {
|
function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Promise<void> {
|
||||||
workerScript.running = true;
|
workerScript.running = true;
|
||||||
|
|
||||||
// The name of the currently running netscript function, to prevent concurrent
|
// The name of the currently running netscript function, to prevent concurrent
|
||||||
@ -79,7 +80,7 @@ function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Pro
|
|||||||
// This is not a problem for legacy Netscript because it also checks the
|
// This is not a problem for legacy Netscript because it also checks the
|
||||||
// stop flag in the evaluator.
|
// stop flag in the evaluator.
|
||||||
if (workerScript.env.stopFlag) {
|
if (workerScript.env.stopFlag) {
|
||||||
throw workerScript;
|
throw new ScriptDeath(workerScript);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (propName === "asleep") return f(...args); // OK for multiple simultaneous calls to sleep.
|
if (propName === "asleep") return f(...args); // OK for multiple simultaneous calls to sleep.
|
||||||
@ -90,7 +91,7 @@ function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Pro
|
|||||||
"promise-returning function? (Currently running: %s tried to run: %s)";
|
"promise-returning function? (Currently running: %s tried to run: %s)";
|
||||||
if (runningFn) {
|
if (runningFn) {
|
||||||
workerScript.errorMessage = makeRuntimeRejectMsg(workerScript, sprintf(msg, runningFn, propName));
|
workerScript.errorMessage = makeRuntimeRejectMsg(workerScript, sprintf(msg, runningFn, propName));
|
||||||
throw workerScript;
|
throw new ScriptDeath(workerScript);
|
||||||
}
|
}
|
||||||
runningFn = propName;
|
runningFn = propName;
|
||||||
|
|
||||||
@ -116,18 +117,29 @@ function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Pro
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const prop of Object.keys(workerScript.env.vars)) {
|
function wrapObject(vars: any, ...tree: string[]): void {
|
||||||
if (typeof workerScript.env.vars[prop] !== "function") continue;
|
for (const prop of Object.keys(vars)) {
|
||||||
workerScript.env.vars[prop] = wrap(prop, workerScript.env.vars[prop]);
|
switch (typeof vars[prop]) {
|
||||||
|
case "function": {
|
||||||
|
vars[prop] = wrap([...tree, prop].join("."), vars[prop]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "object": {
|
||||||
|
if (Array.isArray(vars[prop])) continue;
|
||||||
|
wrapObject(vars[prop], ...tree, prop);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
workerScript.env.vars.stanek.charge = wrap("stanek.charge", workerScript.env.vars.stanek.charge);
|
wrapObject(workerScript.env.vars);
|
||||||
|
|
||||||
// Note: the environment that we pass to the JS script only needs to contain the functions visible
|
// Note: the environment that we pass to the JS script only needs to contain the functions visible
|
||||||
// to that script, which env.vars does at this point.
|
// to that script, which env.vars does at this point.
|
||||||
return new Promise<WorkerScript>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
executeJSScript(player, workerScript.getServer().scripts, workerScript)
|
executeJSScript(player, workerScript.getServer().scripts, workerScript)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
resolve(workerScript);
|
resolve();
|
||||||
})
|
})
|
||||||
.catch((e) => reject(e));
|
.catch((e) => reject(e));
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
@ -140,20 +152,21 @@ function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Pro
|
|||||||
e.message + ((e.stack && "\nstack:\n" + e.stack.toString()) || ""),
|
e.message + ((e.stack && "\nstack:\n" + e.stack.toString()) || ""),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
throw workerScript;
|
throw new ScriptDeath(workerScript);
|
||||||
} else if (isScriptErrorMessage(e)) {
|
} else if (isScriptErrorMessage(e)) {
|
||||||
workerScript.errorMessage = e;
|
workerScript.errorMessage = e;
|
||||||
throw workerScript;
|
throw new ScriptDeath(workerScript);
|
||||||
} else if (e instanceof WorkerScript) {
|
} else if (e instanceof ScriptDeath) {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
workerScript.errorMessage = makeRuntimeRejectMsg(workerScript, e);
|
// Don't know what to do with it, let's try making an error message out of it
|
||||||
throw workerScript; // Don't know what to do with it, let's rethrow.
|
workerScript.errorMessage = makeRuntimeRejectMsg(workerScript, "" + e);
|
||||||
|
throw new ScriptDeath(workerScript);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript> {
|
function startNetscript1Script(workerScript: WorkerScript): Promise<void> {
|
||||||
const code = workerScript.code;
|
const code = workerScript.code;
|
||||||
workerScript.running = true;
|
workerScript.running = true;
|
||||||
|
|
||||||
@ -168,7 +181,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
|||||||
workerScript.env.stopFlag = true;
|
workerScript.env.stopFlag = true;
|
||||||
workerScript.running = false;
|
workerScript.running = false;
|
||||||
killWorkerScript(workerScript);
|
killWorkerScript(workerScript);
|
||||||
return Promise.resolve(workerScript);
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
const interpreterInitialization = function (int: any, scope: any): void {
|
const interpreterInitialization = function (int: any, scope: any): void {
|
||||||
@ -201,7 +214,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
|||||||
})
|
})
|
||||||
.catch(function (err: any) {
|
.catch(function (err: any) {
|
||||||
// workerscript is when you cancel a delay
|
// workerscript is when you cancel a delay
|
||||||
if (!(err instanceof WorkerScript)) {
|
if (!(err instanceof ScriptDeath)) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
const errorTextArray = err.split("|DELIMITER|");
|
const errorTextArray = err.split("|DELIMITER|");
|
||||||
const hostname = errorTextArray[1];
|
const hostname = errorTextArray[1];
|
||||||
@ -214,7 +227,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
|||||||
workerScript.env.stopFlag = true;
|
workerScript.env.stopFlag = true;
|
||||||
workerScript.running = false;
|
workerScript.running = false;
|
||||||
killWorkerScript(workerScript);
|
killWorkerScript(workerScript);
|
||||||
return Promise.resolve(workerScript);
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -277,14 +290,14 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
|||||||
workerScript.env.stopFlag = true;
|
workerScript.env.stopFlag = true;
|
||||||
workerScript.running = false;
|
workerScript.running = false;
|
||||||
killWorkerScript(workerScript);
|
killWorkerScript(workerScript);
|
||||||
return Promise.resolve(workerScript);
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
function runInterpreter(): void {
|
function runInterpreter(): void {
|
||||||
try {
|
try {
|
||||||
if (workerScript.env.stopFlag) {
|
if (workerScript.env.stopFlag) {
|
||||||
return reject(workerScript);
|
return reject(new ScriptDeath(workerScript));
|
||||||
}
|
}
|
||||||
|
|
||||||
let more = true;
|
let more = true;
|
||||||
@ -297,7 +310,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
|||||||
if (more) {
|
if (more) {
|
||||||
setTimeout(runInterpreter, Settings.CodeInstructionRunTime);
|
setTimeout(runInterpreter, Settings.CodeInstructionRunTime);
|
||||||
} else {
|
} else {
|
||||||
resolve(workerScript);
|
resolve();
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
e = e.toString();
|
e = e.toString();
|
||||||
@ -305,7 +318,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
|||||||
e = makeRuntimeRejectMsg(workerScript, e);
|
e = makeRuntimeRejectMsg(workerScript, e);
|
||||||
}
|
}
|
||||||
workerScript.errorMessage = e;
|
workerScript.errorMessage = e;
|
||||||
return reject(workerScript);
|
return reject(new ScriptDeath(workerScript));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,11 +327,12 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
|||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
if (isString(e)) {
|
if (isString(e)) {
|
||||||
workerScript.errorMessage = e;
|
workerScript.errorMessage = e;
|
||||||
return reject(workerScript);
|
return reject(new ScriptDeath(workerScript));
|
||||||
} else if (e instanceof WorkerScript) {
|
} else if (e instanceof ScriptDeath) {
|
||||||
return reject(e);
|
return reject(e);
|
||||||
} else {
|
} else {
|
||||||
return reject(workerScript);
|
console.error(e);
|
||||||
|
return reject(new ScriptDeath(workerScript));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -541,83 +555,85 @@ function createAndAddWorkerScript(
|
|||||||
|
|
||||||
// Create the WorkerScript. NOTE: WorkerScript ctor will set the underlying
|
// Create the WorkerScript. NOTE: WorkerScript ctor will set the underlying
|
||||||
// RunningScript's PID as well
|
// RunningScript's PID as well
|
||||||
const s = new WorkerScript(runningScriptObj, pid, NetscriptFunctions);
|
const workerScript = new WorkerScript(runningScriptObj, pid, NetscriptFunctions);
|
||||||
s.ramUsage = oneRamUsage;
|
workerScript.ramUsage = oneRamUsage;
|
||||||
|
|
||||||
// Add the WorkerScript to the global pool
|
// Add the WorkerScript to the global pool
|
||||||
workerScripts.set(pid, s);
|
workerScripts.set(pid, workerScript);
|
||||||
WorkerScriptStartStopEventEmitter.emit();
|
WorkerScriptStartStopEventEmitter.emit();
|
||||||
|
|
||||||
// Start the script's execution
|
// Start the script's execution
|
||||||
let p: Promise<WorkerScript> | null = null; // Script's resulting promise
|
let scriptExecution: Promise<void> | null = null; // Script's resulting promise
|
||||||
if (s.name.endsWith(".js") || s.name.endsWith(".ns")) {
|
if (workerScript.name.endsWith(".js") || workerScript.name.endsWith(".ns")) {
|
||||||
p = startNetscript2Script(player, s);
|
scriptExecution = startNetscript2Script(player, workerScript);
|
||||||
} else {
|
} else {
|
||||||
p = startNetscript1Script(s);
|
scriptExecution = startNetscript1Script(workerScript);
|
||||||
if (!(p instanceof Promise)) {
|
if (!(scriptExecution instanceof Promise)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Once the code finishes (either resolved or rejected, doesnt matter), set its
|
// Once the code finishes (either resolved or rejected, doesnt matter), set its
|
||||||
// running status to false
|
// running status to false
|
||||||
p.then(function (w: WorkerScript) {
|
scriptExecution
|
||||||
w.running = false;
|
.then(function () {
|
||||||
w.env.stopFlag = true;
|
workerScript.running = false;
|
||||||
// On natural death, the earnings are transfered to the parent if it still exists.
|
workerScript.env.stopFlag = true;
|
||||||
if (parent !== undefined) {
|
// On natural death, the earnings are transfered to the parent if it still exists.
|
||||||
if (parent.running) {
|
if (parent !== undefined) {
|
||||||
parent.scriptRef.onlineExpGained += runningScriptObj.onlineExpGained;
|
if (parent.running) {
|
||||||
parent.scriptRef.onlineMoneyMade += runningScriptObj.onlineMoneyMade;
|
parent.scriptRef.onlineExpGained += runningScriptObj.onlineExpGained;
|
||||||
|
parent.scriptRef.onlineMoneyMade += runningScriptObj.onlineMoneyMade;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
killWorkerScript(s);
|
killWorkerScript(workerScript);
|
||||||
w.log("", () => "Script finished running");
|
workerScript.log("", () => "Script finished running");
|
||||||
}).catch(function (w) {
|
})
|
||||||
if (w instanceof Error) {
|
.catch(function (e) {
|
||||||
dialogBoxCreate("Script runtime unknown error. This is a bug please contact game developer");
|
if (e instanceof Error) {
|
||||||
console.error("Evaluating workerscript returns an Error. THIS SHOULDN'T HAPPEN: " + w.toString());
|
dialogBoxCreate("Script runtime unknown error. This is a bug please contact game developer");
|
||||||
return;
|
console.error("Evaluating workerscript returns an Error. THIS SHOULDN'T HAPPEN: " + e.toString());
|
||||||
} else if (w instanceof WorkerScript) {
|
return;
|
||||||
if (isScriptErrorMessage(w.errorMessage)) {
|
} else if (e instanceof ScriptDeath) {
|
||||||
const errorTextArray = w.errorMessage.split("|DELIMITER|");
|
if (isScriptErrorMessage(workerScript.errorMessage)) {
|
||||||
if (errorTextArray.length != 4) {
|
const errorTextArray = workerScript.errorMessage.split("|DELIMITER|");
|
||||||
console.error("ERROR: Something wrong with Error text in evaluator...");
|
if (errorTextArray.length != 4) {
|
||||||
console.error("Error text: " + w.errorMessage);
|
console.error("ERROR: Something wrong with Error text in evaluator...");
|
||||||
return;
|
console.error("Error text: " + workerScript.errorMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const hostname = errorTextArray[1];
|
||||||
|
const scriptName = errorTextArray[2];
|
||||||
|
const errorMsg = errorTextArray[3];
|
||||||
|
|
||||||
|
let msg = `RUNTIME ERROR<br>${scriptName}@${hostname} (PID - ${workerScript.pid})<br>`;
|
||||||
|
if (workerScript.args.length > 0) {
|
||||||
|
msg += `Args: ${arrayToString(workerScript.args)}<br>`;
|
||||||
|
}
|
||||||
|
msg += "<br>";
|
||||||
|
msg += errorMsg;
|
||||||
|
|
||||||
|
dialogBoxCreate(msg);
|
||||||
|
workerScript.log("", () => "Script crashed with runtime error");
|
||||||
|
} else {
|
||||||
|
workerScript.log("", () => "Script killed");
|
||||||
|
return; // Already killed, so stop here
|
||||||
}
|
}
|
||||||
const hostname = errorTextArray[1];
|
} else if (isScriptErrorMessage(e)) {
|
||||||
const scriptName = errorTextArray[2];
|
dialogBoxCreate("Script runtime unknown error. This is a bug please contact game developer");
|
||||||
const errorMsg = errorTextArray[3];
|
console.error(
|
||||||
|
"ERROR: Evaluating workerscript returns only error message rather than WorkerScript object. THIS SHOULDN'T HAPPEN: " +
|
||||||
let msg = `RUNTIME ERROR<br>${scriptName}@${hostname}<br>`;
|
e.toString(),
|
||||||
if (w.args.length > 0) {
|
);
|
||||||
msg += `Args: ${arrayToString(w.args)}<br>`;
|
return;
|
||||||
}
|
|
||||||
msg += "<br>";
|
|
||||||
msg += errorMsg;
|
|
||||||
|
|
||||||
dialogBoxCreate(msg);
|
|
||||||
w.log("", () => "Script crashed with runtime error");
|
|
||||||
} else {
|
} else {
|
||||||
w.log("", () => "Script killed");
|
dialogBoxCreate("An unknown script died for an unknown reason. This is a bug please contact game dev");
|
||||||
return; // Already killed, so stop here
|
console.error(e);
|
||||||
}
|
}
|
||||||
} else if (isScriptErrorMessage(w)) {
|
|
||||||
dialogBoxCreate("Script runtime unknown error. This is a bug please contact game developer");
|
|
||||||
console.error(
|
|
||||||
"ERROR: Evaluating workerscript returns only error message rather than WorkerScript object. THIS SHOULDN'T HAPPEN: " +
|
|
||||||
w.toString(),
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
dialogBoxCreate("An unknown script died for an unknown reason. This is a bug please contact game dev");
|
|
||||||
console.error(w);
|
|
||||||
}
|
|
||||||
|
|
||||||
killWorkerScript(s);
|
killWorkerScript(workerScript);
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ export const GraftingRoot = (): React.ReactElement => {
|
|||||||
<Typography variant="h5">Graft Augmentations</Typography>
|
<Typography variant="h5">Graft Augmentations</Typography>
|
||||||
{getAvailableAugs(player).length > 0 ? (
|
{getAvailableAugs(player).length > 0 ? (
|
||||||
<Paper sx={{ my: 1, width: "fit-content", display: "grid", gridTemplateColumns: "1fr 3fr" }}>
|
<Paper sx={{ my: 1, width: "fit-content", display: "grid", gridTemplateColumns: "1fr 3fr" }}>
|
||||||
<List sx={{ maxHeight: 400, overflowY: "scroll", borderRight: `1px solid ${Settings.theme.welllight}` }}>
|
<List sx={{ height: 400, overflowY: "scroll", borderRight: `1px solid ${Settings.theme.welllight}` }}>
|
||||||
{getAvailableAugs(player).map((k, i) => (
|
{getAvailableAugs(player).map((k, i) => (
|
||||||
<ListItemButton key={i + 1} onClick={() => setSelectedAug(k)} selected={selectedAug === k}>
|
<ListItemButton key={i + 1} onClick={() => setSelectedAug(k)} selected={selectedAug === k}>
|
||||||
<Typography>{k}</Typography>
|
<Typography>{k}</Typography>
|
||||||
|
60
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
60
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
@ -101,24 +101,46 @@ interface Player {
|
|||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
interface RunningScript {
|
export interface RunningScript {
|
||||||
|
/** Arguments the script was called with */
|
||||||
args: string[];
|
args: string[];
|
||||||
|
/** Filename of the script */
|
||||||
filename: string;
|
filename: string;
|
||||||
|
/**
|
||||||
|
* Script logs as an array. The newest log entries are at the bottom.
|
||||||
|
* Timestamps, if enabled, are placed inside `[brackets]` at the start of each line.
|
||||||
|
**/
|
||||||
logs: string[];
|
logs: string[];
|
||||||
|
/** Total amount of hacking experience earned from this script when offline */
|
||||||
offlineExpGained: number;
|
offlineExpGained: number;
|
||||||
|
/** Total amount of money made by this script when offline */
|
||||||
offlineMoneyMade: number;
|
offlineMoneyMade: number;
|
||||||
/** Offline running time of the script, in seconds **/
|
/** Number of seconds that the script has been running offline */
|
||||||
offlineRunningTime: number;
|
offlineRunningTime: number;
|
||||||
|
/** Total amount of hacking experience earned from this script when online */
|
||||||
onlineExpGained: number;
|
onlineExpGained: number;
|
||||||
|
/** Total amount of money made by this script when online */
|
||||||
onlineMoneyMade: number;
|
onlineMoneyMade: number;
|
||||||
/** Online running time of the script, in seconds **/
|
/** Number of seconds that this script has been running online */
|
||||||
onlineRunningTime: number;
|
onlineRunningTime: number;
|
||||||
|
/** Process ID. Must be an integer */
|
||||||
pid: number;
|
pid: number;
|
||||||
|
/** How much RAM this script uses for ONE thread */
|
||||||
ramUsage: number;
|
ramUsage: number;
|
||||||
|
/** Hostname of the server on which this script runs */
|
||||||
server: string;
|
server: string;
|
||||||
|
/** Number of threads that this script runs with */
|
||||||
threads: number;
|
threads: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface RecentScript extends RunningScript {
|
||||||
|
/** Timestamp of when the script was killed */
|
||||||
|
timeOfDeath: Date;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data representing the internal values of a crime.
|
* Data representing the internal values of a crime.
|
||||||
* @public
|
* @public
|
||||||
@ -2618,7 +2640,7 @@ export interface Hacknet {
|
|||||||
* // NS1:
|
* // NS1:
|
||||||
* var upgradeName = "Sell for Corporation Funds";
|
* var upgradeName = "Sell for Corporation Funds";
|
||||||
* if (hacknet.numHashes() > hacknet.hashCost(upgradeName)) {
|
* if (hacknet.numHashes() > hacknet.hashCost(upgradeName)) {
|
||||||
* hacknet.spendHashes(upgName);
|
* hacknet.spendHashes(upgradeName);
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
* @example
|
* @example
|
||||||
@ -2626,7 +2648,7 @@ export interface Hacknet {
|
|||||||
* // NS2:
|
* // NS2:
|
||||||
* const upgradeName = "Sell for Corporation Funds";
|
* const upgradeName = "Sell for Corporation Funds";
|
||||||
* if (ns.hacknet.numHashes() > ns.hacknet.hashCost(upgradeName)) {
|
* if (ns.hacknet.numHashes() > ns.hacknet.hashCost(upgradeName)) {
|
||||||
* ns.hacknet.spendHashes(upgName);
|
* ns.hacknet.spendHashes(upgradeName);
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
* @param upgName - Name of the upgrade of Hacknet Node.
|
* @param upgName - Name of the upgrade of Hacknet Node.
|
||||||
@ -4555,9 +4577,10 @@ export interface NS extends Singularity {
|
|||||||
* Returns the security increase that would occur if a hack with this many threads happened.
|
* Returns the security increase that would occur if a hack with this many threads happened.
|
||||||
*
|
*
|
||||||
* @param threads - Amount of threads that will be used.
|
* @param threads - Amount of threads that will be used.
|
||||||
|
* @param hostname - Hostname of the target server. The number of threads is limited to the number needed to hack the servers maximum amount of money.
|
||||||
* @returns The security increase.
|
* @returns The security increase.
|
||||||
*/
|
*/
|
||||||
hackAnalyzeSecurity(threads: number): number;
|
hackAnalyzeSecurity(threads: number, hostname?: string): number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the chance of successfully hacking a server.
|
* Get the chance of successfully hacking a server.
|
||||||
@ -4781,6 +4804,27 @@ export interface NS extends Singularity {
|
|||||||
*/
|
*/
|
||||||
getScriptLogs(fn?: string, host?: string, ...args: any[]): string[];
|
getScriptLogs(fn?: string, host?: string, ...args: any[]): string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of recently killed scripts across all servers.
|
||||||
|
* @remarks
|
||||||
|
* RAM cost: 0.2 GB
|
||||||
|
*
|
||||||
|
* The most recently killed script is the first element in the array.
|
||||||
|
* Note that there is a maximum number of recently killed scripts which are tracked.
|
||||||
|
* This is configurable in the game's options as `Recently killed scripts size`.
|
||||||
|
*
|
||||||
|
* @usage below:
|
||||||
|
* ```ts
|
||||||
|
* let recentScripts = ns.getRecentScripts();
|
||||||
|
* let mostRecent = recentScripts.shift()
|
||||||
|
* if (mostRecent)
|
||||||
|
* ns.tprint(mostRecent.logs.join('\n'))
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @returns Array with information about previously killed scripts.
|
||||||
|
*/
|
||||||
|
getRecentScripts(): RecentScript[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open the tail window of a script.
|
* Open the tail window of a script.
|
||||||
* @remarks
|
* @remarks
|
||||||
@ -6965,8 +7009,10 @@ interface Office {
|
|||||||
maxMor: number;
|
maxMor: number;
|
||||||
/** Name of all the employees */
|
/** Name of all the employees */
|
||||||
employees: string[];
|
employees: string[];
|
||||||
/** Positions of the employees */
|
/** Production of the employees */
|
||||||
employeeProd: EmployeeJobs;
|
employeeProd: EmployeeJobs;
|
||||||
|
/** Positions of the employees */
|
||||||
|
employeeJobs: EmployeeJobs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -981,11 +981,11 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
</Button>
|
</Button>
|
||||||
<Typography>
|
<Typography>
|
||||||
{" "}
|
{" "}
|
||||||
Documentation:{" "}
|
<strong>Documentation:</strong>{" "}
|
||||||
<Link target="_blank" href="https://bitburner.readthedocs.io/en/latest/index.html">
|
<Link target="_blank" href="https://bitburner.readthedocs.io/en/latest/index.html">
|
||||||
Basic
|
Basic
|
||||||
</Link>{" "}
|
</Link>
|
||||||
|
|
{" | "}
|
||||||
<Link target="_blank" href="https://github.com/danielyxie/bitburner/blob/dev/markdown/bitburner.ns.md">
|
<Link target="_blank" href="https://github.com/danielyxie/bitburner/blob/dev/markdown/bitburner.ns.md">
|
||||||
Full
|
Full
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -63,6 +63,11 @@ interface IDefaultSettings {
|
|||||||
*/
|
*/
|
||||||
Locale: string;
|
Locale: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Limit the number of recently killed script entries being tracked.
|
||||||
|
*/
|
||||||
|
MaxRecentScriptsCapacity: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Limit the number of log entries for each script being executed on each server.
|
* Limit the number of log entries for each script being executed on each server.
|
||||||
*/
|
*/
|
||||||
@ -191,6 +196,7 @@ export const defaultSettings: IDefaultSettings = {
|
|||||||
EnableBashHotkeys: false,
|
EnableBashHotkeys: false,
|
||||||
TimestampsFormat: "",
|
TimestampsFormat: "",
|
||||||
Locale: "en",
|
Locale: "en",
|
||||||
|
MaxRecentScriptsCapacity: 50,
|
||||||
MaxLogCapacity: 50,
|
MaxLogCapacity: 50,
|
||||||
MaxPortCapacity: 50,
|
MaxPortCapacity: 50,
|
||||||
MaxTerminalCapacity: 500,
|
MaxTerminalCapacity: 500,
|
||||||
@ -228,6 +234,7 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
|
|||||||
EnableBashHotkeys: defaultSettings.EnableBashHotkeys,
|
EnableBashHotkeys: defaultSettings.EnableBashHotkeys,
|
||||||
TimestampsFormat: defaultSettings.TimestampsFormat,
|
TimestampsFormat: defaultSettings.TimestampsFormat,
|
||||||
Locale: "en",
|
Locale: "en",
|
||||||
|
MaxRecentScriptsCapacity: defaultSettings.MaxRecentScriptsCapacity,
|
||||||
MaxLogCapacity: defaultSettings.MaxLogCapacity,
|
MaxLogCapacity: defaultSettings.MaxLogCapacity,
|
||||||
MaxPortCapacity: defaultSettings.MaxPortCapacity,
|
MaxPortCapacity: defaultSettings.MaxPortCapacity,
|
||||||
MaxTerminalCapacity: defaultSettings.MaxTerminalCapacity,
|
MaxTerminalCapacity: defaultSettings.MaxTerminalCapacity,
|
||||||
|
@ -191,8 +191,8 @@ export const HelpTexts: IMap<string[]> = {
|
|||||||
"Usage: connect [hostname]",
|
"Usage: connect [hostname]",
|
||||||
" ",
|
" ",
|
||||||
"Connect to a remote server. The hostname of the remote server must be given as the argument ",
|
"Connect to a remote server. The hostname of the remote server must be given as the argument ",
|
||||||
"to this command. Note that only servers that are immediately adjacent to the current server in the network can be connected to. To ",
|
"to this command. Note that only servers that are immediately adjacent to the current server in the network and the ones that have",
|
||||||
"see which servers can be connected to, use the 'scan' command.",
|
"a backdoor installed can be connected to. To see which servers can be connected to, use the 'scan' command.",
|
||||||
" ",
|
" ",
|
||||||
],
|
],
|
||||||
cp: ["Usage: cp [src] [dst]", " ", "Copy a file on this server. To copy a file to another server use scp.", " "],
|
cp: ["Usage: cp [src] [dst]", " ", "Copy a file on this server. To copy a file to another server use scp.", " "],
|
||||||
|
@ -4,7 +4,8 @@ import { getSubdirectories } from "./DirectoryServerHelpers";
|
|||||||
import { Aliases, GlobalAliases, substituteAliases } from "../Alias";
|
import { Aliases, GlobalAliases, substituteAliases } from "../Alias";
|
||||||
import { DarkWebItems } from "../DarkWeb/DarkWebItems";
|
import { DarkWebItems } from "../DarkWeb/DarkWebItems";
|
||||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||||
import { GetServer, GetAllServers } from "../Server/AllServers";
|
import { GetAllServers } from "../Server/AllServers";
|
||||||
|
import { Server } from "../Server/Server";
|
||||||
import { ParseCommand, ParseCommands } from "./Parser";
|
import { ParseCommand, ParseCommands } from "./Parser";
|
||||||
import { HelpTexts } from "./HelpText";
|
import { HelpTexts } from "./HelpText";
|
||||||
import { isScriptFilename } from "../Script/isScriptFilename";
|
import { isScriptFilename } from "../Script/isScriptFilename";
|
||||||
@ -238,16 +239,14 @@ export async function determineAllPossibilitiesForTabCompletion(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isCommand("connect")) {
|
if (isCommand("connect")) {
|
||||||
// All network connections
|
// All directly connected and backdoored servers are reachable
|
||||||
for (let i = 0; i < currServ.serversOnNetwork.length; ++i) {
|
console.log(GetAllServers());
|
||||||
const serv = GetServer(currServ.serversOnNetwork[i]);
|
return GetAllServers()
|
||||||
if (serv == null) {
|
.filter(
|
||||||
continue;
|
(server) =>
|
||||||
}
|
currServ.serversOnNetwork.includes(server.hostname) || (server instanceof Server && server.backdoorInstalled),
|
||||||
allPos.push(serv.hostname);
|
)
|
||||||
}
|
.map((server) => server.hostname);
|
||||||
|
|
||||||
return allPos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCommand("nano") || isCommand("vim")) {
|
if (isCommand("nano") || isCommand("vim")) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { WorkerScript } from "./Netscript/WorkerScript";
|
import { ScriptDeath } from "./Netscript/ScriptDeath";
|
||||||
import { isScriptErrorMessage } from "./NetscriptEvaluator";
|
import { isScriptErrorMessage } from "./NetscriptEvaluator";
|
||||||
import { dialogBoxCreate } from "./ui/React/DialogBox";
|
import { dialogBoxCreate } from "./ui/React/DialogBox";
|
||||||
|
|
||||||
@ -14,9 +14,9 @@ export function setupUncaughtPromiseHandler(): void {
|
|||||||
msg += "<br>";
|
msg += "<br>";
|
||||||
msg += errorMsg;
|
msg += errorMsg;
|
||||||
dialogBoxCreate(msg);
|
dialogBoxCreate(msg);
|
||||||
} else if (e.reason instanceof WorkerScript) {
|
} else if (e.reason instanceof ScriptDeath) {
|
||||||
const msg =
|
const msg =
|
||||||
`UNCAUGHT PROMISE ERROR<br>You forgot to await a promise<br>${e.reason.name}@${e.reason.hostname}<br>` +
|
`UNCAUGHT PROMISE ERROR<br>You forgot to await a promise<br>${e.reason.name}@${e.reason.hostname} (PID - ${e.reason.pid})<br>` +
|
||||||
`Maybe hack / grow / weaken ?`;
|
`Maybe hack / grow / weaken ?`;
|
||||||
dialogBoxCreate(msg);
|
dialogBoxCreate(msg);
|
||||||
}
|
}
|
||||||
|
@ -1256,7 +1256,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
|||||||
"Convert it into a binary string and encode it as a 'Hamming-Code'. eg:\n ",
|
"Convert it into a binary string and encode it as a 'Hamming-Code'. eg:\n ",
|
||||||
"Value 8 will result into binary '1000', which will be encoded",
|
"Value 8 will result into binary '1000', which will be encoded",
|
||||||
"with the pattern 'pppdpddd', where p is a paritybit and d a databit,\n",
|
"with the pattern 'pppdpddd', where p is a paritybit and d a databit,\n",
|
||||||
"or '10101' (Value 21) will result into (pppdpdddpd) '1111101011'.\n\n",
|
"or '10101' (Value 21) will result into (pppdpdddpd) '1001101011'.\n\n",
|
||||||
"NOTE: You need an parity Bit on Index 0 as an 'overall'-paritybit. \n",
|
"NOTE: You need an parity Bit on Index 0 as an 'overall'-paritybit. \n",
|
||||||
"NOTE 2: You should watch the HammingCode-video from 3Blue1Brown, which explains the 'rule' of encoding,",
|
"NOTE 2: You should watch the HammingCode-video from 3Blue1Brown, which explains the 'rule' of encoding,",
|
||||||
"including the first Index parity-bit mentioned on the first note.\n\n",
|
"including the first Index parity-bit mentioned on the first note.\n\n",
|
||||||
|
@ -57,8 +57,8 @@ export function RecentScriptAccordion(props: IProps): React.ReactElement {
|
|||||||
<ListItemText
|
<ListItemText
|
||||||
primary={
|
primary={
|
||||||
<Typography>
|
<Typography>
|
||||||
└ {recentScript.filename} (died{" "}
|
└ {recentScript.runningScript.filename} (died{" "}
|
||||||
{convertTimeMsToTimeElapsedString(new Date().getTime() - recentScript.timestamp.getTime())} ago)
|
{convertTimeMsToTimeElapsedString(new Date().getTime() - recentScript.timeOfDeath.getTime())} ago)
|
||||||
</Typography>
|
</Typography>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -78,7 +78,7 @@ export function RecentScriptAccordion(props: IProps): React.ReactElement {
|
|||||||
</TableRow>
|
</TableRow>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell className={classes.noborder} colSpan={2}>
|
<TableCell className={classes.noborder} colSpan={2}>
|
||||||
<Typography>└ Args: {arrayToString(recentScript.args)}</Typography>
|
<Typography>└ Args: {arrayToString(recentScript.runningScript.args)}</Typography>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
|
@ -13,7 +13,7 @@ export function RecentScriptsPage(): React.ReactElement {
|
|||||||
<>
|
<>
|
||||||
<Typography>List of all recently killed scripts.</Typography>
|
<Typography>List of all recently killed scripts.</Typography>
|
||||||
{recentScripts.map((r) => (
|
{recentScripts.map((r) => (
|
||||||
<RecentScriptAccordion key={r.pid} recentScript={r} />
|
<RecentScriptAccordion key={r.runningScript.pid} recentScript={r} />
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,144 +1,80 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import { Paper, Table, TableBody, Box, IconButton, Typography, Container, Tooltip } from "@mui/material";
|
||||||
|
import { MoreHoriz, Info } from "@mui/icons-material";
|
||||||
import { numeralWrapper } from "./numeralFormat";
|
import React, { useEffect, useState } from "react";
|
||||||
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
|
|
||||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
|
||||||
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
|
|
||||||
import { getPurchaseServerLimit } from "../Server/ServerPurchases";
|
|
||||||
import { HacknetServerConstants } from "../Hacknet/data/Constants";
|
|
||||||
import { StatsTable } from "./React/StatsTable";
|
|
||||||
import { Money } from "./React/Money";
|
|
||||||
import { use } from "./Context";
|
|
||||||
import { MoneySourceTracker } from "../utils/MoneySourceTracker";
|
|
||||||
import { BitNodes } from "../BitNode/BitNode";
|
import { BitNodes } from "../BitNode/BitNode";
|
||||||
|
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||||
import Typography from "@mui/material/Typography";
|
import { HacknetServerConstants } from "../Hacknet/data/Constants";
|
||||||
import Box from "@mui/material/Box";
|
import { getPurchaseServerLimit } from "../Server/ServerPurchases";
|
||||||
import IconButton from "@mui/material/IconButton";
|
import { Settings } from "../Settings/Settings";
|
||||||
import MoreHorizIcon from "@mui/icons-material/MoreHoriz";
|
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
|
||||||
|
import { MoneySourceTracker } from "../utils/MoneySourceTracker";
|
||||||
|
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
|
||||||
|
import { use } from "./Context";
|
||||||
|
import { numeralWrapper } from "./numeralFormat";
|
||||||
import { Modal } from "./React/Modal";
|
import { Modal } from "./React/Modal";
|
||||||
|
import { Money } from "./React/Money";
|
||||||
|
import { StatsRow } from "./React/StatsRow";
|
||||||
|
import { StatsTable } from "./React/StatsTable";
|
||||||
|
|
||||||
import TableBody from "@mui/material/TableBody";
|
interface EmployersModalProps {
|
||||||
import { Table, TableCell } from "./React/Table";
|
open: boolean;
|
||||||
import TableRow from "@mui/material/TableRow";
|
onClose: () => void;
|
||||||
|
|
||||||
function LastEmployer(): React.ReactElement {
|
|
||||||
const player = use.Player();
|
|
||||||
if (player.companyName) {
|
|
||||||
return <Typography>Employer at which you last worked: {player.companyName}</Typography>;
|
|
||||||
}
|
|
||||||
return <></>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function LastJob(): React.ReactElement {
|
const EmployersModal = ({ open, onClose }: EmployersModalProps): React.ReactElement => {
|
||||||
const player = use.Player();
|
const player = use.Player();
|
||||||
if (player.companyName !== "") {
|
return (
|
||||||
return <Typography>Job you last worked: {player.jobs[player.companyName]}</Typography>;
|
<Modal open={open} onClose={onClose}>
|
||||||
}
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Employers(): React.ReactElement {
|
|
||||||
const player = use.Player();
|
|
||||||
if (player.jobs && Object.keys(player.jobs).length !== 0)
|
|
||||||
return (
|
|
||||||
<>
|
<>
|
||||||
<Typography>All Employers:</Typography>
|
<Typography variant="h5">All Employers</Typography>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
{Object.keys(player.jobs).map((j) => (
|
{Object.keys(player.jobs).map((j) => (
|
||||||
<Typography key={j}> * {j}</Typography>
|
<Typography key={j}>* {j}</Typography>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</>
|
</>
|
||||||
);
|
</Modal>
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Hacknet(): React.ReactElement {
|
|
||||||
const player = use.Player();
|
|
||||||
// Can't import HacknetHelpers for some reason.
|
|
||||||
if (!(player.bitNodeN === 9 || SourceFileFlags[9] > 0)) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Typography>{`Hacknet Nodes owned: ${player.hacknetNodes.length}`}</Typography>
|
|
||||||
<br />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Typography>{`Hacknet Servers owned: ${player.hacknetNodes.length} / ${HacknetServerConstants.MaxServers}`}</Typography>
|
|
||||||
<br />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Intelligence(): React.ReactElement {
|
|
||||||
const player = use.Player();
|
|
||||||
if (player.intelligence > 0 && (player.bitNodeN === 5 || SourceFileFlags[5] > 0)) {
|
|
||||||
return (
|
|
||||||
<TableRow>
|
|
||||||
<TableCell>
|
|
||||||
<Typography>Intelligence: </Typography>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell align="right">
|
|
||||||
<Typography>{numeralWrapper.formatSkill(player.intelligence)} </Typography>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell align="right">
|
|
||||||
<Typography noWrap>({numeralWrapper.formatExp(player.intelligence_exp)} exp)</Typography>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function MultiplierTable(props: any): React.ReactElement {
|
|
||||||
function bn5Stat(r: any): JSX.Element {
|
|
||||||
if (SourceFileFlags[5] > 0 && r.length > 2 && r[1] != r[2]) {
|
|
||||||
return (
|
|
||||||
<TableCell key="2" align="right">
|
|
||||||
<Typography noWrap>({numeralWrapper.formatPercentage(r[2])})</Typography>
|
|
||||||
</TableCell>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Table size="small" padding="none">
|
|
||||||
<TableBody>
|
|
||||||
{props.rows.map((r: any) => (
|
|
||||||
<TableRow key={r[0]}>
|
|
||||||
<TableCell key="0">
|
|
||||||
<Typography noWrap>{`${r[0]} multiplier:`} </Typography>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell key="1" align="right">
|
|
||||||
<Typography noWrap>{numeralWrapper.formatPercentage(r[1])}</Typography>
|
|
||||||
</TableCell>
|
|
||||||
{bn5Stat(r)}
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface MultTableProps {
|
||||||
|
rows: (string | number)[][];
|
||||||
|
color: string;
|
||||||
|
noMargin?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function BladeburnerMults(): React.ReactElement {
|
function MultiplierTable(props: MultTableProps): React.ReactElement {
|
||||||
const player = use.Player();
|
|
||||||
if (!player.canAccessBladeburner()) return <></>;
|
|
||||||
return (
|
return (
|
||||||
<MultiplierTable
|
<Table sx={{ display: "table", width: "100%", mb: (props.noMargin ?? false) === true ? 0 : 2 }}>
|
||||||
rows={[
|
<TableBody>
|
||||||
["Bladeburner Success Chance", player.bladeburner_success_chance_mult],
|
{props.rows.map((data) => {
|
||||||
["Bladeburner Max Stamina", player.bladeburner_max_stamina_mult],
|
const mult = data[0] as string,
|
||||||
["Bladeburner Stamina Gain", player.bladeburner_stamina_gain_mult],
|
value = data[1] as number,
|
||||||
["Bladeburner Field Analysis", player.bladeburner_analysis_mult],
|
modded = data[2] as number | null;
|
||||||
]}
|
|
||||||
/>
|
if (modded && modded !== value && SourceFileFlags[5] > 0) {
|
||||||
|
return (
|
||||||
|
<StatsRow key={mult} name={mult} color={props.color} data={{}}>
|
||||||
|
<>
|
||||||
|
<Typography color={props.color}>
|
||||||
|
<span style={{ opacity: 0.5 }}>{numeralWrapper.formatPercentage(value)}</span>{" "}
|
||||||
|
{numeralWrapper.formatPercentage(modded)}
|
||||||
|
</Typography>
|
||||||
|
</>
|
||||||
|
</StatsRow>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<StatsRow
|
||||||
|
key={mult}
|
||||||
|
name={mult}
|
||||||
|
color={props.color}
|
||||||
|
data={{ content: numeralWrapper.formatPercentage(value) }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,15 +82,17 @@ function CurrentBitNode(): React.ReactElement {
|
|||||||
const player = use.Player();
|
const player = use.Player();
|
||||||
if (player.sourceFiles.length > 0) {
|
if (player.sourceFiles.length > 0) {
|
||||||
const index = "BitNode" + player.bitNodeN;
|
const index = "BitNode" + player.bitNodeN;
|
||||||
|
const currentSourceFile = player.sourceFiles.find((sourceFile) => sourceFile.n == player.bitNodeN);
|
||||||
|
const lvl = currentSourceFile ? currentSourceFile.lvl : 0;
|
||||||
return (
|
return (
|
||||||
<>
|
<Box>
|
||||||
<Typography variant="h4">
|
<Paper sx={{ p: 1 }}>
|
||||||
BitNode {player.bitNodeN}: {BitNodes[index].name}
|
<Typography variant="h5">
|
||||||
</Typography>
|
BitNode {player.bitNodeN}: {BitNodes[index].name} (Level {lvl})
|
||||||
<Typography sx={{ mx: 2 }} style={{ whiteSpace: "pre-wrap", overflowWrap: "break-word" }}>
|
</Typography>
|
||||||
{BitNodes[index].info}
|
<Typography sx={{ whiteSpace: "pre-wrap", overflowWrap: "break-word" }}>{BitNodes[index].info}</Typography>
|
||||||
</Typography>
|
</Paper>
|
||||||
</>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,6 +200,7 @@ function MoneyModal({ open, onClose }: IMoneyModalProps): React.ReactElement {
|
|||||||
export function CharacterStats(): React.ReactElement {
|
export function CharacterStats(): React.ReactElement {
|
||||||
const player = use.Player();
|
const player = use.Player();
|
||||||
const [moneyOpen, setMoneyOpen] = useState(false);
|
const [moneyOpen, setMoneyOpen] = useState(false);
|
||||||
|
const [employersOpen, setEmployersOpen] = useState(false);
|
||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
function rerender(): void {
|
function rerender(): void {
|
||||||
setRerender((old) => !old);
|
setRerender((old) => !old);
|
||||||
@ -273,229 +212,307 @@ export function CharacterStats(): React.ReactElement {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const timeRows = [
|
const timeRows = [
|
||||||
["Time played since last Augmentation:", convertTimeMsToTimeElapsedString(player.playtimeSinceLastAug)],
|
["Since last Augmentation installation", convertTimeMsToTimeElapsedString(player.playtimeSinceLastAug)],
|
||||||
];
|
];
|
||||||
if (player.sourceFiles.length > 0) {
|
if (player.sourceFiles.length > 0) {
|
||||||
timeRows.push([
|
timeRows.push(["Since last Bitnode destroyed", convertTimeMsToTimeElapsedString(player.playtimeSinceLastBitnode)]);
|
||||||
"Time played since last Bitnode destroyed:",
|
|
||||||
convertTimeMsToTimeElapsedString(player.playtimeSinceLastBitnode),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
timeRows.push(["Total Time played:", convertTimeMsToTimeElapsedString(player.totalPlaytime)]);
|
timeRows.push(["Total", convertTimeMsToTimeElapsedString(player.totalPlaytime)]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Container maxWidth="lg" disableGutters sx={{ mx: 0 }}>
|
||||||
<Typography variant="h4">General</Typography>
|
|
||||||
<Box sx={{ mx: 2 }}>
|
|
||||||
<Typography>Current City: {player.city}</Typography>
|
|
||||||
<LastEmployer />
|
|
||||||
<LastJob />
|
|
||||||
<Employers />
|
|
||||||
|
|
||||||
<Typography>
|
|
||||||
Money: <Money money={player.money} />
|
|
||||||
<IconButton onClick={() => setMoneyOpen(true)}>
|
|
||||||
<MoreHorizIcon color="info" />
|
|
||||||
</IconButton>
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<br />
|
|
||||||
<Typography variant="h4">Stats</Typography>
|
<Typography variant="h4">Stats</Typography>
|
||||||
<Box sx={{ mx: 2 }}>
|
<Box sx={{ display: "grid", gridTemplateColumns: "1fr 1fr", minWidth: "fit-content", mb: 1, gap: 1 }}>
|
||||||
<Table size="small" padding="none">
|
<Paper sx={{ p: 1 }}>
|
||||||
<TableBody>
|
<Typography variant="h5">General</Typography>
|
||||||
<TableRow>
|
<Table>
|
||||||
<TableCell>
|
<TableBody>
|
||||||
<Typography noWrap>Hacking: </Typography>
|
<StatsRow name="Current City" color={Settings.theme.primary} data={{ content: player.city }} />
|
||||||
</TableCell>
|
<StatsRow name="Money" color={Settings.theme.money} data={{}}>
|
||||||
<TableCell align="right">
|
<>
|
||||||
<Typography noWrap>{numeralWrapper.formatSkill(player.hacking)} </Typography>
|
<Money money={player.money} />
|
||||||
</TableCell>
|
<IconButton onClick={() => setMoneyOpen(true)} sx={{ p: 0 }}>
|
||||||
<TableCell align="right">
|
<MoreHoriz color="info" />
|
||||||
<Typography noWrap>({numeralWrapper.formatExp(player.hacking_exp)} exp)</Typography>
|
</IconButton>
|
||||||
</TableCell>
|
</>
|
||||||
</TableRow>
|
</StatsRow>
|
||||||
<TableRow>
|
{player.companyName ? (
|
||||||
<TableCell>
|
<>
|
||||||
<Typography noWrap>Strength: </Typography>
|
<StatsRow
|
||||||
</TableCell>
|
name="Last Employer"
|
||||||
<TableCell align="right">
|
color={Settings.theme.primary}
|
||||||
<Typography noWrap>{numeralWrapper.formatSkill(player.strength)} </Typography>
|
data={{ content: player.companyName }}
|
||||||
</TableCell>
|
/>
|
||||||
<TableCell align="right">
|
<StatsRow
|
||||||
<Typography noWrap>({numeralWrapper.formatExp(player.strength_exp)} exp)</Typography>
|
name="Last Job"
|
||||||
</TableCell>
|
color={Settings.theme.primary}
|
||||||
</TableRow>
|
data={{ content: player.jobs[player.companyName] }}
|
||||||
<TableRow>
|
/>
|
||||||
<TableCell>
|
</>
|
||||||
<Typography noWrap>Defense: </Typography>
|
) : (
|
||||||
</TableCell>
|
<></>
|
||||||
<TableCell align="right">
|
)}
|
||||||
<Typography noWrap>{numeralWrapper.formatSkill(player.defense)} </Typography>
|
{player.jobs && Object.keys(player.jobs).length !== 0 ? (
|
||||||
</TableCell>
|
<StatsRow name="All Employers" color={Settings.theme.primary} data={{}}>
|
||||||
<TableCell align="right">
|
<>
|
||||||
<Typography noWrap>({numeralWrapper.formatExp(player.defense_exp)} exp)</Typography>
|
<span style={{ color: Settings.theme.primary }}>{Object.keys(player.jobs).length} total</span>
|
||||||
</TableCell>
|
<IconButton onClick={() => setEmployersOpen(true)} sx={{ p: 0 }}>
|
||||||
</TableRow>
|
<MoreHoriz color="info" />
|
||||||
<TableRow>
|
</IconButton>
|
||||||
<TableCell>
|
</>
|
||||||
<Typography noWrap>Dexterity: </Typography>
|
</StatsRow>
|
||||||
</TableCell>
|
) : (
|
||||||
<TableCell align="right">
|
<></>
|
||||||
<Typography noWrap>{numeralWrapper.formatSkill(player.dexterity)} </Typography>
|
)}
|
||||||
</TableCell>
|
<StatsRow
|
||||||
<TableCell align="right">
|
name="Servers Owned"
|
||||||
<Typography noWrap>({numeralWrapper.formatExp(player.dexterity_exp)} exp)</Typography>
|
color={Settings.theme.primary}
|
||||||
</TableCell>
|
data={{ content: `${player.purchasedServers.length} / ${getPurchaseServerLimit()}` }}
|
||||||
</TableRow>
|
/>
|
||||||
<TableRow>
|
<StatsRow
|
||||||
<TableCell>
|
name={`Hacknet ${player.bitNodeN === 9 || SourceFileFlags[9] > 0 ? "Servers" : "Nodes"} owned`}
|
||||||
<Typography noWrap>Agility: </Typography>
|
color={Settings.theme.primary}
|
||||||
</TableCell>
|
data={{
|
||||||
<TableCell align="right">
|
content: `${player.hacknetNodes.length}${
|
||||||
<Typography noWrap>{numeralWrapper.formatSkill(player.agility)} </Typography>
|
player.bitNodeN === 9 || SourceFileFlags[9] > 0 ? ` / ${HacknetServerConstants.MaxServers}` : ""
|
||||||
</TableCell>
|
}`,
|
||||||
<TableCell align="right">
|
}}
|
||||||
<Typography noWrap>({numeralWrapper.formatExp(player.agility_exp)} exp)</Typography>
|
/>
|
||||||
</TableCell>
|
<StatsRow
|
||||||
</TableRow>
|
name="Augmentations Installed"
|
||||||
<TableRow>
|
color={Settings.theme.primary}
|
||||||
<TableCell>
|
data={{ content: String(player.augmentations.length) }}
|
||||||
<Typography noWrap>Charisma: </Typography>
|
/>
|
||||||
</TableCell>
|
</TableBody>
|
||||||
<TableCell align="right">
|
</Table>
|
||||||
<Typography noWrap>{numeralWrapper.formatSkill(player.charisma)} </Typography>
|
</Paper>
|
||||||
</TableCell>
|
<Paper sx={{ p: 1 }}>
|
||||||
<TableCell align="right">
|
<Typography variant="h5">Skills</Typography>
|
||||||
<Typography noWrap>({numeralWrapper.formatExp(player.charisma_exp)} exp)</Typography>
|
<Table>
|
||||||
</TableCell>
|
<TableBody>
|
||||||
</TableRow>
|
<StatsRow
|
||||||
<Intelligence />
|
name="Hacking"
|
||||||
</TableBody>
|
color={Settings.theme.hack}
|
||||||
</Table>
|
data={{ level: player.hacking, exp: player.hacking_exp }}
|
||||||
<br />
|
/>
|
||||||
|
<StatsRow
|
||||||
|
name="Strength"
|
||||||
|
color={Settings.theme.combat}
|
||||||
|
data={{ level: player.strength, exp: player.strength_exp }}
|
||||||
|
/>
|
||||||
|
<StatsRow
|
||||||
|
name="Defense"
|
||||||
|
color={Settings.theme.combat}
|
||||||
|
data={{ level: player.defense, exp: player.defense_exp }}
|
||||||
|
/>
|
||||||
|
<StatsRow
|
||||||
|
name="Dexterity"
|
||||||
|
color={Settings.theme.combat}
|
||||||
|
data={{ level: player.dexterity, exp: player.dexterity_exp }}
|
||||||
|
/>
|
||||||
|
<StatsRow
|
||||||
|
name="Agility"
|
||||||
|
color={Settings.theme.combat}
|
||||||
|
data={{ level: player.agility, exp: player.agility_exp }}
|
||||||
|
/>
|
||||||
|
<StatsRow
|
||||||
|
name="Charisma"
|
||||||
|
color={Settings.theme.cha}
|
||||||
|
data={{ level: player.charisma, exp: player.charisma_exp }}
|
||||||
|
/>
|
||||||
|
{player.intelligence > 0 && (player.bitNodeN === 5 || SourceFileFlags[5] > 0) && (
|
||||||
|
<StatsRow
|
||||||
|
name="Intelligence"
|
||||||
|
color={Settings.theme.int}
|
||||||
|
data={{ level: player.intelligence, exp: player.intelligence_exp }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
<br />
|
<Box sx={{ mb: 1 }}>
|
||||||
<Typography variant="h4">Multipliers</Typography>
|
<Paper sx={{ p: 1 }}>
|
||||||
<Box sx={{ mx: 2 }}>
|
<Typography variant="h5" color="primary" sx={{ display: "flex", alignItems: "center", flexWrap: "wrap" }}>
|
||||||
<MultiplierTable
|
Multipliers
|
||||||
rows={[
|
{SourceFileFlags[5] > 0 && (
|
||||||
["Hacking Chance", player.hacking_chance_mult],
|
<Tooltip
|
||||||
["Hacking Speed", player.hacking_speed_mult],
|
title={
|
||||||
[
|
<Typography>
|
||||||
"Hacking Money",
|
Displays your current multipliers.
|
||||||
player.hacking_money_mult,
|
<br />
|
||||||
player.hacking_money_mult * BitNodeMultipliers.ScriptHackMoney,
|
<br />
|
||||||
],
|
When there is a dim number next to a multiplier, that means that the multiplier in question is being
|
||||||
[
|
affected by BitNode multipliers.
|
||||||
"Hacking Growth",
|
<br />
|
||||||
player.hacking_grow_mult,
|
<br />
|
||||||
player.hacking_grow_mult * BitNodeMultipliers.ServerGrowthRate,
|
The dim number is the raw multiplier, and the undimmed number is the effective multiplier, as
|
||||||
],
|
dictated by the BitNode.
|
||||||
]}
|
</Typography>
|
||||||
/>
|
}
|
||||||
<br />
|
>
|
||||||
<MultiplierTable
|
<Info sx={{ ml: 1, mb: 0.5 }} color="info" />
|
||||||
rows={[
|
</Tooltip>
|
||||||
["Hacking Level", player.hacking_mult, player.hacking_mult * BitNodeMultipliers.HackingLevelMultiplier],
|
)}
|
||||||
["Hacking Experience", player.hacking_exp_mult, player.hacking_exp_mult * BitNodeMultipliers.HackExpGain],
|
</Typography>
|
||||||
]}
|
<Box sx={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 1 }}>
|
||||||
/>
|
<Box>
|
||||||
<br />
|
<MultiplierTable
|
||||||
|
rows={[
|
||||||
|
["Hacking Chance", player.hacking_chance_mult],
|
||||||
|
["Hacking Speed", player.hacking_speed_mult],
|
||||||
|
[
|
||||||
|
"Hacking Money",
|
||||||
|
player.hacking_money_mult,
|
||||||
|
player.hacking_money_mult * BitNodeMultipliers.ScriptHackMoney,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Hacking Growth",
|
||||||
|
player.hacking_grow_mult,
|
||||||
|
player.hacking_grow_mult * BitNodeMultipliers.ServerGrowthRate,
|
||||||
|
],
|
||||||
|
]}
|
||||||
|
color={Settings.theme.hack}
|
||||||
|
/>
|
||||||
|
<MultiplierTable
|
||||||
|
rows={[
|
||||||
|
[
|
||||||
|
"Hacking Level",
|
||||||
|
player.hacking_mult,
|
||||||
|
player.hacking_mult * BitNodeMultipliers.HackingLevelMultiplier,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Hacking Experience",
|
||||||
|
player.hacking_exp_mult,
|
||||||
|
player.hacking_exp_mult * BitNodeMultipliers.HackExpGain,
|
||||||
|
],
|
||||||
|
]}
|
||||||
|
color={Settings.theme.hack}
|
||||||
|
/>
|
||||||
|
<MultiplierTable
|
||||||
|
rows={[
|
||||||
|
[
|
||||||
|
"Strength Level",
|
||||||
|
player.strength_mult,
|
||||||
|
player.strength_mult * BitNodeMultipliers.StrengthLevelMultiplier,
|
||||||
|
],
|
||||||
|
["Strength Experience", player.strength_exp_mult],
|
||||||
|
]}
|
||||||
|
color={Settings.theme.combat}
|
||||||
|
/>
|
||||||
|
<MultiplierTable
|
||||||
|
rows={[
|
||||||
|
[
|
||||||
|
"Defense Level",
|
||||||
|
player.defense_mult,
|
||||||
|
player.defense_mult * BitNodeMultipliers.DefenseLevelMultiplier,
|
||||||
|
],
|
||||||
|
["Defense Experience", player.defense_exp_mult],
|
||||||
|
]}
|
||||||
|
color={Settings.theme.combat}
|
||||||
|
/>
|
||||||
|
<MultiplierTable
|
||||||
|
rows={[
|
||||||
|
[
|
||||||
|
"Dexterity Level",
|
||||||
|
player.dexterity_mult,
|
||||||
|
player.dexterity_mult * BitNodeMultipliers.DexterityLevelMultiplier,
|
||||||
|
],
|
||||||
|
["Dexterity Experience", player.dexterity_exp_mult],
|
||||||
|
]}
|
||||||
|
color={Settings.theme.combat}
|
||||||
|
/>
|
||||||
|
<MultiplierTable
|
||||||
|
rows={[
|
||||||
|
[
|
||||||
|
"Agility Level",
|
||||||
|
player.agility_mult,
|
||||||
|
player.agility_mult * BitNodeMultipliers.AgilityLevelMultiplier,
|
||||||
|
],
|
||||||
|
["Agility Experience", player.agility_exp_mult],
|
||||||
|
]}
|
||||||
|
color={Settings.theme.combat}
|
||||||
|
/>
|
||||||
|
<MultiplierTable
|
||||||
|
rows={[
|
||||||
|
[
|
||||||
|
"Charisma Level",
|
||||||
|
player.charisma_mult,
|
||||||
|
player.charisma_mult * BitNodeMultipliers.CharismaLevelMultiplier,
|
||||||
|
],
|
||||||
|
["Charisma Experience", player.charisma_exp_mult],
|
||||||
|
]}
|
||||||
|
color={Settings.theme.cha}
|
||||||
|
noMargin
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
<MultiplierTable
|
<Box>
|
||||||
rows={[
|
<MultiplierTable
|
||||||
["Strength Level", player.strength_mult, player.strength_mult * BitNodeMultipliers.StrengthLevelMultiplier],
|
rows={[
|
||||||
["Strength Experience", player.strength_exp_mult],
|
[
|
||||||
]}
|
"Hacknet Node production",
|
||||||
/>
|
player.hacknet_node_money_mult,
|
||||||
<br />
|
player.hacknet_node_money_mult * BitNodeMultipliers.HacknetNodeMoney,
|
||||||
|
],
|
||||||
<MultiplierTable
|
["Hacknet Node purchase cost", player.hacknet_node_purchase_cost_mult],
|
||||||
rows={[
|
["Hacknet Node RAM upgrade cost", player.hacknet_node_ram_cost_mult],
|
||||||
["Defense Level", player.defense_mult, player.defense_mult * BitNodeMultipliers.DefenseLevelMultiplier],
|
["Hacknet Node Core purchase cost", player.hacknet_node_core_cost_mult],
|
||||||
["Defense Experience", player.defense_exp_mult],
|
["Hacknet Node level upgrade cost", player.hacknet_node_level_cost_mult],
|
||||||
]}
|
]}
|
||||||
/>
|
color={Settings.theme.primary}
|
||||||
<br />
|
/>
|
||||||
|
<MultiplierTable
|
||||||
<MultiplierTable
|
rows={[
|
||||||
rows={[
|
["Company reputation gain", player.company_rep_mult],
|
||||||
[
|
[
|
||||||
"Dexterity Level",
|
"Faction reputation gain",
|
||||||
player.dexterity_mult,
|
player.faction_rep_mult,
|
||||||
player.dexterity_mult * BitNodeMultipliers.DexterityLevelMultiplier,
|
player.faction_rep_mult * BitNodeMultipliers.FactionWorkRepGain,
|
||||||
],
|
],
|
||||||
["Dexterity Experience", player.dexterity_exp_mult],
|
["Salary", player.work_money_mult, player.work_money_mult * BitNodeMultipliers.CompanyWorkMoney],
|
||||||
]}
|
]}
|
||||||
/>
|
color={Settings.theme.money}
|
||||||
<br />
|
/>
|
||||||
|
<MultiplierTable
|
||||||
<MultiplierTable
|
rows={[
|
||||||
rows={[
|
["Crime success", player.crime_success_mult],
|
||||||
["Agility Level", player.agility_mult, player.agility_mult * BitNodeMultipliers.AgilityLevelMultiplier],
|
["Crime money", player.crime_money_mult, player.crime_money_mult * BitNodeMultipliers.CrimeMoney],
|
||||||
["Agility Experience", player.agility_exp_mult],
|
]}
|
||||||
]}
|
color={Settings.theme.combat}
|
||||||
/>
|
/>
|
||||||
<br />
|
{player.canAccessBladeburner() && (
|
||||||
|
<MultiplierTable
|
||||||
<MultiplierTable
|
rows={[
|
||||||
rows={[
|
["Bladeburner Success Chance", player.bladeburner_success_chance_mult],
|
||||||
["Charisma Level", player.charisma_mult, player.charisma_mult * BitNodeMultipliers.CharismaLevelMultiplier],
|
["Bladeburner Max Stamina", player.bladeburner_max_stamina_mult],
|
||||||
["Charisma Experience", player.charisma_exp_mult],
|
["Bladeburner Stamina Gain", player.bladeburner_stamina_gain_mult],
|
||||||
]}
|
["Bladeburner Field Analysis", player.bladeburner_analysis_mult],
|
||||||
/>
|
]}
|
||||||
<br />
|
color={Settings.theme.primary}
|
||||||
|
noMargin
|
||||||
<MultiplierTable
|
/>
|
||||||
rows={[
|
)}
|
||||||
[
|
</Box>
|
||||||
"Hacknet Node production",
|
</Box>
|
||||||
player.hacknet_node_money_mult,
|
</Paper>
|
||||||
player.hacknet_node_money_mult * BitNodeMultipliers.HacknetNodeMoney,
|
|
||||||
],
|
|
||||||
["Hacknet Node purchase cost", player.hacknet_node_purchase_cost_mult],
|
|
||||||
["Hacknet Node RAM upgrade cost", player.hacknet_node_ram_cost_mult],
|
|
||||||
["Hacknet Node Core purchase cost", player.hacknet_node_core_cost_mult],
|
|
||||||
["Hacknet Node level upgrade cost", player.hacknet_node_level_cost_mult],
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<MultiplierTable
|
|
||||||
rows={[
|
|
||||||
["Company reputation gain", player.company_rep_mult],
|
|
||||||
[
|
|
||||||
"Faction reputation gain",
|
|
||||||
player.faction_rep_mult,
|
|
||||||
player.faction_rep_mult * BitNodeMultipliers.FactionWorkRepGain,
|
|
||||||
],
|
|
||||||
["Salary", player.work_money_mult, player.work_money_mult * BitNodeMultipliers.CompanyWorkMoney],
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<MultiplierTable
|
|
||||||
rows={[
|
|
||||||
["Crime success", player.crime_success_mult],
|
|
||||||
["Crime money", player.crime_money_mult, player.crime_money_mult * BitNodeMultipliers.CrimeMoney],
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<BladeburnerMults />
|
|
||||||
</Box>
|
</Box>
|
||||||
<br />
|
|
||||||
|
|
||||||
<Typography variant="h4">Misc</Typography>
|
<Box sx={{ mb: 1 }}>
|
||||||
<Box sx={{ mx: 2 }}>
|
<Paper sx={{ p: 1 }}>
|
||||||
<Typography>{`Servers owned: ${player.purchasedServers.length} / ${getPurchaseServerLimit()}`}</Typography>
|
<Typography variant="h5">Time Played</Typography>
|
||||||
<Hacknet />
|
<Table>
|
||||||
<Typography>{`Augmentations installed: ${player.augmentations.length}`}</Typography>
|
<TableBody>
|
||||||
<StatsTable rows={timeRows} />
|
{timeRows.map(([name, content]) => (
|
||||||
|
<StatsRow key={name} name={name} color={Settings.theme.primary} data={{ content: content }} />
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
<br />
|
|
||||||
<CurrentBitNode />
|
<CurrentBitNode />
|
||||||
<MoneyModal open={moneyOpen} onClose={() => setMoneyOpen(false)} />
|
<MoneyModal open={moneyOpen} onClose={() => setMoneyOpen(false)} />
|
||||||
</>
|
<EmployersModal open={employersOpen} onClose={() => setEmployersOpen(false)} />
|
||||||
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -491,7 +491,6 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
|||||||
}}
|
}}
|
||||||
installAugmentationsFn={() => {
|
installAugmentationsFn={() => {
|
||||||
installAugmentations();
|
installAugmentations();
|
||||||
Router.toTerminal();
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -64,6 +64,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
|||||||
const importInput = useRef<HTMLInputElement>(null);
|
const importInput = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const [execTime, setExecTime] = useState(Settings.CodeInstructionRunTime);
|
const [execTime, setExecTime] = useState(Settings.CodeInstructionRunTime);
|
||||||
|
const [recentScriptsSize, setRecentScriptsSize] = useState(Settings.MaxRecentScriptsCapacity);
|
||||||
const [logSize, setLogSize] = useState(Settings.MaxLogCapacity);
|
const [logSize, setLogSize] = useState(Settings.MaxLogCapacity);
|
||||||
const [portSize, setPortSize] = useState(Settings.MaxPortCapacity);
|
const [portSize, setPortSize] = useState(Settings.MaxPortCapacity);
|
||||||
const [terminalSize, setTerminalSize] = useState(Settings.MaxTerminalCapacity);
|
const [terminalSize, setTerminalSize] = useState(Settings.MaxTerminalCapacity);
|
||||||
@ -79,6 +80,11 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
|||||||
Settings.CodeInstructionRunTime = newValue as number;
|
Settings.CodeInstructionRunTime = newValue as number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleRecentScriptsSizeChange(event: any, newValue: number | number[]): void {
|
||||||
|
setRecentScriptsSize(newValue as number);
|
||||||
|
Settings.MaxRecentScriptsCapacity = newValue as number;
|
||||||
|
}
|
||||||
|
|
||||||
function handleLogSizeChange(event: any, newValue: number | number[]): void {
|
function handleLogSizeChange(event: any, newValue: number | number[]): void {
|
||||||
setLogSize(newValue as number);
|
setLogSize(newValue as number);
|
||||||
Settings.MaxLogCapacity = newValue as number;
|
Settings.MaxLogCapacity = newValue as number;
|
||||||
@ -176,6 +182,24 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
|||||||
max={100}
|
max={100}
|
||||||
valueLabelDisplay="auto"
|
valueLabelDisplay="auto"
|
||||||
/>
|
/>
|
||||||
|
<Tooltip
|
||||||
|
title={
|
||||||
|
<Typography>
|
||||||
|
The maximum number of recently killed script entries being tracked. Setting this too high can
|
||||||
|
cause the game to use a lot of memory.
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Typography>Recently killed scripts size</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
<Slider
|
||||||
|
value={recentScriptsSize}
|
||||||
|
onChange={handleRecentScriptsSizeChange}
|
||||||
|
step={25}
|
||||||
|
min={0}
|
||||||
|
max={500}
|
||||||
|
valueLabelDisplay="auto"
|
||||||
|
/>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
<Typography>
|
<Typography>
|
||||||
|
@ -17,9 +17,10 @@ interface IProps {
|
|||||||
color: string;
|
color: string;
|
||||||
classes?: any;
|
classes?: any;
|
||||||
data: ITableRowData;
|
data: ITableRowData;
|
||||||
|
children?: React.ReactElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StatsRow = ({ name, color, classes = useStyles(), data }: IProps): React.ReactElement => {
|
export const StatsRow = ({ name, color, classes = useStyles(), children, data }: IProps): React.ReactElement => {
|
||||||
let content;
|
let content;
|
||||||
|
|
||||||
if (data.content !== undefined) {
|
if (data.content !== undefined) {
|
||||||
@ -36,7 +37,8 @@ export const StatsRow = ({ name, color, classes = useStyles(), data }: IProps):
|
|||||||
<Typography style={{ color: color }}>{name}</Typography>
|
<Typography style={{ color: color }}>{name}</Typography>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||||
<Typography style={{ color: color }}>{content}</Typography>
|
{content ? <Typography style={{ color: color }}>{content}</Typography> : <></>}
|
||||||
|
{children}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
|
@ -42,10 +42,10 @@ describe("determineAllPossibilitiesForTabCompletion", function () {
|
|||||||
requiredHackingSkill: 1,
|
requiredHackingSkill: 1,
|
||||||
serverGrowth: 3000,
|
serverGrowth: 3000,
|
||||||
});
|
});
|
||||||
Player.getHomeComputer().serversOnNetwork.push(closeServer.ip);
|
Player.getHomeComputer().serversOnNetwork.push(closeServer.hostname);
|
||||||
closeServer.serversOnNetwork.push(Player.getHomeComputer().ip);
|
closeServer.serversOnNetwork.push(Player.getHomeComputer().hostname);
|
||||||
closeServer.serversOnNetwork.push(farServer.ip);
|
closeServer.serversOnNetwork.push(farServer.hostname);
|
||||||
farServer.serversOnNetwork.push(closeServer.ip);
|
farServer.serversOnNetwork.push(closeServer.hostname);
|
||||||
AddToAllServers(closeServer);
|
AddToAllServers(closeServer);
|
||||||
AddToAllServers(farServer);
|
AddToAllServers(farServer);
|
||||||
});
|
});
|
||||||
|
22
tools/package-electron.sh
Normal file
22
tools/package-electron.sh
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
# Clear out any files remaining from old builds
|
||||||
|
rm -rf .package
|
||||||
|
|
||||||
|
mkdir -p .package/dist/ || true
|
||||||
|
|
||||||
|
cp index.html .package
|
||||||
|
cp favicon.ico .package
|
||||||
|
cp -r electron/* .package
|
||||||
|
cp -r dist .package
|
||||||
|
|
||||||
|
# Install electron sub-dependencies
|
||||||
|
cd electron
|
||||||
|
npm install
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
BUILD_PLATFORM="${1:-"all"}"
|
||||||
|
# And finally build the app.
|
||||||
|
npm run electron:packager-$BUILD_PLATFORM
|
@ -11,7 +11,7 @@ module.exports = (env, argv) => {
|
|||||||
const runInContainer = (env || {}).runInContainer === true;
|
const runInContainer = (env || {}).runInContainer === true;
|
||||||
const isDevelopment = argv.mode === "development";
|
const isDevelopment = argv.mode === "development";
|
||||||
const isFastRefresh = argv.fast === "true";
|
const isFastRefresh = argv.fast === "true";
|
||||||
const outputDirectory = isDevServer ? "dist-dev" : "dist";
|
const outputDirectory = "dist";
|
||||||
const entry = "./src/index.tsx";
|
const entry = "./src/index.tsx";
|
||||||
|
|
||||||
const statsConfig = {
|
const statsConfig = {
|
||||||
@ -46,6 +46,48 @@ module.exports = (env, argv) => {
|
|||||||
// https://stackoverflow.com/a/38401256
|
// https://stackoverflow.com/a/38401256
|
||||||
const commitHash = require("child_process").execSync("git rev-parse --short HEAD").toString().trim();
|
const commitHash = require("child_process").execSync("git rev-parse --short HEAD").toString().trim();
|
||||||
|
|
||||||
|
const htmlConfig = {
|
||||||
|
title: "Bitburner",
|
||||||
|
template: "src/index.html",
|
||||||
|
favicon: "favicon.ico",
|
||||||
|
googleAnalytics: {
|
||||||
|
trackingId: "UA-100157497-1",
|
||||||
|
},
|
||||||
|
meta: {},
|
||||||
|
minify: isDevelopment
|
||||||
|
? false
|
||||||
|
: {
|
||||||
|
collapseBooleanAttributes: true,
|
||||||
|
collapseInlineTagWhitespace: false,
|
||||||
|
collapseWhitespace: false,
|
||||||
|
conservativeCollapse: false,
|
||||||
|
html5: true,
|
||||||
|
includeAutoGeneratedTags: false,
|
||||||
|
keepClosingSlash: true,
|
||||||
|
minifyCSS: false,
|
||||||
|
minifyJS: false,
|
||||||
|
minifyURLs: false,
|
||||||
|
preserveLineBreaks: false,
|
||||||
|
preventAttributesEscaping: false,
|
||||||
|
processConditionalComments: false,
|
||||||
|
quoteCharacter: '"',
|
||||||
|
removeAttributeQuotes: false,
|
||||||
|
removeComments: false,
|
||||||
|
removeEmptyAttributes: false,
|
||||||
|
removeEmptyElements: false,
|
||||||
|
removeOptionalTags: false,
|
||||||
|
removeScriptTypeAttributes: false,
|
||||||
|
removeStyleLinkTypeAttributes: false,
|
||||||
|
removeTagWhitespace: false,
|
||||||
|
sortAttributes: false,
|
||||||
|
sortClassName: false,
|
||||||
|
useShortDoctype: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (!isDevelopment) {
|
||||||
|
htmlConfig.filename = "../index.html";
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.DefinePlugin({
|
new webpack.DefinePlugin({
|
||||||
@ -60,44 +102,7 @@ module.exports = (env, argv) => {
|
|||||||
jQuery: "jquery",
|
jQuery: "jquery",
|
||||||
$: "jquery",
|
$: "jquery",
|
||||||
}),
|
}),
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin(htmlConfig),
|
||||||
title: "Bitburner",
|
|
||||||
template: "src/index.html",
|
|
||||||
favicon: "favicon.ico",
|
|
||||||
googleAnalytics: {
|
|
||||||
trackingId: "UA-100157497-1",
|
|
||||||
},
|
|
||||||
meta: {},
|
|
||||||
minify: isDevelopment
|
|
||||||
? false
|
|
||||||
: {
|
|
||||||
collapseBooleanAttributes: true,
|
|
||||||
collapseInlineTagWhitespace: false,
|
|
||||||
collapseWhitespace: false,
|
|
||||||
conservativeCollapse: false,
|
|
||||||
html5: true,
|
|
||||||
includeAutoGeneratedTags: false,
|
|
||||||
keepClosingSlash: true,
|
|
||||||
minifyCSS: false,
|
|
||||||
minifyJS: false,
|
|
||||||
minifyURLs: false,
|
|
||||||
preserveLineBreaks: false,
|
|
||||||
preventAttributesEscaping: false,
|
|
||||||
processConditionalComments: false,
|
|
||||||
quoteCharacter: '"',
|
|
||||||
removeAttributeQuotes: false,
|
|
||||||
removeComments: false,
|
|
||||||
removeEmptyAttributes: false,
|
|
||||||
removeEmptyElements: false,
|
|
||||||
removeOptionalTags: false,
|
|
||||||
removeScriptTypeAttributes: false,
|
|
||||||
removeStyleLinkTypeAttributes: false,
|
|
||||||
removeTagWhitespace: false,
|
|
||||||
sortAttributes: false,
|
|
||||||
sortClassName: false,
|
|
||||||
useShortDoctype: false,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
new MiniCssExtractPlugin({
|
new MiniCssExtractPlugin({
|
||||||
filename: "[name].css",
|
filename: "[name].css",
|
||||||
}),
|
}),
|
||||||
@ -136,7 +141,7 @@ module.exports = (env, argv) => {
|
|||||||
// },
|
// },
|
||||||
entry: entry,
|
entry: entry,
|
||||||
output: {
|
output: {
|
||||||
path: path.resolve(__dirname, "./"),
|
path: path.resolve(__dirname, outputDirectory),
|
||||||
filename: "[name].bundle.js",
|
filename: "[name].bundle.js",
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
@ -161,7 +166,7 @@ module.exports = (env, argv) => {
|
|||||||
loader: "file-loader",
|
loader: "file-loader",
|
||||||
options: {
|
options: {
|
||||||
name: "[contenthash].[ext]",
|
name: "[contenthash].[ext]",
|
||||||
outputPath: "dist/images",
|
outputPath: "images",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -184,7 +189,7 @@ module.exports = (env, argv) => {
|
|||||||
cacheGroups: {
|
cacheGroups: {
|
||||||
vendor: {
|
vendor: {
|
||||||
test: /[\\/]node_modules[\\/]/,
|
test: /[\\/]node_modules[\\/]/,
|
||||||
name: `${outputDirectory}/vendor`,
|
name: `vendor`,
|
||||||
chunks: "all",
|
chunks: "all",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user