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

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

@ -100,6 +100,7 @@
flex: row nowrap; flex: row nowrap;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
cursor: grab;
} }
.log-box-log-container { .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) { win.webContents.on("new-window", function (e, url) {
// make sure local urls stay in electron perimeter // make sure local urls stay in electron perimeter
if ("file://" === url.substr(0, "file://".length)) { if (url.substr(0, "file://".length) === "file://") {
return; 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; display: flex;
flex: row nowrap; flex: row nowrap;
align-items: center; align-items: center;
justify-content: space-between; } justify-content: space-between;
cursor: grab; }
.log-box-log-container { .log-box-log-container {
overflow-y: auto; } overflow-y: auto; }

File diff suppressed because one or more lines are too long

@ -2,366 +2,433 @@ const numSpaces = 4;
const maxLineLength = 160; const maxLineLength = 160;
module.exports = { module.exports = {
env: { "env": {
es6: true, "es6": true,
node: true, "node": true
},
extends: "eslint:recommended",
parserOptions: {
ecmaFeatures: {
experimentalObjectRestSpread: true,
}, },
ecmaVersion: 8, "extends": "eslint:recommended",
sourceType: "module", "parserOptions": {
}, "ecmaFeatures": {
rules: { "experimentalObjectRestSpread": true
"accessor-pairs": [ },
"error", "ecmaVersion": 8,
{ "sourceType": "module"
getWithoutSet: false, },
setWithoutGet: true, "rules": {
}, "accessor-pairs": [
], "error",
"array-bracket-newline": ["error"], {
"array-bracket-spacing": ["error"], "getWithoutSet": false,
"array-callback-return": ["error"], "setWithoutGet": true
"array-element-newline": ["error"], }
"arrow-body-style": ["error"], ],
"arrow-parens": ["error"], "array-bracket-newline": ["error"],
"arrow-spacing": ["error"], "array-bracket-spacing": ["error"],
"block-scoped-var": ["error"], "array-callback-return": ["error"],
"block-spacing": ["error"], "array-element-newline": ["error"],
"brace-style": ["error"], "arrow-body-style": ["error"],
"callback-return": ["error"], "arrow-parens": ["error"],
camelcase: ["error"], "arrow-spacing": ["error"],
"capitalized-comments": ["error"], "block-scoped-var": ["error"],
"class-methods-use-this": ["error"], "block-spacing": ["error"],
"comma-dangle": ["error"], "brace-style": ["error"],
"comma-spacing": ["error"], "callback-return": ["error"],
"comma-style": ["error", "last"], "camelcase": ["error"],
complexity: ["error"], "capitalized-comments": ["error"],
"computed-property-spacing": ["error", "never"], "class-methods-use-this": ["error"],
"consistent-return": ["error"], "comma-dangle": ["error"],
"consistent-this": ["error"], "comma-spacing": ["error"],
"constructor-super": ["error"], "comma-style": [
curly: ["error"], "error",
"default-case": ["error"], "last"
"dot-location": ["error", "property"], ],
"dot-notation": ["error"], "complexity": ["error"],
"eol-last": ["error"], "computed-property-spacing": [
eqeqeq: ["error"], "error",
"for-direction": ["error"], "never"
"func-call-spacing": ["error"], ],
"func-name-matching": ["error"], "consistent-return": ["error"],
"func-names": ["error", "never"], "consistent-this": ["error"],
"func-style": ["error"], "constructor-super": ["error"],
"function-paren-newline": ["error"], "curly": ["error"],
"generator-star-spacing": ["error", "before"], "default-case": ["error"],
"getter-return": [ "dot-location": [
"error", "error",
{ "property"
allowImplicit: false, ],
}, "dot-notation": ["error"],
], "eol-last": ["error"],
"global-require": ["error"], "eqeqeq": ["error"],
"guard-for-in": ["error"], "for-direction": ["error"],
"handle-callback-err": ["error"], "func-call-spacing": ["error"],
"id-blacklist": ["error"], "func-name-matching": ["error"],
"id-length": ["error"], "func-names": [
"id-match": ["error"], "error",
"implicit-arrow-linebreak": ["error", "beside"], "never"
indent: [ ],
"error", "func-style": ["error"],
numSpaces, "function-paren-newline": ["error"],
{ "generator-star-spacing": [
SwitchCase: 1, "error",
}, "before"
], ],
"init-declarations": ["error"], "getter-return": [
"jsx-quotes": ["error"], "error",
"key-spacing": ["error"], {
"keyword-spacing": ["error"], "allowImplicit": false
"line-comment-position": ["error"], }
"linebreak-style": ["error", "windows"], ],
"lines-around-comment": ["error"], "global-require": ["error"],
"lines-between-class-members": ["error"], "guard-for-in": ["error"],
"max-depth": ["error"], "handle-callback-err": ["error"],
"max-len": ["error", maxLineLength], "id-blacklist": ["error"],
"max-lines": [ "id-length": ["error"],
"error", "id-match": ["error"],
{ "implicit-arrow-linebreak": [
skipBlankLines: true, "error",
skipComments: true, "beside"
}, ],
], "indent": [
"max-nested-callbacks": ["error"], "error",
"max-params": ["error"], numSpaces,
"max-statements": ["error"], {
"max-statements-per-line": ["error"], "SwitchCase": 1
"multiline-comment-style": ["off", "starred-block"], }
"multiline-ternary": ["error", "never"], ],
"new-cap": ["error"], "init-declarations": ["error"],
"new-parens": ["error"], "jsx-quotes": ["error"],
// TODO: configure this... "key-spacing": ["error"],
"newline-before-return": ["error"], "keyword-spacing": ["error"],
"newline-per-chained-call": ["error"], "line-comment-position": ["error"],
"no-alert": ["error"], "linebreak-style": [
"no-array-constructor": ["error"], "error",
"no-await-in-loop": ["error"], "windows"
"no-bitwise": ["error"], ],
"no-buffer-constructor": ["error"], "lines-around-comment": ["error"],
"no-caller": ["error"], "lines-between-class-members": ["error"],
"no-case-declarations": ["error"], "max-depth": ["error"],
"no-catch-shadow": ["error"], "max-len": [
"no-class-assign": ["error"], "error",
"no-compare-neg-zero": ["error"], maxLineLength
"no-cond-assign": ["error", "except-parens"], ],
"no-confusing-arrow": ["error"], "max-lines": [
"no-console": ["error"], "error",
"no-const-assign": ["error"], {
"no-constant-condition": [ "skipBlankLines": true,
"error", "skipComments": true
{ }
checkLoops: false, ],
}, "max-nested-callbacks": ["error"],
], "max-params": ["error"],
"no-continue": ["off"], "max-statements": ["error"],
"no-control-regex": ["error"], "max-statements-per-line": ["error"],
"no-debugger": ["error"], "multiline-comment-style": [
"no-delete-var": ["error"], "off",
"no-div-regex": ["error"], "starred-block"
"no-dupe-args": ["error"], ],
"no-dupe-class-members": ["error"], "multiline-ternary": [
"no-dupe-keys": ["error"], "error",
"no-duplicate-case": ["error"], "never"
"no-duplicate-imports": [ ],
"error", "new-cap": ["error"],
{ "new-parens": ["error"],
includeExports: true, // TODO: configure this...
}, "newline-before-return": ["error"],
], "newline-per-chained-call": ["error"],
"no-else-return": ["error"], "no-alert": ["error"],
"no-empty": [ "no-array-constructor": ["error"],
"error", "no-await-in-loop": ["error"],
{ "no-bitwise": ["error"],
allowEmptyCatch: false, "no-buffer-constructor": ["error"],
}, "no-caller": ["error"],
], "no-case-declarations": ["error"],
"no-empty-character-class": ["error"], "no-catch-shadow": ["error"],
"no-empty-function": ["error"], "no-class-assign": ["error"],
"no-empty-pattern": ["error"], "no-compare-neg-zero": ["error"],
"no-eq-null": ["error"], "no-cond-assign": [
"no-eval": ["error"], "error",
"no-ex-assign": ["error"], "except-parens"
"no-extend-native": ["error"], ],
"no-extra-bind": ["error"], "no-confusing-arrow": ["error"],
"no-extra-boolean-cast": ["error"], "no-console": ["error"],
"no-extra-label": ["error"], "no-const-assign": ["error"],
"no-extra-parens": [ "no-constant-condition": [
"error", "error",
"all", {
{ "checkLoops": false
conditionalAssign: false, }
}, ],
], "no-continue": ["off"],
"no-extra-semi": ["error"], "no-control-regex": ["error"],
"no-fallthrough": ["error"], "no-debugger": ["error"],
"no-floating-decimal": ["error"], "no-delete-var": ["error"],
"no-func-assign": ["error"], "no-div-regex": ["error"],
"no-global-assign": ["error"], "no-dupe-args": ["error"],
"no-implicit-coercion": ["error"], "no-dupe-class-members": ["error"],
"no-implicit-globals": ["error"], "no-dupe-keys": ["error"],
"no-implied-eval": ["error"], "no-duplicate-case": ["error"],
"no-inline-comments": ["error"], "no-duplicate-imports": [
"no-inner-declarations": ["error", "both"], "error",
"no-invalid-regexp": ["error"], {
"no-invalid-this": ["error"], "includeExports": true
"no-irregular-whitespace": [ }
"error", ],
{ "no-else-return": ["error"],
skipComments: false, "no-empty": [
skipRegExps: false, "error",
skipStrings: false, {
skipTemplates: false, "allowEmptyCatch": false
}, }
], ],
"no-iterator": ["error"], "no-empty-character-class": ["error"],
"no-label-var": ["error"], "no-empty-function": ["error"],
"no-labels": ["error"], "no-empty-pattern": ["error"],
"no-lone-blocks": ["error"], "no-eq-null": ["error"],
"no-lonely-if": ["error"], "no-eval": ["error"],
"no-loop-func": ["error"], "no-ex-assign": ["error"],
"no-magic-numbers": [ "no-extend-native": ["error"],
"error", "no-extra-bind": ["error"],
{ "no-extra-boolean-cast": ["error"],
ignore: [-1, 0, 1], "no-extra-label": ["error"],
ignoreArrayIndexes: true, "no-extra-parens": [
}, "error",
], "all",
"no-mixed-operators": ["error"], {
"no-mixed-requires": ["error"], "conditionalAssign": false
"no-mixed-spaces-and-tabs": ["error"], }
"no-multi-assign": ["error"], ],
"no-multi-spaces": ["error"], "no-extra-semi": ["error"],
"no-multi-str": ["error"], "no-fallthrough": ["error"],
"no-multiple-empty-lines": [ "no-floating-decimal": ["error"],
"error", "no-func-assign": ["error"],
{ "no-global-assign": ["error"],
max: 1, "no-implicit-coercion": ["error"],
}, "no-implicit-globals": ["error"],
], "no-implied-eval": ["error"],
"no-native-reassign": ["error"], "no-inline-comments": ["error"],
"no-negated-condition": ["error"], "no-inner-declarations": [
"no-negated-in-lhs": ["error"], "error",
"no-nested-ternary": ["error"], "both"
"no-new": ["error"], ],
"no-new-func": ["error"], "no-invalid-regexp": ["error"],
"no-new-object": ["error"], "no-invalid-this": ["error"],
"no-new-require": ["error"], "no-irregular-whitespace": [
"no-new-symbol": ["error"], "error",
"no-new-wrappers": ["error"], {
"no-obj-calls": ["error"], "skipComments": false,
"no-octal": ["error"], "skipRegExps": false,
"no-octal-escape": ["error"], "skipStrings": false,
"no-param-reassign": ["error"], "skipTemplates": false
"no-path-concat": ["error"], }
"no-plusplus": [ ],
"error", "no-iterator": ["error"],
{ "no-label-var": ["error"],
allowForLoopAfterthoughts: true, "no-labels": ["error"],
}, "no-lone-blocks": ["error"],
], "no-lonely-if": ["error"],
"no-process-env": ["error"], "no-loop-func": ["error"],
"no-process-exit": ["error"], "no-magic-numbers": [
"no-proto": ["error"], "error",
"no-prototype-builtins": ["error"], {
"no-redeclare": ["error"], "ignore": [
"no-regex-spaces": ["error"], -1,
"no-restricted-globals": ["error"], 0,
"no-restricted-imports": ["error"], 1
"no-restricted-modules": ["error"], ],
"no-restricted-properties": [ "ignoreArrayIndexes": true
"error", }
{ ],
message: "'log' is too general, use an appropriate level when logging.", "no-mixed-operators": ["error"],
object: "console", "no-mixed-requires": ["error"],
property: "log", "no-mixed-spaces-and-tabs": ["error"],
}, "no-multi-assign": ["error"],
], "no-multi-spaces": ["error"],
"no-restricted-syntax": ["error"], "no-multi-str": ["error"],
"no-return-assign": ["error"], "no-multiple-empty-lines": [
"no-return-await": ["error"], "error",
"no-script-url": ["error"], {
"no-self-assign": [ "max": 1
"error", }
{ ],
props: false, "no-native-reassign": ["error"],
}, "no-negated-condition": ["error"],
], "no-negated-in-lhs": ["error"],
"no-self-compare": ["error"], "no-nested-ternary": ["error"],
"no-sequences": ["error"], "no-new": ["error"],
"no-shadow": ["error"], "no-new-func": ["error"],
"no-shadow-restricted-names": ["error"], "no-new-object": ["error"],
"no-spaced-func": ["error"], "no-new-require": ["error"],
"no-sparse-arrays": ["error"], "no-new-symbol": ["error"],
"no-sync": ["error"], "no-new-wrappers": ["error"],
"no-tabs": ["error"], "no-obj-calls": ["error"],
"no-template-curly-in-string": ["error"], "no-octal": ["error"],
"no-ternary": ["off"], "no-octal-escape": ["error"],
"no-this-before-super": ["error"], "no-param-reassign": ["error"],
"no-throw-literal": ["error"], "no-path-concat": ["error"],
"no-trailing-spaces": ["error"], "no-plusplus": [
"no-undef": ["error"], "error",
"no-undef-init": ["error"], {
"no-undefined": ["error"], "allowForLoopAfterthoughts": true
"no-underscore-dangle": ["error"], }
"no-unexpected-multiline": ["error"], ],
"no-unmodified-loop-condition": ["error"], "no-process-env": ["error"],
"no-unneeded-ternary": ["error"], "no-process-exit": ["error"],
"no-unreachable": ["error"], "no-proto": ["error"],
"no-unsafe-finally": ["error"], "no-prototype-builtins": ["error"],
"no-unsafe-negation": ["error"], "no-redeclare": ["error"],
"no-unused-expressions": ["error"], "no-regex-spaces": ["error"],
"no-unused-labels": ["error"], "no-restricted-globals": ["error"],
"no-unused-vars": ["error"], "no-restricted-imports": ["error"],
"no-use-before-define": ["error"], "no-restricted-modules": ["error"],
"no-useless-call": ["error"], "no-restricted-properties": [
"no-useless-computed-key": ["error"], "error",
"no-useless-concat": ["error"], {
"no-useless-constructor": ["error"], "message": "'log' is too general, use an appropriate level when logging.",
"no-useless-escape": ["error"], "object": "console",
"no-useless-rename": [ "property": "log"
"error", }
{ ],
ignoreDestructuring: false, "no-restricted-syntax": ["error"],
ignoreExport: false, "no-return-assign": ["error"],
ignoreImport: false, "no-return-await": ["error"],
}, "no-script-url": ["error"],
], "no-self-assign": [
"no-useless-return": ["error"], "error",
"no-var": ["error"], {
"no-void": ["error"], "props": false
"no-warning-comments": ["error"], }
"no-whitespace-before-property": ["error"], ],
"no-with": ["error"], "no-self-compare": ["error"],
"nonblock-statement-body-position": ["error", "below"], "no-sequences": ["error"],
"object-curly-newline": ["error"], "no-shadow": ["error"],
"object-curly-spacing": ["error"], "no-shadow-restricted-names": ["error"],
"object-property-newline": ["error"], "no-spaced-func": ["error"],
"object-shorthand": ["error"], "no-sparse-arrays": ["error"],
"one-var": ["off"], "no-sync": ["error"],
"one-var-declaration-per-line": ["error"], "no-tabs": ["error"],
"operator-assignment": ["error"], "no-template-curly-in-string": ["error"],
"operator-linebreak": ["error", "none"], "no-ternary": ["off"],
"padded-blocks": ["off"], "no-this-before-super": ["error"],
"padding-line-between-statements": ["error"], "no-throw-literal": ["error"],
"prefer-arrow-callback": ["error"], "no-trailing-spaces": ["error"],
"prefer-const": ["error"], "no-undef": ["error"],
"prefer-destructuring": ["off"], "no-undef-init": ["error"],
"prefer-numeric-literals": ["error"], "no-undefined": ["error"],
"prefer-promise-reject-errors": ["off"], "no-underscore-dangle": ["error"],
"prefer-reflect": ["error"], "no-unexpected-multiline": ["error"],
"prefer-rest-params": ["error"], "no-unmodified-loop-condition": ["error"],
"prefer-spread": ["error"], "no-unneeded-ternary": ["error"],
"prefer-template": ["error"], "no-unreachable": ["error"],
"quote-props": ["error"], "no-unsafe-finally": ["error"],
quotes: ["error"], "no-unsafe-negation": ["error"],
radix: ["error", "as-needed"], "no-unused-expressions": ["error"],
"require-await": ["error"], "no-unused-labels": ["error"],
"require-jsdoc": ["off"], "no-unused-vars": ["error"],
"require-yield": ["error"], "no-use-before-define": ["error"],
"rest-spread-spacing": ["error", "never"], "no-useless-call": ["error"],
semi: ["error"], "no-useless-computed-key": ["error"],
"semi-spacing": ["error"], "no-useless-concat": ["error"],
"semi-style": ["error", "last"], "no-useless-constructor": ["error"],
"sort-imports": ["error"], "no-useless-escape": ["error"],
"sort-keys": ["error"], "no-useless-rename": [
"sort-vars": ["error"], "error",
"space-before-blocks": ["error"], {
"space-before-function-paren": ["off"], "ignoreDestructuring": false,
"space-in-parens": ["error"], "ignoreExport": false,
"space-infix-ops": ["error"], "ignoreImport": false
"space-unary-ops": ["error"], }
"spaced-comment": ["error"], ],
strict: ["error"], "no-useless-return": ["error"],
"switch-colon-spacing": [ "no-var": ["error"],
"error", "no-void": ["error"],
{ "no-warning-comments": ["error"],
after: true, "no-whitespace-before-property": ["error"],
before: false, "no-with": ["error"],
}, "nonblock-statement-body-position": [
], "error",
"symbol-description": ["error"], "below"
"template-curly-spacing": ["error"], ],
"template-tag-spacing": ["error"], "object-curly-newline": ["error"],
"unicode-bom": ["error", "never"], "object-curly-spacing": ["error"],
"use-isnan": ["error"], "object-property-newline": ["error"],
"valid-jsdoc": ["error"], "object-shorthand": ["error"],
"valid-typeof": ["error"], "one-var": ["off"],
"vars-on-top": ["error"], "one-var-declaration-per-line": ["error"],
"wrap-iife": ["error", "any"], "operator-assignment": ["error"],
"wrap-regex": ["error"], "operator-linebreak": [
"yield-star-spacing": ["error", "before"], "error",
yoda: ["error", "never"], "none"
}, ],
"padded-blocks": ["off"],
"padding-line-between-statements": ["error"],
"prefer-arrow-callback": ["error"],
"prefer-const": ["error"],
"prefer-destructuring": ["off"],
"prefer-numeric-literals": ["error"],
"prefer-promise-reject-errors": ["off"],
"prefer-reflect": ["error"],
"prefer-rest-params": ["error"],
"prefer-spread": ["error"],
"prefer-template": ["error"],
"quote-props": ["error"],
"quotes": ["error"],
"radix": [
"error",
"as-needed"
],
"require-await": ["error"],
"require-jsdoc": ["off"],
"require-yield": ["error"],
"rest-spread-spacing": [
"error",
"never"
],
"semi": ["error"],
"semi-spacing": ["error"],
"semi-style": [
"error",
"last"
],
"sort-imports": ["error"],
"sort-keys": ["error"],
"sort-vars": ["error"],
"space-before-blocks": ["error"],
"space-before-function-paren": ["off"],
"space-in-parens": ["error"],
"space-infix-ops": ["error"],
"space-unary-ops": ["error"],
"spaced-comment": ["error"],
"strict": ["error"],
"switch-colon-spacing": [
"error",
{
"after": true,
"before": false
}
],
"symbol-description": ["error"],
"template-curly-spacing": ["error"],
"template-tag-spacing": ["error"],
"unicode-bom": [
"error",
"never"
],
"use-isnan": ["error"],
"valid-jsdoc": ["error"],
"valid-typeof": ["error"],
"vars-on-top": ["error"],
"wrap-iife": [
"error",
"any"
],
"wrap-regex": ["error"],
"yield-star-spacing": [
"error",
"before"
],
"yoda": [
"error",
"never"
]
}
}; };

@ -8,74 +8,66 @@ const path = require("path");
const exec = require("child_process").exec; const exec = require("child_process").exec;
const semver = require("./semver"); const semver = require("./semver");
const getPackageJson = () => const getPackageJson = () => new Promise((resolve, reject) => {
new Promise((resolve, reject) => {
try { try {
/* eslint-disable-next-line global-require */ /* eslint-disable-next-line global-require */
resolve(require(path.resolve(process.cwd(), "package.json"))); resolve(require(path.resolve(process.cwd(), "package.json")));
} catch (error) { } catch (error) {
reject(error); reject(error);
} }
}); });
const getEngines = (data) => const getEngines = (data) => new Promise((resolve, reject) => {
new Promise((resolve, reject) => {
let versions = null; let versions = null;
if (data.engines) { if (data.engines) {
versions = data.engines; versions = data.engines;
} }
if (versions) { if (versions) {
resolve(versions); resolve(versions);
} else { } else {
reject("Missing or improper 'engines' property in 'package.json'"); reject("Missing or improper 'engines' property in 'package.json'");
} }
}); });
const checkNpmVersion = (engines) => const checkNpmVersion = (engines) => new Promise((resolve, reject) => {
new Promise((resolve, reject) => {
exec("npm -v", (error, stdout, stderr) => { exec("npm -v", (error, stdout, stderr) => {
if (error) { if (error) {
reject(`Unable to find NPM version\n${stderr}`); reject(`Unable to find NPM version\n${stderr}`);
} }
const npmVersion = stdout.trim(); const npmVersion = stdout.trim();
const engineVersion = engines.npm || ">=0"; const engineVersion = engines.npm || ">=0";
if (semver.satisfies(npmVersion, engineVersion)) { if (semver.satisfies(npmVersion, engineVersion)) {
resolve(); resolve();
} else { } else {
reject( reject(`Incorrect npm version\n'package.json' specifies "${engineVersion}", you are currently running "${npmVersion}".`);
`Incorrect npm version\n'package.json' specifies "${engineVersion}", you are currently running "${npmVersion}".`, }
);
}
}); });
}); });
const checkNodeVersion = (engines) => const checkNodeVersion = (engines) => new Promise((resolve, reject) => {
new Promise((resolve, reject) => {
const nodeVersion = process.version.substring(1); const nodeVersion = process.version.substring(1);
if (semver.satisfies(nodeVersion, engines.node)) { if (semver.satisfies(nodeVersion, engines.node)) {
resolve(engines); resolve(engines);
} else { } else {
reject( reject(`Incorrect node version\n'package.json' specifies "${engines.node}", you are currently running "${process.version}".`);
`Incorrect node version\n'package.json' specifies "${engines.node}", you are currently running "${process.version}".`,
);
} }
}); });
getPackageJson() getPackageJson()
.then(getEngines) .then(getEngines)
.then(checkNodeVersion) .then(checkNodeVersion)
.then(checkNpmVersion) .then(checkNpmVersion)
.then( .then(
() => true, () => true,
(error) => { (error) => {
// Specifically disable these as the error message gets lost in the normal unhandled output. // Specifically disable these as the error message gets lost in the normal unhandled output.
/* eslint-disable no-console, no-process-exit */ /* eslint-disable no-console, no-process-exit */
console.error(error); console.error(error);
process.exit(1); process.exit(1);
}, }
); );

File diff suppressed because it is too large Load Diff

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

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

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

@ -1,19 +1,28 @@
/** /**
* React Component for displaying a list of the player's installed Augmentations * React Component for displaying all of the player's installed Augmentations and
* on the Augmentations UI * 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 React, { useState } 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 { AugmentationAccordion } from "../../ui/React/AugmentationAccordion"; 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 { 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) { if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
sourceAugs.sort((aug1, aug2) => { sourceAugs.sort((aug1, aug2) => {
@ -21,20 +30,42 @@ export function InstalledAugmentations(): React.ReactElement {
}); });
} }
const augs = sourceAugs.map((e) => { function rerender(): void {
const aug = Augmentations[e.name]; setRerender((old) => !old);
}
let level = null; function sortByAcquirementTime(): void {
if (e.name === AugmentationNames.NeuroFluxGovernor) { Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.AcquirementTime;
level = e.level; rerender();
} }
return ( function sortInOrder(): void {
<li key={e.name}> Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.Alphabetically;
<AugmentationAccordion aug={aug} level={level} /> rerender();
</li> }
);
});
return <>{augs}</>; 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;
if (e.name === AugmentationNames.NeuroFluxGovernor) {
level = e.level;
}
return <AugmentationAccordion key={aug.name} aug={aug} level={level} />;
})}
</List>
</>
);
} }

@ -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,20 +20,18 @@ export function OwnedSourceFiles(): React.ReactElement {
}); });
} }
const sfs = sourceSfs.map((e) => { return (
const srcFileKey = "SourceFile" + e.n; <>
const sfObj = SourceFiles[srcFileKey]; {sourceSfs.map((e) => {
if (sfObj == null) { const srcFileKey = "SourceFile" + e.n;
console.error(`Invalid source file number: ${e.n}`); const sfObj = SourceFiles[srcFileKey];
return null; if (sfObj == null) {
} console.error(`Invalid source file number: ${e.n}`);
return null;
}
return ( return <SourceFileAccordion key={e.n} level={e.lvl} sf={sfObj} />;
<li key={e.n}> })}
<SourceFileAccordion level={e.lvl} sf={sfObj} /> </>
</li> );
);
});
return <>{sfs}</>;
} }

@ -6,6 +6,11 @@ import * as React from "react";
import { Player } from "../../Player"; import { Player } from "../../Player";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { Augmentations } from "../Augmentations"; import { Augmentations } from "../Augmentations";
import { Table, TableCell } from "../../ui/React/Table";
import TableBody from "@mui/material/TableBody";
import TableRow from "@mui/material/TableRow";
import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";
function calculateAugmentedStats(): any { function calculateAugmentedStats(): any {
const augP: any = {}; const augP: any = {};
@ -19,62 +24,73 @@ function calculateAugmentedStats(): any {
return augP; return augP;
} }
export function PlayerMultipliers(): React.ReactElement { function Improvements({ r }: { r: number }): React.ReactElement {
const mults = calculateAugmentedStats(); if (r) {
function MultiplierTable(rows: any[]): React.ReactElement {
function improvements(r: number): JSX.Element[] {
let elems: JSX.Element[] = [];
if (r) {
elems = [<td key="2">&nbsp;{"=>"}&nbsp;</td>, <td key="3">{numeralWrapper.formatPercentage(r)}</td>];
}
return elems;
}
return ( return (
<table> <>
<tbody> <TableCell key="2">
{rows.map((r: any) => ( <Typography>&nbsp;{"=>"}&nbsp;</Typography>
<tr key={r[0]}> </TableCell>
<td key="0"> <TableCell key="3">
<span>{r[0]} multiplier:&nbsp;</span> <Typography>{numeralWrapper.formatPercentage(r)}</Typography>
</td> </TableCell>
<td key="1" style={{ textAlign: "right" }}> </>
{numeralWrapper.formatPercentage(r[1])}
</td>
{improvements(r[2])}
</tr>
))}
</tbody>
</table>
); );
} }
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 { function BladeburnerMults(): React.ReactElement {
if (!Player.canAccessBladeburner()) return <></>; if (!Player.canAccessBladeburner()) return <></>;
return ( return (
<> <>
{MultiplierTable([ <MultiplierTable
[ rows={[
"Bladeburner Success Chance", [
Player.bladeburner_success_chance_mult, "Bladeburner Success Chance",
Player.bladeburner_success_chance_mult * mults.bladeburner_success_chance_mult, Player.bladeburner_success_chance_mult,
], Player.bladeburner_success_chance_mult * mults.bladeburner_success_chance_mult,
[ ],
"Bladeburner Max Stamina", [
Player.bladeburner_max_stamina_mult, "Bladeburner Max Stamina",
Player.bladeburner_max_stamina_mult * mults.bladeburner_max_stamina_mult, Player.bladeburner_max_stamina_mult,
], Player.bladeburner_max_stamina_mult * mults.bladeburner_max_stamina_mult,
[ ],
"Bladeburner Stamina Gain", [
Player.bladeburner_stamina_gain_mult, "Bladeburner Stamina Gain",
Player.bladeburner_stamina_gain_mult * mults.bladeburner_stamina_gain_mult, Player.bladeburner_stamina_gain_mult,
], Player.bladeburner_stamina_gain_mult * mults.bladeburner_stamina_gain_mult,
[ ],
"Bladeburner Field Analysis", [
Player.bladeburner_analysis_mult, "Bladeburner Field Analysis",
Player.bladeburner_analysis_mult * mults.bladeburner_analysis_mult, Player.bladeburner_analysis_mult,
], Player.bladeburner_analysis_mult * mults.bladeburner_analysis_mult,
])} ],
]}
/>
<br /> <br />
</> </>
); );
@ -82,99 +98,116 @@ export function PlayerMultipliers(): React.ReactElement {
return ( return (
<> <>
<p> <Typography variant="h4">Multipliers</Typography>
<strong> <Box mx={2}>
<u>Multipliers:</u> <MultiplierTable
</strong> rows={[
</p> ["Hacking Chance ", Player.hacking_chance_mult, Player.hacking_chance_mult * mults.hacking_chance_mult],
<br /> ["Hacking Speed ", Player.hacking_speed_mult, Player.hacking_speed_mult * mults.hacking_speed_mult],
{MultiplierTable([ ["Hacking Money ", Player.hacking_money_mult, Player.hacking_money_mult * mults.hacking_money_mult],
["Hacking Chance ", Player.hacking_chance_mult, Player.hacking_chance_mult * mults.hacking_chance_mult], ["Hacking Growth ", Player.hacking_grow_mult, Player.hacking_grow_mult * mults.hacking_grow_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 />
])}
<br />
{MultiplierTable([ <MultiplierTable
["Hacking Level ", Player.hacking_mult, Player.hacking_mult * mults.hacking_mult], rows={[
["Hacking Experience ", Player.hacking_exp_mult, Player.hacking_exp_mult * mults.hacking_exp_mult], ["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 /> ]}
/>
<br />
{MultiplierTable([ <MultiplierTable
["Strength Level ", Player.strength_mult, Player.strength_mult * mults.strength_mult], rows={[
["Strength Experience ", Player.strength_exp_mult, Player.strength_exp_mult * mults.strength_exp_mult], ["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 /> ]}
/>
<br />
{MultiplierTable([ <MultiplierTable
["Defense Level ", Player.defense_mult, Player.defense_mult * mults.defense_mult], rows={[
["Defense Experience ", Player.defense_exp_mult, Player.defense_exp_mult * mults.defense_exp_mult], ["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 /> ]}
/>
<br />
{MultiplierTable([ <MultiplierTable
["Dexterity Level ", Player.dexterity_mult, Player.dexterity_mult * mults.dexterity_mult], rows={[
["Dexterity Experience ", Player.dexterity_exp_mult, Player.dexterity_exp_mult * mults.dexterity_exp_mult], ["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 /> ]}
/>
<br />
{MultiplierTable([ <MultiplierTable
["Agility Level ", Player.agility_mult, Player.agility_mult * mults.agility_mult], rows={[
["Agility Experience ", Player.agility_exp_mult, Player.agility_exp_mult * mults.agility_exp_mult], ["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 /> ]}
/>
<br />
{MultiplierTable([ <MultiplierTable
["Charisma Level ", Player.charisma_mult, Player.charisma_mult * mults.charisma_mult], rows={[
["Charisma Experience ", Player.charisma_exp_mult, Player.charisma_exp_mult * mults.charisma_exp_mult], ["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 /> ]}
/>
<br />
{MultiplierTable([ <MultiplierTable
[ rows={[
"Hacknet Node production ", [
Player.hacknet_node_money_mult, "Hacknet Node production ",
Player.hacknet_node_money_mult * mults.hacknet_node_money_mult, Player.hacknet_node_money_mult,
], Player.hacknet_node_money_mult * mults.hacknet_node_money_mult,
[ ],
"Hacknet Node purchase cost ", [
Player.hacknet_node_purchase_cost_mult, "Hacknet Node purchase cost ",
Player.hacknet_node_purchase_cost_mult * mults.hacknet_node_purchase_cost_mult, Player.hacknet_node_purchase_cost_mult,
], Player.hacknet_node_purchase_cost_mult * mults.hacknet_node_purchase_cost_mult,
[ ],
"Hacknet Node RAM upgrade cost ", [
Player.hacknet_node_ram_cost_mult, "Hacknet Node RAM upgrade cost ",
Player.hacknet_node_ram_cost_mult * mults.hacknet_node_ram_cost_mult, Player.hacknet_node_ram_cost_mult,
], Player.hacknet_node_ram_cost_mult * mults.hacknet_node_ram_cost_mult,
[ ],
"Hacknet Node Core purchase cost ", [
Player.hacknet_node_core_cost_mult, "Hacknet Node Core purchase cost ",
Player.hacknet_node_core_cost_mult * mults.hacknet_node_core_cost_mult, Player.hacknet_node_core_cost_mult,
], Player.hacknet_node_core_cost_mult * mults.hacknet_node_core_cost_mult,
[ ],
"Hacknet Node level upgrade cost ", [
Player.hacknet_node_level_cost_mult, "Hacknet Node level upgrade cost ",
Player.hacknet_node_level_cost_mult * mults.hacknet_node_level_cost_mult, Player.hacknet_node_level_cost_mult,
], Player.hacknet_node_level_cost_mult * mults.hacknet_node_level_cost_mult,
])} ],
<br /> ]}
/>
<br />
{MultiplierTable([ <MultiplierTable
["Company reputation gain ", Player.company_rep_mult, Player.company_rep_mult * mults.company_rep_mult], rows={[
["Faction reputation gain ", Player.faction_rep_mult, Player.faction_rep_mult * mults.faction_rep_mult], ["Company reputation gain ", Player.company_rep_mult, Player.company_rep_mult * mults.company_rep_mult],
["Salary ", Player.work_money_mult, Player.work_money_mult * mults.work_money_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 /> ]}
/>
<br />
{MultiplierTable([ <MultiplierTable
["Crime success ", Player.crime_success_mult, Player.crime_success_mult * mults.crime_success_mult], rows={[
["Crime money ", Player.crime_money_mult, Player.crime_money_mult * mults.crime_money_mult], ["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 /> ]}
/>
<br />
<BladeburnerMults /> <BladeburnerMults />
</Box>
</> </>
); );
} }

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

@ -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 { AllPages } from "./AllPages";
import { use } from "../../ui/Context"; import { use } from "../../ui/Context";
import { IBladeburner } from "../IBladeburner";
export function BladeburnerRoot(): React.ReactElement { export function BladeburnerRoot(): React.ReactElement {
const player = use.Player(); const player = use.Player();

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

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

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

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

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

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

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

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

@ -15,9 +15,7 @@ import { Settings } from "../../Settings/Settings";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { Reputation } from "../../ui/React/Reputation"; import { Reputation } from "../../ui/React/Reputation";
import { createPopup } from "../../ui/React/createPopup"; 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 { Augmentation as AugFormat } from "../../ui/React/Augmentation";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography"; 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 ( return (
<React.Fragment key="f"> <React.Fragment key="f">
<TableCell key={1}> <TableCell key={1}>
@ -80,26 +77,6 @@ export function PurchaseableAugmentation(props: IProps): React.ReactElement {
const aug = Augmentations[props.augName]; const aug = Augmentations[props.augName];
if (aug == null) throw new Error(`aug ${props.augName} does not exists`); 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) { if (aug == null) {
console.error( console.error(
`Invalid Augmentation when trying to create PurchaseableAugmentation display element: ${props.augName}`, `Invalid Augmentation when trying to create PurchaseableAugmentation display element: ${props.augName}`,
@ -175,10 +152,7 @@ export function PurchaseableAugmentation(props: IProps): React.ReactElement {
placement="top" placement="top"
disableFocusListener disableFocusListener
disableTouchListener disableTouchListener
enterNextDelay={1000} disableInteractive
enterDelay={500}
leaveDelay={0}
leaveTouchDelay={0}
> >
<Typography>{btnTxt}</Typography> <Typography>{btnTxt}</Typography>
</Tooltip> </Tooltip>

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

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

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

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

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

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

@ -1,22 +1,20 @@
import React, { useEffect } from "react"; import React, { useEffect } from "react";
interface IProps { interface IProps {
onKeyDown: (event: React.KeyboardEvent<HTMLElement>) => void; onKeyDown: (this: Document, event: KeyboardEvent) => void;
onFailure: (options?: { automated: boolean }) => void; onFailure: (options?: { automated: boolean }) => void;
} }
export function KeyHandler(props: IProps): React.ReactElement { export function KeyHandler(props: IProps): React.ReactElement {
let elem: any; useEffect(() => {
useEffect(() => elem.focus()); function press(this: Document, event: KeyboardEvent): void {
const f = props.onKeyDown.bind(this);
function onKeyDown(event: React.KeyboardEvent<HTMLElement>): void { f(event);
if (!event.isTrusted) {
props.onFailure({ automated: true });
return;
} }
props.onKeyDown(event); document.addEventListener("keydown", press);
} return () => document.removeEventListener("keydown", press);
});
// invisible autofocused element that eats all the keypress for the minigames. // 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 [pos, setPos] = useState([0, 0]);
const [memoryPhase, setMemoryPhase] = useState(true); const [memoryPhase, setMemoryPhase] = useState(true);
function press(event: React.KeyboardEvent<HTMLElement>): void { function press(this: Document, event: KeyboardEvent): void {
event.preventDefault(); event.preventDefault();
if (memoryPhase) return; if (memoryPhase) return;
const move = [0, 0]; const move = [0, 0];

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

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

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

@ -3,10 +3,10 @@
* *
* TThis subcomponent renders all of the buttons for traveling to different cities * 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 { CityName } from "../data/CityNames";
import { TravelConfirmationPopup } from "./TravelConfirmationPopup"; import { TravelConfirmationModal } from "./TravelConfirmationModal";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
@ -14,11 +14,15 @@ import { IRouter } from "../../ui/Router";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { StdButton } from "../../ui/React/StdButton"; import { StdButton } from "../../ui/React/StdButton";
import { createPopup } from "../../ui/React/createPopup"; import { use } from "../../ui/Context";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { WorldMap } from "../../ui/React/WorldMap"; import { WorldMap } from "../../ui/React/WorldMap";
import { dialogBoxCreate } from "../../../utils/DialogBox"; 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 = { type IProps = {
p: IPlayer; p: IPlayer;
router: IRouter; router: IRouter;
@ -27,7 +31,6 @@ type IProps = {
function travel(p: IPlayer, router: IRouter, to: CityName): void { function travel(p: IPlayer, router: IRouter, to: CityName): void {
const cost = CONSTANTS.TravelCost; const cost = CONSTANTS.TravelCost;
if (!p.canAfford(cost)) { if (!p.canAfford(cost)) {
dialogBoxCreate(`You cannot afford to travel to ${to}`);
return; return;
} }
@ -37,69 +40,69 @@ function travel(p: IPlayer, router: IRouter, to: CityName): void {
router.toCity(); router.toCity();
} }
function createTravelPopup(p: IPlayer, router: IRouter, city: CityName): void {
if (Settings.SuppressTravelConfirmation) {
travel(p, router, city);
return;
}
const popupId = `travel-confirmation`;
createPopup(popupId, TravelConfirmationPopup, {
player: p,
city: city,
travel: () => travel(p, router, city),
popupId: popupId,
});
}
function ASCIIWorldMap(props: IProps): React.ReactElement {
return (
<div className="noselect">
<p>
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 (
<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}`}
/>
);
})}
</div>
);
}
export function TravelAgencyRoot(props: IProps): React.ReactElement { 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;
}
if (Settings.SuppressTravelConfirmation) {
travel(player, router, city);
return;
}
setOpen(true);
setDestination(city);
}
return ( return (
<> <>
<h1>Travel Agency</h1> <Typography variant="h4">Travel Agency</Typography>
{Settings.DisableASCIIArt ? ( <Box mx={2}>
<ListWorldMap p={props.p} router={props.router} /> <Typography>
) : ( From here, you can travel to any other city! A ticket costs{" "}
<ASCIIWorldMap p={props.p} router={props.router} /> <Money money={CONSTANTS.TravelCost} player={props.p} />.
)} </Typography>
{Settings.DisableASCIIArt ? (
<div>
{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 (
<React.Fragment key={city}>
<Button onClick={() => startTravel(city as CityName)} sx={{ m: 2 }}>
<Typography>Travel to {city}</Typography>
</Button>
<br />
</React.Fragment>
);
})}
</div>
) : (
<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 { function showMessage(msg: Message): void {
var txt = const txt =
"Message received from unknown sender: <br><br>" + "Message received from unknown sender: <br><br>" +
"<i>" + "<i>" +
msg.msg + msg.msg +
@ -33,15 +33,15 @@ function showMessage(msg: Message): void {
//Adds a message to a server //Adds a message to a server
function addMessageToServer(msg: Message, serverHostname: string): void { function addMessageToServer(msg: Message, serverHostname: string): void {
var server = GetServerByHostname(serverHostname); const server = GetServerByHostname(serverHostname);
if (server == null) { if (server == null) {
console.warn(`Could not find server ${serverHostname}`); console.warn(`Could not find server ${serverHostname}`);
return; return;
} }
for (var i = 0; i < server.messages.length; ++i) { for (let i = 0; i < server.messages.length; ++i) {
const msg = server.messages[i]; const other = server.messages[i];
if (typeof msg === "string") continue; if (typeof other === "string") continue;
if (msg.filename === msg.filename) { if (msg.filename === other.filename) {
return; //Already exists return; //Already exists
} }
} }
@ -49,7 +49,7 @@ function addMessageToServer(msg: Message, serverHostname: string): void {
} }
//Checks if any of the 'timed' messages should be sent //Checks if any of the 'timed' messages should be sent
function checkForMessagesToSend() { function checkForMessagesToSend(): void {
if (redPillFlag) return; if (redPillFlag) return;
const jumper0 = Messages[MessageFilenames.Jumper0]; const jumper0 = Messages[MessageFilenames.Jumper0];
const jumper1 = Messages[MessageFilenames.Jumper1]; const jumper1 = Messages[MessageFilenames.Jumper1];
@ -107,7 +107,7 @@ function loadMessages(saveString: string): void {
Messages = JSON.parse(saveString, Reviver); Messages = JSON.parse(saveString, Reviver);
} }
let MessageFilenames = { const MessageFilenames = {
Jumper0: "j0.msg", Jumper0: "j0.msg",
Jumper1: "j1.msg", Jumper1: "j1.msg",
Jumper2: "j2.msg", Jumper2: "j2.msg",
@ -119,7 +119,7 @@ let MessageFilenames = {
RedPill: "icarus.msg", RedPill: "icarus.msg",
}; };
function initMessages() { function initMessages(): void {
//Reset //Reset
Messages = {}; Messages = {};

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

@ -12,7 +12,7 @@ import { AllServers } from "../Server/AllServers";
import { compareArrays } from "../../utils/helpers/compareArrays"; import { compareArrays } from "../../utils/helpers/compareArrays";
import { roundToTwo } from "../../utils/helpers/roundToTwo"; 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(workerScript: WorkerScript): boolean;
export function killWorkerScript(pid: number): boolean; export function killWorkerScript(pid: number): boolean;
export function killWorkerScript( 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) { export function makeRuntimeRejectMsg(workerScript: WorkerScript, msg: string): string {
var lineNum = ""; const lineNum = "";
if (exp != null) {
var num = getErrorLineNumber(exp, workerScript);
lineNum = " (Line " + num + ")";
}
const server = AllServers[workerScript.serverIp]; const server = AllServers[workerScript.serverIp];
if (server == null) { if (server == null) {
throw new Error(`WorkerScript constructed with invalid server ip: ${workerScript.serverIp}`); throw new Error(`WorkerScript constructed with invalid server ip: ${workerScript.serverIp}`);
@ -32,7 +28,7 @@ export function resolveNetscriptRequestedThreads(
workerScript: WorkerScript, workerScript: WorkerScript,
functionName: string, functionName: string,
requestedThreads: number, requestedThreads: number,
) { ): number {
const threads = workerScript.scriptRef.threads; const threads = workerScript.scriptRef.threads;
if (!requestedThreads) { if (!requestedThreads) {
return isNaN(threads) || threads < 1 ? 1 : threads; return isNaN(threads) || threads < 1 ? 1 : threads;
@ -53,26 +49,11 @@ export function resolveNetscriptRequestedThreads(
return requestedThreadsAsInt; 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 { export function isScriptErrorMessage(msg: string): boolean {
if (!isString(msg)) { if (!isString(msg)) {
return false; return false;
} }
let splitMsg = msg.split("|"); const splitMsg = msg.split("|");
if (splitMsg.length != 4) { if (splitMsg.length != 4) {
return false; 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.). // (i.e. hack, grow, etc.).
// When the promise returned by this resolves, we'll have finished // When the promise returned by this resolves, we'll have finished
// running the main function of the script. // running the main function of the script.
export async function executeJSScript(scripts: Script[] = [], workerScript: WorkerScript) { export async function executeJSScript(scripts: Script[] = [], workerScript: WorkerScript): Promise<void> {
let loadedModule;
let uurls: ScriptUrl[] = []; let uurls: ScriptUrl[] = [];
let script = workerScript.getScript(); const script = workerScript.getScript();
if (script === null) throw new Error("script is null"); if (script === null) throw new Error("script is null");
if (shouldCompile(script, scripts)) { if (shouldCompile(script, scripts)) {
// The URL at the top is the one we want to import. It will // 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.module = new Promise((resolve) => resolve(eval("import(uurls[uurls.length - 1].url)")));
script.dependencies = uurls; 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 // TODO: putting await in a non-async function yields unhelpful
// "SyntaxError: unexpected reserved word" with no line number information. // "SyntaxError: unexpected reserved word" with no line number information.

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -10,7 +10,7 @@ import { numeralWrapper } from "../ui/numeralFormat";
import { compareArrays } from "../../utils/helpers/compareArrays"; 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 //The Player object stores the last update time from when we were online
const thisUpdate = new Date().getTime(); const thisUpdate = new Date().getTime();
const lastUpdate = Player.lastUpdate; const lastUpdate = Player.lastUpdate;
@ -83,8 +83,12 @@ export function scriptCalculateOfflineProduction(runningScript: RunningScript) {
//Returns a RunningScript object matching the filename and arguments on the //Returns a RunningScript object matching the filename and arguments on the
//designated server, and false otherwise //designated server, and false otherwise
export function findRunningScript(filename: string, args: (string | number)[], server: BaseServer) { export function findRunningScript(
for (var i = 0; i < server.runningScripts.length; ++i) { 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)) { if (server.runningScripts[i].filename === filename && compareArrays(server.runningScripts[i].args, args)) {
return server.runningScripts[i]; 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 //Returns a RunningScript object matching the pid on the
//designated server, and false otherwise //designated server, and false otherwise
export function findRunningScriptByPid(pid: number, server: BaseServer) { export function findRunningScriptByPid(pid: number, server: BaseServer): RunningScript | null {
for (var i = 0; i < server.runningScripts.length; ++i) { for (let i = 0; i < server.runningScripts.length; ++i) {
if (server.runningScripts[i].pid === pid) { if (server.runningScripts[i].pid === pid) {
return server.runningScripts[i]; 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 React, { useState, useEffect, useRef } from "react";
import { StdButton } from "../../ui/React/StdButton";
import Editor from "@monaco-editor/react"; import Editor from "@monaco-editor/react";
import * as monaco from "monaco-editor"; import * as monaco from "monaco-editor";
type IStandaloneCodeEditor = monaco.editor.IStandaloneCodeEditor; type IStandaloneCodeEditor = monaco.editor.IStandaloneCodeEditor;
import { createPopup } from "../../ui/React/createPopup"; import { OptionsModal } from "./OptionsModal";
import { OptionsPopup } from "./OptionsPopup";
import { Options } from "./Options"; import { Options } from "./Options";
import { js_beautify as beautifyCode } from "js-beautify"; import { js_beautify as beautifyCode } from "js-beautify";
import { isValidFilePath } from "../../Terminal/DirectoryHelpers"; import { isValidFilePath } from "../../Terminal/DirectoryHelpers";
@ -24,7 +22,14 @@ import { WorkerScript } from "../../Netscript/WorkerScript";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { iTutorialNextStep, ITutorial, iTutorialSteps } from "../../InteractiveTutorial"; 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[] = []; let symbols: string[] = [];
(function () { (function () {
const ns = NetscriptFunctions({} as WorkerScript); 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 [filename, setFilename] = useState(props.filename ? props.filename : lastFilename);
const [code, setCode] = useState<string>(props.filename ? props.code : lastCode); const [code, setCode] = useState<string>(props.filename ? props.code : lastCode);
const [ram, setRAM] = useState("RAM: ???"); const [ram, setRAM] = useState("RAM: ???");
const [optionsOpen, setOptionsOpen] = useState(false);
const [options, setOptions] = useState<Options>({ const [options, setOptions] = useState<Options>({
theme: Settings.MonacoTheme, theme: Settings.MonacoTheme,
insertSpaces: Settings.MonacoInsertSpaces, insertSpaces: Settings.MonacoInsertSpaces,
@ -121,14 +127,14 @@ export function Root(props: IProps): React.ReactElement {
let found = false; let found = false;
for (let i = 0; i < server.scripts.length; i++) { for (let i = 0; i < server.scripts.length; i++) {
if (filename == server.scripts[i].filename) { 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; found = true;
} }
} }
if (!found) { if (!found) {
const script = new Script(); 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); 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 //If the current script already exists on the server, overwrite it
for (let i = 0; i < server.scripts.length; i++) { for (let i = 0; i < server.scripts.length; i++) {
if (filename == server.scripts[i].filename) { 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(); props.router.toTerminal();
return; return;
} }
@ -164,7 +170,7 @@ export function Root(props: IProps): React.ReactElement {
//If the current script does NOT exist, create a new one //If the current script does NOT exist, create a new one
const script = new Script(); 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); server.scripts.push(script);
} else if (filename.endsWith(".txt")) { } else if (filename.endsWith(".txt")) {
for (let i = 0; i < server.textFiles.length; ++i) { for (let i = 0; i < server.textFiles.length; ++i) {
@ -198,24 +204,6 @@ export function Root(props: IProps): React.ReactElement {
setFilename(event.target.value); 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 { function updateCode(newCode?: string): void {
if (newCode === undefined) return; if (newCode === undefined) return;
lastCode = newCode; 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.javascriptDefaults.addExtraLib(libSource, "netscript.d.ts");
monaco.languages.typescript.typescriptDefaults.addExtraLib(libSource, "netscript.d.ts"); monaco.languages.typescript.typescriptDefaults.addExtraLib(libSource, "netscript.d.ts");
loaded = true;
} }
return ( return (
<> <>
<div id="script-editor-filename-wrapper"> <Box display="flex" flexDirection="row" alignItems="center">
<p id="script-editor-filename-tag" className="noselect"> <Typography>Script name: </Typography>
{" "} <TextField variant="standard" type="text" tabIndex={1} value={filename} onChange={onFilenameChange} />
<strong style={{ backgroundColor: "#555" }}>Script name: </strong> <IconButton onClick={() => setOptionsOpen(true)}>
</p> <>
<input <SettingsIcon />
id="script-editor-filename" options
type="text" </>
maxLength={100} </IconButton>
tabIndex={1} </Box>
value={filename}
onChange={onFilenameChange}
/>
<StdButton text={"options"} onClick={openOptions} />
</div>
<Editor <Editor
beforeMount={beforeMount} beforeMount={beforeMount}
onMount={onMount} onMount={onMount}
@ -333,23 +315,27 @@ export function Root(props: IProps): React.ReactElement {
theme={options.theme} theme={options.theme}
options={options} options={options}
/> />
<div id="script-editor-buttons-wrapper"> <Box display="flex" flexDirection="row" sx={{ m: 1 }} alignItems="center">
<StdButton text={"Beautify"} onClick={beautify} /> <Button onClick={beautify}>Beautify</Button>
<p id="script-editor-status-text" style={{ display: "inline-block", margin: "10px" }}> <Typography sx={{ mx: 1 }}>{ram}</Typography>
{ram} <Button onClick={save}>Save & Close (Ctrl/Cmd + b)</Button>
</p> <Link sx={{ mx: 1 }} target="_blank" href="https://bitburner.readthedocs.io/en/latest/index.html">
<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"
>
Netscript Documentation Netscript Documentation
</a> </Link>
</div> </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 // 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 * Allowed values for the 'OwnedAugmentationsOrder' setting
*/ */

@ -1,13 +1,13 @@
import { BitNodes } from "../BitNode/BitNode"; import { BitNodes } from "../BitNode/BitNode";
export class SourceFile { export class SourceFile {
info: string; info: JSX.Element;
lvl = 1; lvl = 1;
n: number; n: number;
name: string; name: string;
owned = false; owned = false;
constructor(number: number, info = "") { constructor(number: number, info: JSX.Element) {
const bitnodeKey = "BitNode" + number; const bitnodeKey = "BitNode" + number;
const bitnode = BitNodes[bitnodeKey]; const bitnode = BitNodes[bitnodeKey];
if (bitnode == null) { 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 { export function TutorialRoot(): React.ReactElement {
return ( return (
<> <>
<Typography variant="h4">Tutorial (AKA Links to Documentation)</Typography> <Typography variant="h4">Tutorial / Documentation</Typography>
<Box m={2}> <Box m={2}>
<Link <Link
color="primary" color="primary"

@ -1,6 +1,3 @@
import { Engine } from "./engine";
import { createStatusText } from "./ui/createStatusText";
function getDB(): Promise<IDBObjectStore> { function getDB(): Promise<IDBObjectStore> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!window.indexedDB) { if (!window.indexedDB) {
@ -24,7 +21,7 @@ function getDB(): Promise<IDBObjectStore> {
reject(`Failed to get IDB ${ev}`); reject(`Failed to get IDB ${ev}`);
}; };
indexedDbRequest.onsuccess = function (this: IDBRequest<IDBDatabase>, ev: Event) { indexedDbRequest.onsuccess = function (this: IDBRequest<IDBDatabase>) {
const db = this.result; const db = this.result;
if (!db) { if (!db) {
reject("database loadign result was undefined"); 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> { export function load(): Promise<string> {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
await getDB() await getDB()
@ -59,11 +51,6 @@ export function load(): Promise<string> {
}); });
} }
interface ISaveCallback {
success: () => void;
error?: () => void;
}
export function save(saveString: string): Promise<void> { export function save(saveString: string): Promise<void> {
return getDB().then((db) => { return getDB().then((db) => {
return new Promise<void>((resolve, reject) => { 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) { if (Engine.Counters.passiveFactionGrowth <= 0) {
var adjustedCycles = Math.floor(5 - Engine.Counters.passiveFactionGrowth); const adjustedCycles = Math.floor(5 - Engine.Counters.passiveFactionGrowth);
processPassiveFactionRepGain(adjustedCycles); processPassiveFactionRepGain(adjustedCycles);
Engine.Counters.passiveFactionGrowth = 5; Engine.Counters.passiveFactionGrowth = 5;
} }
@ -334,7 +334,7 @@ const Engine: {
} }
// Hacknet Nodes offline progress // Hacknet Nodes offline progress
var offlineProductionFromHacknetNodes = processHacknetEarnings(Player, numCyclesOffline); const offlineProductionFromHacknetNodes = processHacknetEarnings(Player, numCyclesOffline);
const hacknetProdInfo = hasHacknetServers(Player) ? ( const hacknetProdInfo = hasHacknetServers(Player) ? (
<>{Hashes(offlineProductionFromHacknetNodes)} hashes</> <>{Hashes(offlineProductionFromHacknetNodes)} hashes</>
) : ( ) : (
@ -384,7 +384,7 @@ const Engine: {
} }
// Update total playtime // Update total playtime
var time = numCyclesOffline * CONSTANTS._idleSpeed; const time = numCyclesOffline * CONSTANTS._idleSpeed;
if (Player.totalPlaytime == null) { if (Player.totalPlaytime == null) {
Player.totalPlaytime = 0; Player.totalPlaytime = 0;
} }

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

@ -28,6 +28,7 @@ export function ActiveScriptsRoot(props: IProps): React.ReactElement {
return ( return (
<> <>
<Typography variant="h4">Active Scripts</Typography>
<Typography> <Typography>
This page displays a list of all of your scripts that are currently running across every machine. It also 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 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; const index = "BitNode" + player.bitNodeN;
return ( return (
<> <>
<Typography variant="h5" color="primary"> <Typography variant="h4">
BitNode {player.bitNodeN}: {BitNodes[index].name} BitNode {player.bitNodeN}: {BitNodes[index].name}
</Typography> </Typography>
<Typography sx={{ mx: 2 }} style={{ whiteSpace: "pre-wrap", overflowWrap: "break-word" }}> <Typography sx={{ mx: 2 }} style={{ whiteSpace: "pre-wrap", overflowWrap: "break-word" }}>
@ -270,9 +270,7 @@ export function CharacterStats(): React.ReactElement {
return ( return (
<> <>
<Typography variant="h5" color="primary"> <Typography variant="h4">General</Typography>
General
</Typography>
<Box sx={{ mx: 2 }}> <Box sx={{ mx: 2 }}>
<Typography>Current City: {player.city}</Typography> <Typography>Current City: {player.city}</Typography>
<LastEmployer /> <LastEmployer />
@ -287,9 +285,7 @@ export function CharacterStats(): React.ReactElement {
</Typography> </Typography>
</Box> </Box>
<br /> <br />
<Typography variant="h5" color="primary"> <Typography variant="h4">Stats</Typography>
Stats
</Typography>
<Box sx={{ mx: 2 }}> <Box sx={{ mx: 2 }}>
<Table size="small" padding="none"> <Table size="small" padding="none">
<TableBody> <TableBody>
@ -365,9 +361,7 @@ export function CharacterStats(): React.ReactElement {
<br /> <br />
</Box> </Box>
<br /> <br />
<Typography variant="h5" color="primary"> <Typography variant="h4">Multipliers</Typography>
Multipliers
</Typography>
<Box sx={{ mx: 2 }}> <Box sx={{ mx: 2 }}>
<MultiplierTable <MultiplierTable
rows={[ rows={[
@ -477,9 +471,7 @@ export function CharacterStats(): React.ReactElement {
</Box> </Box>
<br /> <br />
<Typography variant="h5" color="primary"> <Typography variant="h4">Misc</Typography>
Misc
</Typography>
<Box sx={{ mx: 2 }}> <Box sx={{ mx: 2 }}>
<Typography>{`Servers owned: ${player.purchasedServers.length} / ${getPurchaseServerLimit()}`}</Typography> <Typography>{`Servers owned: ${player.purchasedServers.length} / ${getPurchaseServerLimit()}`}</Typography>
<Hacknet /> <Hacknet />

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

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

@ -4,30 +4,47 @@
* The header of the accordion contains the Source-Files's name and level, * The header of the accordion contains the Source-Files's name and level,
* and the accordion's panel contains the Source-File's description. * and the accordion's panel contains the Source-File's description.
*/ */
import * as React from "react"; import React, { useState } from "react";
import { BBAccordion } from "./BBAccordion";
import { SourceFile } from "../../SourceFile/SourceFile"; 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 = { type IProps = {
level: number; level: number;
sf: SourceFile; sf: SourceFile;
}; };
export function SourceFileAccordion(props: IProps): React.ReactElement { export function SourceFileAccordion(props: IProps): React.ReactElement {
const [open, setOpen] = useState(false);
const maxLevel = props.sf.n === 12 ? "∞" : "3"; const maxLevel = props.sf.n === 12 ? "∞" : "3";
return ( return (
<BBAccordion <Box component={Paper}>
headerContent={ <ListItemButton onClick={() => setOpen((old) => !old)}>
<> <ListItemText
{props.sf.name} primary={
<br /> <Typography style={{ whiteSpace: "pre-wrap" }}>
{`Level ${props.level} / ${maxLevel}`} {props.sf.name}
</> <br />
} {`Level ${props.level} / ${maxLevel}`}
panelContent={<p dangerouslySetInnerHTML={{ __html: props.sf.info }}></p>} </Typography>
/> }
/>
{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; let theme: Theme;
export function refreshTheme() { export function refreshTheme(): void {
theme = createTheme({ theme = createTheme({
colors: { colors: {
hp: Settings.theme.hp, hp: Settings.theme.hp,

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

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

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