merge dev

This commit is contained in:
Olivier Gagnon 2021-10-15 15:13:48 -04:00
commit d2f2ab07d7
79 changed files with 4525 additions and 15846 deletions

36
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

@ -0,0 +1,18 @@
atExit() Netscript Function
============================
.. js:function:: atExit(f)
:RAM cost: 0 GB
:param function f: function to call when the script dies.
Runs when the script dies.
Example:
.. code-block:: javascript
function onDeath() {
console.log('I died!!!')
}
atExit(onDeath);

@ -0,0 +1,36 @@
autocomplete() Netscript Function
============================
.. warning:: This feature is not officially supported yet and the API might change.
.. js:function:: autocomplete(data, args)
:RAM cost: 0 GB
:param Object data: general data about the game you might want to autocomplete.
:param string[] args: current arguments.
data is an object with the following properties::
{
servers: list of all servers in the game.
txts: list of all text files on the current server.
scripts: list of all scripts on the current server.
}
Example:
.. code-block:: javascript
export function autocomplete(data, args) {
return [...data.servers]; // This script autocompletes the list of servers.
return [...data.servers, ...data.scripts]; // Autocomplete servers and scripts
return ["low", "medium", "high"]; // Autocomplete 3 specific strings.
}
Terminal:
.. code-block:: javascript
$ run demo.ns mega\t
// results in
$ run demo.ns megacorp

@ -10,3 +10,4 @@ they contain spoilers for the game.
getBitNodeMultipliers() <advancedfunctions/getBitNodeMultipliers> getBitNodeMultipliers() <advancedfunctions/getBitNodeMultipliers>
getServer() <advancedfunctions/getServer> getServer() <advancedfunctions/getServer>
autocomplete() <advancedfunctions/autocomplete>

@ -43,6 +43,7 @@ The player has access to all of these functions while in BitNode-4. Completing B
getFactionRep() <singularityfunctions/getFactionRep> getFactionRep() <singularityfunctions/getFactionRep>
getFactionFavor() <singularityfunctions/getFactionFavor> getFactionFavor() <singularityfunctions/getFactionFavor>
getFactionFavorGain() <singularityfunctions/getFactionFavorGain> getFactionFavorGain() <singularityfunctions/getFactionFavorGain>
upgradeHomeCores() <singularityfunctions/upgradeHomeCores>
.. toctree:: .. toctree::
:caption: Level 3 Functions :caption: Level 3 Functions

@ -0,0 +1,12 @@
upgradeHomeRam() Netscript Function
===================================
.. js:function:: upgradeHomeCores()
:RAM cost: 3 GB
If you are not in BitNode-4, then you must have Level 2 of Source-File 4 in order to use this function.
This function will upgrade amount of CORES on the player's home computer. The cost is the same as if you were to do it manually.
This function will return true if the player's home computer core count is successfully upgraded, and false otherwise.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

14361
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -14,7 +14,6 @@
"@emotion/styled": "^11.3.0", "@emotion/styled": "^11.3.0",
"@monaco-editor/react": "^4.2.2", "@monaco-editor/react": "^4.2.2",
"@mui/icons-material": "^5.0.3", "@mui/icons-material": "^5.0.3",
"@mui/lab": "^5.0.0-alpha.46",
"@mui/material": "^5.0.3", "@mui/material": "^5.0.3",
"@mui/styles": "^5.0.1", "@mui/styles": "^5.0.1",
"@types/escodegen": "^0.0.7", "@types/escodegen": "^0.0.7",
@ -25,47 +24,23 @@
"@types/react-resizable": "^1.7.3", "@types/react-resizable": "^1.7.3",
"acorn": "^8.4.1", "acorn": "^8.4.1",
"acorn-walk": "^8.1.1", "acorn-walk": "^8.1.1",
"ajv": "^5.1.5",
"ajv-keywords": "^2.0.0",
"arg": "^5.0.0", "arg": "^5.0.0",
"async": "^2.6.1",
"autosize": "^4.0.2",
"better-react-mathjax": "^1.0.3", "better-react-mathjax": "^1.0.3",
"brace": "^0.11.1", "clsx": "^1.1.1",
"codemirror": "^5.58.2",
"decimal.js": "7.2.3", "decimal.js": "7.2.3",
"enhanced-resolve": "^4.0.0",
"escodegen": "^1.11.0", "escodegen": "^1.11.0",
"escope": "^3.6.0",
"file-saver": "^1.3.8", "file-saver": "^1.3.8",
"interpret": "^1.0.0",
"jquery": "^3.5.0", "jquery": "^3.5.0",
"jshint": "^2.10.2",
"json-loader": "^0.5.4",
"jsplumb": "^2.6.8",
"jszip": "^3.7.0", "jszip": "^3.7.0",
"loader-runner": "^2.3.0",
"loader-utils": "^1.1.0",
"material-ui-color": "^1.2.0", "material-ui-color": "^1.2.0",
"mathjax-full": "^3.2.0",
"memory-fs": "~0.4.1",
"monaco-editor": "^0.27.0", "monaco-editor": "^0.27.0",
"node-sass": "^6.0.1",
"normalize.css": "^8.0.0",
"notistack": "^2.0.2", "notistack": "^2.0.2",
"numeral": "2.0.6", "numeral": "2.0.6",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-draggable": "^4.4.4", "react-draggable": "^4.4.4",
"react-modal": "^3.12.1",
"react-resizable": "^3.0.4", "react-resizable": "^3.0.4",
"sprintf-js": "^1.1.1", "sprintf-js": "^1.1.1"
"tapable": "^1.0.0",
"treant-js": "^1.0.1",
"unused-webpack-plugin": "^2.4.0",
"uuid": "^3.2.1",
"w3c-blob": "0.0.1",
"webpack-deadcode-plugin": "^0.1.15"
}, },
"description": "A cyberpunk-themed incremental game", "description": "A cyberpunk-themed incremental game",
"devDependencies": { "devDependencies": {
@ -76,64 +51,33 @@
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.1", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.1",
"@testing-library/cypress": "^8.0.1", "@testing-library/cypress": "^8.0.1",
"@types/file-saver": "^2.0.3", "@types/file-saver": "^2.0.3",
"@types/jest": "^27.0.1",
"@types/lodash": "^4.14.168", "@types/lodash": "^4.14.168",
"@types/node": "^16.9.1",
"@typescript-eslint/eslint-plugin": "^4.22.0", "@typescript-eslint/eslint-plugin": "^4.22.0",
"@typescript-eslint/parser": "^4.22.0", "@typescript-eslint/parser": "^4.22.0",
"babel-jest": "^27.0.6", "babel-jest": "^27.0.6",
"babel-loader": "^8.0.5", "babel-loader": "^8.0.5",
"beautify-lint": "^1.0.3",
"benchmark": "^2.1.1",
"bundle-loader": "~0.5.0",
"css-loader": "^0.28.11",
"cypress": "^8.3.1", "cypress": "^8.3.1",
"electron": "^14.0.1", "electron": "^14.0.1",
"electron-packager": "^15.4.0", "electron-packager": "^15.4.0",
"es6-promise-polyfill": "^1.1.1",
"eslint": "^7.24.0", "eslint": "^7.24.0",
"eslint-plugin-node": "^11.1.0",
"file-loader": "^1.1.11",
"fork-ts-checker-webpack-plugin": "^6.3.3", "fork-ts-checker-webpack-plugin": "^6.3.3",
"html-webpack-plugin": "^3.2.0", "html-webpack-plugin": "^3.2.0",
"http-server": "^13.0.1", "http-server": "^13.0.1",
"i18n-webpack-plugin": "^1.0.0",
"istanbul": "^0.4.5",
"jest": "^27.1.0", "jest": "^27.1.0",
"js-beautify": "^1.5.10", "js-beautify": "^1.5.10",
"jsdom": "^15.0.0", "jsdom": "^15.0.0",
"jsdom-global": "^3.0.2",
"json5": "^1.0.1",
"less": "^3.9.0",
"less-loader": "^4.1.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mini-css-extract-plugin": "^0.4.1", "mini-css-extract-plugin": "^0.4.1",
"mkdirp": "^0.5.1",
"null-loader": "^1.0.0",
"prettier": "^2.3.2", "prettier": "^2.3.2",
"raw-loader": "~0.5.0",
"react-refresh": "^0.10.0", "react-refresh": "^0.10.0",
"regenerator-runtime": "^0.13.9", "regenerator-runtime": "^0.13.9",
"sass-loader": "^7.0.3",
"script-loader": "~0.7.0",
"should": "^11.1.1",
"simple-git": "^1.96.0",
"sinon": "^2.3.2",
"source-map": "^0.7.3", "source-map": "^0.7.3",
"start-server-and-test": "^1.14.0", "start-server-and-test": "^1.14.0",
"style-loader": "^0.21.0",
"stylelint": "^9.2.1",
"stylelint-order": "^0.8.1",
"typescript": "^4.2.4", "typescript": "^4.2.4",
"uglify-es": "^3.3.9",
"uglifyjs-webpack-plugin": "^1.3.0",
"url-loader": "^1.0.1",
"watchpack": "^1.6.0",
"webpack": "^4.46.0", "webpack": "^4.46.0",
"webpack-cli": "^3.3.12", "webpack-cli": "^3.3.12",
"webpack-dev-middleware": "^3.7.3", "webpack-dev-middleware": "^3.7.3",
"webpack-dev-server": "^3.11.2", "webpack-dev-server": "^3.11.2"
"worker-loader": "^2.0.0"
}, },
"engines": { "engines": {
"node": ">=8 || <=9" "node": ">=8 || <=9"
@ -157,7 +101,6 @@
"build:dev": "webpack --mode development", "build:dev": "webpack --mode development",
"lint": "npm run lint:jsts & npm run lint:style", "lint": "npm run lint:jsts & npm run lint:style",
"lint:jsts": "eslint --fix . --ext js,jsx,ts,tsx", "lint:jsts": "eslint --fix . --ext js,jsx,ts,tsx",
"lint:style": "stylelint --fix ./css/*",
"preinstall": "node ./scripts/engines-check.js", "preinstall": "node ./scripts/engines-check.js",
"test": "jest", "test": "jest",
"test:watch": "jest --watch", "test:watch": "jest --watch",

@ -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

@ -15,7 +15,7 @@ import { Skill } from "./Skill";
import { City } from "./City"; import { City } from "./City";
import { IAction } from "./IAction"; import { IAction } from "./IAction";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { IRouter } from "../ui/Router"; import { IRouter, Page } from "../ui/Router";
import { ConsoleHelpText } from "./data/Help"; import { ConsoleHelpText } from "./data/Help";
import { exceptionAlert } from "../utils/helpers/exceptionAlert"; import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { getRandomInt } from "../utils/helpers/getRandomInt"; import { getRandomInt } from "../utils/helpers/getRandomInt";
@ -26,7 +26,6 @@ import { addOffset } from "../utils/helpers/addOffset";
import { Faction } from "../Faction/Faction"; import { Faction } from "../Faction/Faction";
import { Factions, factionExists } from "../Faction/Factions"; import { Factions, factionExists } from "../Faction/Factions";
import { calculateHospitalizationCost } from "../Hospital/Hospital"; import { calculateHospitalizationCost } from "../Hospital/Hospital";
import { redPillFlag } from "../RedPill";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Settings } from "../Settings/Settings"; import { Settings } from "../Settings/Settings";
import { Augmentations } from "../Augmentation/Augmentations"; import { Augmentations } from "../Augmentation/Augmentations";
@ -145,7 +144,7 @@ export class Bladeburner implements IBladeburner {
if (action.count < 1) { if (action.count < 1) {
return this.resetAction(); return this.resetAction();
} }
if (actionId.name === "Raid" && this.getCurrentCity().commsEst === 0) { if (actionId.name === "Raid" && this.getCurrentCity().comms === 0) {
return this.resetAction(); return this.resetAction();
} }
this.actionTimeToComplete = action.getActionTime(this); this.actionTimeToComplete = action.getActionTime(this);
@ -184,6 +183,7 @@ export class Bladeburner implements IBladeburner {
break; break;
case ActionTypes["Diplomacy"]: case ActionTypes["Diplomacy"]:
case ActionTypes["Hyperbolic Regeneration Chamber"]: case ActionTypes["Hyperbolic Regeneration Chamber"]:
case ActionTypes["Incite Violence"]:
this.actionTimeToComplete = 60; this.actionTimeToComplete = 60;
break; break;
default: default:
@ -339,6 +339,10 @@ export class Bladeburner implements IBladeburner {
action.type = ActionTypes["Hyperbolic Regeneration Chamber"]; action.type = ActionTypes["Hyperbolic Regeneration Chamber"];
action.name = "Hyperbolic Regeneration Chamber"; action.name = "Hyperbolic Regeneration Chamber";
break; break;
case "stir trouble":
action.type = ActionTypes["Incite Violence"];
action.name = "Incite Violence";
break;
default: default:
return null; return null;
} }
@ -1088,9 +1092,6 @@ export class Bladeburner implements IBladeburner {
case "Investigation": case "Investigation":
if (success) { if (success) {
city.improvePopulationEstimateByPercentage(0.4 * this.skillMultipliers.successChanceEstimate); city.improvePopulationEstimateByPercentage(0.4 * this.skillMultipliers.successChanceEstimate);
if (Math.random() < 0.02 * this.skillMultipliers.successChanceEstimate) {
city.improveCommunityEstimate(1);
}
} else { } else {
this.triggerPotentialMigration(this.city, 0.1); this.triggerPotentialMigration(this.city, 0.1);
} }
@ -1098,9 +1099,6 @@ export class Bladeburner implements IBladeburner {
case "Undercover Operation": case "Undercover Operation":
if (success) { if (success) {
city.improvePopulationEstimateByPercentage(0.8 * this.skillMultipliers.successChanceEstimate); city.improvePopulationEstimateByPercentage(0.8 * this.skillMultipliers.successChanceEstimate);
if (Math.random() < 0.02 * this.skillMultipliers.successChanceEstimate) {
city.improveCommunityEstimate(1);
}
} else { } else {
this.triggerPotentialMigration(this.city, 0.15); this.triggerPotentialMigration(this.city, 0.15);
} }
@ -1121,7 +1119,6 @@ export class Bladeburner implements IBladeburner {
nonZero: true, nonZero: true,
}); });
--city.comms; --city.comms;
--city.commsEst;
} else { } else {
const change = getRandomInt(-10, -5) / 10; const change = getRandomInt(-10, -5) / 10;
city.changePopulationByPercentage(change, { city.changePopulationByPercentage(change, {
@ -1174,6 +1171,8 @@ export class Bladeburner implements IBladeburner {
return GeneralActions["Diplomacy"]; return GeneralActions["Diplomacy"];
case ActionTypes["Hyperbolic Regeneration Chamber"]: case ActionTypes["Hyperbolic Regeneration Chamber"]:
return GeneralActions["Hyperbolic Regeneration Chamber"]; return GeneralActions["Hyperbolic Regeneration Chamber"];
case ActionTypes["Incite Violence"]:
return GeneralActions["Incite Violence"];
default: default:
return null; return null;
} }
@ -1500,6 +1499,23 @@ export class Bladeburner implements IBladeburner {
} }
break; break;
} }
case ActionTypes["Incite Violence"]: {
for (const contract of Object.keys(this.contracts)) {
const growthF = Growths[contract];
if (!growthF) throw new Error("trying to generate count for action that doesn't exist? " + contract);
this.contracts[contract].count += (60 * 3 * growthF()) / BladeburnerConstants.ActionCountGrowthPeriod;
}
for (const operation of Object.keys(this.operations)) {
const growthF = Growths[operation];
if (!growthF) throw new Error("trying to generate count for action that doesn't exist? " + operation);
this.operations[operation].count += (60 * 3 * growthF()) / BladeburnerConstants.ActionCountGrowthPeriod;
}
if (this.logging.general) {
this.log(`Incited violence in the synthoid communities.`);
}
this.startAction(player, this.action);
break;
}
default: default:
console.error(`Bladeburner.completeAction() called for invalid action: ${this.action.type}`); console.error(`Bladeburner.completeAction() called for invalid action: ${this.action.type}`);
break; break;
@ -1854,7 +1870,7 @@ export class Bladeburner implements IBladeburner {
process(router: IRouter, player: IPlayer): void { process(router: IRouter, player: IPlayer): void {
// Edge case condition...if Operation Daedalus is complete trigger the BitNode // Edge case condition...if Operation Daedalus is complete trigger the BitNode
if (redPillFlag === false && this.blackops.hasOwnProperty("Operation Daedalus")) { if (router.page() !== Page.BitVerse && this.blackops.hasOwnProperty("Operation Daedalus")) {
return router.toBitVerse(false, false); return router.toBitVerse(false, false);
} }
@ -1967,6 +1983,7 @@ export class Bladeburner implements IBladeburner {
"Field Analysis", "Field Analysis",
"Diplomacy", "Diplomacy",
"Hyperbolic Regeneration Chamber", "Hyperbolic Regeneration Chamber",
"Incite Violence",
]; ];
if (gen.includes(res.type)) { if (gen.includes(res.type)) {
res.type = "General"; res.type = "General";
@ -2091,6 +2108,7 @@ export class Bladeburner implements IBladeburner {
return this.getRecruitmentTime(player); return this.getRecruitmentTime(player);
case ActionTypes["Diplomacy"]: case ActionTypes["Diplomacy"]:
case ActionTypes["Hyperbolic Regeneration Chamber"]: case ActionTypes["Hyperbolic Regeneration Chamber"]:
case ActionTypes["Incite Violence"]:
return 60; return 60;
default: default:
workerScript.log("bladeburner.getActionTime", errorLogText); workerScript.log("bladeburner.getActionTime", errorLogText);
@ -2128,6 +2146,7 @@ export class Bladeburner implements IBladeburner {
case ActionTypes["FieldAnalysis"]: case ActionTypes["FieldAnalysis"]:
case ActionTypes["Diplomacy"]: case ActionTypes["Diplomacy"]:
case ActionTypes["Hyperbolic Regeneration Chamber"]: case ActionTypes["Hyperbolic Regeneration Chamber"]:
case ActionTypes["Incite Violence"]:
return [1, 1]; return [1, 1];
case ActionTypes["Recruitment"]: { case ActionTypes["Recruitment"]: {
const recChance = this.getRecruitmentSuccessChance(player); const recChance = this.getRecruitmentSuccessChance(player);
@ -2170,6 +2189,7 @@ export class Bladeburner implements IBladeburner {
case ActionTypes["FieldAnalysis"]: case ActionTypes["FieldAnalysis"]:
case ActionTypes["Diplomacy"]: case ActionTypes["Diplomacy"]:
case ActionTypes["Hyperbolic Regeneration Chamber"]: case ActionTypes["Hyperbolic Regeneration Chamber"]:
case ActionTypes["Incite Violence"]:
return Infinity; return Infinity;
default: default:
workerScript.log("bladeburner.getActionCountRemaining", errorLogText); workerScript.log("bladeburner.getActionCountRemaining", errorLogText);

@ -34,11 +34,6 @@ export class City {
*/ */
comms = 0; comms = 0;
/**
* Estimated number of communities in the city.
*/
commsEst = 0;
/** /**
* Chaos level of the city. * Chaos level of the city.
*/ */
@ -53,8 +48,6 @@ export class City {
// Number of Synthoid communities population and estimate // Number of Synthoid communities population and estimate
this.comms = getRandomInt(5, 150); this.comms = getRandomInt(5, 150);
this.commsEst = this.comms + getRandomInt(-5, 5);
if (this.commsEst < 0) this.commsEst = 0;
this.chaos = 0; this.chaos = 0;
} }
@ -113,23 +106,6 @@ export class City {
} }
} }
improveCommunityEstimate(n = 1): void {
if (isNaN(n)) {
throw new Error("NaN passed into City.improveCommunityEstimate()");
}
if (this.commsEst < this.comms) {
this.commsEst += n;
if (this.commsEst > this.comms) {
this.commsEst = this.comms;
}
} else if (this.commsEst > this.comms) {
this.commsEst -= n;
if (this.commsEst < this.comms) {
this.commsEst = this.comms;
}
}
}
/** /**
* @params options: * @params options:
* estChange(int): How much the estimate should change by * estChange(int): How much the estimate should change by

@ -30,4 +30,8 @@ export const GeneralActions: IMap<Action> = {};
GeneralActions[actionName] = new Action({ GeneralActions[actionName] = new Action({
name: actionName, name: actionName,
}); });
actionName = "Incite Violence";
GeneralActions[actionName] = new Action({
name: actionName,
});
})(); })();

@ -1,6 +1,6 @@
import { IBladeburner } from "./IBladeburner"; import { IBladeburner } from "./IBladeburner";
export interface IStatsMultiplier { interface IStatsMultiplier {
[key: string]: number; [key: string]: number;
hack: number; hack: number;

@ -12,6 +12,7 @@ export const ActionTypes: {
"Field Analysis": number; "Field Analysis": number;
Diplomacy: number; Diplomacy: number;
"Hyperbolic Regeneration Chamber": number; "Hyperbolic Regeneration Chamber": number;
"Incite Violence": number;
} = { } = {
Idle: 1, Idle: 1,
Contract: 2, Contract: 2,
@ -24,4 +25,5 @@ export const ActionTypes: {
"Field Analysis": 7, "Field Analysis": 7,
Diplomacy: 8, Diplomacy: 8,
"Hyperbolic Regeneration Chamber": 9, "Hyperbolic Regeneration Chamber": 9,
"Incite Violence": 10,
}; };

@ -62,4 +62,7 @@ export const GeneralActions: {
</> </>
), ),
}, },
"Incite Violence": {
desc: <>Purposefully stir trouble in the synthoid community in order to gain a political edge.</>,
},
}; };

@ -55,14 +55,18 @@ export function ActionLevel({ action, isActive, bladeburner, rerender }: IProps)
</Tooltip> </Tooltip>
</Box> </Box>
<Tooltip title={isActive ? <Typography>WARNING: changing the level will restart the Operation</Typography> : ""}> <Tooltip title={isActive ? <Typography>WARNING: changing the level will restart the Operation</Typography> : ""}>
<IconButton disabled={!canIncrease} onClick={increaseLevel}> <span>
<ArrowDropUpIcon /> <IconButton disabled={!canIncrease} onClick={increaseLevel}>
</IconButton> <ArrowDropUpIcon />
</IconButton>
</span>
</Tooltip> </Tooltip>
<Tooltip title={isActive ? <Typography>WARNING: changing the level will restart the Operation</Typography> : ""}> <Tooltip title={isActive ? <Typography>WARNING: changing the level will restart the Operation</Typography> : ""}>
<IconButton disabled={!canDecrease} onClick={decreaseLevel}> <span>
<ArrowDropDownIcon /> <IconButton disabled={!canDecrease} onClick={decreaseLevel}>
</IconButton> <ArrowDropDownIcon />
</IconButton>
</span>
</Tooltip> </Tooltip>
</Box> </Box>
); );

@ -27,7 +27,11 @@ export function BlackOpElem(props: IProps): React.ReactElement {
} }
const isCompleted = props.bladeburner.blackops[props.action.name] != null; const isCompleted = props.bladeburner.blackops[props.action.name] != null;
if (isCompleted) { if (isCompleted) {
return <h2 style={{ display: "block" }}>{props.action.name} (COMPLETED)</h2>; return (
<Paper sx={{ my: 1, p: 1 }}>
<Typography>{props.action.name} (COMPLETED)</Typography>
</Paper>
);
} }
const isActive = const isActive =
@ -47,33 +51,35 @@ export function BlackOpElem(props: IProps): React.ReactElement {
return ( return (
<Paper sx={{ my: 1, p: 1 }}> <Paper sx={{ my: 1, p: 1 }}>
<Typography> {isActive ? (
{isActive ? ( <>
<>
<>
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
<p style={{ display: "block" }}>
{createProgressBarText({
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
})}
</p>
</>
</>
) : (
<> <>
<CopyableText value={props.action.name} /> <CopyableText value={props.action.name} />
<Typography>
<StartButton (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
bladeburner={props.bladeburner} {formatNumber(props.bladeburner.actionTimeToComplete, 0)})
type={ActionTypes.BlackOperation} </Typography>
name={props.action.name} <Typography>
rerender={rerender} {createProgressBarText({
/> progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
<TeamSizeButton action={props.action} bladeburner={props.bladeburner} /> })}
</Typography>
</> </>
)} </>
</Typography> ) : (
<>
<CopyableText value={props.action.name} />
<StartButton
bladeburner={props.bladeburner}
type={ActionTypes.BlackOperation}
name={props.action.name}
rerender={rerender}
/>
<TeamSizeButton action={props.action} bladeburner={props.bladeburner} />
</>
)}
<br /> <br />
<br /> <br />
<Typography>{actionData.desc}</Typography> <Typography>{actionData.desc}</Typography>

@ -37,6 +37,7 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
return 30; return 30;
case "Diplomacy": case "Diplomacy":
case "Hyperbolic Regeneration Chamber": case "Hyperbolic Regeneration Chamber":
case "Incite Violence":
return 60; return 60;
case "Recruitment": case "Recruitment":
return props.bladeburner.getRecruitmentTime(props.player); return props.bladeburner.getRecruitmentTime(props.player);
@ -57,8 +58,9 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
<Paper sx={{ my: 1, p: 1 }}> <Paper sx={{ my: 1, p: 1 }}>
{isActive ? ( {isActive ? (
<> <>
<CopyableText value={props.action.name} />
<Typography> <Typography>
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "} (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
{formatNumber(props.bladeburner.actionTimeToComplete, 0)}) {formatNumber(props.bladeburner.actionTimeToComplete, 0)})
</Typography> </Typography>
<Typography> <Typography>
@ -69,9 +71,7 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
</> </>
) : ( ) : (
<Box display="flex" flexDirection="row" alignItems="center"> <Box display="flex" flexDirection="row" alignItems="center">
<Typography> <CopyableText value={props.action.name} />
<CopyableText value={props.action.name} />
</Typography>
<StartButton <StartButton
bladeburner={props.bladeburner} bladeburner={props.bladeburner}
type={ActionTypes[props.action.name as string]} type={ActionTypes[props.action.name as string]}

@ -117,9 +117,7 @@ export function Stats(props: IProps): React.ReactElement {
</Typography> </Typography>
} }
> >
<Typography> <Typography>Synthoid Communities: {formatNumber(props.bladeburner.getCurrentCity().comms, 0)}</Typography>
Est. Synthoid Communities: {formatNumber(props.bladeburner.getCurrentCity().comms, 0)}
</Typography>
</Tooltip> </Tooltip>
</Box> </Box>
<br /> <br />

@ -1,4 +1,4 @@
export interface RNG { interface RNG {
random(): number; random(): number;
} }

@ -45,7 +45,7 @@ export function generateRandomContractOnHome(): void {
serv.addContract(contract); serv.addContract(contract);
} }
export interface IGenerateContractParams { interface IGenerateContractParams {
problemType?: string; problemType?: string;
server?: string; server?: string;
fn?: string; fn?: string;

@ -8,7 +8,7 @@ import { CodingContractEvent } from "./ui/React/CodingContractModal";
/* tslint:disable:no-magic-numbers completed-docs max-classes-per-file no-console */ /* tslint:disable:no-magic-numbers completed-docs max-classes-per-file no-console */
/* Represents different types of problems that a Coding Contract can have */ /* Represents different types of problems that a Coding Contract can have */
export class CodingContractType { class CodingContractType {
/** /**
* Function that generates a description of the problem * Function that generates a description of the problem
*/ */

@ -45,8 +45,3 @@ export function initCompanies(): void {
export function loadCompanies(saveString: string): void { export function loadCompanies(saveString: string): void {
Companies = JSON.parse(saveString, Reviver); Companies = JSON.parse(saveString, Reviver);
} }
// Utility function to check if a string is valid company name
export function companyExists(name: string): boolean {
return Companies.hasOwnProperty(name);
}

@ -20,7 +20,7 @@ export function IndustryProductEquation(props: IProps): React.ReactElement {
return ( return (
<MathJaxContext> <MathJaxContext>
<MathJax>{"\\(" + reqs.join("+") + `\\Rightarrow` + prod.map((p) => `1\\text{ }${p}`).join("+") + "\\)"}</MathJax> <MathJax>{"\\(" + reqs.join("+") + `\\Rightarrow` + prod.map((p) => `1 \\text{${p}}`).join("+") + "\\)"}</MathJax>
</MathJaxContext> </MathJaxContext>
); );
} }

@ -76,11 +76,6 @@ function MarketTA2(props: IMarketTA2Props): React.ReactElement {
</Tooltip> </Tooltip>
} }
/> />
<Typography>
Note that Market-TA.II overrides Market-TA.I. This means that if both are enabled, then Market-TA.II will take
effect, not Market-TA.I
</Typography>
</> </>
); );
} }
@ -93,6 +88,7 @@ interface IProps {
// Create a popup that lets the player use the Market TA research for Materials // Create a popup that lets the player use the Market TA research for Materials
export function MaterialMarketTaModal(props: IProps): React.ReactElement { export function MaterialMarketTaModal(props: IProps): React.ReactElement {
const division = useDivision();
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
function rerender(): void { function rerender(): void {
setRerender((old) => !old); setRerender((old) => !old);
@ -106,28 +102,32 @@ export function MaterialMarketTaModal(props: IProps): React.ReactElement {
return ( return (
<Modal open={props.open} onClose={props.onClose}> <Modal open={props.open} onClose={props.onClose}>
<Typography variant="h4">Market-TA.I</Typography> {!division.hasResearch("Market-TA.II") && (
<Typography> <>
The maximum sale price you can mark this up to is {numeralWrapper.formatMoney(props.mat.bCost + markupLimit)}. <Typography variant="h4">Market-TA.I</Typography>
This means that if you set the sale price higher than this, you will begin to experience a loss in number of <Typography>
sales The maximum sale price you can mark this up to is{" "}
</Typography> {numeralWrapper.formatMoney(props.mat.bCost + markupLimit)}. This means that if you set the sale price
higher than this, you will begin to experience a loss in number of sales
</Typography>
<FormControlLabel <FormControlLabel
control={<Switch checked={props.mat.marketTa1} onChange={onMarketTA1} />} control={<Switch checked={props.mat.marketTa1} onChange={onMarketTA1} />}
label={ label={
<Tooltip <Tooltip
title={ title={
<Typography> <Typography>
If this is enabled, then this Material will automatically be sold at the price identified by Market-TA.I If this is enabled, then this Material will automatically be sold at the price identified by
(i.e. the price shown above) Market-TA.I (i.e. the price shown above)
</Typography> </Typography>
}
>
<Typography>Use Market-TA.I for Auto-Sale Price</Typography>
</Tooltip>
} }
> />
<Typography>Use Market-TA.I for Auto-Sale Price</Typography> </>
</Tooltip> )}
}
/>
<MarketTA2 mat={props.mat} /> <MarketTA2 mat={props.mat} />
</Modal> </Modal>
); );

@ -66,11 +66,6 @@ function MarketTA2(props: ITa2Props): React.ReactElement {
</Tooltip> </Tooltip>
} }
/> />
<Typography>
Note that Market-TA.II overrides Market-TA.I. This means that if both are enabled, then Market-TA.II will take
effect, not Market-TA.I
</Typography>
</> </>
); );
} }
@ -83,6 +78,7 @@ interface IProps {
// Create a popup that lets the player use the Market TA research for Products // Create a popup that lets the player use the Market TA research for Products
export function ProductMarketTaModal(props: IProps): React.ReactElement { export function ProductMarketTaModal(props: IProps): React.ReactElement {
const division = useDivision();
const markupLimit = props.product.rat / props.product.mku; const markupLimit = props.product.rat / props.product.mku;
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
function rerender(): void { function rerender(): void {
@ -96,29 +92,32 @@ export function ProductMarketTaModal(props: IProps): React.ReactElement {
return ( return (
<Modal open={props.open} onClose={props.onClose}> <Modal open={props.open} onClose={props.onClose}>
<Typography variant="h4">Market-TA.I</Typography> {!division.hasResearch("Market-TA.II") && (
<Typography> <>
The maximum sale price you can mark this up to is{" "} <Typography variant="h4">Market-TA.I</Typography>
{numeralWrapper.formatMoney(props.product.pCost + markupLimit)}. This means that if you set the sale price <Typography>
higher than this, you will begin to experience a loss in number of sales The maximum sale price you can mark this up to is{" "}
</Typography> {numeralWrapper.formatMoney(props.product.pCost + markupLimit)}. This means that if you set the sale price
higher than this, you will begin to experience a loss in number of sales
</Typography>
<FormControlLabel <FormControlLabel
control={<Switch checked={props.product.marketTa1} onChange={onChange} />} control={<Switch checked={props.product.marketTa1} onChange={onChange} />}
label={ label={
<Tooltip <Tooltip
title={ title={
<Typography> <Typography>
If this is enabled, then this Material will automatically be sold at the price identified by Market-TA.I If this is enabled, then this Material will automatically be sold at the price identified by
(i.e. the price shown above) Market-TA.I (i.e. the price shown above)
</Typography> </Typography>
}
>
<Typography>Use Market-TA.I for Auto-Sale Price</Typography>
</Tooltip>
} }
> />
<Typography>Use Market-TA.I for Auto-Sale Price</Typography> </>
</Tooltip> )}
}
/>
<MarketTA2 product={props.product} /> <MarketTA2 product={props.product} />
</Modal> </Modal>
); );

@ -4,7 +4,7 @@ import { IPlayerOrSleeve } from "../PersonObjects/IPlayerOrSleeve";
import { IRouter } from "../ui/Router"; import { IRouter } from "../ui/Router";
import { WorkerScript } from "../Netscript/WorkerScript"; import { WorkerScript } from "../Netscript/WorkerScript";
export interface IConstructorParams { interface IConstructorParams {
hacking_success_weight?: number; hacking_success_weight?: number;
strength_success_weight?: number; strength_success_weight?: number;
defense_success_weight?: number; defense_success_weight?: number;

@ -24,7 +24,7 @@ export function loadFactions(saveString: string): void {
} }
} }
export function AddToFactions(faction: Faction): void { function AddToFactions(faction: Faction): void {
const name: string = faction.name; const name: string = faction.name;
Factions[name] = faction; Factions[name] = faction;
} }
@ -42,7 +42,7 @@ export function initFactions(): void {
//Resets a faction during (re-)initialization. Saves the favor in the new //Resets a faction during (re-)initialization. Saves the favor in the new
//Faction object and deletes the old Faction Object from "Factions". Then //Faction object and deletes the old Faction Object from "Factions". Then
//reinserts the new Faction object //reinserts the new Faction object
export function resetFaction(newFactionObject: Faction): void { function resetFaction(newFactionObject: Faction): void {
if (!(newFactionObject instanceof Faction)) { if (!(newFactionObject instanceof Faction)) {
throw new Error("Invalid argument 'newFactionObject' passed into resetFaction()"); throw new Error("Invalid argument 'newFactionObject' passed into resetFaction()");
} }

@ -5,7 +5,7 @@ import { ITaskParams } from "../ITaskParams";
* Defines the parameters that can be used to initialize and describe a GangMemberTask * Defines the parameters that can be used to initialize and describe a GangMemberTask
* (defined in Gang.js) * (defined in Gang.js)
*/ */
export interface IGangMemberTaskMetadata { interface IGangMemberTaskMetadata {
/** /**
* Description of the task * Description of the task
*/ */

@ -19,7 +19,7 @@ export enum UpgradeType {
* Defines the parameters that can be used to initialize and describe a GangMemberUpgrade * Defines the parameters that can be used to initialize and describe a GangMemberUpgrade
* (defined in Gang.js) * (defined in Gang.js)
*/ */
export interface IGangMemberUpgradeMetadata { interface IGangMemberUpgradeMetadata {
cost: number; cost: number;
mults: IMults; mults: IMults;
name: string; name: string;

@ -512,7 +512,6 @@ export function purchaseHashUpgrade(player: IPlayer, upgName: string, upgTarget:
const old = target.moneyMax; const old = target.moneyMax;
target.changeMaximumMoney(upg.value); target.changeMaximumMoney(upg.value);
console.log(target.moneyMax / old);
} catch (e) { } catch (e) {
player.hashManager.refundUpgrade(upgName); player.hashManager.refundUpgrade(upgName);
return false; return false;

@ -69,6 +69,7 @@ export function TechVendorLocation(props: IProps): React.ReactElement {
return ( return (
<> <>
<br />
{purchaseServerButtons} {purchaseServerButtons}
<br /> <br />
<Typography> <Typography>

@ -217,4 +217,4 @@ function initMessages(): void {
); );
} }
export { Messages, checkForMessagesToSend, sendMessage, showMessage, loadMessages, initMessages, Message }; export { Messages, checkForMessagesToSend, showMessage, loadMessages, initMessages };

@ -1,11 +0,0 @@
import { Milestone } from "./Milestone";
export class Quest {
title: string;
milestones: Milestone[];
constructor(title: string, milestones: Milestone[]) {
this.title = title;
this.milestones = milestones;
}
}

@ -106,6 +106,11 @@ export class WorkerScript {
*/ */
hostname: string; hostname: string;
/**
* Function called when the script ends.
*/
atExit: any;
constructor(runningScriptObj: RunningScript, pid: number, nsFuncsGenerator?: (ws: WorkerScript) => any) { constructor(runningScriptObj: RunningScript, pid: number, nsFuncsGenerator?: (ws: WorkerScript) => any) {
this.name = runningScriptObj.filename; this.name = runningScriptObj.filename;
this.hostname = runningScriptObj.server; this.hostname = runningScriptObj.server;
@ -132,15 +137,13 @@ export class WorkerScript {
if (!found) { if (!found) {
throw new Error(`WorkerScript constructed with invalid script filename: ${this.name}`); throw new Error(`WorkerScript constructed with invalid script filename: ${this.name}`);
} }
this.scriptRef = runningScriptObj;
this.args = runningScriptObj.args.slice();
this.env = new Environment(null); this.env = new Environment(null);
if (typeof nsFuncsGenerator === "function") { if (typeof nsFuncsGenerator === "function") {
this.env.vars = nsFuncsGenerator(this); this.env.vars = nsFuncsGenerator(this);
} }
this.env.set("args", runningScriptObj.args.slice()); this.env.set("args", runningScriptObj.args.slice());
this.scriptRef = runningScriptObj;
this.args = runningScriptObj.args.slice();
} }
/** /**

@ -10,6 +10,7 @@ import { RunningScript } from "../Script/RunningScript";
import { GetServer } from "../Server/AllServers"; import { GetServer } from "../Server/AllServers";
import { compareArrays } from "../utils/helpers/compareArrays"; import { compareArrays } from "../utils/helpers/compareArrays";
import { dialogBoxCreate } from "../ui/React/DialogBox";
export function killWorkerScript(runningScriptObj: RunningScript, hostname: string, rerenderUi?: boolean): boolean; export function killWorkerScript(runningScriptObj: RunningScript, hostname: string, rerenderUi?: boolean): boolean;
export function killWorkerScript(workerScript: WorkerScript): boolean; export function killWorkerScript(workerScript: WorkerScript): boolean;
@ -67,6 +68,16 @@ function killWorkerScriptByPid(pid: number, rerenderUi = true): boolean {
function stopAndCleanUpWorkerScript(workerScript: WorkerScript, rerenderUi = true): void { function stopAndCleanUpWorkerScript(workerScript: WorkerScript, rerenderUi = true): void {
workerScript.env.stopFlag = true; workerScript.env.stopFlag = true;
killNetscriptDelay(workerScript); killNetscriptDelay(workerScript);
if (typeof workerScript.atExit === "function") {
try {
workerScript.atExit();
} catch (e: any) {
dialogBoxCreate(
`Error trying to call atExit for script ${workerScript.name} on ${workerScript.hostname} ${workerScript.scriptRef.args} ${e}`,
);
}
workerScript.atExit = undefined;
}
removeWorkerScript(workerScript, rerenderUi); removeWorkerScript(workerScript, rerenderUi);
} }

File diff suppressed because it is too large Load Diff

@ -0,0 +1,224 @@
import { INetscriptHelper } from "./INetscriptHelper";
import { WorkerScript } from "../Netscript/WorkerScript";
import { IPlayer } from "../PersonObjects/IPlayer";
import { purchaseAugmentation } from "../Faction/FactionHelpers";
import { startWorkerScript } from "../NetscriptWorker";
import { Augmentation } from "../Augmentation/Augmentation";
import { Augmentations } from "../Augmentation/Augmentations";
import { augmentationExists, installAugmentations } from "../Augmentation/AugmentationHelpers";
import { prestigeAugmentation } from "../Prestige";
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { killWorkerScript } from "../Netscript/killWorkerScript";
import { CONSTANTS } from "../Constants";
import { isString } from "../utils/helpers/isString";
import { getRamCost } from "../Netscript/RamCostGenerator";
import { RunningScript } from "../Script/RunningScript";
export interface INetscriptAugmentations {
getOwnedAugmentations(purchased?: any): any;
getOwnedSourceFiles(): any;
getAugmentationsFromFaction(facname: any): any;
getAugmentationCost(name: any): any;
getAugmentationPrereq(name: any): any;
getAugmentationPrice(name: any): any;
getAugmentationRepReq(name: any): any;
getAugmentationStats(name: any): any;
purchaseAugmentation(faction: any, name: any): any;
softReset(cbScript: any): any;
installAugmentations(cbScript: any): any;
}
export function NetscriptAugmentations(
player: IPlayer,
workerScript: WorkerScript,
helper: INetscriptHelper,
): INetscriptAugmentations {
const getAugmentation = function (func: any, name: any): Augmentation {
if (!augmentationExists(name)) {
throw helper.makeRuntimeErrorMsg(func, `Invalid augmentation: '${name}'`);
}
return Augmentations[name];
};
const runAfterReset = function (cbScript = null): void {
//Run a script after reset
if (cbScript && isString(cbScript)) {
const home = player.getHomeComputer();
for (const script of home.scripts) {
if (script.filename === cbScript) {
const ramUsage = script.ramUsage;
const ramAvailable = home.maxRam - home.ramUsed;
if (ramUsage > ramAvailable) {
return; // Not enough RAM
}
const runningScriptObj = new RunningScript(script, []); // No args
runningScriptObj.threads = 1; // Only 1 thread
startWorkerScript(runningScriptObj, home);
}
}
}
};
return {
getOwnedAugmentations: function (purchased: any = false): any {
helper.updateDynamicRam("getOwnedAugmentations", getRamCost("getOwnedAugmentations"));
helper.checkSingularityAccess("getOwnedAugmentations", 3);
const res = [];
for (let i = 0; i < player.augmentations.length; ++i) {
res.push(player.augmentations[i].name);
}
if (purchased) {
for (let i = 0; i < player.queuedAugmentations.length; ++i) {
res.push(player.queuedAugmentations[i].name);
}
}
return res;
},
getOwnedSourceFiles: function (): any {
helper.updateDynamicRam("getOwnedSourceFiles", getRamCost("getOwnedSourceFiles"));
helper.checkSingularityAccess("getOwnedSourceFiles", 3);
const res = [];
for (let i = 0; i < player.sourceFiles.length; ++i) {
res.push({
n: player.sourceFiles[i].n,
lvl: player.sourceFiles[i].lvl,
});
}
return res;
},
getAugmentationsFromFaction: function (facname: any): any {
helper.updateDynamicRam("getAugmentationsFromFaction", getRamCost("getAugmentationsFromFaction"));
helper.checkSingularityAccess("getAugmentationsFromFaction", 3);
const faction = helper.getFaction("getAugmentationsFromFaction", facname);
// If player has a gang with this faction, return all augmentations.
if (player.hasGangWith(facname)) {
const res = [];
for (const augName in Augmentations) {
const aug = Augmentations[augName];
if (!aug.isSpecial) {
res.push(augName);
}
}
return res;
}
return faction.augmentations.slice();
},
getAugmentationCost: function (name: any): any {
helper.updateDynamicRam("getAugmentationCost", getRamCost("getAugmentationCost"));
helper.checkSingularityAccess("getAugmentationCost", 3);
const aug = getAugmentation("getAugmentationCost", name);
return [aug.baseRepRequirement, aug.baseCost];
},
getAugmentationPrereq: function (name: any): any {
helper.updateDynamicRam("getAugmentationPrereq", getRamCost("getAugmentationPrereq"));
helper.checkSingularityAccess("getAugmentationPrereq", 3);
const aug = getAugmentation("getAugmentationPrereq", name);
return aug.prereqs.slice();
},
getAugmentationPrice: function (name: any): any {
helper.updateDynamicRam("getAugmentationPrice", getRamCost("getAugmentationPrice"));
helper.checkSingularityAccess("getAugmentationPrice", 3);
const aug = getAugmentation("getAugmentationPrice", name);
return aug.baseCost;
},
getAugmentationRepReq: function (name: any): any {
helper.updateDynamicRam("getAugmentationRepReq", getRamCost("getAugmentationRepReq"));
helper.checkSingularityAccess("getAugmentationRepReq", 3);
const aug = getAugmentation("getAugmentationRepReq", name);
return aug.baseRepRequirement;
},
getAugmentationStats: function (name: any): any {
helper.updateDynamicRam("getAugmentationStats", getRamCost("getAugmentationStats"));
helper.checkSingularityAccess("getAugmentationStats", 3);
const aug = getAugmentation("getAugmentationStats", name);
return Object.assign({}, aug.mults);
},
purchaseAugmentation: function (faction: any, name: any): any {
helper.updateDynamicRam("purchaseAugmentation", getRamCost("purchaseAugmentation"));
helper.checkSingularityAccess("purchaseAugmentation", 3);
const fac = helper.getFaction("purchaseAugmentation", faction);
const aug = getAugmentation("purchaseAugmentation", name);
let augs = [];
if (player.hasGangWith(faction)) {
for (const augName in Augmentations) {
const tempAug = Augmentations[augName];
if (!tempAug.isSpecial) {
augs.push(augName);
}
}
} else {
augs = fac.augmentations;
}
if (!augs.includes(name)) {
workerScript.log("purchaseAugmentation", `Faction '${faction}' does not have the '${name}' augmentation.`);
return false;
}
const isNeuroflux = aug.name === AugmentationNames.NeuroFluxGovernor;
if (!isNeuroflux) {
for (let j = 0; j < player.queuedAugmentations.length; ++j) {
if (player.queuedAugmentations[j].name === aug.name) {
workerScript.log("purchaseAugmentation", `You already have the '${name}' augmentation.`);
return false;
}
}
for (let j = 0; j < player.augmentations.length; ++j) {
if (player.augmentations[j].name === aug.name) {
workerScript.log("purchaseAugmentation", `You already have the '${name}' augmentation.`);
return false;
}
}
}
if (fac.playerReputation < aug.baseRepRequirement) {
workerScript.log("purchaseAugmentation", `You do not have enough reputation with '${fac.name}'.`);
return false;
}
const res = purchaseAugmentation(aug, fac, true);
workerScript.log("purchaseAugmentation", res);
if (isString(res) && res.startsWith("You purchased")) {
player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain);
return true;
} else {
return false;
}
},
softReset: function (cbScript: any): any {
helper.updateDynamicRam("softReset", getRamCost("softReset"));
helper.checkSingularityAccess("softReset", 3);
workerScript.log("softReset", "Soft resetting. This will cause this script to be killed");
setTimeout(() => {
prestigeAugmentation();
runAfterReset(cbScript);
}, 0);
// Prevent workerScript from "finishing execution naturally"
workerScript.running = false;
killWorkerScript(workerScript);
},
installAugmentations: function (cbScript: any): any {
helper.updateDynamicRam("installAugmentations", getRamCost("installAugmentations"));
helper.checkSingularityAccess("installAugmentations", 3);
if (player.queuedAugmentations.length === 0) {
workerScript.log("installAugmentations", "You do not have any Augmentations to be installed.");
return false;
}
player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain);
workerScript.log("installAugmentations", "Installing Augmentations. This will cause this script to be killed");
setTimeout(() => {
installAugmentations();
runAfterReset(cbScript);
}, 0);
workerScript.running = false; // Prevent workerScript from "finishing execution naturally"
killWorkerScript(workerScript);
},
};
}

@ -0,0 +1,403 @@
import { INetscriptHelper } from "./INetscriptHelper";
import { WorkerScript } from "../Netscript/WorkerScript";
import { IPlayer } from "../PersonObjects/IPlayer";
import { Bladeburner } from "../Bladeburner/Bladeburner";
import { getRamCost } from "../Netscript/RamCostGenerator";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
export interface INetscriptBladeburner {
getContractNames(): any;
getOperationNames(): any;
getBlackOpNames(): any;
getBlackOpRank(name?: any): any;
getGeneralActionNames(): any;
getSkillNames(): any;
startAction(type?: any, name?: any): any;
stopBladeburnerAction(): any;
getCurrentAction(): any;
getActionTime(type?: any, name?: any): any;
getActionEstimatedSuccessChance(type?: any, name?: any): any;
getActionRepGain(type?: any, name?: any, level?: any): any;
getActionCountRemaining(type?: any, name?: any): any;
getActionMaxLevel(type?: any, name?: any): any;
getActionCurrentLevel(type?: any, name?: any): any;
getActionAutolevel(type?: any, name?: any): any;
setActionAutolevel(type?: any, name?: any, autoLevel?: any): any;
setActionLevel(type?: any, name?: any, level?: any): any;
getRank(): any;
getSkillPoints(): any;
getSkillLevel(skillName?: any): any;
getSkillUpgradeCost(skillName?: any): any;
upgradeSkill(skillName: any): any;
getTeamSize(type?: any, name?: any): any;
setTeamSize(type?: any, name?: any, size?: any): any;
getCityEstimatedPopulation(cityName: any): any;
getCityEstimatedCommunities(cityName: any): any;
getCityChaos(cityName: any): any;
getCity(): any;
switchCity(cityName: any): any;
getStamina(): any;
joinBladeburnerFaction(): any;
joinBladeburnerDivision(): any;
getBonusTime(): any;
}
export function NetscriptBladeburner(
player: IPlayer,
workerScript: WorkerScript,
helper: INetscriptHelper,
): INetscriptBladeburner {
const checkBladeburnerAccess = function (func: any, skipjoined: any = false): void {
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Must have joined bladeburner");
const apiAccess =
player.bitNodeN === 7 ||
player.sourceFiles.some((a) => {
return a.n === 7;
});
if (!apiAccess) {
const apiDenied = `You do not currently have access to the Bladeburner API. You must either be in BitNode-7 or have Source-File 7.`;
throw helper.makeRuntimeErrorMsg(`bladeburner.${func}`, apiDenied);
}
if (!skipjoined) {
const bladeburnerAccess = bladeburner instanceof Bladeburner;
if (!bladeburnerAccess) {
const bladeburnerDenied = `You must be a member of the Bladeburner division to use this API.`;
throw helper.makeRuntimeErrorMsg(`bladeburner.${func}`, bladeburnerDenied);
}
}
};
const checkBladeburnerCity = function (func: any, city: any): void {
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Must have joined bladeburner");
if (!bladeburner.cities.hasOwnProperty(city)) {
throw helper.makeRuntimeErrorMsg(`bladeburner.${func}`, `Invalid city: ${city}`);
}
};
const getBladeburnerActionObject = function (func: any, type: any, name: any): any {
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Must have joined bladeburner");
const actionId = bladeburner.getActionIdFromTypeAndName(type, name);
if (!actionId) {
throw helper.makeRuntimeErrorMsg(`bladeburner.${func}`, `Invalid action type='${type}', name='${name}'`);
}
const actionObj = bladeburner.getActionObject(actionId);
if (!actionObj) {
throw helper.makeRuntimeErrorMsg(`bladeburner.${func}`, `Invalid action type='${type}', name='${name}'`);
}
return actionObj;
};
return {
getContractNames: function (): any {
helper.updateDynamicRam("getContractNames", getRamCost("bladeburner", "getContractNames"));
checkBladeburnerAccess("getContractNames");
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.getContractNamesNetscriptFn();
},
getOperationNames: function (): any {
helper.updateDynamicRam("getOperationNames", getRamCost("bladeburner", "getOperationNames"));
checkBladeburnerAccess("getOperationNames");
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.getOperationNamesNetscriptFn();
},
getBlackOpNames: function (): any {
helper.updateDynamicRam("getBlackOpNames", getRamCost("bladeburner", "getBlackOpNames"));
checkBladeburnerAccess("getBlackOpNames");
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.getBlackOpNamesNetscriptFn();
},
getBlackOpRank: function (name: any = ""): any {
helper.updateDynamicRam("getBlackOpRank", getRamCost("bladeburner", "getBlackOpRank"));
checkBladeburnerAccess("getBlackOpRank");
const action: any = getBladeburnerActionObject("getBlackOpRank", "blackops", name);
return action.reqdRank;
},
getGeneralActionNames: function (): any {
helper.updateDynamicRam("getGeneralActionNames", getRamCost("bladeburner", "getGeneralActionNames"));
checkBladeburnerAccess("getGeneralActionNames");
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.getGeneralActionNamesNetscriptFn();
},
getSkillNames: function (): any {
helper.updateDynamicRam("getSkillNames", getRamCost("bladeburner", "getSkillNames"));
checkBladeburnerAccess("getSkillNames");
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.getSkillNamesNetscriptFn();
},
startAction: function (type: any = "", name: any = ""): any {
helper.updateDynamicRam("startAction", getRamCost("bladeburner", "startAction"));
checkBladeburnerAccess("startAction");
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try {
return bladeburner.startActionNetscriptFn(player, type, name, workerScript);
} catch (e: any) {
throw helper.makeRuntimeErrorMsg("bladeburner.startAction", e);
}
},
stopBladeburnerAction: function (): any {
helper.updateDynamicRam("stopBladeburnerAction", getRamCost("bladeburner", "stopBladeburnerAction"));
checkBladeburnerAccess("stopBladeburnerAction");
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.resetAction();
},
getCurrentAction: function (): any {
helper.updateDynamicRam("getCurrentAction", getRamCost("bladeburner", "getCurrentAction"));
checkBladeburnerAccess("getCurrentAction");
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.getTypeAndNameFromActionId(bladeburner.action);
},
getActionTime: function (type: any = "", name: any = ""): any {
helper.updateDynamicRam("getActionTime", getRamCost("bladeburner", "getActionTime"));
checkBladeburnerAccess("getActionTime");
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try {
return bladeburner.getActionTimeNetscriptFn(player, type, name, workerScript);
} catch (e: any) {
throw helper.makeRuntimeErrorMsg("bladeburner.getActionTime", e);
}
},
getActionEstimatedSuccessChance: function (type: any = "", name: any = ""): any {
helper.updateDynamicRam(
"getActionEstimatedSuccessChance",
getRamCost("bladeburner", "getActionEstimatedSuccessChance"),
);
checkBladeburnerAccess("getActionEstimatedSuccessChance");
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try {
return bladeburner.getActionEstimatedSuccessChanceNetscriptFn(player, type, name, workerScript);
} catch (e: any) {
throw helper.makeRuntimeErrorMsg("bladeburner.getActionEstimatedSuccessChance", e);
}
},
getActionRepGain: function (type: any = "", name: any = "", level: any): any {
helper.updateDynamicRam("getActionRepGain", getRamCost("bladeburner", "getActionRepGain"));
checkBladeburnerAccess("getActionRepGain");
const action = getBladeburnerActionObject("getActionRepGain", type, name);
let rewardMultiplier;
if (level == null || isNaN(level)) {
rewardMultiplier = Math.pow(action.rewardFac, action.level - 1);
} else {
rewardMultiplier = Math.pow(action.rewardFac, level - 1);
}
return action.rankGain * rewardMultiplier * BitNodeMultipliers.BladeburnerRank;
},
getActionCountRemaining: function (type: any = "", name: any = ""): any {
helper.updateDynamicRam("getActionCountRemaining", getRamCost("bladeburner", "getActionCountRemaining"));
checkBladeburnerAccess("getActionCountRemaining");
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try {
return bladeburner.getActionCountRemainingNetscriptFn(type, name, workerScript);
} catch (e: any) {
throw helper.makeRuntimeErrorMsg("bladeburner.getActionCountRemaining", e);
}
},
getActionMaxLevel: function (type: any = "", name: any = ""): any {
helper.updateDynamicRam("getActionMaxLevel", getRamCost("bladeburner", "getActionMaxLevel"));
checkBladeburnerAccess("getActionMaxLevel");
const action = getBladeburnerActionObject("getActionMaxLevel", type, name);
return action.maxLevel;
},
getActionCurrentLevel: function (type: any = "", name: any = ""): any {
helper.updateDynamicRam("getActionCurrentLevel", getRamCost("bladeburner", "getActionCurrentLevel"));
checkBladeburnerAccess("getActionCurrentLevel");
const action = getBladeburnerActionObject("getActionCurrentLevel", type, name);
return action.level;
},
getActionAutolevel: function (type: any = "", name: any = ""): any {
helper.updateDynamicRam("getActionAutolevel", getRamCost("bladeburner", "getActionAutolevel"));
checkBladeburnerAccess("getActionAutolevel");
const action = getBladeburnerActionObject("getActionCurrentLevel", type, name);
return action.autoLevel;
},
setActionAutolevel: function (type: any = "", name: any = "", autoLevel: any = true): any {
helper.updateDynamicRam("setActionAutolevel", getRamCost("bladeburner", "setActionAutolevel"));
checkBladeburnerAccess("setActionAutolevel");
const action = getBladeburnerActionObject("setActionAutolevel", type, name);
action.autoLevel = autoLevel;
},
setActionLevel: function (type: any = "", name: any = "", level: any = 1): any {
helper.updateDynamicRam("setActionLevel", getRamCost("bladeburner", "setActionLevel"));
checkBladeburnerAccess("setActionLevel");
const action = getBladeburnerActionObject("setActionLevel", type, name);
if (level < 1 || level > action.maxLevel) {
throw helper.makeRuntimeErrorMsg(
"bladeburner.setActionLevel",
`Level must be between 1 and ${action.maxLevel}, is ${level}`,
);
}
action.level = level;
},
getRank: function (): any {
helper.updateDynamicRam("getRank", getRamCost("bladeburner", "getRank"));
checkBladeburnerAccess("getRank");
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.rank;
},
getSkillPoints: function (): any {
helper.updateDynamicRam("getSkillPoints", getRamCost("bladeburner", "getSkillPoints"));
checkBladeburnerAccess("getSkillPoints");
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.skillPoints;
},
getSkillLevel: function (skillName: any = ""): any {
helper.updateDynamicRam("getSkillLevel", getRamCost("bladeburner", "getSkillLevel"));
checkBladeburnerAccess("getSkillLevel");
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try {
return bladeburner.getSkillLevelNetscriptFn(skillName, workerScript);
} catch (e: any) {
throw helper.makeRuntimeErrorMsg("bladeburner.getSkillLevel", e);
}
},
getSkillUpgradeCost: function (skillName: any = ""): any {
helper.updateDynamicRam("getSkillUpgradeCost", getRamCost("bladeburner", "getSkillUpgradeCost"));
checkBladeburnerAccess("getSkillUpgradeCost");
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try {
return bladeburner.getSkillUpgradeCostNetscriptFn(skillName, workerScript);
} catch (e: any) {
throw helper.makeRuntimeErrorMsg("bladeburner.getSkillUpgradeCost", e);
}
},
upgradeSkill: function (skillName: any): any {
helper.updateDynamicRam("upgradeSkill", getRamCost("bladeburner", "upgradeSkill"));
checkBladeburnerAccess("upgradeSkill");
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try {
return bladeburner.upgradeSkillNetscriptFn(skillName, workerScript);
} catch (e: any) {
throw helper.makeRuntimeErrorMsg("bladeburner.upgradeSkill", e);
}
},
getTeamSize: function (type: any = "", name: any = ""): any {
helper.updateDynamicRam("getTeamSize", getRamCost("bladeburner", "getTeamSize"));
checkBladeburnerAccess("getTeamSize");
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try {
return bladeburner.getTeamSizeNetscriptFn(type, name, workerScript);
} catch (e: any) {
throw helper.makeRuntimeErrorMsg("bladeburner.getTeamSize", e);
}
},
setTeamSize: function (type: any = "", name: any = "", size: any): any {
helper.updateDynamicRam("setTeamSize", getRamCost("bladeburner", "setTeamSize"));
checkBladeburnerAccess("setTeamSize");
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try {
return bladeburner.setTeamSizeNetscriptFn(type, name, size, workerScript);
} catch (e: any) {
throw helper.makeRuntimeErrorMsg("bladeburner.setTeamSize", e);
}
},
getCityEstimatedPopulation: function (cityName: any): any {
helper.updateDynamicRam("getCityEstimatedPopulation", getRamCost("bladeburner", "getCityEstimatedPopulation"));
checkBladeburnerAccess("getCityEstimatedPopulation");
checkBladeburnerCity("getCityEstimatedPopulation", cityName);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.cities[cityName].popEst;
},
getCityEstimatedCommunities: function (cityName: any): any {
helper.updateDynamicRam("getCityEstimatedCommunities", getRamCost("bladeburner", "getCityEstimatedCommunities"));
checkBladeburnerAccess("getCityEstimatedCommunities");
checkBladeburnerCity("getCityEstimatedCommunities", cityName);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.cities[cityName].commsEst;
},
getCityChaos: function (cityName: any): any {
helper.updateDynamicRam("getCityChaos", getRamCost("bladeburner", "getCityChaos"));
checkBladeburnerAccess("getCityChaos");
checkBladeburnerCity("getCityChaos", cityName);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.cities[cityName].chaos;
},
getCity: function (): any {
helper.updateDynamicRam("getCity", getRamCost("bladeburner", "getCity"));
checkBladeburnerAccess("getCityChaos");
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.city;
},
switchCity: function (cityName: any): any {
helper.updateDynamicRam("switchCity", getRamCost("bladeburner", "switchCity"));
checkBladeburnerAccess("switchCity");
checkBladeburnerCity("switchCity", cityName);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return (bladeburner.city = cityName);
},
getStamina: function (): any {
helper.updateDynamicRam("getStamina", getRamCost("bladeburner", "getStamina"));
checkBladeburnerAccess("getStamina");
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return [bladeburner.stamina, bladeburner.maxStamina];
},
joinBladeburnerFaction: function (): any {
helper.updateDynamicRam("joinBladeburnerFaction", getRamCost("bladeburner", "joinBladeburnerFaction"));
checkBladeburnerAccess("joinBladeburnerFaction", true);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.joinBladeburnerFactionNetscriptFn(workerScript);
},
joinBladeburnerDivision: function (): any {
helper.updateDynamicRam("joinBladeburnerDivision", getRamCost("bladeburner", "joinBladeburnerDivision"));
if (player.bitNodeN === 7 || player.sourceFileLvl(7) > 0) {
if (player.bitNodeN === 8) {
return false;
}
if (player.bladeburner instanceof Bladeburner) {
return true; // Already member
} else if (
player.strength >= 100 &&
player.defense >= 100 &&
player.dexterity >= 100 &&
player.agility >= 100
) {
player.bladeburner = new Bladeburner(player);
workerScript.log("joinBladeburnerDivision", "You have been accepted into the Bladeburner division");
return true;
} else {
workerScript.log(
"joinBladeburnerDivision",
"You do not meet the requirements for joining the Bladeburner division",
);
return false;
}
}
},
getBonusTime: function (): any {
helper.updateDynamicRam("getBonusTime", getRamCost("bladeburner", "getBonusTime"));
checkBladeburnerAccess("getBonusTime");
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return Math.round(bladeburner.storedCycles / 5);
},
};
}

@ -0,0 +1,109 @@
import { INetscriptHelper } from "./INetscriptHelper";
import { WorkerScript } from "../Netscript/WorkerScript";
import { IPlayer } from "../PersonObjects/IPlayer";
import { getRamCost } from "../Netscript/RamCostGenerator";
import { is2DArray } from "../utils/helpers/is2DArray";
import { CodingContract } from "../CodingContracts";
export interface INetscriptCodingContract {
attempt(answer: any, fn: any, ip?: any, options?: { returnReward: any }): any;
getContractType(fn: any, ip?: any): any;
getData(fn: any, ip?: any): any;
getDescription(fn: any, ip?: any): any;
getNumTriesRemaining(fn: any, ip?: any): any;
}
export function NetscriptCodingContract(
player: IPlayer,
workerScript: WorkerScript,
helper: INetscriptHelper,
): INetscriptCodingContract {
const getCodingContract = function (func: any, ip: any, fn: any): CodingContract {
const server = helper.getServer(ip, func);
const contract = server.getContract(fn);
if (contract == null) {
throw helper.makeRuntimeErrorMsg(`codingcontract.${func}`, `Cannot find contract '${fn}' on server '${ip}'`);
}
return contract;
};
return {
attempt: function (answer: any, fn: any, ip: any = workerScript.hostname, { returnReward }: any = {}): any {
helper.updateDynamicRam("attempt", getRamCost("codingcontract", "attempt"));
const contract = getCodingContract("attempt", ip, fn);
// Convert answer to string. If the answer is a 2D array, then we have to
// manually add brackets for the inner arrays
if (is2DArray(answer)) {
const answerComponents = [];
for (let i = 0; i < answer.length; ++i) {
answerComponents.push(["[", answer[i].toString(), "]"].join(""));
}
answer = answerComponents.join(",");
} else {
answer = String(answer);
}
const creward = contract.reward;
if (creward === null) throw new Error("Somehow solved a contract that didn't have a reward");
const serv = helper.getServer(ip, "codingcontract.attempt");
if (contract.isSolution(answer)) {
const reward = player.gainCodingContractReward(creward, contract.getDifficulty());
workerScript.log("attempt", `Successfully completed Coding Contract '${fn}'. Reward: ${reward}`);
serv.removeContract(fn);
return returnReward ? reward : true;
} else {
++contract.tries;
if (contract.tries >= contract.getMaxNumTries()) {
workerScript.log("attempt", `Coding Contract attempt '${fn}' failed. Contract is now self-destructing`);
serv.removeContract(fn);
} else {
workerScript.log(
"attempt",
`Coding Contract attempt '${fn}' failed. ${contract.getMaxNumTries() - contract.tries} attempts remaining.`,
);
}
return returnReward ? "" : false;
}
},
getContractType: function (fn: any, ip: any = workerScript.hostname): any {
helper.updateDynamicRam("getContractType", getRamCost("codingcontract", "getContractType"));
const contract = getCodingContract("getContractType", ip, fn);
return contract.getType();
},
getData: function (fn: any, ip: any = workerScript.hostname): any {
helper.updateDynamicRam("getData", getRamCost("codingcontract", "getData"));
const contract = getCodingContract("getData", ip, fn);
const data = contract.getData();
if (data.constructor === Array) {
// For two dimensional arrays, we have to copy the internal arrays using
// slice() as well. As of right now, no contract has arrays that have
// more than two dimensions
const copy = data.slice();
for (let i = 0; i < copy.length; ++i) {
if (data[i].constructor === Array) {
copy[i] = data[i].slice();
}
}
return copy;
} else {
return data;
}
},
getDescription: function (fn: any, ip: any = workerScript.hostname): any {
helper.updateDynamicRam("getDescription", getRamCost("codingcontract", "getDescription"));
const contract = getCodingContract("getDescription", ip, fn);
return contract.getDescription();
},
getNumTriesRemaining: function (fn: any, ip: any = workerScript.hostname): any {
helper.updateDynamicRam("getNumTriesRemaining", getRamCost("codingcontract", "getNumTriesRemaining"));
const contract = getCodingContract("getNumTriesRemaining", ip, fn);
return contract.getMaxNumTries() - contract.tries;
},
};
}

@ -0,0 +1,305 @@
import { INetscriptHelper } from "./INetscriptHelper";
import { WorkerScript } from "../Netscript/WorkerScript";
import { IPlayer } from "../PersonObjects/IPlayer";
import { OfficeSpace } from "../Corporation/OfficeSpace";
import { Employee } from "../Corporation/Employee";
import { Product } from "../Corporation/Product";
import { Material } from "../Corporation/Material";
import { Warehouse } from "../Corporation/Warehouse";
import { IIndustry } from "../Corporation/IIndustry";
import {
NewIndustry,
NewCity,
UnlockUpgrade,
LevelUpgrade,
IssueDividends,
SellMaterial,
SellProduct,
SetSmartSupply,
BuyMaterial,
AssignJob,
UpgradeOfficeSize,
ThrowParty,
PurchaseWarehouse,
UpgradeWarehouse,
BuyCoffee,
HireAdVert,
MakeProduct,
Research,
ExportMaterial,
CancelExportMaterial,
SetMaterialMarketTA1,
SetMaterialMarketTA2,
SetProductMarketTA1,
SetProductMarketTA2,
} from "../Corporation/Actions";
import { CorporationUnlockUpgrades } from "../Corporation/data/CorporationUnlockUpgrades";
import { CorporationUpgrades } from "../Corporation/data/CorporationUpgrades";
export interface INetscriptCorporation {
expandIndustry(industryName: any, divisionName: any): any;
expandCity(divisionName: any, cityName: any): any;
unlockUpgrade(upgradeName: any): any;
levelUpgrade(upgradeName: any): any;
issueDividends(percent: any): any;
sellMaterial(divisionName: any, cityName: any, materialName: any, amt: any, price: any): any;
sellProduct(divisionName: any, cityName: any, productName: any, amt: any, price: any, all: any): any;
discontinueProduct(divisionName: any, productName: any): any;
setSmartSupply(divisionName: any, cityName: any, enabled: any): any;
buyMaterial(divisionName: any, cityName: any, materialName: any, amt: any): any;
employees(divisionName: any, cityName: any): any;
assignJob(divisionName: any, cityName: any, employeeName: any, job: any): any;
hireEmployee(divisionName: any, cityName: any): any;
upgradeOfficeSize(divisionName: any, cityName: any, size: any): any;
throwParty(divisionName: any, cityName: any, costPerEmployee: any): any;
purchaseWarehouse(divisionName: any, cityName: any): any;
upgradeWarehouse(divisionName: any, cityName: any): any;
buyCoffee(divisionName: any, cityName: any): any;
hireAdVert(divisionName: any): any;
makeProduct(divisionName: any, cityName: any, productName: any, designInvest: any, marketingInvest: any): any;
research(divisionName: any, researchName: any): any;
exportMaterial(
sourceDivision: any,
sourceCity: any,
targetDivision: any,
targetCity: any,
materialName: any,
amt: any,
): any;
cancelExportMaterial(
sourceDivision: any,
sourceCity: any,
targetDivision: any,
targetCity: any,
materialName: any,
amt: any,
): any;
setMaterialMarketTA1(divisionName: any, cityName: any, materialName: any, on: any): any;
setMaterialMarketTA2(divisionName: any, cityName: any, materialName: any, on: any): any;
setProductMarketTA1(divisionName: any, productName: any, on: any): any;
setProductMarketTA2(divisionName: any, productName: any, on: any): any;
getDivision(divisionName: any): any;
getOffice(divisionName: any, cityName: any): any;
getWarehouse(divisionName: any, cityName: any): any;
getMaterial(divisionName: any, cityName: any, materialName: any): any;
getProduct(divisionName: any, productName: any): any;
getEmployee(divisionName: any, cityName: any, employeeName: any): any;
}
export function NetscriptCorporation(
player: IPlayer,
workerScript: WorkerScript,
helper: INetscriptHelper,
): INetscriptCorporation {
function getDivision(divisionName: any): IIndustry {
const corporation = player.corporation;
if (corporation === null) throw new Error("cannot be called without a corporation");
const division = corporation.divisions.find((div) => div.name === divisionName);
if (division === undefined) throw new Error(`No division named '${divisionName}'`);
return division;
}
function getOffice(divisionName: any, cityName: any): OfficeSpace {
const division = getDivision(divisionName);
if (!(cityName in division.offices)) throw new Error(`Invalid city name '${cityName}'`);
const office = division.offices[cityName];
if (office === 0) throw new Error(`${division.name} has not expanded to '${cityName}'`);
return office;
}
function getWarehouse(divisionName: any, cityName: any): Warehouse {
const division = getDivision(divisionName);
if (!(cityName in division.warehouses)) throw new Error(`Invalid city name '${cityName}'`);
const warehouse = division.warehouses[cityName];
if (warehouse === 0) throw new Error(`${division.name} has not expanded to '${cityName}'`);
return warehouse;
}
function getMaterial(divisionName: any, cityName: any, materialName: any): Material {
const warehouse = getWarehouse(divisionName, cityName);
const material = warehouse.materials[materialName];
if (material === undefined) throw new Error(`Invalid material name: '${materialName}'`);
return material;
}
function getProduct(divisionName: any, productName: any): Product {
const division = getDivision(divisionName);
const product = division.products[productName];
if (product === undefined) throw new Error(`Invalid product name: '${productName}'`);
return product;
}
function getEmployee(divisionName: any, cityName: any, employeeName: any): Employee {
const office = getOffice(divisionName, cityName);
const employee = office.employees.find((e) => e.name === employeeName);
if (employee === undefined) throw new Error(`Invalid employee name: '${employeeName}'`);
return employee;
}
// Hi, if you're reading this you're a bit nosy.
// There's a corporation API but it's very imbalanced right now.
// It's here so players can test with if they want.
return {
expandIndustry: function (industryName: any, divisionName: any): any {
const corporation = player.corporation;
if (corporation === null) throw new Error("Should not be called without a corporation");
NewIndustry(corporation, industryName, divisionName);
},
expandCity: function (divisionName: any, cityName: any): any {
const division = getDivision(divisionName);
const corporation = player.corporation;
if (corporation === null) throw new Error("Should not be called without a corporation");
NewCity(corporation, division, cityName);
},
unlockUpgrade: function (upgradeName: any): any {
const upgrade = Object.values(CorporationUnlockUpgrades).find((upgrade) => upgrade[2] === upgradeName);
if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`);
const corporation = player.corporation;
if (corporation === null) throw new Error("Should not be called without a corporation");
UnlockUpgrade(corporation, upgrade);
},
levelUpgrade: function (upgradeName: any): any {
const upgrade = Object.values(CorporationUpgrades).find((upgrade) => upgrade[4] === upgradeName);
if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`);
const corporation = player.corporation;
if (corporation === null) throw new Error("Should not be called without a corporation");
LevelUpgrade(corporation, upgrade);
},
issueDividends: function (percent: any): any {
const corporation = player.corporation;
if (corporation === null) throw new Error("Should not be called without a corporation");
IssueDividends(corporation, percent);
},
sellMaterial: function (divisionName: any, cityName: any, materialName: any, amt: any, price: any): any {
const material = getMaterial(divisionName, cityName, materialName);
SellMaterial(material, amt, price);
},
sellProduct: function (divisionName: any, cityName: any, productName: any, amt: any, price: any, all: any): any {
const product = getProduct(divisionName, productName);
SellProduct(product, cityName, amt, price, all);
},
discontinueProduct: function (divisionName: any, productName: any): any {
getDivision(divisionName).discontinueProduct(getProduct(divisionName, productName));
},
setSmartSupply: function (divisionName: any, cityName: any, enabled: any): any {
const warehouse = getWarehouse(divisionName, cityName);
SetSmartSupply(warehouse, enabled);
},
// setSmartSupplyUseLeftovers: function (): any {},
buyMaterial: function (divisionName: any, cityName: any, materialName: any, amt: any): any {
const material = getMaterial(divisionName, cityName, materialName);
BuyMaterial(material, amt);
},
employees: function (divisionName: any, cityName: any): any {
const office = getOffice(divisionName, cityName);
return office.employees.map((e) => Object.assign({}, e));
},
assignJob: function (divisionName: any, cityName: any, employeeName: any, job: any): any {
const employee = getEmployee(divisionName, cityName, employeeName);
AssignJob(employee, job);
},
hireEmployee: function (divisionName: any, cityName: any): any {
const office = getOffice(divisionName, cityName);
office.hireRandomEmployee();
},
upgradeOfficeSize: function (divisionName: any, cityName: any, size: any): any {
const office = getOffice(divisionName, cityName);
const corporation = player.corporation;
if (corporation === null) throw new Error("Should not be called without a corporation");
UpgradeOfficeSize(corporation, office, size);
},
throwParty: function (divisionName: any, cityName: any, costPerEmployee: any): any {
const office = getOffice(divisionName, cityName);
const corporation = player.corporation;
if (corporation === null) throw new Error("Should not be called without a corporation");
ThrowParty(corporation, office, costPerEmployee);
},
purchaseWarehouse: function (divisionName: any, cityName: any): any {
const corporation = player.corporation;
if (corporation === null) throw new Error("Should not be called without a corporation");
PurchaseWarehouse(corporation, getDivision(divisionName), cityName);
},
upgradeWarehouse: function (divisionName: any, cityName: any): any {
const corporation = player.corporation;
if (corporation === null) throw new Error("Should not be called without a corporation");
UpgradeWarehouse(corporation, getDivision(divisionName), getWarehouse(divisionName, cityName));
},
buyCoffee: function (divisionName: any, cityName: any): any {
const corporation = player.corporation;
if (corporation === null) throw new Error("Should not be called without a corporation");
BuyCoffee(corporation, getDivision(divisionName), getOffice(divisionName, cityName));
},
hireAdVert: function (divisionName: any): any {
const corporation = player.corporation;
if (corporation === null) throw new Error("Should not be called without a corporation");
HireAdVert(corporation, getDivision(divisionName), getOffice(divisionName, "Sector-12"));
},
makeProduct: function (
divisionName: any,
cityName: any,
productName: any,
designInvest: any,
marketingInvest: any,
): any {
const corporation = player.corporation;
if (corporation === null) throw new Error("Should not be called without a corporation");
MakeProduct(corporation, getDivision(divisionName), cityName, productName, designInvest, marketingInvest);
},
research: function (divisionName: any, researchName: any): any {
Research(getDivision(divisionName), researchName);
},
exportMaterial: function (
sourceDivision: any,
sourceCity: any,
targetDivision: any,
targetCity: any,
materialName: any,
amt: any,
): any {
ExportMaterial(targetDivision, targetCity, getMaterial(sourceDivision, sourceCity, materialName), amt + "");
},
cancelExportMaterial: function (
sourceDivision: any,
sourceCity: any,
targetDivision: any,
targetCity: any,
materialName: any,
amt: any,
): any {
CancelExportMaterial(targetDivision, targetCity, getMaterial(sourceDivision, sourceCity, materialName), amt + "");
},
setMaterialMarketTA1: function (divisionName: any, cityName: any, materialName: any, on: any): any {
SetMaterialMarketTA1(getMaterial(divisionName, cityName, materialName), on);
},
setMaterialMarketTA2: function (divisionName: any, cityName: any, materialName: any, on: any) {
SetMaterialMarketTA2(getMaterial(divisionName, cityName, materialName), on);
},
setProductMarketTA1: function (divisionName: any, productName: any, on: any): any {
SetProductMarketTA1(getProduct(divisionName, productName), on);
},
setProductMarketTA2: function (divisionName: any, productName: any, on: any) {
SetProductMarketTA2(getProduct(divisionName, productName), on);
},
// If you modify these objects you will affect them for real, it's not
// copies.
getDivision: function (divisionName: any): any {
return getDivision(divisionName);
},
getOffice: function (divisionName: any, cityName: any): any {
return getOffice(divisionName, cityName);
},
getWarehouse: function (divisionName: any, cityName: any): any {
return getWarehouse(divisionName, cityName);
},
getMaterial: function (divisionName: any, cityName: any, materialName: any): any {
return getMaterial(divisionName, cityName, materialName);
},
getProduct: function (divisionName: any, productName: any): any {
return getProduct(divisionName, productName);
},
getEmployee: function (divisionName: any, cityName: any, employeeName: any): any {
return getEmployee(divisionName, cityName, employeeName);
},
};
}

@ -0,0 +1,37 @@
import { toNative } from "./toNative";
import * as libarg from "arg";
export function Flags(vargs: string[]): any {
return function (data: any): any {
data = toNative(data);
// We always want the help flag.
const args: {
[key: string]: any;
} = {};
for (const d of data) {
let t: any = String;
if (typeof d[1] === "number") {
t = Number;
} else if (typeof d[1] === "boolean") {
t = Boolean;
} else if (Array.isArray(d[1])) {
t = [String];
}
const numDashes = d[0].length > 1 ? 2 : 1;
args["-".repeat(numDashes) + d[0]] = t;
}
const ret = libarg(args, { argv: vargs });
for (const d of data) {
if (!ret.hasOwnProperty("--" + d[0]) || !ret.hasOwnProperty("-" + d[0])) ret[d[0]] = d[1];
}
for (const key of Object.keys(ret)) {
if (!key.startsWith("-")) continue;
const value = ret[key];
delete ret[key];
const numDashes = key.length === 2 ? 1 : 2;
ret[key.slice(numDashes)] = value;
}
return ret;
};
}

@ -0,0 +1,187 @@
import { INetscriptHelper } from "./INetscriptHelper";
import { WorkerScript } from "../Netscript/WorkerScript";
import { IPlayer } from "../PersonObjects/IPlayer";
import { calculateServerGrowth } from "../Server/formulas/grow";
import {
calculateMoneyGainRate,
calculateLevelUpgradeCost,
calculateRamUpgradeCost,
calculateCoreUpgradeCost,
calculateNodeCost,
} from "../Hacknet/formulas/HacknetNodes";
import {
calculateHashGainRate as HScalculateHashGainRate,
calculateLevelUpgradeCost as HScalculateLevelUpgradeCost,
calculateRamUpgradeCost as HScalculateRamUpgradeCost,
calculateCoreUpgradeCost as HScalculateCoreUpgradeCost,
calculateCacheUpgradeCost as HScalculateCacheUpgradeCost,
calculateServerCost as HScalculateServerCost,
} from "../Hacknet/formulas/HacknetServers";
import { HacknetNodeConstants, HacknetServerConstants } from "../Hacknet/data/Constants";
import { calculateSkill, calculateExp } from "../PersonObjects/formulas/skill";
import {
calculateHackingChance,
calculateHackingExpGain,
calculatePercentMoneyHacked,
calculateHackingTime,
calculateGrowTime,
calculateWeakenTime,
} from "../Hacking";
export interface INetscriptFormulas {
basic: {
calculateSkill(exp: any, mult?: any): any;
calculateExp(skill: any, mult?: any): any;
hackChance(server: any, player: any): any;
hackExp(server: any, player: any): any;
hackPercent(server: any, player: any): any;
growPercent(server: any, threads: any, player: any, cores?: any): any;
hackTime(server: any, player: any): any;
growTime(server: any, player: any): any;
weakenTime(server: any, player: any): any;
};
hacknetNodes: {
moneyGainRate(level: any, ram: any, cores: any, mult?: any): any;
levelUpgradeCost(startingLevel: any, extraLevels?: any, costMult?: any): any;
ramUpgradeCost(startingRam: any, extraLevels?: any, costMult?: any): any;
coreUpgradeCost(startingCore: any, extraCores?: any, costMult?: any): any;
hacknetNodeCost(n: any, mult: any): any;
constants(): any;
};
hacknetServers: {
hashGainRate(level: any, ramUsed: any, maxRam: any, cores: any, mult?: any): any;
levelUpgradeCost(startingLevel: any, extraLevels?: any, costMult?: any): any;
ramUpgradeCost(startingRam: any, extraLevels?: any, costMult?: any): any;
coreUpgradeCost(startingCore: any, extraCores?: any, costMult?: any): any;
cacheUpgradeCost(startingCache: any, extraCache?: any): any;
hashUpgradeCost(upgName: any, level: any): any;
hacknetServerCost(n: any, mult: any): any;
constants(): any;
};
}
export function NetscriptFormulas(
player: IPlayer,
workerScript: WorkerScript,
helper: INetscriptHelper,
): INetscriptFormulas {
const checkFormulasAccess = function (func: any, n: any): void {
if (
(player.sourceFileLvl(5) < 1 && player.bitNodeN !== 5) ||
(player.sourceFileLvl(n) < 1 && player.bitNodeN !== n)
) {
let extra = "";
if (n !== 5) {
extra = ` and Source-File ${n}-1`;
}
throw helper.makeRuntimeErrorMsg(`formulas.${func}`, `Requires Source-File 5-1${extra} to run.`);
}
};
return {
basic: {
calculateSkill: function (exp: any, mult: any = 1): any {
checkFormulasAccess("basic.calculateSkill", 5);
return calculateSkill(exp, mult);
},
calculateExp: function (skill: any, mult: any = 1): any {
checkFormulasAccess("basic.calculateExp", 5);
return calculateExp(skill, mult);
},
hackChance: function (server: any, player: any): any {
checkFormulasAccess("basic.hackChance", 5);
return calculateHackingChance(server, player);
},
hackExp: function (server: any, player: any): any {
checkFormulasAccess("basic.hackExp", 5);
return calculateHackingExpGain(server, player);
},
hackPercent: function (server: any, player: any): any {
checkFormulasAccess("basic.hackPercent", 5);
return calculatePercentMoneyHacked(server, player);
},
growPercent: function (server: any, threads: any, player: any, cores: any = 1): any {
checkFormulasAccess("basic.growPercent", 5);
return calculateServerGrowth(server, threads, player, cores);
},
hackTime: function (server: any, player: any): any {
checkFormulasAccess("basic.hackTime", 5);
return calculateHackingTime(server, player);
},
growTime: function (server: any, player: any): any {
checkFormulasAccess("basic.growTime", 5);
return calculateGrowTime(server, player);
},
weakenTime: function (server: any, player: any): any {
checkFormulasAccess("basic.weakenTime", 5);
return calculateWeakenTime(server, player);
},
},
hacknetNodes: {
moneyGainRate: function (level: any, ram: any, cores: any, mult: any = 1): any {
checkFormulasAccess("hacknetNodes.moneyGainRate", 5);
return calculateMoneyGainRate(level, ram, cores, mult);
},
levelUpgradeCost: function (startingLevel: any, extraLevels: any = 1, costMult: any = 1): any {
checkFormulasAccess("hacknetNodes.levelUpgradeCost", 5);
return calculateLevelUpgradeCost(startingLevel, extraLevels, costMult);
},
ramUpgradeCost: function (startingRam: any, extraLevels: any = 1, costMult: any = 1): any {
checkFormulasAccess("hacknetNodes.ramUpgradeCost", 5);
return calculateRamUpgradeCost(startingRam, extraLevels, costMult);
},
coreUpgradeCost: function (startingCore: any, extraCores: any = 1, costMult: any = 1): any {
checkFormulasAccess("hacknetNodes.coreUpgradeCost", 5);
return calculateCoreUpgradeCost(startingCore, extraCores, costMult);
},
hacknetNodeCost: function (n: any, mult: any): any {
checkFormulasAccess("hacknetNodes.hacknetNodeCost", 5);
return calculateNodeCost(n, mult);
},
constants: function (): any {
checkFormulasAccess("hacknetNodes.constants", 5);
return Object.assign({}, HacknetNodeConstants);
},
},
hacknetServers: {
hashGainRate: function (level: any, ramUsed: any, maxRam: any, cores: any, mult: any = 1): any {
checkFormulasAccess("hacknetServers.hashGainRate", 9);
return HScalculateHashGainRate(level, ramUsed, maxRam, cores, mult);
},
levelUpgradeCost: function (startingLevel: any, extraLevels: any = 1, costMult: any = 1): any {
checkFormulasAccess("hacknetServers.levelUpgradeCost", 9);
return HScalculateLevelUpgradeCost(startingLevel, extraLevels, costMult);
},
ramUpgradeCost: function (startingRam: any, extraLevels: any = 1, costMult: any = 1): any {
checkFormulasAccess("hacknetServers.ramUpgradeCost", 9);
return HScalculateRamUpgradeCost(startingRam, extraLevels, costMult);
},
coreUpgradeCost: function (startingCore: any, extraCores: any = 1, costMult: any = 1): any {
checkFormulasAccess("hacknetServers.coreUpgradeCost", 9);
return HScalculateCoreUpgradeCost(startingCore, extraCores, costMult);
},
cacheUpgradeCost: function (startingCache: any, extraCache: any = 1): any {
checkFormulasAccess("hacknetServers.cacheUpgradeCost", 9);
return HScalculateCacheUpgradeCost(startingCache, extraCache);
},
hashUpgradeCost: function (upgName: any, level: any): any {
checkFormulasAccess("hacknetServers.hashUpgradeCost", 9);
const upg = player.hashManager.getUpgrade(upgName);
if (!upg) {
throw helper.makeRuntimeErrorMsg(
"formulas.hacknetServers.calculateHashUpgradeCost",
`Invalid Hash Upgrade: ${upgName}`,
);
}
return upg.getCost(level);
},
hacknetServerCost: function (n: any, mult: any): any {
checkFormulasAccess("hacknetServers.hacknetServerCost", 9);
return HScalculateServerCost(n, mult);
},
constants: function (): any {
checkFormulasAccess("hacknetServers.constants", 9);
return Object.assign({}, HacknetServerConstants);
},
},
};
}

@ -1,7 +1,13 @@
import { BaseServer } from "../Server/BaseServer";
import { Faction } from "../Faction/Faction";
export interface INetscriptHelper { export interface INetscriptHelper {
updateDynamicRam(functionName: string, ram: number): void; updateDynamicRam(functionName: string, ram: number): void;
makeRuntimeErrorMsg(functionName: string, message: string): void; makeRuntimeErrorMsg(functionName: string, message: string): void;
string(funcName: string, argName: string, v: any): string; string(funcName: string, argName: string, v: any): string;
number(funcName: string, argName: string, v: any): number; number(funcName: string, argName: string, v: any): number;
boolean(v: any): boolean; boolean(v: any): boolean;
getServer(ip: any, fn: any): BaseServer;
checkSingularityAccess(func: string, n: number): void;
getFaction(func: string, name: string): Faction;
} }

@ -0,0 +1,377 @@
import { INetscriptHelper } from "./INetscriptHelper";
import { WorkerScript } from "../Netscript/WorkerScript";
import { IPlayer } from "../PersonObjects/IPlayer";
import { getRamCost } from "../Netscript/RamCostGenerator";
import { buyStock, sellStock, shortStock, sellShort } from "../StockMarket/BuyingAndSelling";
import { StockMarket, SymbolToStockMap, placeOrder, cancelOrder } from "../StockMarket/StockMarket";
import { getBuyTransactionCost, getSellTransactionGain } from "../StockMarket/StockMarketHelpers";
import { OrderTypes } from "../StockMarket/data/OrderTypes";
import { PositionTypes } from "../StockMarket/data/PositionTypes";
import { StockSymbols } from "../StockMarket/data/StockSymbols";
import { getStockMarket4SDataCost, getStockMarket4STixApiCost } from "../StockMarket/StockMarketCosts";
import { Stock } from "../StockMarket/Stock";
export interface INetscriptStockMarket {
getStockSymbols(): any;
getStockPrice(symbol: any): any;
getStockAskPrice(symbol: any): any;
getStockBidPrice(symbol: any): any;
getStockPosition(symbol: any): any;
getStockMaxShares(symbol: any): any;
getStockPurchaseCost(symbol: any, shares: any, posType: any): any;
getStockSaleGain(symbol: any, shares: any, posType: any): any;
buyStock(symbol: any, shares: any): any;
sellStock(symbol: any, shares: any): any;
shortStock(symbol: any, shares: any): any;
sellShort(symbol: any, shares: any): any;
placeOrder(symbol: any, shares: any, price: any, type: any, pos: any): any;
cancelOrder(symbol: any, shares: any, price: any, type: any, pos: any): any;
getOrders(): any;
getStockVolatility(symbol: any): any;
getStockForecast(symbol: any): any;
purchase4SMarketData(): void;
purchase4SMarketDataTixApi(): void;
}
export function NetscriptStockMarket(
player: IPlayer,
workerScript: WorkerScript,
helper: INetscriptHelper,
): INetscriptStockMarket {
/**
* Checks if the player has TIX API access. Throws an error if the player does not
*/
const checkTixApiAccess = function (callingFn: string): void {
if (!player.hasWseAccount) {
throw helper.makeRuntimeErrorMsg(callingFn, `You don't have WSE Access! Cannot use ${callingFn}()`);
}
if (!player.hasTixApiAccess) {
throw helper.makeRuntimeErrorMsg(callingFn, `You don't have TIX API Access! Cannot use ${callingFn}()`);
}
};
const getStockFromSymbol = function (symbol: string, callingFn: string): Stock {
const stock = SymbolToStockMap[symbol];
if (stock == null) {
throw helper.makeRuntimeErrorMsg(callingFn, `Invalid stock symbol: '${symbol}'`);
}
return stock;
};
return {
getStockSymbols: function (): any {
helper.updateDynamicRam("getStockSymbols", getRamCost("getStockSymbols"));
checkTixApiAccess("getStockSymbols");
return Object.values(StockSymbols);
},
getStockPrice: function (symbol: any): any {
helper.updateDynamicRam("getStockPrice", getRamCost("getStockPrice"));
checkTixApiAccess("getStockPrice");
const stock = getStockFromSymbol(symbol, "getStockPrice");
return stock.price;
},
getStockAskPrice: function (symbol: any): any {
helper.updateDynamicRam("getStockAskPrice", getRamCost("getStockAskPrice"));
checkTixApiAccess("getStockAskPrice");
const stock = getStockFromSymbol(symbol, "getStockAskPrice");
return stock.getAskPrice();
},
getStockBidPrice: function (symbol: any): any {
helper.updateDynamicRam("getStockBidPrice", getRamCost("getStockBidPrice"));
checkTixApiAccess("getStockBidPrice");
const stock = getStockFromSymbol(symbol, "getStockBidPrice");
return stock.getBidPrice();
},
getStockPosition: function (symbol: any): any {
helper.updateDynamicRam("getStockPosition", getRamCost("getStockPosition"));
checkTixApiAccess("getStockPosition");
const stock = SymbolToStockMap[symbol];
if (stock == null) {
throw helper.makeRuntimeErrorMsg("getStockPosition", `Invalid stock symbol: ${symbol}`);
}
return [stock.playerShares, stock.playerAvgPx, stock.playerShortShares, stock.playerAvgShortPx];
},
getStockMaxShares: function (symbol: any): any {
helper.updateDynamicRam("getStockMaxShares", getRamCost("getStockMaxShares"));
checkTixApiAccess("getStockMaxShares");
const stock = getStockFromSymbol(symbol, "getStockMaxShares");
return stock.maxShares;
},
getStockPurchaseCost: function (symbol: any, shares: any, posType: any): any {
helper.updateDynamicRam("getStockPurchaseCost", getRamCost("getStockPurchaseCost"));
checkTixApiAccess("getStockPurchaseCost");
const stock = getStockFromSymbol(symbol, "getStockPurchaseCost");
shares = Math.round(shares);
let pos;
const sanitizedPosType = posType.toLowerCase();
if (sanitizedPosType.includes("l")) {
pos = PositionTypes.Long;
} else if (sanitizedPosType.includes("s")) {
pos = PositionTypes.Short;
} else {
return Infinity;
}
const res = getBuyTransactionCost(stock, shares, pos);
if (res == null) {
return Infinity;
}
return res;
},
getStockSaleGain: function (symbol: any, shares: any, posType: any): any {
helper.updateDynamicRam("getStockSaleGain", getRamCost("getStockSaleGain"));
checkTixApiAccess("getStockSaleGain");
const stock = getStockFromSymbol(symbol, "getStockSaleGain");
shares = Math.round(shares);
let pos;
const sanitizedPosType = posType.toLowerCase();
if (sanitizedPosType.includes("l")) {
pos = PositionTypes.Long;
} else if (sanitizedPosType.includes("s")) {
pos = PositionTypes.Short;
} else {
return 0;
}
const res = getSellTransactionGain(stock, shares, pos);
if (res == null) {
return 0;
}
return res;
},
buyStock: function (symbol: any, shares: any): any {
helper.updateDynamicRam("buyStock", getRamCost("buyStock"));
checkTixApiAccess("buyStock");
const stock = getStockFromSymbol(symbol, "buyStock");
const res = buyStock(stock, shares, workerScript, {});
return res ? stock.price : 0;
},
sellStock: function (symbol: any, shares: any): any {
helper.updateDynamicRam("sellStock", getRamCost("sellStock"));
checkTixApiAccess("sellStock");
const stock = getStockFromSymbol(symbol, "sellStock");
const res = sellStock(stock, shares, workerScript, {});
return res ? stock.price : 0;
},
shortStock: function (symbol: any, shares: any): any {
helper.updateDynamicRam("shortStock", getRamCost("shortStock"));
checkTixApiAccess("shortStock");
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 1) {
throw helper.makeRuntimeErrorMsg(
"shortStock",
"You must either be in BitNode-8 or you must have Source-File 8 Level 2.",
);
}
}
const stock = getStockFromSymbol(symbol, "shortStock");
const res = shortStock(stock, shares, workerScript, {});
return res ? stock.price : 0;
},
sellShort: function (symbol: any, shares: any): any {
helper.updateDynamicRam("sellShort", getRamCost("sellShort"));
checkTixApiAccess("sellShort");
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 1) {
throw helper.makeRuntimeErrorMsg(
"sellShort",
"You must either be in BitNode-8 or you must have Source-File 8 Level 2.",
);
}
}
const stock = getStockFromSymbol(symbol, "sellShort");
const res = sellShort(stock, shares, workerScript, {});
return res ? stock.price : 0;
},
placeOrder: function (symbol: any, shares: any, price: any, type: any, pos: any): any {
helper.updateDynamicRam("placeOrder", getRamCost("placeOrder"));
checkTixApiAccess("placeOrder");
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 2) {
throw helper.makeRuntimeErrorMsg(
"placeOrder",
"You must either be in BitNode-8 or you must have Source-File 8 Level 3.",
);
}
}
const stock = getStockFromSymbol(symbol, "placeOrder");
let orderType;
let orderPos;
const ltype = type.toLowerCase();
if (ltype.includes("limit") && ltype.includes("buy")) {
orderType = OrderTypes.LimitBuy;
} else if (ltype.includes("limit") && ltype.includes("sell")) {
orderType = OrderTypes.LimitSell;
} else if (ltype.includes("stop") && ltype.includes("buy")) {
orderType = OrderTypes.StopBuy;
} else if (ltype.includes("stop") && ltype.includes("sell")) {
orderType = OrderTypes.StopSell;
} else {
throw helper.makeRuntimeErrorMsg("placeOrder", `Invalid order type: ${type}`);
}
const lpos = pos.toLowerCase();
if (lpos.includes("l")) {
orderPos = PositionTypes.Long;
} else if (lpos.includes("s")) {
orderPos = PositionTypes.Short;
} else {
throw helper.makeRuntimeErrorMsg("placeOrder", `Invalid position type: ${pos}`);
}
return placeOrder(stock, shares, price, orderType, orderPos, workerScript);
},
cancelOrder: function (symbol: any, shares: any, price: any, type: any, pos: any): any {
helper.updateDynamicRam("cancelOrder", getRamCost("cancelOrder"));
checkTixApiAccess("cancelOrder");
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 2) {
throw helper.makeRuntimeErrorMsg(
"cancelOrder",
"You must either be in BitNode-8 or you must have Source-File 8 Level 3.",
);
}
}
const stock = getStockFromSymbol(symbol, "cancelOrder");
if (isNaN(shares) || isNaN(price)) {
throw helper.makeRuntimeErrorMsg(
"cancelOrder",
`Invalid shares or price. Must be numeric. shares=${shares}, price=${price}`,
);
}
let orderType;
let orderPos;
const ltype = type.toLowerCase();
if (ltype.includes("limit") && ltype.includes("buy")) {
orderType = OrderTypes.LimitBuy;
} else if (ltype.includes("limit") && ltype.includes("sell")) {
orderType = OrderTypes.LimitSell;
} else if (ltype.includes("stop") && ltype.includes("buy")) {
orderType = OrderTypes.StopBuy;
} else if (ltype.includes("stop") && ltype.includes("sell")) {
orderType = OrderTypes.StopSell;
} else {
throw helper.makeRuntimeErrorMsg("cancelOrder", `Invalid order type: ${type}`);
}
const lpos = pos.toLowerCase();
if (lpos.includes("l")) {
orderPos = PositionTypes.Long;
} else if (lpos.includes("s")) {
orderPos = PositionTypes.Short;
} else {
throw helper.makeRuntimeErrorMsg("cancelOrder", `Invalid position type: ${pos}`);
}
const params = {
stock: stock,
shares: shares,
price: price,
type: orderType,
pos: orderPos,
};
return cancelOrder(params, workerScript);
},
getOrders: function (): any {
helper.updateDynamicRam("getOrders", getRamCost("getOrders"));
checkTixApiAccess("getOrders");
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 2) {
throw helper.makeRuntimeErrorMsg(
"getOrders",
"You must either be in BitNode-8 or have Source-File 8 Level 3.",
);
}
}
const orders: any = {};
const stockMarketOrders = StockMarket["Orders"];
for (const symbol in stockMarketOrders) {
const orderBook = stockMarketOrders[symbol];
if (orderBook.constructor === Array && orderBook.length > 0) {
orders[symbol] = [];
for (let i = 0; i < orderBook.length; ++i) {
orders[symbol].push({
shares: orderBook[i].shares,
price: orderBook[i].price,
type: orderBook[i].type,
position: orderBook[i].pos,
});
}
}
}
return orders;
},
getStockVolatility: function (symbol: any): any {
helper.updateDynamicRam("getStockVolatility", getRamCost("getStockVolatility"));
if (!player.has4SDataTixApi) {
throw helper.makeRuntimeErrorMsg("getStockVolatility", "You don't have 4S Market Data TIX API Access!");
}
const stock = getStockFromSymbol(symbol, "getStockVolatility");
return stock.mv / 100; // Convert from percentage to decimal
},
getStockForecast: function (symbol: any): any {
helper.updateDynamicRam("getStockForecast", getRamCost("getStockForecast"));
if (!player.has4SDataTixApi) {
throw helper.makeRuntimeErrorMsg("getStockForecast", "You don't have 4S Market Data TIX API Access!");
}
const stock = getStockFromSymbol(symbol, "getStockForecast");
let forecast = 50;
stock.b ? (forecast += stock.otlkMag) : (forecast -= stock.otlkMag);
return forecast / 100; // Convert from percentage to decimal
},
purchase4SMarketData: function () {
helper.updateDynamicRam("purchase4SMarketData", getRamCost("purchase4SMarketData"));
checkTixApiAccess("purchase4SMarketData");
if (player.has4SData) {
workerScript.log("purchase4SMarketData", "Already purchased 4S Market Data.");
return true;
}
if (player.money.lt(getStockMarket4SDataCost())) {
workerScript.log("purchase4SMarketData", "Not enough money to purchase 4S Market Data.");
return false;
}
player.has4SData = true;
player.loseMoney(getStockMarket4SDataCost());
workerScript.log("purchase4SMarketData", "Purchased 4S Market Data");
return true;
},
purchase4SMarketDataTixApi: function () {
helper.updateDynamicRam("purchase4SMarketDataTixApi", getRamCost("purchase4SMarketDataTixApi"));
checkTixApiAccess("purchase4SMarketDataTixApi");
if (player.has4SDataTixApi) {
workerScript.log("purchase4SMarketDataTixApi", "Already purchased 4S Market Data TIX API");
return true;
}
if (player.money.lt(getStockMarket4STixApiCost())) {
workerScript.log("purchase4SMarketDataTixApi", "Not enough money to purchase 4S Market Data TIX API");
return false;
}
player.has4SDataTixApi = true;
player.loseMoney(getStockMarket4STixApiCost());
workerScript.log("purchase4SMarketDataTixApi", "Purchased 4S Market Data TIX API");
return true;
},
};
}

@ -0,0 +1,36 @@
import { Interpreter } from "../ThirdParty/JSInterpreter";
const defaultInterpreter = new Interpreter("", () => undefined);
// the acorn interpreter has a bug where it doesn't convert arrays correctly.
// so we have to more or less copy it here.
export function toNative(pseudoObj: any): any {
if (pseudoObj == null) return null;
if (
!pseudoObj.hasOwnProperty("properties") ||
!pseudoObj.hasOwnProperty("getter") ||
!pseudoObj.hasOwnProperty("setter") ||
!pseudoObj.hasOwnProperty("proto")
) {
return pseudoObj; // it wasn't a pseudo object anyway.
}
let nativeObj: any;
if (pseudoObj.hasOwnProperty("class") && pseudoObj.class === "Array") {
nativeObj = [];
const length = defaultInterpreter.getProperty(pseudoObj, "length");
for (let i = 0; i < length; i++) {
if (defaultInterpreter.hasProperty(pseudoObj, i)) {
nativeObj[i] = toNative(defaultInterpreter.getProperty(pseudoObj, i));
}
}
} else {
// Object.
nativeObj = {};
for (const key in pseudoObj.properties) {
const val = pseudoObj.properties[key];
nativeObj[key] = toNative(val);
}
}
return nativeObj;
}

@ -8,6 +8,22 @@ function makeScriptBlob(code: string): Blob {
return new Blob([code], { type: "text/javascript" }); return new Blob([code], { type: "text/javascript" });
} }
export function compile(script: Script, scripts: Script[]): void {
if (!shouldCompile(script, scripts)) return;
// The URL at the top is the one we want to import. It will
// recursively import all the other modules in the urlStack.
//
// Webpack likes to turn the import into a require, which sort of
// but not really behaves like import. Particularly, it cannot
// load fully dynamic content. So we hide the import from webpack
// by placing it inside an eval call.
script.markUpdated();
const uurls = _getScriptUrls(script, scripts, []);
script.url = uurls[uurls.length - 1].url;
script.module = new Promise((resolve) => resolve(eval("import(uurls[uurls.length - 1].url)")));
script.dependencies = uurls;
}
// Begin executing a user JS script, and return a promise that resolves // Begin executing a user JS script, and return a promise that resolves
// or rejects when the script finishes. // or rejects when the script finishes.
// - script is a script to execute (see Script.js). We depend only on .filename and .code. // - script is a script to execute (see Script.js). We depend only on .filename and .code.
@ -17,23 +33,9 @@ function makeScriptBlob(code: string): Blob {
// 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): Promise<void> { export async function executeJSScript(scripts: Script[] = [], workerScript: WorkerScript): Promise<void> {
let uurls: ScriptUrl[] = [];
const 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)) { compile(script, scripts);
// The URL at the top is the one we want to import. It will
// recursively import all the other modules in the urlStack.
//
// Webpack likes to turn the import into a require, which sort of
// but not really behaves like import. Particularly, it cannot
// load fully dynamic content. So we hide the import from webpack
// by placing it inside an eval call.
script.markUpdated();
uurls = _getScriptUrls(script, scripts, []);
script.url = uurls[uurls.length - 1].url;
script.module = new Promise((resolve) => resolve(eval("import(uurls[uurls.length - 1].url)")));
script.dependencies = uurls;
}
const loadedModule = await script.module; const loadedModule = await script.module;
const ns = workerScript.env.vars; const ns = workerScript.env.vars;

@ -470,11 +470,7 @@ export function startWorkerScript(runningScript: RunningScript, server: BaseServ
* @param {Server} server - Server on which the script is to be run * @param {Server} server - Server on which the script is to be run
* returns {boolean} indicating whether or not the workerScript was successfully added * returns {boolean} indicating whether or not the workerScript was successfully added
*/ */
export function createAndAddWorkerScript( function createAndAddWorkerScript(runningScriptObj: RunningScript, server: BaseServer, parent?: WorkerScript): boolean {
runningScriptObj: RunningScript,
server: BaseServer,
parent?: WorkerScript,
): boolean {
// Update server's ram usage // Update server's ram usage
let threads = 1; let threads = 1;
if (runningScriptObj.threads && !isNaN(runningScriptObj.threads)) { if (runningScriptObj.threads && !isNaN(runningScriptObj.threads)) {

@ -276,7 +276,6 @@ export interface IPlayer {
setBitNodeNumber(n: number): void; setBitNodeNumber(n: number): void;
getMult(name: string): number; getMult(name: string): number;
setMult(name: string, mult: number): void; setMult(name: string, mult: number): void;
canAccessCotMG(): boolean; canAccessCotMG(): boolean;
sourceFileLvl(n: number): number; sourceFileLvl(n: number): number;
} }

@ -1,3 +0,0 @@
import { EventEmitter } from "../utils/EventEmitter";
export const PlayerEvents = new EventEmitter<[]>();

@ -1,100 +0,0 @@
import * as React from "react";
export const SleeveFaq = (
<>
<strong>
<u>How do Duplicate Sleeves work?</u>
</strong>
<br />
Duplicate Sleeves are essentially clones. You can use them to perform any work type action, such as working for a
company/faction or committing a crime. Having sleeves perform these tasks earns you money, experience, and
reputation.
<br />
<br />
Sleeves are their own individuals, which means they each have their own experience and stats.
<br />
<br />
When a sleeve earns experience, it earns experience for itself, the player's original 'consciousness', as well as
all of the player's other sleeves.
<br />
<br />
<strong>
<u>What is Synchronization (Sync)?</u>
</strong>
<br />
Synchronization is a measure of how aligned your consciousness is with that of your Duplicate Sleeves. It is a
numerical value between 1 and 100, and it affects how much experience is earned when the sleeve is performing a
task.
<br />
<br />
Let N be the sleeve's synchronization. When the sleeve earns experience by performing a task, both the sleeve and
the player's original host consciousness earn N% of the amount of experience normally earned by the task. All of the
player's other sleeves earn ((N/100)^2 * 100)% of the experience.
<br />
<br />
Synchronization can be increased by assigning sleeves to the 'Synchronize' task.
<br />
<br />
<strong>
<u>What is Shock?</u>
</strong>
<br />
Sleeve shock is a measure of how much trauma the sleeve has due to being placed in a new body. It is a numerical
value between 0 and 99, where 99 indicates full shock and 0 indicates no shock. Shock affects the amount of
experience earned by the sleeve.
<br />
<br />
Sleeve shock slowly decreases over time. You can further increase the rate at which it decreases by assigning
sleeves to the 'Shock Recovery' task.
<br />
<br />
<strong>
<u>Why can't I work for this company or faction?</u>
</strong>
<br />
Only one of your sleeves can work for a given company/faction a time. To clarify further, if you have two sleeves
they can work for two different companies, but they cannot both work for the same company.
<br />
<br />
<strong>
<u>Why did my Sleeve stop working?</u>
</strong>
<br />
Sleeves are subject to the same time restrictions as you. This means that they automatically stop working at a
company after 8 hours, and stop working for a faction after 20 hours.
<br />
<br />
<strong>
<u>How do I buy Augmentations for my Sleeves?</u>
</strong>
<br />
Your Sleeve needs to have a Shock of 0 in order for you to buy Augmentations for it.
<br />
<br />
<strong>
<u>Why can't I buy the X Augmentation for my sleeve?</u>
</strong>
<br />
Certain Augmentations, like Bladeburner-specific ones and NeuroFlux Governor, are not available for sleeves.
<br />
<br />
<strong>
<u>Do sleeves get reset when installing Augmentations or switching BitNodes?</u>
</strong>
<br />
Sleeves are reset when switching BitNodes, but not when installing Augmentations.
<br />
<br />
<strong>
<u>What is Memory?</u>
</strong>
<br />
Sleeve memory dictates what a sleeve's synchronization will be when its reset by switching BitNodes. For example, if
a sleeve has a memory of 25, then when you switch BitNodes its synchronization will initially be set to 25, rather
than 1.
<br />
<br />
Memory can only be increased by purchasing upgrades from The Covenant. It is a persistent stat, meaning it never
gets resets back to 1. The maximum possible value for a sleeve's memory is 100.
</>
);

@ -24,7 +24,7 @@ function bitFlumeRequirements() {
}; };
} }
export interface IProgramCreationParams { interface IProgramCreationParams {
key: string; key: string;
name: string; name: string;
create: IProgramCreate | null; create: IProgramCreate | null;

@ -1,7 +1,7 @@
export type Position = { interface Position {
row: number; row: number;
column: number; column: number;
}; }
class PositionTracker { class PositionTracker {
positions: Map<string, Position>; positions: Map<string, Position>;

@ -132,7 +132,6 @@ export class Server extends BaseServer {
const aboveCap = this.moneyMax - softCap; const aboveCap = this.moneyMax - softCap;
n = 1 + (n - 1) / Math.log(aboveCap) / Math.log(8); n = 1 + (n - 1) / Math.log(aboveCap) / Math.log(8);
} }
console.log(n);
this.moneyMax *= n; this.moneyMax *= n;
} }

@ -197,7 +197,7 @@ export function initSymbolToStockMap(): void {
} }
} }
export function stockMarketCycle(): void { function stockMarketCycle(): void {
for (const name in StockMarket) { for (const name in StockMarket) {
const stock = StockMarket[name]; const stock = StockMarket[name];
if (!(stock instanceof Stock)) { if (!(stock instanceof Stock)) {

@ -1,14 +1,6 @@
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
export function getStockMarketAccountCost(): number {
return CONSTANTS.WSEAccountCost;
}
export function getStockMarketTixApiCost(): number {
return CONSTANTS.TIXAPICost;
}
export function getStockMarket4SDataCost(): number { export function getStockMarket4SDataCost(): number {
return CONSTANTS.MarketData4SCost * BitNodeMultipliers.FourSigmaMarketDataCost; return CONSTANTS.MarketData4SCost * BitNodeMultipliers.FourSigmaMarketDataCost;
} }

@ -15,7 +15,7 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
import { EventEmitter } from "../../utils/EventEmitter"; import { EventEmitter } from "../../utils/EventEmitter";
type txFn = (stock: Stock, shares: number) => boolean; type txFn = (stock: Stock, shares: number) => boolean;
export type placeOrderFn = ( type placeOrderFn = (
stock: Stock, stock: Stock,
shares: number, shares: number,
price: number, price: number,

@ -39,8 +39,8 @@ enum SelectorOrderType {
Stop = "Stop Order", Stop = "Stop Order",
} }
export type txFn = (stock: Stock, shares: number) => boolean; type txFn = (stock: Stock, shares: number) => boolean;
export type placeOrderFn = ( type placeOrderFn = (
stock: Stock, stock: Stock,
shares: number, shares: number,
price: number, price: number,

@ -47,7 +47,7 @@ function LongPosition(props: IProps): React.ReactElement {
</Box> </Box>
<Typography>Shares: {numeralWrapper.formatShares(stock.playerShares)}</Typography> <Typography>Shares: {numeralWrapper.formatShares(stock.playerShares)}</Typography>
<Typography> <Typography>
Average Price: <Money money={stock.playerAvgPx} /> (Total Cost: <Money money={totalCost} /> Average Price: <Money money={stock.playerAvgPx} /> (Total Cost: <Money money={totalCost} />)
</Typography> </Typography>
<Typography> <Typography>
Profit: <Money money={gains} /> ({numeralWrapper.formatPercentage(percentageGains)}) Profit: <Money money={gains} /> ({numeralWrapper.formatPercentage(percentageGains)})

@ -16,8 +16,8 @@ import { PositionTypes } from "../data/PositionTypes";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { EventEmitter } from "../../utils/EventEmitter"; import { EventEmitter } from "../../utils/EventEmitter";
export type txFn = (stock: Stock, shares: number) => boolean; type txFn = (stock: Stock, shares: number) => boolean;
export type placeOrderFn = ( type placeOrderFn = (
stock: Stock, stock: Stock,
shares: number, shares: number,
price: number, price: number,

@ -35,7 +35,7 @@ export function removeTrailingSlash(s: string): string {
* Checks whether a string is a valid filename. Only used for the filename itself, * Checks whether a string is a valid filename. Only used for the filename itself,
* not the entire filepath * not the entire filepath
*/ */
export function isValidFilename(filename: string): boolean { function isValidFilename(filename: string): boolean {
// Allows alphanumerics, hyphens, underscores, and percentage signs // Allows alphanumerics, hyphens, underscores, and percentage signs
// Must have a file extension // Must have a file extension
const regex = /^[.a-zA-Z0-9_-]+[.][a-zA-Z0-9]+(?:-\d+(?:\.\d*)?%-INC)?$/; const regex = /^[.a-zA-Z0-9_-]+[.][a-zA-Z0-9]+(?:-\d+(?:\.\d*)?%-INC)?$/;
@ -48,7 +48,7 @@ export function isValidFilename(filename: string): boolean {
* Checks whether a string is a valid directory name. Only used for the directory itself, * Checks whether a string is a valid directory name. Only used for the directory itself,
* not an entire path * not an entire path
*/ */
export function isValidDirectoryName(name: string): boolean { function isValidDirectoryName(name: string): boolean {
// Allows alphanumerics, hyphens, underscores, and percentage signs. // Allows alphanumerics, hyphens, underscores, and percentage signs.
// Name can begin with a single period, but otherwise cannot have any // Name can begin with a single period, but otherwise cannot have any
const regex = /^.?[a-zA-Z0-9_-]+$/; const regex = /^.?[a-zA-Z0-9_-]+$/;

@ -543,7 +543,6 @@ export class Terminal implements ITerminal {
} }
clear(): void { clear(): void {
// TODO: remove this once we figure out the height issue.
this.outputHistory = [new Output(`Bitburner v${CONSTANTS.Version}`, "primary")]; this.outputHistory = [new Output(`Bitburner v${CONSTANTS.Version}`, "primary")];
TerminalEvents.emit(); TerminalEvents.emit();
TerminalClearEvents.emit(); TerminalClearEvents.emit();

@ -19,15 +19,6 @@ export function run(
} else { } else {
const executableName = args[0] + ""; const executableName = args[0] + "";
// Secret Music player!
// if (executableName === "musicplayer") {
// post(
// '<iframe src="https://open.spotify.com/embed/user/danielyxie/playlist/1ORnnL6YNvXOracUaUV2kh" width="300" height="380" frameborder="0" allowtransparency="true"></iframe>',
// false,
// );
// return;
// }
// Check if its a script or just a program/executable // Check if its a script or just a program/executable
if (isScriptFilename(executableName)) { if (isScriptFilename(executableName)) {
runScript(terminal, router, player, server, args); runScript(terminal, router, player, server, args);

@ -3,9 +3,13 @@ import { getSubdirectories } from "./DirectoryServerHelpers";
import { Aliases, GlobalAliases } from "../Alias"; import { Aliases, GlobalAliases } from "../Alias";
import { DarkWebItems } from "../DarkWeb/DarkWebItems"; import { DarkWebItems } from "../DarkWeb/DarkWebItems";
import { Message } from "../Message/Message";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { GetServer, GetAllServers } from "../Server/AllServers"; import { GetServer, GetAllServers } from "../Server/AllServers";
import { ParseCommand, ParseCommands } from "./Parser";
import { isScriptFilename } from "../Script/isScriptFilename";
import { compile } from "../NetscriptJSEvaluator";
import { Flags } from "../NetscriptFunctions/Flags";
import * as libarg from "arg";
// An array of all Terminal commands // An array of all Terminal commands
const commands = [ const commands = [
@ -48,12 +52,12 @@ const commands = [
"weaken", "weaken",
]; ];
export function determineAllPossibilitiesForTabCompletion( export async function determineAllPossibilitiesForTabCompletion(
p: IPlayer, p: IPlayer,
input: string, input: string,
index: number, index: number,
currPath = "", currPath = "",
): string[] { ): Promise<string[]> {
let allPos: string[] = []; let allPos: string[] = [];
allPos = allPos.concat(Object.keys(GlobalAliases)); allPos = allPos.concat(Object.keys(GlobalAliases));
const currServ = p.getCurrentServer(); const currServ = p.getCurrentServer();
@ -287,13 +291,58 @@ export function determineAllPossibilitiesForTabCompletion(
return allPos; return allPos;
} }
async function scriptAutocomplete(): Promise<string[] | undefined> {
if (!isCommand("run")) return;
const commands = ParseCommands(input);
if (commands.length === 0) return;
const command = ParseCommand(commands[commands.length - 1]);
const filename = command[1] + "";
if (!isScriptFilename(filename)) return; // Not a script.
if (filename.endsWith(".script")) return; // Doesn't work with ns1.
const script = currServ.scripts.find((script) => script.filename === filename);
if (!script) return; // Doesn't exist.
if (!script.module) {
compile(script, currServ.scripts);
}
const loadedModule = await script.module;
if (!loadedModule.autocomplete) return; // Doesn't have an autocomplete function.
const runArgs = { "--tail": Boolean, "-t": Number };
const flags = libarg(runArgs, {
permissive: true,
argv: command.slice(2),
});
const flagFunc = Flags(flags._);
let pos: string[] = [];
let pos2: string[] = [];
pos = pos.concat(
loadedModule.autocomplete(
{
servers: GetAllServers().map((server) => server.hostname),
scripts: currServ.scripts.map((script) => script.filename),
txts: currServ.textFiles.map((txt) => txt.fn),
flags: (schema: any) => {
pos2 = schema.map((f: any) => "--" + f[0]);
try {
return flagFunc(schema);
} catch (err) {
return undefined;
}
},
},
flags._,
),
);
return pos.concat(pos2);
}
const pos = await scriptAutocomplete();
if (pos) return pos;
if (isCommand("run")) { if (isCommand("run")) {
addAllScripts(); addAllScripts();
addAllPrograms(); addAllPrograms();
addAllCodingContracts(); addAllCodingContracts();
addAllDirectories(); addAllDirectories();
return allPos;
} }
if (isCommand("cat")) { if (isCommand("cat")) {

@ -44,7 +44,7 @@ export function tabCompletion(
} else if (allPossibilities.length === 1) { } else if (allPossibilities.length === 1) {
if (arg === "") { if (arg === "") {
//Autocomplete command //Autocomplete command
val = allPossibilities[0] + " "; val = allPossibilities[0];
} else { } else {
val = command + " " + allPossibilities[0]; val = command + " " + allPossibilities[0];
} }

@ -162,7 +162,7 @@ export function TerminalInput({ terminal, router, player }: IProps): React.React
return () => document.removeEventListener("keydown", keyDown); return () => document.removeEventListener("keydown", keyDown);
}); });
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void { async function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): Promise<void> {
// Run command. // Run command.
if (event.keyCode === KEY.ENTER && value !== "") { if (event.keyCode === KEY.ENTER && value !== "") {
event.preventDefault(); event.preventDefault();
@ -190,7 +190,7 @@ export function TerminalInput({ terminal, router, player }: IProps): React.React
if (index < -1) { if (index < -1) {
index = 0; index = 0;
} }
const allPos = determineAllPossibilitiesForTabCompletion(player, copy, index, terminal.cwd()); const allPos = await determineAllPossibilitiesForTabCompletion(player, copy, index, terminal.cwd());
if (allPos.length == 0) { if (allPos.length == 0) {
return; return;
} }
@ -213,8 +213,9 @@ export function TerminalInput({ terminal, router, player }: IProps): React.React
command = commandArray.join(" "); command = commandArray.join(" ");
} }
const newValue = tabCompletion(command, arg, allPos, value); let newValue = tabCompletion(command, arg, allPos, value);
if (typeof newValue === "string" && newValue !== "") { if (typeof newValue === "string" && newValue !== "") {
if (!newValue.endsWith(" ") && allPos.length === 1) newValue += " ";
saveValue(newValue); saveValue(newValue);
} }
if (Array.isArray(newValue)) { if (Array.isArray(newValue)) {

@ -12,7 +12,7 @@ export type SolverFunc = (data: any, answer: string) => boolean;
Requires the 'data' of a Contract as input */ Requires the 'data' of a Contract as input */
export type DescriptionFunc = (data: any) => string; export type DescriptionFunc = (data: any) => string;
export interface ICodingContractTypeMetadata { interface ICodingContractTypeMetadata {
desc: DescriptionFunc; desc: DescriptionFunc;
difficulty: number; difficulty: number;
gen: GeneratorFunc; gen: GeneratorFunc;

@ -10,11 +10,6 @@ export interface IMap<T> {
[key: string]: T; [key: string]: T;
} }
/**
* Performs some action, with no returned value.
*/
export type Action = () => void;
/** /**
* Contains a method to initialize itself to a known state. * Contains a method to initialize itself to a known state.
*/ */

@ -1,7 +1,7 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { KEY } from "../../utils/helpers/keyCodes"; import { KEY } from "../../utils/helpers/keyCodes";
import { CodingContract, CodingContractType, CodingContractTypes } from "../../CodingContracts"; import { CodingContract, CodingContractTypes } from "../../CodingContracts";
import { CopyableText } from "./CopyableText"; import { CopyableText } from "./CopyableText";
import { Modal } from "./Modal"; import { Modal } from "./Modal";
import { EventEmitter } from "../../utils/EventEmitter"; import { EventEmitter } from "../../utils/EventEmitter";
@ -49,7 +49,7 @@ export function CodingContractModal(): React.ReactElement {
setProps(null); setProps(null);
} }
const contractType: CodingContractType = CodingContractTypes[props.c.type]; const contractType = CodingContractTypes[props.c.type];
const description = []; const description = [];
for (const [i, value] of contractType.desc(props.c.data).split("\n").entries()) for (const [i, value] of contractType.desc(props.c.data).split("\n").entries())
description.push(<span key={i} dangerouslySetInnerHTML={{ __html: value + "<br />" }}></span>); description.push(<span key={i} dangerouslySetInnerHTML={{ __html: value + "<br />" }}></span>);

@ -12,6 +12,8 @@ import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles"; import createStyles from "@mui/styles/createStyles";
import ArrowForwardIosIcon from "@mui/icons-material/ArrowForwardIos"; import ArrowForwardIosIcon from "@mui/icons-material/ArrowForwardIos";
let layerCounter = 0;
export const LogBoxEvents = new EventEmitter<[RunningScript]>(); export const LogBoxEvents = new EventEmitter<[RunningScript]>();
interface Log { interface Log {
@ -86,9 +88,18 @@ function LogWindow(props: IProps): React.ReactElement {
killWorkerScript(props.script, props.script.server, true); killWorkerScript(props.script, props.script.server, true);
props.onClose(); props.onClose();
} }
//useEffect(() => TerminalEvents.subscribe(_.debounce(async () => rerender(), 25, { maxWait: 50 })), []);
function updateLayer(): void {
const c = container.current;
if (c === null) return;
c.style.zIndex = 1500 + layerCounter + "";
layerCounter++;
rerender();
}
return ( return (
<Draggable handle="#drag"> <Draggable handle=".drag">
<Paper <Paper
style={{ style={{
display: "flex", display: "flex",
@ -100,45 +111,47 @@ function LogWindow(props: IProps): React.ReactElement {
}} }}
ref={container} ref={container}
> >
<Paper <div onMouseDown={updateLayer}>
style={{ <Paper
cursor: "grab", style={{
}} cursor: "grab",
> }}
<Box id="drag" display="flex" alignItems="center">
<Typography color="primary" variant="h6">
{props.script.filename} {props.script.args.map((x: any): string => `${x}`).join(" ")}
</Typography>
<Box position="absolute" right={0}>
<Button onClick={kill}>Kill Script</Button>
<Button onClick={props.onClose}>Close</Button>
</Box>
</Box>
</Paper>
<Paper sx={{ overflow: "scroll", overflowWrap: "break-word", whiteSpace: "pre-line" }}>
<ResizableBox
className={classes.logs}
height={500}
width={500}
handle={
<span style={{ position: "absolute", right: "-10px", bottom: "-13px", cursor: "nw-resize" }}>
<ArrowForwardIosIcon color="primary" style={{ transform: "rotate(45deg)" }} />
</span>
}
> >
<Box> <Box className="drag" display="flex" alignItems="center">
{props.script.logs.map( <Typography color="primary" variant="h6">
(line: string, i: number): JSX.Element => ( {props.script.filename} {props.script.args.map((x: any): string => `${x}`).join(" ")}
<Typography key={i}> </Typography>
{line}
<br /> <Box position="absolute" right={0}>
</Typography> <Button onClick={kill}>Kill Script</Button>
), <Button onClick={props.onClose}>Close</Button>
)} </Box>
</Box> </Box>
</ResizableBox> </Paper>
</Paper> <Paper sx={{ overflow: "scroll", overflowWrap: "break-word", whiteSpace: "pre-line" }}>
<ResizableBox
className={classes.logs}
height={500}
width={500}
handle={
<span style={{ position: "absolute", right: "-10px", bottom: "-13px", cursor: "nw-resize" }}>
<ArrowForwardIosIcon color="primary" style={{ transform: "rotate(45deg)" }} />
</span>
}
>
<Box>
{props.script.logs.map(
(line: string, i: number): JSX.Element => (
<Typography key={i}>
{line}
<br />
</Typography>
),
)}
</Box>
</ResizableBox>
</Paper>
</div>
</Paper> </Paper>
</Draggable> </Draggable>
); );

@ -1,32 +0,0 @@
/**
* React component for a popup content container
*
* Takes in a prop for rendering the content inside the popup
*/
import React, { useEffect } from "react";
interface IProps<T> {
content: (props: T) => React.ReactElement;
id: string;
props: T;
removePopup: () => void;
}
export function Popup<T>(props: IProps<T>): React.ReactElement {
function keyDown(event: KeyboardEvent): void {
if (event.key === "Escape") props.removePopup();
}
useEffect(() => {
document.addEventListener("keydown", keyDown);
return () => {
document.removeEventListener("keydown", keyDown);
};
});
return (
<div className={"popup-box-content"} id={`${props.id}-content`}>
{React.createElement(props.content, props.props)}
</div>
);
}

@ -4,7 +4,7 @@
type cbFn = (...args: any[]) => any; type cbFn = (...args: any[]) => any;
export interface ISubscriber { interface ISubscriber {
/** /**
* Callback function that will be run when an event is emitted * Callback function that will be run when an event is emitted
*/ */

@ -1,6 +1,6 @@
/* 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 { interface IReviverValue {
ctor: string; ctor: string;
data: any; data: any;
} }
@ -19,7 +19,7 @@ export function Reviver(key: string, value: IReviverValue | null): any {
// Compatibility for version v0.43.1 // Compatibility for version v0.43.1
// TODO Remove this eventually // TODO Remove this eventually
if (value.ctor === "AllServersMap") { if (value.ctor === "AllServersMap") {
console.log("Converting AllServersMap for v0.43.1"); console.warn("Converting AllServersMap for v0.43.1");
return value.data; return value.data;
} }

@ -1,13 +1,6 @@
import { EqualityFunc } from "../types"; import { EqualityFunc } from "../types";
import { isString } from "./helpers/isString"; import { isString } from "./helpers/isString";
// Netburner String helper functions
// Replaces the character at an index with a new character
function replaceAt(base: string, index: number, character: string): string {
return base.substr(0, index) + character + base.substr(index + character.length);
}
/* /*
Converts a date representing time in milliseconds to a string with the format H hours M minutes and S seconds Converts a date representing time in milliseconds to a string with the format H hours M minutes and S seconds
e.g. 10000 -> "10 seconds" e.g. 10000 -> "10 seconds"
@ -92,20 +85,6 @@ function formatNumber(num: number, numFractionDigits = 0): string {
}); });
} }
// Checks if a string contains HTML elements
function isHTML(str: string): boolean {
const element: HTMLDivElement = document.createElement("div");
element.innerHTML = str;
const c: NodeListOf<Node & ChildNode> = element.childNodes;
for (let i: number = c.length - 1; i >= 0; i--) {
if (c[i].nodeType === 1) {
return true;
}
}
return false;
}
// Generates a random alphanumeric string with N characters // Generates a random alphanumeric string with N characters
function generateRandomString(n: number): string { function generateRandomString(n: number): string {
let str = ""; let str = "";
@ -118,12 +97,4 @@ function generateRandomString(n: number): string {
return str; return str;
} }
export { export { convertTimeMsToTimeElapsedString, longestCommonStart, containsAllStrings, formatNumber, generateRandomString };
convertTimeMsToTimeElapsedString,
longestCommonStart,
containsAllStrings,
formatNumber,
isHTML,
generateRandomString,
replaceAt,
};

@ -4,9 +4,7 @@ const webpack = require("webpack");
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin");
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin"); const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
const UnusedWebpackPlugin = require("unused-webpack-plugin");
const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin"); const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
const DeadCodePlugin = require("webpack-deadcode-plugin");
module.exports = (env, argv) => { module.exports = (env, argv) => {
const isDevServer = (env || {}).devServer === true; const isDevServer = (env || {}).devServer === true;
@ -107,14 +105,6 @@ module.exports = (env, argv) => {
}, },
}, },
}), }),
new UnusedWebpackPlugin({
// Source directories
directories: [path.join(__dirname, "src"), path.join(__dirname, "utils")],
// Exclude patterns
exclude: ["*.test.js"],
// Root directory (optional)
root: __dirname,
}),
// In dev mode, use a faster method of create sourcemaps // In dev mode, use a faster method of create sourcemaps
// while keeping lines/columns accurate // while keeping lines/columns accurate
isDevServer && isDevServer &&
@ -132,10 +122,6 @@ module.exports = (env, argv) => {
module: true, module: true,
}), }),
isFastRefresh && new ReactRefreshWebpackPlugin(), isFastRefresh && new ReactRefreshWebpackPlugin(),
new DeadCodePlugin({
patterns: ["src/**/*.(js|jsx|css|ts|tsx)"],
exclude: ["**/*.(stories|spec).(js|jsx)"],
}),
].filter(Boolean), ].filter(Boolean),
target: "web", target: "web",
entry: entry, entry: entry,