Merge pull request #1380 from danielyxie/dev

all typescript
This commit is contained in:
hydroflame 2021-09-25 11:24:13 -04:00 committed by GitHub
commit b7f1572288
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
92 changed files with 3346 additions and 2987 deletions

@ -4,3 +4,4 @@ dist/
tests/*.bundle.*
src/ThirdParty/*
src/JSInterpreter.js
main.bundle.js

@ -100,6 +100,7 @@
flex: row nowrap;
align-items: center;
justify-content: space-between;
cursor: grab;
}
.log-box-log-container {

40
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

@ -23,7 +23,7 @@ function createWindow() {
win.webContents.on("new-window", function (e, url) {
// make sure local urls stay in electron perimeter
if ("file://" === url.substr(0, "file://".length)) {
if (url.substr(0, "file://".length) === "file://") {
return;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -2178,7 +2178,8 @@ input[type="checkbox"] {
display: flex;
flex: row nowrap;
align-items: center;
justify-content: space-between; }
justify-content: space-between;
cursor: grab; }
.log-box-log-container {
overflow-y: auto; }

File diff suppressed because one or more lines are too long

@ -2,25 +2,25 @@ const numSpaces = 4;
const maxLineLength = 160;
module.exports = {
env: {
es6: true,
node: true,
"env": {
"es6": true,
"node": true
},
extends: "eslint:recommended",
parserOptions: {
ecmaFeatures: {
experimentalObjectRestSpread: true,
"extends": "eslint:recommended",
"parserOptions": {
"ecmaFeatures": {
"experimentalObjectRestSpread": true
},
ecmaVersion: 8,
sourceType: "module",
"ecmaVersion": 8,
"sourceType": "module"
},
rules: {
"rules": {
"accessor-pairs": [
"error",
{
getWithoutSet: false,
setWithoutGet: true,
},
"getWithoutSet": false,
"setWithoutGet": true
}
],
"array-bracket-newline": ["error"],
"array-bracket-spacing": ["error"],
@ -33,35 +33,50 @@ module.exports = {
"block-spacing": ["error"],
"brace-style": ["error"],
"callback-return": ["error"],
camelcase: ["error"],
"camelcase": ["error"],
"capitalized-comments": ["error"],
"class-methods-use-this": ["error"],
"comma-dangle": ["error"],
"comma-spacing": ["error"],
"comma-style": ["error", "last"],
complexity: ["error"],
"computed-property-spacing": ["error", "never"],
"comma-style": [
"error",
"last"
],
"complexity": ["error"],
"computed-property-spacing": [
"error",
"never"
],
"consistent-return": ["error"],
"consistent-this": ["error"],
"constructor-super": ["error"],
curly: ["error"],
"curly": ["error"],
"default-case": ["error"],
"dot-location": ["error", "property"],
"dot-location": [
"error",
"property"
],
"dot-notation": ["error"],
"eol-last": ["error"],
eqeqeq: ["error"],
"eqeqeq": ["error"],
"for-direction": ["error"],
"func-call-spacing": ["error"],
"func-name-matching": ["error"],
"func-names": ["error", "never"],
"func-names": [
"error",
"never"
],
"func-style": ["error"],
"function-paren-newline": ["error"],
"generator-star-spacing": ["error", "before"],
"generator-star-spacing": [
"error",
"before"
],
"getter-return": [
"error",
{
allowImplicit: false,
},
"allowImplicit": false
}
],
"global-require": ["error"],
"guard-for-in": ["error"],
@ -69,37 +84,52 @@ module.exports = {
"id-blacklist": ["error"],
"id-length": ["error"],
"id-match": ["error"],
"implicit-arrow-linebreak": ["error", "beside"],
indent: [
"implicit-arrow-linebreak": [
"error",
"beside"
],
"indent": [
"error",
numSpaces,
{
SwitchCase: 1,
},
"SwitchCase": 1
}
],
"init-declarations": ["error"],
"jsx-quotes": ["error"],
"key-spacing": ["error"],
"keyword-spacing": ["error"],
"line-comment-position": ["error"],
"linebreak-style": ["error", "windows"],
"linebreak-style": [
"error",
"windows"
],
"lines-around-comment": ["error"],
"lines-between-class-members": ["error"],
"max-depth": ["error"],
"max-len": ["error", maxLineLength],
"max-len": [
"error",
maxLineLength
],
"max-lines": [
"error",
{
skipBlankLines: true,
skipComments: true,
},
"skipBlankLines": true,
"skipComments": true
}
],
"max-nested-callbacks": ["error"],
"max-params": ["error"],
"max-statements": ["error"],
"max-statements-per-line": ["error"],
"multiline-comment-style": ["off", "starred-block"],
"multiline-ternary": ["error", "never"],
"multiline-comment-style": [
"off",
"starred-block"
],
"multiline-ternary": [
"error",
"never"
],
"new-cap": ["error"],
"new-parens": ["error"],
// TODO: configure this...
@ -115,15 +145,18 @@ module.exports = {
"no-catch-shadow": ["error"],
"no-class-assign": ["error"],
"no-compare-neg-zero": ["error"],
"no-cond-assign": ["error", "except-parens"],
"no-cond-assign": [
"error",
"except-parens"
],
"no-confusing-arrow": ["error"],
"no-console": ["error"],
"no-const-assign": ["error"],
"no-constant-condition": [
"error",
{
checkLoops: false,
},
"checkLoops": false
}
],
"no-continue": ["off"],
"no-control-regex": ["error"],
@ -137,15 +170,15 @@ module.exports = {
"no-duplicate-imports": [
"error",
{
includeExports: true,
},
"includeExports": true
}
],
"no-else-return": ["error"],
"no-empty": [
"error",
{
allowEmptyCatch: false,
},
"allowEmptyCatch": false
}
],
"no-empty-character-class": ["error"],
"no-empty-function": ["error"],
@ -161,8 +194,8 @@ module.exports = {
"error",
"all",
{
conditionalAssign: false,
},
"conditionalAssign": false
}
],
"no-extra-semi": ["error"],
"no-fallthrough": ["error"],
@ -173,17 +206,20 @@ module.exports = {
"no-implicit-globals": ["error"],
"no-implied-eval": ["error"],
"no-inline-comments": ["error"],
"no-inner-declarations": ["error", "both"],
"no-inner-declarations": [
"error",
"both"
],
"no-invalid-regexp": ["error"],
"no-invalid-this": ["error"],
"no-irregular-whitespace": [
"error",
{
skipComments: false,
skipRegExps: false,
skipStrings: false,
skipTemplates: false,
},
"skipComments": false,
"skipRegExps": false,
"skipStrings": false,
"skipTemplates": false
}
],
"no-iterator": ["error"],
"no-label-var": ["error"],
@ -194,9 +230,13 @@ module.exports = {
"no-magic-numbers": [
"error",
{
ignore: [-1, 0, 1],
ignoreArrayIndexes: true,
},
"ignore": [
-1,
0,
1
],
"ignoreArrayIndexes": true
}
],
"no-mixed-operators": ["error"],
"no-mixed-requires": ["error"],
@ -207,8 +247,8 @@ module.exports = {
"no-multiple-empty-lines": [
"error",
{
max: 1,
},
"max": 1
}
],
"no-native-reassign": ["error"],
"no-negated-condition": ["error"],
@ -228,8 +268,8 @@ module.exports = {
"no-plusplus": [
"error",
{
allowForLoopAfterthoughts: true,
},
"allowForLoopAfterthoughts": true
}
],
"no-process-env": ["error"],
"no-process-exit": ["error"],
@ -243,10 +283,10 @@ module.exports = {
"no-restricted-properties": [
"error",
{
message: "'log' is too general, use an appropriate level when logging.",
object: "console",
property: "log",
},
"message": "'log' is too general, use an appropriate level when logging.",
"object": "console",
"property": "log"
}
],
"no-restricted-syntax": ["error"],
"no-return-assign": ["error"],
@ -255,8 +295,8 @@ module.exports = {
"no-self-assign": [
"error",
{
props: false,
},
"props": false
}
],
"no-self-compare": ["error"],
"no-sequences": ["error"],
@ -293,10 +333,10 @@ module.exports = {
"no-useless-rename": [
"error",
{
ignoreDestructuring: false,
ignoreExport: false,
ignoreImport: false,
},
"ignoreDestructuring": false,
"ignoreExport": false,
"ignoreImport": false
}
],
"no-useless-return": ["error"],
"no-var": ["error"],
@ -304,7 +344,10 @@ module.exports = {
"no-warning-comments": ["error"],
"no-whitespace-before-property": ["error"],
"no-with": ["error"],
"nonblock-statement-body-position": ["error", "below"],
"nonblock-statement-body-position": [
"error",
"below"
],
"object-curly-newline": ["error"],
"object-curly-spacing": ["error"],
"object-property-newline": ["error"],
@ -312,7 +355,10 @@ module.exports = {
"one-var": ["off"],
"one-var-declaration-per-line": ["error"],
"operator-assignment": ["error"],
"operator-linebreak": ["error", "none"],
"operator-linebreak": [
"error",
"none"
],
"padded-blocks": ["off"],
"padding-line-between-statements": ["error"],
"prefer-arrow-callback": ["error"],
@ -325,15 +371,24 @@ module.exports = {
"prefer-spread": ["error"],
"prefer-template": ["error"],
"quote-props": ["error"],
quotes: ["error"],
radix: ["error", "as-needed"],
"quotes": ["error"],
"radix": [
"error",
"as-needed"
],
"require-await": ["error"],
"require-jsdoc": ["off"],
"require-yield": ["error"],
"rest-spread-spacing": ["error", "never"],
semi: ["error"],
"rest-spread-spacing": [
"error",
"never"
],
"semi": ["error"],
"semi-spacing": ["error"],
"semi-style": ["error", "last"],
"semi-style": [
"error",
"last"
],
"sort-imports": ["error"],
"sort-keys": ["error"],
"sort-vars": ["error"],
@ -343,25 +398,37 @@ module.exports = {
"space-infix-ops": ["error"],
"space-unary-ops": ["error"],
"spaced-comment": ["error"],
strict: ["error"],
"strict": ["error"],
"switch-colon-spacing": [
"error",
{
after: true,
before: false,
},
"after": true,
"before": false
}
],
"symbol-description": ["error"],
"template-curly-spacing": ["error"],
"template-tag-spacing": ["error"],
"unicode-bom": ["error", "never"],
"unicode-bom": [
"error",
"never"
],
"use-isnan": ["error"],
"valid-jsdoc": ["error"],
"valid-typeof": ["error"],
"vars-on-top": ["error"],
"wrap-iife": ["error", "any"],
"wrap-iife": [
"error",
"any"
],
"wrap-regex": ["error"],
"yield-star-spacing": ["error", "before"],
yoda: ["error", "never"],
},
"yield-star-spacing": [
"error",
"before"
],
"yoda": [
"error",
"never"
]
}
};

@ -8,18 +8,16 @@ const path = require("path");
const exec = require("child_process").exec;
const semver = require("./semver");
const getPackageJson = () =>
new Promise((resolve, reject) => {
const getPackageJson = () => new Promise((resolve, reject) => {
try {
/* eslint-disable-next-line global-require */
resolve(require(path.resolve(process.cwd(), "package.json")));
} catch (error) {
reject(error);
}
});
});
const getEngines = (data) =>
new Promise((resolve, reject) => {
const getEngines = (data) => new Promise((resolve, reject) => {
let versions = null;
if (data.engines) {
@ -31,10 +29,9 @@ const getEngines = (data) =>
} else {
reject("Missing or improper 'engines' property in 'package.json'");
}
});
});
const checkNpmVersion = (engines) =>
new Promise((resolve, reject) => {
const checkNpmVersion = (engines) => new Promise((resolve, reject) => {
exec("npm -v", (error, stdout, stderr) => {
if (error) {
reject(`Unable to find NPM version\n${stderr}`);
@ -46,25 +43,20 @@ const checkNpmVersion = (engines) =>
if (semver.satisfies(npmVersion, engineVersion)) {
resolve();
} else {
reject(
`Incorrect npm version\n'package.json' specifies "${engineVersion}", you are currently running "${npmVersion}".`,
);
reject(`Incorrect npm version\n'package.json' specifies "${engineVersion}", you are currently running "${npmVersion}".`);
}
});
});
});
const checkNodeVersion = (engines) =>
new Promise((resolve, reject) => {
const checkNodeVersion = (engines) => new Promise((resolve, reject) => {
const nodeVersion = process.version.substring(1);
if (semver.satisfies(nodeVersion, engines.node)) {
resolve(engines);
} else {
reject(
`Incorrect node version\n'package.json' specifies "${engines.node}", you are currently running "${process.version}".`,
);
reject(`Incorrect node version\n'package.json' specifies "${engines.node}", you are currently running "${process.version}".`);
}
});
});
getPackageJson()
.then(getEngines)
@ -77,5 +69,5 @@ getPackageJson()
/* eslint-disable no-console, no-process-exit */
console.error(error);
process.exit(1);
},
}
);

@ -444,6 +444,7 @@ function parseComparator(comp, loose) {
}
class SemVer {
/**
* A semantic version.
* @param {string} version The version.
@ -487,7 +488,7 @@ class SemVer {
// Numberify any prerelease numeric ids
if (matches[4]) {
this.prerelease = matches[4].split(".").map((id) => {
if (/^[0-9]+$/.test(id)) {
if ((/^[0-9]+$/).test(id)) {
const num = Number(id);
if (num >= 0 && num < MAX_SAFE_INTEGER) {
return num;
@ -531,9 +532,7 @@ class SemVer {
}
return (
compareIdentifiers(this.major, other.major) ||
compareIdentifiers(this.minor, other.minor) ||
compareIdentifiers(this.patch, other.patch)
compareIdentifiers(this.major, other.major) || compareIdentifiers(this.minor, other.minor) || compareIdentifiers(this.patch, other.patch)
);
}
@ -573,8 +572,7 @@ class SemVer {
}
}
const compare = (leftVersion, rightVersion, loose) =>
new SemVer(leftVersion, loose).compare(new SemVer(rightVersion, loose));
const compare = (leftVersion, rightVersion, loose) => new SemVer(leftVersion, loose).compare(new SemVer(rightVersion, loose));
const gt = (leftVersion, rightVersion, loose) => compare(leftVersion, rightVersion, loose) > 0;
const lt = (leftVersion, rightVersion, loose) => compare(leftVersion, rightVersion, loose) < 0;
const eq = (leftVersion, rightVersion, loose) => compare(leftVersion, rightVersion, loose) === 0;

@ -12,7 +12,7 @@ import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviv
export interface IConstructorParams {
info: string | JSX.Element;
stats?: JSX.Element;
stats?: JSX.Element | null;
isSpecial?: boolean;
moneyCost: number;
name: string;
@ -369,7 +369,7 @@ export class Augmentation {
info: string | JSX.Element;
// Description of the stats, often autogenerated, sometimes manually written.
stats: JSX.Element;
stats: JSX.Element | null;
// Any Augmentation not immediately available in BitNode-1 is special (e.g. Bladeburner augs)
isSpecial = false;
@ -507,8 +507,9 @@ export class Augmentation {
this.mults.bladeburner_success_chance_mult = params.bladeburner_success_chance_mult;
}
if (params.stats) this.stats = params.stats;
else this.stats = generateStatsDescription(this.mults, params.programs, params.startingMoney);
if (params.stats === undefined)
this.stats = generateStatsDescription(this.mults, params.programs, params.startingMoney);
else this.stats = params.stats;
}
// Adds this Augmentation to the specified Factions

@ -111,8 +111,8 @@ function getRandomBonus(): any {
return bonuses[Math.floor(bonuses.length * randomNumber.random())];
}
function initAugmentations() {
for (var name in Factions) {
function initAugmentations(): void {
for (const name in Factions) {
if (Factions.hasOwnProperty(name)) {
Factions[name].augmentations = [];
}
@ -1555,7 +1555,7 @@ function initAugmentations() {
repCost: 2.5e6,
moneyCost: 0,
info: "It's time to leave the cave.",
stats: <></>,
stats: null,
});
RedPill.addToFactions(["Daedalus"]);
if (augmentationExists(AugmentationNames.TheRedPill)) {
@ -1595,7 +1595,7 @@ function initAugmentations() {
"exactly the implant does, but they promise that it will greatly " +
"enhance your abilities.",
hacking_grow_mult: 3,
stats: <></>,
stats: null,
});
HiveMind.addToFactions(["ECorp"]);
if (augmentationExists(AugmentationNames.HiveMind)) {
@ -2349,7 +2349,7 @@ function initAugmentations() {
CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][SourceFileFlags[11]],
Player.queuedAugmentations.length,
);
for (var name in Augmentations) {
for (const name in Augmentations) {
if (Augmentations.hasOwnProperty(name)) {
Augmentations[name].baseCost *= mult;
}
@ -2363,7 +2363,7 @@ function resetAugmentation(newAugObject: Augmentation): void {
if (!(newAugObject instanceof Augmentation)) {
throw new Error("Invalid argument 'newAugObject' passed into resetAugmentation");
}
var name = newAugObject.name;
const name = newAugObject.name;
if (augmentationExists(name)) {
delete Augmentations[name];
}
@ -2397,12 +2397,12 @@ function applyAugmentation(aug: IPlayerOwnedAugmentation, reapply = false): void
// Push onto Player's Augmentation list
if (!reapply) {
var ownedAug = new PlayerOwnedAugmentation(aug.name);
const ownedAug = new PlayerOwnedAugmentation(aug.name);
Player.augmentations.push(ownedAug);
}
}
function installAugmentations() {
function installAugmentations(): boolean {
if (Player.queuedAugmentations.length == 0) {
dialogBoxCreate("You have not purchased any Augmentations to install!");
return false;
@ -2440,9 +2440,10 @@ function installAugmentations() {
"<br>You wake up in your home...you feel different...",
);
prestigeAugmentation();
return true;
}
function augmentationExists(name: string) {
function augmentationExists(name: string): boolean {
return Augmentations.hasOwnProperty(name);
}

@ -2,42 +2,33 @@
* Root React component for the Augmentations UI page that display all of your
* owned and purchased Augmentations and Source-Files.
*/
import * as React from "react";
import React, { useState } from "react";
import { InstalledAugmentationsAndSourceFiles } from "./InstalledAugmentationsAndSourceFiles";
import { InstalledAugmentations } from "./InstalledAugmentations";
import { PlayerMultipliers } from "./PlayerMultipliers";
import { PurchasedAugmentations } from "./PurchasedAugmentations";
import { SourceFiles } from "./SourceFiles";
import { Player } from "../../Player";
import { StdButton } from "../../ui/React/StdButton";
import { canGetBonus } from "../../ExportBonus";
type IProps = {
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import Tooltip from "@mui/material/Tooltip";
import Box from "@mui/material/Box";
interface IProps {
exportGameFn: () => void;
installAugmentationsFn: () => void;
};
}
type IState = {
rerender: boolean;
};
export function AugmentationsRoot(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
export class AugmentationsRoot extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
rerender: false,
};
this.export = this.export.bind(this);
function doExport(): void {
props.exportGameFn();
setRerender((o) => !o);
}
export(): void {
this.props.exportGameFn();
this.setState({
rerender: !this.state.rerender,
});
}
render(): React.ReactNode {
function exportBonusStr(): string {
if (canGetBonus()) return "(+1 favor to all factions)";
return "";
@ -45,49 +36,53 @@ export class AugmentationsRoot extends React.Component<IProps, IState> {
return (
<>
<div className="augmentations-content">
<h1>Purchased Augmentations</h1>
<p>
<Typography variant="h4">Augmentations</Typography>
<Box mx={2}>
<Typography>
Below is a list of all Augmentations you have purchased but not yet installed. Click the button below to
install them.
</p>
<p>WARNING: Installing your Augmentations resets most of your progress, including:</p>
</Typography>
<Typography>WARNING: Installing your Augmentations resets most of your progress, including:</Typography>
<br />
<p>- Stats/Skill levels and Experience</p>
<p>- Money</p>
<p>- Scripts on every computer but your home computer</p>
<p>- Purchased servers</p>
<p>- Hacknet Nodes</p>
<p>- Faction/Company reputation</p>
<p>- Stocks</p>
<Typography>- Stats/Skill levels and Experience</Typography>
<Typography>- Money</Typography>
<Typography>- Scripts on every computer but your home computer</Typography>
<Typography>- Purchased servers</Typography>
<Typography>- Hacknet Nodes</Typography>
<Typography>- Faction/Company reputation</Typography>
<Typography>- Stocks</Typography>
<br />
<p>
<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)
</p>
<StdButton
onClick={this.props.installAugmentationsFn}
text="Install Augmentations"
tooltip="'I never asked for this'"
/>
<StdButton
addClasses="flashing-button"
onClick={this.export}
text={`Backup Save ${exportBonusStr()}`}
tooltip="It's always a good idea to backup/export your save!"
/>
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>
</Box>
<Typography variant="h4" color="primary">
Purchased Augmentations
</Typography>
<Box mx={2}>
<Tooltip title={"'I never asked for this'"}>
<Button onClick={props.installAugmentationsFn}>
<Typography>Install Augmentations</Typography>
</Button>
</Tooltip>
<Tooltip title={"It's always a good idea to backup/export your save!"}>
<Button sx={{ mx: 2 }} onClick={doExport}>
<Typography color="error">Backup Save {exportBonusStr()}</Typography>
</Button>
</Tooltip>
<PurchasedAugmentations />
<h1>Installed Augmentations</h1>
<p>
{`List of all Augmentations ${Player.sourceFiles.length > 0 ? "and Source Files " : ""} ` +
`that have been installed. You have gained the effects of these.`}
</p>
<InstalledAugmentationsAndSourceFiles />
<br /> <br />
</Box>
<Typography variant="h4">Installed Augmentations</Typography>
<Box mx={2}>
<Typography>
List of all Augmentations that have been installed. You have gained the effects of these.
</Typography>
<InstalledAugmentations />
</Box>
<PlayerMultipliers />
</div>
<SourceFiles />
</>
);
}
}

@ -1,19 +1,28 @@
/**
* React Component for displaying a list of the player's installed Augmentations
* on the Augmentations UI
* React Component for displaying all of the player's installed Augmentations and
* Source-Files.
*
* It also contains 'configuration' buttons that allow you to change how the
* Augs/SF's are displayed
*/
import * as React from "react";
import { Player } from "../../Player";
import { Augmentations } from "../../Augmentation/Augmentations";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Settings } from "../../Settings/Settings";
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
import React, { useState } from "react";
import { AugmentationAccordion } from "../../ui/React/AugmentationAccordion";
import { Augmentations } from "../../Augmentation/Augmentations";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Settings } from "../../Settings/Settings";
import { use } from "../../ui/Context";
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
import Button from "@mui/material/Button";
import Tooltip from "@mui/material/Tooltip";
import List from "@mui/material/List";
export function InstalledAugmentations(): React.ReactElement {
const sourceAugs = Player.augmentations.slice();
const setRerender = useState(true)[1];
const player = use.Player();
const sourceAugs = player.augmentations.slice();
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
sourceAugs.sort((aug1, aug2) => {
@ -21,7 +30,32 @@ export function InstalledAugmentations(): React.ReactElement {
});
}
const augs = sourceAugs.map((e) => {
function rerender(): void {
setRerender((old) => !old);
}
function sortByAcquirementTime(): void {
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.AcquirementTime;
rerender();
}
function sortInOrder(): void {
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.Alphabetically;
rerender();
}
return (
<>
<Tooltip title={"Sorts the Augmentations alphabetically in numeral order"}>
<Button onClick={sortInOrder}>Sort in Order</Button>
</Tooltip>
<Tooltip title={"Sorts the Augmentations based on when you acquired them (same as default)"}>
<Button sx={{ mx: 2 }} onClick={sortByAcquirementTime}>
Sort by Acquirement Time
</Button>
</Tooltip>
<List dense>
{sourceAugs.map((e) => {
const aug = Augmentations[e.name];
let level = null;
@ -29,12 +63,9 @@ export function InstalledAugmentations(): React.ReactElement {
level = e.level;
}
return (
<li key={e.name}>
<AugmentationAccordion aug={aug} level={level} />
</li>
return <AugmentationAccordion key={aug.name} aug={aug} level={level} />;
})}
</List>
</>
);
});
return <>{augs}</>;
}

@ -1,115 +0,0 @@
/**
* React Component for displaying all of the player's installed Augmentations and
* Source-Files.
*
* It also contains 'configuration' buttons that allow you to change how the
* Augs/SF's are displayed
*/
import * as React from "react";
import { InstalledAugmentations } from "./InstalledAugmentations";
import { ListConfiguration } from "./ListConfiguration";
import { OwnedSourceFiles } from "./OwnedSourceFiles";
import { SourceFileMinus1 } from "./SourceFileMinus1";
import { Settings } from "../../Settings/Settings";
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
type IProps = {
// nothing special.
};
type IState = {
rerenderFlag: boolean;
};
export class InstalledAugmentationsAndSourceFiles extends React.Component<IProps, IState> {
listRef: React.RefObject<HTMLUListElement>;
constructor(props: IProps) {
super(props);
this.state = {
rerenderFlag: false,
};
this.collapseAllHeaders = this.collapseAllHeaders.bind(this);
this.expandAllHeaders = this.expandAllHeaders.bind(this);
this.sortByAcquirementTime = this.sortByAcquirementTime.bind(this);
this.sortInOrder = this.sortInOrder.bind(this);
this.listRef = React.createRef();
}
collapseAllHeaders(): void {
const ul = this.listRef.current;
if (ul == null) {
return;
}
const tickers = ul.getElementsByClassName("accordion-header");
for (let i = 0; i < tickers.length; ++i) {
const ticker = tickers[i];
if (!(ticker instanceof HTMLButtonElement)) {
continue;
}
if (ticker.classList.contains("active")) {
ticker.click();
}
}
}
expandAllHeaders(): void {
const ul = this.listRef.current;
if (ul == null) {
return;
}
const tickers = ul.getElementsByClassName("accordion-header");
for (let i = 0; i < tickers.length; ++i) {
const ticker = tickers[i];
if (!(ticker instanceof HTMLButtonElement)) {
continue;
}
if (!ticker.classList.contains("active")) {
ticker.click();
}
}
}
rerender(): void {
this.setState((prevState) => {
return {
rerenderFlag: !prevState.rerenderFlag,
};
});
}
sortByAcquirementTime(): void {
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.AcquirementTime;
this.rerender();
}
sortInOrder(): void {
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.Alphabetically;
this.rerender();
}
render(): React.ReactNode {
return (
<>
<ListConfiguration
collapseAllButtonsFn={this.collapseAllHeaders}
expandAllButtonsFn={this.expandAllHeaders}
sortByAcquirementTimeFn={this.sortByAcquirementTime}
sortInOrderFn={this.sortInOrder}
/>
<ul className="augmentations-list" ref={this.listRef}>
<SourceFileMinus1 />
<OwnedSourceFiles />
<InstalledAugmentations />
</ul>
</>
);
}
}

@ -1,33 +0,0 @@
/**
* React Component for configuring the way installed augmentations and
* Source-Files are displayed in the Augmentations UI
*/
import * as React from "react";
import { StdButton } from "../../ui/React/StdButton";
type IProps = {
collapseAllButtonsFn: () => void;
expandAllButtonsFn: () => void;
sortByAcquirementTimeFn: () => void;
sortInOrderFn: () => void;
};
export function ListConfiguration(props: IProps): React.ReactElement {
return (
<>
<StdButton onClick={props.expandAllButtonsFn} text="Expand All" />
<StdButton onClick={props.collapseAllButtonsFn} text="Collapse All" />
<StdButton
onClick={props.sortInOrderFn}
text="Sort in Order"
tooltip="Sorts the Augmentations alphabetically and Source-Files in numeral order"
/>
<StdButton
onClick={props.sortByAcquirementTimeFn}
text="Sort by Acquirement Time"
tooltip="Sorts the Augmentations and Source-Files based on when you acquired them (same as default)"
/>
</>
);
}

@ -20,7 +20,9 @@ export function OwnedSourceFiles(): React.ReactElement {
});
}
const sfs = sourceSfs.map((e) => {
return (
<>
{sourceSfs.map((e) => {
const srcFileKey = "SourceFile" + e.n;
const sfObj = SourceFiles[srcFileKey];
if (sfObj == null) {
@ -28,12 +30,8 @@ export function OwnedSourceFiles(): React.ReactElement {
return null;
}
return (
<li key={e.n}>
<SourceFileAccordion level={e.lvl} sf={sfObj} />
</li>
return <SourceFileAccordion key={e.n} level={e.lvl} sf={sfObj} />;
})}
</>
);
});
return <>{sfs}</>;
}

@ -6,6 +6,11 @@ import * as React from "react";
import { Player } from "../../Player";
import { numeralWrapper } from "../../ui/numeralFormat";
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";
function calculateAugmentedStats(): any {
const augP: any = {};
@ -19,41 +24,51 @@ function calculateAugmentedStats(): any {
return augP;
}
export function PlayerMultipliers(): React.ReactElement {
const mults = calculateAugmentedStats();
function MultiplierTable(rows: any[]): React.ReactElement {
function improvements(r: number): JSX.Element[] {
let elems: JSX.Element[] = [];
function Improvements({ r }: { r: number }): React.ReactElement {
if (r) {
elems = [<td key="2">&nbsp;{"=>"}&nbsp;</td>, <td key="3">{numeralWrapper.formatPercentage(r)}</td>];
}
return elems;
}
return (
<table>
<tbody>
{rows.map((r: any) => (
<tr key={r[0]}>
<td key="0">
<span>{r[0]} multiplier:&nbsp;</span>
</td>
<td key="1" style={{ textAlign: "right" }}>
{numeralWrapper.formatPercentage(r[1])}
</td>
{improvements(r[2])}
</tr>
))}
</tbody>
</table>
<>
<TableCell key="2">
<Typography>&nbsp;{"=>"}&nbsp;</Typography>
</TableCell>
<TableCell key="3">
<Typography>{numeralWrapper.formatPercentage(r)}</Typography>
</TableCell>
</>
);
}
return <></>;
}
function MultiplierTable({ rows }: { rows: [string, number, number][] }): React.ReactElement {
return (
<Table size="small" padding="none">
<TableBody>
{rows.map((r: any) => (
<TableRow key={r[0]}>
<TableCell key="0">
<Typography noWrap>{r[0]} multiplier:&nbsp;</Typography>
</TableCell>
<TableCell key="1" style={{ textAlign: "right" }}>
<Typography noWrap>{numeralWrapper.formatPercentage(r[1])}</Typography>
</TableCell>
<Improvements r={r[2]} />
</TableRow>
))}
</TableBody>
</Table>
);
}
export function PlayerMultipliers(): React.ReactElement {
const mults = calculateAugmentedStats();
function BladeburnerMults(): React.ReactElement {
if (!Player.canAccessBladeburner()) return <></>;
return (
<>
{MultiplierTable([
<MultiplierTable
rows={[
[
"Bladeburner Success Chance",
Player.bladeburner_success_chance_mult,
@ -74,7 +89,8 @@ export function PlayerMultipliers(): React.ReactElement {
Player.bladeburner_analysis_mult,
Player.bladeburner_analysis_mult * mults.bladeburner_analysis_mult,
],
])}
]}
/>
<br />
</>
);
@ -82,57 +98,68 @@ export function PlayerMultipliers(): React.ReactElement {
return (
<>
<p>
<strong>
<u>Multipliers:</u>
</strong>
</p>
<br />
{MultiplierTable([
<Typography variant="h4">Multipliers</Typography>
<Box mx={2}>
<MultiplierTable
rows={[
["Hacking Chance ", Player.hacking_chance_mult, Player.hacking_chance_mult * mults.hacking_chance_mult],
["Hacking Speed ", Player.hacking_speed_mult, Player.hacking_speed_mult * mults.hacking_speed_mult],
["Hacking Money ", Player.hacking_money_mult, Player.hacking_money_mult * mults.hacking_money_mult],
["Hacking Growth ", Player.hacking_grow_mult, Player.hacking_grow_mult * mults.hacking_grow_mult],
])}
]}
/>
<br />
{MultiplierTable([
<MultiplierTable
rows={[
["Hacking Level ", Player.hacking_mult, Player.hacking_mult * mults.hacking_mult],
["Hacking Experience ", Player.hacking_exp_mult, Player.hacking_exp_mult * mults.hacking_exp_mult],
])}
]}
/>
<br />
{MultiplierTable([
<MultiplierTable
rows={[
["Strength Level ", Player.strength_mult, Player.strength_mult * mults.strength_mult],
["Strength Experience ", Player.strength_exp_mult, Player.strength_exp_mult * mults.strength_exp_mult],
])}
]}
/>
<br />
{MultiplierTable([
<MultiplierTable
rows={[
["Defense Level ", Player.defense_mult, Player.defense_mult * mults.defense_mult],
["Defense Experience ", Player.defense_exp_mult, Player.defense_exp_mult * mults.defense_exp_mult],
])}
]}
/>
<br />
{MultiplierTable([
<MultiplierTable
rows={[
["Dexterity Level ", Player.dexterity_mult, Player.dexterity_mult * mults.dexterity_mult],
["Dexterity Experience ", Player.dexterity_exp_mult, Player.dexterity_exp_mult * mults.dexterity_exp_mult],
])}
]}
/>
<br />
{MultiplierTable([
<MultiplierTable
rows={[
["Agility Level ", Player.agility_mult, Player.agility_mult * mults.agility_mult],
["Agility Experience ", Player.agility_exp_mult, Player.agility_exp_mult * mults.agility_exp_mult],
])}
]}
/>
<br />
{MultiplierTable([
<MultiplierTable
rows={[
["Charisma Level ", Player.charisma_mult, Player.charisma_mult * mults.charisma_mult],
["Charisma Experience ", Player.charisma_exp_mult, Player.charisma_exp_mult * mults.charisma_exp_mult],
])}
]}
/>
<br />
{MultiplierTable([
<MultiplierTable
rows={[
[
"Hacknet Node production ",
Player.hacknet_node_money_mult,
@ -158,23 +185,29 @@ export function PlayerMultipliers(): React.ReactElement {
Player.hacknet_node_level_cost_mult,
Player.hacknet_node_level_cost_mult * mults.hacknet_node_level_cost_mult,
],
])}
]}
/>
<br />
{MultiplierTable([
<MultiplierTable
rows={[
["Company reputation gain ", Player.company_rep_mult, Player.company_rep_mult * mults.company_rep_mult],
["Faction reputation gain ", Player.faction_rep_mult, Player.faction_rep_mult * mults.faction_rep_mult],
["Salary ", Player.work_money_mult, Player.work_money_mult * mults.work_money_mult],
])}
]}
/>
<br />
{MultiplierTable([
<MultiplierTable
rows={[
["Crime success ", Player.crime_success_mult, Player.crime_success_mult * mults.crime_success_mult],
["Crime money ", Player.crime_money_mult, Player.crime_money_mult * mults.crime_money_mult],
])}
]}
/>
<br />
<BladeburnerMults />
</Box>
</>
);
}

@ -9,6 +9,7 @@ import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Player } from "../../Player";
import { AugmentationAccordion } from "../../ui/React/AugmentationAccordion";
import List from "@mui/material/List";
export function PurchasedAugmentations(): React.ReactElement {
const augs: React.ReactElement[] = [];
@ -29,12 +30,8 @@ export function PurchasedAugmentations(): React.ReactElement {
level = ownedAug.level;
}
augs.push(
<li key={`${ownedAug.name}${ownedAug.level}`}>
<AugmentationAccordion aug={aug} level={level} />
</li>,
);
augs.push(<AugmentationAccordion key={aug.name} aug={aug} level={level} />);
}
return <ul className="augmentations-list">{augs}</ul>;
return <List dense>{augs}</List>;
}

@ -2,14 +2,22 @@
* React Component for displaying a list of the player's Source-Files
* on the Augmentations UI
*/
import * as React from "react";
import React, { useState } from "react";
import { Player } from "../../Player";
import { Exploit, ExploitName } from "../../Exploits/Exploit";
import { BBAccordion } from "../../ui/React/BBAccordion";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemText from "@mui/material/ListItemText";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper";
import Collapse from "@mui/material/Collapse";
import ExpandMore from "@mui/icons-material/ExpandMore";
import ExpandLess from "@mui/icons-material/ExpandLess";
export function SourceFileMinus1(): React.ReactElement {
const [open, setOpen] = useState(false);
const exploits = Player.exploits;
if (exploits.length === 0) {
@ -17,33 +25,35 @@ export function SourceFileMinus1(): React.ReactElement {
}
return (
<li key={-1}>
<BBAccordion
headerContent={
<>
<Box component={Paper}>
<ListItemButton onClick={() => setOpen((old) => !old)}>
<ListItemText
primary={
<Typography style={{ whiteSpace: "pre-wrap" }}>
Source-File -1: Exploits in the BitNodes
<br />
Level {exploits.length} / ?
</>
}
panelContent={
<>
<p>
This Source-File can only be acquired with obscure knowledge of the game, javascript, and the web
ecosystem.
</p>
<p>It increases all of the player's multipliers by 0.1%</p>
<br />
<p>You have found the following exploits:</p>
<ul>
{exploits.map((c: Exploit) => (
<li key={c}>* {ExploitName(c)}</li>
))}
</ul>
</>
</Typography>
}
/>
</li>
{open ? <ExpandLess color="primary" /> : <ExpandMore color="primary" />}
</ListItemButton>
<Collapse in={open} unmountOnExit>
<Box m={4}>
<Typography>
This Source-File can only be acquired with obscure knowledge of the game, javascript, and the web ecosystem.
</Typography>
<Typography>It increases all of the player's multipliers by 0.1%</Typography>
<br />
<Typography>You have found the following exploits:</Typography>
<Box mx={2}>
{exploits.map((c: Exploit) => (
<Typography key={c}>* {ExploitName(c)}</Typography>
))}
</Box>
</Box>
</Collapse>
</Box>
);
}

@ -0,0 +1,21 @@
import React from "react";
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";
export function SourceFiles(): React.ReactElement {
return (
<>
<Typography variant="h4">Source Files</Typography>
<Box mx={2}>
<List dense>
<SourceFileMinus1 />
<OwnedSourceFiles />
</List>
</Box>
</>
);
}

@ -4,7 +4,6 @@ import { Console } from "./Console";
import { AllPages } from "./AllPages";
import { use } from "../../ui/Context";
import { IBladeburner } from "../IBladeburner";
export function BladeburnerRoot(): React.ReactElement {
const player = use.Player();

@ -7,7 +7,6 @@ import { IIndustry } from "../IIndustry";
import { NewIndustryPopup } from "./NewIndustryPopup";
import { createPopup } from "../../ui/React/createPopup";
import { ICorporation } from "../ICorporation";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { MainPanel } from "./MainPanel";
import { Industries } from "../IndustryData";
import { use } from "../../ui/Context";

@ -47,7 +47,7 @@ export function joinFaction(faction: Faction): void {
Factions[enemy].isBanned = true;
}
}
for (var i = 0; i < Player.factionInvitations.length; ++i) {
for (let i = 0; i < Player.factionInvitations.length; ++i) {
if (Player.factionInvitations[i] == faction.name || Factions[Player.factionInvitations[i]].isBanned) {
Player.factionInvitations.splice(i, 1);
i--;
@ -91,28 +91,29 @@ export function hasAugmentationPrereqs(aug: Augmentation): boolean {
export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = false): string {
const factionInfo = fac.getInfo();
var hasPrereqs = hasAugmentationPrereqs(aug);
const hasPrereqs = hasAugmentationPrereqs(aug);
if (!hasPrereqs) {
var 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.";
if (sing) {
return txt;
} else {
dialogBoxCreate(txt);
}
} else if (aug.baseCost !== 0 && Player.money.lt(aug.baseCost * factionInfo.augmentationPriceMult)) {
let 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) {
return txt;
}
dialogBoxCreate(txt);
} else if (fac.playerReputation < aug.baseRepRequirement) {
let txt = "You don't have enough faction reputation to purchase " + aug.name;
const txt = "You don't have enough faction reputation to purchase " + aug.name;
if (sing) {
return txt;
}
dialogBoxCreate(txt);
} else if (aug.baseCost === 0 || Player.money.gte(aug.baseCost * factionInfo.augmentationPriceMult)) {
var queuedAugmentation = new PlayerOwnedAugmentation(aug.name);
const queuedAugmentation = new PlayerOwnedAugmentation(aug.name);
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
queuedAugmentation.level = getNextNeurofluxLevel();
}
@ -122,18 +123,18 @@ export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = fal
// If you just purchased Neuroflux Governor, recalculate the cost
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
var nextLevel = getNextNeurofluxLevel();
let nextLevel = getNextNeurofluxLevel();
--nextLevel;
var mult = Math.pow(CONSTANTS.NeuroFluxGovernorLevelMult, nextLevel);
const mult = Math.pow(CONSTANTS.NeuroFluxGovernorLevelMult, nextLevel);
aug.baseRepRequirement = 500 * mult * BitNodeMultipliers.AugmentationRepCost;
aug.baseCost = 750e3 * mult * BitNodeMultipliers.AugmentationMoneyCost;
for (var i = 0; i < Player.queuedAugmentations.length - 1; ++i) {
for (let i = 0; i < Player.queuedAugmentations.length - 1; ++i) {
aug.baseCost *= CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][SourceFileFlags[11]];
}
}
for (var name in Augmentations) {
for (const name in Augmentations) {
if (Augmentations.hasOwnProperty(name)) {
Augmentations[name].baseCost *= CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][SourceFileFlags[11]];
}
@ -166,14 +167,14 @@ export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = fal
export function getNextNeurofluxLevel(): number {
// Get current Neuroflux level based on Player's augmentations
let currLevel = 0;
for (var i = 0; i < Player.augmentations.length; ++i) {
for (let i = 0; i < Player.augmentations.length; ++i) {
if (Player.augmentations[i].name === AugmentationNames.NeuroFluxGovernor) {
currLevel = Player.augmentations[i].level;
}
}
// Account for purchased but uninstalled Augmentations
for (var i = 0; i < Player.queuedAugmentations.length; ++i) {
for (let i = 0; i < Player.queuedAugmentations.length; ++i) {
if (Player.queuedAugmentations[i].name == AugmentationNames.NeuroFluxGovernor) {
++currLevel;
}

@ -12,7 +12,6 @@ import { PurchaseAugmentationsOrderSetting } from "../../Settings/SettingEnums";
import { Settings } from "../../Settings/Settings";
import { hasAugmentationPrereqs } from "../FactionHelpers";
import { StdButton } from "../../ui/React/StdButton";
import { use } from "../../ui/Context";
import Button from "@mui/material/Button";
@ -87,7 +86,6 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
const augs = getAugs();
function canBuy(augName: string): boolean {
const aug = Augmentations[augName];
const moneyCost = aug.baseCost * props.faction.getInfo().augmentationPriceMult;
const repCost = aug.baseRepRequirement * props.faction.getInfo().augmentationRepRequirementMult;
const hasReq = props.faction.playerReputation >= repCost;
const hasRep = hasAugmentationPrereqs(aug);

@ -12,8 +12,6 @@ import { Favor } from "../../ui/React/Favor";
import { Money } from "../../ui/React/Money";
import { Reputation } from "../../ui/React/Reputation";
import { StdButton } from "../../ui/React/StdButton";
import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../../utils/DialogBox";
@ -32,11 +30,6 @@ type IProps = {
rerender: () => void;
};
const inputStyleMarkup = {
margin: "5px",
height: "26px",
};
export function DonateOption(props: IProps): React.ReactElement {
const [donateAmt, setDonateAmt] = useState<number | null>(null);
const digits = (CONSTANTS.DonateMoneyToRepDivisor + "").length - 1;
@ -52,7 +45,6 @@ export function DonateOption(props: IProps): React.ReactElement {
const amt = numeralWrapper.parseMoney(event.target.value);
if (event.target.value === "" || isNaN(amt)) setDonateAmt(null);
else setDonateAmt(amt);
console.log("set");
}
function donate(): void {

@ -3,7 +3,7 @@
* This is the component for displaying a single faction's UI, not the list of all
* accessible factions
*/
import React, { useState, useEffect } from "react";
import React, { useState } from "react";
import { AugmentationsPage } from "./AugmentationsPage";
import { DonateOption } from "./DonateOption";

@ -39,9 +39,7 @@ export function FactionsRoot(props: IProps): React.ReactElement {
return (
<>
<Typography variant="h5" color="primary">
Factions
</Typography>
<Typography variant="h4">Factions</Typography>
<Typography>Lists all factions you have joined</Typography>
<br />
<Box display="flex" flexDirection="column">

@ -7,13 +7,10 @@ import React, { useState, useEffect } from "react";
import { Faction } from "../../Faction/Faction";
import { FactionInfo } from "../../Faction/FactionInfo";
import { AutoupdatingParagraph } from "../../ui/React/AutoupdatingParagraph";
import { ParagraphWithTooltip } from "../../ui/React/ParagraphWithTooltip";
import { Reputation } from "../../ui/React/Reputation";
import { Favor } from "../../ui/React/Favor";
import { MathComponent } from "mathjax-react";
import { Theme } from "@mui/material/styles";
import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles";
import Typography from "@mui/material/Typography";
@ -25,10 +22,11 @@ type IProps = {
factionInfo: FactionInfo;
};
const useStyles = makeStyles((theme: Theme) =>
const useStyles = makeStyles(() =>
createStyles({
noformat: {
whiteSpace: "pre-wrap",
lineHeight: "1em",
},
}),
);

@ -5,8 +5,6 @@
*/
import * as React from "react";
import { StdButton } from "../../ui/React/StdButton";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import Paper from "@mui/material/Paper";

@ -15,9 +15,7 @@ import { Settings } from "../../Settings/Settings";
import { Money } from "../../ui/React/Money";
import { Reputation } from "../../ui/React/Reputation";
import { createPopup } from "../../ui/React/createPopup";
import { IMap } from "../../types";
import { StdButton } from "../../ui/React/StdButton";
import { Augmentation as AugFormat } from "../../ui/React/Augmentation";
import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography";
@ -51,7 +49,6 @@ function Requirements(props: IReqProps): React.ReactElement {
);
}
let color = !props.hasRep || !props.hasCost ? "error" : "primary";
return (
<React.Fragment key="f">
<TableCell key={1}>
@ -80,26 +77,6 @@ export function PurchaseableAugmentation(props: IProps): React.ReactElement {
const aug = Augmentations[props.augName];
if (aug == null) throw new Error(`aug ${props.augName} does not exists`);
// Whether the player has this augmentations (purchased OR installed)
function owned(): boolean {
let owned = false;
for (const queuedAug of props.p.queuedAugmentations) {
if (queuedAug.name === props.augName) {
owned = true;
break;
}
}
for (const installedAug of props.p.augmentations) {
if (installedAug.name === props.augName) {
owned = true;
break;
}
}
return owned;
}
if (aug == null) {
console.error(
`Invalid Augmentation when trying to create PurchaseableAugmentation display element: ${props.augName}`,
@ -175,10 +152,7 @@ export function PurchaseableAugmentation(props: IProps): React.ReactElement {
placement="top"
disableFocusListener
disableTouchListener
enterNextDelay={1000}
enterDelay={500}
leaveDelay={0}
leaveTouchDelay={0}
disableInteractive
>
<Typography>{btnTxt}</Typography>
</Tooltip>

@ -6,7 +6,6 @@ import { ManagementSubpage } from "./ManagementSubpage";
import { TerritorySubpage } from "./TerritorySubpage";
import { use } from "../../ui/Context";
import { Factions } from "../../Faction/Factions";
import { Gang } from "../Gang";
export function GangRoot(): React.ReactElement {
const player = use.Player();

@ -33,7 +33,7 @@ export function BackwardGame(props: IMinigameProps): React.ReactElement {
const [answer] = useState(makeAnswer(difficulty));
const [guess, setGuess] = useState("");
function press(event: React.KeyboardEvent<HTMLElement>): void {
function press(this: Document, event: KeyboardEvent): void {
event.preventDefault();
if (event.keyCode === 16) return;
const nextGuess = guess + event.key.toUpperCase();

@ -36,7 +36,7 @@ function generateLeftSide(difficulty: Difficulty): string {
return str;
}
function getChar(event: React.KeyboardEvent<HTMLElement>): string {
function getChar(event: KeyboardEvent): string {
if (event.keyCode == 48 && event.shiftKey) return ")";
if (event.keyCode == 221 && !event.shiftKey) return "]";
if (event.keyCode == 221 && event.shiftKey) return "}";
@ -60,7 +60,7 @@ export function BracketGame(props: IMinigameProps): React.ReactElement {
const [right, setRight] = useState("");
const [left] = useState(generateLeftSide(difficulty));
function press(event: React.KeyboardEvent<HTMLElement>): void {
function press(this: Document, event: KeyboardEvent): void {
event.preventDefault();
const char = getChar(event);
if (!char) return;

@ -30,7 +30,7 @@ export function BribeGame(props: IMinigameProps): React.ReactElement {
const [choices] = useState(makeChoices(difficulty));
const [index, setIndex] = useState(0);
function press(event: React.KeyboardEvent<HTMLElement>): void {
function press(this: Document, event: KeyboardEvent): void {
event.preventDefault();
const k = event.keyCode;
if (k === 32) {

@ -32,7 +32,7 @@ export function CheatCodeGame(props: IMinigameProps): React.ReactElement {
const [code] = useState(generateCode(difficulty));
const [index, setIndex] = useState(0);
function press(event: React.KeyboardEvent<HTMLElement>): void {
function press(this: Document, event: KeyboardEvent): void {
event.preventDefault();
if (code[index] !== getArrow(event)) {
props.onFailure();

@ -35,7 +35,7 @@ export function Cyberpunk2077Game(props: IMinigameProps): React.ReactElement {
const [index, setIndex] = useState(0);
const [pos, setPos] = useState([0, 0]);
function press(event: React.KeyboardEvent<HTMLElement>): void {
function press(this: Document, event: KeyboardEvent): void {
event.preventDefault();
const move = [0, 0];
const arrow = getArrow(event);

@ -1,22 +1,20 @@
import React, { useEffect } from "react";
interface IProps {
onKeyDown: (event: React.KeyboardEvent<HTMLElement>) => void;
onKeyDown: (this: Document, event: KeyboardEvent) => void;
onFailure: (options?: { automated: boolean }) => void;
}
export function KeyHandler(props: IProps): React.ReactElement {
let elem: any;
useEffect(() => elem.focus());
function onKeyDown(event: React.KeyboardEvent<HTMLElement>): void {
if (!event.isTrusted) {
props.onFailure({ automated: true });
return;
}
props.onKeyDown(event);
useEffect(() => {
function press(this: Document, event: KeyboardEvent): void {
const f = props.onKeyDown.bind(this);
f(event);
}
document.addEventListener("keydown", press);
return () => document.removeEventListener("keydown", press);
});
// invisible autofocused element that eats all the keypress for the minigames.
return <div tabIndex={1} ref={(c) => (elem = c)} onKeyDown={onKeyDown} />;
return <></>;
}

@ -35,7 +35,7 @@ export function MinesweeperGame(props: IMinigameProps): React.ReactElement {
const [pos, setPos] = useState([0, 0]);
const [memoryPhase, setMemoryPhase] = useState(true);
function press(event: React.KeyboardEvent<HTMLElement>): void {
function press(this: Document, event: KeyboardEvent): void {
event.preventDefault();
if (memoryPhase) return;
const move = [0, 0];

@ -27,7 +27,7 @@ export function SlashGame(props: IMinigameProps): React.ReactElement {
interpolate(difficulties, props.difficulty, difficulty);
const [guarding, setGuarding] = useState(true);
function press(event: React.KeyboardEvent<HTMLElement>): void {
function press(this: Document, event: KeyboardEvent): void {
event.preventDefault();
if (event.keyCode !== 32) return;
if (guarding) {

@ -60,7 +60,7 @@ export function WireCuttingGame(props: IMinigameProps): React.ReactElement {
const [cutWires, setCutWires] = useState(new Array(wires.length).fill(false));
const [questions] = useState(generateQuestion(wires, difficulty));
function press(event: React.KeyboardEvent<HTMLElement>): void {
function press(this: Document, event: KeyboardEvent): void {
event.preventDefault();
const wireNum = parseInt(event.key);

@ -1,10 +1,8 @@
import React from "react";
export function random(min: number, max: number): number {
return Math.random() * (max - min) + min;
}
export function getArrow(event: React.KeyboardEvent<HTMLElement>): string {
export function getArrow(event: KeyboardEvent): string {
switch (event.keyCode) {
case 38:
case 87:

@ -3,10 +3,10 @@
*
* TThis subcomponent renders all of the buttons for traveling to different cities
*/
import * as React from "react";
import React, { useState, useEffect } from "react";
import { CityName } from "../data/CityNames";
import { TravelConfirmationPopup } from "./TravelConfirmationPopup";
import { TravelConfirmationModal } from "./TravelConfirmationModal";
import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer";
@ -14,11 +14,15 @@ import { IRouter } from "../../ui/Router";
import { Settings } from "../../Settings/Settings";
import { StdButton } from "../../ui/React/StdButton";
import { createPopup } from "../../ui/React/createPopup";
import { use } from "../../ui/Context";
import { Money } from "../../ui/React/Money";
import { WorldMap } from "../../ui/React/WorldMap";
import { dialogBoxCreate } from "../../../utils/DialogBox";
import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
type IProps = {
p: IPlayer;
router: IRouter;
@ -27,7 +31,6 @@ type IProps = {
function travel(p: IPlayer, router: IRouter, to: CityName): void {
const cost = CONSTANTS.TravelCost;
if (!p.canAfford(cost)) {
dialogBoxCreate(`You cannot afford to travel to ${to}`);
return;
}
@ -37,69 +40,69 @@ function travel(p: IPlayer, router: IRouter, to: CityName): void {
router.toCity();
}
function createTravelPopup(p: IPlayer, router: IRouter, city: CityName): void {
if (Settings.SuppressTravelConfirmation) {
travel(p, router, city);
export function TravelAgencyRoot(props: IProps): React.ReactElement {
const player = use.Player();
const router = use.Router();
const setRerender = useState(false)[1];
const [open, setOpen] = useState(false);
const [destination, setDestination] = useState(CityName.Sector12);
function rerender(): void {
setRerender((o) => !o);
}
useEffect(() => {
const id = setInterval(rerender, 1000);
return () => clearInterval(id);
}, []);
function startTravel(city: CityName): void {
const cost = CONSTANTS.TravelCost;
if (!player.canAfford(cost)) {
return;
}
const popupId = `travel-confirmation`;
createPopup(popupId, TravelConfirmationPopup, {
player: p,
city: city,
travel: () => travel(p, router, city),
popupId: popupId,
});
}
if (Settings.SuppressTravelConfirmation) {
travel(player, router, city);
return;
}
setOpen(true);
setDestination(city);
}
function ASCIIWorldMap(props: IProps): React.ReactElement {
return (
<div className="noselect">
<p>
<>
<Typography variant="h4">Travel Agency</Typography>
<Box mx={2}>
<Typography>
From here, you can travel to any other city! A ticket costs{" "}
<Money money={CONSTANTS.TravelCost} player={props.p} />.
</p>
<WorldMap
currentCity={props.p.city}
onTravel={(city: CityName) => createTravelPopup(props.p, props.router, city)}
/>
</div>
);
}
function ListWorldMap(props: IProps): React.ReactElement {
return (
</Typography>
{Settings.DisableASCIIArt ? (
<div>
<p>
From here, you can travel to any other city! A ticket costs{" "}
<Money money={CONSTANTS.TravelCost} player={props.p} />.
</p>
{Object.values(CityName)
.filter((city: string) => city != props.p.city)
.map((city: string) => {
const match = Object.entries(CityName).find((entry) => entry[1] === city);
if (match === undefined) throw new Error(`could not find key for city '${city}'`);
return (
<StdButton
key={city}
onClick={() => createTravelPopup(props.p, props.router, city as CityName)}
style={{ display: "block" }}
text={`Travel to ${city}`}
/>
<React.Fragment key={city}>
<Button onClick={() => startTravel(city as CityName)} sx={{ m: 2 }}>
<Typography>Travel to {city}</Typography>
</Button>
<br />
</React.Fragment>
);
})}
</div>
);
}
export function TravelAgencyRoot(props: IProps): React.ReactElement {
return (
<>
<h1>Travel Agency</h1>
{Settings.DisableASCIIArt ? (
<ListWorldMap p={props.p} router={props.router} />
) : (
<ASCIIWorldMap p={props.p} router={props.router} />
<WorldMap currentCity={props.p.city} onTravel={(city: CityName) => startTravel(city)} />
)}
</Box>
<TravelConfirmationModal
city={destination}
travel={() => travel(player, router, destination)}
open={open}
onClose={() => setOpen(false)}
/>
</>
);
}

@ -0,0 +1,37 @@
import React from "react";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { CONSTANTS } from "../../Constants";
import { Money } from "../../ui/React/Money";
import { Modal } from "../../ui/React/Modal";
import { use } from "../../ui/Context";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
interface IProps {
city: string;
travel: () => void;
open: boolean;
onClose: () => void;
}
export function TravelConfirmationModal(props: IProps): React.ReactElement {
const player = use.Player();
const cost = CONSTANTS.TravelCost;
function travel(): void {
props.travel();
}
return (
<Modal open={props.open} onClose={props.onClose}>
<Typography>
Would you like to travel to {props.city}? The trip will cost <Money money={cost} player={player} />.
</Typography>
<br />
<br />
<Button onClick={travel}>
<Typography>Travel</Typography>
</Button>
</Modal>
);
}

@ -1,33 +0,0 @@
import React from "react";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { CONSTANTS } from "../../Constants";
import { Money } from "../../ui/React/Money";
import { removePopup } from "../../ui/React/createPopup";
interface IProps {
player: IPlayer;
city: string;
travel: () => void;
popupId: string;
}
export function TravelConfirmationPopup(props: IProps): React.ReactElement {
const cost = CONSTANTS.TravelCost;
function travel(): void {
props.travel();
removePopup(props.popupId);
}
return (
<>
<span>
Would you like to travel to {props.city}? The trip will cost <Money money={cost} player={props.player} />.
</span>
<br />
<br />
<button className="std-button" onClick={travel}>
Travel
</button>
</>
);
}

@ -20,7 +20,7 @@ function sendMessage(msg: Message, forced = false): void {
}
function showMessage(msg: Message): void {
var txt =
const txt =
"Message received from unknown sender: <br><br>" +
"<i>" +
msg.msg +
@ -33,15 +33,15 @@ function showMessage(msg: Message): void {
//Adds a message to a server
function addMessageToServer(msg: Message, serverHostname: string): void {
var server = GetServerByHostname(serverHostname);
const server = GetServerByHostname(serverHostname);
if (server == null) {
console.warn(`Could not find server ${serverHostname}`);
return;
}
for (var i = 0; i < server.messages.length; ++i) {
const msg = server.messages[i];
if (typeof msg === "string") continue;
if (msg.filename === msg.filename) {
for (let i = 0; i < server.messages.length; ++i) {
const other = server.messages[i];
if (typeof other === "string") continue;
if (msg.filename === other.filename) {
return; //Already exists
}
}
@ -49,7 +49,7 @@ function addMessageToServer(msg: Message, serverHostname: string): void {
}
//Checks if any of the 'timed' messages should be sent
function checkForMessagesToSend() {
function checkForMessagesToSend(): void {
if (redPillFlag) return;
const jumper0 = Messages[MessageFilenames.Jumper0];
const jumper1 = Messages[MessageFilenames.Jumper1];
@ -107,7 +107,7 @@ function loadMessages(saveString: string): void {
Messages = JSON.parse(saveString, Reviver);
}
let MessageFilenames = {
const MessageFilenames = {
Jumper0: "j0.msg",
Jumper1: "j1.msg",
Jumper2: "j2.msg",
@ -119,7 +119,7 @@ let MessageFilenames = {
RedPill: "icarus.msg",
};
function initMessages() {
function initMessages(): void {
//Reset
Messages = {};

@ -40,14 +40,14 @@ export class WorkerScript {
/**
* Stores names of all functions that have logging disabled
*/
disableLogs: IMap<string> = {};
disableLogs: IMap<boolean> = {};
/**
* Used for dynamic RAM calculation. Stores names of all functions that have
* already been checked by this script.
* TODO: Could probably just combine this with loadedFns?
*/
dynamicLoadedFns: IMap<string> = {};
dynamicLoadedFns: IMap<boolean> = {};
/**
* Tracks dynamic RAM usage

@ -12,7 +12,7 @@ import { AllServers } from "../Server/AllServers";
import { compareArrays } from "../../utils/helpers/compareArrays";
import { roundToTwo } from "../../utils/helpers/roundToTwo";
export function killWorkerScript(runningScriptObj: RunningScript, serverIp: string, rerenderUi: boolean): boolean;
export function killWorkerScript(runningScriptObj: RunningScript, serverIp: string, rerenderUi?: boolean): boolean;
export function killWorkerScript(workerScript: WorkerScript): boolean;
export function killWorkerScript(pid: number): boolean;
export function killWorkerScript(

@ -14,12 +14,8 @@ export function netscriptDelay(time: number, workerScript: WorkerScript): Promis
});
}
export function makeRuntimeRejectMsg(workerScript: WorkerScript, msg: string, exp: any = null) {
var lineNum = "";
if (exp != null) {
var num = getErrorLineNumber(exp, workerScript);
lineNum = " (Line " + num + ")";
}
export function makeRuntimeRejectMsg(workerScript: WorkerScript, msg: string): string {
const lineNum = "";
const server = AllServers[workerScript.serverIp];
if (server == null) {
throw new Error(`WorkerScript constructed with invalid server ip: ${workerScript.serverIp}`);
@ -32,7 +28,7 @@ export function resolveNetscriptRequestedThreads(
workerScript: WorkerScript,
functionName: string,
requestedThreads: number,
) {
): number {
const threads = workerScript.scriptRef.threads;
if (!requestedThreads) {
return isNaN(threads) || threads < 1 ? 1 : threads;
@ -53,26 +49,11 @@ export function resolveNetscriptRequestedThreads(
return requestedThreadsAsInt;
}
export function getErrorLineNumber(exp: any, workerScript: WorkerScript): number {
return -1;
// TODO wtf is codeCode?
// var code = workerScript.scriptRef.codeCode();
// //Split code up to the start of the node
// try {
// code = code.substring(0, exp.start);
// return (code.match(/\n/g) || []).length + 1;
// } catch (e) {
// return -1;
// }
}
export function isScriptErrorMessage(msg: string): boolean {
if (!isString(msg)) {
return false;
}
let splitMsg = msg.split("|");
const splitMsg = msg.split("|");
if (splitMsg.length != 4) {
return false;
}

@ -1,2 +0,0 @@
import { WorkerScript } from "./Netscript/WorkerScript";
export declare function NetscriptFunctions(workerScript: WorkerScript): any;

File diff suppressed because it is too large Load Diff

@ -16,10 +16,9 @@ function makeScriptBlob(code: string): Blob {
// (i.e. hack, grow, etc.).
// When the promise returned by this resolves, we'll have finished
// running the main function of the script.
export async function executeJSScript(scripts: Script[] = [], workerScript: WorkerScript) {
let loadedModule;
export async function executeJSScript(scripts: Script[] = [], workerScript: WorkerScript): Promise<void> {
let uurls: ScriptUrl[] = [];
let script = workerScript.getScript();
const script = workerScript.getScript();
if (script === null) throw new Error("script is null");
if (shouldCompile(script, scripts)) {
// The URL at the top is the one we want to import. It will
@ -35,9 +34,9 @@ export async function executeJSScript(scripts: Script[] = [], workerScript: Work
script.module = new Promise((resolve) => resolve(eval("import(uurls[uurls.length - 1].url)")));
script.dependencies = uurls;
}
loadedModule = await script.module;
const loadedModule = await script.module;
let ns = workerScript.env.vars;
const ns = workerScript.env.vars;
// TODO: putting await in a non-async function yields unhelpful
// "SyntaxError: unexpected reserved word" with no line number information.

@ -14,13 +14,11 @@ import { isScriptErrorMessage, makeRuntimeRejectMsg } from "./NetscriptEvaluator
import { NetscriptFunctions } from "./NetscriptFunctions";
import { executeJSScript } from "./NetscriptJSEvaluator";
import { NetscriptPort, IPort } from "./NetscriptPort";
import { Player } from "./Player";
import { RunningScript } from "./Script/RunningScript";
import { getRamUsageFromRunningScript } from "./Script/RunningScriptHelpers";
import { scriptCalculateOfflineProduction } from "./Script/ScriptHelpers";
import { Script } from "./Script/Script";
import { AllServers } from "./Server/AllServers";
import { Server } from "./Server/Server";
import { BaseServer } from "./Server/BaseServer";
import { Settings } from "./Settings/Settings";
import { setTimeoutRef } from "./utils/SetTimeoutRef";
@ -38,11 +36,11 @@ import { simple as walksimple } from "acorn-walk";
// Netscript Ports are instantiated here
export const NetscriptPorts: IPort[] = [];
for (var i = 0; i < CONSTANTS.NumNetscriptPorts; ++i) {
for (let i = 0; i < CONSTANTS.NumNetscriptPorts; ++i) {
NetscriptPorts.push(NetscriptPort());
}
export function prestigeWorkerScripts() {
export function prestigeWorkerScripts(): void {
for (const ws of workerScripts.values()) {
ws.env.stopFlag = true;
killWorkerScript(ws);
@ -65,7 +63,7 @@ function startNetscript2Script(workerScript: WorkerScript): Promise<WorkerScript
// We need to go through the environment and wrap each function in such a way that it
// can be called at most once at a time. This will prevent situations where multiple
// hack promises are outstanding, for example.
function wrap(propName: string, f: Function): Function {
function wrap(propName: string, f: (...args: any[]) => Promise<void>): (...args: any[]) => Promise<void> {
// This function unfortunately cannot be an async function, because we don't
// know if the original one was, and there's no way to tell.
return function (...args: any[]) {
@ -85,7 +83,7 @@ function startNetscript2Script(workerScript: WorkerScript): Promise<WorkerScript
"Did you forget to await hack(), grow(), or some other " +
"promise-returning function? (Currently running: %s tried to run: %s)";
if (runningFn) {
workerScript.errorMessage = makeRuntimeRejectMsg(workerScript, sprintf(msg, runningFn, propName), null);
workerScript.errorMessage = makeRuntimeRejectMsg(workerScript, sprintf(msg, runningFn, propName));
throw workerScript;
}
runningFn = propName;
@ -112,7 +110,7 @@ function startNetscript2Script(workerScript: WorkerScript): Promise<WorkerScript
};
}
for (let prop in workerScript.env.vars) {
for (const prop in workerScript.env.vars) {
if (typeof workerScript.env.vars[prop] !== "function") continue;
workerScript.env.vars[prop] = wrap(prop, workerScript.env.vars[prop]);
}
@ -145,9 +143,9 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
workerScript.running = true;
//Process imports
var codeWithImports, codeLineOffset;
let codeWithImports, codeLineOffset;
try {
let importProcessingRes = processNetscript1Imports(code, workerScript);
const importProcessingRes = processNetscript1Imports(code, workerScript);
codeWithImports = importProcessingRes.code;
codeLineOffset = importProcessingRes.lineOffset;
} catch (e) {
@ -158,11 +156,11 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
return Promise.resolve(workerScript);
}
var interpreterInitialization = function (int: any, scope: any) {
const interpreterInitialization = function (int: any, scope: any): void {
//Add the Netscript environment
var ns = NetscriptFunctions(workerScript);
for (let name in ns) {
let entry = ns[name];
const ns = NetscriptFunctions(workerScript);
for (const name in ns) {
const entry = ns[name];
if (typeof entry === "function") {
//Async functions need to be wrapped. See JS-Interpreter documentation
if (
@ -173,20 +171,20 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
name === "prompt" ||
name === "manualHack"
) {
let tempWrapper = function () {
let fnArgs = [];
const tempWrapper = function (...args: any[]): void {
const fnArgs = [];
//All of the Object/array elements are in JSInterpreter format, so
//we have to convert them back to native format to pass them to these fns
for (let i = 0; i < arguments.length - 1; ++i) {
if (typeof arguments[i] === "object" || arguments[i].constructor === Array) {
fnArgs.push(int.pseudoToNative(arguments[i]));
for (let i = 0; i < args.length - 1; ++i) {
if (typeof args[i] === "object" || args[i].constructor === Array) {
fnArgs.push(int.pseudoToNative(args[i]));
} else {
fnArgs.push(arguments[i]);
fnArgs.push(args[i]);
}
}
let cb = arguments[arguments.length - 1];
let fnPromise = entry.apply(null, fnArgs);
const cb = args[args.length - 1];
const fnPromise = entry(...fnArgs);
fnPromise
.then(function (res: any) {
cb(res);
@ -206,25 +204,25 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
name === "run" ||
name === "exec"
) {
let tempWrapper = function () {
let fnArgs = [];
const tempWrapper = function (...args: any[]): void {
const fnArgs = [];
//All of the Object/array elements are in JSInterpreter format, so
//we have to convert them back to native format to pass them to these fns
for (let i = 0; i < arguments.length; ++i) {
if (typeof arguments[i] === "object" || arguments[i].constructor === Array) {
fnArgs.push(int.pseudoToNative(arguments[i]));
for (let i = 0; i < args.length; ++i) {
if (typeof args[i] === "object" || args[i].constructor === Array) {
fnArgs.push(int.pseudoToNative(args[i]));
} else {
fnArgs.push(arguments[i]);
fnArgs.push(args[i]);
}
}
return entry.apply(null, fnArgs);
return entry(...fnArgs);
};
int.setProperty(scope, name, int.createNativeFunction(tempWrapper));
} else {
let tempWrapper = function () {
let res = entry.apply(null, arguments);
const tempWrapper = function (...args: any[]): any {
const res = entry(...args);
if (res == null) {
return res;
@ -247,7 +245,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
int.setProperty(scope, "args", int.nativeToPseudo(workerScript.args));
};
var interpreter: any;
let interpreter: any;
try {
interpreter = new Interpreter(codeWithImports, interpreterInitialization, codeLineOffset);
} catch (e) {
@ -259,7 +257,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
}
return new Promise(function (resolve, reject) {
function runInterpreter() {
function runInterpreter(): void {
try {
if (workerScript.env.stopFlag) {
return reject(workerScript);
@ -314,7 +312,7 @@ function processNetscript1Imports(code: string, workerScript: WorkerScript): any
sourceType: "module",
});
var server = workerScript.getServer();
const server = workerScript.getServer();
if (server == null) {
throw new Error("Failed to find underlying Server object for script");
}
@ -339,11 +337,11 @@ function processNetscript1Imports(code: string, workerScript: WorkerScript): any
if (scriptName.startsWith("./")) {
scriptName = scriptName.slice(2);
}
let script = getScript(scriptName);
const script = getScript(scriptName);
if (script == null) {
throw new Error("'Import' failed due to invalid script: " + scriptName);
}
let scriptAst = parse(script.code, {
const scriptAst = parse(script.code, {
ecmaVersion: 9,
allowReserved: true,
sourceType: "module",
@ -351,9 +349,9 @@ function processNetscript1Imports(code: string, workerScript: WorkerScript): any
if (node.specifiers.length === 1 && node.specifiers[0].type === "ImportNamespaceSpecifier") {
// import * as namespace from script
let namespace = node.specifiers[0].local.name;
let fnNames: string[] = []; //Names only
let fnDeclarations: any[] = []; //FunctionDeclaration Node objects
const namespace = node.specifiers[0].local.name;
const fnNames: string[] = []; //Names only
const fnDeclarations: any[] = []; //FunctionDeclaration Node objects
walksimple(scriptAst, {
FunctionDeclaration: (node: any) => {
fnNames.push(node.id.name);
@ -382,13 +380,13 @@ function processNetscript1Imports(code: string, workerScript: WorkerScript): any
//import {...} from script
//Get array of all fns to import
let fnsToImport: string[] = [];
const fnsToImport: string[] = [];
node.specifiers.forEach((e: any) => {
fnsToImport.push(e.local.name);
});
//Walk through script and get FunctionDeclaration code for all specified fns
let fnDeclarations: any[] = [];
const fnDeclarations: any[] = [];
walksimple(scriptAst, {
FunctionDeclaration: (node: any) => {
if (fnsToImport.includes(node.id.name)) {
@ -412,7 +410,7 @@ function processNetscript1Imports(code: string, workerScript: WorkerScript): any
}
//Remove ImportDeclarations from AST. These ImportDeclarations must be in top-level
var linesRemoved = 0;
let linesRemoved = 0;
if (ast.type !== "Program" || ast.body == null) {
throw new Error("Code could not be properly parsed");
}
@ -424,7 +422,7 @@ function processNetscript1Imports(code: string, workerScript: WorkerScript): any
}
//Calculated line offset
var lineOffset = (generatedCode.match(/\n/g) || []).length - linesRemoved;
const lineOffset = (generatedCode.match(/\n/g) || []).length - linesRemoved;
//Convert the AST back into code
code = generate(ast);
@ -432,7 +430,7 @@ function processNetscript1Imports(code: string, workerScript: WorkerScript): any
//Add the imported code and re-generate in ES5 (JS Interpreter for NS1 only supports ES5);
code = generatedCode + code;
var res = {
const res = {
code: code,
lineOffset: lineOffset,
};
@ -595,8 +593,8 @@ export function createAndAddWorkerScript(
/**
* Updates the online running time stat of all running scripts
*/
export function updateOnlineScriptTimes(numCycles = 1) {
var time = (numCycles * CONSTANTS._idleSpeed) / 1000; //seconds
export function updateOnlineScriptTimes(numCycles = 1): void {
const time = (numCycles * CONSTANTS._idleSpeed) / 1000; //seconds
for (const ws of workerScripts.values()) {
ws.scriptRef.onlineRunningTime += time;
}
@ -606,8 +604,8 @@ export function updateOnlineScriptTimes(numCycles = 1) {
* Called when the game is loaded. Loads all running scripts (from all servers)
* into worker scripts so that they will start running
*/
export function loadAllRunningScripts() {
let skipScriptLoad = window.location.href.toLowerCase().indexOf("?noscripts") !== -1;
export function loadAllRunningScripts(): void {
const skipScriptLoad = window.location.href.toLowerCase().indexOf("?noscripts") !== -1;
if (skipScriptLoad) {
console.info("Skipping the load of any scripts during startup");
}
@ -661,7 +659,7 @@ export function runScriptFromScript(
}
// Check if the script is already running
let runningScriptObj = server.getRunningScript(scriptname, args);
const runningScriptObj = server.getRunningScript(scriptname, args);
if (runningScriptObj != null) {
workerScript.log(caller, `'${scriptname}' is already running on '${server.hostname}'`);
return 0;
@ -703,7 +701,7 @@ export function runScriptFromScript(
caller,
`'${scriptname}' on '${server.hostname}' with ${threads} threads and args: ${arrayToString(args)}.`,
);
let runningScriptObj = new RunningScript(script, args);
const runningScriptObj = new RunningScript(script, args);
runningScriptObj.threads = threads;
return startWorkerScript(runningScriptObj, server, workerScript);

@ -206,7 +206,7 @@ export interface IPlayer {
regenerateHp(amt: number): void;
recordMoneySource(amt: number, source: string): void;
setMoney(amt: number): void;
singularityStopWork(): void;
singularityStopWork(): string;
startBladeburner(p: any): void;
startFactionWork(router: IRouter, faction: Faction): void;
startClass(router: IRouter, costMult: number, expMult: number, className: string): void;

@ -213,7 +213,7 @@ export class PlayerObject implements IPlayer {
regenerateHp: (amt: number) => void;
recordMoneySource: (amt: number, source: string) => void;
setMoney: (amt: number) => void;
singularityStopWork: () => void;
singularityStopWork: () => string;
startBladeburner: (p: any) => void;
startFactionWork: (router: IRouter, faction: Faction) => void;
startClass: (router: IRouter, costMult: number, expMult: number, className: string) => void;
@ -409,8 +409,8 @@ export class PlayerObject implements IPlayer {
this.crimeType = "";
(this.timeWorked = 0), //in m;
(this.timeWorkedCreateProgram = 0);
this.timeWorked = 0; //in m;
this.timeWorkedCreateProgram = 0;
this.timeNeededToCompleteWork = 0;
this.work_money_mult = 1;
@ -438,15 +438,15 @@ export class PlayerObject implements IPlayer {
this.bladeburner = null;
this.bladeburner_max_stamina_mult = 1;
this.bladeburner_stamina_gain_mult = 1;
(this.bladeburner_analysis_mult = 1), //Field Analysis Onl;
(this.bladeburner_success_chance_mult = 1);
this.bladeburner_analysis_mult = 1; //Field Analysis Onl;
this.bladeburner_success_chance_mult = 1;
// Sleeves & Re-sleeving
this.sleeves = [];
this.resleeves = [];
(this.sleevesFromCovenant = 0), // # of Duplicate sleeves purchased from the covenan;
this.sleevesFromCovenant = 0; // # of Duplicate sleeves purchased from the covenan;
//bitnode
(this.bitNodeN = 1);
this.bitNodeN = 1;
//Used to store the last update time.
this.lastUpdate = 0;
@ -455,10 +455,10 @@ export class PlayerObject implements IPlayer {
this.playtimeSinceLastBitnode = 0;
// Keep track of where money comes from
(this.moneySourceA = new MoneySourceTracker()), // Where money comes from since last-installed Augmentatio;
(this.moneySourceB = new MoneySourceTracker()), // Where money comes from for this entire BitNode ru;
this.moneySourceA = new MoneySourceTracker(); // Where money comes from since last-installed Augmentatio;
this.moneySourceB = new MoneySourceTracker(); // Where money comes from for this entire BitNode ru;
// Production since last Augmentation installation
(this.scriptProdSinceLastAug = 0);
this.scriptProdSinceLastAug = 0;
this.exploits = [];

@ -2,7 +2,7 @@ import { Bladeburner } from "../../Bladeburner/Bladeburner";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { IPlayer } from "../IPlayer";
export function canAccessBladeburner(this: IPlayer) {
export function canAccessBladeburner(this: IPlayer): boolean {
if (this.bitNodeN === 8) {
return false;
}

@ -4,7 +4,7 @@ import { applyAugmentation } from "../../Augmentation/AugmentationHelpers";
import { PlayerOwnedAugmentation } from "../../Augmentation/PlayerOwnedAugmentation";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { CodingContractRewardType } from "../../CodingContracts";
import { CodingContractRewardType, ICodingContractReward } from "../../CodingContracts";
import { Company } from "../../Company/Company";
import { Companies } from "../../Company/Companies";
import { getNextCompanyPositionHelper } from "../../Company/GetNextCompanyPosition";
@ -15,13 +15,11 @@ import * as posNames from "../../Company/data/companypositionnames";
import { CONSTANTS } from "../../Constants";
import { Programs } from "../../Programs/Programs";
import { determineCrimeSuccess } from "../../Crime/CrimeHelpers";
import { ICodingContractReward } from "../../CodingContracts";
import { Crimes } from "../../Crime/Crimes";
import { Exploit } from "../../Exploits/Exploit";
import { Faction } from "../../Faction/Faction";
import { Factions } from "../../Faction/Factions";
import { resetGangs } from "../../Gang/AllGangs";
import { hasHacknetServers } from "../../Hacknet/HacknetHelpers";
import { Cities } from "../../Locations/Cities";
import { Locations } from "../../Locations/Locations";
import { CityName } from "../../Locations/data/CityNames";
@ -60,9 +58,9 @@ import { Money } from "../../ui/React/Money";
import React from "react";
export function init(this: IPlayer) {
export function init(this: IPlayer): void {
/* Initialize Player's home computer */
var t_homeComp = safetlyCreateUniqueServer({
const t_homeComp = safetlyCreateUniqueServer({
adminRights: true,
hostname: "home",
ip: createUniqueRandomIp(),
@ -78,8 +76,8 @@ export function init(this: IPlayer) {
this.getHomeComputer().programs.push(Programs.NukeProgram.name);
}
export function prestigeAugmentation(this: IPlayer) {
var homeComp = this.getHomeComputer();
export function prestigeAugmentation(this: IPlayer): void {
const homeComp = this.getHomeComputer();
this.currentServer = homeComp.ip;
this.homeComputer = homeComp.ip;
@ -178,7 +176,7 @@ export function prestigeAugmentation(this: IPlayer) {
this.hp = this.max_hp;
}
export function prestigeSourceFile(this: IPlayer) {
export function prestigeSourceFile(this: IPlayer): void {
this.prestigeAugmentation();
// Duplicate sleeves are reset to level 1 every Bit Node (but the number of sleeves you have persists)
for (let i = 0; i < this.sleeves.length; ++i) {
@ -262,7 +260,7 @@ export function updateSkillLevels(this: IPlayer): void {
this.intelligence = 0;
}
var ratio = this.hp / this.max_hp;
const ratio = this.hp / this.max_hp;
this.max_hp = Math.floor(10 + this.defense / 10);
this.hp = Math.round(this.max_hp * ratio);
}
@ -313,7 +311,7 @@ export function hasProgram(this: IPlayer, programName: string): boolean {
return false;
}
for (var i = 0; i < home.programs.length; ++i) {
for (let i = 0; i < home.programs.length; ++i) {
if (programName.toLowerCase() == home.programs[i].toLowerCase()) {
return true;
}
@ -354,7 +352,7 @@ export function canAfford(this: IPlayer, cost: number): boolean {
return this.money.gte(cost);
}
export function recordMoneySource(this: IPlayer, amt: number, source: string) {
export function recordMoneySource(this: IPlayer, amt: number, source: string): void {
if (!(this.moneySourceA instanceof MoneySourceTracker)) {
console.warn(`Player.moneySourceA was not properly initialized. Resetting`);
this.moneySourceA = new MoneySourceTracker();
@ -367,7 +365,7 @@ export function recordMoneySource(this: IPlayer, amt: number, source: string) {
this.moneySourceB.record(amt, source);
}
export function gainHackingExp(this: IPlayer, exp: number) {
export function gainHackingExp(this: IPlayer, exp: number): void {
if (isNaN(exp)) {
console.error("ERR: NaN passed into Player.gainHackingExp()");
return;
@ -380,7 +378,7 @@ export function gainHackingExp(this: IPlayer, exp: number) {
this.hacking_skill = calculateSkillF(this.hacking_exp, this.hacking_mult * BitNodeMultipliers.HackingLevelMultiplier);
}
export function gainStrengthExp(this: IPlayer, exp: number) {
export function gainStrengthExp(this: IPlayer, exp: number): void {
if (isNaN(exp)) {
console.error("ERR: NaN passed into Player.gainStrengthExp()");
return;
@ -393,7 +391,7 @@ export function gainStrengthExp(this: IPlayer, exp: number) {
this.strength = calculateSkillF(this.strength_exp, this.strength_mult * BitNodeMultipliers.StrengthLevelMultiplier);
}
export function gainDefenseExp(this: IPlayer, exp: number) {
export function gainDefenseExp(this: IPlayer, exp: number): void {
if (isNaN(exp)) {
console.error("ERR: NaN passed into player.gainDefenseExp()");
return;
@ -406,7 +404,7 @@ export function gainDefenseExp(this: IPlayer, exp: number) {
this.defense = calculateSkillF(this.defense_exp, this.defense_mult * BitNodeMultipliers.DefenseLevelMultiplier);
}
export function gainDexterityExp(this: IPlayer, exp: number) {
export function gainDexterityExp(this: IPlayer, exp: number): void {
if (isNaN(exp)) {
console.error("ERR: NaN passed into Player.gainDexterityExp()");
return;
@ -520,7 +518,7 @@ export function resetWorkStatus(this: IPlayer, generalType?: string, group?: str
this.className = "";
}
export function processWorkEarnings(this: IPlayer, numCycles = 1) {
export function processWorkEarnings(this: IPlayer, numCycles = 1): void {
const focusBonus = this.focus ? 1 : 0.8;
const hackExpGain = focusBonus * this.workHackExpGainRate * numCycles;
const strExpGain = focusBonus * this.workStrExpGainRate * numCycles;
@ -574,7 +572,7 @@ export function startWork(this: IPlayer, router: IRouter, companyName: string):
router.toWork();
}
export function cancelationPenalty(this: IPlayer) {
export function cancelationPenalty(this: IPlayer): number {
const specialIp = SpecialServerIps[this.companyName];
if (typeof specialIp === "string" && specialIp !== "") {
const server = AllServers[specialIp];
@ -588,7 +586,7 @@ export function cancelationPenalty(this: IPlayer) {
export function work(this: IPlayer, numCycles: number): boolean {
// Cap the number of cycles being processed to whatever would put you at
// the work time limit (8 hours)
var overMax = false;
let overMax = false;
if (this.timeWorked + CONSTANTS._idleSpeed * numCycles >= CONSTANTS.MillisecondsPer8Hours) {
overMax = true;
numCycles = Math.round((CONSTANTS.MillisecondsPer8Hours - this.timeWorked) / CONSTANTS._idleSpeed);
@ -714,7 +712,7 @@ export function startWorkPartTime(this: IPlayer, router: IRouter, companyName: s
export function workPartTime(this: IPlayer, numCycles: number): boolean {
//Cap the number of cycles being processed to whatever would put you at the
//work time limit (8 hours)
var overMax = false;
let overMax = false;
if (this.timeWorked + CONSTANTS._idleSpeed * numCycles >= CONSTANTS.MillisecondsPer8Hours) {
overMax = true;
numCycles = Math.round((CONSTANTS.MillisecondsPer8Hours - this.timeWorked) / CONSTANTS._idleSpeed);
@ -733,7 +731,7 @@ export function workPartTime(this: IPlayer, numCycles: number): boolean {
}
export function finishWorkPartTime(this: IPlayer, sing = false): string {
var company = Companies[this.companyName];
const company = Companies[this.companyName];
company.playerReputation += this.workRepGained;
this.updateSkillLevels();
@ -764,7 +762,7 @@ export function finishWorkPartTime(this: IPlayer, sing = false): string {
this.resetWorkStatus();
if (sing) {
var res =
const res =
"You worked for " +
convertTimeMsToTimeElapsedString(this.timeWorked) +
" and " +
@ -802,7 +800,7 @@ export function stopFocusing(this: IPlayer): void {
/* Working for Faction */
export function startFactionWork(this: IPlayer, router: IRouter, faction: Faction): void {
//Update reputation gain rate to account for faction favor
var favorMult = 1 + faction.favor / 100;
let favorMult = 1 + faction.favor / 100;
if (isNaN(favorMult)) {
favorMult = 1;
}
@ -818,7 +816,7 @@ export function startFactionWork(this: IPlayer, router: IRouter, faction: Factio
router.toWork();
}
export function startFactionHackWork(this: IPlayer, router: IRouter, faction: Faction) {
export function startFactionHackWork(this: IPlayer, router: IRouter, faction: Faction): void {
this.resetWorkStatus(CONSTANTS.WorkTypeFaction, faction.name, CONSTANTS.FactionWorkHacking);
this.workHackExpGainRate = 0.15 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
@ -833,7 +831,7 @@ export function startFactionHackWork(this: IPlayer, router: IRouter, faction: Fa
this.startFactionWork(router, faction);
}
export function startFactionFieldWork(this: IPlayer, router: IRouter, faction: Faction) {
export function startFactionFieldWork(this: IPlayer, router: IRouter, faction: Faction): void {
this.resetWorkStatus(CONSTANTS.WorkTypeFaction, faction.name, CONSTANTS.FactionWorkField);
this.workHackExpGainRate = 0.1 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
@ -850,7 +848,7 @@ export function startFactionFieldWork(this: IPlayer, router: IRouter, faction: F
this.startFactionWork(router, faction);
}
export function startFactionSecurityWork(this: IPlayer, router: IRouter, faction: Faction) {
export function startFactionSecurityWork(this: IPlayer, router: IRouter, faction: Faction): void {
this.resetWorkStatus(CONSTANTS.WorkTypeFaction, faction.name, CONSTANTS.FactionWorkSecurity);
this.workHackExpGainRate = 0.05 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
@ -867,7 +865,7 @@ export function startFactionSecurityWork(this: IPlayer, router: IRouter, faction
this.startFactionWork(router, faction);
}
export function workForFaction(this: IPlayer, numCycles: number) {
export function workForFaction(this: IPlayer, numCycles: number): boolean {
const faction = Factions[this.currentWorkFactionName];
//Constantly update the rep gain rate
@ -933,7 +931,7 @@ export function finishFactionWork(this: IPlayer, cancelled: boolean, sing = fals
this.isWorking = false;
this.resetWorkStatus();
if (sing) {
var res =
const res =
"You worked for your faction " +
faction.name +
" for a total of " +
@ -1146,7 +1144,7 @@ export function getWorkRepGain(this: IPlayer): number {
return 0;
}
var jobPerformance = companyPosition.calculateJobPerformance(
let jobPerformance = companyPosition.calculateJobPerformance(
this.hacking_skill,
this.strength,
this.defense,
@ -1159,7 +1157,7 @@ export function getWorkRepGain(this: IPlayer): number {
jobPerformance += this.intelligence / CONSTANTS.MaxSkillLevel;
//Update reputation gain rate to account for company favor
var favorMult = 1 + company.favor / 100;
let favorMult = 1 + company.favor / 100;
if (isNaN(favorMult)) {
favorMult = 1;
}
@ -1208,14 +1206,14 @@ export function startCreateProgramWork(
this.timeNeededToCompleteWork = time;
//Check for incomplete program
for (var i = 0; i < this.getHomeComputer().programs.length; ++i) {
var programFile = this.getHomeComputer().programs[i];
for (let i = 0; i < this.getHomeComputer().programs.length; ++i) {
const programFile = this.getHomeComputer().programs[i];
if (programFile.startsWith(programName) && programFile.endsWith("%-INC")) {
var res = programFile.split("-");
const res = programFile.split("-");
if (res.length != 3) {
break;
}
var percComplete = Number(res[1].slice(0, -1));
const percComplete = Number(res[1].slice(0, -1));
if (isNaN(percComplete) || percComplete < 0 || percComplete >= 100) {
break;
}
@ -1230,8 +1228,8 @@ export function startCreateProgramWork(
export function createProgramWork(this: IPlayer, numCycles: number): boolean {
//Higher hacking skill will allow you to create programs faster
var reqLvl = this.createProgramReqLvl;
var skillMult = (this.hacking_skill / reqLvl) * this.getIntelligenceBonus(3); //This should always be greater than 1;
const reqLvl = this.createProgramReqLvl;
let skillMult = (this.hacking_skill / reqLvl) * this.getIntelligenceBonus(3); //This should always be greater than 1;
skillMult = 1 + (skillMult - 1) / 5; //The divider constant can be adjusted as necessary
//Skill multiplier directly applied to "time worked"
@ -1246,7 +1244,7 @@ export function createProgramWork(this: IPlayer, numCycles: number): boolean {
}
export function finishCreateProgramWork(this: IPlayer, cancelled: boolean): string {
var programName = this.createProgramName;
const programName = this.createProgramName;
if (cancelled === false) {
dialogBoxCreate(
"You've finished creating " + programName + "!<br>" + "The new program can be found on your home computer.",
@ -1254,8 +1252,8 @@ export function finishCreateProgramWork(this: IPlayer, cancelled: boolean): stri
this.getHomeComputer().programs.push(programName);
} else {
var perc = (Math.floor((this.timeWorkedCreateProgram / this.timeNeededToCompleteWork) * 10000) / 100).toString();
var incompleteName = programName + "-" + perc + "%-INC";
const perc = (Math.floor((this.timeWorkedCreateProgram / this.timeNeededToCompleteWork) * 10000) / 100).toString();
const incompleteName = programName + "-" + perc + "%-INC";
this.getHomeComputer().programs.push(incompleteName);
}
@ -1281,8 +1279,8 @@ export function startClass(this: IPlayer, router: IRouter, costMult: number, exp
const gameCPS = 1000 / CONSTANTS._idleSpeed;
//Find cost and exp gain per game cycle
var cost = 0;
var hackExp = 0,
let cost = 0;
let hackExp = 0,
strExp = 0,
defExp = 0,
dexExp = 0,
@ -1381,7 +1379,7 @@ export function finishClass(this: IPlayer, sing = false): string {
this.isWorking = false;
if (sing) {
var res =
const res =
"After " +
this.className +
" for " +
@ -1709,7 +1707,7 @@ export function applyForJob(this: IPlayer, entryPosType: CompanyPosition, sing =
}
while (true) {
let newPos = getNextCompanyPositionHelper(pos);
const newPos = getNextCompanyPositionHelper(pos);
if (newPos == null) {
break;
}
@ -1737,7 +1735,7 @@ export function applyForJob(this: IPlayer, entryPosType: CompanyPosition, sing =
return false;
} else if (company.hasPosition(nextPos)) {
if (!sing) {
var reqText = getJobRequirementText(company, nextPos);
const reqText = getJobRequirementText(company, nextPos);
dialogBoxCreate("Unfortunately, you do not qualify for a promotion<br>" + reqText);
}
return false;
@ -1766,7 +1764,7 @@ export function getNextCompanyPosition(
company: Company,
entryPosType: CompanyPosition,
): CompanyPosition | null {
var currCompany = null;
let currCompany = null;
if (this.companyName !== "") {
currCompany = Companies[this.companyName];
}
@ -1818,7 +1816,7 @@ export function applyForItJob(this: IPlayer, sing = false): boolean {
}
export function applyForSecurityEngineerJob(this: IPlayer, sing = false): boolean {
var company = Companies[this.location]; //Company being applied to
const company = Companies[this.location]; //Company being applied to
if (this.isQualified(company, CompanyPositions[posNames.SecurityEngineerCompanyPositions[0]])) {
return this.applyForJob(CompanyPositions[posNames.SecurityEngineerCompanyPositions[0]], sing);
} else {
@ -1830,7 +1828,7 @@ export function applyForSecurityEngineerJob(this: IPlayer, sing = false): boolea
}
export function applyForNetworkEngineerJob(this: IPlayer, sing = false): boolean {
var company = Companies[this.location]; //Company being applied to
const company = Companies[this.location]; //Company being applied to
if (this.isQualified(company, CompanyPositions[posNames.NetworkEngineerCompanyPositions[0]])) {
const pos = CompanyPositions[posNames.NetworkEngineerCompanyPositions[0]];
return this.applyForJob(pos, sing);
@ -1857,7 +1855,7 @@ export function applyForSecurityJob(this: IPlayer, sing = false): boolean {
}
export function applyForAgentJob(this: IPlayer, sing = false): boolean {
var company = Companies[this.location]; //Company being applied to
const company = Companies[this.location]; //Company being applied to
if (this.isQualified(company, CompanyPositions[posNames.AgentCompanyPositions[0]])) {
const pos = CompanyPositions[posNames.AgentCompanyPositions[0]];
return this.applyForJob(pos, sing);
@ -1870,7 +1868,7 @@ export function applyForAgentJob(this: IPlayer, sing = false): boolean {
}
export function applyForEmployeeJob(this: IPlayer, sing = false): boolean {
var company = Companies[this.location]; //Company being applied to
const company = Companies[this.location]; //Company being applied to
if (this.isQualified(company, CompanyPositions[posNames.MiscCompanyPositions[1]])) {
this.companyName = company.name;
this.jobs[company.name] = posNames.MiscCompanyPositions[1];
@ -1889,7 +1887,7 @@ export function applyForEmployeeJob(this: IPlayer, sing = false): boolean {
}
export function applyForPartTimeEmployeeJob(this: IPlayer, sing = false): boolean {
var company = Companies[this.location]; //Company being applied to
const company = Companies[this.location]; //Company being applied to
if (this.isQualified(company, CompanyPositions[posNames.PartTimeCompanyPositions[1]])) {
this.jobs[company.name] = posNames.PartTimeCompanyPositions[1];
if (!sing) {
@ -1907,7 +1905,7 @@ export function applyForPartTimeEmployeeJob(this: IPlayer, sing = false): boolea
}
export function applyForWaiterJob(this: IPlayer, sing = false): boolean {
var company = Companies[this.location]; //Company being applied to
const company = Companies[this.location]; //Company being applied to
if (this.isQualified(company, CompanyPositions[posNames.MiscCompanyPositions[0]])) {
this.companyName = company.name;
this.jobs[company.name] = posNames.MiscCompanyPositions[0];
@ -1924,7 +1922,7 @@ export function applyForWaiterJob(this: IPlayer, sing = false): boolean {
}
export function applyForPartTimeWaiterJob(this: IPlayer, sing = false): boolean {
var company = Companies[this.location]; //Company being applied to
const company = Companies[this.location]; //Company being applied to
if (this.isQualified(company, CompanyPositions[posNames.PartTimeCompanyPositions[0]])) {
this.companyName = company.name;
this.jobs[company.name] = posNames.PartTimeCompanyPositions[0];
@ -1942,13 +1940,13 @@ export function applyForPartTimeWaiterJob(this: IPlayer, sing = false): boolean
//Checks if the Player is qualified for a certain position
export function isQualified(this: IPlayer, company: Company, position: CompanyPosition): boolean {
var offset = company.jobStatReqOffset;
var reqHacking = position.requiredHacking > 0 ? position.requiredHacking + offset : 0;
var reqStrength = position.requiredStrength > 0 ? position.requiredStrength + offset : 0;
var reqDefense = position.requiredDefense > 0 ? position.requiredDefense + offset : 0;
var reqDexterity = position.requiredDexterity > 0 ? position.requiredDexterity + offset : 0;
var reqAgility = position.requiredDexterity > 0 ? position.requiredDexterity + offset : 0;
var reqCharisma = position.requiredCharisma > 0 ? position.requiredCharisma + offset : 0;
const offset = company.jobStatReqOffset;
const reqHacking = position.requiredHacking > 0 ? position.requiredHacking + offset : 0;
const reqStrength = position.requiredStrength > 0 ? position.requiredStrength + offset : 0;
const reqDefense = position.requiredDefense > 0 ? position.requiredDefense + offset : 0;
const reqDexterity = position.requiredDexterity > 0 ? position.requiredDexterity + offset : 0;
const reqAgility = position.requiredDexterity > 0 ? position.requiredDexterity + offset : 0;
const reqCharisma = position.requiredCharisma > 0 ? position.requiredCharisma + offset : 0;
if (
this.hacking_skill >= reqHacking &&
@ -1977,7 +1975,7 @@ export function reapplyAllAugmentations(this: IPlayer, resetMultipliers = true):
}
const augName = this.augmentations[i].name;
var aug = Augmentations[augName];
const aug = Augmentations[augName];
if (aug == null) {
console.warn(`Invalid augmentation name in Player.reapplyAllAugmentations(). Aug ${augName} will be skipped`);
continue;
@ -2000,8 +1998,8 @@ export function reapplyAllSourceFiles(this: IPlayer): void {
//this.resetMultipliers();
for (let i = 0; i < this.sourceFiles.length; ++i) {
var srcFileKey = "SourceFile" + this.sourceFiles[i].n;
var sourceFileObject = SourceFiles[srcFileKey];
const srcFileKey = "SourceFile" + this.sourceFiles[i].n;
const sourceFileObject = SourceFiles[srcFileKey];
if (sourceFileObject == null) {
console.error(`Invalid source file number: ${this.sourceFiles[i].n}`);
continue;
@ -2016,9 +2014,9 @@ export function reapplyAllSourceFiles(this: IPlayer): void {
//those requirements and will return an array of all factions that the Player should
//receive an invitation to
export function checkForFactionInvitations(this: IPlayer): Faction[] {
let invitedFactions: Faction[] = []; //Array which will hold all Factions the player should be invited to
const invitedFactions: Faction[] = []; //Array which will hold all Factions the player should be invited to
var numAugmentations = this.augmentations.length;
const numAugmentations = this.augmentations.length;
const allCompanies = Object.keys(this.jobs);
const allPositions = Object.values(this.jobs);
@ -2042,7 +2040,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//Illuminati
var illuminatiFac = Factions["Illuminati"];
const illuminatiFac = Factions["Illuminati"];
if (
!illuminatiFac.isBanned &&
!illuminatiFac.isMember &&
@ -2059,7 +2057,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//Daedalus
var daedalusFac = Factions["Daedalus"];
const daedalusFac = Factions["Daedalus"];
if (
!daedalusFac.isBanned &&
!daedalusFac.isMember &&
@ -2073,7 +2071,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//The Covenant
var covenantFac = Factions["The Covenant"];
const covenantFac = Factions["The Covenant"];
if (
!covenantFac.isBanned &&
!covenantFac.isMember &&
@ -2090,7 +2088,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//ECorp
var ecorpFac = Factions["ECorp"];
const ecorpFac = Factions["ECorp"];
if (
!ecorpFac.isBanned &&
!ecorpFac.isMember &&
@ -2101,7 +2099,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//MegaCorp
var megacorpFac = Factions["MegaCorp"];
const megacorpFac = Factions["MegaCorp"];
if (
!megacorpFac.isBanned &&
!megacorpFac.isMember &&
@ -2112,7 +2110,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//Bachman & Associates
var bachmanandassociatesFac = Factions["Bachman & Associates"];
const bachmanandassociatesFac = Factions["Bachman & Associates"];
if (
!bachmanandassociatesFac.isBanned &&
!bachmanandassociatesFac.isMember &&
@ -2123,7 +2121,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//Blade Industries
var bladeindustriesFac = Factions["Blade Industries"];
const bladeindustriesFac = Factions["Blade Industries"];
if (
!bladeindustriesFac.isBanned &&
!bladeindustriesFac.isMember &&
@ -2134,7 +2132,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//NWO
var nwoFac = Factions["NWO"];
const nwoFac = Factions["NWO"];
if (
!nwoFac.isBanned &&
!nwoFac.isMember &&
@ -2145,7 +2143,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//Clarke Incorporated
var clarkeincorporatedFac = Factions["Clarke Incorporated"];
const clarkeincorporatedFac = Factions["Clarke Incorporated"];
if (
!clarkeincorporatedFac.isBanned &&
!clarkeincorporatedFac.isMember &&
@ -2156,7 +2154,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//OmniTek Incorporated
var omnitekincorporatedFac = Factions["OmniTek Incorporated"];
const omnitekincorporatedFac = Factions["OmniTek Incorporated"];
if (
!omnitekincorporatedFac.isBanned &&
!omnitekincorporatedFac.isMember &&
@ -2167,7 +2165,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//Four Sigma
var foursigmaFac = Factions["Four Sigma"];
const foursigmaFac = Factions["Four Sigma"];
if (
!foursigmaFac.isBanned &&
!foursigmaFac.isMember &&
@ -2178,7 +2176,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//KuaiGong International
var kuaigonginternationalFac = Factions["KuaiGong International"];
const kuaigonginternationalFac = Factions["KuaiGong International"];
if (
!kuaigonginternationalFac.isBanned &&
!kuaigonginternationalFac.isMember &&
@ -2261,7 +2259,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//Chongqing
var chongqingFac = Factions["Chongqing"];
const chongqingFac = Factions["Chongqing"];
if (
!chongqingFac.isBanned &&
!chongqingFac.isMember &&
@ -2273,7 +2271,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//Sector-12
var sector12Fac = Factions["Sector-12"];
const sector12Fac = Factions["Sector-12"];
if (
!sector12Fac.isBanned &&
!sector12Fac.isMember &&
@ -2285,7 +2283,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//New Tokyo
var newtokyoFac = Factions["New Tokyo"];
const newtokyoFac = Factions["New Tokyo"];
if (
!newtokyoFac.isBanned &&
!newtokyoFac.isMember &&
@ -2297,7 +2295,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//Aevum
var aevumFac = Factions["Aevum"];
const aevumFac = Factions["Aevum"];
if (
!aevumFac.isBanned &&
!aevumFac.isMember &&
@ -2309,7 +2307,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//Ishima
var ishimaFac = Factions["Ishima"];
const ishimaFac = Factions["Ishima"];
if (
!ishimaFac.isBanned &&
!ishimaFac.isMember &&
@ -2321,7 +2319,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//Volhaven
var volhavenFac = Factions["Volhaven"];
const volhavenFac = Factions["Volhaven"];
if (
!volhavenFac.isBanned &&
!volhavenFac.isMember &&
@ -2333,7 +2331,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//Speakers for the Dead
var speakersforthedeadFac = Factions["Speakers for the Dead"];
const speakersforthedeadFac = Factions["Speakers for the Dead"];
if (
!speakersforthedeadFac.isBanned &&
!speakersforthedeadFac.isMember &&
@ -2352,7 +2350,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//The Dark Army
var thedarkarmyFac = Factions["The Dark Army"];
const thedarkarmyFac = Factions["The Dark Army"];
if (
!thedarkarmyFac.isBanned &&
!thedarkarmyFac.isMember &&
@ -2372,7 +2370,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//The Syndicate
var thesyndicateFac = Factions["The Syndicate"];
const thesyndicateFac = Factions["The Syndicate"];
if (
!thesyndicateFac.isBanned &&
!thesyndicateFac.isMember &&
@ -2392,7 +2390,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//Silhouette
var silhouetteFac = Factions["Silhouette"];
const silhouetteFac = Factions["Silhouette"];
if (
!silhouetteFac.isBanned &&
!silhouetteFac.isMember &&
@ -2407,7 +2405,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//Tetrads
var tetradsFac = Factions["Tetrads"];
const tetradsFac = Factions["Tetrads"];
if (
!tetradsFac.isBanned &&
!tetradsFac.isMember &&
@ -2423,7 +2421,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//SlumSnakes
var slumsnakesFac = Factions["Slum Snakes"];
const slumsnakesFac = Factions["Slum Snakes"];
if (
!slumsnakesFac.isBanned &&
!slumsnakesFac.isMember &&
@ -2439,10 +2437,10 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//Netburners
var netburnersFac = Factions["Netburners"];
var totalHacknetRam = 0;
var totalHacknetCores = 0;
var totalHacknetLevels = 0;
const netburnersFac = Factions["Netburners"];
let totalHacknetRam = 0;
let totalHacknetCores = 0;
let totalHacknetLevels = 0;
for (let i = 0; i < this.hacknetNodes.length; ++i) {
const v = this.hacknetNodes[i];
if (typeof v === "string") {
@ -2470,7 +2468,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
//Tian Di Hui
var tiandihuiFac = Factions["Tian Di Hui"];
const tiandihuiFac = Factions["Tian Di Hui"];
if (
!tiandihuiFac.isBanned &&
!tiandihuiFac.isMember &&
@ -2503,11 +2501,11 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
}
/************* BitNodes **************/
export function setBitNodeNumber(this: IPlayer, n: number) {
export function setBitNodeNumber(this: IPlayer, n: number): void {
this.bitNodeN = n;
}
export function queueAugmentation(this: IPlayer, name: string) {
export function queueAugmentation(this: IPlayer, name: string): void {
for (const i in this.queuedAugmentations) {
if (this.queuedAugmentations[i].name == name) {
console.warn(`tried to queue ${name} twice, this may be a bug`);
@ -2526,7 +2524,7 @@ export function queueAugmentation(this: IPlayer, name: string) {
}
/************* Coding Contracts **************/
export function gainCodingContractReward(this: IPlayer, reward: ICodingContractReward, difficulty = 1) {
export function gainCodingContractReward(this: IPlayer, reward: ICodingContractReward, difficulty = 1): string {
if (reward == null || reward.type == null || reward == null) {
return `No reward for this contract`;
}
@ -2539,7 +2537,7 @@ export function gainCodingContractReward(this: IPlayer, reward: ICodingContractR
reward.type = CodingContractRewardType.FactionReputationAll;
return this.gainCodingContractReward(reward);
}
var repGain = CONSTANTS.CodingContractBaseFactionRepGain * difficulty;
const repGain = CONSTANTS.CodingContractBaseFactionRepGain * difficulty;
Factions[reward.name].playerReputation += repGain;
return `Gained ${repGain} faction reputation for ${reward.name}`;
case CodingContractRewardType.FactionReputationAll:
@ -2547,8 +2545,7 @@ export function gainCodingContractReward(this: IPlayer, reward: ICodingContractR
// Ignore Bladeburners and other special factions for this calculation
const specialFactions = ["Bladeburners"];
var factions = this.factions.slice();
factions = factions.filter((f) => {
const factions = this.factions.slice().filter((f) => {
return !specialFactions.includes(f);
});
@ -2567,28 +2564,28 @@ export function gainCodingContractReward(this: IPlayer, reward: ICodingContractR
}
return `Gained ${gainPerFaction} reputation for each of the following factions: ${factions.toString()}`;
break;
case CodingContractRewardType.CompanyReputation:
case CodingContractRewardType.CompanyReputation: {
if (reward.name == null || !(Companies[reward.name] instanceof Company)) {
//If no/invalid company was designated, just give rewards to all factions
reward.type = CodingContractRewardType.FactionReputationAll;
return this.gainCodingContractReward(reward);
}
var repGain = CONSTANTS.CodingContractBaseCompanyRepGain * difficulty;
const repGain = CONSTANTS.CodingContractBaseCompanyRepGain * difficulty;
Companies[reward.name].playerReputation += repGain;
return `Gained ${repGain} company reputation for ${reward.name}`;
break;
}
case CodingContractRewardType.Money:
default:
var moneyGain = CONSTANTS.CodingContractBaseMoneyGain * difficulty * BitNodeMultipliers.CodingContractMoney;
default: {
const moneyGain = CONSTANTS.CodingContractBaseMoneyGain * difficulty * BitNodeMultipliers.CodingContractMoney;
this.gainMoney(moneyGain);
this.recordMoneySource(moneyGain, "codingcontract");
return `Gained ${numeralWrapper.formatMoney(moneyGain)}`;
break;
}
}
/* eslint-enable no-case-declarations */
}
export function travel(this: IPlayer, to: CityName) {
export function travel(this: IPlayer, to: CityName): boolean {
if (Cities[to] == null) {
console.warn(`Player.travel() called with invalid city: ${to}`);
return false;
@ -2598,7 +2595,7 @@ export function travel(this: IPlayer, to: CityName) {
return true;
}
export function gotoLocation(this: IPlayer, to: LocationName) {
export function gotoLocation(this: IPlayer, to: LocationName): boolean {
if (Locations[to] == null) {
console.warn(`Player.gotoLocation() called with invalid location: ${to}`);
return false;
@ -2608,21 +2605,21 @@ export function gotoLocation(this: IPlayer, to: LocationName) {
return true;
}
export function canAccessResleeving(this: IPlayer) {
export function canAccessResleeving(this: IPlayer): boolean {
return this.bitNodeN === 10 || SourceFileFlags[10] > 0;
}
export function giveExploit(this: IPlayer, exploit: Exploit) {
export function giveExploit(this: IPlayer, exploit: Exploit): void {
if (!this.exploits.includes(exploit)) {
this.exploits.push(exploit);
}
}
export function getIntelligenceBonus(this: IPlayer, weight: number) {
export function getIntelligenceBonus(this: IPlayer, weight: number): number {
return calculateIntelligenceBonus(this.intelligence, weight);
}
export function getCasinoWinnings(this: IPlayer) {
export function getCasinoWinnings(this: IPlayer): number {
return this.moneySourceA.casino;
}

@ -479,6 +479,7 @@ export class Sleeve extends Person {
let time = this.storedCycles * CONSTANTS.MilliPerCycle;
let cyclesUsed = this.storedCycles;
cyclesUsed = Math.min(cyclesUsed, 15);
if (this.currentTaskMaxTime !== 0 && this.currentTaskTime + time > this.currentTaskMaxTime) {
time = this.currentTaskMaxTime - this.currentTaskTime;
cyclesUsed = Math.floor(time / CONSTANTS.MilliPerCycle);

@ -47,7 +47,7 @@ export function SleeveAugmentationsPopup(props: IProps): React.ReactElement {
tooltip = renderToStaticMarkup(tooltip);
}
tooltip += "<br /><br />";
tooltip += renderToStaticMarkup(aug.stats);
tooltip += renderToStaticMarkup(aug.stats || <></>);
return (
<div key={augName} className="gang-owned-upgrade tooltip">
{augName}
@ -70,7 +70,7 @@ export function SleeveAugmentationsPopup(props: IProps): React.ReactElement {
info = renderToStaticMarkup(info);
}
info += "<br /><br />";
info += renderToStaticMarkup(aug.stats);
info += renderToStaticMarkup(aug.stats || <></>);
return (
<div key={aug.name} className="cmpy-mgmt-upgrade-div" onClick={() => purchaseAugmentation(aug)}>

@ -1,6 +1,8 @@
import { Sleeve } from "../Sleeve";
import { numeralWrapper } from "../../../ui/numeralFormat";
import * as React from "react";
import { convertTimeMsToTimeElapsedString } from "../../../../utils/StringHelperFunctions";
import { CONSTANTS } from "../../../Constants";
interface IProps {
sleeve: Sleeve;
@ -77,6 +79,14 @@ export function StatsElement(props: IProps): React.ReactElement {
{numeralWrapper.formatSleeveMemory(props.sleeve.memory)}
</td>
</tr>
{props.sleeve.storedCycles > 15 && (
<tr>
<td>Bonus time: </td>
<td style={style}>
{convertTimeMsToTimeElapsedString((props.sleeve.storedCycles / (1000 / CONSTANTS._idleSpeed)) * 1000)}
</td>
</tr>
)}
</tbody>
</table>
</>

@ -6,7 +6,6 @@ import { Bladeburner } from "./Bladeburner/Bladeburner";
import { Companies, initCompanies } from "./Company/Companies";
import { resetIndustryResearchTrees } from "./Corporation/IndustryData";
import { Programs } from "./Programs/Programs";
import { Engine } from "./engine";
import { Faction } from "./Faction/Faction";
import { Factions, initFactions } from "./Faction/Factions";
import { joinFaction } from "./Faction/FactionHelpers";
@ -155,7 +154,7 @@ function prestigeSourceFile(flume: boolean): void {
Player.prestigeSourceFile();
prestigeWorkerScripts(); // Delete all Worker Scripts objects
var homeComp = Player.getHomeComputer();
const homeComp = Player.getHomeComputer();
// Delete all servers except home computer
prestigeAllServers(); // Must be done before initForeignServers()
@ -180,14 +179,14 @@ function prestigeSourceFile(flume: boolean): void {
homeComp.cpuCores = 1;
// Reset favor for Companies
for (var member in Companies) {
for (const member in Companies) {
if (Companies.hasOwnProperty(member)) {
Companies[member].favor = 0;
}
}
// Reset favor for factions
for (var member in Factions) {
for (const member in Factions) {
if (Factions.hasOwnProperty(member)) {
Factions[member].favor = 0;
}
@ -199,7 +198,7 @@ function prestigeSourceFile(flume: boolean): void {
}
// Delete all Augmentations
for (var name in Augmentations) {
for (const name in Augmentations) {
if (Augmentations.hasOwnProperty(name)) {
delete Augmentations[name];
}

@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
import { use } from "../../ui/Context";
import { getAvailableCreatePrograms } from "../ProgramHelpers";
import { Box, ButtonGroup, Tooltip, Typography } from "@mui/material";
import { Box, Tooltip, Typography } from "@mui/material";
import Button from "@mui/material/Button";
export function ProgramsRoot(): React.ReactElement {
@ -20,33 +20,34 @@ export function ProgramsRoot(): React.ReactElement {
return (
<>
<div>
<Box>
<Typography variant="h4">Create program</Typography>
<Typography>
This page displays any programs that you are able to create. Writing the code for a program takes time,
which can vary based on how complex the program is. If you are working on creating a program you can cancel
at any time. Your progress will be saved and you can continue later.
This page displays any programs that you are able to create. Writing the code for a program takes time, which
can vary based on how complex the program is. If you are working on creating a program you can cancel at any
time. Your progress will be saved and you can continue later.
</Typography>
</Box>
<ButtonGroup>
{getAvailableCreatePrograms(player).map((program) => {
const create = program.create;
if (create === null) return <></>;
return (
<Tooltip key={program.name} title={create.tooltip}>
<React.Fragment key={program.name}>
<Tooltip title={create.tooltip}>
<Button
onClick={() => {
sx={{ my: 1 }}
onClick={(event) => {
if (!event.isTrusted) return;
player.startCreateProgramWork(router, program.name, create.time, create.level);
}}
>
{program.name}
</Button>
</Tooltip>
<br />
</React.Fragment>
);
})}
</ButtonGroup>
</div>
</>
);
}

@ -17,17 +17,17 @@ export function setRedPillFlag(b: boolean): void {
}
function giveSourceFile(bitNodeNumber: number): void {
var sourceFileKey = "SourceFile" + bitNodeNumber.toString();
var sourceFile = SourceFiles[sourceFileKey];
const sourceFileKey = "SourceFile" + bitNodeNumber.toString();
const sourceFile = SourceFiles[sourceFileKey];
if (sourceFile == null) {
console.error(`Could not find source file for Bit node: ${bitNodeNumber}`);
return;
}
// Check if player already has this source file
var alreadyOwned = false;
var ownedSourceFile = null;
for (var i = 0; i < Player.sourceFiles.length; ++i) {
let alreadyOwned = false;
let ownedSourceFile = null;
for (let i = 0; i < Player.sourceFiles.length; ++i) {
if (Player.sourceFiles[i].n === bitNodeNumber) {
alreadyOwned = true;
ownedSourceFile = Player.sourceFiles[i];
@ -51,7 +51,7 @@ function giveSourceFile(bitNodeNumber: number): void {
);
}
} else {
var playerSrcFile = new PlayerOwnedSourceFile(bitNodeNumber, 1);
const playerSrcFile = new PlayerOwnedSourceFile(bitNodeNumber, 1);
Player.sourceFiles.push(playerSrcFile);
if (bitNodeNumber === 5 && Player.intelligence === 0) {
// Artificial Intelligence

6
src/SaveObject.d.ts vendored

@ -1,6 +0,0 @@
export declare const saveObject: {
getSaveString: () => string;
saveGame: () => void;
exportGame: () => void;
};
export declare function loadGame(s: string): boolean;

@ -1,7 +1,6 @@
import { loadAliases, loadGlobalAliases, Aliases, GlobalAliases } from "./Alias";
import { Companies, loadCompanies } from "./Company/Companies";
import { CONSTANTS } from "./Constants";
import { Engine } from "./engine";
import { Factions, loadFactions } from "./Faction/Factions";
import { loadAllGangs, AllGangs } from "./Gang/AllGangs";
import { loadMessages, initMessages, Messages } from "./Message/MessageHelpers";
@ -18,12 +17,9 @@ import { setTimeoutRef } from "./utils/SetTimeoutRef";
import * as ExportBonus from "./ExportBonus";
import { dialogBoxCreate } from "../utils/DialogBox";
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver";
import { save } from "./db";
import Decimal from "decimal.js";
/* SaveObject.js
* Defines the object used to save/load games
*/
@ -47,14 +43,14 @@ class BitburnerSaveObject {
this.PlayerSave = JSON.stringify(Player);
// Delete all logs from all running scripts
var TempAllServers = JSON.parse(JSON.stringify(AllServers), Reviver);
for (var ip in TempAllServers) {
var server = TempAllServers[ip];
const TempAllServers = JSON.parse(JSON.stringify(AllServers), Reviver);
for (const ip in TempAllServers) {
const server = TempAllServers[ip];
if (server == null) {
continue;
}
for (var i = 0; i < server.runningScripts.length; ++i) {
var runningScriptObj = server.runningScripts[i];
for (let i = 0; i < server.runningScripts.length; ++i) {
const runningScriptObj = server.runningScripts[i];
runningScriptObj.logs.length = 0;
runningScriptObj.logs = [];
}
@ -74,7 +70,7 @@ class BitburnerSaveObject {
if (Player.inGang()) {
this.AllGangsSave = JSON.stringify(AllGangs);
}
var saveString = btoa(unescape(encodeURIComponent(JSON.stringify(this))));
const saveString = btoa(unescape(encodeURIComponent(JSON.stringify(this))));
return saveString;
}
@ -94,13 +90,13 @@ class BitburnerSaveObject {
const epochTime = Math.round(Date.now() / 1000);
const bn = Player.bitNodeN;
const filename = `bitburnerSave_BN${bn}x${SourceFileFlags[bn]}_${epochTime}.json`;
var file = new Blob([saveString], { type: "text/plain" });
const file = new Blob([saveString], { type: "text/plain" });
if (window.navigator.msSaveOrOpenBlob) {
// IE10+
window.navigator.msSaveOrOpenBlob(file, filename);
} else {
// Others
var a = document.createElement("a"),
const a = document.createElement("a"),
url = URL.createObjectURL(file);
a.href = url;
a.download = filename;
@ -124,7 +120,7 @@ class BitburnerSaveObject {
// Makes necessary changes to the loaded/imported data to ensure
// the game stills works with new versions
function evaluateVersionCompatibility(ver: string) {
function evaluateVersionCompatibility(ver: string): void {
// We have to do this because ts won't let us otherwise
const anyPlayer = Player as any;
// This version refactored the Company/job-related code
@ -239,7 +235,7 @@ function loadGame(saveString: string): boolean {
}
if (saveObj.hasOwnProperty("VersionSave")) {
try {
var ver = JSON.parse(saveObj.VersionSave, Reviver);
const ver = JSON.parse(saveObj.VersionSave, Reviver);
evaluateVersionCompatibility(ver);
if (window.location.href.toLowerCase().includes("bitburner-beta")) {
@ -265,7 +261,7 @@ function loadGame(saveString: string): boolean {
return true;
}
function createNewUpdateText() {
function createNewUpdateText(): void {
dialogBoxCreate(
"New update!<br>" +
"Please report any bugs/issues through the github repository " +
@ -274,7 +270,7 @@ function createNewUpdateText() {
);
}
function createBetaUpdateText() {
function createBetaUpdateText(): void {
dialogBoxCreate(
"You are playing on the beta environment! This branch of the game " +
"features the latest developments in the game. This version may be unstable.<br>" +
@ -288,4 +284,4 @@ Reviver.constructors.BitburnerSaveObject = BitburnerSaveObject;
export { saveObject, loadGame };
let saveObject = new BitburnerSaveObject();
const saveObject = new BitburnerSaveObject();

@ -104,7 +104,7 @@ async function parseOnlyRamCalculate(
}
let script = null;
let fn = nextModule.startsWith("./") ? nextModule.slice(2) : nextModule;
const fn = nextModule.startsWith("./") ? nextModule.slice(2) : nextModule;
for (const s of otherScripts) {
if (s.filename === fn) {
script = s;
@ -147,14 +147,14 @@ async function parseOnlyRamCalculate(
if (ref.endsWith(".*")) {
// A prefix reference. We need to find all matching identifiers.
const prefix = ref.slice(0, ref.length - 2);
for (let ident of Object.keys(dependencyMap).filter((k) => k.startsWith(prefix))) {
for (let dep of dependencyMap[ident] || []) {
for (const ident of Object.keys(dependencyMap).filter((k) => k.startsWith(prefix))) {
for (const dep of dependencyMap[ident] || []) {
if (!resolvedRefs.has(dep)) unresolvedRefs.push(dep);
}
}
} else {
// An exact reference. Add all dependencies of this ref.
for (let dep of dependencyMap[ref] || []) {
for (const dep of dependencyMap[ref] || []) {
if (!resolvedRefs.has(dep)) unresolvedRefs.push(dep);
}
}
@ -162,7 +162,7 @@ async function parseOnlyRamCalculate(
// Check if this identifier is a function in the workerScript environment.
// If it is, then we need to get its RAM cost.
try {
function applyFuncRam(func: any) {
function applyFuncRam(func: any): number {
if (typeof func === "function") {
try {
let res;
@ -235,9 +235,9 @@ function parseOnlyCalculateDeps(code: string, currentModule: string): any {
// If we reference this internal name, we're really referencing that external name.
// Filled when we import names from other modules.
let internalToExternal: { [key: string]: string | undefined } = {};
const internalToExternal: { [key: string]: string | undefined } = {};
let additionalModules: string[] = [];
const additionalModules: string[] = [];
// References get added pessimistically. They are added for thisModule.name, name, and for
// any aliases.
@ -256,7 +256,7 @@ function parseOnlyCalculateDeps(code: string, currentModule: string): any {
// If we discover a dependency identifier, state.key is the dependent identifier.
// walkDeeper is for doing recursive walks of expressions in composites that we handle.
function commonVisitors() {
function commonVisitors(): any {
return {
Identifier: (node: any, st: any) => {
if (objectPrototypeProperties.includes(node.name)) {

@ -91,16 +91,11 @@ export class Script {
* @param {string} code - The new contents of the script
* @param {Script[]} otherScripts - Other scripts on the server. Used to process imports
*/
saveScript(code: string, serverIp: string, otherScripts: Script[]): void {
saveScript(filename: string, code: string, serverIp: string, otherScripts: Script[]): void {
// Update code and filename
this.code = code.replace(/^\s+|\s+$/g, "");
const filenameElem: HTMLInputElement | null = document.getElementById("script-editor-filename") as HTMLInputElement;
if (filenameElem == null) {
console.error(`Failed to get Script filename DOM element`);
return;
}
this.filename = filenameElem.value;
this.filename = filename;
this.server = serverIp;
this.updateRamUsage(otherScripts);
this.markUpdated();

@ -10,7 +10,7 @@ import { numeralWrapper } from "../ui/numeralFormat";
import { compareArrays } from "../../utils/helpers/compareArrays";
export function scriptCalculateOfflineProduction(runningScript: RunningScript) {
export function scriptCalculateOfflineProduction(runningScript: RunningScript): void {
//The Player object stores the last update time from when we were online
const thisUpdate = new Date().getTime();
const lastUpdate = Player.lastUpdate;
@ -83,8 +83,12 @@ export function scriptCalculateOfflineProduction(runningScript: RunningScript) {
//Returns a RunningScript object matching the filename and arguments on the
//designated server, and false otherwise
export function findRunningScript(filename: string, args: (string | number)[], server: BaseServer) {
for (var i = 0; i < server.runningScripts.length; ++i) {
export function findRunningScript(
filename: string,
args: (string | number)[],
server: BaseServer,
): RunningScript | null {
for (let i = 0; i < server.runningScripts.length; ++i) {
if (server.runningScripts[i].filename === filename && compareArrays(server.runningScripts[i].args, args)) {
return server.runningScripts[i];
}
@ -94,8 +98,8 @@ export function findRunningScript(filename: string, args: (string | number)[], s
//Returns a RunningScript object matching the pid on the
//designated server, and false otherwise
export function findRunningScriptByPid(pid: number, server: BaseServer) {
for (var i = 0; i < server.runningScripts.length; ++i) {
export function findRunningScriptByPid(pid: number, server: BaseServer): RunningScript | null {
for (let i = 0; i < server.runningScripts.length; ++i) {
if (server.runningScripts[i].pid === pid) {
return server.runningScripts[i];
}

@ -0,0 +1,54 @@
import React, { useState } from "react";
import { Options } from "./Options";
import { Modal } from "../../ui/React/Modal";
import Button from "@mui/material/Button";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import Select from "@mui/material/Select";
import Switch from "@mui/material/Switch";
import MenuItem from "@mui/material/MenuItem";
interface IProps {
options: Options;
save: (options: Options) => void;
onClose: () => void;
open: boolean;
}
export function OptionsModal(props: IProps): React.ReactElement {
const [theme, setTheme] = useState(props.options.theme);
const [insertSpaces, setInsertSpaces] = useState(props.options.insertSpaces);
function save(): void {
props.save({
theme: theme,
insertSpaces: insertSpaces,
});
props.onClose();
}
return (
<Modal open={props.open} onClose={props.onClose}>
<Box display="flex" flexDirection="row" alignItems="center">
<Typography>Theme: </Typography>
<Select
variant="standard"
color="primary"
onChange={(event) => setTheme(event.target.value)}
defaultValue={props.options.theme}
>
<MenuItem value="vs-dark">dark</MenuItem>
<MenuItem value="light">light</MenuItem>
</Select>
</Box>
<Box display="flex" flexDirection="row" alignItems="center">
<Typography>Use whitespace over tabs: </Typography>
<Switch onChange={(event) => setInsertSpaces(event.target.checked)} checked={insertSpaces} />
</Box>
<br />
<Button onClick={save}>Save</Button>
</Modal>
);
}

@ -1,41 +0,0 @@
import React, { useState } from "react";
import { Options } from "./Options";
import { StdButton } from "../../ui/React/StdButton";
import { removePopup } from "../../ui/React/createPopup";
interface IProps {
id: string;
options: Options;
save: (options: Options) => void;
}
export function OptionsPopup(props: IProps): React.ReactElement {
const [theme, setTheme] = useState(props.options.theme);
const [insertSpaces, setInsertSpaces] = useState(props.options.insertSpaces);
function save(): void {
props.save({
theme: theme,
insertSpaces: insertSpaces,
});
removePopup(props.id);
}
return (
<div className="editor-options-container noselect">
<div className="editor-options-line">
<p>Theme: </p>
<select className="dropdown" onChange={(event) => setTheme(event.target.value)} defaultValue={theme}>
<option value="vs-dark">vs-dark</option>
<option value="light">light</option>
</select>
</div>
<div className="editor-options-line">
<p>Use whitespace over tabs: </p>
<input type="checkbox" onChange={(event) => setInsertSpaces(event.target.checked)} checked={insertSpaces} />
</div>
<br />
<StdButton style={{ width: "50px" }} text={"Save"} onClick={save} />
</div>
);
}

@ -1,10 +1,8 @@
import React, { useState, useEffect, useRef } from "react";
import { StdButton } from "../../ui/React/StdButton";
import Editor from "@monaco-editor/react";
import * as monaco from "monaco-editor";
type IStandaloneCodeEditor = monaco.editor.IStandaloneCodeEditor;
import { createPopup } from "../../ui/React/createPopup";
import { OptionsPopup } from "./OptionsPopup";
import { OptionsModal } from "./OptionsModal";
import { Options } from "./Options";
import { js_beautify as beautifyCode } from "js-beautify";
import { isValidFilePath } from "../../Terminal/DirectoryHelpers";
@ -24,7 +22,14 @@ import { WorkerScript } from "../../Netscript/WorkerScript";
import { Settings } from "../../Settings/Settings";
import { iTutorialNextStep, ITutorial, iTutorialSteps } from "../../InteractiveTutorial";
let loaded = false;
import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography";
import Link from "@mui/material/Link";
import Box from "@mui/material/Box";
import TextField from "@mui/material/TextField";
import IconButton from "@mui/material/IconButton";
import SettingsIcon from "@mui/icons-material/Settings";
let symbols: string[] = [];
(function () {
const ns = NetscriptFunctions({} as WorkerScript);
@ -78,6 +83,7 @@ export function Root(props: IProps): React.ReactElement {
const [filename, setFilename] = useState(props.filename ? props.filename : lastFilename);
const [code, setCode] = useState<string>(props.filename ? props.code : lastCode);
const [ram, setRAM] = useState("RAM: ???");
const [optionsOpen, setOptionsOpen] = useState(false);
const [options, setOptions] = useState<Options>({
theme: Settings.MonacoTheme,
insertSpaces: Settings.MonacoInsertSpaces,
@ -121,14 +127,14 @@ export function Root(props: IProps): React.ReactElement {
let found = false;
for (let i = 0; i < server.scripts.length; i++) {
if (filename == server.scripts[i].filename) {
server.scripts[i].saveScript(code, props.player.currentServer, server.scripts);
server.scripts[i].saveScript(filename, code, props.player.currentServer, server.scripts);
found = true;
}
}
if (!found) {
const script = new Script();
script.saveScript(code, props.player.currentServer, server.scripts);
script.saveScript(filename, code, props.player.currentServer, server.scripts);
server.scripts.push(script);
}
@ -156,7 +162,7 @@ export function Root(props: IProps): React.ReactElement {
//If the current script already exists on the server, overwrite it
for (let i = 0; i < server.scripts.length; i++) {
if (filename == server.scripts[i].filename) {
server.scripts[i].saveScript(code, props.player.currentServer, server.scripts);
server.scripts[i].saveScript(filename, code, props.player.currentServer, server.scripts);
props.router.toTerminal();
return;
}
@ -164,7 +170,7 @@ export function Root(props: IProps): React.ReactElement {
//If the current script does NOT exist, create a new one
const script = new Script();
script.saveScript(code, props.player.currentServer, server.scripts);
script.saveScript(filename, code, props.player.currentServer, server.scripts);
server.scripts.push(script);
} else if (filename.endsWith(".txt")) {
for (let i = 0; i < server.textFiles.length; ++i) {
@ -198,24 +204,6 @@ export function Root(props: IProps): React.ReactElement {
setFilename(event.target.value);
}
function openOptions(): void {
const id = "script-editor-options-popup";
const newOptions = {
theme: "",
insertSpaces: false,
};
Object.assign(newOptions, options);
createPopup(id, OptionsPopup, {
id: id,
options: newOptions,
save: (options: Options) => {
setOptions(options);
Settings.MonacoTheme = options.theme;
Settings.MonacoInsertSpaces = options.insertSpaces;
},
});
}
function updateCode(newCode?: string): void {
if (newCode === undefined) return;
lastCode = newCode;
@ -302,26 +290,20 @@ export function Root(props: IProps): React.ReactElement {
});
monaco.languages.typescript.javascriptDefaults.addExtraLib(libSource, "netscript.d.ts");
monaco.languages.typescript.typescriptDefaults.addExtraLib(libSource, "netscript.d.ts");
loaded = true;
}
return (
<>
<div id="script-editor-filename-wrapper">
<p id="script-editor-filename-tag" className="noselect">
{" "}
<strong style={{ backgroundColor: "#555" }}>Script name: </strong>
</p>
<input
id="script-editor-filename"
type="text"
maxLength={100}
tabIndex={1}
value={filename}
onChange={onFilenameChange}
/>
<StdButton text={"options"} onClick={openOptions} />
</div>
<Box display="flex" flexDirection="row" alignItems="center">
<Typography>Script name: </Typography>
<TextField variant="standard" type="text" tabIndex={1} value={filename} onChange={onFilenameChange} />
<IconButton onClick={() => setOptionsOpen(true)}>
<>
<SettingsIcon />
options
</>
</IconButton>
</Box>
<Editor
beforeMount={beforeMount}
onMount={onMount}
@ -333,23 +315,27 @@ export function Root(props: IProps): React.ReactElement {
theme={options.theme}
options={options}
/>
<div id="script-editor-buttons-wrapper">
<StdButton text={"Beautify"} onClick={beautify} />
<p id="script-editor-status-text" style={{ display: "inline-block", margin: "10px" }}>
{ram}
</p>
<button className="std-button" style={{ display: "inline-block" }} onClick={save}>
Save & Close (Ctrl/Cmd + b)
</button>
<a
className="std-button"
style={{ display: "inline-block" }}
target="_blank"
href="https://bitburner.readthedocs.io/en/latest/index.html"
>
<Box display="flex" flexDirection="row" sx={{ m: 1 }} alignItems="center">
<Button onClick={beautify}>Beautify</Button>
<Typography sx={{ mx: 1 }}>{ram}</Typography>
<Button onClick={save}>Save & Close (Ctrl/Cmd + b)</Button>
<Link sx={{ mx: 1 }} target="_blank" href="https://bitburner.readthedocs.io/en/latest/index.html">
Netscript Documentation
</a>
</div>
</Link>
</Box>
<OptionsModal
open={optionsOpen}
onClose={() => setOptionsOpen(false)}
options={{
theme: Settings.MonacoTheme,
insertSpaces: Settings.MonacoInsertSpaces,
}}
save={(options: Options) => {
setOptions(options);
Settings.MonacoTheme = options.theme;
Settings.MonacoInsertSpaces = options.insertSpaces;
}}
/>
</>
);
}

@ -1,94 +1,5 @@
// Enums that defined allowed values for setting configuration
/**
* Allowed values for 'Keybinding/Keymap' setting in Ace editor
*/
export enum AceKeybindingSetting {
Ace = "ace",
Emacs = "emacs",
Vim = "vim",
}
/**
* Allowed values for 'Keybinding/Keymap' setting in Code Mirror editor
*/
export enum CodeMirrorKeybindingSetting {
Default = "default",
Emacs = "emacs",
Sublime = "sublime",
Vim = "vim",
}
/**
* Allowed values for 'Theme' setting in Code Mirror editor
*/
export enum CodeMirrorThemeSetting {
Monokai = "monokai",
Day_3024 = "3024-day",
Night_3024 = "3024-night",
abcdef = "abcdef",
Ambiance_mobile = "ambiance-mobile",
Ambiance = "ambiance",
Base16_dark = "base16-dark",
Base16_light = "base16-light",
Bespin = "bespin",
Blackboard = "blackboard",
Cobalt = "cobalt",
Colorforth = "colorforth",
Darcula = "darcula",
Dracula = "dracula",
Duotone_dark = "duotone-dark",
Duotone_light = "duotone-light",
Eclipse = "eclipse",
Elegant = "elegant",
Erlang_dark = "erlang-dark",
Gruvbox_dark = "gruvbox-dark",
Hopscotch = "hopscotch",
Icecoder = "icecoder",
Idea = "idea",
Isotope = "isotope",
Lesser_dark = "lesser-dark",
Liquibyte = "liquibyte",
Lucario = "lucario",
Material = "material",
Mbo = "mbo",
Mdn_like = "mdn-like",
Midnight = "midnight",
Neat = "neat",
Neo = "neo",
Night = "night",
Oceanic_next = "oceanic-next",
Panda_syntax = "panda-syntax",
Paraiso_dark = "paraiso-dark",
Paraiso_light = "paraiso-light",
Pastel_on_dark = "pastel-on-dark",
Railscasts = "railscasts",
Rubyblue = "rubyblue",
Seti = "seti",
Shadowfox = "shadowfox",
Solarized = "solarized",
SolarizedDark = "solarized dark",
ssms = "ssms",
The_matrix = "the-matrix",
Tomorrow_night_bright = "tomorrow-night-bright",
Tomorrow_night_eighties = "tomorrow-night-eighties",
Ttcn = "ttcn",
Twilight = "twilight",
Vibrant_ink = "vibrant-ink",
xq_dark = "xq-dark",
xq_light = "xq-light",
Yeti = "yeti",
Zenburn = "zenburn",
}
/**
* Allowed values for the "Editor" setting
*/
export enum EditorSetting {
Ace = "Ace",
CodeMirror = "CodeMirror",
}
/**
* Allowed values for the 'OwnedAugmentationsOrder' setting
*/

@ -1,13 +1,13 @@
import { BitNodes } from "../BitNode/BitNode";
export class SourceFile {
info: string;
info: JSX.Element;
lvl = 1;
n: number;
name: string;
owned = false;
constructor(number: number, info = "") {
constructor(number: number, info: JSX.Element) {
const bitnodeKey = "BitNode" + number;
const bitnode = BitNodes[bitnodeKey];
if (bitnode == null) {

@ -1,104 +0,0 @@
import { SourceFile } from "./SourceFile";
import { IMap } from "../types";
export const SourceFiles: IMap<SourceFile> = {};
SourceFiles["SourceFile1"] = new SourceFile(
1,
"This Source-File lets the player start with 32GB of RAM on his/her " +
"home computer. It also increases all of the player's multipliers by:<br><br>" +
"Level 1: 16%<br>" +
"Level 2: 24%<br>" +
"Level 3: 28%",
);
SourceFiles["SourceFile2"] = new SourceFile(
2,
"This Source-File allows you to form gangs in other BitNodes " +
"once your karma decreases to a certain value. It also increases the player's " +
"crime success rate, crime money, and charisma multipliers by:<br><br>" +
"Level 1: 24%<br>" +
"Level 2: 36%<br>" +
"Level 3: 42%",
);
SourceFiles["SourceFile3"] = new SourceFile(
3,
"This Source-File lets you create corporations on other BitNodes (although " +
"some BitNodes will disable this mechanic). This Source-File also increases your charisma and company salary multipliers by:<br>" +
"Level 1: 8%<br>" +
"Level 2: 12%<br>" +
"Level 3: 14%",
);
SourceFiles["SourceFile4"] = new SourceFile(
4,
"This Source-File lets you access and use the Singularity Functions in every BitNode. Every " +
"level of this Source-File opens up more of the Singularity Functions you can use.",
);
SourceFiles["SourceFile5"] = new SourceFile(
5,
"This Source-File grants a special new stat called Intelligence. Intelligence " +
"is unique because it is permanent and persistent (it never gets reset back to 1). However, " +
"gaining Intelligence experience is much slower than other stats, and it is also hidden (you won't " +
"know when you gain experience and how much). Higher Intelligence levels will boost your production " +
"for many actions in the game. In addition, this Source-File will unlock the getBitNodeMultipliers() " +
"and getServer() Netscript functions, as well as the formulas API, and will raise all of your " +
"hacking-related multipliers by:<br><br> " +
"Level 1: 8%<br>" +
"Level 2: 12%<br>" +
"Level 3: 14%",
);
SourceFiles["SourceFile6"] = new SourceFile(
6,
"This Source-File allows you to access the NSA's Bladeburner Division in other " +
"BitNodes. In addition, this Source-File will raise both the level and experience gain rate of all your combat stats by:<br><br>" +
"Level 1: 8%<br>" +
"Level 2: 12%<br>" +
"Level 3: 14%",
);
SourceFiles["SourceFile7"] = new SourceFile(
7,
"This Source-File allows you to access the Bladeburner Netscript API in other " +
"BitNodes. In addition, this Source-File will increase all of your Bladeburner multipliers by:<br><br>" +
"Level 1: 8%<br>" +
"Level 2: 12%<br>" +
"Level 3: 14%",
);
SourceFiles["SourceFile8"] = new SourceFile(
8,
"This Source-File grants the following benefits:<br><br>" +
"Level 1: Permanent access to WSE and TIX API<br>" +
"Level 2: Ability to short stocks in other BitNodes<br>" +
"Level 3: Ability to use limit/stop orders in other BitNodes<br><br>" +
"This Source-File also increases your hacking growth multipliers by: " +
"<br>Level 1: 12%<br>Level 2: 18%<br>Level 3: 21%",
);
SourceFiles["SourceFile9"] = new SourceFile(
9,
"This Source-File grants the following benefits:<br><br>" +
"Level 1: Permanently unlocks the Hacknet Server in other BitNodes<br>" +
"Level 2: You start with 128GB of RAM on your home computer when entering a new BitNode<br>" +
"Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode<br><br>" +
"(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT " +
"when installing Augmentations)",
);
SourceFiles["SourceFile10"] = new SourceFile(
10,
"This Source-File unlocks Sleeve technology in other BitNodes. Each level of this " +
"Source-File also grants you a Duplicate Sleeve",
);
SourceFiles["SourceFile11"] = new SourceFile(
11,
"This Source-File makes it so that company favor increases BOTH the player's salary and reputation gain rate " +
"at that company by 1% per favor (rather than just the reputation gain). This Source-File also " +
" increases the player's company salary and reputation gain multipliers by:<br><br>" +
"Level 1: 32%<br>" +
"Level 2: 48%<br>" +
"Level 3: 56%<br><br>" +
"It also reduces the price increase for every aug bought by:<br><br>" +
"Level 1: 4%<br>" +
"Level 2: 6%<br>" +
"Level 3: 7%",
);
SourceFiles["SourceFile12"] = new SourceFile(
12,
"This Source-File lets the player start with Neuroflux Governor equal to the level of this Source-File.",
);

@ -0,0 +1,197 @@
import React from "react";
import { SourceFile } from "./SourceFile";
import { IMap } from "../types";
export const SourceFiles: IMap<SourceFile> = {};
SourceFiles["SourceFile1"] = new SourceFile(
1,
(
<>
This Source-File lets the player start with 32GB of RAM on his/her home computer. It also increases all of the
player's multipliers by:
<br />
<br />
Level 1: 16%
<br />
Level 2: 24%
<br />
Level 3: 28%
</>
),
);
SourceFiles["SourceFile2"] = new SourceFile(
2,
(
<>
This Source-File allows you to form gangs in other BitNodes once your karma decreases to a certain value. It also
increases the player's crime success rate, crime money, and charisma multipliers by:
<br />
<br />
Level 1: 24%
<br />
Level 2: 36%
<br />
Level 3: 42%
</>
),
);
SourceFiles["SourceFile3"] = new SourceFile(
3,
(
<>
This Source-File lets you create corporations on other BitNodes (although some BitNodes will disable this
mechanic). This Source-File also increases your charisma and company salary multipliers by:
<br />
Level 1: 8%
<br />
Level 2: 12%
<br />
Level 3: 14%
</>
),
);
SourceFiles["SourceFile4"] = new SourceFile(
4,
(
<>
This Source-File lets you access and use the Singularity Functions in every BitNode. Every level of this
Source-File opens up more of the Singularity Functions you can use.
</>
),
);
SourceFiles["SourceFile5"] = new SourceFile(
5,
(
<>
This Source-File grants a special new stat called Intelligence. Intelligence is unique because it is permanent and
persistent (it never gets reset back to 1). However, gaining Intelligence experience is much slower than other
stats, and it is also hidden (you won't know when you gain experience and how much). Higher Intelligence levels
will boost your production for many actions in the game. In addition, this Source-File will unlock the
getBitNodeMultipliers() and getServer() Netscript functions, as well as the formulas API, and will raise all of
your hacking-related multipliers by:
<br />
<br />
Level 1: 8%
<br />
Level 2: 12%
<br />
Level 3: 14%
</>
),
);
SourceFiles["SourceFile6"] = new SourceFile(
6,
(
<>
This Source-File allows you to access the NSA's Bladeburner Division in other BitNodes. In addition, this
Source-File will raise both the level and experience gain rate of all your combat stats by:
<br />
<br />
Level 1: 8%
<br />
Level 2: 12%
<br />
Level 3: 14%
</>
),
);
SourceFiles["SourceFile7"] = new SourceFile(
7,
(
<>
This Source-File allows you to access the Bladeburner Netscript API in other BitNodes. In addition, this
Source-File will increase all of your Bladeburner multipliers by:
<br />
<br />
Level 1: 8%
<br />
Level 2: 12%
<br />
Level 3: 14%
</>
),
);
SourceFiles["SourceFile8"] = new SourceFile(
8,
(
<>
This Source-File grants the following benefits:
<br />
<br />
Level 1: Permanent access to WSE and TIX API
<br />
Level 2: Ability to short stocks in other BitNodes
<br />
Level 3: Ability to use limit/stop orders in other BitNodes
<br />
<br />
This Source-File also increases your hacking growth multipliers by:
<br />
Level 1: 12%
<br />
Level 2: 18%
<br />
Level 3: 21%
</>
),
);
SourceFiles["SourceFile9"] = new SourceFile(
9,
(
<>
This Source-File grants the following benefits:
<br />
<br />
Level 1: Permanently unlocks the Hacknet Server in other BitNodes
<br />
Level 2: You start with 128GB of RAM on your home computer when entering a new BitNode
<br />
Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode
<br />
<br />
(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT when installing
Augmentations)
</>
),
);
SourceFiles["SourceFile10"] = new SourceFile(
10,
(
<>
This Source-File unlocks Sleeve technology in other BitNodes. Each level of this Source-File also grants you a
Duplicate Sleeve
</>
),
);
SourceFiles["SourceFile11"] = new SourceFile(
11,
(
<>
This Source-File makes it so that company favor increases BOTH the player's salary and reputation gain rate at
that company by 1% per favor (rather than just the reputation gain). This Source-File also increases the player's
company salary and reputation gain multipliers by:
<br />
<br />
Level 1: 32%
<br />
Level 2: 48%
<br />
Level 3: 56%
<br />
<br />
It also reduces the price increase for every aug bought by:
<br />
<br />
Level 1: 4%
<br />
Level 2: 6%
<br />
Level 3: 7%
</>
),
);
SourceFiles["SourceFile12"] = new SourceFile(
12,
<>This Source-File lets the player start with Neuroflux Governor equal to the level of this Source-File.</>,
);

@ -6,7 +6,7 @@ import Box from "@mui/material/Box";
export function TutorialRoot(): React.ReactElement {
return (
<>
<Typography variant="h4">Tutorial (AKA Links to Documentation)</Typography>
<Typography variant="h4">Tutorial / Documentation</Typography>
<Box m={2}>
<Link
color="primary"

@ -1,6 +1,3 @@
import { Engine } from "./engine";
import { createStatusText } from "./ui/createStatusText";
function getDB(): Promise<IDBObjectStore> {
return new Promise((resolve, reject) => {
if (!window.indexedDB) {
@ -24,7 +21,7 @@ function getDB(): Promise<IDBObjectStore> {
reject(`Failed to get IDB ${ev}`);
};
indexedDbRequest.onsuccess = function (this: IDBRequest<IDBDatabase>, ev: Event) {
indexedDbRequest.onsuccess = function (this: IDBRequest<IDBDatabase>) {
const db = this.result;
if (!db) {
reject("database loadign result was undefined");
@ -35,11 +32,6 @@ function getDB(): Promise<IDBObjectStore> {
});
}
interface ILoadCallback {
success: (s: string) => void;
error?: () => void;
}
export function load(): Promise<string> {
return new Promise(async (resolve, reject) => {
await getDB()
@ -59,11 +51,6 @@ export function load(): Promise<string> {
});
}
interface ISaveCallback {
success: () => void;
error?: () => void;
}
export function save(saveString: string): Promise<void> {
return getDB().then((db) => {
return new Promise<void>((resolve, reject) => {

1
src/engine.d.ts vendored

@ -1 +0,0 @@
export declare const Engine: IEngine;

@ -231,7 +231,7 @@ const Engine: {
}
if (Engine.Counters.passiveFactionGrowth <= 0) {
var adjustedCycles = Math.floor(5 - Engine.Counters.passiveFactionGrowth);
const adjustedCycles = Math.floor(5 - Engine.Counters.passiveFactionGrowth);
processPassiveFactionRepGain(adjustedCycles);
Engine.Counters.passiveFactionGrowth = 5;
}
@ -334,7 +334,7 @@ const Engine: {
}
// Hacknet Nodes offline progress
var offlineProductionFromHacknetNodes = processHacknetEarnings(Player, numCyclesOffline);
const offlineProductionFromHacknetNodes = processHacknetEarnings(Player, numCyclesOffline);
const hacknetProdInfo = hasHacknetServers(Player) ? (
<>{Hashes(offlineProductionFromHacknetNodes)} hashes</>
) : (
@ -384,7 +384,7 @@ const Engine: {
}
// Update total playtime
var time = numCyclesOffline * CONSTANTS._idleSpeed;
const time = numCyclesOffline * CONSTANTS._idleSpeed;
if (Player.totalPlaytime == null) {
Player.totalPlaytime = 0;
}

@ -12,7 +12,7 @@ ReactDOM.render(
document.getElementById("mainmenu-container"),
);
function rerender() {
function rerender(): void {
refreshTheme();
ReactDOM.render(
<Theme>

@ -28,6 +28,7 @@ export function ActiveScriptsRoot(props: IProps): React.ReactElement {
return (
<>
<Typography variant="h4">Active Scripts</Typography>
<Typography>
This page displays a list of all of your scripts that are currently running across every machine. It also
provides information about each script's production. The scripts are categorized by the hostname of the servers

@ -145,7 +145,7 @@ function CurrentBitNode(): React.ReactElement {
const index = "BitNode" + player.bitNodeN;
return (
<>
<Typography variant="h5" color="primary">
<Typography variant="h4">
BitNode {player.bitNodeN}: {BitNodes[index].name}
</Typography>
<Typography sx={{ mx: 2 }} style={{ whiteSpace: "pre-wrap", overflowWrap: "break-word" }}>
@ -270,9 +270,7 @@ export function CharacterStats(): React.ReactElement {
return (
<>
<Typography variant="h5" color="primary">
General
</Typography>
<Typography variant="h4">General</Typography>
<Box sx={{ mx: 2 }}>
<Typography>Current City: {player.city}</Typography>
<LastEmployer />
@ -287,9 +285,7 @@ export function CharacterStats(): React.ReactElement {
</Typography>
</Box>
<br />
<Typography variant="h5" color="primary">
Stats
</Typography>
<Typography variant="h4">Stats</Typography>
<Box sx={{ mx: 2 }}>
<Table size="small" padding="none">
<TableBody>
@ -365,9 +361,7 @@ export function CharacterStats(): React.ReactElement {
<br />
</Box>
<br />
<Typography variant="h5" color="primary">
Multipliers
</Typography>
<Typography variant="h4">Multipliers</Typography>
<Box sx={{ mx: 2 }}>
<MultiplierTable
rows={[
@ -477,9 +471,7 @@ export function CharacterStats(): React.ReactElement {
</Box>
<br />
<Typography variant="h5" color="primary">
Misc
</Typography>
<Typography variant="h4">Misc</Typography>
<Box sx={{ mx: 2 }}>
<Typography>{`Servers owned: ${player.purchasedServers.length} / ${getPurchaseServerLimit()}`}</Typography>
<Hacknet />

@ -23,7 +23,7 @@ export function LoadingScreen(): React.ReactElement {
});
useEffect(() => {
async function doLoad() {
async function doLoad(): Promise<void> {
await load()
.then((saveString) => {
Engine.load(saveString);

@ -4,12 +4,18 @@
* The header of the accordion contains the Augmentation's name (and level, if
* applicable), and the accordion's panel contains the Augmentation's description.
*/
import * as React from "react";
import { BBAccordion } from "./BBAccordion";
import React, { useState } from "react";
import { Augmentation } from "../../Augmentation/Augmentation";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemText from "@mui/material/ListItemText";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper";
import Collapse from "@mui/material/Collapse";
import ExpandMore from "@mui/icons-material/ExpandMore";
import ExpandLess from "@mui/icons-material/ExpandLess";
type IProps = {
aug: Augmentation;
@ -17,6 +23,7 @@ type IProps = {
};
export function AugmentationAccordion(props: IProps): React.ReactElement {
const [open, setOpen] = useState(false);
let displayName = props.aug.name;
if (props.level != null) {
if (props.aug.name === AugmentationNames.NeuroFluxGovernor) {
@ -26,31 +33,47 @@ export function AugmentationAccordion(props: IProps): React.ReactElement {
if (typeof props.aug.info === "string") {
return (
<BBAccordion
headerContent={<>{displayName}</>}
panelContent={
<p>
<span dangerouslySetInnerHTML={{ __html: props.aug.info }} />
<Box component={Paper}>
<ListItemButton onClick={() => setOpen((old) => !old)}>
<ListItemText primary={<Typography style={{ whiteSpace: "pre-wrap" }}>{displayName}</Typography>} />
{open ? <ExpandLess color="primary" /> : <ExpandMore color="primary" />}
</ListItemButton>
<Collapse in={open} unmountOnExit>
<Box m={4}>
<Typography dangerouslySetInnerHTML={{ __html: props.aug.info }} />
{props.aug.stats && (
<>
<br />
<br />
{props.aug.stats}
</p>
}
/>
<Typography>{props.aug.stats}</Typography>
</>
)}
</Box>
</Collapse>
</Box>
);
}
return (
<BBAccordion
headerContent={<>{displayName}</>}
panelContent={
<p>
<Box component={Paper}>
<ListItemButton onClick={() => setOpen((old) => !old)}>
<ListItemText primary={<Typography style={{ whiteSpace: "pre-wrap" }}>{displayName}</Typography>} />
{open ? <ExpandLess color="primary" /> : <ExpandMore color="primary" />}
</ListItemButton>
<Collapse in={open} unmountOnExit>
<Box m={4}>
<Typography>
{props.aug.info}
{props.aug.stats && (
<>
<br />
<br />
{props.aug.stats}
</p>
}
/>
</>
)}
</Typography>
</Box>
</Collapse>
</Box>
);
}

@ -4,30 +4,47 @@
* The header of the accordion contains the Source-Files's name and level,
* and the accordion's panel contains the Source-File's description.
*/
import * as React from "react";
import { BBAccordion } from "./BBAccordion";
import React, { useState } from "react";
import { SourceFile } from "../../SourceFile/SourceFile";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemText from "@mui/material/ListItemText";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper";
import Collapse from "@mui/material/Collapse";
import ExpandMore from "@mui/icons-material/ExpandMore";
import ExpandLess from "@mui/icons-material/ExpandLess";
type IProps = {
level: number;
sf: SourceFile;
};
export function SourceFileAccordion(props: IProps): React.ReactElement {
const [open, setOpen] = useState(false);
const maxLevel = props.sf.n === 12 ? "∞" : "3";
return (
<BBAccordion
headerContent={
<>
<Box component={Paper}>
<ListItemButton onClick={() => setOpen((old) => !old)}>
<ListItemText
primary={
<Typography style={{ whiteSpace: "pre-wrap" }}>
{props.sf.name}
<br />
{`Level ${props.level} / ${maxLevel}`}
</>
</Typography>
}
panelContent={<p dangerouslySetInnerHTML={{ __html: props.sf.info }}></p>}
/>
{open ? <ExpandLess color="primary" /> : <ExpandMore color="primary" />}
</ListItemButton>
<Collapse in={open} unmountOnExit>
<Box m={4}>
<Typography>{props.sf.info}</Typography>
</Box>
</Collapse>
</Box>
);
}

@ -32,7 +32,7 @@ declare module "@mui/material/styles" {
let theme: Theme;
export function refreshTheme() {
export function refreshTheme(): void {
theme = createTheme({
colors: {
hp: Settings.theme.hp,

@ -3,7 +3,6 @@ import { Modal } from "./Modal";
import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import DoneIcon from "@mui/icons-material/Done";
import IconButton from "@mui/material/IconButton";
import ReplyIcon from "@mui/icons-material/Reply";
import { Color, ColorPicker } from "material-ui-color";
@ -15,30 +14,15 @@ interface IProps {
onClose: () => void;
}
function ColorEditor({ name }: { name: string }): React.ReactElement {
const [color, setColor] = useState(Settings.theme[name]);
if (color === undefined) return <></>;
const valid = color.match(/#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})/g);
interface IColorEditorProps {
name: string;
color: string | undefined;
onColorChange: (name: string, value: string) => void;
defaultColor: string;
}
function set(): void {
if (!valid) return;
Settings.theme[name] = color;
ThemeEvents.emit();
}
function revert(): void {
Settings.theme[name] = defaultSettings.theme[name];
setColor(defaultSettings.theme[name]);
ThemeEvents.emit();
}
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
setColor(event.target.value);
}
function onColorPickerChange(newValue: Color): void {
setColor("#" + newValue.hex.toLowerCase());
}
function ColorEditor({ name, onColorChange, color, defaultColor }: IColorEditorProps): React.ReactElement {
if (color === undefined) throw new Error("should not happen");
return (
<>
@ -46,20 +30,20 @@ function ColorEditor({ name }: { name: string }): React.ReactElement {
sx={{ mx: 1 }}
label={name}
value={color}
onChange={onChange}
variant="standard"
InputProps={{
startAdornment: (
<>
<ColorPicker hideTextfield value={color} onChange={onColorPickerChange} />
<ColorPicker
hideTextfield
value={color}
onChange={(newColor: Color) => onColorChange(name, "#" + newColor.hex)}
/>
</>
),
endAdornment: (
<>
<IconButton onClick={set} disabled={!valid}>
<DoneIcon color={valid ? "primary" : "error"} />
</IconButton>
<IconButton onClick={revert}>
<IconButton onClick={() => onColorChange(name, defaultColor)}>
<ReplyIcon color="primary" />
</IconButton>
</>
@ -71,6 +55,34 @@ function ColorEditor({ name }: { name: string }): React.ReactElement {
}
export function ThemeEditorModal(props: IProps): React.ReactElement {
const [customTheme, setCustomTheme] = useState<{ [key: string]: string | undefined }>({
...Settings.theme,
});
function onThemeChange(event: React.ChangeEvent<HTMLInputElement>): void {
try {
const importedTheme = JSON.parse(event.target.value);
if (typeof importedTheme !== "object") return;
setCustomTheme(importedTheme);
for (const key of Object.keys(importedTheme)) {
Settings.theme[key] = importedTheme[key];
}
ThemeEvents.emit();
} catch (err) {
// ignore
}
}
function onColorChange(name: string, value: string): void {
setCustomTheme((old: any) => {
old[name] = value;
return old;
});
Settings.theme[name] = value;
ThemeEvents.emit();
}
return (
<Modal open={props.open} onClose={props.onClose}>
<Button color="primary">primary</Button>
@ -84,44 +96,177 @@ export function ThemeEditorModal(props: IProps): React.ReactElement {
<Typography color="info">info</Typography>
<Typography color="error">error</Typography>
<br />
<ColorEditor name="primarylight" />
<ColorEditor name="primary" />
<ColorEditor name="primarydark" />
<ColorEditor
name="primarylight"
onColorChange={onColorChange}
color={customTheme["primarylight"]}
defaultColor={defaultSettings.theme["primarylight"]}
/>
<ColorEditor
name="primary"
onColorChange={onColorChange}
color={customTheme["primary"]}
defaultColor={defaultSettings.theme["primary"]}
/>
<ColorEditor
name="primarydark"
onColorChange={onColorChange}
color={customTheme["primarydark"]}
defaultColor={defaultSettings.theme["primarydark"]}
/>
<br />
<ColorEditor name="errorlight" />
<ColorEditor name="error" />
<ColorEditor name="errordark" />
<ColorEditor
name="errorlight"
onColorChange={onColorChange}
color={customTheme["errorlight"]}
defaultColor={defaultSettings.theme["errorlight"]}
/>
<ColorEditor
name="error"
onColorChange={onColorChange}
color={customTheme["error"]}
defaultColor={defaultSettings.theme["error"]}
/>
<ColorEditor
name="errordark"
onColorChange={onColorChange}
color={customTheme["errordark"]}
defaultColor={defaultSettings.theme["errordark"]}
/>
<br />
<ColorEditor name="secondarylight" />
<ColorEditor name="secondary" />
<ColorEditor name="secondarydark" />
<ColorEditor
name="secondarylight"
onColorChange={onColorChange}
color={customTheme["secondarylight"]}
defaultColor={defaultSettings.theme["secondarylight"]}
/>
<ColorEditor
name="secondary"
onColorChange={onColorChange}
color={customTheme["secondary"]}
defaultColor={defaultSettings.theme["secondary"]}
/>
<ColorEditor
name="secondarydark"
onColorChange={onColorChange}
color={customTheme["secondarydark"]}
defaultColor={defaultSettings.theme["secondarydark"]}
/>
<br />
<ColorEditor name="warninglight" />
<ColorEditor name="warning" />
<ColorEditor name="warningdark" />
<ColorEditor
name="warninglight"
onColorChange={onColorChange}
color={customTheme["warninglight"]}
defaultColor={defaultSettings.theme["warninglight"]}
/>
<ColorEditor
name="warning"
onColorChange={onColorChange}
color={customTheme["warning"]}
defaultColor={defaultSettings.theme["warning"]}
/>
<ColorEditor
name="warningdark"
onColorChange={onColorChange}
color={customTheme["warningdark"]}
defaultColor={defaultSettings.theme["warningdark"]}
/>
<br />
<ColorEditor name="infolight" />
<ColorEditor name="info" />
<ColorEditor name="infodark" />
<ColorEditor
name="infolight"
onColorChange={onColorChange}
color={customTheme["infolight"]}
defaultColor={defaultSettings.theme["infolight"]}
/>
<ColorEditor
name="info"
onColorChange={onColorChange}
color={customTheme["info"]}
defaultColor={defaultSettings.theme["info"]}
/>
<ColorEditor
name="infodark"
onColorChange={onColorChange}
color={customTheme["infodark"]}
defaultColor={defaultSettings.theme["infodark"]}
/>
<br />
<ColorEditor name="welllight" />
<ColorEditor name="well" />
<ColorEditor name="white" />
<ColorEditor name="black" />
<ColorEditor
name="welllight"
onColorChange={onColorChange}
color={customTheme["welllight"]}
defaultColor={defaultSettings.theme["welllight"]}
/>
<ColorEditor
name="well"
onColorChange={onColorChange}
color={customTheme["well"]}
defaultColor={defaultSettings.theme["well"]}
/>
<ColorEditor
name="white"
onColorChange={onColorChange}
color={customTheme["white"]}
defaultColor={defaultSettings.theme["white"]}
/>
<ColorEditor
name="black"
onColorChange={onColorChange}
color={customTheme["black"]}
defaultColor={defaultSettings.theme["black"]}
/>
<br />
<ColorEditor name="hp" />
<ColorEditor name="money" />
<ColorEditor name="hack" />
<ColorEditor name="combat" />
<ColorEditor name="cha" />
<ColorEditor name="int" />
<ColorEditor name="rep" />
<ColorEditor
name="hp"
onColorChange={onColorChange}
color={customTheme["hp"]}
defaultColor={defaultSettings.theme["hp"]}
/>
<ColorEditor
name="money"
onColorChange={onColorChange}
color={customTheme["money"]}
defaultColor={defaultSettings.theme["money"]}
/>
<ColorEditor
name="hack"
onColorChange={onColorChange}
color={customTheme["hack"]}
defaultColor={defaultSettings.theme["hack"]}
/>
<ColorEditor
name="combat"
onColorChange={onColorChange}
color={customTheme["combat"]}
defaultColor={defaultSettings.theme["combat"]}
/>
<ColorEditor
name="cha"
onColorChange={onColorChange}
color={customTheme["cha"]}
defaultColor={defaultSettings.theme["cha"]}
/>
<ColorEditor
name="int"
onColorChange={onColorChange}
color={customTheme["int"]}
defaultColor={defaultSettings.theme["int"]}
/>
<ColorEditor
name="rep"
onColorChange={onColorChange}
color={customTheme["rep"]}
defaultColor={defaultSettings.theme["rep"]}
/>
<br />
<br />
<TextField label={"import / export theme"} value={JSON.stringify(customTheme)} onChange={onThemeChange} />
</Modal>
);
}

@ -1,5 +1,7 @@
import React from "react";
import { CityName } from "../../Locations/data/CityNames";
import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip";
interface ICityProps {
currentCity: CityName;
@ -10,19 +12,14 @@ interface ICityProps {
function City(props: ICityProps): React.ReactElement {
if (props.city !== props.currentCity) {
return (
<Tooltip title={props.city}>
<span
className="tooltip"
style={{
color: "white",
whiteSpace: "nowrap",
margin: "0px",
padding: "0px",
}}
onClick={() => props.onTravel(props.city)}
style={{ color: "white", lineHeight: "1em", whiteSpace: "pre" }}
>
<span className="tooltiptext">{props.city}</span>
<b>{props.city[0]}</b>
{props.city[0]}
</span>
</Tooltip>
);
}
return <span>{props.city[0]}</span>;
@ -37,28 +34,28 @@ export function WorldMap(props: IProps): React.ReactElement {
// prettier-ignore
return (
<div className="noselect">
<pre> ,_ . ._. _. .</pre>
<pre> , _-\','|~\~ ~/ ;-'_ _-' ,;_;_, ~~-</pre>
<pre> /~~-\_/-'~'--' \~~| ', ,' / / ~|-_\_/~/~ ~~--~~~~'--_</pre>
<pre> / ,/'-/~ '\ ,' _ , '<City onTravel={props.onTravel} currentCity={props.currentCity} city={CityName.Volhaven} />,'|~ ._/-, /~</pre>
<pre> ~/-'~\_, '-,| '|. ' ~ ,\ /'~ / /_ /~</pre>
<pre>.-~ '| '',\~|\ _\~ ,_ , <City onTravel={props.onTravel} currentCity={props.currentCity} city={CityName.Chongqing} /> /,</pre>
<pre> '\ <City onTravel={props.onTravel} currentCity={props.currentCity} city={CityName.Sector12} /> /'~ |_/~\\,-,~ \ " ,_,/ |</pre>
<pre> | / ._-~'\_ _~| \ ) <City onTravel={props.onTravel} currentCity={props.currentCity} city={CityName.NewTokyo} /></pre>
<pre> \ __-\ '/ ~ |\ \_ / ~</pre>
<pre> ., '\ |, ~-_ - | \\_' ~| /\ \~ ,</pre>
<pre> ~-_' _; '\ '-, \,' /\/ |</pre>
<pre> '\_,~'\_ \_ _, /' ' |, /|'</pre>
<pre> / \_ ~ | / \ ~'; -,_.</pre>
<pre> | ~\ | | , '-_, ,; ~ ~\</pre>
<pre> \, <City onTravel={props.onTravel} currentCity={props.currentCity} city={CityName.Aevum} /> / \ / /| ,-, , -,</pre>
<pre> | ,/ | |' |/ ,- ~ \ '.</pre>
<pre> ,| ,/ \ ,/ \ <City onTravel={props.onTravel} currentCity={props.currentCity} city={CityName.Ishima} /> |</pre>
<pre> / | ~ -~~-, / _</pre>
<pre> | ,-' ~ /</pre>
<pre> / ,' ~</pre>
<pre> ',| ~</pre>
<pre> ~'</pre>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> ,_ . ._. _. .</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> , _-\','|~\~ ~/ ;-'_ _-' ,;_;_, ~~-</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> /~~-\_/-'~'--' \~~| ', ,' / / ~|-_\_/~/~ ~~--~~~~'--_</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> / ,/'-/~ '\ ,' _ , '<City onTravel={props.onTravel} currentCity={props.currentCity} city={CityName.Volhaven} />,'|~ ._/-, /~</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> ~/-'~\_, '-,| '|. ' ~ ,\ /'~ / /_ /~</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>.-~ '| '',\~|\ _\~ ,_ , <City onTravel={props.onTravel} currentCity={props.currentCity} city={CityName.Chongqing} /> /,</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> '\ <City onTravel={props.onTravel} currentCity={props.currentCity} city={CityName.Sector12} /> /'~ |_/~\\,-,~ \ " ,_,/ |</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | / ._-~'\_ _~| \ ) <City onTravel={props.onTravel} currentCity={props.currentCity} city={CityName.NewTokyo} /></Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ __-\ '/ ~ |\ \_ / ~</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> ., '\ |, ~-_ - | \\_' ~| /\ \~ ,</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> ~-_' _; '\ '-, \,' /\/ |</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> '\_,~'\_ \_ _, /' ' |, /|'</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> / \_ ~ | / \ ~'; -,_.</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | ~\ | | , '-_, ,; ~ ~\</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \, <City onTravel={props.onTravel} currentCity={props.currentCity} city={CityName.Aevum} /> / \ / /| ,-, , -,</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | ,/ | |' |/ ,- ~ \ '.</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> ,| ,/ \ ,/ \ <City onTravel={props.onTravel} currentCity={props.currentCity} city={CityName.Ishima} /> |</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> / | ~ -~~-, / _</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | ,-' ~ /</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> / ,' ~</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> ',| ~</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> ~'</Typography>
</div>
);
}

@ -97,10 +97,6 @@ export function WorkInProgressRoot(): React.ReactElement {
player.finishClass(true);
router.toCity();
}
function unfocus(): void {
player.stopFocusing();
router.toCity();
}
let stopText = "";
if (

@ -1,10 +0,0 @@
interface IReviverValue {
ctor: string;
data: any;
}
export function Generic_fromJSON<T>(ctor: new () => T, data: any): T;
export function Generic_toJSON(ctorName: string, obj: any, keys?: string[]): string;
export function Reviver(key, value: IReviverValue);
export namespace Reviver {
export let constructors: any;
}

@ -1,12 +1,16 @@
/* Generic Reviver, toJSON, and fromJSON functions used for saving and loading objects */
export interface IReviverValue {
ctor: string;
data: any;
}
// A generic "smart reviver" function.
// Looks for object values with a `ctor` property and
// a `data` property. If it finds them, and finds a matching
// constructor that has a `fromJSON` property on it, it hands
// off to that `fromJSON` fuunction, passing in the value.
function Reviver(key, value) {
var ctor;
export function Reviver(key: string, value: IReviverValue | null): any {
if (value == null) {
console.log("Reviver WRONGLY called with key: " + key + ", and value: " + value);
return 0;
@ -20,7 +24,7 @@ function Reviver(key, value) {
return value.data;
}
ctor = Reviver.constructors[value.ctor] || window[value.ctor];
const ctor = Reviver.constructors[value.ctor];
if (typeof ctor === "function" && typeof ctor.fromJSON === "function") {
return ctor.fromJSON(value);
@ -28,7 +32,11 @@ function Reviver(key, value) {
}
return value;
}
Reviver.constructors = {}; // A list of constructors the smart reviver should know about
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Reviver {
export const constructors: { [key: string]: any } = {};
}
// A generic "toJSON" function that creates the data expected
// by Reviver.
@ -41,16 +49,15 @@ Reviver.constructors = {}; // A list of constructors the smart reviver should kn
// regardless of whether it's an "own" property.)
// Returns: The structure (which will then be turned into a string
// as part of the JSON.stringify algorithm)
function Generic_toJSON(ctorName, obj, keys) {
var data, key;
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function Generic_toJSON(ctorName: string, obj: any, keys?: string[]): IReviverValue {
if (!keys) {
keys = Object.keys(obj); // Only "own" properties are included
}
data = {};
const data: any = {};
for (let index = 0; index < keys.length; ++index) {
key = keys[index];
const key = keys[index];
data[key] = obj[key];
}
return { ctor: ctorName, data: data };
@ -63,14 +70,11 @@ function Generic_toJSON(ctorName, obj, keys) {
// `ctor` The constructor to call
// `data` The data to apply
// Returns: The object
function Generic_fromJSON(ctor, data) {
var obj, name;
obj = new ctor();
for (name in data) {
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function Generic_fromJSON<T>(ctor: new () => T, data: any): T {
const obj: any = new ctor();
for (const name in data) {
obj[name] = data[name];
}
return obj;
}
export { Reviver, Generic_toJSON, Generic_fromJSON };