Compare commits

...

72 Commits

Author SHA1 Message Date
Olivier Gagnon
c96c7e3d2e v0.57.0 2021-10-16 22:32:48 -04:00
Olivier Gagnon
84a1d27b9a fix bug with workForFaction 2021-10-16 15:43:28 -04:00
Olivier Gagnon
99c75baea0 Fix working for the CIA 2021-10-16 12:19:53 -04:00
Olivier Gagnon
371d41b7c3 added placeholder for filename 2021-10-15 21:28:17 -04:00
Olivier Gagnon
02b085cc75 script editor now saves on server that opened the file and displays server name 2021-10-15 21:27:02 -04:00
Olivier Gagnon
e38bfc70cf remove unused export 2021-10-15 21:04:17 -04:00
Olivier Gagnon
52a96b94f9 fix cancel penalty for company servers 2021-10-15 21:03:25 -04:00
Olivier Gagnon
73b9cae203 logbox title isn't infinitely long anymore 2021-10-15 20:59:16 -04:00
Olivier Gagnon
30588a885b make succes toast brighter 2021-10-15 20:50:16 -04:00
Olivier Gagnon
45c46ce2a0 fix exploit that enabled IPC 2021-10-15 19:13:05 -04:00
Olivier Gagnon
58650c5895 Fix blade not being able to start Incite Violence 2021-10-15 19:08:21 -04:00
Olivier Gagnon
09f621e342 Incite Violence doubles chaos 2021-10-15 19:04:44 -04:00
Olivier Gagnon
762e7f127c improve monokai again 2021-10-15 18:58:10 -04:00
Olivier Gagnon
d6ea9c55b1 fixed folder adding space in autocom 2021-10-15 18:33:27 -04:00
Olivier Gagnon
34fa906013 save some text editor notes 2021-10-15 18:27:02 -04:00
Olivier Gagnon
80d197652d improve monokai by making the language a superset of javascript 2021-10-15 18:25:22 -04:00
Olivier Gagnon
b5a8ed1d9d improve script editor display 2021-10-15 16:16:11 -04:00
Olivier Gagnon
e047653ed7 improve text editor scaling 2021-10-15 16:14:27 -04:00
Olivier Gagnon
f645d08a50 small screen script editor 2021-10-15 15:47:17 -04:00
Olivier Gagnon
515f9e5d4b fixed layer til the end of time 2021-10-15 14:16:30 -04:00
Olivier Gagnon
5e9143371a comment 2021-10-15 14:05:56 -04:00
Olivier Gagnon
536b8360cf small improvement to lgobox layer 2021-10-15 14:04:42 -04:00
Olivier Gagnon
8b59260bd3 logbox layer on click 2021-10-15 13:59:42 -04:00
Olivier Gagnon
3bd05ea398 logbox window z index fix 2021-10-15 13:49:03 -04:00
Olivier Gagnon
79a6c7eb7b logbox uses class to identify drag handle instead of id. 2021-10-15 13:24:00 -04:00
Olivier Gagnon
1d302a0320 doc typo 2021-10-15 13:19:00 -04:00
Olivier Gagnon
d35bac1ace Build autocomplete 2021-10-15 13:15:42 -04:00
hydroflame
e99ffcfe2b Merge pull request #1501 from danielyxie/autocomplete
Autocomplete
2021-10-15 13:13:04 -04:00
Olivier Gagnon
c1d4ced331 autocomplete 2021-10-15 13:12:18 -04:00
Olivier Gagnon
6b0e5416c4 wtf 2021-10-15 12:47:43 -04:00
Olivier Gagnon
745fb4fdf6 Merge branch 'dev' into autocomplete 2021-10-15 00:22:15 -04:00
Olivier Gagnon
af816dbc7e fix blade leaving bitverse 2021-10-14 23:57:41 -04:00
Olivier Gagnon
89fa79c4de fix incite violence 2021-10-14 23:39:30 -04:00
Olivier Gagnon
8819042c0f reduce time and effect of incite violence 2021-10-14 23:13:56 -04:00
Olivier Gagnon
7417ff8a10 tmp 2021-10-14 23:11:31 -04:00
Olivier Gagnon
e6a4456d81 Added Incite Violence bladeburner action 2021-10-14 23:01:04 -04:00
Olivier Gagnon
4603216aa0 autocomplete 2021-10-14 22:36:28 -04:00
Olivier Gagnon
3fddb3c9f2 added atExit 2021-10-14 20:13:26 -04:00
Olivier Gagnon
fe6473f426 gotoLocation sing function 2021-10-14 19:43:19 -04:00
Olivier Gagnon
89a6bf175d nuke half the dependencies because they were unused. 2021-10-14 19:31:17 -04:00
Olivier Gagnon
6b114fab7d remove unused stuff 2021-10-14 18:45:50 -04:00
Olivier Gagnon
542b2d6b8a improve market ta ui 2021-10-14 18:05:17 -04:00
Olivier Gagnon
139ccd11ff typo 2021-10-14 17:38:06 -04:00
Olivier Gagnon
38d915372f fix blade raid issue 2021-10-14 17:35:22 -04:00
Olivier Gagnon
dda6235591 revert dynamic ram miscalc 2021-10-14 17:20:13 -04:00
Olivier Gagnon
3d97f2d770 build new function 2021-10-14 15:30:26 -04:00
hydroflame
f60af97e74 Merge pull request #1485 from Tyasuh/dev
upgradeHomeCores
2021-10-14 15:28:58 -04:00
tyasuh.taeragan@gmail.com
db9c3193f7 Commented Fixes 2021-10-14 15:20:05 -04:00
Olivier Gagnon
c556408208 fix parent not being updated when children ram cost increase. 2021-10-14 14:50:57 -04:00
tyasuh.taeragan@gmail.com
f6ffe5b5be Corrective Revisions 2021-10-14 14:35:57 -04:00
Olivier Gagnon
cc056ceef4 fix completed black op being invisible. 2021-10-14 13:57:26 -04:00
tyasuh.taeragan@gmail.com
cee716bbb0 Typo Corrections 2021-10-14 12:22:35 -04:00
tyasuh.taeragan@gmail.com
737d9e027f upgradeHomeCores 2021-10-14 12:01:42 -04:00
Olivier Gagnon
63e467986e build 2021-10-14 03:23:02 -04:00
Olivier Gagnon
d3fc6a9d48 split NetscriptFunctions 2021-10-14 03:22:02 -04:00
Olivier Gagnon
e245c2d3a7 fixed a few things 2021-10-14 02:07:05 -04:00
Olivier Gagnon
3f1d4875e7 Added toast function 2021-10-13 17:25:58 -04:00
hydroflame
921a1517df Merge pull request #1481 from danielyxie/improve
fix mathjax
2021-10-13 15:58:19 -04:00
Olivier Gagnon
c4e17c3fb3 fix mathjax 2021-10-13 15:57:15 -04:00
hydroflame
2f673b0767 Merge pull request #1479 from danielyxie/improve
fix package dependency issue
2021-10-13 15:48:45 -04:00
Olivier Gagnon
b7823f46e4 change location of home cores cost formula 2021-10-13 15:48:34 -04:00
Olivier Gagnon
e2c8fed307 figured out new mathjax 2021-10-13 02:42:43 -04:00
Olivier Gagnon
e436d7f3a0 up to date omg 2021-10-13 02:27:55 -04:00
Olivier Gagnon
a5a28db47f asd 2021-10-13 02:15:29 -04:00
Olivier Gagnon
c66a8b5974 update pl 2021-10-12 21:45:16 -04:00
Olivier Gagnon
3c2a237140 reduce code of ENM core 2021-10-12 20:23:21 -04:00
Olivier Gagnon
0dbeac52ac Added alert function 2021-10-12 20:02:37 -04:00
Olivier Gagnon
6543e73f6f Fix blade corp gang equal 0 2021-10-12 19:23:36 -04:00
Olivier Gagnon
85aa67ac26 blade action in ui 2021-10-12 16:06:32 -04:00
Olivier Gagnon
bf75cf80b8 rm console log 2021-10-12 14:22:57 -04:00
Olivier Gagnon
a90575aea9 load monokai 2021-10-12 10:56:19 -04:00
Olivier Gagnon
a0baab6f6c dont update int when you dont have it 2021-10-12 10:29:41 -04:00
114 changed files with 15356 additions and 26315 deletions

78
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

@@ -0,0 +1,15 @@
alert() Netscript Function
============================
.. js:function:: alert(message)
:RAM cost: 0 GB
:param string message: message to display
Spawns an alert box.
Example:
.. code-block:: javascript
alert("Reached $1b");

View File

@@ -0,0 +1,17 @@
toast() Netscript Function
============================
.. js:function:: toast(message[, variant])
:RAM cost: 0 GB
:param string message: message to display
:param success|info|warning|error variant: color of the toast
Spawns a toast (those bottom left notifications).
Example:
.. code-block:: javascript
toast("Reached $1b");
toast("Failed to hack home", "error");

View File

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

View File

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

View File

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

34801
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -13,10 +13,9 @@
"@emotion/react": "^11.4.1",
"@emotion/styled": "^11.3.0",
"@monaco-editor/react": "^4.2.2",
"@mui/icons-material": "^5.0.0-rc.1",
"@mui/lab": "^5.0.0-alpha.46",
"@mui/material": "^5.0.0-rc.1",
"@mui/styles": "^5.0.0-rc.1",
"@mui/icons-material": "^5.0.3",
"@mui/material": "^5.0.3",
"@mui/styles": "^5.0.1",
"@types/escodegen": "^0.0.7",
"@types/js-beautify": "^1.13.2",
"@types/numeral": "0.0.25",
@@ -25,46 +24,23 @@
"@types/react-resizable": "^1.7.3",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"ajv": "^5.1.5",
"ajv-keywords": "^2.0.0",
"arg": "^5.0.0",
"async": "^2.6.1",
"autosize": "^4.0.2",
"brace": "^0.11.1",
"codemirror": "^5.58.2",
"better-react-mathjax": "^1.0.3",
"clsx": "^1.1.1",
"decimal.js": "7.2.3",
"enhanced-resolve": "^4.0.0",
"escodegen": "^1.11.0",
"escope": "^3.6.0",
"file-saver": "^1.3.8",
"interpret": "^1.0.0",
"jquery": "^3.5.0",
"jshint": "^2.10.2",
"json-loader": "^0.5.4",
"jsplumb": "^2.6.8",
"jszip": "^3.7.0",
"loader-runner": "^2.3.0",
"loader-utils": "^1.1.0",
"material-ui-color": "^1.2.0",
"mathjax-full": "^3.2.0",
"mathjax-react": "^1.0.6",
"memory-fs": "~0.4.1",
"monaco-editor": "^0.27.0",
"node-sass": "^6.0.1",
"normalize.css": "^8.0.0",
"notistack": "^2.0.2",
"numeral": "2.0.6",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-draggable": "^4.4.4",
"react-modal": "^3.12.1",
"react-resizable": "^3.0.4",
"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"
"sprintf-js": "^1.1.1"
},
"description": "A cyberpunk-themed incremental game",
"devDependencies": {
@@ -75,65 +51,33 @@
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.1",
"@testing-library/cypress": "^8.0.1",
"@types/file-saver": "^2.0.3",
"@types/jest": "^27.0.1",
"@types/lodash": "^4.14.168",
"@types/node": "^16.9.1",
"@typescript-eslint/eslint-plugin": "^4.22.0",
"@typescript-eslint/parser": "^4.22.0",
"babel-jest": "^27.0.6",
"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",
"electron": "^14.0.1",
"electron-packager": "^15.4.0",
"es6-promise-polyfill": "^1.1.1",
"eslint": "^7.24.0",
"eslint-plugin-node": "^11.1.0",
"file-loader": "^1.1.11",
"fork-ts-checker-webpack-plugin": "^6.3.3",
"html-webpack-plugin": "^3.2.0",
"http-server": "^13.0.1",
"i18n-webpack-plugin": "^1.0.0",
"istanbul": "^0.4.5",
"jest": "^27.1.0",
"js-beautify": "^1.5.10",
"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",
"mini-css-extract-plugin": "^0.4.1",
"mkdirp": "^0.5.1",
"null-loader": "^1.0.0",
"prettier": "^2.3.2",
"raw-loader": "~0.5.0",
"react-refresh": "^0.10.0",
"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",
"start-server-and-test": "^1.14.0",
"style-loader": "^0.21.0",
"stylelint": "^9.2.1",
"stylelint-declaration-use-variable": "^1.6.1",
"stylelint-order": "^0.8.1",
"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-cli": "^3.3.12",
"webpack-dev-middleware": "^3.7.3",
"webpack-dev-server": "^3.11.2",
"worker-loader": "^2.0.0"
"webpack-dev-server": "^3.11.2"
},
"engines": {
"node": ">=8 || <=9"
@@ -157,7 +101,6 @@
"build:dev": "webpack --mode development",
"lint": "npm run lint:jsts & npm run lint:style",
"lint:jsts": "eslint --fix . --ext js,jsx,ts,tsx",
"lint:style": "stylelint --fix ./css/*",
"preinstall": "node ./scripts/engines-check.js",
"test": "jest",
"test:watch": "jest --watch",

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -735,7 +735,7 @@ function initAugmentations(): void {
const ENMCore = new Augmentation({
name: AugmentationNames.ENMCore,
repCost: 2.5e5,
repCost: 175e3,
moneyCost: 2.5e9,
info:
"The Core library is an implant that upgrades the firmware of the Embedded Netburner Module. " +

View File

@@ -567,6 +567,7 @@ export function initBitNodeMultipliers(p: IPlayer): void {
BitNodeMultipliers.FactionWorkRepGain = 0.5;
BitNodeMultipliers.FactionPassiveRepGain = 0;
BitNodeMultipliers.GangKarmaRequirement = 0;
BitNodeMultipliers.PurchasedServerSoftcap = 1.4;
break;
case 3: // Corporatocracy
BitNodeMultipliers.HackingLevelMultiplier = 0.8;
@@ -583,6 +584,7 @@ export function initBitNodeMultipliers(p: IPlayer): void {
BitNodeMultipliers.HomeComputerRamCost = 1.5;
BitNodeMultipliers.PurchasedServerCost = 2;
BitNodeMultipliers.GangKarmaRequirement = 3;
BitNodeMultipliers.PurchasedServerSoftcap = 1.4;
break;
case 4: // The Singularity
BitNodeMultipliers.ServerMaxMoney = 0.15;
@@ -597,6 +599,7 @@ export function initBitNodeMultipliers(p: IPlayer): void {
BitNodeMultipliers.HackExpGain = 0.4;
BitNodeMultipliers.CrimeExpGain = 0.5;
BitNodeMultipliers.FactionWorkRepGain = 0.75;
BitNodeMultipliers.PurchasedServerSoftcap = 1.3;
break;
case 5: // Artificial intelligence
BitNodeMultipliers.ServerMaxMoney = 2;
@@ -610,6 +613,7 @@ export function initBitNodeMultipliers(p: IPlayer): void {
BitNodeMultipliers.AugmentationMoneyCost = 2;
BitNodeMultipliers.HackExpGain = 0.5;
BitNodeMultipliers.CorporationValuation = 0.5;
BitNodeMultipliers.PurchasedServerSoftcap = 1.3;
break;
case 6: // Bladeburner
BitNodeMultipliers.HackingLevelMultiplier = 0.35;
@@ -626,6 +630,7 @@ export function initBitNodeMultipliers(p: IPlayer): void {
BitNodeMultipliers.HackExpGain = 0.25;
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
BitNodeMultipliers.GangKarmaRequirement = 5;
BitNodeMultipliers.PurchasedServerSoftcap = 2;
break;
case 7: // Bladeburner 2079
BitNodeMultipliers.BladeburnerRank = 0.6;
@@ -647,6 +652,7 @@ export function initBitNodeMultipliers(p: IPlayer): void {
BitNodeMultipliers.FourSigmaMarketDataApiCost = 2;
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
BitNodeMultipliers.GangKarmaRequirement = 5;
BitNodeMultipliers.PurchasedServerSoftcap = 2;
break;
case 8: // Ghost of Wall Street
BitNodeMultipliers.ScriptHackMoney = 0.3;
@@ -660,6 +666,7 @@ export function initBitNodeMultipliers(p: IPlayer): void {
BitNodeMultipliers.CorporationValuation = 0;
BitNodeMultipliers.CodingContractMoney = 0;
BitNodeMultipliers.GangKarmaRequirement = 10;
BitNodeMultipliers.PurchasedServerSoftcap = 5;
break;
case 9: // Hacktocracy
BitNodeMultipliers.HackingLevelMultiplier = 0.4;
@@ -706,6 +713,7 @@ export function initBitNodeMultipliers(p: IPlayer): void {
BitNodeMultipliers.PurchasedServerMaxRam = 0.5;
BitNodeMultipliers.BladeburnerRank = 0.8;
BitNodeMultipliers.GangKarmaRequirement = 3;
BitNodeMultipliers.PurchasedServerSoftcap = 1.2;
break;
case 11: //The Big Crash
BitNodeMultipliers.HackingLevelMultiplier = 0.5;
@@ -724,6 +732,7 @@ export function initBitNodeMultipliers(p: IPlayer): void {
BitNodeMultipliers.CodingContractMoney = 0.25;
BitNodeMultipliers.FourSigmaMarketDataCost = 4;
BitNodeMultipliers.FourSigmaMarketDataApiCost = 4;
BitNodeMultipliers.PurchasedServerSoftcap = 2.2;
break;
case 12: {
//The Recursion
@@ -760,6 +769,7 @@ export function initBitNodeMultipliers(p: IPlayer): void {
BitNodeMultipliers.PurchasedServerCost = inc;
BitNodeMultipliers.PurchasedServerLimit = dec;
BitNodeMultipliers.PurchasedServerMaxRam = dec;
BitNodeMultipliers.PurchasedServerSoftcap = inc;
BitNodeMultipliers.ManualHackMoney = dec;
BitNodeMultipliers.ScriptHackMoney = dec;

View File

@@ -156,6 +156,11 @@ interface IBitNodeMultipliers {
*/
PurchasedServerCost: number;
/**
* Influence how much it costs to purchase a server
*/
PurchasedServerSoftcap: number;
/**
* Influences the maximum number of purchased servers you can have
*/
@@ -237,6 +242,7 @@ export const BitNodeMultipliers: IBitNodeMultipliers = {
HomeComputerRamCost: 1,
PurchasedServerCost: 1,
PurchasedServerSoftcap: 1,
PurchasedServerLimit: 1,
PurchasedServerMaxRam: 1,

View File

@@ -15,7 +15,7 @@ import { Skill } from "./Skill";
import { City } from "./City";
import { IAction } from "./IAction";
import { IPlayer } from "../PersonObjects/IPlayer";
import { IRouter } from "../ui/Router";
import { IRouter, Page } from "../ui/Router";
import { ConsoleHelpText } from "./data/Help";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { getRandomInt } from "../utils/helpers/getRandomInt";
@@ -26,7 +26,6 @@ import { addOffset } from "../utils/helpers/addOffset";
import { Faction } from "../Faction/Faction";
import { Factions, factionExists } from "../Faction/Factions";
import { calculateHospitalizationCost } from "../Hospital/Hospital";
import { redPillFlag } from "../RedPill";
import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Settings } from "../Settings/Settings";
import { Augmentations } from "../Augmentation/Augmentations";
@@ -132,7 +131,7 @@ export class Bladeburner implements IBladeburner {
return this.resetAction();
}
this.actionTimeToComplete = action.getActionTime(this);
} catch (e) {
} catch (e: any) {
exceptionAlert(e);
}
break;
@@ -145,11 +144,11 @@ export class Bladeburner implements IBladeburner {
if (action.count < 1) {
return this.resetAction();
}
if (actionId.name === "Raid" && this.getCurrentCity().commsEst === 0) {
if (actionId.name === "Raid" && this.getCurrentCity().comms === 0) {
return this.resetAction();
}
this.actionTimeToComplete = action.getActionTime(this);
} catch (e) {
} catch (e: any) {
exceptionAlert(e);
}
break;
@@ -169,7 +168,7 @@ export class Bladeburner implements IBladeburner {
throw new Error("Failed to get BlackOperation object for: " + actionId.name);
}
this.actionTimeToComplete = action.getActionTime(this);
} catch (e) {
} catch (e: any) {
exceptionAlert(e);
}
break;
@@ -184,6 +183,7 @@ export class Bladeburner implements IBladeburner {
break;
case ActionTypes["Diplomacy"]:
case ActionTypes["Hyperbolic Regeneration Chamber"]:
case ActionTypes["Incite Violence"]:
this.actionTimeToComplete = 60;
break;
default:
@@ -220,7 +220,7 @@ export class Bladeburner implements IBladeburner {
for (let i = 0; i < arrayOfCommands.length; ++i) {
this.executeConsoleCommand(player, arrayOfCommands[i]);
}
} catch (e) {
} catch (e: any) {
exceptionAlert(e);
}
}
@@ -339,6 +339,10 @@ export class Bladeburner implements IBladeburner {
action.type = ActionTypes["Hyperbolic Regeneration Chamber"];
action.name = "Hyperbolic Regeneration Chamber";
break;
case "incite violence":
action.type = ActionTypes["Incite Violence"];
action.name = "Incite Violence";
break;
default:
return null;
}
@@ -1088,9 +1092,6 @@ export class Bladeburner implements IBladeburner {
case "Investigation":
if (success) {
city.improvePopulationEstimateByPercentage(0.4 * this.skillMultipliers.successChanceEstimate);
if (Math.random() < 0.02 * this.skillMultipliers.successChanceEstimate) {
city.improveCommunityEstimate(1);
}
} else {
this.triggerPotentialMigration(this.city, 0.1);
}
@@ -1098,9 +1099,6 @@ export class Bladeburner implements IBladeburner {
case "Undercover Operation":
if (success) {
city.improvePopulationEstimateByPercentage(0.8 * this.skillMultipliers.successChanceEstimate);
if (Math.random() < 0.02 * this.skillMultipliers.successChanceEstimate) {
city.improveCommunityEstimate(1);
}
} else {
this.triggerPotentialMigration(this.city, 0.15);
}
@@ -1121,7 +1119,6 @@ export class Bladeburner implements IBladeburner {
nonZero: true,
});
--city.comms;
--city.commsEst;
} else {
const change = getRandomInt(-10, -5) / 10;
city.changePopulationByPercentage(change, {
@@ -1174,6 +1171,8 @@ export class Bladeburner implements IBladeburner {
return GeneralActions["Diplomacy"];
case ActionTypes["Hyperbolic Regeneration Chamber"]:
return GeneralActions["Hyperbolic Regeneration Chamber"];
case ActionTypes["Incite Violence"]:
return GeneralActions["Incite Violence"];
default:
return null;
}
@@ -1298,7 +1297,7 @@ export class Bladeburner implements IBladeburner {
action.level = action.maxLevel;
} // Autolevel
this.startAction(player, this.action); // Repeat action
} catch (e) {
} catch (e: any) {
exceptionAlert(e);
}
break;
@@ -1387,7 +1386,7 @@ export class Bladeburner implements IBladeburner {
this.log("You lost " + formatNumber(losses, 0) + " team members during " + action.name);
}
}
} catch (e) {
} catch (e: any) {
exceptionAlert(e);
}
break;
@@ -1500,6 +1499,25 @@ export class Bladeburner implements IBladeburner {
}
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 * 6 * 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 * 6 * growthF()) / BladeburnerConstants.ActionCountGrowthPeriod;
}
if (this.logging.general) {
this.log(`Incited violence in the synthoid communities.`);
}
const city = this.cities[this.city];
city.chaos *= (city.chaos + 100) * 2;
this.startAction(player, this.action);
break;
}
default:
console.error(`Bladeburner.completeAction() called for invalid action: ${this.action.type}`);
break;
@@ -1854,7 +1872,7 @@ export class Bladeburner implements IBladeburner {
process(router: IRouter, player: IPlayer): void {
// 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);
}
@@ -1967,6 +1985,7 @@ export class Bladeburner implements IBladeburner {
"Field Analysis",
"Diplomacy",
"Hyperbolic Regeneration Chamber",
"Incite Violence",
];
if (gen.includes(res.type)) {
res.type = "General";
@@ -2056,7 +2075,7 @@ export class Bladeburner implements IBladeburner {
this.startAction(player, actionId);
workerScript.log("bladeburner.startAction", `Starting bladeburner action with type '${type}' and name ${name}"`);
return true;
} catch (e) {
} catch (e: any) {
this.resetAction();
workerScript.log("bladeburner.startAction", errorLogText);
return false;
@@ -2091,6 +2110,7 @@ export class Bladeburner implements IBladeburner {
return this.getRecruitmentTime(player);
case ActionTypes["Diplomacy"]:
case ActionTypes["Hyperbolic Regeneration Chamber"]:
case ActionTypes["Incite Violence"]:
return 60;
default:
workerScript.log("bladeburner.getActionTime", errorLogText);
@@ -2128,6 +2148,7 @@ export class Bladeburner implements IBladeburner {
case ActionTypes["FieldAnalysis"]:
case ActionTypes["Diplomacy"]:
case ActionTypes["Hyperbolic Regeneration Chamber"]:
case ActionTypes["Incite Violence"]:
return [1, 1];
case ActionTypes["Recruitment"]: {
const recChance = this.getRecruitmentSuccessChance(player);
@@ -2170,6 +2191,7 @@ export class Bladeburner implements IBladeburner {
case ActionTypes["FieldAnalysis"]:
case ActionTypes["Diplomacy"]:
case ActionTypes["Hyperbolic Regeneration Chamber"]:
case ActionTypes["Incite Violence"]:
return Infinity;
default:
workerScript.log("bladeburner.getActionCountRemaining", errorLogText);

View File

@@ -34,11 +34,6 @@ export class City {
*/
comms = 0;
/**
* Estimated number of communities in the city.
*/
commsEst = 0;
/**
* Chaos level of the city.
*/
@@ -53,8 +48,6 @@ export class City {
// Number of Synthoid communities population and estimate
this.comms = getRandomInt(5, 150);
this.commsEst = this.comms + getRandomInt(-5, 5);
if (this.commsEst < 0) this.commsEst = 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:
* estChange(int): How much the estimate should change by

View File

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

View File

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

View File

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

View File

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

View File

@@ -12,13 +12,13 @@ export const Growths: {
["Stealth Retirement Operation"]: () => number;
["Assassination"]: () => number;
} = {
Tracking: () => getRandomInt(5, 75) / 10,
"Bounty Hunter": () => getRandomInt(5, 75) / 10,
Retirement: () => getRandomInt(5, 75) / 10,
Investigation: () => getRandomInt(10, 40) / 10,
"Undercover Operation": () => getRandomInt(10, 40) / 10,
"Sting Operation": () => getRandomInt(3, 40) / 10,
Raid: () => getRandomInt(2, 40) / 10,
"Stealth Retirement Operation": () => getRandomInt(1, 20) / 10,
Assassination: () => getRandomInt(1, 20) / 10,
Tracking: () => getRandomInt(5, 75) / 20,
"Bounty Hunter": () => getRandomInt(5, 75) / 20,
Retirement: () => getRandomInt(5, 75) / 20,
Investigation: () => getRandomInt(10, 40) / 20,
"Undercover Operation": () => getRandomInt(10, 40) / 20,
"Sting Operation": () => getRandomInt(3, 40) / 20,
Raid: () => getRandomInt(2, 40) / 20,
"Stealth Retirement Operation": () => getRandomInt(1, 20) / 20,
Assassination: () => getRandomInt(1, 20) / 20,
};

View File

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

View File

@@ -27,7 +27,11 @@ export function BlackOpElem(props: IProps): React.ReactElement {
}
const isCompleted = props.bladeburner.blackops[props.action.name] != null;
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 =
@@ -47,33 +51,35 @@ export function BlackOpElem(props: IProps): React.ReactElement {
return (
<Paper sx={{ my: 1, p: 1 }}>
<Typography>
{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>
</>
</>
) : (
{isActive ? (
<>
<>
<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} />
<Typography>
(IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
</Typography>
<Typography>
{createProgressBarText({
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
})}
</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 />
<Typography>{actionData.desc}</Typography>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -45,8 +45,3 @@ export function initCompanies(): void {
export function loadCompanies(saveString: string): void {
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);
}

View File

@@ -114,7 +114,7 @@ export const CONSTANTS: {
TotalNumBitNodes: number;
LatestUpdate: string;
} = {
Version: "0.56.0",
Version: "0.57.0",
// Speed (in ms) at which the main loop is updated
_idleSpeed: 200,
@@ -281,107 +281,55 @@ export const CONSTANTS: {
TotalNumBitNodes: 24,
LatestUpdate: `
v0.56.0 - 2021-10-11 Trimming the backlog (hydroflame & community)
v0.57.0 - 2021-10-16 It was too cheap! (hydroflame & community)
-------------------------------------------
** BREAKING **
** BREAKING (kindof) **
* The 'write' function is now async. This helps when making scripts that write scripts.
* purchased server cost now scales exponentially past 2^10.
I'm going to actually explain this one: Currently the cost of a 2^20GB server is 57b
Most players can get that before their first install. In an effort to nerf good players
a softcap was added. This softcap is different for every BN.
** Terminal **
** Script Editor **
* 'grow' and 'weaken' have been added as terminal command. This should help player transition
from commands to scripts. The tutorial also talks about it.
* 'cp' command added
* Improved performance by rate limiting refresh.
** IP vs Hostname **
* The game now uses hostname as primary key for it's servers (yeah believe it or not IPs were
used until then). This has caused some issues with purchased servers (they couldn't be sold).
You might need to soft reset for the game to fully convert itself.
** Sleeve **
* Fixed bug where they couldn't train at Volhaven.
* No longer consume all bonus time at once, making it look buggy.
** SF9 **
* Now boosts hacknet production by 8/12/14%
** Hacknet Servers **
* production nerfed by 10%
* Max money increase gets weaker above 10t max money
** Corporation **
* Warehouse tooltip now also displays the amount of space taken by products.
* Changed research box completely to avoid dependency on Treant (Treant is a pita)
* All textbox should accept MAX/MP case insensitive.
* Fixed export popup not refreshing dropdowns correctly.
* Fixed product mku becoming zero
* Increased scaling of Wilson to avoid feedback loop.
* Can no longer get in debt by buying real estate
* Bonus time is consumed faster.
* Added a theme that is close to monokai. Unfortunately a full monokai is impossible because
Monaco doesn't have a very good tokenizer.
* Opening a file and connecting to a new server will still save the file on the server that the file
was opened.
** Netscript **
* isBusy takes bitverse and infiltration into account
* hospitalize can't be called when in infiltration.
* setToCommitCrime now accepts crime rough name instead of perfect name.
* disableLog All now works for bladeburner functions.
* Fixed netscript port for ns1.
* New function: alert, which creates a textbox.
* New function: toast, creates a notification in the bottom right.
* New function: upgradeHomeCores (@Saynt_Garmo)
* New function: atExit, allows you to set a callback for when the script closes.
* New kindof function: autocomplete, this allows you to tell the game what it should
autocomplete on the terminal.
** Augmentation **
* Added augmentation to Ti Di Hui that removes penalty for being unfocused.
* Neuroflux no longer appears in special factions.
* ENM Core (the Augmentation from The Black Hand with the highest rep cost) rep cost
reduced from 250 to 175. This will help new players transition from TBH to BitRunners more easily.
** Script Editor **
** Bladeburner **
* Ram check is debounced instead of refreshed every second.
* Added the vscode extension documentation to the game (it doesn't work well, thought)
* Fixed issue where autocomplete list would grow forever
* Added semi-monokai as theme.
* Fixed issue where modifying filename would mess it up.
* Font size can be changed now.
** Infiltration **
* Fixed issue where game controls would become unfocused.
* New general action: Incite Violence. This action adds other action counts but increases chaos.
** Misc. **
* Fixed loader incorrectly assuming some null values are incorrect.
* installBackdoor trigger Bitverse sequence
* Some improvements to the theme editor
* Improved documentation about where to learn javascript.
* Added some instructions for contributors.
* Fixed typo in corporation sell shares modal (@Saynt_Garmo)
* Fixed pagination being black on black in Active Scripts
* Create Script tab renamed to Script Editor
* Fixed an issue where corp some textbox wouldn't update when changing city.
* Fixed an issue where hacknet online time was always 0.
* Netscript function prompt fixed.
* Fixed miscalculation in growth.
* Script with syntax errors will try to be a tad more helpful.
* Corporations can no longer bribe bladeburners.
* Augmentation Graphene Branchiblade renamed to Brachi, like the rest of them.
* All ram is displayed in GB/TB/PB now.
* Game now saves when saving a file, this can be turned off.
* Several improvement to log window.
* Bladeburner current action returns General type instead of the name of the action.
* Bladeburner travel and Sleeve travel respect disable ASCII.
* Tutorial fits on small screens.
* Import is much slower but more consistent now.
* Fix intelligence not updating properly.
* Added SF -1: Time Compression
* ReadTheDoc theme now matches the game.
* Logbox should wrap text better
* Logbox behavior should feel better.
* Fix font for AutoLink.exe
* Current bladeburner action is shown on the character overview.
* Fix blackop being #000 on #000.
* The last clicked Tail Box goes in front of the others.
* Fixed an issue where some values were loaded as 0 when they should be null.
* Implemented toasts.
* .msg are no longer saved in the text file.
* Tail boxes no longer display all the args, they use "..." after 30 characters.
* Fixed cancelation penalty bonus not being properly applied after the IP <-> hostname switch.
* Fixed an exploit where you could send non-strings or numbers to other scripts.
* Fixed issue when trying to work for a faction with a work type that doesn't exist while
already working for that faction.
* Fixed not being able to work for the CIA. (Don't ask)
* nerf noodle bar
`,
};

View File

@@ -99,7 +99,7 @@ export class Warehouse {
updateSize(corporation: ICorporation, industry: IIndustry): void {
try {
this.size = this.level * 100 * corporation.getStorageMultiplier() * industry.getStorageMultiplier();
} catch (e) {
} catch (e: any) {
exceptionAlert(e);
}
}

View File

@@ -1,6 +1,6 @@
import React from "react";
import { IIndustry } from "../IIndustry";
import { MathComponent } from "mathjax-react";
import { MathJax, MathJaxContext } from "better-react-mathjax";
interface IProps {
division: IIndustry;
@@ -19,9 +19,8 @@ export function IndustryProductEquation(props: IProps): React.ReactElement {
}
return (
<MathComponent
display={false}
tex={reqs.join("+") + String.raw`\Rightarrow` + prod.map((p) => String.raw`1\text{ }${p}`).join("+")}
/>
<MathJaxContext>
<MathJax>{"\\(" + reqs.join("+") + `\\Rightarrow` + prod.map((p) => `1 \\text{${p}}`).join("+") + "\\)"}</MathJax>
</MathJaxContext>
);
}

View File

@@ -76,11 +76,6 @@ function MarketTA2(props: IMarketTA2Props): React.ReactElement {
</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
export function MaterialMarketTaModal(props: IProps): React.ReactElement {
const division = useDivision();
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
@@ -106,28 +102,32 @@ export function MaterialMarketTaModal(props: IProps): React.ReactElement {
return (
<Modal open={props.open} onClose={props.onClose}>
<Typography variant="h4">Market-TA.I</Typography>
<Typography>
The maximum sale price you can mark this up to is {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>
{!division.hasResearch("Market-TA.II") && (
<>
<Typography variant="h4">Market-TA.I</Typography>
<Typography>
The maximum sale price you can mark this up to is{" "}
{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
control={<Switch checked={props.mat.marketTa1} onChange={onMarketTA1} />}
label={
<Tooltip
title={
<Typography>
If this is enabled, then this Material will automatically be sold at the price identified by Market-TA.I
(i.e. the price shown above)
</Typography>
<FormControlLabel
control={<Switch checked={props.mat.marketTa1} onChange={onMarketTA1} />}
label={
<Tooltip
title={
<Typography>
If this is enabled, then this Material will automatically be sold at the price identified by
Market-TA.I (i.e. the price shown above)
</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} />
</Modal>
);

View File

@@ -66,11 +66,6 @@ function MarketTA2(props: ITa2Props): React.ReactElement {
</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
export function ProductMarketTaModal(props: IProps): React.ReactElement {
const division = useDivision();
const markupLimit = props.product.rat / props.product.mku;
const setRerender = useState(false)[1];
function rerender(): void {
@@ -96,29 +92,32 @@ export function ProductMarketTaModal(props: IProps): React.ReactElement {
return (
<Modal open={props.open} onClose={props.onClose}>
<Typography variant="h4">Market-TA.I</Typography>
<Typography>
The maximum sale price you can mark this up to is{" "}
{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>
{!division.hasResearch("Market-TA.II") && (
<>
<Typography variant="h4">Market-TA.I</Typography>
<Typography>
The maximum sale price you can mark this up to is{" "}
{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
control={<Switch checked={props.product.marketTa1} onChange={onChange} />}
label={
<Tooltip
title={
<Typography>
If this is enabled, then this Material will automatically be sold at the price identified by Market-TA.I
(i.e. the price shown above)
</Typography>
<FormControlLabel
control={<Switch checked={props.product.marketTa1} onChange={onChange} />}
label={
<Tooltip
title={
<Typography>
If this is enabled, then this Material will automatically be sold at the price identified by
Market-TA.I (i.e. the price shown above)
</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} />
</Modal>
);

View File

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

View File

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

View File

@@ -15,7 +15,7 @@ import { Reputation } from "../../ui/React/Reputation";
import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { MathComponent } from "mathjax-react";
import { MathJax, MathJaxContext } from "better-react-mathjax";
import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper";
@@ -98,9 +98,9 @@ export function DonateOption(props: IProps): React.ReactElement {
}}
/>
<Typography>
<MathComponent
tex={String.raw`reputation = \frac{\text{donation amount} \times \text{reputation multiplier}}{10^{${digits}}}`}
/>
<MathJaxContext>
<MathJax>{`\\(reputation = \\frac{\\text{donation amount} \\cdot \\text{reputation multiplier}}{10^{${digits}}}\\)`}</MathJax>
</MathJaxContext>
</Typography>
</>
)}

View File

@@ -9,7 +9,7 @@ import { FactionInfo } from "../../Faction/FactionInfo";
import { Reputation } from "../../ui/React/Reputation";
import { Favor } from "../../ui/React/Favor";
import { MathComponent } from "mathjax-react";
import { MathJax, MathJaxContext } from "better-react-mathjax";
import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles";
@@ -57,10 +57,14 @@ export function Info(props: IProps): React.ReactElement {
You will have <Favor favor={props.faction.favor + favorGain} /> faction favor after installing an
Augmentation.
</Typography>
<MathComponent tex={String.raw`\large{r = \text{total faction reputation}}`} />
<MathComponent
tex={String.raw`\large{favor=\left\lfloor\log_{1.02}\left(\frac{r+25000}{25500}\right)\right\rfloor}`}
/>
<MathJaxContext>
<MathJax>{"\\(\\huge{r = \\text{total faction reputation}}\\)"}</MathJax>
</MathJaxContext>
<MathJaxContext>
<MathJax>
{"\\(\\huge{favor=\\left\\lfloor\\log_{1.02}\\left(\\frac{r+25000}{25500}\\right)\\right\\rfloor}\\)"}
</MathJax>
</MathJaxContext>
</>
}
>
@@ -81,8 +85,13 @@ export function Info(props: IProps): React.ReactElement {
favor is gained whenever you install an Augmentation. The amount of favor you gain depends on the total
amount of reputation you earned with this faction. Across all resets.
</Typography>
<MathComponent tex={String.raw`\large{r = reputation}`} />
<MathComponent tex={String.raw`\large{\Delta r = \Delta r \times \frac{100+favor}{100}}`} />
<MathJaxContext>
<MathJax>{"\\(\\huge{r = reputation}\\)"}</MathJax>
</MathJaxContext>
<MathJaxContext>
<MathJax>{"\\(\\huge{\\Delta r = \\Delta r \\times \\frac{100+favor}{100}}\\)"}</MathJax>
</MathJaxContext>
</>
}
>

View File

@@ -97,7 +97,7 @@ export class Gang {
this.processExperienceGains(cycles);
this.processTerritoryAndPowerGains(cycles);
this.storedCycles -= cycles;
} catch (e) {
} catch (e: any) {
console.error(`Exception caught when processing Gang: ${e}`);
}
}
@@ -344,7 +344,7 @@ export class Gang {
workerScript.log("ascend", `Ascended Gang member ${member.name}`);
}
return res;
} catch (e) {
} catch (e: any) {
if (workerScript == null) {
exceptionAlert(e);
}

View File

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

View File

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

View File

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

View File

@@ -5,7 +5,7 @@ import Tooltip from "@mui/material/Tooltip";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Money } from "../../ui/React/Money";
import { MathComponent } from "mathjax-react";
import { MathJax, MathJaxContext } from "better-react-mathjax";
type IProps = {
p: IPlayer;
@@ -19,7 +19,7 @@ export function CoresButton(props: IProps): React.ReactElement {
return <Button>Upgrade 'home' cores - MAX</Button>;
}
const cost = 1e9 * Math.pow(7.5, homeComputer.cpuCores);
const cost = props.p.getUpgradeHomeCoresCost();
function buy(): void {
if (maxCores) return;
@@ -30,7 +30,13 @@ export function CoresButton(props: IProps): React.ReactElement {
}
return (
<Tooltip title={<MathComponent tex={String.raw`\large{cost = 10^9 \times 7.5 ^{\text{cores}}}`} />}>
<Tooltip
title={
<MathJaxContext>
<MathJax>{`\\(\\large{cost = 10^9 \\cdot 7.5 ^{\\text{cores}}}\\)`}</MathJax>
</MathJaxContext>
}
>
<span>
<Button disabled={!props.p.canAfford(cost)} onClick={buy}>
Upgrade 'home' cores ({homeComputer.cpuCores} -&gt; {homeComputer.cpuCores + 1}) -&nbsp;

View File

@@ -7,8 +7,8 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
import { purchaseRamForHomeComputer } from "../../Server/ServerPurchases";
import { Money } from "../../ui/React/Money";
import { MathComponent } from "mathjax-react";
import { numeralWrapper } from "../../ui/numeralFormat";
import { MathJax, MathJaxContext } from "better-react-mathjax";
type IProps = {
p: IPlayer;
@@ -29,7 +29,13 @@ export function RamButton(props: IProps): React.ReactElement {
}
return (
<Tooltip title={<MathComponent tex={String.raw`\large{cost = 3.2 \times 10^3 \times 1.58^{log_2{(ram)}}}`} />}>
<Tooltip
title={
<MathJaxContext>
<MathJax>{`\\(\\large{cost = 3.2 \\cdot 10^3 \\cdot 1.58^{log_2{(ram)}}}\\)`}</MathJax>
</MathJaxContext>
}
>
<span>
<Button disabled={!props.p.canAfford(cost)} onClick={buy}>
Upgrade 'home' RAM ({numeralWrapper.formatRAM(homeComputer.maxRam)} -&gt;&nbsp;

View File

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

View File

@@ -13,12 +13,14 @@ import { Reviver } from "../utils/JSONReviver";
function sendMessage(msg: Message, forced = false): void {
msg.recvd = true;
if (forced || !Settings.SuppressMessages) {
showMessage(msg);
showMessage(msg.filename);
}
addMessageToServer(msg, "home");
}
function showMessage(msg: Message): void {
function showMessage(name: string): void {
const msg = Messages[name];
if (!msg) throw new Error("trying to display unexistent message");
const txt =
"Message received from unknown sender: <br><br>" +
"<i>" +
@@ -39,12 +41,11 @@ function addMessageToServer(msg: Message, serverHostname: string): void {
}
for (let i = 0; i < server.messages.length; ++i) {
const other = server.messages[i];
if (typeof other === "string") continue;
if (msg.filename === other.filename) {
if (msg.filename === other) {
return; //Already exists
}
}
server.messages.push(msg);
server.messages.push(msg.filename);
}
//Checks if any of the 'timed' messages should be sent
@@ -216,4 +217,4 @@ function initMessages(): void {
);
}
export { Messages, checkForMessagesToSend, sendMessage, showMessage, loadMessages, initMessages, Message };
export { Messages, checkForMessagesToSend, showMessage, loadMessages, initMessages };

View File

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

View File

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

View File

@@ -10,6 +10,7 @@ import { RunningScript } from "../Script/RunningScript";
import { GetServer } from "../Server/AllServers";
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(workerScript: WorkerScript): boolean;
@@ -67,6 +68,16 @@ function killWorkerScriptByPid(pid: number, rerenderUi = true): boolean {
function stopAndCleanUpWorkerScript(workerScript: WorkerScript, rerenderUi = true): void {
workerScript.env.stopFlag = true;
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);
}

File diff suppressed because it is too large Load Diff

View File

@@ -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);
},
};
}

View File

@@ -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);
},
};
}

View File

@@ -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;
},
};
}

View File

@@ -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);
},
};
}

View File

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

View File

@@ -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);
},
},
};
}

View File

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

View File

@@ -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;
},
};
}

View File

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

View File

@@ -8,6 +8,22 @@ function makeScriptBlob(code: string): Blob {
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
// or rejects when the script finishes.
// - 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
// running the main function of the script.
export async function executeJSScript(scripts: Script[] = [], workerScript: WorkerScript): Promise<void> {
let uurls: ScriptUrl[] = [];
const script = workerScript.getScript();
if (script === null) throw new Error("script is null");
if (shouldCompile(script, scripts)) {
// The URL at the top is the one we want to import. It will
// 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;
}
compile(script, scripts);
const loadedModule = await script.module;
const ns = workerScript.env.vars;

View File

@@ -93,7 +93,7 @@ function startNetscript2Script(workerScript: WorkerScript): Promise<WorkerScript
let result;
try {
result = f(...args);
} catch (e) {
} catch (e: any) {
runningFn = null;
throw e;
}
@@ -151,7 +151,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
const importProcessingRes = processNetscript1Imports(code, workerScript);
codeWithImports = importProcessingRes.code;
codeLineOffset = importProcessingRes.lineOffset;
} catch (e) {
} catch (e: any) {
dialogBoxCreate("Error processing Imports in " + workerScript.name + ":<br>" + e);
workerScript.env.stopFlag = true;
workerScript.running = false;
@@ -251,7 +251,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
let interpreter: any;
try {
interpreter = new Interpreter(codeWithImports, interpreterInitialization, codeLineOffset);
} catch (e) {
} catch (e: any) {
dialogBoxCreate("Syntax ERROR in " + workerScript.name + ":<br>" + e);
workerScript.env.stopFlag = true;
workerScript.running = false;
@@ -271,7 +271,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
} else {
resolve(workerScript);
}
} catch (e) {
} catch (e: any) {
e = e.toString();
if (!isScriptErrorMessage(e)) {
e = makeRuntimeRejectMsg(workerScript, e);
@@ -283,7 +283,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
try {
runInterpreter();
} catch (e) {
} catch (e: any) {
if (isString(e)) {
workerScript.errorMessage = e;
return reject(workerScript);
@@ -469,11 +469,7 @@ export function startWorkerScript(runningScript: RunningScript, server: BaseServ
* @param {Server} server - Server on which the script is to be run
* returns {boolean} indicating whether or not the workerScript was successfully added
*/
export function createAndAddWorkerScript(
runningScriptObj: RunningScript,
server: BaseServer,
parent?: WorkerScript,
): boolean {
function createAndAddWorkerScript(runningScriptObj: RunningScript, server: BaseServer, parent?: WorkerScript): boolean {
// Update server's ram usage
let threads = 1;
if (runningScriptObj.threads && !isNaN(runningScriptObj.threads)) {
@@ -650,6 +646,11 @@ export function runScriptFromScript(
return 0;
}
args = args.map((arg) => {
if (typeof arg === "number") return arg;
return arg + ""; // force cast to string
});
// Check if the script is already running
const runningScriptObj = server.getRunningScript(scriptname, args);
if (runningScriptObj != null) {

View File

@@ -191,6 +191,7 @@ export interface IPlayer {
getHomeComputer(): Server;
getNextCompanyPosition(company: Company, entryPosType: CompanyPosition): CompanyPosition | null;
getUpgradeHomeRamCost(): number;
getUpgradeHomeCoresCost(): number;
gotoLocation(to: LocationName): boolean;
hasAugmentation(aug: string | Augmentation): boolean;
hasCorporation(): boolean;
@@ -275,4 +276,5 @@ export interface IPlayer {
setBitNodeNumber(n: number): void;
getMult(name: string): number;
setMult(name: string, mult: number): void;
sourceFileLvl(n: number): number;
}

View File

@@ -198,6 +198,7 @@ export class PlayerObject implements IPlayer {
getHomeComputer: () => Server;
getNextCompanyPosition: (company: Company, entryPosType: CompanyPosition) => CompanyPosition | null;
getUpgradeHomeRamCost: () => number;
getUpgradeHomeCoresCost: () => number;
gotoLocation: (to: LocationName) => boolean;
hasAugmentation: (aug: string | Augmentation) => boolean;
hasCorporation: () => boolean;
@@ -282,6 +283,7 @@ export class PlayerObject implements IPlayer {
setBitNodeNumber: (n: number) => void;
getMult: (name: string) => number;
setMult: (name: string, mult: number) => void;
sourceFileLvl: (n: number) => number;
constructor() {
//Skills and stats
@@ -567,6 +569,7 @@ export class PlayerObject implements IPlayer {
this.getCurrentServer = serverMethods.getCurrentServer;
this.getHomeComputer = serverMethods.getHomeComputer;
this.getUpgradeHomeRamCost = serverMethods.getUpgradeHomeRamCost;
this.getUpgradeHomeCoresCost = serverMethods.getUpgradeHomeCoresCost;
this.createHacknetServer = serverMethods.createHacknetServer;
this.factionWorkType = "";
this.committingCrimeThruSingFn = false;
@@ -574,6 +577,7 @@ export class PlayerObject implements IPlayer {
this.getMult = generalMethods.getMult;
this.setMult = generalMethods.setMult;
this.sourceFileLvl = generalMethods.sourceFileLvl;
}
/**

View File

@@ -58,6 +58,7 @@ import { Reputation } from "../../ui/React/Reputation";
import { Money } from "../../ui/React/Money";
import React from "react";
import { serverMetadata } from "../../Server/data/servers";
export function init(this: IPlayer): void {
/* Initialize Player's home computer */
@@ -451,9 +452,8 @@ export function gainIntelligenceExp(this: IPlayer, exp: number): void {
}
if (SourceFileFlags[5] > 0 || this.intelligence > 0) {
this.intelligence_exp += exp;
this.intelligence = Math.floor(this.calculateSkill(this.intelligence_exp));
}
this.intelligence = Math.floor(this.calculateSkill(this.intelligence_exp));
}
//Given a string expression like "str" or "strength", returns the given stat
@@ -516,6 +516,7 @@ export function resetWorkStatus(this: IPlayer, generalType?: string, group?: str
this.currentWorkFactionDescription = "";
this.createProgramName = "";
this.className = "";
this.workType = "";
}
export function processWorkEarnings(this: IPlayer, numCycles = 1): void {
@@ -607,7 +608,9 @@ export function process(this: IPlayer, router: IRouter, numCycles = 1): void {
}
export function cancelationPenalty(this: IPlayer): number {
const server = GetServer(this.companyName);
const data = serverMetadata.find((s) => s.specialName === this.companyName);
if (!data) return 0.5; // Does not have special server.
const server = GetServer(data.hostname);
if (server instanceof Server) {
if (server && server.backdoorInstalled) return 0.75;
}
@@ -2655,3 +2658,13 @@ export function setMult(this: IPlayer, name: string, mult: number): void {
if (!this.hasOwnProperty(name)) return;
(this as any)[name] = mult;
}
export function canAccessCotMG(this: IPlayer): boolean {
return this.bitNodeN === 13 || SourceFileFlags[13] > 0;
}
export function sourceFileLvl(this: IPlayer, n: number): number {
const sf = this.sourceFiles.find((sf) => sf.n === n);
if (!sf) return 0;
return sf.lvl;
}

View File

@@ -40,6 +40,10 @@ export function getUpgradeHomeRamCost(this: IPlayer): number {
return cost;
}
export function getUpgradeHomeCoresCost(this: IPlayer): number {
return 1e9 * Math.pow(7.5, this.getHomeComputer().cpuCores);
}
export function createHacknetServer(this: IPlayer): HacknetServer {
const numOwned = this.hacknetNodes.length;
const name = `hacknet-node-${numOwned}`;

View File

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

View File

@@ -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.
</>
);

View File

@@ -29,5 +29,4 @@ export function loadPlayer(saveString: string): void {
}
Player.exploits = sanitizeExploits(Player.exploits);
console.log(Player.bladeburner);
}

View File

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

View File

@@ -10,7 +10,7 @@ import { Settings } from "./Settings/Settings";
import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
import { loadStockMarket, StockMarket } from "./StockMarket/StockMarket";
import { GameSavedEvents } from "./ui/React/Snackbar";
import { SnackbarEvents } from "./ui/React/Snackbar";
import * as ExportBonus from "./ExportBonus";
@@ -61,7 +61,7 @@ class BitburnerSaveObject {
const saveString = this.getSaveString();
save(saveString)
.then(() => GameSavedEvents.emit())
.then(() => SnackbarEvents.emit("Game Saved!", "info"))
.catch((err) => console.error(err));
}
@@ -73,9 +73,10 @@ class BitburnerSaveObject {
const bn = Player.bitNodeN;
const filename = `bitburnerSave_BN${bn}x${SourceFileFlags[bn]}_${epochTime}.json`;
const file = new Blob([saveString], { type: "text/plain" });
if (window.navigator.msSaveOrOpenBlob) {
const navigator = window.navigator as any;
if (navigator.msSaveOrOpenBlob) {
// IE10+
window.navigator.msSaveOrOpenBlob(file, filename);
navigator.msSaveOrOpenBlob(file, filename);
} else {
// Others
const a = document.createElement("a"),
@@ -153,6 +154,24 @@ function evaluateVersionCompatibility(ver: string): void {
}
}
}
if (ver < "0.56.1") {
if (anyPlayer.bladeburner === 0) {
anyPlayer.bladeburner = null;
}
if (anyPlayer.gang === 0) {
anyPlayer.gang = null;
}
if (anyPlayer.corporation === 0) {
anyPlayer.corporation = null;
}
// convert all Messages to just filename to save space.
const home = anyPlayer.getHomeComputer();
for (let i = 0; i < home.messages.length; i++) {
if (home.messages[i].filename) {
home.messages[i] = home.messages[i].filename;
}
}
}
}
function loadGame(saveString: string): boolean {

View File

@@ -58,9 +58,10 @@ export class Script {
download(): void {
const filename = this.filename + ".js";
const file = new Blob([this.code], { type: "text/plain" });
if (window.navigator.msSaveOrOpenBlob) {
const navigator = window.navigator as any;
if (navigator.msSaveOrOpenBlob) {
// IE10+
window.navigator.msSaveOrOpenBlob(file, filename);
navigator.msSaveOrOpenBlob(file, filename);
} else {
// Others
const a = document.createElement("a"),
@@ -112,6 +113,10 @@ export class Script {
this.markUpdated();
}
imports(): string[] {
return [];
}
// Serialize the current object to a JSON save state
toJSON(): any {
return Generic_toJSON("Script", this);

View File

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

View File

@@ -23,6 +23,7 @@ import { Settings } from "../../Settings/Settings";
import { iTutorialNextStep, ITutorial, iTutorialSteps } from "../../InteractiveTutorial";
import { debounce } from "lodash";
import { saveObject } from "../../SaveObject";
import { loadThemes } from "./themes";
import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography";
@@ -54,12 +55,13 @@ export function SetupTextEditor(): void {
symbols = populate(ns);
const exclude = ["heart", "break", "exploit", "bypass", "corporation"];
symbols = symbols.filter((symbol: string) => !exclude.includes(symbol));
symbols = symbols.filter((symbol: string) => !exclude.includes(symbol)).sort();
}
interface IProps {
filename: string;
code: string;
hostname: string;
player: IPlayer;
router: IRouter;
}
@@ -74,17 +76,23 @@ interface IProps {
// https://www.npmjs.com/package/@monaco-editor/react#development-playground
// https://microsoft.github.io/monaco-editor/playground.html#extending-language-services-custom-languages
// https://github.com/threehams/typescript-error-guide/blob/master/stories/components/Editor.tsx#L11-L39
// https://blog.checklyhq.com/customizing-monaco/
// These variables are used to reload a script when it's clicked on. Because we
// won't have references to the old script.
let lastFilename = "";
let lastCode = "";
let hostname = "";
let lastPosition: monaco.Position | null = null;
export function Root(props: IProps): React.ReactElement {
const editorRef = useRef<IStandaloneCodeEditor | null>(null);
const [filename, setFilename] = useState(props.filename ? props.filename : lastFilename);
const [code, setCode] = useState<string>(props.filename ? props.code : lastCode);
hostname = props.filename ? props.hostname : hostname;
if (hostname === "") {
hostname = props.player.getCurrentServer().hostname;
}
const [ram, setRAM] = useState("RAM: ???");
const [updatingRam, setUpdatingRam] = useState(false);
const [optionsOpen, setOptionsOpen] = useState(false);
@@ -127,7 +135,7 @@ export function Root(props: IProps): React.ReactElement {
if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
//Make sure filename + code properly follow tutorial
if (filename !== "n00dles.script") {
dialogBoxCreate("Leave the script name as 'n00dles'!");
dialogBoxCreate("Leave the script name as 'n00dles.script'!");
return;
}
if (code.replace(/\s/g, "").indexOf("while(true){hack('n00dles');}") == -1) {
@@ -141,14 +149,14 @@ export function Root(props: IProps): React.ReactElement {
let found = false;
for (let i = 0; i < server.scripts.length; i++) {
if (filename == server.scripts[i].filename) {
server.scripts[i].saveScript(filename, code, props.player.currentServer, server.scripts);
server.scripts[i].saveScript(filename, code, hostname, server.scripts);
found = true;
}
}
if (!found) {
const script = new Script();
script.saveScript(filename, code, props.player.currentServer, server.scripts);
script.saveScript(filename, code, hostname, server.scripts);
server.scripts.push(script);
}
@@ -310,19 +318,38 @@ export function Root(props: IProps): React.ReactElement {
return { suggestions: suggestions };
},
});
(async function () {
// We have to improve the default js language otherwise theme sucks
const l = await monaco.languages
.getLanguages()
.find((l: any) => l.id === "javascript")
.loader();
l.language.tokenizer.root.unshift(["ns", { token: "ns" }]);
for (const symbol of symbols) l.language.tokenizer.root.unshift(["\\." + symbol, { token: "netscriptfunction" }]);
const otherKeywords = ["let", "const", "var", "function"];
const otherKeyvars = ["true", "false", "null", "undefined"];
otherKeywords.forEach((k) => l.language.tokenizer.root.unshift([k, { token: "otherkeywords" }]));
otherKeyvars.forEach((k) => l.language.tokenizer.root.unshift([k, { token: "otherkeyvars" }]));
l.language.tokenizer.root.unshift(["this", { token: "this" }]);
})();
monaco.languages.typescript.javascriptDefaults.addExtraLib(libSource, "netscript.d.ts");
monaco.languages.typescript.typescriptDefaults.addExtraLib(libSource, "netscript.d.ts");
loadThemes(monaco);
}
// 370px 71%, 725px 85.1%, 1085px 90%, 1300px 91.7%
// fuck around in desmos until you find a function
const p = 11000 / -window.innerHeight + 100;
return (
<>
<Box display="flex" flexDirection="row" alignItems="center">
<TextField
placeholder="filename"
type="text"
tabIndex={1}
value={filename}
onChange={onFilenameChange}
InputProps={{ startAdornment: <Typography>Script&nbsp;name:&nbsp;</Typography> }}
InputProps={{ startAdornment: <Typography>{hostname}:~/</Typography> }}
/>
<IconButton onClick={() => setOptionsOpen(true)}>
<>
@@ -335,7 +362,7 @@ export function Root(props: IProps): React.ReactElement {
beforeMount={beforeMount}
onMount={onMount}
loading={<Typography>Loading script editor!</Typography>}
height="90%"
height={p + "%"}
defaultLanguage="javascript"
defaultValue={code}
onChange={updateCode}

View File

@@ -19,6 +19,10 @@ export async function loadThemes(monaco: { editor: any }): Promise<void> {
token: "number",
foreground: "ae81ff",
},
{
token: "otherkeyvars",
foreground: "ae81ff",
},
{
foreground: "ae81ff",
token: "function",
@@ -31,11 +35,22 @@ export async function loadThemes(monaco: { editor: any }): Promise<void> {
token: "storage.type.function.js",
foreground: "ae81ff",
},
// {
// foreground: "ae81ff",
// token: "entity.name.function",
// },
{
token: "ns",
foreground: "97d92b",
},
{
token: "netscriptfunction",
foreground: "53d3e4",
},
{
token: "otherkeywords",
foreground: "53d3e4",
},
{
token: "this",
foreground: "fd971f",
},
],
colors: {
"editor.foreground": "#F8F8F2",

View File

@@ -2,7 +2,6 @@
* Abstract Base Class for any Server object
*/
import { CodingContract } from "../CodingContracts";
import { Message } from "../Message/Message";
import { RunningScript } from "../Script/RunningScript";
import { Script } from "../Script/Script";
import { isValidFilePath } from "../Terminal/DirectoryHelpers";
@@ -61,7 +60,7 @@ export class BaseServer {
// For Literature files, this array contains only the filename (string)
// For Messages, it contains the actual Message object
// TODO Separate literature files into its own property
messages: (Message | string)[] = [];
messages: string[] = [];
// Name of company/faction/etc. that this server belongs to.
// Optional, not applicable to all Servers

View File

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

View File

@@ -28,7 +28,14 @@ export function getPurchaseServerCost(ram: number): number {
return Infinity;
}
return sanitizedRam * CONSTANTS.BaseCostFor1GBOfRamServer * BitNodeMultipliers.PurchasedServerCost;
const upg = Math.max(0, Math.log(sanitizedRam) / Math.log(2) - 9);
return (
sanitizedRam *
CONSTANTS.BaseCostFor1GBOfRamServer *
BitNodeMultipliers.PurchasedServerCost *
Math.pow(BitNodeMultipliers.PurchasedServerSoftcap, upg)
);
}
export function getPurchaseServerLimit(): number {

View File

@@ -239,7 +239,7 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
SuppressMessages: defaultSettings.SuppressMessages,
SuppressTravelConfirmation: defaultSettings.SuppressTravelConfirmation,
SuppressBladeburnerPopup: defaultSettings.SuppressBladeburnerPopup,
MonacoTheme: "vs-dark",
MonacoTheme: "monokai",
MonacoInsertSpaces: false,
MonacoFontSize: 20,

View File

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

View File

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

View File

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

View File

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

View File

@@ -47,7 +47,7 @@ function LongPosition(props: IProps): React.ReactElement {
</Box>
<Typography>Shares: {numeralWrapper.formatShares(stock.playerShares)}</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>
Profit: <Money money={gains} /> ({numeralWrapper.formatPercentage(percentageGains)})

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,6 @@ import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
import { showMessage } from "../../Message/MessageHelpers";
import { Message } from "../../Message/Message";
import { showLiterature } from "../../Literature/LiteratureHelpers";
export function cat(
@@ -29,12 +28,12 @@ export function cat(
for (let i = 0; i < server.messages.length; ++i) {
if (filename.endsWith(".lit") && server.messages[i] === filename) {
const file = server.messages[i];
if (file instanceof Message) throw new Error(".lit file should not be a .msg");
if (file.endsWith(".msg")) throw new Error(".lit file should not be a .msg");
showLiterature(file);
return;
} else if (filename.endsWith(".msg")) {
const file = server.messages[i] as Message;
if (file.filename !== filename) continue;
const file = server.messages[i];
if (file !== filename) continue;
showMessage(file);
return;
}

View File

@@ -2,7 +2,6 @@ import { ITerminal } from "../ITerminal";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
import { Message } from "../../Message/Message";
import { getFirstParentDirectory, isValidDirectoryPath, evaluateDirectoryPath } from "../../Terminal/DirectoryHelpers";
export function ls(
@@ -103,8 +102,7 @@ export function ls(
for (const script of s.scripts) handleFn(script.filename, allScripts);
for (const txt of s.textFiles) handleFn(txt.fn, allTextFiles);
for (const contract of s.contracts) handleFn(contract.fn, allContracts);
for (const msgOrLit of s.messages)
msgOrLit instanceof Message ? handleFn(msgOrLit.filename, allMessages) : handleFn(msgOrLit, allMessages);
for (const msgOrLit of s.messages) handleFn(msgOrLit, allMessages);
// Sort the files/folders alphabetically then print each
allPrograms.sort();

View File

@@ -19,15 +19,6 @@ export function run(
} else {
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
if (isScriptFilename(executableName)) {
runScript(terminal, router, player, server, args);

View File

@@ -34,7 +34,7 @@ export function scp(
if (scriptname.endsWith(".lit")) {
let found = false;
for (let i = 0; i < server.messages.length; ++i) {
if (!(server.messages[i] instanceof Message) && server.messages[i] == scriptname) {
if (server.messages[i] == scriptname) {
found = true;
break;
}

View File

@@ -3,9 +3,13 @@ import { getSubdirectories } from "./DirectoryServerHelpers";
import { Aliases, GlobalAliases } from "../Alias";
import { DarkWebItems } from "../DarkWeb/DarkWebItems";
import { Message } from "../Message/Message";
import { IPlayer } from "../PersonObjects/IPlayer";
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
const commands = [
@@ -48,12 +52,12 @@ const commands = [
"weaken",
];
export function determineAllPossibilitiesForTabCompletion(
export async function determineAllPossibilitiesForTabCompletion(
p: IPlayer,
input: string,
index: number,
currPath = "",
): string[] {
): Promise<string[]> {
let allPos: string[] = [];
allPos = allPos.concat(Object.keys(GlobalAliases));
const currServ = p.getCurrentServer();
@@ -71,7 +75,7 @@ export function determineAllPossibilitiesForTabCompletion(
function addAllLitFiles(): void {
for (const file of currServ.messages) {
if (!(file instanceof Message)) {
if (!file.endsWith(".msg")) {
allPos.push(file);
}
}
@@ -79,8 +83,8 @@ export function determineAllPossibilitiesForTabCompletion(
function addAllMessages(): void {
for (const file of currServ.messages) {
if (file instanceof Message) {
allPos.push(file.filename);
if (file.endsWith(".msg")) {
allPos.push(file);
}
}
}
@@ -287,13 +291,58 @@ export function determineAllPossibilitiesForTabCompletion(
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")) {
addAllScripts();
addAllPrograms();
addAllCodingContracts();
addAllDirectories();
return allPos;
}
if (isCommand("cat")) {

View File

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

View File

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

View File

@@ -35,9 +35,10 @@ export class TextFile {
const filename: string = this.fn;
const file: Blob = new Blob([this.text], { type: "text/plain" });
/* tslint:disable-next-line:strict-boolean-expressions */
if (window.navigator.msSaveOrOpenBlob) {
const navigator = window.navigator as any;
if (navigator.msSaveOrOpenBlob) {
// IE10+
window.navigator.msSaveOrOpenBlob(file, filename);
navigator.msSaveOrOpenBlob(file, filename);
} else {
// Others
const a: HTMLAnchorElement = document.createElement("a");

View File

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

View File

@@ -10,11 +10,6 @@ export interface IMap<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.
*/

Some files were not shown because too many files have changed in this diff Show More