Compare commits

..

21 Commits

Author SHA1 Message Date
Olivier Gagnon
87c63cde59 merge v0.56.0 2021-10-12 01:35:30 -04:00
hydroflame
383b02fdbb Merge pull request #1457 from Snarling/patch-3
Change effect replacer text to x% instead of +x%
2021-10-11 14:27:01 -04:00
Snarling
41de3102c7 Change effect replacer text to x% instead of +x%
This will make replacement work for hacknet cost (which is -x% so currently is not seen for replacement) and will keep the + or - for effectiveness (e.g. +12.3% hacking skill instead of just 12.3% hacking skill)
2021-10-10 23:00:11 -04:00
Olivier Gagnon
783750051e build w dev 2021-10-09 12:51:44 -04:00
Olivier Gagnon
0aa26df9d7 build sg 2021-10-09 01:09:27 -04:00
Olivier Gagnon
4355420349 made staneks gift work with prestiges 2021-10-08 03:16:51 -04:00
Olivier Gagnon
f4ecbd9b48 merge dev 2021-10-08 00:26:35 -04:00
Olivier Gagnon
528a8f30db more elements around 2021-10-08 00:13:18 -04:00
Olivier Gagnon
500063e87e Merge branch 'dev' into sg 2021-10-07 23:53:49 -04:00
Olivier Gagnon
8d7f0488f8 merge dev 2021-10-07 17:58:32 -04:00
Olivier Gagnon
62bdfb1875 notes 2021-10-07 14:49:40 -04:00
Olivier Gagnon
8e5c10cc2f enable dev for beta 2021-10-07 01:47:13 -04:00
Olivier Gagnon
15a03dd532 enable dev for beta 2021-10-07 01:42:06 -04:00
Olivier Gagnon
8e58482db0 sg 2021-10-07 01:36:59 -04:00
Olivier Gagnon
da746a63c3 add incremental game plaza stuff 2021-10-04 23:56:24 -04:00
Olivier Gagnon
2f677c7ec8 more work 2021-10-04 23:51:39 -04:00
Olivier Gagnon
c5e29dafc4 fix mc 2021-10-04 22:31:07 -04:00
Olivier Gagnon
880654c222 ui work 2021-10-04 12:28:57 -04:00
Olivier Gagnon
4fc6d393e4 fix mc 2021-10-03 21:44:15 -04:00
Olivier Gagnon
7304e5379f sg 2021-10-03 20:34:36 -04:00
Olivier Gagnon
793d9b34ce update BN13 for new UI 2021-09-25 17:21:50 -04:00
161 changed files with 28612 additions and 15488 deletions

23
css/staneksgift.scss Normal file
View File

@@ -0,0 +1,23 @@
.staneksgift_row {
padding: 0;
margin: 0;
}
.staneksgift_cell {
width: 25px;
height: 25px;
background-color: #808080;
font-color: white;
padding: 0px;
margin: 0px;
border: 1px solid black;
float: left;
}
.staneksgift_cell:first-child {
clear: left;
}
.staneksgift_container {
position: fixed;
}

138
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -30,5 +30,6 @@ to reach out to the developer!
Gang API <netscript/netscriptgangapi>
Coding Contract API <netscript/netscriptcodingcontractapi>
Sleeve API <netscript/netscriptsleeveapi>
Stanek API <netscript/netscriptstanekapi>
Formulas API <netscript/netscriptformulasapi>
Miscellaneous <netscript/netscriptmisc>

View File

@@ -1,18 +0,0 @@
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

@@ -1,36 +0,0 @@
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

@@ -1,15 +0,0 @@
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

@@ -1,17 +0,0 @@
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,4 +10,3 @@ they contain spoilers for the game.
getBitNodeMultipliers() <advancedfunctions/getBitNodeMultipliers>
getServer() <advancedfunctions/getServer>
autocomplete() <advancedfunctions/autocomplete>

View File

@@ -43,7 +43,6 @@ 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,20 @@
.. _netscriptstanek:
Netscript Stanek Functions
============================
.. warning:: This page contains spoilers for the game.
The Stanek API allow you to control Stanek's Gift.
All these function require Source-File 13-1 or to be in BitNode 13.
.. toctree::
charge() <stanekapi/charge>
fragmentDefinitions() <stanekapi/fragmentDefinitions>
placedFragments() <stanekapi/placedFragments>
clear() <stanekapi/clear>
canPlace() <stanekapi/canPlace>
place() <stanekapi/place>
fragmentAt() <stanekapi/fragmentAt>
deleteAt() <stanekapi/deleteAt>

View File

@@ -1,12 +0,0 @@
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.

View File

@@ -0,0 +1,15 @@
canPlace() Netscript Function
=======================================
.. js:function:: canPlace(worldX, worldY, fragmentId)
:RAM cost: 0.5 GB
:param int worldX: World X against which to align the top left of the fragment.
:param int worldY: World Y against which to align the top left of the fragment.
:param int fragmentId: ID of the fragment to place.
:returns: `true` if the fragment can be placed at that position. `false` otherwise.
Example:
.. code-block:: javascript
canPlace(0, 4, 17); // returns true

View File

@@ -0,0 +1,21 @@
charge() Netscript Function
=======================================
.. js:function:: charge(worldX, worldY)
:RAM cost: 0.4 GB
:param int worldX: World X of the fragment to charge.
:param int worldY: World Y of the fragment to charge.
Charge a fragment, increasing it's power but also it's heat. The
effectiveness of the charge depends on the amount of ram the running script
consumes as well as the fragments current heat. This operation takes time to
complete.
Example:
.. code-block:: javascript
charge(0, 4); // Finishes 5 seconds later.
.. warning::
Netscript JS users: This function is `async`

View File

@@ -0,0 +1,13 @@
clear() Netscript Function
=======================================
.. js:function:: clear()
:RAM cost: 0 GB
Completely clear Stanek's Gift.
Example:
.. code-block:: javascript
clear(); // No more fragments.

View File

@@ -0,0 +1,16 @@
deleteAt() Netscript Function
=======================================
.. js:function:: deleteAt(worldX, worldY)
:RAM cost: 0.15 GB
:param int worldX: World X coordinate of the fragment to delete.
:param int worldY: World Y coordinate of the fragment to delete.
:returns: `true` if the fragment was deleted. `false` otherwise.
Delete the fragment located at `[worldX, worldY]`.
Example:
.. code-block:: javascript
deleteAt(0, 4); // returns true

View File

@@ -0,0 +1,28 @@
fragmentAt() Netscript Function
=======================================
.. js:function:: fragmentAt(worldX, worldY)
:RAM cost: 2 GB
:param int worldX: World X coordinate of the fragment.
:param int worldY: World Y coordinate of the fragment.
:returns: The fragment located at `[worldX, worldY]` in Stanek's Gift, or null.
.. code-block:: typescript
{
// In world coordinates
x: number;
y: number;
heat: number;
charge: number;
id: number;
shape: boolean[][];
type: string;
magnitude: number;
limit: number;
}
Example:
.. code-block:: javascript
var fragment = fragmentAt(0, 4);
print(fragment); // {'heat': 50, 'charge': 98}

View File

@@ -0,0 +1,23 @@
fragmentDefinitions() Netscript Function
=======================================
.. js:function:: fragmentDefinitions()
:RAM cost: 0 GB
:returns: The list of all fragment that can be embedded in Stanek's Gift.
.. code-block:: typescript
[
{
id: number;
shape: boolean[][];
type: string;
magnitude: number;
limit: number;
}
]
Example:
.. code-block:: javascript
var fragments = fragmentDefinitions();
print(fragment); // prints all possible fragments

View File

@@ -0,0 +1,15 @@
place() Netscript Function
=======================================
.. js:function:: place(worldX, worldY, fragmentId)
:RAM cost: 5 GB
:param int worldX: World X against which to align the top left of the fragment.
:param int worldY: World Y against which to align the top left of the fragment.
:param int fragmentId: ID of the fragment to place.
:returns: `true` if the fragment has been placed at that position. `false` otherwise.
Example:
.. code-block:: javascript
place(0, 4, 17); // returns true

View File

@@ -0,0 +1,27 @@
placedFragments() Netscript Function
=======================================
.. js:function:: placedFragments()
:RAM cost: 5 GB
:returns: The list of all fragment that are embedded in Stanek's Gift.
.. code-block:: typescript
[
{
// In world coordinates
x: number;
y: number;
heat: number;
charge: number;
id: number;
shape: boolean[][];
type: string;
magnitude: number;
limit: number;
}
]
Example:
.. code-block:: javascript
var myFragments = placedFragments();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

35063
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -13,9 +13,10 @@
"@emotion/react": "^11.4.1",
"@emotion/styled": "^11.3.0",
"@monaco-editor/react": "^4.2.2",
"@mui/icons-material": "^5.0.3",
"@mui/material": "^5.0.3",
"@mui/styles": "^5.0.1",
"@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",
"@types/escodegen": "^0.0.7",
"@types/js-beautify": "^1.13.2",
"@types/numeral": "0.0.25",
@@ -24,23 +25,46 @@
"@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",
"better-react-mathjax": "^1.0.3",
"clsx": "^1.1.1",
"async": "^2.6.1",
"autosize": "^4.0.2",
"brace": "^0.11.1",
"codemirror": "^5.58.2",
"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",
"notistack": "^2.0.2",
"node-sass": "^6.0.1",
"normalize.css": "^8.0.0",
"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"
"sprintf-js": "^1.1.1",
"tapable": "^1.0.0",
"treant-js": "^1.0.1",
"unused-webpack-plugin": "^2.4.0",
"uuid": "^3.2.1",
"w3c-blob": "0.0.1",
"webpack-deadcode-plugin": "^0.1.15"
},
"description": "A cyberpunk-themed incremental game",
"devDependencies": {
@@ -51,33 +75,65 @@
"@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"
"webpack-dev-server": "^3.11.2",
"worker-loader": "^2.0.0"
},
"engines": {
"node": ">=8 || <=9"
@@ -101,6 +157,7 @@
"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,433 +2,366 @@ const numSpaces = 4;
const maxLineLength = 160;
module.exports = {
"env": {
"es6": true,
"node": true
env: {
es6: true,
node: true,
},
extends: "eslint:recommended",
parserOptions: {
ecmaFeatures: {
experimentalObjectRestSpread: true,
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaFeatures": {
"experimentalObjectRestSpread": true
},
"ecmaVersion": 8,
"sourceType": "module"
},
"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"
]
}
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,66 +8,74 @@ 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: 175e3,
repCost: 2.5e5,
moneyCost: 2.5e9,
info:
"The Core library is an implant that upgrades the firmware of the Embedded Netburner Module. " +
@@ -2366,6 +2366,145 @@ function initAugmentations(): void {
resetAugmentation(BladesSimulacrum);
}
// Special CotMG Augmentations
const ChurchOfTheMachineGodFactionName = "Church of the Machine God";
if (factionExists(ChurchOfTheMachineGodFactionName)) {
const StaneksGift1 = new Augmentation({
name: AugmentationNames.StaneksGift1,
repCost: 0,
moneyCost: 0,
info:
'Allison "Mother" Stanek imparts you with her gift. An ' +
"experimental Augmentation implanted at the base of the neck. " +
"It allows you to overclock your entire system by carefully " +
"changing the configuration.",
isSpecial: true,
hacking_chance_mult: 0.9,
hacking_speed_mult: 0.9,
hacking_money_mult: 0.9,
hacking_grow_mult: 0.9,
hacking_mult: 0.9,
strength_mult: 0.9,
defense_mult: 0.9,
dexterity_mult: 0.9,
agility_mult: 0.9,
charisma_mult: 0.9,
hacking_exp_mult: 0.9,
strength_exp_mult: 0.9,
defense_exp_mult: 0.9,
dexterity_exp_mult: 0.9,
agility_exp_mult: 0.9,
charisma_exp_mult: 0.9,
company_rep_mult: 0.9,
faction_rep_mult: 0.9,
crime_money_mult: 0.9,
crime_success_mult: 0.9,
hacknet_node_money_mult: 0.9,
hacknet_node_purchase_cost_mult: 1.1,
hacknet_node_ram_cost_mult: 1.1,
hacknet_node_core_cost_mult: 1.1,
hacknet_node_level_cost_mult: 1.1,
work_money_mult: 0.9,
stats: <>Its unstable nature decreases all your stats by 10%</>,
});
StaneksGift1.addToFactions([ChurchOfTheMachineGodFactionName]);
resetAugmentation(StaneksGift1);
const StaneksGift2 = new Augmentation({
name: AugmentationNames.StaneksGift2,
repCost: 1000,
moneyCost: 0,
info:
'TODO, something about Mother being bullshit and you get more control over her "gift"<br><br>' +
"The penalty for the gift is only 5%",
prereqs: [AugmentationNames.StaneksGift1],
isSpecial: true,
hacking_chance_mult: 0.95 / 0.9,
hacking_speed_mult: 0.95 / 0.9,
hacking_money_mult: 0.95 / 0.9,
hacking_grow_mult: 0.95 / 0.9,
hacking_mult: 0.95 / 0.9,
strength_mult: 0.95 / 0.9,
defense_mult: 0.95 / 0.9,
dexterity_mult: 0.95 / 0.9,
agility_mult: 0.95 / 0.9,
charisma_mult: 0.95 / 0.9,
hacking_exp_mult: 0.95 / 0.9,
strength_exp_mult: 0.95 / 0.9,
defense_exp_mult: 0.95 / 0.9,
dexterity_exp_mult: 0.95 / 0.9,
agility_exp_mult: 0.95 / 0.9,
charisma_exp_mult: 0.95 / 0.9,
company_rep_mult: 0.95 / 0.9,
faction_rep_mult: 0.95 / 0.9,
crime_money_mult: 0.95 / 0.9,
crime_success_mult: 0.95 / 0.9,
hacknet_node_money_mult: 0.95 / 0.9,
hacknet_node_purchase_cost_mult: 1.05 / 1.1,
hacknet_node_ram_cost_mult: 1.05 / 1.1,
hacknet_node_core_cost_mult: 1.05 / 1.1,
hacknet_node_level_cost_mult: 1.05 / 1.1,
work_money_mult: 0.95 / 0.9,
stats: null,
});
StaneksGift2.addToFactions([ChurchOfTheMachineGodFactionName]);
resetAugmentation(StaneksGift2);
const StaneksGift3 = new Augmentation({
name: AugmentationNames.StaneksGift3,
repCost: 10000,
moneyCost: 0,
info:
"TODO, learn more about Allisons scheme, gain full control over the gift.<br><br>" +
"Finally freed from the penalty of the gift.",
prereqs: [AugmentationNames.StaneksGift2],
isSpecial: true,
hacking_chance_mult: 1 / 0.95,
hacking_speed_mult: 1 / 0.95,
hacking_money_mult: 1 / 0.95,
hacking_grow_mult: 1 / 0.95,
hacking_mult: 1 / 0.95,
strength_mult: 1 / 0.95,
defense_mult: 1 / 0.95,
dexterity_mult: 1 / 0.95,
agility_mult: 1 / 0.95,
charisma_mult: 1 / 0.95,
hacking_exp_mult: 1 / 0.95,
strength_exp_mult: 1 / 0.95,
defense_exp_mult: 1 / 0.95,
dexterity_exp_mult: 1 / 0.95,
agility_exp_mult: 1 / 0.95,
charisma_exp_mult: 1 / 0.95,
company_rep_mult: 1 / 0.95,
faction_rep_mult: 1 / 0.95,
crime_money_mult: 1 / 0.95,
crime_success_mult: 1 / 0.95,
hacknet_node_money_mult: 1 / 0.95,
hacknet_node_purchase_cost_mult: 1 / 1.05,
hacknet_node_ram_cost_mult: 1 / 1.05,
hacknet_node_core_cost_mult: 1 / 1.05,
hacknet_node_level_cost_mult: 1 / 1.05,
work_money_mult: 1 / 0.95,
stats: null,
});
StaneksGift3.addToFactions([ChurchOfTheMachineGodFactionName]);
resetAugmentation(StaneksGift3);
const StaneksGiftAscension4 = new Augmentation({
name: AugmentationNames.StaneksGift4,
repCost: 500000000,
moneyCost: 0,
info:
"Allow Allison to install an Ascension port in her Gift. Allowing you to connect with the Machine God.<br><br>" +
"(hydro notes: Finishes the BN, eventually)",
prereqs: [AugmentationNames.StaneksGift3],
isSpecial: true,
stats: null,
});
StaneksGiftAscension4.addToFactions([ChurchOfTheMachineGodFactionName]);
resetAugmentation(StaneksGiftAscension4);
}
// Update costs based on how many have been purchased
mult = Math.pow(
CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][SourceFileFlags[11]],

View File

@@ -108,6 +108,10 @@ export const AugmentationNames: {
BladeArmorOmnibeam: string;
BladeArmorIPU: string;
BladesSimulacrum: string;
StaneksGift1: string;
StaneksGift2: string;
StaneksGift3: string;
StaneksGift4: string;
} = {
Targeting1: "Augmented Targeting I",
Targeting2: "Augmented Targeting II",
@@ -219,6 +223,11 @@ export const AugmentationNames: {
BladeArmorIPU: "BLADE-51b Tesla Armor: IPU Upgrade",
BladesSimulacrum: "The Blade's Simulacrum",
StaneksGift1: "Stanek's Gift - Genesis",
StaneksGift2: "Stanek's Gift - Awakening",
StaneksGift3: "Stanek's Gift - Serenity",
StaneksGift4: "Stanek's Gift - Ascension",
//Wasteland Augs
//PepBoy: "P.E.P-Boy", Plasma Energy Projection System
//PepBoyForceField Generates plasma force fields

View File

@@ -530,8 +530,38 @@ BitNodes["BitNode12"] = new BitNode(
</>
),
);
BitNodes["BitNode13"] = new BitNode(
13,
2,
"They're lunatics",
"1 step back, 2 steps forward",
(
<>
With the invention of Augmentations in the 2040s a religious group known as the Church of the Machine God has
rallied far more support than anyone would have hoped.
<br />
<br />
Their leader, Allison "Mother" Stanek is said to have created her own Augmentation whose power goes beyond any
other. Find her in Chongquing and gain her trust.
<br />
<br />
In this BitNode:
<br />
<br />
Every is significantly reduced
<br />
Stanek's Gift power is significantly increased.
<br />
<br />
Destroying this BitNode will give you Source-File 13, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File lets the Church of the Machine God appear in other BitNodes.
<br />
<br />
Each level of this Source-File increases the size of Stanek's Gift.
</>
),
);
// Books: Frontera, Shiner
BitNodes["BitNode13"] = new BitNode(13, 2, "fOS", "COMING SOON"); //Unlocks the new game mode and the rest of the BitNodes
BitNodes["BitNode14"] = new BitNode(14, 2, "", "COMING SOON");
BitNodes["BitNode15"] = new BitNode(15, 2, "", "COMING SOON");
BitNodes["BitNode16"] = new BitNode(16, 2, "", "COMING SOON");
@@ -553,6 +583,8 @@ export function initBitNodeMultipliers(p: IPlayer): void {
BitNodeMultipliers[mult] = 1;
}
}
// Special case.
BitNodeMultipliers.StaneksGiftExtraSize = 0;
switch (p.bitNodeN) {
case 1: // Source Genesis (every multiplier is 1)
@@ -567,7 +599,6 @@ 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;
@@ -584,7 +615,6 @@ 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;
@@ -599,7 +629,6 @@ 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;
@@ -613,7 +642,6 @@ 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;
@@ -630,7 +658,6 @@ 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;
@@ -652,7 +679,6 @@ 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;
@@ -666,7 +692,6 @@ 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;
@@ -713,7 +738,6 @@ 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;
@@ -732,7 +756,6 @@ 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
@@ -769,7 +792,6 @@ export function initBitNodeMultipliers(p: IPlayer): void {
BitNodeMultipliers.PurchasedServerCost = inc;
BitNodeMultipliers.PurchasedServerLimit = dec;
BitNodeMultipliers.PurchasedServerMaxRam = dec;
BitNodeMultipliers.PurchasedServerSoftcap = inc;
BitNodeMultipliers.ManualHackMoney = dec;
BitNodeMultipliers.ScriptHackMoney = dec;
@@ -803,6 +825,47 @@ export function initBitNodeMultipliers(p: IPlayer): void {
BitNodeMultipliers.BladeburnerSkillCost = inc;
break;
}
case 13: {
BitNodeMultipliers.DaedalusAugsRequirement = 100;
BitNodeMultipliers.HackingLevelMultiplier = 0.2;
BitNodeMultipliers.StrengthLevelMultiplier = 0.2;
BitNodeMultipliers.DefenseLevelMultiplier = 0.2;
BitNodeMultipliers.DexterityLevelMultiplier = 0.2;
BitNodeMultipliers.AgilityLevelMultiplier = 0.2;
BitNodeMultipliers.CharismaLevelMultiplier = 0.2;
BitNodeMultipliers.ServerMaxMoney = 0.15;
BitNodeMultipliers.ServerStartingMoney = 0.75;
BitNodeMultipliers.ServerStartingSecurity = 2;
BitNodeMultipliers.ScriptHackMoney = 0.2;
BitNodeMultipliers.CompanyWorkMoney = 0.2;
BitNodeMultipliers.CrimeMoney = 0.2;
BitNodeMultipliers.HacknetNodeMoney = 0.2;
BitNodeMultipliers.CodingContractMoney = 0.2;
BitNodeMultipliers.CompanyWorkExpGain = 0.1;
BitNodeMultipliers.ClassGymExpGain = 0.1;
BitNodeMultipliers.FactionWorkExpGain = 0.1;
BitNodeMultipliers.HackExpGain = 0.1;
BitNodeMultipliers.CrimeExpGain = 0.1;
BitNodeMultipliers.FactionWorkRepGain = 0.4;
BitNodeMultipliers.FourSigmaMarketDataCost = 10;
BitNodeMultipliers.FourSigmaMarketDataApiCost = 10;
BitNodeMultipliers.CorporationValuation = 0.001;
BitNodeMultipliers.BladeburnerRank = 0.1;
BitNodeMultipliers.BladeburnerSkillCost = 5;
BitNodeMultipliers.GangKarmaRequirement = 20;
BitNodeMultipliers.StaneksGiftPowerMultiplier = 2;
BitNodeMultipliers.StaneksGiftExtraSize = 1;
break;
}
default:
console.warn("Player.bitNodeN invalid");
break;

View File

@@ -156,11 +156,6 @@ 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
*/
@@ -217,6 +212,16 @@ interface IBitNodeMultipliers {
*/
StrengthLevelMultiplier: number;
/**
* Influences the power of the gift.
*/
StaneksGiftPowerMultiplier: number;
/**
* Influences the size of the gift.
*/
StaneksGiftExtraSize: number;
// Index signature
[key: string]: number;
}
@@ -242,7 +247,6 @@ export const BitNodeMultipliers: IBitNodeMultipliers = {
HomeComputerRamCost: 1,
PurchasedServerCost: 1,
PurchasedServerSoftcap: 1,
PurchasedServerLimit: 1,
PurchasedServerMaxRam: 1,
@@ -280,4 +284,7 @@ export const BitNodeMultipliers: IBitNodeMultipliers = {
DaedalusAugsRequirement: 1,
GangKarmaRequirement: 1,
StaneksGiftPowerMultiplier: 1,
StaneksGiftExtraSize: 0,
};

View File

@@ -160,7 +160,7 @@ export function BitverseRoot(props: IProps): React.ReactElement {
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>O | | | \| | O / _/ | / O | |/ | | | O</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | | |O / | | O / | O O | | \ O| | | |</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | |/ \/ / __| | |/ \ | \ | |__ \ \/ \| | |</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| O | |_/ |\| \ O \__| \_| | O |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| O | |_/ |\| \ <BitNodePortal n={13} level={nextSourceFileFlags[13]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \__| \_| | O |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | |_/ | | \| / | \_| | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| / \| | / / \ |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | <BitNodePortal n={10} level={nextSourceFileFlags[10]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | / | <BitNodePortal n={11} level={nextSourceFileFlags[11]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | </Typography>

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, Page } from "../ui/Router";
import { IRouter } from "../ui/Router";
import { ConsoleHelpText } from "./data/Help";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { getRandomInt } from "../utils/helpers/getRandomInt";
@@ -26,6 +26,7 @@ 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";
@@ -131,7 +132,7 @@ export class Bladeburner implements IBladeburner {
return this.resetAction();
}
this.actionTimeToComplete = action.getActionTime(this);
} catch (e: any) {
} catch (e) {
exceptionAlert(e);
}
break;
@@ -144,11 +145,11 @@ export class Bladeburner implements IBladeburner {
if (action.count < 1) {
return this.resetAction();
}
if (actionId.name === "Raid" && this.getCurrentCity().comms === 0) {
if (actionId.name === "Raid" && this.getCurrentCity().commsEst === 0) {
return this.resetAction();
}
this.actionTimeToComplete = action.getActionTime(this);
} catch (e: any) {
} catch (e) {
exceptionAlert(e);
}
break;
@@ -168,7 +169,7 @@ export class Bladeburner implements IBladeburner {
throw new Error("Failed to get BlackOperation object for: " + actionId.name);
}
this.actionTimeToComplete = action.getActionTime(this);
} catch (e: any) {
} catch (e) {
exceptionAlert(e);
}
break;
@@ -183,7 +184,6 @@ 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: any) {
} catch (e) {
exceptionAlert(e);
}
}
@@ -339,10 +339,6 @@ 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;
}
@@ -1092,6 +1088,9 @@ 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);
}
@@ -1099,6 +1098,9 @@ 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);
}
@@ -1119,6 +1121,7 @@ export class Bladeburner implements IBladeburner {
nonZero: true,
});
--city.comms;
--city.commsEst;
} else {
const change = getRandomInt(-10, -5) / 10;
city.changePopulationByPercentage(change, {
@@ -1171,8 +1174,6 @@ 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;
}
@@ -1297,7 +1298,7 @@ export class Bladeburner implements IBladeburner {
action.level = action.maxLevel;
} // Autolevel
this.startAction(player, this.action); // Repeat action
} catch (e: any) {
} catch (e) {
exceptionAlert(e);
}
break;
@@ -1386,7 +1387,7 @@ export class Bladeburner implements IBladeburner {
this.log("You lost " + formatNumber(losses, 0) + " team members during " + action.name);
}
}
} catch (e: any) {
} catch (e) {
exceptionAlert(e);
}
break;
@@ -1499,25 +1500,6 @@ 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;
@@ -1872,7 +1854,7 @@ export class Bladeburner implements IBladeburner {
process(router: IRouter, player: IPlayer): void {
// Edge case condition...if Operation Daedalus is complete trigger the BitNode
if (router.page() !== Page.BitVerse && this.blackops.hasOwnProperty("Operation Daedalus")) {
if (redPillFlag === false && this.blackops.hasOwnProperty("Operation Daedalus")) {
return router.toBitVerse(false, false);
}
@@ -1985,7 +1967,6 @@ export class Bladeburner implements IBladeburner {
"Field Analysis",
"Diplomacy",
"Hyperbolic Regeneration Chamber",
"Incite Violence",
];
if (gen.includes(res.type)) {
res.type = "General";
@@ -2075,7 +2056,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: any) {
} catch (e) {
this.resetAction();
workerScript.log("bladeburner.startAction", errorLogText);
return false;
@@ -2110,7 +2091,6 @@ 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);
@@ -2148,7 +2128,6 @@ 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);
@@ -2191,7 +2170,6 @@ 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,6 +34,11 @@ export class City {
*/
comms = 0;
/**
* Estimated number of communities in the city.
*/
commsEst = 0;
/**
* Chaos level of the city.
*/
@@ -48,6 +53,8 @@ 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;
}
@@ -106,6 +113,23 @@ 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,8 +30,4 @@ 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";
interface IStatsMultiplier {
export interface IStatsMultiplier {
[key: string]: number;
hack: number;

View File

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

View File

@@ -62,7 +62,4 @@ 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) / 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,
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,
};

View File

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

View File

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

View File

@@ -37,7 +37,6 @@ 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);
@@ -58,9 +57,8 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
<Paper sx={{ my: 1, p: 1 }}>
{isActive ? (
<>
<CopyableText value={props.action.name} />
<Typography>
(IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
</Typography>
<Typography>
@@ -71,7 +69,9 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
</>
) : (
<Box display="flex" flexDirection="row" alignItems="center">
<CopyableText value={props.action.name} />
<Typography>
<CopyableText value={props.action.name} />
</Typography>
<StartButton
bladeburner={props.bladeburner}
type={ActionTypes[props.action.name as string]}

View File

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

View File

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

View File

@@ -45,7 +45,7 @@ export function generateRandomContractOnHome(): void {
serv.addContract(contract);
}
interface IGenerateContractParams {
export 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 */
class CodingContractType {
export class CodingContractType {
/**
* Function that generates a description of the problem
*/

View File

@@ -45,3 +45,8 @@ 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.57.0",
Version: "0.56.0",
// Speed (in ms) at which the main loop is updated
_idleSpeed: 200,
@@ -281,55 +281,107 @@ export const CONSTANTS: {
TotalNumBitNodes: 24,
LatestUpdate: `
v0.57.0 - 2021-10-16 It was too cheap! (hydroflame & community)
v0.56.0 - 2021-10-11 Trimming the backlog (hydroflame & community)
-------------------------------------------
** BREAKING (kindof) **
** BREAKING **
* 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.
* The 'write' function is now async. This helps when making scripts that write scripts.
** Script Editor **
** Terminal **
* 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.
* '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.
** Netscript **
* 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.
* 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.
** Augmentation **
* 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.
* Added augmentation to Ti Di Hui that removes penalty for being unfocused.
* Neuroflux no longer appears in special factions.
** Bladeburner **
** Script Editor **
* New general action: Incite Violence. This action adds other action counts but increases chaos.
* 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.
** Misc. **
* 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)
* 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
* 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: any) {
} catch (e) {
exceptionAlert(e);
}
}

View File

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

View File

@@ -76,6 +76,11 @@ 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>
</>
);
}
@@ -88,7 +93,6 @@ 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);
@@ -102,32 +106,28 @@ export function MaterialMarketTaModal(props: IProps): React.ReactElement {
return (
<Modal open={props.open} onClose={props.onClose}>
{!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>
<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>
}
>
<Typography>Use Market-TA.I for Auto-Sale Price</Typography>
</Tooltip>
<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>
}
/>
<MarketTA2 mat={props.mat} />
</Modal>
);

View File

@@ -66,6 +66,11 @@ 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>
</>
);
}
@@ -78,7 +83,6 @@ 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 {
@@ -92,32 +96,29 @@ export function ProductMarketTaModal(props: IProps): React.ReactElement {
return (
<Modal open={props.open} onClose={props.onClose}>
{!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>
<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>
}
>
<Typography>Use Market-TA.I for Auto-Sale Price</Typography>
</Tooltip>
<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>
}
/>
<MarketTA2 product={props.product} />
</Modal>
);

View File

@@ -0,0 +1,90 @@
import { Fragment, FragmentById } from "./Fragment";
import { FragmentType } from "./FragmentType";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
const noCharge = [FragmentType.None, FragmentType.Delete, FragmentType.Booster];
export interface IActiveFragmentParams {
x: number;
y: number;
fragment: Fragment;
}
export class ActiveFragment {
id: number;
charge: number;
x: number;
y: number;
constructor(params?: IActiveFragmentParams) {
if (params) {
this.id = params.fragment.id;
this.x = params.x;
this.y = params.y;
this.charge = 1;
if (noCharge.includes(params.fragment.type)) this.charge = 0;
} else {
this.id = -1;
this.x = -1;
this.y = -1;
this.charge = -1;
}
}
collide(other: ActiveFragment): boolean {
const thisFragment = this.fragment();
const otherFragment = other.fragment();
// These 2 variables converts 'this' local coordinates to world to other local.
const dx: number = other.x - this.x;
const dy: number = other.y - this.y;
for (let j = 0; j < thisFragment.shape.length; j++) {
for (let i = 0; i < thisFragment.shape[j].length; i++) {
if (thisFragment.fullAt(i, j) && otherFragment.fullAt(i - dx, j - dy)) return true;
}
}
return false;
}
fragment(): Fragment {
const fragment = FragmentById(this.id);
if (fragment === null) throw new Error("ActiveFragment id refers to unknown Fragment.");
return fragment;
}
fullAt(worldX: number, worldY: number): boolean {
return this.fragment().fullAt(worldX - this.x, worldY - this.y);
}
neighboors(): number[][] {
return this.fragment()
.neighboors()
.map((cell) => [this.x + cell[0], this.y + cell[1]]);
}
copy(): ActiveFragment {
// We have to do a round trip because the constructor.
const fragment = FragmentById(this.id);
if (fragment === null) throw new Error("ActiveFragment id refers to unknown Fragment.");
const c = new ActiveFragment({ x: this.x, y: this.y, fragment: fragment });
c.charge = this.charge;
return c;
}
/**
* Serialize an active fragment to a JSON save state.
*/
toJSON(): any {
return Generic_toJSON("ActiveFragment", this);
}
/**
* Initializes an acive fragment from a JSON save state
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
static fromJSON(value: any): ActiveFragment {
return Generic_fromJSON(ActiveFragment, value.data);
}
}
Reviver.constructors.ActiveFragment = ActiveFragment;

329
src/CotMG/Fragment.ts Normal file
View File

@@ -0,0 +1,329 @@
import { FragmentType } from "./FragmentType";
export const Fragments: Fragment[] = [];
export class Fragment {
id: number;
shape: boolean[][];
type: FragmentType;
power: number;
limit: number;
constructor(id: number, shape: boolean[][], type: FragmentType, power: number, limit: number) {
this.id = id;
this.shape = shape;
this.type = type;
this.power = power;
this.limit = limit;
}
fullAt(x: number, y: number): boolean {
if (y < 0) return false;
if (y >= this.shape.length) return false;
if (x < 0) return false;
if (x >= this.shape[y].length) return false;
// Yes it's ordered y first.
return this.shape[y][x];
}
width(): number {
// check every line for robustness.
return Math.max(...this.shape.map((line) => line.length));
}
height(): number {
return this.shape.length;
}
// List of direct neighboors of this fragment.
neighboors(): number[][] {
const candidates: number[][] = [];
const add = (x: number, y: number): void => {
if (this.fullAt(x, y)) return;
if (candidates.some((coord) => coord[0] === x && coord[1] === y)) return;
candidates.push([x, y]);
};
for (let y = 0; y < this.shape.length; y++) {
for (let x = 0; x < this.shape[y].length; x++) {
// This cell is full, add all it's neighboors.
if (!this.shape[y][x]) continue;
add(x - 1, y);
add(x + 1, y);
add(x, y - 1);
add(x, y + 1);
}
}
const cells: number[][] = [];
for (const candidate of candidates) {
if (cells.some((cell) => cell[0] === candidate[0] && cell[1] === candidate[1])) continue;
cells.push(candidate);
}
return cells;
}
copy(): Fragment {
return new Fragment(
this.id,
this.shape.map((a) => a.slice()),
this.type,
this.power,
this.limit,
);
}
}
export function FragmentById(id: number): Fragment | null {
for (const fragment of Fragments) {
if (fragment.id === id) return fragment;
}
return null;
}
(function () {
const _ = false;
const X = true;
Fragments.push(
new Fragment(
0, // id
[
// shape
[X, X, X],
[_, _, X],
[_, _, X],
],
FragmentType.Hacking, // type
1,
1, // limit
),
);
Fragments.push(
new Fragment(
1, // id
[
// shape
[_, X, _],
[X, X, X],
[_, X, _],
],
FragmentType.Hacking, // type
1,
1, // limit
),
);
Fragments.push(
new Fragment(
5, // id
[
// shape
[X, X],
],
FragmentType.HackingSpeed, // type
1.3,
1, // limit
),
);
Fragments.push(
new Fragment(
6, // id
[
[X, _],
[X, X],
], // shape
FragmentType.HackingMoney, // type
2, // power
1, // limit
),
);
Fragments.push(
new Fragment(
7, // id
[
[X, X],
[X, X],
], // shape
FragmentType.HackingGrow, // type
0.5, // power
1, // limit
),
);
Fragments.push(
new Fragment(
8, // id
[
[X, X, X],
[_, X, _],
[X, X, X],
], // shape
FragmentType.Hacking, // type
1, // power
1, // limit
),
);
Fragments.push(
new Fragment(
10, // id
[
[X, X],
[_, X],
], // shape
FragmentType.Strength, // type
2, // power
1, // limit
),
);
Fragments.push(
new Fragment(
12, // id
[
[_, X],
[X, X],
], // shape
FragmentType.Defense, // type
2, // power
1, // limit
),
);
Fragments.push(
new Fragment(
14, // id
[
[X, X],
[X, _],
], // shape
FragmentType.Dexterity, // type
2, // power
1, // limit
),
);
Fragments.push(
new Fragment(
16, // id
[
[X, _],
[X, X],
], // shape
FragmentType.Agility, // type
2, // power
1, // limit
),
);
Fragments.push(
new Fragment(
18, // id
[
[X, X],
[X, _],
], // shape
FragmentType.Charisma, // type
3, // power
1, // limit
),
);
Fragments.push(
new Fragment(
20, // id
[
[X, _, _],
[X, X, _],
[X, X, X],
], // shape
FragmentType.HacknetMoney, // type
1, // power
1, // limit
),
);
Fragments.push(
new Fragment(
21, // id
[
[X, X],
[_, X],
[_, X],
], // shape
FragmentType.HacknetCost, // type
-1, // power
1, // limit
),
);
Fragments.push(
new Fragment(
25, // id
[
[X, X, X],
[_, X, _],
], // shape
FragmentType.Rep, // type
0.5, // power
1, // limit
),
);
Fragments.push(
new Fragment(
27, // id
[
[X, _],
[_, X],
], // shape
FragmentType.WorkMoney, // type
10, // power
1, // limit
),
);
Fragments.push(
new Fragment(
28, // id
[[X, X]], // shape
FragmentType.Crime, // type
2, // power
1, // limit
),
);
Fragments.push(
new Fragment(
30, // id
[
[X, X, X],
[X, X, X],
[X, X, X],
], // shape
FragmentType.Bladeburner, // type
1.3, // power
1, // limit
),
);
Fragments.push(
new Fragment(
2, // id
[
// shape
[X, X, X],
[X, _, X],
[X, X, X],
],
FragmentType.Booster, // type
1.1, // power
3, // limit
),
);
Fragments.push(
new Fragment(
31, // id
[
// shape
[X],
[X],
[X],
[X],
],
FragmentType.Booster, // type
1.1, // power
3, // limit
),
);
})();
export const NoneFragment = new Fragment(-2, [], FragmentType.None, 0, Infinity);
export const DeleteFragment = new Fragment(-2, [], FragmentType.Delete, 0, Infinity);

96
src/CotMG/FragmentType.ts Normal file
View File

@@ -0,0 +1,96 @@
export enum FragmentType {
// Special fragments for the UI
None,
Delete,
// Stats boosting fragments
HackingChance,
HackingSpeed,
HackingMoney,
HackingGrow,
Hacking,
Strength,
Defense,
Dexterity,
Agility,
Charisma,
HacknetMoney,
HacknetCost,
Rep,
WorkMoney,
Crime,
Bladeburner,
// utility fragments.
Booster,
}
export function Effect(tpe: FragmentType): string {
switch (tpe) {
case FragmentType.HackingChance: {
return "+x% hack() success chance";
break;
}
case FragmentType.HackingSpeed: {
return "+x% faster hack(), grow(), and weaken()";
break;
}
case FragmentType.HackingMoney: {
return "+x% hack() power";
break;
}
case FragmentType.HackingGrow: {
return "+x% grow() power";
break;
}
case FragmentType.Hacking: {
return "+x% hacking skill";
break;
}
case FragmentType.Strength: {
return "+x% strength skill";
break;
}
case FragmentType.Defense: {
return "+x% defense skill";
break;
}
case FragmentType.Dexterity: {
return "+x% dexterity skill";
break;
}
case FragmentType.Agility: {
return "+x% agility skill";
break;
}
case FragmentType.Charisma: {
return "+x% charisma skill";
break;
}
case FragmentType.HacknetMoney: {
return "+x% hacknet production";
break;
}
case FragmentType.HacknetCost: {
return "-x% all hacknet cost";
break;
}
case FragmentType.Rep: {
return "+x% reputation from factions and companies";
break;
}
case FragmentType.WorkMoney: {
return "+x% work money";
break;
}
case FragmentType.Crime: {
return "+x% crime money";
break;
}
case FragmentType.Bladeburner: {
return "+x% all bladeburner stats";
break;
}
}
throw new Error("Calling effect for fragment type that doesn't have an effect " + tpe);
}

14
src/CotMG/Helper.tsx Normal file
View File

@@ -0,0 +1,14 @@
import { Reviver } from "../utils/JSONReviver";
import { IStaneksGift } from "./IStaneksGift";
import { StaneksGift } from "./StaneksGift";
export let staneksGift: IStaneksGift = new StaneksGift();
export function loadStaneksGift(saveString: string): void {
if (saveString) {
staneksGift = JSON.parse(saveString, Reviver);
} else {
staneksGift = new StaneksGift();
}
}

22
src/CotMG/IStaneksGift.ts Normal file
View File

@@ -0,0 +1,22 @@
import { ActiveFragment } from "./ActiveFragment";
import { Fragment } from "./Fragment";
import { IPlayer } from "../PersonObjects/IPlayer";
export interface IStaneksGift {
storedCycles: number;
fragments: ActiveFragment[];
width(): number;
height(): number;
charge(worldX: number, worldY: number, ram: number): number;
process(p: IPlayer, n: number): void;
effect(fragment: ActiveFragment): number;
canPlace(x: number, y: number, fragment: Fragment): boolean;
place(x: number, y: number, fragment: Fragment): boolean;
fragmentAt(worldX: number, worldY: number): ActiveFragment | null;
deleteAt(worldX: number, worldY: number): boolean;
clear(): void;
count(fragment: Fragment): number;
inBonus(): boolean;
prestigeAugmentation(): void;
prestigeSourceFile(): void;
}

222
src/CotMG/StaneksGift.ts Normal file
View File

@@ -0,0 +1,222 @@
import { Fragment } from "./Fragment";
import { ActiveFragment } from "./ActiveFragment";
import { FragmentType } from "./FragmentType";
import { IStaneksGift } from "./IStaneksGift";
import { IPlayer } from "../PersonObjects/IPlayer";
import { Factions } from "../Faction/Factions";
import { CalculateEffect } from "./formulas/effect";
import { CalculateCharge } from "./formulas/charge";
import { StaneksGiftEvents } from "./StaneksGiftEvents";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
import { CONSTANTS } from "../Constants";
import { StanekConstants } from "./data/Constants";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { Player } from "../Player";
export class StaneksGift implements IStaneksGift {
storedCycles = 0;
fragments: ActiveFragment[] = [];
baseSize(): number {
return StanekConstants.BaseSize + BitNodeMultipliers.StaneksGiftExtraSize + Player.sourceFileLvl(13);
}
width(): number {
return Math.floor(this.baseSize() / 2 + 1);
}
height(): number {
return Math.floor(this.baseSize() / 2 + 0.6);
}
charge(worldX: number, worldY: number, ram: number): number {
const af = this.fragmentAt(worldX, worldY);
if (af === null) return 0;
const charge = CalculateCharge(ram);
af.charge += charge;
Factions["Church of the Machine God"].playerReputation += Math.log(ram) / Math.log(2);
return ram;
}
inBonus(): boolean {
return (this.storedCycles * CONSTANTS._idleSpeed) / 1000 > 1;
}
process(p: IPlayer, numCycles = 1): void {
this.storedCycles += numCycles;
this.storedCycles -= 5;
this.storedCycles = Math.max(0, this.storedCycles);
this.updateMults(p);
StaneksGiftEvents.emit();
}
effect(fragment: ActiveFragment): number {
// Find all the neighbooring cells
const cells = fragment.neighboors();
// find the neighbooring active fragments.
const maybeFragments = cells.map((n) => this.fragmentAt(n[0], n[1]));
// Filter out nulls with typescript "Type guard". Whatever
let neighboors = maybeFragments.filter((v: ActiveFragment | null): v is ActiveFragment => !!v);
neighboors = neighboors.filter((fragment) => fragment.fragment().type === FragmentType.Booster);
let boost = 1;
for (const neighboor of neighboors) {
boost *= neighboor.fragment().power;
}
return CalculateEffect(fragment.charge, fragment.fragment().power, boost);
}
canPlace(x: number, y: number, fragment: Fragment): boolean {
if (x + fragment.width() > this.width()) return false;
if (y + fragment.height() > this.height()) return false;
if (this.count(fragment) >= fragment.limit) return false;
const newFrag = new ActiveFragment({ x: x, y: y, fragment: fragment });
for (const aFrag of this.fragments) {
if (aFrag.collide(newFrag)) return false;
}
return true;
}
place(x: number, y: number, fragment: Fragment): boolean {
if (!this.canPlace(x, y, fragment)) return false;
this.fragments.push(new ActiveFragment({ x: x, y: y, fragment: fragment }));
return true;
}
fragmentAt(worldX: number, worldY: number): ActiveFragment | null {
for (const aFrag of this.fragments) {
if (aFrag.fullAt(worldX, worldY)) {
return aFrag;
}
}
return null;
}
count(fragment: Fragment): number {
let amt = 0;
for (const aFrag of this.fragments) {
if (aFrag.fragment().id === fragment.id) amt++;
}
return amt;
}
deleteAt(worldX: number, worldY: number): boolean {
for (let i = 0; i < this.fragments.length; i++) {
if (this.fragments[i].fullAt(worldX, worldY)) {
this.fragments.splice(i, 1);
return true;
}
}
return false;
}
clear(): void {
this.fragments = [];
}
updateMults(p: IPlayer): void {
p.reapplyAllAugmentations(true);
p.reapplyAllSourceFiles();
for (const aFrag of this.fragments) {
const fragment = aFrag.fragment();
const power = this.effect(aFrag);
switch (fragment.type) {
case FragmentType.HackingChance:
p.hacking_chance_mult *= power;
break;
case FragmentType.HackingSpeed:
p.hacking_speed_mult *= power;
break;
case FragmentType.HackingMoney:
p.hacking_money_mult *= power;
break;
case FragmentType.HackingGrow:
p.hacking_grow_mult *= power;
break;
case FragmentType.Hacking:
p.hacking_mult *= power;
p.hacking_exp_mult *= power;
break;
case FragmentType.Strength:
p.strength_mult *= power;
p.strength_exp_mult *= power;
break;
case FragmentType.Defense:
p.defense_mult *= power;
p.defense_exp_mult *= power;
break;
case FragmentType.Dexterity:
p.dexterity_mult *= power;
p.dexterity_exp_mult *= power;
break;
case FragmentType.Agility:
p.agility_mult *= power;
p.agility_exp_mult *= power;
break;
case FragmentType.Charisma:
p.charisma_mult *= power;
p.charisma_exp_mult *= power;
break;
case FragmentType.HacknetMoney:
p.hacknet_node_money_mult *= power;
break;
case FragmentType.HacknetCost:
p.hacknet_node_purchase_cost_mult *= power;
p.hacknet_node_ram_cost_mult *= power;
p.hacknet_node_core_cost_mult *= power;
p.hacknet_node_level_cost_mult *= power;
break;
case FragmentType.Rep:
p.company_rep_mult *= power;
p.faction_rep_mult *= power;
break;
case FragmentType.WorkMoney:
p.work_money_mult *= power;
break;
case FragmentType.Crime:
p.crime_success_mult *= power;
p.crime_money_mult *= power;
break;
case FragmentType.Bladeburner:
p.bladeburner_max_stamina_mult *= power;
p.bladeburner_stamina_gain_mult *= power;
p.bladeburner_analysis_mult *= power;
p.bladeburner_success_chance_mult *= power;
break;
}
}
}
prestigeAugmentation(): void {
this.clear();
}
prestigeSourceFile(): void {
this.clear();
}
/**
* Serialize Staneks Gift to a JSON save state.
*/
toJSON(): any {
return Generic_toJSON("StaneksGift", this);
}
/**
* Initializes Staneks Gift from a JSON save state
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
static fromJSON(value: any): StaneksGift {
return Generic_fromJSON(StaneksGift, value.data);
}
}
Reviver.constructors.StaneksGift = StaneksGift;

View File

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

View File

@@ -0,0 +1,7 @@
export const StanekConstants: {
RAMBonus: number;
BaseSize: number;
} = {
RAMBonus: 0.1,
BaseSize: 12,
};

View File

@@ -0,0 +1,5 @@
import { StanekConstants } from "../data/Constants";
export function CalculateCharge(ram: number): number {
return ram * Math.pow(1 + Math.log2(ram) * StanekConstants.RAMBonus, 0.7);
}

View File

@@ -0,0 +1,3 @@
export function CalculateEffect(charge: number, power: number, boost: number): number {
return 1 + (Math.log(charge + 1) / (Math.log(3) * 100)) * power * boost;
}

4
src/CotMG/notes Normal file
View File

@@ -0,0 +1,4 @@
incentive for more threads
boosters just multiply output, eg 20% * 1.5 = 30%
git remote add danielyxie git@github.com:danielyxie/bitburner.git

40
src/CotMG/ui/Cell.tsx Normal file
View File

@@ -0,0 +1,40 @@
import * as React from "react";
import makeStyles from "@mui/styles/makeStyles";
import { TableCell as MuiTableCell, TableCellProps } from "@mui/material";
const useStyles = makeStyles({
root: {
border: "1px solid white",
width: "5px",
height: "5px",
},
});
export const TableCell: React.FC<TableCellProps> = (props: TableCellProps) => {
return (
<MuiTableCell
{...props}
classes={{
root: useStyles().root,
...props.classes,
}}
/>
);
};
type IProps = {
onMouseEnter?: () => void;
onClick?: () => void;
color: string;
};
export function Cell(cellProps: IProps): React.ReactElement {
return (
<TableCell
style={{ backgroundColor: cellProps.color }}
onMouseEnter={cellProps.onMouseEnter}
onClick={cellProps.onClick}
></TableCell>
);
}

View File

@@ -0,0 +1,79 @@
import React, { useState, useEffect } from "react";
import { ActiveFragment } from "../ActiveFragment";
import { IStaneksGift } from "../IStaneksGift";
import { FragmentType, Effect } from "../FragmentType";
import { numeralWrapper } from "../../ui/numeralFormat";
import Paper from "@mui/material/Paper";
import Typography from "@mui/material/Typography";
type IProps = {
gift: IStaneksGift;
fragment: ActiveFragment | null;
x: number;
y: number;
};
export function FragmentInspector(props: IProps): React.ReactElement {
const [, setC] = useState(new Date());
useEffect(() => {
const id = setInterval(() => setC(new Date()), 250);
return () => clearInterval(id);
}, []);
if (props.fragment === null) {
return (
<Paper>
<Typography>
ID: N/A
<br />
Effect: N/A
<br />
Magnitude: N/A
<br />
Charge: N/A
<br />
Heat: N/A
<br />
Effect: N/A
<br />
[X, Y] N/A
<br />
[X, Y] {props.x}, {props.y}
</Typography>
</Paper>
);
}
const f = props.fragment.fragment();
let charge = numeralWrapper.formatStaneksGiftCharge(props.fragment.charge);
let effect = "N/A";
// Boosters and cooling don't deal with heat.
if ([FragmentType.Booster, FragmentType.None, FragmentType.Delete].includes(f.type)) {
charge = "N/A";
effect = `${f.power}x adjacent fragment power`;
} else {
effect = Effect(f.type).replace("x%", numeralWrapper.formatPercentage(props.gift.effect(props.fragment) - 1));
}
return (
<Paper>
<Typography>
ID: {props.fragment.id}
<br />
Effect: {effect}
<br />
Power: {numeralWrapper.formatStaneksGiftPower(f.power)}
<br />
Charge: {charge}
<br />
<br />
root [X, Y] {props.fragment.x}, {props.fragment.y}
<br />
[X, Y] {props.x}, {props.y}
</Typography>
</Paper>
);
}

View File

@@ -0,0 +1,31 @@
import * as React from "react";
import { Cell } from "./Cell";
import TableRow from "@mui/material/TableRow";
import TableBody from "@mui/material/TableBody";
import { Table } from "../../ui/React/Table";
type IProps = {
width: number;
height: number;
colorAt: (x: number, y: number) => string;
};
export function FragmentPreview(props: IProps): React.ReactElement {
// switch the width/length to make axis consistent.
const elems = [];
for (let j = 0; j < props.height; j++) {
const cells = [];
for (let i = 0; i < props.width; i++) {
cells.push(<Cell key={i} color={props.colorAt(i, j)} />);
}
elems.push(<TableRow key={j}>{cells}</TableRow>);
}
return (
<Table>
<TableBody>{elems}</TableBody>
</Table>
);
}

View File

@@ -0,0 +1,91 @@
import React, { useState } from "react";
import { Fragments, Fragment, NoneFragment, DeleteFragment } from "../Fragment";
import { FragmentType, Effect } from "../FragmentType";
import { IStaneksGift } from "../IStaneksGift";
import { FragmentPreview } from "./FragmentPreview";
import { numeralWrapper } from "../../ui/numeralFormat";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import MenuItem from "@mui/material/MenuItem";
import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";
type IOptionProps = {
gift: IStaneksGift;
fragment: Fragment;
selectFragment: (fragment: Fragment) => void;
};
function FragmentOption(props: IOptionProps): React.ReactElement {
const remaining =
props.fragment.limit !== Infinity ? (
<>{props.fragment.limit - props.gift.count(props.fragment)} remaining</>
) : (
<></>
);
return (
<Box display="flex">
<Box sx={{ mx: 2 }}>
<FragmentPreview
width={props.fragment.width()}
height={props.fragment.height()}
colorAt={(x, y) => {
return !props.fragment.fullAt(x, y) ? "" : props.fragment.type === FragmentType.Booster ? "blue" : "green";
}}
/>
</Box>
<Typography>
{props.fragment.type === FragmentType.Booster
? `${props.fragment.power}x adjacent fragment power`
: Effect(props.fragment.type)}
<br />
power: {numeralWrapper.formatStaneksGiftPower(props.fragment.power)}
<br />
{remaining}
</Typography>
</Box>
);
}
type IProps = {
gift: IStaneksGift;
selectFragment: (fragment: Fragment) => void;
};
export function FragmentSelector(props: IProps): React.ReactElement {
const [value, setValue] = useState<string | number>("None");
function onChange(event: SelectChangeEvent<string | number>): void {
const v = event.target.value;
setValue(v);
if (v === "None") {
props.selectFragment(NoneFragment);
return;
} else if (v === "Delete") {
props.selectFragment(DeleteFragment);
return;
}
const fragment = Fragments.find((f) => f.id === v);
if (fragment === undefined) throw new Error("Fragment selector selected an undefined fragment with id " + v);
if (typeof v === "number") props.selectFragment(fragment);
}
return (
<Select sx={{ width: "100%" }} onChange={onChange} value={value}>
<MenuItem value="None">
<Typography>None</Typography>
</MenuItem>
<MenuItem value="Delete">
<Typography>Delete</Typography>
</MenuItem>
{Fragments.map((fragment) => (
<MenuItem key={fragment.id} value={fragment.id}>
<FragmentOption
key={fragment.id}
gift={props.gift}
selectFragment={props.selectFragment}
fragment={fragment}
/>
</MenuItem>
))}
</Select>
);
}

145
src/CotMG/ui/Grid.tsx Normal file
View File

@@ -0,0 +1,145 @@
import * as React from "react";
import { Fragment, NoneFragment } from "../Fragment";
import { ActiveFragment } from "../ActiveFragment";
import { FragmentType } from "../FragmentType";
import { IStaneksGift } from "../IStaneksGift";
import { Cell } from "./Cell";
import { FragmentInspector } from "./FragmentInspector";
import { FragmentSelector } from "./FragmentSelector";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import TableRow from "@mui/material/TableRow";
import TableBody from "@mui/material/TableBody";
import { Table } from "../../ui/React/Table";
function zeros(dimensions: number[]): any {
const array = [];
for (let i = 0; i < dimensions[0]; ++i) {
array.push(dimensions.length == 1 ? 0 : zeros(dimensions.slice(1)));
}
return array;
}
function randomColor(fragment: ActiveFragment): string {
// Can't set Math.random seed so copy casino. TODO refactor both RNG later.
let s1 = Math.pow((fragment.x + 1) * (fragment.y + 1), 10);
let s2 = s1;
let s3 = s1;
const colors = [];
for (let i = 0; i < 3; i++) {
s1 = (171 * s1) % 30269;
s2 = (172 * s2) % 30307;
s3 = (170 * s3) % 30323;
colors.push((s1 / 30269.0 + s2 / 30307.0 + s3 / 30323.0) % 1.0);
}
return `rgb(${colors[0] * 256}, ${colors[1] * 256}, ${colors[2] * 256})`;
}
type GridProps = {
gift: IStaneksGift;
};
export function Grid(props: GridProps): React.ReactElement {
function calculateGrid(gift: IStaneksGift): any {
const newgrid = zeros([gift.width(), gift.height()]);
for (let i = 0; i < gift.width(); i++) {
for (let j = 0; j < gift.height(); j++) {
const fragment = gift.fragmentAt(i, j);
if (fragment === null) continue;
newgrid[i][j] = 1;
}
}
return newgrid;
}
const [grid, setGrid] = React.useState(calculateGrid(props.gift));
const [ghostGrid, setGhostGrid] = React.useState(zeros([props.gift.width(), props.gift.height()]));
const [pos, setPos] = React.useState([0, 0]);
const [selectedFragment, setSelectedFragment] = React.useState(NoneFragment);
function moveGhost(worldX: number, worldY: number): void {
const newgrid = zeros([props.gift.width(), props.gift.height()]);
for (let i = 0; i < selectedFragment.shape.length; i++) {
for (let j = 0; j < selectedFragment.shape[i].length; j++) {
if (!selectedFragment.shape[i][j]) continue;
if (worldX + j > newgrid.length - 1) continue;
if (worldY + i > newgrid[worldX + j].length - 1) continue;
newgrid[worldX + j][worldY + i] = 1;
}
}
setGhostGrid(newgrid);
setPos([worldX, worldY]);
}
function deleteAt(worldX: number, worldY: number): boolean {
return props.gift.deleteAt(worldX, worldY);
}
function clickAt(worldX: number, worldY: number): void {
if (selectedFragment.type == FragmentType.None) return;
if (selectedFragment.type == FragmentType.Delete) {
deleteAt(worldX, worldY);
} else {
if (!props.gift.canPlace(worldX, worldY, selectedFragment)) return;
props.gift.place(worldX, worldY, selectedFragment);
}
setGrid(calculateGrid(props.gift));
}
function color(worldX: number, worldY: number): string {
if (ghostGrid[worldX][worldY] && grid[worldX][worldY]) return "red";
if (ghostGrid[worldX][worldY]) return "white";
if (grid[worldX][worldY]) {
const fragment = props.gift.fragmentAt(worldX, worldY);
if (fragment === null) throw new Error("ActiveFragment should not be null");
return randomColor(fragment);
}
return "";
}
function clear(): void {
props.gift.clear();
setGrid(zeros([props.gift.width(), props.gift.height()]));
}
// switch the width/length to make axis consistent.
const elems = [];
for (let j = 0; j < props.gift.height(); j++) {
const cells = [];
for (let i = 0; i < props.gift.width(); i++) {
cells.push(
<Cell key={i} onMouseEnter={() => moveGhost(i, j)} onClick={() => clickAt(i, j)} color={color(i, j)} />,
);
}
elems.push(
<TableRow key={j} className="staneksgift_row">
{cells}
</TableRow>,
);
}
function updateSelectedFragment(fragment: Fragment): void {
setSelectedFragment(fragment);
const newgrid = zeros([props.gift.width(), props.gift.height()]);
setGhostGrid(newgrid);
}
return (
<>
<Button onClick={clear}>Clear</Button>
<Box display="flex">
<Table>
<TableBody>{elems}</TableBody>
</Table>
<FragmentInspector gift={props.gift} x={pos[0]} y={pos[1]} fragment={props.gift.fragmentAt(pos[0], pos[1])} />
</Box>
<FragmentSelector gift={props.gift} selectFragment={updateSelectedFragment} />
</>
);
}

View File

@@ -0,0 +1,36 @@
import React, { useState, useEffect } from "react";
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { CONSTANTS } from "../../Constants";
import { StaneksGiftEvents } from "../StaneksGiftEvents";
import { Grid } from "./Grid";
import { IStaneksGift } from "../IStaneksGift";
import Typography from "@mui/material/Typography";
type IProps = {
staneksGift: IStaneksGift;
};
export function StaneksGiftRoot({ staneksGift }: IProps): React.ReactElement {
const setRerender = useState(true)[1];
function rerender(): void {
setRerender((o) => !o);
}
useEffect(() => StaneksGiftEvents.subscribe(rerender), []);
return (
<>
<Typography variant="h4">Stanek's Gift</Typography>
<Typography>
The gift is a grid on which you can place upgrades called fragments. The main type of fragment increases a stat,
like your hacking skill or agility exp. Once a stat fragment is placed it then needs to be charged via scripts
in order to become useful. The other kind of fragment is called booster fragments. They increase the efficiency
of the charged happening on fragments neighboring them (no diagonal)
</Typography>
{staneksGift.storedCycles > 5 && (
<Typography>
Bonus time: {convertTimeMsToTimeElapsedString(CONSTANTS._idleSpeed * staneksGift.storedCycles)}
</Typography>
)}
<Grid gift={staneksGift} />
</>
);
}

View File

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

View File

@@ -2,6 +2,7 @@ import { IPlayer } from "./PersonObjects/IPlayer";
import { Bladeburner } from "./Bladeburner/Bladeburner";
import { IEngine } from "./IEngine";
import { IRouter } from "./ui/Router";
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
import React from "react";
@@ -19,6 +20,7 @@ import { Corporation } from "./DevMenu/ui/Corporation";
import { CodingContracts } from "./DevMenu/ui/CodingContracts";
import { StockMarket } from "./DevMenu/ui/StockMarket";
import { Sleeves } from "./DevMenu/ui/Sleeves";
import { Stanek } from "./DevMenu/ui/Stanek";
import { TimeSkip } from "./DevMenu/ui/TimeSkip";
import Typography from "@mui/material/Typography";
@@ -52,6 +54,7 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
{props.player.hasWseAccount && <StockMarket />}
{props.player.sleeves.length > 0 && <Sleeves player={props.player} />}
{props.player.augmentations.some((aug) => aug.name === AugmentationNames.StaneksGift1) && <Stanek />}
<TimeSkip player={props.player} engine={props.engine} />
</>

79
src/DevMenu/ui/Stanek.tsx Normal file
View File

@@ -0,0 +1,79 @@
import React from "react";
import { staneksGift } from "../../CotMG/Helper";
import Accordion from "@mui/material/Accordion";
import AccordionSummary from "@mui/material/AccordionSummary";
import AccordionDetails from "@mui/material/AccordionDetails";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Typography from "@mui/material/Typography";
import { Adjuster } from "./Adjuster";
export function Stanek(): React.ReactElement {
function addCycles(): void {
staneksGift.storedCycles = 1e6;
}
function modCycles(modify: number): (x: number) => void {
return function (cycles: number): void {
staneksGift.storedCycles += cycles * modify;
};
}
function resetCycles(): void {
staneksGift.storedCycles = 0;
}
function addCharge(): void {
staneksGift.fragments.forEach((f) => (f.charge = 1e21));
}
function modCharge(modify: number): (x: number) => void {
return function (cycles: number): void {
staneksGift.fragments.forEach((f) => (f.charge += cycles * modify));
};
}
function resetCharge(): void {
staneksGift.fragments.forEach((f) => (f.charge = 0));
}
return (
<Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography>Stanek's Gift</Typography>
</AccordionSummary>
<AccordionDetails>
<table>
<tbody>
<tr>
<td>
<Adjuster
label="cycles"
placeholder="amt"
tons={addCycles}
add={modCycles(1)}
subtract={modCycles(-1)}
reset={resetCycles}
/>
</td>
</tr>
<tr>
<td>
<Adjuster
label="all charge"
placeholder="amt"
tons={addCharge}
add={modCharge(1)}
subtract={modCharge(-1)}
reset={resetCharge}
/>
</td>
</tr>
</tbody>
</table>
</AccordionDetails>
</Accordion>
);
}

View File

@@ -526,4 +526,47 @@ export const FactionInfos: IMap<FactionInfo> = {
true,
false,
),
// prettier-ignore
"Church of the Machine God": new FactionInfo(<>
{" `` "}<br />
{" -odmmNmds: "}<br />
{" `hNmo:..-omNh. "}<br />
{" yMd` `hNh "}<br />
{" mMd oNm "}<br />
{" oMNo .mM/ "}<br />
{" `dMN+ -mM+ "}<br />
{" -mMNo -mN+ "}<br />
{" .+- :mMNo/mN/ "}<br />
{":yNMd. :NMNNN/ "}<br />
{"-mMMMh. /NMMh` "}<br />
{" .dMMMd. /NMMMy` "}<br />
{" `yMMMd. /NNyNMMh` "}<br />
{" `sMMMd. +Nm: +NMMh. "}<br />
{" oMMMm- oNm: /NMMd. "}<br />
{" +NMMmsMm- :mMMd. "}<br />
{" /NMMMm- -mMMd. "}<br />
{" /MMMm- -mMMd. "}<br />
{" `sMNMMm- .mMmo "}<br />
{" `sMd:hMMm. ./. "}<br />
{" `yMy` `yNMd` "}<br />
{" `hMs` oMMy "}<br />
{" `hMh sMN- "}<br />
{" /MM- .NMo "}<br />
{" +MM: :MM+ "}<br />
{" sNNo-.`.-omNy` "}<br />
{" -smNNNNmdo- "}<br />
{" `..` "}<br /><br />
Many cultures predict an end to humanity in the near future, a final
Armageddon that will end the world; but we disagree.
<br /><br />Note that for this faction, reputation can
only be gained by charging Stanek's gift.</>,
[],
false,
false,
false,
false,
true,
true,
),
};

View File

@@ -24,7 +24,7 @@ export function loadFactions(saveString: string): void {
}
}
function AddToFactions(faction: Faction): void {
export 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
function resetFaction(newFactionObject: Faction): void {
export 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 { MathJax, MathJaxContext } from "better-react-mathjax";
import { MathComponent } from "mathjax-react";
import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper";
@@ -98,9 +98,9 @@ export function DonateOption(props: IProps): React.ReactElement {
}}
/>
<Typography>
<MathJaxContext>
<MathJax>{`\\(reputation = \\frac{\\text{donation amount} \\cdot \\text{reputation multiplier}}{10^{${digits}}}\\)`}</MathJax>
</MathJaxContext>
<MathComponent
tex={String.raw`reputation = \frac{\text{donation amount} \times \text{reputation multiplier}}{10^{${digits}}}`}
/>
</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 { MathJax, MathJaxContext } from "better-react-mathjax";
import { MathComponent } from "mathjax-react";
import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles";
@@ -57,14 +57,10 @@ export function Info(props: IProps): React.ReactElement {
You will have <Favor favor={props.faction.favor + favorGain} /> faction favor after installing an
Augmentation.
</Typography>
<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>
<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}`}
/>
</>
}
>
@@ -85,13 +81,8 @@ 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>
<MathJaxContext>
<MathJax>{"\\(\\huge{r = reputation}\\)"}</MathJax>
</MathJaxContext>
<MathJaxContext>
<MathJax>{"\\(\\huge{\\Delta r = \\Delta r \\times \\frac{100+favor}{100}}\\)"}</MathJax>
</MathJaxContext>
<MathComponent tex={String.raw`\large{r = reputation}`} />
<MathComponent tex={String.raw`\large{\Delta r = \Delta r \times \frac{100+favor}{100}}`} />
</>
}
>

View File

@@ -97,7 +97,7 @@ export class Gang {
this.processExperienceGains(cycles);
this.processTerritoryAndPowerGains(cycles);
this.storedCycles -= cycles;
} catch (e: any) {
} catch (e) {
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: any) {
} catch (e) {
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)
*/
interface IGangMemberTaskMetadata {
export 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)
*/
interface IGangMemberUpgradeMetadata {
export interface IGangMemberUpgradeMetadata {
cost: number;
mults: IMults;
name: string;

View File

@@ -512,6 +512,7 @@ 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

@@ -85,7 +85,7 @@ Cities[CityName.Chongqing].asciiArt = `
\\ o 78 [kuaigong international]
\\ /
38 o----x--x------x------A------G--
/ 39 | 41 [church]
/ 39 | 41 [church]
37 o + 79 o--x--x-C-0
/ | /
/ x-----+-----x-----0 [hospital]

View File

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

View File

@@ -17,6 +17,9 @@ import Button from "@mui/material/Button";
import { Location } from "../Location";
import { CreateCorporationModal } from "../../Corporation/ui/CreateCorporationModal";
import { LocationName } from "../data/LocationNames";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Factions } from "../../Faction/Factions";
import { joinFaction } from "../../Faction/FactionHelpers";
import { use } from "../../ui/Context";
@@ -109,6 +112,21 @@ export function SpecialLocation(props: IProps): React.ReactElement {
return <Button onClick={handleResleeving}>Re-Sleeve</Button>;
}
function handleCotMG(): void {
const faction = Factions["Church of the Machine God"];
if (!player.factions.includes("Church of the Machine God")) {
joinFaction(faction);
}
if (
!player.augmentations.some((a) => a.name === AugmentationNames.StaneksGift1) &&
!player.queuedAugmentations.some((a) => a.name === AugmentationNames.StaneksGift1)
) {
player.queueAugmentation(AugmentationNames.StaneksGift1);
}
router.toFaction(faction);
}
function renderCotMG(): React.ReactElement {
// prettier-ignore
const symbol = <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>
@@ -140,15 +158,56 @@ export function SpecialLocation(props: IProps): React.ReactElement {
{" sNNo-.`.-omNy` "}<br />
{" -smNNNNmdo- "}<br />
{" `..` "}</Typography>
if (player.factions.includes("Church of the Machine God")) {
return (
<>
<Typography>
<i>Allison "Mother" Stanek: Welcome back my child!</i>
</Typography>
{symbol}
</>
);
}
if (!player.canAccessCotMG()) {
return (
<>
<Typography>
A decrepit altar stands in the middle of a dilapidated church.
<br />
<br />A symbol is carved in the altar.
</Typography>
<br />
{symbol}
</>
);
}
if (
player.augmentations.filter((a) => a.name !== AugmentationNames.NeuroFluxGovernor).length > 0 ||
player.queuedAugmentations.filter((a) => a.name !== AugmentationNames.NeuroFluxGovernor).length > 0
) {
return (
<>
<Typography>
<i>
Allison "Mother" Stanek: Begone you filth! My gift must be the first modification that your body should
have!
</i>
</Typography>
</>
);
}
return (
<>
<Typography>
A decrepit altar stands in the middle of a dilapidated church.
<br />
<br />A symbol is carved in the altar.
<i>
Allison "Mother" Stanek: Welcome child, I see your body is pure. Are you ready to ascend beyond our human
form? If you are, accept my gift.
</i>
</Typography>
<br />
<Button onClick={handleCotMG}>Accept Stanek's Gift</Button>
{symbol}
</>
);

View File

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

View File

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

11
src/Milestones/Quest.ts Normal file
View File

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

View File

@@ -54,6 +54,15 @@ export const RamCostConstants: IMap<number> = {
ScriptGangApiBaseRamCost: 4,
ScriptBladeburnerApiBaseRamCost: 4,
ScriptStanekCharge: 0.4,
ScriptStanekFragmentDefinitions: 0,
ScriptStanekPlacedFragments: 5,
ScriptStanekClear: 0,
ScriptStanekCanPlace: 0.5,
ScriptStanekPlace: 5,
ScriptStanekFragmentAt: 2,
ScriptStanekDeleteAt: 0.15,
};
export const RamCosts: IMap<any> = {
@@ -313,6 +322,17 @@ export const RamCosts: IMap<any> = {
purchaseSleeveAug: () => RamCostConstants.ScriptSleeveBaseRamCost,
},
stanek: {
charge: () => RamCostConstants.ScriptStanekCharge,
fragmentDefinitions: () => RamCostConstants.ScriptStanekFragmentDefinitions,
placedFragments: () => RamCostConstants.ScriptStanekPlacedFragments,
clear: () => RamCostConstants.ScriptStanekClear,
canPlace: () => RamCostConstants.ScriptStanekCanPlace,
place: () => RamCostConstants.ScriptStanekPlace,
fragmentAt: () => RamCostConstants.ScriptStanekFragmentAt,
deleteAt: () => RamCostConstants.ScriptStanekDeleteAt,
},
heart: {
// Easter egg function
break: () => 0,

View File

@@ -106,11 +106,6 @@ 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;
@@ -137,13 +132,15 @@ 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,7 +10,6 @@ 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;
@@ -68,16 +67,6 @@ 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

@@ -1,224 +0,0 @@
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

@@ -1,403 +0,0 @@
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

@@ -1,109 +0,0 @@
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

@@ -1,305 +0,0 @@
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

@@ -1,37 +0,0 @@
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

@@ -1,187 +0,0 @@
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,13 +1,7 @@
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,85 @@
import { INetscriptHelper } from "./INetscriptHelper";
import { IPlayer } from "../PersonObjects/IPlayer";
import { WorkerScript } from "../Netscript/WorkerScript";
import { netscriptDelay } from "../NetscriptEvaluator";
import { getRamCost } from "../Netscript/RamCostGenerator";
import { staneksGift } from "../CotMG/Helper";
import { Fragments, FragmentById } from "../CotMG/Fragment";
export interface INetscriptStanek {
charge(worldX: number, worldY: number): any;
fragmentDefinitions(): any;
placedFragments(): any;
clear(): void;
canPlace(worldX: number, worldY: number, fragmentId: number): boolean;
place(worldX: number, worldY: number, fragmentId: number): boolean;
fragmentAt(worldX: number, worldY: number): any;
deleteAt(worldX: number, worldY: number): boolean;
}
export function NetscriptStanek(
player: IPlayer,
workerScript: WorkerScript,
helper: INetscriptHelper,
): INetscriptStanek {
return {
charge: function (worldX: any, worldY: any): any {
helper.updateDynamicRam("charge", getRamCost("stanek", "charge"));
//checkStanekAPIAccess("charge");
const fragment = staneksGift.fragmentAt(worldX, worldY);
if (!fragment) throw helper.makeRuntimeErrorMsg("stanek.charge", `No fragment at (${worldX}, ${worldY})`);
const time = staneksGift.inBonus() ? 200 : 1000;
return netscriptDelay(time, workerScript).then(function () {
if (workerScript.env.stopFlag) {
return Promise.reject(workerScript);
}
const ram = workerScript.scriptRef.ramUsage * workerScript.scriptRef.threads;
return Promise.resolve(staneksGift.charge(worldX, worldY, ram));
});
},
fragmentDefinitions: function () {
helper.updateDynamicRam("fragmentDefinitions", getRamCost("stanek", "fragmentDefinitions"));
//checkStanekAPIAccess("fragmentDefinitions");
return Fragments.map((f) => f.copy());
},
placedFragments: function () {
helper.updateDynamicRam("placedFragments", getRamCost("stanek", "placedFragments"));
//checkStanekAPIAccess("placedFragments");
return staneksGift.fragments.map((af) => {
return { ...af.copy(), ...af.fragment().copy() };
});
},
clear: function () {
helper.updateDynamicRam("clear", getRamCost("stanek", "clear"));
//checkStanekAPIAccess("clear");
staneksGift.clear();
},
canPlace: function (worldX: any, worldY: any, fragmentId: any): any {
helper.updateDynamicRam("canPlace", getRamCost("stanek", "canPlace"));
//checkStanekAPIAccess("canPlace");
const fragment = FragmentById(fragmentId);
if (!fragment) throw helper.makeRuntimeErrorMsg("stanek.canPlace", `Invalid fragment id: ${fragmentId}`);
return staneksGift.canPlace(worldX, worldY, fragment);
},
place: function (worldX: any, worldY: any, fragmentId: any): any {
helper.updateDynamicRam("place", getRamCost("stanek", "place"));
//checkStanekAPIAccess("place");
const fragment = FragmentById(fragmentId);
if (!fragment) throw helper.makeRuntimeErrorMsg("stanek.place", `Invalid fragment id: ${fragmentId}`);
return staneksGift.place(worldX, worldY, fragment);
},
fragmentAt: function (worldX: any, worldY: any): any {
helper.updateDynamicRam("fragmentAt", getRamCost("stanek", "fragmentAt"));
//checkStanekAPIAccess("fragmentAt");
const fragment = staneksGift.fragmentAt(worldX, worldY);
if (fragment !== null) return fragment.copy();
return null;
},
deleteAt: function (worldX: any, worldY: any): any {
helper.updateDynamicRam("deleteAt", getRamCost("stanek", "deleteAt"));
//checkStanekAPIAccess("deleteAt");
return staneksGift.deleteAt(worldX, worldY);
},
};
}

View File

@@ -1,377 +0,0 @@
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

@@ -1,36 +0,0 @@
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;
}

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