mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-16 14:28:36 +02:00
Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42704d8695 | ||
|
|
e75197dee3 | ||
|
|
9e92df47a5 | ||
|
|
c110c22efb | ||
|
|
c9ab7908a7 | ||
|
|
3ab306f9d7 | ||
|
|
f08aa8924c | ||
|
|
c4914fa54f | ||
|
|
fa5e2f4964 | ||
|
|
77eda1fd75 | ||
|
|
c987c91a11 | ||
|
|
feaa74ed34 | ||
|
|
701fba7ec7 | ||
|
|
51bd626e88 | ||
|
|
ab4863e7df | ||
|
|
1a8bcf66cc | ||
|
|
7bfceb1690 | ||
|
|
27e22814a9 | ||
|
|
ceb4e304fd | ||
|
|
e2d74f9432 | ||
|
|
79345a49b4 | ||
|
|
7066a793a1 | ||
|
|
2a5cf62168 | ||
|
|
6495be5705 | ||
|
|
0d6d05db49 | ||
|
|
5d59620dce | ||
|
|
60d95a90d0 | ||
|
|
51debc60da | ||
|
|
faf625b34d | ||
|
|
1a8b194341 | ||
|
|
386f8a11c5 | ||
|
|
4278191b0e | ||
|
|
6d2b8b4f6f | ||
|
|
b148b2f0b5 | ||
|
|
4a9bac99d2 | ||
|
|
0b3c114cd0 | ||
|
|
49cc75a575 | ||
|
|
e0d631f8b3 | ||
|
|
8289c9fc75 | ||
|
|
d66e36b637 | ||
|
|
6cd7465b82 | ||
|
|
c7125e2e46 | ||
|
|
a564957092 | ||
|
|
4b8e63f342 | ||
|
|
480d47eece | ||
|
|
4de20f8cce | ||
|
|
4b38d296a8 | ||
|
|
9ac75d5bf5 | ||
|
|
6561413137 | ||
|
|
1fb5105d0a | ||
|
|
b67c03ff8a | ||
|
|
7db3716256 | ||
|
|
ee5a70901b | ||
|
|
63b2c77907 | ||
|
|
aa3ad3164c | ||
|
|
fea25249a8 | ||
|
|
3826de72ef | ||
|
|
5098ef6232 | ||
|
|
27ee65f524 | ||
|
|
1d0f193c34 | ||
|
|
08908c87ea | ||
|
|
3957a517db | ||
|
|
21daab32c1 | ||
|
|
5e2ed7a79e | ||
|
|
d9e60ea124 | ||
|
|
2750eb293a |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
.vscode
|
||||
Changelog.txt
|
||||
Netburner.txt
|
||||
/doc/build
|
||||
@@ -6,3 +7,6 @@ Netburner.txt
|
||||
/test/*.map
|
||||
/test/*.bundle.*
|
||||
/test/*.css
|
||||
|
||||
# editor files
|
||||
.vscode
|
||||
|
||||
9
babel.config.js
Normal file
9
babel.config.js
Normal file
@@ -0,0 +1,9 @@
|
||||
const TEST = process.env.NODE_ENV === "test";
|
||||
|
||||
module.exports = {
|
||||
"presets": [
|
||||
"@babel/preset-react",
|
||||
TEST && "@babel/preset-env",
|
||||
TEST && "@babel/preset-typescript",
|
||||
].filter(Boolean),
|
||||
}
|
||||
@@ -99,7 +99,6 @@
|
||||
}
|
||||
|
||||
.log-box-log-container {
|
||||
max-width: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@@ -113,6 +112,13 @@
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
.log-box-button:hover,
|
||||
.log-box-button:focus {
|
||||
color: var(--my-font-color);
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dialog-box-content {
|
||||
z-index: 2;
|
||||
background-color: var(--my-background-color);
|
||||
|
||||
4
dist/engine.bundle.js
vendored
4
dist/engine.bundle.js
vendored
File diff suppressed because one or more lines are too long
2
dist/engineStyle.bundle.js
vendored
2
dist/engineStyle.bundle.js
vendored
@@ -1,2 +1,2 @@
|
||||
!function(n){function t(t){for(var e,i,f=t[0],c=t[1],l=t[2],p=0,s=[];p<f.length;p++)i=f[p],u[i]&&s.push(u[i][0]),u[i]=0;for(e in c)Object.prototype.hasOwnProperty.call(c,e)&&(n[e]=c[e]);for(a&&a(t);s.length;)s.shift()();return r.push.apply(r,l||[]),o()}function o(){for(var n,t=0;t<r.length;t++){for(var o=r[t],e=!0,f=1;f<o.length;f++){var c=o[f];0!==u[c]&&(e=!1)}e&&(r.splice(t--,1),n=i(i.s=o[0]))}return n}var e={},u={1:0},r=[];function i(t){if(e[t])return e[t].exports;var o=e[t]={i:t,l:!1,exports:{}};return n[t].call(o.exports,o,o.exports,i),o.l=!0,o.exports}i.m=n,i.c=e,i.d=function(n,t,o){i.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:o})},i.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},i.t=function(n,t){if(1&t&&(n=i(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var o=Object.create(null);if(i.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var e in n)i.d(o,e,function(t){return n[t]}.bind(null,e));return o},i.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return i.d(t,"a",t),t},i.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},i.p="";var f=window.webpackJsonp=window.webpackJsonp||[],c=f.push.bind(f);f.push=t,f=f.slice();for(var l=0;l<f.length;l++)t(f[l]);var a=c;r.push([862,0]),o()}({799:function(n,t,o){},801:function(n,t,o){},803:function(n,t,o){},805:function(n,t,o){},807:function(n,t,o){},809:function(n,t,o){},811:function(n,t,o){},813:function(n,t,o){},815:function(n,t,o){},817:function(n,t,o){},819:function(n,t,o){},821:function(n,t,o){},823:function(n,t,o){},825:function(n,t,o){},827:function(n,t,o){},829:function(n,t,o){},831:function(n,t,o){},833:function(n,t,o){},835:function(n,t,o){},837:function(n,t,o){},839:function(n,t,o){},841:function(n,t,o){},843:function(n,t,o){},845:function(n,t,o){},847:function(n,t,o){},849:function(n,t,o){},851:function(n,t,o){},853:function(n,t,o){},855:function(n,t,o){},857:function(n,t,o){},859:function(n,t,o){},862:function(n,t,o){"use strict";o.r(t);o(861),o(859),o(857),o(855),o(853),o(851),o(849),o(847),o(845),o(843),o(841),o(839),o(837),o(835),o(833),o(831),o(829),o(827),o(825),o(823),o(821),o(819),o(817),o(815),o(813),o(811),o(809),o(807),o(805),o(803),o(801),o(799)}});
|
||||
!function(n){function t(t){for(var e,i,f=t[0],c=t[1],l=t[2],p=0,s=[];p<f.length;p++)i=f[p],u[i]&&s.push(u[i][0]),u[i]=0;for(e in c)Object.prototype.hasOwnProperty.call(c,e)&&(n[e]=c[e]);for(a&&a(t);s.length;)s.shift()();return r.push.apply(r,l||[]),o()}function o(){for(var n,t=0;t<r.length;t++){for(var o=r[t],e=!0,f=1;f<o.length;f++){var c=o[f];0!==u[c]&&(e=!1)}e&&(r.splice(t--,1),n=i(i.s=o[0]))}return n}var e={},u={1:0},r=[];function i(t){if(e[t])return e[t].exports;var o=e[t]={i:t,l:!1,exports:{}};return n[t].call(o.exports,o,o.exports,i),o.l=!0,o.exports}i.m=n,i.c=e,i.d=function(n,t,o){i.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:o})},i.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},i.t=function(n,t){if(1&t&&(n=i(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var o=Object.create(null);if(i.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var e in n)i.d(o,e,function(t){return n[t]}.bind(null,e));return o},i.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return i.d(t,"a",t),t},i.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},i.p="";var f=window.webpackJsonp=window.webpackJsonp||[],c=f.push.bind(f);f.push=t,f=f.slice();for(var l=0;l<f.length;l++)t(f[l]);var a=c;r.push([866,0]),o()}({803:function(n,t,o){},805:function(n,t,o){},807:function(n,t,o){},809:function(n,t,o){},811:function(n,t,o){},813:function(n,t,o){},815:function(n,t,o){},817:function(n,t,o){},819:function(n,t,o){},821:function(n,t,o){},823:function(n,t,o){},825:function(n,t,o){},827:function(n,t,o){},829:function(n,t,o){},831:function(n,t,o){},833:function(n,t,o){},835:function(n,t,o){},837:function(n,t,o){},839:function(n,t,o){},841:function(n,t,o){},843:function(n,t,o){},845:function(n,t,o){},847:function(n,t,o){},849:function(n,t,o){},851:function(n,t,o){},853:function(n,t,o){},855:function(n,t,o){},857:function(n,t,o){},859:function(n,t,o){},861:function(n,t,o){},863:function(n,t,o){},866:function(n,t,o){"use strict";o.r(t);o(865),o(863),o(861),o(859),o(857),o(855),o(853),o(851),o(849),o(847),o(845),o(843),o(841),o(839),o(837),o(835),o(833),o(831),o(829),o(827),o(825),o(823),o(821),o(819),o(817),o(815),o(813),o(811),o(809),o(807),o(805),o(803)}});
|
||||
//# sourceMappingURL=engineStyle.bundle.js.map
|
||||
7
dist/engineStyle.css
vendored
7
dist/engineStyle.css
vendored
@@ -2433,7 +2433,6 @@ input[type="checkbox"] {
|
||||
justify-content: space-between; }
|
||||
|
||||
.log-box-log-container {
|
||||
max-width: 400px;
|
||||
overflow-y: auto; }
|
||||
|
||||
.log-box-button {
|
||||
@@ -2445,6 +2444,12 @@ input[type="checkbox"] {
|
||||
border: 1px solid #fff;
|
||||
background-color: #000; }
|
||||
|
||||
.log-box-button:hover,
|
||||
.log-box-button:focus {
|
||||
color: var(--my-font-color);
|
||||
text-decoration: none;
|
||||
cursor: pointer; }
|
||||
|
||||
.dialog-box-content {
|
||||
z-index: 2;
|
||||
background-color: var(--my-background-color);
|
||||
|
||||
30
dist/vendor.bundle.js
vendored
30
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
@@ -3,6 +3,85 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
v0.52.9 - 2021-07-27 Less lag! (hydroflame & community)
|
||||
-------------------------------------------
|
||||
|
||||
** Active Scripts page **
|
||||
|
||||
* Now less laggy, has pagination.
|
||||
|
||||
** File diagnostic **
|
||||
|
||||
* Added a popup found under options that shows the files you own and how
|
||||
large they are. This help find bugs and leftover massive logs files.
|
||||
|
||||
** Corporation **
|
||||
|
||||
* Added safeguard against a very specific bug that causes NaN money. I'm
|
||||
still not sure what the root cause is but it should prevent corp from
|
||||
breaking.
|
||||
|
||||
** Netscript **
|
||||
|
||||
* tprintf is a new function that doesn't print the filename.
|
||||
|
||||
** Misc. **
|
||||
|
||||
* Infiltration kills you if you try to automate it. (@threehams)
|
||||
* Fix beautify button not working
|
||||
* Added bladeburner_analysis_mult to getPlayer() (@brubsby)
|
||||
* Fixed joining bladeburner via netscript functions. (@omuretsu)
|
||||
* All bladeburner actions are click-to-copy
|
||||
* nerf noodle bar
|
||||
|
||||
v0.52.8 - 2021-07-23 Fixing the previous patch tbh ROUND 2 (hydroflame)
|
||||
-------------------------------------------
|
||||
|
||||
** Script editor **
|
||||
|
||||
* Correctly reloads old script when clicking "Script Editor"
|
||||
* No longer jumps to the end of the text for no reason.
|
||||
|
||||
** Hash upgrades **
|
||||
|
||||
* Fixed an issue where the default option would say ecorp but was really
|
||||
foodnstuff
|
||||
|
||||
** Misc. **
|
||||
|
||||
* The "Delete all active script" button under the options has a clearer
|
||||
description.
|
||||
* Removed some debug console.log
|
||||
* nerf noodle bar
|
||||
|
||||
v0.52.7 - 2021-07-21 Fixing the previous patch tbh (hydroflame)
|
||||
-------------------------------------------
|
||||
|
||||
** Netscript **
|
||||
|
||||
* API BREAKING CHANGE: getActionEstimatedSuccessChance now returns a pair of
|
||||
value to reflect the UI changes. I'm very sorry.
|
||||
|
||||
** Bladeburner **
|
||||
|
||||
* General actions now display time required.
|
||||
* Recruitment now displays success chance.
|
||||
* All other success chance now display a range instead of a single value
|
||||
The real value is guaranteed to be within that range.
|
||||
|
||||
** Misc. **
|
||||
|
||||
* Fix tutorial not working after Monaco upate
|
||||
* Fix logbox logs not taking up the whole logbox
|
||||
* Fix script editor shortcut (ctrl+b)
|
||||
* Fix Corporation popup appearing in the wrong order, hiding one of them
|
||||
* Fix error when loading Corp
|
||||
* Fix logbox dragging (smoother now)
|
||||
* Fix logbox name collision
|
||||
* Fix logbox allowing to open the same box multiple times
|
||||
* Fix netscript write.
|
||||
* nerf noodle bar
|
||||
|
||||
v0.52.6 - 2021-07-21 Logboxes and VS-code (hydroflame)
|
||||
-------------------------------------------
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ documentation_title = '{0} Documentation'.format(project)
|
||||
# The short X.Y version.
|
||||
version = '0.52'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.52.5'
|
||||
release = '0.52.9'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
17
doc/source/netscript/basicfunctions/tprintf.rst
Normal file
17
doc/source/netscript/basicfunctions/tprintf.rst
Normal file
@@ -0,0 +1,17 @@
|
||||
tprint() Netscript Function
|
||||
===========================
|
||||
|
||||
.. js:function:: tprintf(format, args...)
|
||||
|
||||
:RAM cost: 0 GB
|
||||
:param format: Format of the string to be printed.
|
||||
:param args: Values to be formatted
|
||||
|
||||
Prints a raw formatted string to the terminal.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
tprintf("Hello world!"); // Prints "Hello world!" to the terminal.
|
||||
tprintf("Hello %s", "world!"); // Prints "Hello world!" to the terminal.
|
||||
@@ -6,10 +6,10 @@ getActionEstimatedSuccessChance() Netscript Function
|
||||
:RAM cost: 4 GB
|
||||
:param string type: Type of action. See :ref:`bladeburner_action_types`
|
||||
:param string name: Name of action. Must be an exact match
|
||||
:returns: Estimated success chance in decimal
|
||||
:returns: Array of 2 number, lower and upper bound of the action chance.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
bladeburner.getActionEstimatedSuccessChance("Contracts", "Tracking"); // returns: .3
|
||||
bladeburner.getActionEstimatedSuccessChance("Contracts", "Tracking"); // returns: [.3, .6]
|
||||
|
||||
@@ -19,6 +19,7 @@ This includes information such as function signatures, what they do, and their r
|
||||
sleep() <basicfunctions/sleep>
|
||||
print() <basicfunctions/print>
|
||||
tprint() <basicfunctions/tprint>
|
||||
tprintf() <basicfunctions/tprint>
|
||||
clearLog() <basicfunctions/clearLog>
|
||||
disableLog() <basicfunctions/disableLog>
|
||||
enableLog() <basicfunctions/enableLog>
|
||||
|
||||
14
index.html
14
index.html
@@ -581,10 +581,12 @@
|
||||
Copy Save data to Clipboard
|
||||
</button>
|
||||
<button id="debug-delete-scripts-link" class="a-link-button tooltip">
|
||||
Delete all active scripts
|
||||
Force kill all active scripts
|
||||
<span class="tooltiptextleft">
|
||||
Forcefully kill all active running scripts, in case there is a bug or some unexpected issue with the game. After
|
||||
using this, save the game and then reload the page.
|
||||
using this, save the game and then reload the page. This is different then normal kill in that normal kill
|
||||
will tell the script to shut down while force kill just removes the references to it (and it should crash on it's own).
|
||||
This will not remove the files on your computer. Just forcefully kill all running instance of all scripts.
|
||||
</span>
|
||||
</button>
|
||||
<button id="debug-soft-reset" class="a-link-button tooltip">
|
||||
@@ -593,6 +595,14 @@
|
||||
Perform a soft reset. Resets everything as if you had just purchased an Augmentation.
|
||||
</span>
|
||||
</button>
|
||||
<button id="debug-files" class="a-link-button tooltip">
|
||||
Diagnose files
|
||||
<span class="tooltiptextleft">
|
||||
If your save file is extremely big you can use this button
|
||||
to view a map of all the files on every server. Be careful
|
||||
there might be spoilers.
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
9
jest.config.js
Normal file
9
jest.config.js
Normal file
@@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
setupFiles: ["./jest.setup.js"],
|
||||
moduleFileExtensions: ["ts", "tsx", "js", "jsx"],
|
||||
transform: {
|
||||
"^.+\\.(js|jsx|ts|tsx)$": "babel-jest",
|
||||
},
|
||||
// testMatch: ["**/?(*.)+(test).[jt]s?(x)"],
|
||||
testEnvironment: "jsdom",
|
||||
};
|
||||
2
jest.setup.js
Normal file
2
jest.setup.js
Normal file
@@ -0,0 +1,2 @@
|
||||
import "regenerator-runtime/runtime";
|
||||
global.$ = require("jquery");
|
||||
14676
package-lock.json
generated
14676
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
17
package.json
17
package.json
@@ -50,17 +50,18 @@
|
||||
"description": "A cyberpunk-themed incremental game",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.3.4",
|
||||
"@babel/preset-env": "^7.15.0",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"@types/chai": "^4.1.7",
|
||||
"@babel/preset-typescript": "^7.15.0",
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/lodash": "^4.14.168",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@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",
|
||||
"chai": "^4.2.0",
|
||||
"css-loader": "^0.28.11",
|
||||
"es6-promise-polyfill": "^1.1.1",
|
||||
"eslint": "^7.24.0",
|
||||
@@ -69,6 +70,7 @@
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"i18n-webpack-plugin": "^1.0.0",
|
||||
"istanbul": "^0.4.5",
|
||||
"jest": "^27.0.6",
|
||||
"js-beautify": "^1.5.10",
|
||||
"jsdom": "^15.0.0",
|
||||
"jsdom-global": "^3.0.2",
|
||||
@@ -78,10 +80,9 @@
|
||||
"lodash": "^4.17.21",
|
||||
"mini-css-extract-plugin": "^0.4.1",
|
||||
"mkdirp": "^0.5.1",
|
||||
"mocha": "^6.1.4",
|
||||
"mochapack": "^1.1.1",
|
||||
"null-loader": "^1.0.0",
|
||||
"raw-loader": "~0.5.0",
|
||||
"regenerator-runtime": "^0.13.9",
|
||||
"sass-loader": "^7.0.3",
|
||||
"script-loader": "~0.7.0",
|
||||
"should": "^11.1.1",
|
||||
@@ -124,10 +125,10 @@
|
||||
"lint:jsts": "eslint --fix '*.{js,jsx,ts,tsx}' './src/**/*.{js,jsx,ts,tsx}' './test/**/*.{js,jsx,ts,tsx}' './utils/**/*.{js,jsx,ts,tsx}'",
|
||||
"lint:style": "stylelint --fix ./css/*",
|
||||
"preinstall": "node ./scripts/engines-check.js",
|
||||
"test": "mochapack --webpack-config webpack.config-test.js -r jsdom-global/register ./test/index.js",
|
||||
"test:container": "mochapack --webpack-config webpack.config-test.js --slow 2000 --timeout 10000 -r jsdom-global/register ./test/index.js",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"watch": "webpack --watch --mode production",
|
||||
"watch:dev": "webpack --watch --mode development"
|
||||
},
|
||||
"version": "0.52.6"
|
||||
"version": "0.52.9"
|
||||
}
|
||||
|
||||
@@ -194,6 +194,23 @@ export class Action implements IAction {
|
||||
return 1;
|
||||
}
|
||||
|
||||
getEstSuccessChance(inst: IBladeburner): number[] {
|
||||
function clamp(x: number): number {
|
||||
return Math.max(0, Math.min(x, 1));
|
||||
}
|
||||
const est = this.getSuccessChance(inst, {est: true});
|
||||
const real = this.getSuccessChance(inst);
|
||||
const diff = Math.abs(real-est);
|
||||
let low = real-diff;
|
||||
let high = real+diff;
|
||||
const city = inst.getCurrentCity();
|
||||
const r = city.pop / city.popEst;
|
||||
|
||||
if(r < 1) low *= r;
|
||||
else high *= r;
|
||||
return [clamp(low), clamp(high)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inst - Bladeburner Object
|
||||
* @params - options:
|
||||
|
||||
@@ -606,13 +606,11 @@ export class Bladeburner implements IBladeburner {
|
||||
const flag = args[1];
|
||||
if (flag.toLowerCase() === "status") {
|
||||
this.postToConsole("Automation: " + (this.automateEnabled ? "enabled" : "disabled"));
|
||||
if (this.automateEnabled) {
|
||||
this.postToConsole("When your stamina drops to " + formatNumber(this.automateThreshLow, 0) +
|
||||
", you will automatically switch to " + this.automateActionLow.name +
|
||||
". When your stamina recovers to " +
|
||||
formatNumber(this.automateThreshHigh, 0) + ", you will automatically " +
|
||||
"switch to " + this.automateActionHigh.name + ".");
|
||||
}
|
||||
this.postToConsole("When your stamina drops to " + formatNumber(this.automateThreshLow, 0) +
|
||||
", you will automatically switch to " + this.automateActionLow.name +
|
||||
". When your stamina recovers to " +
|
||||
formatNumber(this.automateThreshHigh, 0) + ", you will automatically " +
|
||||
"switch to " + this.automateActionHigh.name + ".");
|
||||
|
||||
} else if (flag.toLowerCase().includes("en")) {
|
||||
if (!(this.automateActionLow instanceof ActionIdentifier) ||
|
||||
@@ -1788,18 +1786,18 @@ export class Bladeburner implements IBladeburner {
|
||||
}
|
||||
}
|
||||
|
||||
getActionEstimatedSuccessChanceNetscriptFn(player: IPlayer, type: string, name: string, workerScript: WorkerScript): number {
|
||||
getActionEstimatedSuccessChanceNetscriptFn(player: IPlayer, type: string, name: string, workerScript: WorkerScript): number[] {
|
||||
const errorLogText = `Invalid action: type='${type}' name='${name}'`
|
||||
const actionId = this.getActionIdFromTypeAndName(type, name);
|
||||
if (actionId == null) {
|
||||
workerScript.log("bladeburner.getActionEstimatedSuccessChance", errorLogText);
|
||||
return -1;
|
||||
return [-1, -1];
|
||||
}
|
||||
|
||||
const actionObj = this.getActionObject(actionId);
|
||||
if (actionObj == null) {
|
||||
workerScript.log("bladeburner.getActionEstimatedSuccessChance", errorLogText);
|
||||
return -1;
|
||||
return [-1, -1];
|
||||
}
|
||||
|
||||
switch (actionId.type) {
|
||||
@@ -1807,16 +1805,17 @@ export class Bladeburner implements IBladeburner {
|
||||
case ActionTypes["Operation"]:
|
||||
case ActionTypes["BlackOp"]:
|
||||
case ActionTypes["BlackOperation"]:
|
||||
return actionObj.getSuccessChance(this, {est:true});
|
||||
return actionObj.getEstSuccessChance(this);
|
||||
case ActionTypes["Training"]:
|
||||
case ActionTypes["Field Analysis"]:
|
||||
case ActionTypes["FieldAnalysis"]:
|
||||
return 1;
|
||||
return [1, 1];
|
||||
case ActionTypes["Recruitment"]:
|
||||
return this.getRecruitmentSuccessChance(player);
|
||||
const recChance = this.getRecruitmentSuccessChance(player);
|
||||
return [recChance, recChance];
|
||||
default:
|
||||
workerScript.log("bladeburner.getActionEstimatedSuccessChance", errorLogText);
|
||||
return -1;
|
||||
return [-1, -1];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ export interface IAction {
|
||||
getActionTypeSkillSuccessBonus(inst: IBladeburner): number;
|
||||
getChaosCompetencePenalty(inst: IBladeburner, params: ISuccessChanceParams): number;
|
||||
getChaosDifficultyBonus(inst: IBladeburner): number;
|
||||
getEstSuccessChance(inst: IBladeburner): number[];
|
||||
getSuccessChance(inst: IBladeburner, params: ISuccessChanceParams): number;
|
||||
getSuccessesNeededForNextLevel(baseSuccessesPerLevel: number): number;
|
||||
setMaxLevel(baseSuccessesPerLevel: number): void;
|
||||
|
||||
@@ -67,7 +67,7 @@ export interface IBladeburner {
|
||||
getSkillNamesNetscriptFn(): string[];
|
||||
startActionNetscriptFn(player: IPlayer, type: string, name: string, workerScript: WorkerScript): boolean;
|
||||
getActionTimeNetscriptFn(player: IPlayer, type: string, name: string, workerScript: WorkerScript): number;
|
||||
getActionEstimatedSuccessChanceNetscriptFn(player: IPlayer, type: string, name: string, workerScript: WorkerScript): number;
|
||||
getActionEstimatedSuccessChanceNetscriptFn(player: IPlayer, type: string, name: string, workerScript: WorkerScript): number[];
|
||||
getActionCountRemainingNetscriptFn(type: string, name: string, workerScript: WorkerScript): number;
|
||||
getSkillLevelNetscriptFn(skillName: string, workerScript: WorkerScript): number;
|
||||
getSkillUpgradeCostNetscriptFn(skillName: string, workerScript: WorkerScript): number;
|
||||
|
||||
@@ -44,6 +44,6 @@ export function AllPages(props: IProps): React.ReactElement {
|
||||
{page === 'BlackOps' && <BlackOpPage bladeburner={props.bladeburner} player={props.player} />}
|
||||
{page === 'Skills' && <SkillPage bladeburner={props.bladeburner} />}
|
||||
</div>
|
||||
<span className="text">{stealthIcon}= This action requires stealth, {killIcon} = This action involves retirement</span>
|
||||
<span className="text">{stealthIcon} = This action requires stealth, {killIcon} = This action involves retirement</span>
|
||||
</>);
|
||||
}
|
||||
@@ -10,6 +10,8 @@ import { createPopup } from "../../ui/React/createPopup";
|
||||
import { TeamSizePopup } from "./TeamSizePopup";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { SuccessChance } from "./SuccessChance";
|
||||
import { CopyableText } from "../../ui/React/CopyableText";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
@@ -26,7 +28,7 @@ export function BlackOpElem(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
const isActive = props.bladeburner.action.type === ActionTypes["BlackOperation"] && props.action.name === props.bladeburner.action.name;
|
||||
const estimatedSuccessChance = props.action.getSuccessChance(props.bladeburner, {est:true});
|
||||
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner);
|
||||
const actionTime = props.action.getActionTime(props.bladeburner);
|
||||
const hasReqdRank = props.bladeburner.rank >= props.action.reqdRank;
|
||||
const computedActionTimeCurrent = Math.min(props.bladeburner.actionTimeCurrent+props.bladeburner.actionTimeOverflow, props.bladeburner.actionTimeToComplete);
|
||||
@@ -50,8 +52,8 @@ export function BlackOpElem(props: IProps): React.ReactElement {
|
||||
return (<>
|
||||
<h2 style={{display: 'inline-block'}}>
|
||||
{isActive ?
|
||||
<>{props.action.name} (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
|
||||
<>{props.action.name}</>
|
||||
<><CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
|
||||
<CopyableText value={props.action.name} />
|
||||
}
|
||||
</h2>
|
||||
{isActive ?
|
||||
@@ -78,10 +80,10 @@ export function BlackOpElem(props: IProps): React.ReactElement {
|
||||
Required Rank: {formatNumber(props.action.reqdRank, 0)}
|
||||
</p>
|
||||
<br />
|
||||
<p style={{display:"inline-block"}}>
|
||||
Estimated Success Chance: {formatNumber(estimatedSuccessChance*100, 1)}% {props.action.isStealth?stealthIcon:<></>}{props.action.isKill?killIcon:<></>}
|
||||
<pre style={{display:"inline-block"}}>
|
||||
Estimated Success Chance: <SuccessChance chance={estimatedSuccessChance} /> {props.action.isStealth?stealthIcon:<></>}{props.action.isKill?killIcon:<></>}
|
||||
<br />
|
||||
Time Required: {convertTimeMsToTimeElapsedString(actionTime*1000)}
|
||||
</p>
|
||||
</pre>
|
||||
</>);
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import * as React from "react";
|
||||
import { BlackOpList } from "./BlackOpList";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { CopyableText } from "../../ui/React/CopyableText";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
|
||||
@@ -26,18 +26,8 @@ export function Console(props: IProps): React.ReactElement {
|
||||
|
||||
// TODO: Figure out how to actually make the scrolling work correctly.
|
||||
function scrollToBottom(): void {
|
||||
function isMaxed(): boolean {
|
||||
if(!lastRef.current) return false;
|
||||
const oldTop = lastRef.current.scrollTop;
|
||||
lastRef.current.scrollTop = lastRef.current.scrollHeight;
|
||||
const maxed = oldTop === lastRef.current.scrollTop;
|
||||
lastRef.current.scrollTop = oldTop;
|
||||
return maxed;
|
||||
}
|
||||
if(lastRef.current) {
|
||||
if(isMaxed())
|
||||
lastRef.current.scrollTop = lastRef.current.scrollHeight;
|
||||
}
|
||||
if(!lastRef.current) return;
|
||||
lastRef.current.scrollTop = lastRef.current.scrollHeight;
|
||||
}
|
||||
|
||||
function rerender(): void {
|
||||
|
||||
@@ -9,6 +9,8 @@ import { stealthIcon, killIcon } from "../data/Icons";
|
||||
import { BladeburnerConstants } from "../data/Constants";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { SuccessChance } from "./SuccessChance";
|
||||
import { CopyableText } from "../../ui/React/CopyableText";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
@@ -19,7 +21,8 @@ interface IProps {
|
||||
export function ContractElem(props: IProps): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
const isActive = props.bladeburner.action.type === ActionTypes["Contract"] && props.action.name === props.bladeburner.action.name;
|
||||
const estimatedSuccessChance = props.action.getSuccessChance(props.bladeburner, {est:true});
|
||||
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner);
|
||||
const successChance = props.action.getSuccessChance(props.bladeburner);
|
||||
const computedActionTimeCurrent = Math.min(props.bladeburner.actionTimeCurrent+props.bladeburner.actionTimeOverflow, props.bladeburner.actionTimeToComplete);
|
||||
const maxLevel = (props.action.level >= props.action.maxLevel);
|
||||
const actionTime = props.action.getActionTime(props.bladeburner);
|
||||
@@ -52,8 +55,8 @@ export function ContractElem(props: IProps): React.ReactElement {
|
||||
return (<>
|
||||
<h2 style={{display: 'inline-block'}}>
|
||||
{isActive ?
|
||||
<>{props.action.name} (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
|
||||
<>{props.action.name}</>
|
||||
<><CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
|
||||
<CopyableText value={props.action.name} />
|
||||
}
|
||||
</h2>
|
||||
{isActive ?
|
||||
@@ -93,7 +96,7 @@ export function ContractElem(props: IProps): React.ReactElement {
|
||||
<pre style={{display: 'inline-block'}}>
|
||||
<span dangerouslySetInnerHTML={{__html: props.action.desc}} />
|
||||
<br /><br />
|
||||
Estimated success chance: {formatNumber(estimatedSuccessChance*100, 1)}% {props.action.isStealth?stealthIcon:<></>}{props.action.isKill?killIcon:<></>}<br />
|
||||
Estimated success chance: <SuccessChance chance={estimatedSuccessChance} /> {props.action.isStealth?stealthIcon:<></>}{props.action.isKill?killIcon:<></>}<br />
|
||||
Time Required: {convertTimeMsToTimeElapsedString(actionTime*1000)}<br />
|
||||
Contracts remaining: {Math.floor(props.action.count)}<br />
|
||||
Successes: {props.action.successes}<br />
|
||||
@@ -114,26 +117,3 @@ Failures: {props.action.failures}
|
||||
onChange={onAutolevel}/>
|
||||
</>);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
// Autolevel Checkbox
|
||||
el.appendChild(createElement("br"));
|
||||
var autolevelCheckboxId = "bladeburner-" + action.name + "-autolevel-checkbox";
|
||||
el.appendChild(createElement("label", {
|
||||
for:autolevelCheckboxId, innerText:"Autolevel: ",color:"white",
|
||||
tooltip:"Automatically increase contract level when possible",
|
||||
}));
|
||||
|
||||
const checkboxInput = createElement("input", {
|
||||
type:"checkbox",
|
||||
id: autolevelCheckboxId,
|
||||
checked: action.autoLevel,
|
||||
changeListener: () => {
|
||||
action.autoLevel = checkboxInput.checked;
|
||||
},
|
||||
});
|
||||
|
||||
el.appendChild(checkboxInput);
|
||||
|
||||
*/
|
||||
@@ -1,9 +1,13 @@
|
||||
import React, { useState } from "react";
|
||||
import { ActionTypes } from "../data/ActionTypes";
|
||||
import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
|
||||
import { formatNumber } from "../../../utils/StringHelperFunctions";
|
||||
import {
|
||||
formatNumber,
|
||||
convertTimeMsToTimeElapsedString,
|
||||
} from "../../../utils/StringHelperFunctions";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { CopyableText } from "../../ui/React/CopyableText";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
@@ -15,6 +19,20 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
const isActive = props.action.name === props.bladeburner.action.name;
|
||||
const computedActionTimeCurrent = Math.min(props.bladeburner.actionTimeCurrent+props.bladeburner.actionTimeOverflow, props.bladeburner.actionTimeToComplete);
|
||||
const actionTime = (function(): number{
|
||||
switch(props.action.name) {
|
||||
case "Training":
|
||||
case "Field Analysis":
|
||||
return 30;
|
||||
case "Diplomacy":
|
||||
case "Hyperbolic Regeneration Chamber":
|
||||
return 60;
|
||||
case "Recruitment":
|
||||
return props.bladeburner.getRecruitmentTime(props.player);
|
||||
}
|
||||
return -1; // dead code
|
||||
})();
|
||||
const successChance = props.action.name === "Recruitment" ? Math.max(0, Math.min(props.bladeburner.getRecruitmentSuccessChance(props.player), 1)) : -1;
|
||||
|
||||
function onStart(): void {
|
||||
props.bladeburner.action.type = ActionTypes[(props.action.name as string)];
|
||||
@@ -26,8 +44,8 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
|
||||
return (<>
|
||||
<h2 style={{display: 'inline-block'}}>
|
||||
{isActive ?
|
||||
<>{props.action.name} (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
|
||||
<>{props.action.name}</>
|
||||
<><CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
|
||||
<CopyableText value={props.action.name} />
|
||||
}
|
||||
</h2>
|
||||
{isActive ?
|
||||
@@ -42,6 +60,10 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
|
||||
</>}
|
||||
<br />
|
||||
<br />
|
||||
<pre style={{display: 'inline-block'}} dangerouslySetInnerHTML={{__html: props.action.desc}}></pre>
|
||||
<pre style={{display: 'inline-block'}} dangerouslySetInnerHTML={{__html: props.action.desc}}></pre><br /><br />
|
||||
<pre style={{display: 'inline-block'}}>
|
||||
Time Required: {convertTimeMsToTimeElapsedString(actionTime*1000)}
|
||||
{successChance !== -1 && <><br />Estimated success chance: {formatNumber(successChance*100, 1)}%</>}
|
||||
</pre>
|
||||
</>);
|
||||
}
|
||||
@@ -11,6 +11,8 @@ import { createPopup } from "../../ui/React/createPopup";
|
||||
import { TeamSizePopup } from "./TeamSizePopup";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { SuccessChance } from "./SuccessChance";
|
||||
import { CopyableText } from "../../ui/React/CopyableText";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
@@ -21,7 +23,7 @@ interface IProps {
|
||||
export function OperationElem(props: IProps): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
const isActive = props.bladeburner.action.type === ActionTypes["Operation"] && props.action.name === props.bladeburner.action.name;
|
||||
const estimatedSuccessChance = props.action.getSuccessChance(props.bladeburner, {est:true});
|
||||
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner);
|
||||
const computedActionTimeCurrent = Math.min(props.bladeburner.actionTimeCurrent+props.bladeburner.actionTimeOverflow,props.bladeburner.actionTimeToComplete);
|
||||
const maxLevel = (props.action.level >= props.action.maxLevel);
|
||||
const actionTime = props.action.getActionTime(props.bladeburner);
|
||||
@@ -63,8 +65,8 @@ export function OperationElem(props: IProps): React.ReactElement {
|
||||
return (<>
|
||||
<h2 style={{display: 'inline-block'}}>
|
||||
{isActive ?
|
||||
<>{props.action.name} (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
|
||||
<>{props.action.name}</>
|
||||
<><CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
|
||||
<CopyableText value={props.action.name} />
|
||||
}
|
||||
</h2>
|
||||
{isActive ?
|
||||
@@ -110,7 +112,7 @@ export function OperationElem(props: IProps): React.ReactElement {
|
||||
<pre style={{display:"inline-block"}}>
|
||||
<span dangerouslySetInnerHTML={{__html: props.action.desc}} />
|
||||
<br /><br />
|
||||
Estimated success chance: {formatNumber(estimatedSuccessChance*100, 1)}% {props.action.isStealth?stealthIcon:<></>}{props.action.isKill?killIcon:<></>}<br />
|
||||
Estimated success chance: <SuccessChance chance={estimatedSuccessChance} /> {props.action.isStealth?stealthIcon:<></>}{props.action.isKill?killIcon:<></>}<br />
|
||||
Time Required: {convertTimeMsToTimeElapsedString(actionTime*1000)}<br />
|
||||
Operations remaining: {Math.floor(props.action.count)}<br />
|
||||
Successes: {props.action.successes}<br />
|
||||
|
||||
14
src/Bladeburner/ui/SuccessChance.tsx
Normal file
14
src/Bladeburner/ui/SuccessChance.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import React from "react";
|
||||
import { formatNumber } from "../../../utils/StringHelperFunctions";
|
||||
|
||||
interface IProps {
|
||||
chance: number[];
|
||||
}
|
||||
|
||||
export function SuccessChance(props: IProps): React.ReactElement {
|
||||
if(props.chance[0] === props.chance[1]) {
|
||||
return (<>{formatNumber(props.chance[0]*100, 1)}%</>);
|
||||
}
|
||||
|
||||
return (<>{formatNumber(props.chance[0]*100, 1)}% ~ {formatNumber(props.chance[1]*100, 1)}%</>);
|
||||
}
|
||||
@@ -147,7 +147,7 @@ export class Roulette extends Game<IProps, IState> {
|
||||
|
||||
|
||||
componentDidMount(): void {
|
||||
this.interval = setInterval(this.step, 50);
|
||||
this.interval = window.setInterval(this.step, 50);
|
||||
}
|
||||
|
||||
step(): void {
|
||||
|
||||
@@ -87,7 +87,7 @@ export class SlotMachine extends Game<IProps, IState> {
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.interval = setInterval(this.step, 50);
|
||||
this.interval = window.setInterval(this.step, 50);
|
||||
}
|
||||
|
||||
step(): void {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { IMap } from "./types";
|
||||
|
||||
export const CONSTANTS: IMap<any> = {
|
||||
Version: "0.52.6",
|
||||
Version: "0.52.9",
|
||||
|
||||
// Speed (in ms) at which the main loop is updated
|
||||
_idleSpeed: 200,
|
||||
@@ -228,26 +228,40 @@ export const CONSTANTS: IMap<any> = {
|
||||
TotalNumBitNodes: 24,
|
||||
|
||||
LatestUpdate: `
|
||||
v0.52.6 - 2021-07-21 Logboxes and VS-code (hydroflame)
|
||||
v0.52.9 - 2021-07-27 Less lag! (hydroflame & community)
|
||||
-------------------------------------------
|
||||
|
||||
** Text Editor **
|
||||
** Active Scripts page **
|
||||
|
||||
* Ace and Codemirror have been removed in favor of monaco (web version of
|
||||
vs-code). The options are a bit lackluster but more will be added as
|
||||
feedback comes.
|
||||
* Now less laggy, has pagination.
|
||||
|
||||
** Log boxes **
|
||||
** File diagnostic **
|
||||
|
||||
* Multiple log boxes can be opened at once. They can be moved around the
|
||||
screen. (but the movement behavior is a bit weird.)
|
||||
* Added a popup found under options that shows the files you own and how
|
||||
large they are. This help find bugs and leftover massive logs files.
|
||||
|
||||
** Corporation **
|
||||
|
||||
* Added safeguard against a very specific bug that causes NaN money. I'm
|
||||
still not sure what the root cause is but it should prevent corp from
|
||||
breaking.
|
||||
|
||||
** Netscript **
|
||||
|
||||
* tprintf is a new function that doesn't print the filename.
|
||||
|
||||
** Misc. **
|
||||
|
||||
* Job promotion now correctly updates the UI.
|
||||
* Milestones now call the faction CyberSec instead of CSEC
|
||||
* Can no longer create file that break the filesystem.
|
||||
* Remove dollar sign in blade contract UI element
|
||||
* Infiltration kills you if you try to automate it. (@threehams)
|
||||
* Fix beautify button not working
|
||||
* Added bladeburner_analysis_mult to getPlayer() (@brusby)
|
||||
* Fixed joining bladeburner via netscript functions. (@omuretsu)
|
||||
* All bladeburner actions are click-to-copy
|
||||
* nerf noodle bar
|
||||
`,
|
||||
|
||||
/*
|
||||
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -119,7 +119,7 @@ function Industry(params={}) {
|
||||
};
|
||||
|
||||
this.name = params.name ? params.name : 0;
|
||||
this.type = params.type ? params.type : 0;
|
||||
this.type = params.type ? params.type : Industries.Agriculture;
|
||||
|
||||
this.sciResearch = new Material({name: "Scientific Research"});
|
||||
this.researched = {}; // Object of acquired Research. Keys = research name
|
||||
@@ -958,7 +958,6 @@ Industry.prototype.processProducts = function(marketCycles=1, corporation) {
|
||||
const mgmtProd = office.employeeProd[EmployeePositions.Management];
|
||||
const opProd = office.employeeProd[EmployeePositions.Operations];
|
||||
const total = engrProd + mgmtProd + opProd;
|
||||
|
||||
if (total <= 0) { break; }
|
||||
|
||||
// Management is a multiplier for the production from Engineers
|
||||
@@ -1113,8 +1112,13 @@ Industry.prototype.processProduct = function(marketCycles=1, product, corporatio
|
||||
} else if (product.marketTa1) {
|
||||
sCost = product.pCost + markupLimit;
|
||||
} else if (isString(product.sCost)) {
|
||||
if(product.mku === 0) {
|
||||
console.error(`mku is zero, reverting to 1 to avoid Infinity`);
|
||||
product.mku = 1;
|
||||
}
|
||||
sCost = product.sCost.replace(/MP/g, product.pCost + product.rat / product.mku);
|
||||
sCost = eval(sCost);
|
||||
|
||||
} else {
|
||||
sCost = product.sCost;
|
||||
}
|
||||
@@ -1397,11 +1401,12 @@ Industry.prototype.createResearchBox = function() {
|
||||
researchTree.research(allResearch[i]);
|
||||
this.researched[allResearch[i]] = true;
|
||||
|
||||
const researchBox = this.createResearchBox();
|
||||
dialogBoxCreate(`Researched ${allResearch[i]}. It may take a market cycle ` +
|
||||
`(~${SecsPerMarketCycle} seconds) before the effects of ` +
|
||||
`the Research apply.`);
|
||||
|
||||
return this.createResearchBox();
|
||||
return researchBox;
|
||||
} else {
|
||||
dialogBoxCreate(`You do not have enough Scientific Research for ${research.name}`);
|
||||
}
|
||||
@@ -1439,11 +1444,11 @@ Industry.prototype.createResearchBox = function() {
|
||||
}
|
||||
|
||||
Industry.prototype.toJSON = function() {
|
||||
return Generic_toJSON("Industry", this);
|
||||
return Generic_toJSON("Industry", this);
|
||||
}
|
||||
|
||||
Industry.fromJSON = function(value) {
|
||||
return Generic_fromJSON(Industry, value.data);
|
||||
return Generic_fromJSON(Industry, value.data);
|
||||
}
|
||||
|
||||
Reviver.constructors.Industry = Industry;
|
||||
@@ -1600,11 +1605,11 @@ Employee.prototype.createUI = function(panel, corporation, industry) {
|
||||
}
|
||||
|
||||
Employee.prototype.toJSON = function() {
|
||||
return Generic_toJSON("Employee", this);
|
||||
return Generic_toJSON("Employee", this);
|
||||
}
|
||||
|
||||
Employee.fromJSON = function(value) {
|
||||
return Generic_fromJSON(Employee, value.data);
|
||||
return Generic_fromJSON(Employee, value.data);
|
||||
}
|
||||
|
||||
Reviver.constructors.Employee = Employee;
|
||||
@@ -1899,11 +1904,11 @@ OfficeSpace.prototype.unassignEmployeeFromJob = function(job) {
|
||||
}
|
||||
|
||||
OfficeSpace.prototype.toJSON = function() {
|
||||
return Generic_toJSON("OfficeSpace", this);
|
||||
return Generic_toJSON("OfficeSpace", this);
|
||||
}
|
||||
|
||||
OfficeSpace.fromJSON = function(value) {
|
||||
return Generic_fromJSON(OfficeSpace, value.data);
|
||||
return Generic_fromJSON(OfficeSpace, value.data);
|
||||
}
|
||||
|
||||
Reviver.constructors.OfficeSpace = OfficeSpace;
|
||||
@@ -1941,6 +1946,14 @@ function Corporation(params={}) {
|
||||
this.state = new CorporationState();
|
||||
}
|
||||
|
||||
Corporation.prototype.addFunds = function(amt) {
|
||||
if(!isFinite(amt)) {
|
||||
console.error('Trying to add invalid amount of funds. Report to a developper.');
|
||||
return;
|
||||
}
|
||||
this.funds = this.funds.plus(amt);
|
||||
}
|
||||
|
||||
Corporation.prototype.getState = function() {
|
||||
return this.state.getState();
|
||||
}
|
||||
@@ -1980,7 +1993,7 @@ Corporation.prototype.process = function() {
|
||||
});
|
||||
var profit = this.revenue.minus(this.expenses);
|
||||
const cycleProfit = profit.times(marketCycles * SecsPerMarketCycle);
|
||||
if (isNaN(this.funds)) {
|
||||
if (isNaN(this.funds) || this.funds === Infinity || this.funds === -Infinity) {
|
||||
dialogBoxCreate("There was an error calculating your Corporations funds and they got reset to 0. " +
|
||||
"This is a bug. Please report to game developer.<br><br>" +
|
||||
"(Your funds have been set to $150b for the inconvenience)");
|
||||
@@ -1999,10 +2012,10 @@ Corporation.prototype.process = function() {
|
||||
const profit = this.numShares * dividendsPerShare * (1 - (this.dividendTaxPercentage / 100));
|
||||
Player.gainMoney(profit);
|
||||
Player.recordMoneySource(profit, "corporation");
|
||||
this.funds = this.funds.plus(retainedEarnings);
|
||||
this.addFunds(retainedEarnings);
|
||||
}
|
||||
} else {
|
||||
this.funds = this.funds.plus(cycleProfit);
|
||||
this.addFunds(cycleProfit);
|
||||
}
|
||||
|
||||
this.updateSharePrice();
|
||||
@@ -2069,7 +2082,7 @@ Corporation.prototype.getInvestment = function() {
|
||||
noBtn.innerHML = "Reject";
|
||||
yesBtn.addEventListener("click", () => {
|
||||
++this.fundingRound;
|
||||
this.funds = this.funds.plus(funding);
|
||||
this.addFunds(funding);
|
||||
this.numShares -= investShares;
|
||||
this.rerender();
|
||||
return yesNoBoxClose();
|
||||
@@ -2123,7 +2136,7 @@ Corporation.prototype.goPublic = function() {
|
||||
this.sharePrice = initialSharePrice;
|
||||
this.issuedShares = numShares;
|
||||
this.numShares -= numShares;
|
||||
this.funds = this.funds.plus(numShares * initialSharePrice);
|
||||
this.addFunds(numShares * initialSharePrice);
|
||||
this.rerender();
|
||||
removeElementById(goPublicPopupId);
|
||||
dialogBoxCreate(`You took your ${this.name} public and earned ` +
|
||||
@@ -2376,11 +2389,11 @@ Corporation.prototype.clearUI = function() {
|
||||
}
|
||||
|
||||
Corporation.prototype.toJSON = function() {
|
||||
return Generic_toJSON("Corporation", this);
|
||||
return Generic_toJSON("Corporation", this);
|
||||
}
|
||||
|
||||
Corporation.fromJSON = function(value) {
|
||||
return Generic_fromJSON(Corporation, value.data);
|
||||
return Generic_fromJSON(Corporation, value.data);
|
||||
}
|
||||
|
||||
Reviver.constructors.Corporation = Corporation;
|
||||
|
||||
@@ -187,6 +187,11 @@ export class Product {
|
||||
this.calculateRating(industry);
|
||||
const advMult = 1 + (Math.pow(this.advCost, 0.1) / 100);
|
||||
this.mku = 100 / (advMult * Math.pow((this.qlt + 0.001), 0.65) * (busRatio + mgmtRatio));
|
||||
|
||||
// I actually don't understand well enough to know if this is right.
|
||||
// I'm adding this to prevent a crash.
|
||||
if(this.mku === 0) this.mku = 1;
|
||||
|
||||
this.dmd = industry.awareness === 0 ? 20 : Math.min(100, advMult * (100 * (industry.popularity / industry.awareness)));
|
||||
this.cmp = getRandomInt(0, 70);
|
||||
|
||||
|
||||
@@ -502,7 +502,7 @@ class DevMenuComponent extends Component {
|
||||
modifyBladeburnerRank(modify) {
|
||||
return function(rank) {
|
||||
if (Player.bladeburner) {
|
||||
Player.bladeburner.changeRank(rank*modify);
|
||||
Player.bladeburner.changeRank(Player, rank*modify);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -514,7 +514,7 @@ class DevMenuComponent extends Component {
|
||||
|
||||
addTonsBladeburnerRank() {
|
||||
if (Player.bladeburner) {
|
||||
Player.bladeburner.changeRank(tonsP);
|
||||
Player.bladeburner.changeRank(Player, tonsP);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
68
src/Diagnostic/FileDiagnosticPopup.tsx
Normal file
68
src/Diagnostic/FileDiagnosticPopup.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import React from "react";
|
||||
import { AllServers } from "../Server/AllServers";
|
||||
import { Script } from "../Script/Script";
|
||||
import { TextFile } from "../TextFile";
|
||||
import { Accordion } from "../ui/React/Accordion";
|
||||
import { numeralWrapper } from "../ui/numeralFormat";
|
||||
|
||||
interface IServerProps {
|
||||
ip: string;
|
||||
}
|
||||
|
||||
export function ServerAccordion(props: IServerProps): React.ReactElement {
|
||||
const server = AllServers[props.ip];
|
||||
let totalSize = 0;
|
||||
for(const f of server.scripts) {
|
||||
totalSize += f.code.length;
|
||||
}
|
||||
|
||||
for(const f of server.textFiles) {
|
||||
totalSize += f.text.length;
|
||||
}
|
||||
|
||||
if(totalSize === 0) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
interface File {
|
||||
name: string;
|
||||
size: number;
|
||||
}
|
||||
|
||||
const files: File[] = [];
|
||||
|
||||
for(const f of server.scripts) {
|
||||
files.push({name: f.filename, size: f.code.length});
|
||||
}
|
||||
|
||||
for(const f of server.textFiles) {
|
||||
files.push({name: f.fn, size: f.text.length});
|
||||
}
|
||||
|
||||
files.sort((a: File, b: File): number => b.size-a.size);
|
||||
|
||||
return <Accordion
|
||||
headerContent={<>{server.hostname} ({numeralWrapper.formatBigNumber(totalSize)}b)</>}
|
||||
panelContent={<ul>
|
||||
{files.map((file: File) => <li key={file.name}>{file.name}: {numeralWrapper.formatBigNumber(file.size)}b</li>) }
|
||||
</ul>}
|
||||
/>
|
||||
}
|
||||
|
||||
interface IProps {}
|
||||
|
||||
export function FileDiagnosticPopup(props: IProps): React.ReactElement {
|
||||
const ips: string[] = [];
|
||||
for(const ip of Object.keys(AllServers)) {
|
||||
ips.push(ip);
|
||||
}
|
||||
|
||||
return (<>
|
||||
<p>
|
||||
Welcome to the file diagnostic! If your save file is really big it's
|
||||
likely because you have too many text/scripts. This tool can help you
|
||||
narrow down where they are.
|
||||
</p>
|
||||
{ips.map((ip: string) => <ServerAccordion key={ip} ip={ip} />)}
|
||||
</>);
|
||||
}
|
||||
@@ -23,7 +23,7 @@ class HashUpgrade extends React.Component {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
selectedServer: "foodnstuff",
|
||||
selectedServer: "ecorp",
|
||||
}
|
||||
|
||||
this.changeTargetServer = this.changeTargetServer.bind(this);
|
||||
|
||||
@@ -46,7 +46,7 @@ export function BackwardGame(props: IMinigameProps): React.ReactElement {
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>Type it backward</h1>
|
||||
<KeyHandler onKeyDown={press} />
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<p style={{transform: 'scaleX(-1)'}}>{answer}</p>
|
||||
|
||||
@@ -78,7 +78,7 @@ export function BracketGame(props: IMinigameProps): React.ReactElement {
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>Close the brackets</h1>
|
||||
<p style={{fontSize: '5em'}}>{`${left}${right}`}<BlinkingCursor /></p>
|
||||
<KeyHandler onKeyDown={press} />
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>)
|
||||
}
|
||||
@@ -51,7 +51,7 @@ export function BribeGame(props: IMinigameProps): React.ReactElement {
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1>Say something nice about the guard.</h1>
|
||||
<KeyHandler onKeyDown={press} />
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<h2 style={{fontSize: "2em"}}>↑</h2>
|
||||
|
||||
@@ -47,7 +47,7 @@ export function CheatCodeGame(props: IMinigameProps): React.ReactElement {
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>Enter the Code!</h1>
|
||||
<p style={{fontSize: '5em'}}>{code[index]}</p>
|
||||
<KeyHandler onKeyDown={press} />
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>)
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ export function Cyberpunk2077Game(props: IMinigameProps): React.ReactElement {
|
||||
return <span key={`${x}${y}`} style={{fontSize: fontSize, color: 'blue'}}>{cell} </span>
|
||||
return <span key={`${x}${y}`} style={{fontSize: fontSize}}>{cell} </span>
|
||||
})}</pre><br /></div>)}
|
||||
<KeyHandler onKeyDown={press} />
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>)
|
||||
}
|
||||
|
||||
@@ -85,10 +85,13 @@ export function Game(props: IProps): React.ReactElement {
|
||||
})
|
||||
}
|
||||
|
||||
function failure(): void {
|
||||
function failure(options?: { automated: boolean }): void {
|
||||
setStage(Stage.Countdown);
|
||||
pushResult(false);
|
||||
if(props.Player.takeDamage(props.StartingDifficulty*3)) {
|
||||
// Kill the player immediately if they use automation, so
|
||||
// it's clear they're not meant to
|
||||
const damage = options?.automated ? props.Player.hp : props.StartingDifficulty*3;
|
||||
if(props.Player.takeDamage(damage)) {
|
||||
const menu = document.getElementById("mainmenu-container");
|
||||
if(menu === null) throw new Error("mainmenu-container not found");
|
||||
menu.style.visibility = "visible";
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
export interface IMinigameProps {
|
||||
onSuccess: () => void;
|
||||
onFailure: () => void;
|
||||
onFailure: (options?: {
|
||||
/** Failed due to using untrusted events (automation) */
|
||||
automated: boolean;
|
||||
}) => void;
|
||||
difficulty: number;
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import React, { useEffect } from 'react';
|
||||
|
||||
interface IProps {
|
||||
onKeyDown: (event: React.KeyboardEvent<HTMLElement>) => void;
|
||||
onFailure: (options?: { automated: boolean }) => void;
|
||||
}
|
||||
|
||||
export function KeyHandler(props: IProps): React.ReactElement {
|
||||
@@ -9,7 +10,12 @@ export function KeyHandler(props: IProps): React.ReactElement {
|
||||
useEffect(() => elem.focus());
|
||||
|
||||
function onKeyDown(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
if(!event.isTrusted) return;
|
||||
console.log("isTrusted?", event.isTrusted)
|
||||
if(!event.isTrusted) {
|
||||
console.log("untrusted event!")
|
||||
props.onFailure({ automated: true });
|
||||
return;
|
||||
}
|
||||
props.onKeyDown(event);
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ export function MinesweeperGame(props: IMinigameProps): React.ReactElement {
|
||||
return <span key={x}>[ ] </span>
|
||||
}
|
||||
})}</pre><br /></div>)}
|
||||
<KeyHandler onKeyDown={press} />
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>)
|
||||
}
|
||||
|
||||
@@ -39,9 +39,9 @@ export function SlashGame(props: IMinigameProps): React.ReactElement {
|
||||
|
||||
useEffect(() => {
|
||||
let id2 = -1;
|
||||
const id = setTimeout(() => {
|
||||
const id = window.setTimeout(() => {
|
||||
setGuarding(false);
|
||||
id2 = setTimeout(()=>setGuarding(true), difficulty.window)
|
||||
id2 = window.setTimeout(()=>setGuarding(true), difficulty.window)
|
||||
}, Math.random()*3250+1500);
|
||||
return () => {
|
||||
clearInterval(id);
|
||||
@@ -54,7 +54,7 @@ export function SlashGame(props: IMinigameProps): React.ReactElement {
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>Slash when his guard is down!</h1>
|
||||
<p style={{fontSize: '5em'}}>{guarding ? "!Guarding!" : "!ATTACKING!"}</p>
|
||||
<KeyHandler onKeyDown={press} />
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>)
|
||||
}
|
||||
@@ -111,7 +111,7 @@ export function WireCuttingGame(props: IMinigameProps): React.ReactElement {
|
||||
})}
|
||||
</pre>
|
||||
</div>)}
|
||||
<KeyHandler onKeyDown={press} />
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>)
|
||||
}
|
||||
|
||||
3
src/InteractiveTutorial.d.ts
vendored
Normal file
3
src/InteractiveTutorial.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export declare function iTutorialNextStep(): void;
|
||||
export declare const ITutorial: {isRunning: boolean, currStep: number};
|
||||
export declare const iTutorialSteps: {[key: string]: number};
|
||||
@@ -936,6 +936,9 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
post(`${workerScript.scriptRef.filename}: ${argsToString(arguments)}`);
|
||||
},
|
||||
tprintf: function(format, ...args) {
|
||||
post(vsprintf(format, args));
|
||||
},
|
||||
clearLog: function() {
|
||||
workerScript.scriptRef.clearLog();
|
||||
},
|
||||
@@ -2149,11 +2152,15 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
return port.write(data);
|
||||
} else if (isString(port)) { // Write to script or text file
|
||||
const fn = removeLeadingSlash(port);
|
||||
let fn = port;
|
||||
if (!isValidFilePath(fn)) {
|
||||
throw makeRuntimeErrorMsg("write", `Invalid filepath: ${fn}`);
|
||||
}
|
||||
|
||||
if(fn.lastIndexOf("/") === 0) {
|
||||
fn = removeLeadingSlash(fn);
|
||||
}
|
||||
|
||||
// Coerce 'data' to be a string
|
||||
try {
|
||||
data = String(data);
|
||||
@@ -3025,6 +3032,7 @@ function NetscriptFunctions(workerScript) {
|
||||
has4SDataTixApi: Player.has4SDataTixApi,
|
||||
bladeburner_max_stamina_mult: Player.bladeburner_max_stamina_mult,
|
||||
bladeburner_stamina_gain_mult: Player.bladeburner_stamina_gain_mult,
|
||||
bladeburner_analysis_mult: Player.bladeburner_analysis_mult,
|
||||
bladeburner_success_chance_mult: Player.bladeburner_success_chance_mult,
|
||||
bitNodeN: Player.bitNodeN,
|
||||
totalPlaytime: Player.totalPlaytime,
|
||||
@@ -4061,7 +4069,7 @@ function NetscriptFunctions(workerScript) {
|
||||
return true; // Already member
|
||||
} else if (Player.strength >= 100 && Player.defense >= 100 &&
|
||||
Player.dexterity >= 100 && Player.agility >= 100) {
|
||||
Player.bladeburner = new Bladeburner();
|
||||
Player.bladeburner = new Bladeburner(Player);
|
||||
workerScript.log("joinBladeburnerDivision", "You have been accepted into the Bladeburner division");
|
||||
|
||||
const worldHeader = document.getElementById("world-menu-header");
|
||||
@@ -4542,4 +4550,4 @@ function NetscriptFunctions(workerScript) {
|
||||
return functions;
|
||||
} // End NetscriptFunction()
|
||||
|
||||
export { NetscriptFunctions };
|
||||
export { NetscriptFunctions };
|
||||
|
||||
@@ -82,7 +82,6 @@ BitburnerSaveObject.prototype.getSaveString = function() {
|
||||
this.GlobalAliasesSave = JSON.stringify(GlobalAliases);
|
||||
this.MessagesSave = JSON.stringify(Messages);
|
||||
this.StockMarketSave = JSON.stringify(StockMarket);
|
||||
console.log(JSON.stringify(Settings));
|
||||
this.SettingsSave = JSON.stringify(Settings);
|
||||
this.FconfSettingsSave = JSON.stringify(FconfSettings);
|
||||
this.VersionSave = JSON.stringify(CONSTANTS.Version);
|
||||
|
||||
@@ -28,19 +28,6 @@ import { dialogBoxCreate } from "../../utils/DialogBox";
|
||||
import { compareArrays } from "../../utils/helpers/compareArrays";
|
||||
import { createElement } from "../../utils/uiHelpers/createElement";
|
||||
|
||||
// TODO(hydroflame): move to Monaco mount/unmount
|
||||
//Define key commands in script editor (ctrl o to save + close, etc.)
|
||||
$(document).keydown(function(e) {
|
||||
if (Settings.DisableHotkeys === true) {return;}
|
||||
if (routing.isOn(Page.ScriptEditor)) {
|
||||
//Ctrl + b
|
||||
if (e.keyCode == 66 && (e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
saveAndCloseScriptEditor(); // deleted function
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export function scriptCalculateOfflineProduction(runningScriptObj) {
|
||||
//The Player object stores the last update time from when we were online
|
||||
const thisUpdate = new Date().getTime();
|
||||
|
||||
@@ -18,8 +18,8 @@ export class PositionTracker {
|
||||
const position = this.positions.get(filename);
|
||||
if (!position) {
|
||||
return {
|
||||
row: 0,
|
||||
column: 0,
|
||||
row: -1,
|
||||
column: -1,
|
||||
};
|
||||
}
|
||||
return position;
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef } from 'react';
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
import Editor from "@monaco-editor/react";
|
||||
import * as monaco from "monaco-editor";
|
||||
import IStandaloneCodeEditor = monaco.editor.IStandaloneCodeEditor;
|
||||
type IStandaloneCodeEditor = monaco.editor.IStandaloneCodeEditor;
|
||||
import { createPopup } from "../../ui/React/createPopup";
|
||||
import { OptionsPopup } from "./OptionsPopup";
|
||||
import { Options } from "./Options";
|
||||
@@ -23,6 +23,12 @@ import { libSource } from "../NetscriptDefinitions";
|
||||
import { NetscriptFunctions } from "../../NetscriptFunctions";
|
||||
import { WorkerScript } from "../../Netscript/WorkerScript";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { GetServerByHostname } from "../../Server/ServerHelpers";
|
||||
import {
|
||||
iTutorialNextStep,
|
||||
ITutorial,
|
||||
iTutorialSteps,
|
||||
} from "../../InteractiveTutorial";
|
||||
|
||||
let symbols: string[] = [];
|
||||
(function() {
|
||||
@@ -50,7 +56,12 @@ interface IProps {
|
||||
code: string;
|
||||
player: IPlayer;
|
||||
engine: IEngine;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
|
||||
// How to load function definition in monaco
|
||||
// https://github.com/Microsoft/monaco-editor/issues/1415
|
||||
@@ -59,16 +70,30 @@ interface IProps {
|
||||
// https://microsoft.github.io/monaco-editor/playground.html#extending-language-services-custom-languages
|
||||
// https://github.com/threehams/typescript-error-guide/blob/master/stories/components/Editor.tsx#L11-L39
|
||||
|
||||
// These variables are used to reload a script when it's clicked on. Because we
|
||||
// won't have references to the old script.
|
||||
let lastFilename = "";
|
||||
let lastCode = "";
|
||||
let lastPosition: monaco.Position | null = null;
|
||||
|
||||
export function Root(props: IProps): React.ReactElement {
|
||||
const editorRef = useRef<IStandaloneCodeEditor | null>(null);
|
||||
const [filename, setFilename] = useState(props.filename);
|
||||
const [code, setCode] = useState<string>(props.code);
|
||||
const [ram, setRAM] = useState('');
|
||||
const [filename, setFilename] = useState(props.filename ? props.filename : lastFilename);
|
||||
const [code, setCode] = useState<string>(props.code ? props.code : lastCode);
|
||||
const [ram, setRAM] = useState('RAM: ???');
|
||||
const [options, setOptions] = useState<Options>({
|
||||
theme: Settings.MonacoTheme,
|
||||
insertSpaces: Settings.MonacoInsertSpaces,
|
||||
});
|
||||
|
||||
// store the last known state in case we need to restart without nano.
|
||||
useEffect(() => {
|
||||
if(props.filename === "") return;
|
||||
lastFilename = props.filename;
|
||||
lastCode = props.code;
|
||||
lastPosition = null;
|
||||
}, []);
|
||||
|
||||
function save(): void {
|
||||
if(editorRef.current !== null) {
|
||||
const position = editorRef.current.getPosition();
|
||||
@@ -79,37 +104,38 @@ export function Root(props: IProps): React.ReactElement {
|
||||
});
|
||||
}
|
||||
}
|
||||
lastPosition = null;
|
||||
|
||||
// TODO(hydroflame): re-enable the tutorial.
|
||||
// if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
|
||||
// //Make sure filename + code properly follow tutorial
|
||||
// if (filename !== "n00dles.script") {
|
||||
// dialogBoxCreate("Leave the script name as 'n00dles'!");
|
||||
// return;
|
||||
// }
|
||||
// code = code.replace(/\s/g, "");
|
||||
// if (code.indexOf("while(true){hack('n00dles');}") == -1) {
|
||||
// dialogBoxCreate("Please copy and paste the code from the tutorial!");
|
||||
// return;
|
||||
// }
|
||||
if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
|
||||
//Make sure filename + code properly follow tutorial
|
||||
if (filename !== "n00dles.script") {
|
||||
dialogBoxCreate("Leave the script name as 'n00dles'!");
|
||||
return;
|
||||
}
|
||||
if (code.replace(/\s/g, "").indexOf("while(true){hack('n00dles');}") == -1) {
|
||||
dialogBoxCreate("Please copy and paste the code from the tutorial!");
|
||||
return;
|
||||
}
|
||||
|
||||
// //Save the script
|
||||
// let s = Player.getCurrentServer();
|
||||
// for (var i = 0; i < s.scripts.length; i++) {
|
||||
// if (filename == s.scripts[i].filename) {
|
||||
// s.scripts[i].saveScript(getCurrentEditor().getCode(), Player.currentServer, Player.getCurrentServer().scripts);
|
||||
// Engine.loadTerminalContent();
|
||||
// return iTutorialNextStep();
|
||||
// }
|
||||
// }
|
||||
//Save the script
|
||||
const server = props.player.getCurrentServer();
|
||||
if(server === null) throw new Error('Server should not be null but it is.');
|
||||
for (let i = 0; i < server.scripts.length; i++) {
|
||||
if (filename == server.scripts[i].filename) {
|
||||
server.scripts[i].saveScript(code, props.player.currentServer, server.scripts);
|
||||
props.engine.loadTerminalContent();
|
||||
return iTutorialNextStep();
|
||||
}
|
||||
}
|
||||
|
||||
// // If the current script does NOT exist, create a new one
|
||||
// let script = new Script();
|
||||
// script.saveScript(getCurrentEditor().getCode(), Player.currentServer, Player.getCurrentServer().scripts);
|
||||
// s.scripts.push(script);
|
||||
// If the current script does NOT exist, create a new one
|
||||
const script = new Script();
|
||||
script.saveScript(code, props.player.currentServer, server.scripts);
|
||||
server.scripts.push(script);
|
||||
|
||||
// return iTutorialNextStep();
|
||||
// }
|
||||
return iTutorialNextStep();
|
||||
}
|
||||
|
||||
if (filename == "") {
|
||||
dialogBoxCreate("You must specify a filename!");
|
||||
@@ -121,7 +147,8 @@ export function Root(props: IProps): React.ReactElement {
|
||||
return;
|
||||
}
|
||||
|
||||
const s = props.player.getCurrentServer();
|
||||
const server = props.player.getCurrentServer();
|
||||
if(server === null) throw new Error('Server should not be null but it is.');
|
||||
if (filename === ".fconf") {
|
||||
try {
|
||||
parseFconfSettings(code);
|
||||
@@ -131,9 +158,9 @@ export function Root(props: IProps): React.ReactElement {
|
||||
}
|
||||
} else if (isScriptFilename(filename)) {
|
||||
//If the current script already exists on the server, overwrite it
|
||||
for (let i = 0; i < s.scripts.length; i++) {
|
||||
if (filename == s.scripts[i].filename) {
|
||||
s.scripts[i].saveScript(code, props.player.currentServer, props.player.getCurrentServer().scripts);
|
||||
for (let i = 0; i < server.scripts.length; i++) {
|
||||
if (filename == server.scripts[i].filename) {
|
||||
server.scripts[i].saveScript(code, props.player.currentServer, server.scripts);
|
||||
props.engine.loadTerminalContent();
|
||||
return;
|
||||
}
|
||||
@@ -141,20 +168,20 @@ export function Root(props: IProps): React.ReactElement {
|
||||
|
||||
//If the current script does NOT exist, create a new one
|
||||
const script = new Script();
|
||||
script.saveScript(code, props.player.currentServer, props.player.getCurrentServer().scripts);
|
||||
s.scripts.push(script);
|
||||
script.saveScript(code, props.player.currentServer, server.scripts);
|
||||
server.scripts.push(script);
|
||||
} else if (filename.endsWith(".txt")) {
|
||||
for (let i = 0; i < s.textFiles.length; ++i) {
|
||||
if (s.textFiles[i].fn === filename) {
|
||||
s.textFiles[i].write(code);
|
||||
for (let i = 0; i < server.textFiles.length; ++i) {
|
||||
if (server.textFiles[i].fn === filename) {
|
||||
server.textFiles[i].write(code);
|
||||
props.engine.loadTerminalContent();
|
||||
return;
|
||||
}
|
||||
}
|
||||
const textFile = new TextFile(filename, code);
|
||||
s.textFiles.push(textFile);
|
||||
server.textFiles.push(textFile);
|
||||
} else {
|
||||
dialogBoxCreate("Invalid filename. Must be either a script (.script) or " +
|
||||
dialogBoxCreate("Invalid filename. Must be either a script (.script, .js, or .ns) or " +
|
||||
" or text file (.txt)")
|
||||
return;
|
||||
}
|
||||
@@ -162,14 +189,17 @@ export function Root(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
function beautify(): void {
|
||||
setCode(code => beautifyCode(code, {
|
||||
if (editorRef.current === null) return;
|
||||
const pretty = beautifyCode(code, {
|
||||
indent_with_tabs: !options.insertSpaces,
|
||||
indent_size: 4,
|
||||
brace_style: "preserve-inline",
|
||||
}));
|
||||
});
|
||||
editorRef.current.setValue(pretty);
|
||||
}
|
||||
|
||||
function onFilenameChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
lastFilename = filename;
|
||||
setFilename(event.target.value);
|
||||
}
|
||||
|
||||
@@ -187,12 +217,16 @@ export function Root(props: IProps): React.ReactElement {
|
||||
setOptions(options);
|
||||
Settings.MonacoTheme = options.theme;
|
||||
Settings.MonacoInsertSpaces = options.insertSpaces;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function updateCode(newCode?: string): void {
|
||||
if(newCode === undefined) return;
|
||||
lastCode = newCode;
|
||||
if(editorRef.current !== null) {
|
||||
lastPosition = editorRef.current.getPosition();
|
||||
}
|
||||
setCode(newCode);
|
||||
}
|
||||
|
||||
@@ -226,11 +260,27 @@ export function Root(props: IProps): React.ReactElement {
|
||||
return () => clearInterval(id);
|
||||
}, [code]);
|
||||
|
||||
useEffect(() => {
|
||||
function maybeSave(event: KeyboardEvent) {
|
||||
if (Settings.DisableHotkeys) return;
|
||||
//Ctrl + b
|
||||
if (event.keyCode == 66 && (event.ctrlKey || event.metaKey)) {
|
||||
event.preventDefault();
|
||||
save();
|
||||
}
|
||||
}
|
||||
document.addEventListener('keydown', maybeSave);
|
||||
return () => document.removeEventListener('keydown', maybeSave);
|
||||
})
|
||||
|
||||
function onMount(editor: IStandaloneCodeEditor): void {
|
||||
editorRef.current = editor;
|
||||
if(editorRef.current === null) return;
|
||||
const position = CursorPositions.getCursor(filename);
|
||||
editorRef.current.setPosition({lineNumber: position.row, column: position.column});
|
||||
if(position.row !== -1)
|
||||
editorRef.current.setPosition({lineNumber: position.row, column: position.column});
|
||||
else if(lastPosition !== null)
|
||||
editorRef.current.setPosition({lineNumber: lastPosition.lineNumber, column: lastPosition.column+1});
|
||||
editorRef.current.focus();
|
||||
}
|
||||
|
||||
@@ -247,13 +297,12 @@ export function Root(props: IProps): React.ReactElement {
|
||||
});
|
||||
}
|
||||
return { suggestions: suggestions };
|
||||
}
|
||||
},
|
||||
});
|
||||
monaco.languages.typescript.javascriptDefaults.addExtraLib(libSource, 'netscript.d.ts');
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(libSource, 'netscript.d.ts');
|
||||
}
|
||||
|
||||
console.log(options);
|
||||
return (<div id="script-editor-wrapper">
|
||||
<div id="script-editor-filename-wrapper">
|
||||
<p id="script-editor-filename-tag" className="noselect"> <strong style={{backgroundColor:'#555'}}>Script name: </strong></p>
|
||||
@@ -267,7 +316,6 @@ export function Root(props: IProps): React.ReactElement {
|
||||
height="80%"
|
||||
defaultLanguage="javascript"
|
||||
defaultValue={code}
|
||||
value={code}
|
||||
onChange={updateCode}
|
||||
theme={options.theme}
|
||||
options={options}
|
||||
@@ -279,4 +327,4 @@ export function Root(props: IProps): React.ReactElement {
|
||||
<a className="std-button" style={{display: "inline-block"}} target="_blank" href="https://bitburner.readthedocs.io/en/latest/index.html">Netscript Documentation</a>
|
||||
</div>
|
||||
</div>);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +145,6 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
|
||||
Object.assign(Settings, defaultSettings);
|
||||
},
|
||||
load(saveString: string) {
|
||||
console.log(saveString);
|
||||
Object.assign(Settings, JSON.parse(saveString));
|
||||
},
|
||||
};
|
||||
|
||||
@@ -57,6 +57,7 @@ import {
|
||||
loadAllRunningScripts,
|
||||
updateOnlineScriptTimes,
|
||||
} from "./NetscriptWorker";
|
||||
import { GetServerByHostname } from "./Server/ServerHelpers";
|
||||
import { Player } from "./Player";
|
||||
import { prestigeAugmentation } from "./Prestige";
|
||||
import {
|
||||
@@ -101,6 +102,9 @@ import { ActiveScriptsRoot } from "./ui/ActiveScripts/Root";
|
||||
import { initializeMainMenuHeaders } from "./ui/MainMenu/Headers";
|
||||
import { initializeMainMenuLinks, MainMenuLinks } from "./ui/MainMenu/Links";
|
||||
|
||||
import { FileDiagnosticPopup } from "./Diagnostic/FileDiagnosticPopup";
|
||||
import { createPopup } from "./ui/React/createPopup";
|
||||
|
||||
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||
import { gameOptionsBoxClose, gameOptionsBoxOpen } from "../utils/GameOptions";
|
||||
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||
@@ -251,9 +255,6 @@ const Engine = {
|
||||
Engine.Display.scriptEditorContent.style.display = "block";
|
||||
routing.navigateTo(Page.ScriptEditor);
|
||||
|
||||
|
||||
const monaco = document.getElementById('monaco-editor');
|
||||
//https://www.npmjs.com/package/@monaco-editor/react#development-playground
|
||||
ReactDOM.render(
|
||||
<ScriptEditorRoot filename={filename} code={code} player={Player} engine={this} />,
|
||||
Engine.Display.scriptEditorContent,
|
||||
@@ -1525,6 +1526,12 @@ const Engine = {
|
||||
gameOptionsBoxClose();
|
||||
return false;
|
||||
});
|
||||
|
||||
// DEBUG File diagnostic
|
||||
document.getElementById("debug-files").addEventListener("click", function() {
|
||||
createPopup("debug-files-diagnostic-popup", FileDiagnosticPopup, {});
|
||||
return false;
|
||||
});
|
||||
},
|
||||
|
||||
start: function() {
|
||||
@@ -1573,4 +1580,4 @@ window.onload = function() {
|
||||
}
|
||||
};
|
||||
|
||||
export {Engine, indexedDb};
|
||||
export {Engine};
|
||||
|
||||
@@ -594,10 +594,12 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
|
||||
Copy Save data to Clipboard
|
||||
</button>
|
||||
<button id="debug-delete-scripts-link" class="a-link-button tooltip">
|
||||
Delete all active scripts
|
||||
Force kill all active scripts
|
||||
<span class="tooltiptextleft">
|
||||
Forcefully kill all active running scripts, in case there is a bug or some unexpected issue with the game. After
|
||||
using this, save the game and then reload the page.
|
||||
using this, save the game and then reload the page. This is different then normal kill in that normal kill
|
||||
will tell the script to shut down while force kill just removes the references to it (and it should crash on it's own).
|
||||
This will not remove the files on your computer. Just forcefully kill all running instance of all scripts.
|
||||
</span>
|
||||
</button>
|
||||
<button id="debug-soft-reset" class="a-link-button tooltip">
|
||||
@@ -606,6 +608,14 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
|
||||
Perform a soft reset. Resets everything as if you had just purchased an Augmentation.
|
||||
</span>
|
||||
</button>
|
||||
<button id="debug-files" class="a-link-button tooltip">
|
||||
Diagnose files
|
||||
<span class="tooltiptextleft">
|
||||
If your save file is extremely big you can use this button
|
||||
to view a map of all the files on every server. Be careful
|
||||
there might be spoilers.
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,6 +6,7 @@ import * as React from "react";
|
||||
|
||||
import { WorkerScriptAccordion } from "./WorkerScriptAccordion";
|
||||
import { Accordion } from "../React/Accordion";
|
||||
import { ServerAccordionContent } from "./ServerAccordionContent";
|
||||
|
||||
import { BaseServer } from "../../Server/BaseServer";
|
||||
import { WorkerScript } from "../../Netscript/WorkerScript";
|
||||
@@ -42,7 +43,7 @@ export function ServerAccordion(props: IProps): React.ReactElement {
|
||||
<pre>{headerTxt}</pre>
|
||||
}
|
||||
panelContent={
|
||||
<ul>{scripts}</ul>
|
||||
<ServerAccordionContent workerScripts={props.workerScripts} />
|
||||
}
|
||||
/>
|
||||
)
|
||||
|
||||
83
src/ui/ActiveScripts/ServerAccordionContent.tsx
Normal file
83
src/ui/ActiveScripts/ServerAccordionContent.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
import React, { useState } from "react";
|
||||
import { WorkerScript } from "../../Netscript/WorkerScript";
|
||||
import { WorkerScriptAccordion } from "./WorkerScriptAccordion";
|
||||
import { AccordionButton } from "../React/AccordionButton";
|
||||
|
||||
const pageSize = 20;
|
||||
|
||||
interface IProps {
|
||||
workerScripts: WorkerScript[];
|
||||
|
||||
}
|
||||
|
||||
export function ServerAccordionContent(props: IProps): React.ReactElement {
|
||||
if(props.workerScripts.length > pageSize) {
|
||||
return <ServerAccordionContentPaginated workerScripts={props.workerScripts} />
|
||||
}
|
||||
|
||||
const scripts = props.workerScripts.map((ws) => {
|
||||
return (
|
||||
<WorkerScriptAccordion key={`${ws.name}_${ws.args}`} workerScript={ws} />
|
||||
)
|
||||
});
|
||||
|
||||
return (<ul>{scripts}</ul>);
|
||||
}
|
||||
|
||||
|
||||
export function ServerAccordionContentPaginated(props: IProps): React.ReactElement {
|
||||
const [page, setPage] = useState(0);
|
||||
const scripts: React.ReactElement[] = [];
|
||||
const maxPage = Math.ceil(props.workerScripts.length/pageSize);
|
||||
const maxScript = Math.min((page+1)*pageSize, props.workerScripts.length);
|
||||
for(let i = page*pageSize; i < maxScript; i++) {
|
||||
const ws = props.workerScripts[i];
|
||||
scripts.push(<WorkerScriptAccordion key={`${ws.name}_${ws.args}`} workerScript={ws} />)
|
||||
}
|
||||
|
||||
function capPage(page: number): number {
|
||||
if(page < 0) {
|
||||
page = 0;
|
||||
}
|
||||
|
||||
if(maxPage-1 < page) {
|
||||
page = maxPage-1;
|
||||
}
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
// in case we're on an invalid page number because scripts were killed.
|
||||
const capped = capPage(page);
|
||||
if(capped !== page)
|
||||
setPage(capped);
|
||||
|
||||
function changePage(n: number): void {
|
||||
setPage(newPage => {
|
||||
newPage += n;
|
||||
newPage = Math.round(newPage);
|
||||
return capPage(newPage);
|
||||
})
|
||||
}
|
||||
|
||||
return (<><ul>{scripts}</ul>
|
||||
<AccordionButton
|
||||
onClick={() => changePage(-1e99)}
|
||||
text="<<"
|
||||
/>
|
||||
<AccordionButton
|
||||
onClick={() => changePage(-1)}
|
||||
text="<"
|
||||
/>
|
||||
<span className="text">{page+1} / {maxPage}</span>
|
||||
<AccordionButton
|
||||
onClick={() => changePage(1)}
|
||||
text=">"
|
||||
/>
|
||||
<AccordionButton
|
||||
onClick={() => changePage(1e99)}
|
||||
text=">>"
|
||||
/>
|
||||
</>);
|
||||
}
|
||||
|
||||
@@ -73,9 +73,11 @@ class AccordionPanel extends React.Component<IPanelProps, any> {
|
||||
className = this.props.panelClass;
|
||||
}
|
||||
|
||||
if(!this.props.opened) return (<></>);
|
||||
|
||||
|
||||
return (
|
||||
<div className={className} style={{display: this.props.opened ? "block" : "none"}}>
|
||||
<div className={className} style={{display: "block"}}>
|
||||
{this.props.panelContent}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -31,7 +31,7 @@ export class AutoupdatingParagraph extends React.Component<IProps, IState> {
|
||||
|
||||
componentDidMount(): void {
|
||||
const time = this.props.intervalTime ? this.props.intervalTime : 1000;
|
||||
this.interval = setInterval(() => this.tick(), time);
|
||||
this.interval = window.setInterval(() => this.tick(), time);
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
|
||||
@@ -38,7 +38,7 @@ export class AutoupdatingStdButton extends React.Component<IProps, IState> {
|
||||
|
||||
componentDidMount(): void {
|
||||
const time = this.props.intervalTime ? this.props.intervalTime : 1000;
|
||||
this.interval = setInterval(() => this.tick(), time);
|
||||
this.interval = window.setInterval(() => this.tick(), time);
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
|
||||
@@ -5,12 +5,6 @@ import { RunningScript } from "../../src/Script/RunningScript";
|
||||
import { Script } from "../../src/Script/Script";
|
||||
import { SourceFileFlags } from "../../src/SourceFile/SourceFileFlags";
|
||||
|
||||
import { expect } from "chai";
|
||||
|
||||
console.log("Beginning Netscript Dynamic RAM Calculation/Generation Tests");
|
||||
|
||||
console.log("Beginning Netscript Static RAM Calculation/Generation Tests");
|
||||
|
||||
const ScriptBaseCost = RamCostConstants.ScriptBaseRamCost;
|
||||
|
||||
describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
|
||||
@@ -27,12 +21,13 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
|
||||
|
||||
// Tests numeric equality, allowing for floating point imprecision
|
||||
function testEquality(val, expected) {
|
||||
expect(val).to.be.within(expected - 100 * Number.EPSILON, expected + 100 * Number.EPSILON);
|
||||
expect(val).toBeGreaterThanOrEqual(expected - 100 * Number.EPSILON);
|
||||
expect(val).toBeLessThanOrEqual(expected + 100 * Number.EPSILON);
|
||||
}
|
||||
|
||||
// Runs a Netscript function and properly catches it if it returns promise
|
||||
function runPotentiallyAsyncFunction(fn) {
|
||||
let res = fn();
|
||||
const res = fn();
|
||||
if (res instanceof Promise) {
|
||||
res.catch(() => undefined);
|
||||
}
|
||||
@@ -47,9 +42,9 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
|
||||
* including the namespace(s). e.g. ["gang", "getMemberNames"]
|
||||
*/
|
||||
async function testNonzeroDynamicRamCost(fnDesc) {
|
||||
if (!Array.isArray(fnDesc)) { expect.fail("Non-array passed to testNonzeroDynamicRamCost()"); }
|
||||
if (!Array.isArray(fnDesc)) { throw new Error("Non-array passed to testNonzeroDynamicRamCost()"); }
|
||||
const expected = getRamCost(...fnDesc);
|
||||
expect(expected).to.be.above(0);
|
||||
expect(expected).toBeGreaterThan(0);
|
||||
|
||||
const code = `${fnDesc.join(".")}();`
|
||||
|
||||
@@ -72,7 +67,7 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
|
||||
let curr = scope[fnDesc[0]];
|
||||
for (let i = 1; i < fnDesc.length; ++i) {
|
||||
if (curr == null) {
|
||||
expect.fail(`Invalid function specified: [${fnDesc}]`);
|
||||
throw new Error(`Invalid function specified: [${fnDesc}]`);
|
||||
}
|
||||
|
||||
if (typeof curr === "function") {
|
||||
@@ -92,13 +87,13 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
|
||||
runPotentiallyAsyncFunction(curr);
|
||||
} catch(e) {}
|
||||
} else {
|
||||
expect.fail(`Invalid function specified: [${fndesc}]`);
|
||||
throw new Error(`Invalid function specified: [${fnDesc}]`);
|
||||
}
|
||||
|
||||
const fnName = fnDesc[fnDesc.length - 1];
|
||||
testEquality(workerScript.dynamicRamUsage - ScriptBaseCost, expected);
|
||||
testEquality(workerScript.dynamicRamUsage, runningScript.ramUsage);
|
||||
expect(workerScript.dynamicLoadedFns).to.have.property(fnName);
|
||||
expect(workerScript.dynamicLoadedFns).toHaveProperty(fnName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,9 +105,9 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
|
||||
* including the namespace(s). e.g. ["gang", "getMemberNames"]
|
||||
*/
|
||||
async function testZeroDynamicRamCost(fnDesc) {
|
||||
if (!Array.isArray(fnDesc)) { expect.fail("Non-array passed to testZeroDynamicRamCost()"); }
|
||||
if (!Array.isArray(fnDesc)) { throw new Error("Non-array passed to testZeroDynamicRamCost()"); }
|
||||
const expected = getRamCost(...fnDesc);
|
||||
expect(expected).to.equal(0);
|
||||
expect(expected).toEqual(0);
|
||||
|
||||
const code = `${fnDesc.join(".")}();`
|
||||
|
||||
@@ -135,7 +130,7 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
|
||||
let curr = scope[fnDesc[0]];
|
||||
for (let i = 1; i < fnDesc.length; ++i) {
|
||||
if (curr == null) {
|
||||
expect.fail(`Invalid function specified: [${fnDesc}]`);
|
||||
throw new Error(`Invalid function specified: [${fnDesc}]`);
|
||||
}
|
||||
|
||||
if (typeof curr === "function") {
|
||||
@@ -155,20 +150,20 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
|
||||
runPotentiallyAsyncFunction(curr);
|
||||
} catch(e) {}
|
||||
} else {
|
||||
expect.fail(`Invalid function specified: [${fndesc}]`);
|
||||
throw new Error(`Invalid function specified: [${fndesc}]`);
|
||||
}
|
||||
|
||||
testEquality(workerScript.dynamicRamUsage, ScriptBaseCost);
|
||||
testEquality(workerScript.dynamicRamUsage, runningScript.ramUsage);
|
||||
}
|
||||
|
||||
before(function() {
|
||||
beforeEach(function() {
|
||||
for (let i = 0; i < SourceFileFlags.length; ++i) {
|
||||
SourceFileFlags[i] = 3;
|
||||
}
|
||||
});
|
||||
|
||||
describe("Basic Functions", async function() {
|
||||
describe("Basic Functions", function() {
|
||||
it("hack()", async function() {
|
||||
const f = ["hack"];
|
||||
await testNonzeroDynamicRamCost(f);
|
||||
@@ -570,14 +565,14 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Advanced Functions", async function() {
|
||||
describe("Advanced Functions", function() {
|
||||
it("getBitNodeMultipliers()", async function() {
|
||||
const f = ["getBitNodeMultipliers"];
|
||||
await testNonzeroDynamicRamCost(f);
|
||||
});
|
||||
});
|
||||
|
||||
describe("TIX API", async function() {
|
||||
describe("TIX API", function() {
|
||||
it("getStockSymbols()", async function() {
|
||||
const f = ["getStockSymbols"];
|
||||
await testNonzeroDynamicRamCost(f);
|
||||
@@ -664,7 +659,7 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Singularity Functions", async function() {
|
||||
describe("Singularity Functions", function() {
|
||||
it("universityCourse()", async function() {
|
||||
const f = ["universityCourse"];
|
||||
await testNonzeroDynamicRamCost(f);
|
||||
@@ -831,7 +826,7 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Bladeburner API", async function() {
|
||||
describe("Bladeburner API", function() {
|
||||
it("getContractNames()", async function() {
|
||||
const f = ["bladeburner", "getContractNames"];
|
||||
await testNonzeroDynamicRamCost(f);
|
||||
@@ -1003,7 +998,7 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Gang API", async function() {
|
||||
describe("Gang API", function() {
|
||||
it("getMemberNames()", async function() {
|
||||
const f = ["gang", "getMemberNames"];
|
||||
await testNonzeroDynamicRamCost(f);
|
||||
@@ -1085,7 +1080,7 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Coding Contract API", async function() {
|
||||
describe("Coding Contract API", function() {
|
||||
it("attempt()", async function() {
|
||||
const f = ["codingcontract", "attempt"];
|
||||
await testNonzeroDynamicRamCost(f);
|
||||
@@ -1112,7 +1107,7 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Sleeve API", async function() {
|
||||
describe("Sleeve API", function() {
|
||||
it("getNumSleeves()", async function() {
|
||||
const f = ["sleeve", "getNumSleeves"];
|
||||
await testNonzeroDynamicRamCost(f);
|
||||
@@ -1,8 +1,5 @@
|
||||
import { getRamCost, RamCostConstants } from "../../src/Netscript/RamCostGenerator";
|
||||
import { calculateRamUsage } from "../../src/Script/RamCalculations"
|
||||
import { expect } from "chai";
|
||||
|
||||
console.log("Beginning Netscript Static RAM Calculation/Generation Tests");
|
||||
|
||||
const ScriptBaseCost = RamCostConstants.ScriptBaseRamCost;
|
||||
const HacknetNamespaceCost = RamCostConstants.ScriptHacknetNodesRamCost;
|
||||
@@ -10,7 +7,8 @@ const HacknetNamespaceCost = RamCostConstants.ScriptHacknetNodesRamCost;
|
||||
describe("Netscript Static RAM Calculation/Generation Tests", function() {
|
||||
// Tests numeric equality, allowing for floating point imprecision
|
||||
function testEquality(val, expected) {
|
||||
expect(val).to.be.within(expected - 100 * Number.EPSILON, expected + 100 * Number.EPSILON);
|
||||
expect(val).toBeGreaterThanOrEqual(expected - 100 * Number.EPSILON);
|
||||
expect(val).toBeLessThanOrEqual(expected + 100 * Number.EPSILON);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -24,7 +22,7 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
|
||||
async function expectNonZeroRamCost(fnDesc) {
|
||||
if (!Array.isArray(fnDesc)) { expect.fail("Non-array passed to expectNonZeroRamCost()"); }
|
||||
const expected = getRamCost(...fnDesc);
|
||||
expect(expected).to.be.above(0);
|
||||
expect(expected).toBeGreaterThan(0);
|
||||
|
||||
const code = fnDesc.join(".") + "(); ";
|
||||
|
||||
@@ -33,7 +31,7 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
|
||||
|
||||
const multipleCallsCode = code.repeat(3);
|
||||
const multipleCallsCalculated = await calculateRamUsage(multipleCallsCode, []);
|
||||
expect(multipleCallsCalculated).to.equal(calculated);
|
||||
expect(multipleCallsCalculated).toEqual(calculated);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,7 +45,7 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
|
||||
async function expectZeroRamCost(fnDesc) {
|
||||
if (!Array.isArray(fnDesc)) { expect.fail("Non-array passed to expectZeroRamCost()"); }
|
||||
const expected = getRamCost(...fnDesc);
|
||||
expect(expected).to.equal(0);
|
||||
expect(expected).toEqual(0);
|
||||
|
||||
const code = fnDesc.join(".") + "(); ";
|
||||
|
||||
@@ -55,10 +53,10 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
|
||||
testEquality(calculated, ScriptBaseCost);
|
||||
|
||||
const multipleCallsCalculated = await calculateRamUsage(code, []);
|
||||
expect(multipleCallsCalculated).to.equal(ScriptBaseCost);
|
||||
expect(multipleCallsCalculated).toEqual(ScriptBaseCost);
|
||||
}
|
||||
|
||||
describe("Basic Functions", async function() {
|
||||
describe("Basic Functions", function() {
|
||||
it("hack()", async function() {
|
||||
const f = ["hack"];
|
||||
await expectNonZeroRamCost(f);
|
||||
@@ -460,14 +458,14 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Advanced Functions", async function() {
|
||||
describe("Advanced Functions", function() {
|
||||
it("getBitNodeMultipliers()", async function() {
|
||||
const f = ["getBitNodeMultipliers"];
|
||||
await expectNonZeroRamCost(f);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Hacknet Node API", async function() {
|
||||
describe("Hacknet Node API", function() {
|
||||
// The Hacknet Node API RAM cost is a bit different because
|
||||
// it's just a one-time cost to access the 'hacknet' namespace.
|
||||
// Otherwise, all functions cost 0 RAM
|
||||
@@ -490,7 +488,7 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
|
||||
]
|
||||
it("should have zero RAM cost for all functions", function() {
|
||||
for (const fn of apiFunctions) {
|
||||
expect(getRamCost("hacknet", fn)).to.equal(0);
|
||||
expect(getRamCost("hacknet", fn)).toEqual(0);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -505,7 +503,7 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("TIX API", async function() {
|
||||
describe("TIX API", function() {
|
||||
it("getStockSymbols()", async function() {
|
||||
const f = ["getStockSymbols"];
|
||||
await expectNonZeroRamCost(f);
|
||||
@@ -602,7 +600,7 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Singularity Functions", async function() {
|
||||
describe("Singularity Functions", function() {
|
||||
it("universityCourse()", async function() {
|
||||
const f = ["universityCourse"];
|
||||
await expectNonZeroRamCost(f);
|
||||
@@ -769,7 +767,7 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Bladeburner API", async function() {
|
||||
describe("Bladeburner API", function() {
|
||||
it("getContractNames()", async function() {
|
||||
const f = ["bladeburner", "getContractNames"];
|
||||
await expectNonZeroRamCost(f);
|
||||
@@ -941,7 +939,7 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Gang API", async function() {
|
||||
describe("Gang API", function() {
|
||||
it("getMemberNames()", async function() {
|
||||
const f = ["gang", "getMemberNames"];
|
||||
await expectNonZeroRamCost(f);
|
||||
@@ -1023,7 +1021,7 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Coding Contract API", async function() {
|
||||
describe("Coding Contract API", function() {
|
||||
it("attempt()", async function() {
|
||||
const f = ["codingcontract", "attempt"];
|
||||
await expectNonZeroRamCost(f);
|
||||
@@ -1050,7 +1048,7 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Sleeve API", async function() {
|
||||
describe("Sleeve API", function() {
|
||||
it("getNumSleeves()", async function() {
|
||||
const f = ["sleeve", "getNumSleeves"];
|
||||
await expectNonZeroRamCost(f);
|
||||
@@ -1,5 +1,10 @@
|
||||
# Unit Tests
|
||||
This directory contains unit tests for Bitburner.
|
||||
|
||||
Unit tests use Mocha/Chai and are run using mochapack (a mocha-webpack fork).
|
||||
Run the test command with `npm run test`
|
||||
Unit tests use jest.
|
||||
|
||||
## Running
|
||||
|
||||
Run tests with: `npm run test`
|
||||
|
||||
To watch for changes: `npm run test:watch`
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
33
test/StringHelperFunctions.test.ts
Normal file
33
test/StringHelperFunctions.test.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
|
||||
|
||||
describe("StringHelperFunctions Tests", function () {
|
||||
it("transforms strings", () => {
|
||||
expect(convertTimeMsToTimeElapsedString(1000)).toEqual("1 seconds");
|
||||
expect(convertTimeMsToTimeElapsedString(5 * 60 * 1000 + 34 * 1000)).toEqual(
|
||||
"5 minutes 34 seconds",
|
||||
);
|
||||
expect(
|
||||
convertTimeMsToTimeElapsedString(
|
||||
2 * 60 * 60 * 24 * 1000 + 5 * 60 * 1000 + 34 * 1000,
|
||||
),
|
||||
).toEqual("2 days 5 minutes 34 seconds");
|
||||
expect(
|
||||
convertTimeMsToTimeElapsedString(
|
||||
2 * 60 * 60 * 24 * 1000 + 5 * 60 * 1000 + 34 * 1000,
|
||||
true,
|
||||
),
|
||||
).toEqual("2 days 5 minutes 34.000 seconds");
|
||||
expect(
|
||||
convertTimeMsToTimeElapsedString(
|
||||
2 * 60 * 60 * 24 * 1000 + 5 * 60 * 1000 + 34 * 1000 + 123,
|
||||
true,
|
||||
),
|
||||
).toEqual("2 days 5 minutes 34.123 seconds");
|
||||
expect(
|
||||
convertTimeMsToTimeElapsedString(
|
||||
2 * 60 * 60 * 24 * 1000 + 5 * 60 * 1000 + 34 * 1000 + 123.888,
|
||||
true,
|
||||
),
|
||||
).toEqual("2 days 5 minutes 34.123 seconds");
|
||||
})
|
||||
});
|
||||
@@ -1,11 +0,0 @@
|
||||
import { expect } from "chai";
|
||||
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
|
||||
|
||||
describe("StringHelperFunctions Tests", function() {
|
||||
expect(convertTimeMsToTimeElapsedString(1000)).to.equal("1 seconds");
|
||||
expect(convertTimeMsToTimeElapsedString(5*60*1000+34*1000)).to.equal("5 minutes 34 seconds");
|
||||
expect(convertTimeMsToTimeElapsedString(2*60*60*24*1000+5*60*1000+34*1000)).to.equal("2 days 5 minutes 34 seconds");
|
||||
expect(convertTimeMsToTimeElapsedString(2*60*60*24*1000+5*60*1000+34*1000, true)).to.equal("2 days 5 minutes 34.000 seconds");
|
||||
expect(convertTimeMsToTimeElapsedString(2*60*60*24*1000+5*60*1000+34*1000+123, true)).to.equal("2 days 5 minutes 34.123 seconds");
|
||||
expect(convertTimeMsToTimeElapsedString(2*60*60*24*1000+5*60*1000+34*1000+123.888, true)).to.equal("2 days 5 minutes 34.123 seconds");
|
||||
})
|
||||
295
test/Terminal/Directory.test.js
Normal file
295
test/Terminal/Directory.test.js
Normal file
@@ -0,0 +1,295 @@
|
||||
import * as dirHelpers from "../../src/Terminal/DirectoryHelpers";
|
||||
|
||||
describe("Terminal Directory Tests", function() {
|
||||
describe("removeLeadingSlash()", function() {
|
||||
const removeLeadingSlash = dirHelpers.removeLeadingSlash;
|
||||
|
||||
it("should remove first slash in a string", function() {
|
||||
expect(removeLeadingSlash("/")).toEqual("");
|
||||
expect(removeLeadingSlash("/foo.txt")).toEqual("foo.txt");
|
||||
expect(removeLeadingSlash("/foo/file.txt")).toEqual("foo/file.txt");
|
||||
});
|
||||
|
||||
it("should only remove one slash", function() {
|
||||
expect(removeLeadingSlash("///")).toEqual("//");
|
||||
expect(removeLeadingSlash("//foo")).toEqual("/foo");
|
||||
});
|
||||
|
||||
it("should do nothing for a string that doesn't start with a slash", function() {
|
||||
expect(removeLeadingSlash("foo.txt")).toEqual("foo.txt");
|
||||
expect(removeLeadingSlash("foo/test.txt")).toEqual("foo/test.txt");
|
||||
});
|
||||
|
||||
it("should not fail on an empty string", function() {
|
||||
expect(removeLeadingSlash.bind(null, "")).not.toThrow();
|
||||
expect(removeLeadingSlash("")).toEqual("");
|
||||
});
|
||||
});
|
||||
|
||||
describe("removeTrailingSlash()", function() {
|
||||
const removeTrailingSlash = dirHelpers.removeTrailingSlash;
|
||||
|
||||
it("should remove last slash in a string", function() {
|
||||
expect(removeTrailingSlash("/")).toEqual("");
|
||||
expect(removeTrailingSlash("foo.txt/")).toEqual("foo.txt");
|
||||
expect(removeTrailingSlash("foo/file.txt/")).toEqual("foo/file.txt");
|
||||
});
|
||||
|
||||
it("should only remove one slash", function() {
|
||||
expect(removeTrailingSlash("///")).toEqual("//");
|
||||
expect(removeTrailingSlash("foo//")).toEqual("foo/");
|
||||
});
|
||||
|
||||
it("should do nothing for a string that doesn't end with a slash", function() {
|
||||
expect(removeTrailingSlash("foo.txt")).toEqual("foo.txt");
|
||||
expect(removeTrailingSlash("foo/test.txt")).toEqual("foo/test.txt");
|
||||
});
|
||||
|
||||
it("should not fail on an empty string", function() {
|
||||
expect(removeTrailingSlash.bind(null, "")).not.toThrow();
|
||||
expect(removeTrailingSlash("")).toEqual("");
|
||||
});
|
||||
});
|
||||
|
||||
describe("isValidFilename()", function() {
|
||||
const isValidFilename = dirHelpers.isValidFilename;
|
||||
|
||||
it("should return true for valid filenames", function() {
|
||||
expect(isValidFilename("test.txt")).toEqual(true);
|
||||
expect(isValidFilename("123.script")).toEqual(true);
|
||||
expect(isValidFilename("foo123.b")).toEqual(true);
|
||||
expect(isValidFilename("my_script.script")).toEqual(true);
|
||||
expect(isValidFilename("my-script.script")).toEqual(true);
|
||||
expect(isValidFilename("_foo.lit")).toEqual(true);
|
||||
expect(isValidFilename("mult.periods.script")).toEqual(true);
|
||||
expect(isValidFilename("mult.per-iods.again.script")).toEqual(true);
|
||||
expect(isValidFilename("BruteSSH.exe-50%-INC")).toEqual(true);
|
||||
expect(isValidFilename("DeepscanV1.exe-1.01%-INC")).toEqual(true);
|
||||
expect(isValidFilename("DeepscanV2.exe-1.00%-INC")).toEqual(true);
|
||||
expect(isValidFilename("AutoLink.exe-1.%-INC")).toEqual(true);
|
||||
});
|
||||
|
||||
it("should return false for invalid filenames", function() {
|
||||
expect(isValidFilename("foo")).toEqual(false);
|
||||
expect(isValidFilename("my script.script")).toEqual(false);
|
||||
expect(isValidFilename("a^.txt")).toEqual(false);
|
||||
expect(isValidFilename("b#.lit")).toEqual(false);
|
||||
expect(isValidFilename("lib().js")).toEqual(false);
|
||||
expect(isValidFilename("foo.script_")).toEqual(false);
|
||||
expect(isValidFilename("foo._script")).toEqual(false);
|
||||
expect(isValidFilename("foo.hyphened-ext")).toEqual(false);
|
||||
expect(isValidFilename("")).toEqual(false);
|
||||
expect(isValidFilename("AutoLink-1.%-INC.exe")).toEqual(false);
|
||||
expect(isValidFilename("AutoLink.exe-1.%-INC.exe")).toEqual(false);
|
||||
expect(isValidFilename("foo%.exe")).toEqual(false);
|
||||
expect(isValidFilename("-1.00%-INC")).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isValidDirectoryName()", function() {
|
||||
const isValidDirectoryName = dirHelpers.isValidDirectoryName;
|
||||
|
||||
it("should return true for valid directory names", function() {
|
||||
expect(isValidDirectoryName("a")).toEqual(true);
|
||||
expect(isValidDirectoryName("foo")).toEqual(true);
|
||||
expect(isValidDirectoryName("foo-dir")).toEqual(true);
|
||||
expect(isValidDirectoryName("foo_dir")).toEqual(true);
|
||||
expect(isValidDirectoryName(".a")).toEqual(true);
|
||||
expect(isValidDirectoryName("1")).toEqual(true);
|
||||
expect(isValidDirectoryName("a1")).toEqual(true);
|
||||
expect(isValidDirectoryName(".a1")).toEqual(true);
|
||||
expect(isValidDirectoryName("._foo")).toEqual(true);
|
||||
expect(isValidDirectoryName("_foo")).toEqual(true);
|
||||
});
|
||||
|
||||
it("should return false for invalid directory names", function() {
|
||||
expect(isValidDirectoryName("")).toEqual(false);
|
||||
expect(isValidDirectoryName("foo.dir")).toEqual(false);
|
||||
expect(isValidDirectoryName("1.")).toEqual(false);
|
||||
expect(isValidDirectoryName("foo.")).toEqual(false);
|
||||
expect(isValidDirectoryName("dir#")).toEqual(false);
|
||||
expect(isValidDirectoryName("dir!")).toEqual(false);
|
||||
expect(isValidDirectoryName("dir*")).toEqual(false);
|
||||
expect(isValidDirectoryName(".")).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isValidDirectoryPath()", function() {
|
||||
const isValidDirectoryPath = dirHelpers.isValidDirectoryPath;
|
||||
|
||||
it("should return false for empty strings", function() {
|
||||
expect(isValidDirectoryPath("")).toEqual(false);
|
||||
});
|
||||
|
||||
it("should return true only for the forward slash if the string has length 1", function() {
|
||||
expect(isValidDirectoryPath("/")).toEqual(true);
|
||||
expect(isValidDirectoryPath(" ")).toEqual(false);
|
||||
expect(isValidDirectoryPath(".")).toEqual(false);
|
||||
expect(isValidDirectoryPath("a")).toEqual(false);
|
||||
});
|
||||
|
||||
it("should return true for valid directory paths", function() {
|
||||
expect(isValidDirectoryPath("/a")).toEqual(true);
|
||||
expect(isValidDirectoryPath("/dir/a")).toEqual(true);
|
||||
expect(isValidDirectoryPath("/dir/foo")).toEqual(true);
|
||||
expect(isValidDirectoryPath("/.dir/foo-dir")).toEqual(true);
|
||||
expect(isValidDirectoryPath("/.dir/foo_dir")).toEqual(true);
|
||||
expect(isValidDirectoryPath("/.dir/.a")).toEqual(true);
|
||||
expect(isValidDirectoryPath("/dir1/1")).toEqual(true);
|
||||
expect(isValidDirectoryPath("/dir1/a1")).toEqual(true);
|
||||
expect(isValidDirectoryPath("/dir1/.a1")).toEqual(true);
|
||||
expect(isValidDirectoryPath("/dir_/._foo")).toEqual(true);
|
||||
expect(isValidDirectoryPath("/dir-/_foo")).toEqual(true);
|
||||
});
|
||||
|
||||
it("should return false if the path does not have a leading slash", function() {
|
||||
expect(isValidDirectoryPath("a")).toEqual(false);
|
||||
expect(isValidDirectoryPath("dir/a")).toEqual(false);
|
||||
expect(isValidDirectoryPath("dir/foo")).toEqual(false);
|
||||
expect(isValidDirectoryPath(".dir/foo-dir")).toEqual(false);
|
||||
expect(isValidDirectoryPath(".dir/foo_dir")).toEqual(false);
|
||||
expect(isValidDirectoryPath(".dir/.a")).toEqual(false);
|
||||
expect(isValidDirectoryPath("dir1/1")).toEqual(false);
|
||||
expect(isValidDirectoryPath("dir1/a1")).toEqual(false);
|
||||
expect(isValidDirectoryPath("dir1/.a1")).toEqual(false);
|
||||
expect(isValidDirectoryPath("dir_/._foo")).toEqual(false);
|
||||
expect(isValidDirectoryPath("dir-/_foo")).toEqual(false);
|
||||
});
|
||||
|
||||
it("should accept dot notation", function() {
|
||||
expect(isValidDirectoryPath("/dir/./a")).toEqual(true);
|
||||
expect(isValidDirectoryPath("/dir/../foo")).toEqual(true);
|
||||
expect(isValidDirectoryPath("/.dir/./foo-dir")).toEqual(true);
|
||||
expect(isValidDirectoryPath("/.dir/../foo_dir")).toEqual(true);
|
||||
expect(isValidDirectoryPath("/.dir/./.a")).toEqual(true);
|
||||
expect(isValidDirectoryPath("/dir1/1/.")).toEqual(true);
|
||||
expect(isValidDirectoryPath("/dir1/a1/..")).toEqual(true);
|
||||
expect(isValidDirectoryPath("/dir1/.a1/..")).toEqual(true);
|
||||
expect(isValidDirectoryPath("/dir_/._foo/.")).toEqual(true);
|
||||
expect(isValidDirectoryPath("/./dir-/_foo")).toEqual(true);
|
||||
expect(isValidDirectoryPath("/../dir-/_foo")).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isValidFilePath()", function() {
|
||||
const isValidFilePath = dirHelpers.isValidFilePath;
|
||||
|
||||
it("should return false for strings that are too short", function() {
|
||||
expect(isValidFilePath("/a")).toEqual(false);
|
||||
expect(isValidFilePath("a.")).toEqual(false);
|
||||
expect(isValidFilePath(".a")).toEqual(false);
|
||||
expect(isValidFilePath("/.")).toEqual(false);
|
||||
});
|
||||
|
||||
it("should return true for arguments that are just filenames", function() {
|
||||
expect(isValidFilePath("test.txt")).toEqual(true);
|
||||
expect(isValidFilePath("123.script")).toEqual(true);
|
||||
expect(isValidFilePath("foo123.b")).toEqual(true);
|
||||
expect(isValidFilePath("my_script.script")).toEqual(true);
|
||||
expect(isValidFilePath("my-script.script")).toEqual(true);
|
||||
expect(isValidFilePath("_foo.lit")).toEqual(true);
|
||||
expect(isValidFilePath("mult.periods.script")).toEqual(true);
|
||||
expect(isValidFilePath("mult.per-iods.again.script")).toEqual(true);
|
||||
});
|
||||
|
||||
it("should return true for valid filepaths", function() {
|
||||
expect(isValidFilePath("/foo/test.txt")).toEqual(true);
|
||||
expect(isValidFilePath("/../123.script")).toEqual(true);
|
||||
expect(isValidFilePath("/./foo123.b")).toEqual(true);
|
||||
expect(isValidFilePath("/dir/my_script.script")).toEqual(true);
|
||||
expect(isValidFilePath("/dir1/dir2/dir3/my-script.script")).toEqual(true);
|
||||
expect(isValidFilePath("/dir1/dir2/././../_foo.lit")).toEqual(true);
|
||||
expect(isValidFilePath("/.dir1/./../.dir2/mult.periods.script")).toEqual(true);
|
||||
expect(isValidFilePath("/_dir/../dir2/mult.per-iods.again.script")).toEqual(true);
|
||||
});
|
||||
|
||||
it("should return false for strings that end with a slash", function() {
|
||||
expect(isValidFilePath("/foo/")).toEqual(false);
|
||||
expect(isValidFilePath("foo.txt/")).toEqual(false);
|
||||
expect(isValidFilePath("/")).toEqual(false);
|
||||
expect(isValidFilePath("/_dir/")).toEqual(false);
|
||||
});
|
||||
|
||||
it("should return false for invalid arguments", function() {
|
||||
expect(isValidFilePath(null)).toEqual(false);
|
||||
expect(isValidFilePath()).toEqual(false);
|
||||
expect(isValidFilePath(5)).toEqual(false);
|
||||
expect(isValidFilePath({})).toEqual(false);
|
||||
})
|
||||
});
|
||||
|
||||
describe("getFirstParentDirectory()", function() {
|
||||
const getFirstParentDirectory = dirHelpers.getFirstParentDirectory;
|
||||
|
||||
it("should return the first parent directory in a filepath", function() {
|
||||
expect(getFirstParentDirectory("/dir1/foo.txt")).toEqual("dir1/");
|
||||
expect(getFirstParentDirectory("/dir1/dir2/dir3/dir4/foo.txt")).toEqual("dir1/");
|
||||
expect(getFirstParentDirectory("/_dir1/dir2/foo.js")).toEqual("_dir1/");
|
||||
});
|
||||
|
||||
it("should return '/' if there is no first parent directory", function() {
|
||||
expect(getFirstParentDirectory("")).toEqual("/");
|
||||
expect(getFirstParentDirectory(" ")).toEqual("/");
|
||||
expect(getFirstParentDirectory("/")).toEqual("/");
|
||||
expect(getFirstParentDirectory("//")).toEqual("/");
|
||||
expect(getFirstParentDirectory("foo.script")).toEqual("/");
|
||||
expect(getFirstParentDirectory("/foo.txt")).toEqual("/");
|
||||
});
|
||||
});
|
||||
|
||||
describe("getAllParentDirectories()", function() {
|
||||
const getAllParentDirectories = dirHelpers.getAllParentDirectories;
|
||||
|
||||
it("should return all parent directories in a filepath", function() {
|
||||
expect(getAllParentDirectories("/")).toEqual("/");
|
||||
expect(getAllParentDirectories("/home/var/foo.txt")).toEqual("/home/var/");
|
||||
expect(getAllParentDirectories("/home/var/")).toEqual("/home/var/");
|
||||
expect(getAllParentDirectories("/home/var/test/")).toEqual("/home/var/test/");
|
||||
});
|
||||
|
||||
it("should return an empty string if there are no parent directories", function() {
|
||||
expect(getAllParentDirectories("foo.txt")).toEqual("");
|
||||
});
|
||||
});
|
||||
|
||||
describe("isInRootDirectory()", function() {
|
||||
const isInRootDirectory = dirHelpers.isInRootDirectory;
|
||||
|
||||
it("should return true for filepaths that refer to a file in the root directory", function() {
|
||||
expect(isInRootDirectory("a.b")).toEqual(true);
|
||||
expect(isInRootDirectory("foo.txt")).toEqual(true);
|
||||
expect(isInRootDirectory("/foo.txt")).toEqual(true);
|
||||
});
|
||||
|
||||
it("should return false for filepaths that refer to a file that's NOT in the root directory", function() {
|
||||
expect(isInRootDirectory("/dir/foo.txt")).toEqual(false);
|
||||
expect(isInRootDirectory("dir/foo.txt")).toEqual(false);
|
||||
expect(isInRootDirectory("/./foo.js")).toEqual(false);
|
||||
expect(isInRootDirectory("../foo.js")).toEqual(false);
|
||||
expect(isInRootDirectory("/dir1/dir2/dir3/foo.txt")).toEqual(false);
|
||||
});
|
||||
|
||||
it("should return false for invalid inputs (inputs that aren't filepaths)", function() {
|
||||
expect(isInRootDirectory(null)).toEqual(false);
|
||||
expect(isInRootDirectory(undefined)).toEqual(false);
|
||||
expect(isInRootDirectory("")).toEqual(false);
|
||||
expect(isInRootDirectory(" ")).toEqual(false);
|
||||
expect(isInRootDirectory("a")).toEqual(false);
|
||||
expect(isInRootDirectory("/dir")).toEqual(false);
|
||||
expect(isInRootDirectory("/dir/")).toEqual(false);
|
||||
expect(isInRootDirectory("/dir/foo")).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("evaluateDirectoryPath()", function() {
|
||||
//const evaluateDirectoryPath = dirHelpers.evaluateDirectoryPath;
|
||||
|
||||
// TODO
|
||||
});
|
||||
|
||||
describe("evaluateFilePath()", function() {
|
||||
//const evaluateFilePath = dirHelpers.evaluateFilePath;
|
||||
|
||||
// TODO
|
||||
})
|
||||
});
|
||||
@@ -1,299 +0,0 @@
|
||||
import * as dirHelpers from "../../src/Terminal/DirectoryHelpers";
|
||||
|
||||
import { expect } from "chai";
|
||||
|
||||
console.log("Beginning Terminal Directory Tests");
|
||||
|
||||
describe("Terminal Directory Tests", function() {
|
||||
describe("removeLeadingSlash()", function() {
|
||||
const removeLeadingSlash = dirHelpers.removeLeadingSlash;
|
||||
|
||||
it("should remove first slash in a string", function() {
|
||||
expect(removeLeadingSlash("/")).to.equal("");
|
||||
expect(removeLeadingSlash("/foo.txt")).to.equal("foo.txt");
|
||||
expect(removeLeadingSlash("/foo/file.txt")).to.equal("foo/file.txt");
|
||||
});
|
||||
|
||||
it("should only remove one slash", function() {
|
||||
expect(removeLeadingSlash("///")).to.equal("//");
|
||||
expect(removeLeadingSlash("//foo")).to.equal("/foo");
|
||||
});
|
||||
|
||||
it("should do nothing for a string that doesn't start with a slash", function() {
|
||||
expect(removeLeadingSlash("foo.txt")).to.equal("foo.txt");
|
||||
expect(removeLeadingSlash("foo/test.txt")).to.equal("foo/test.txt");
|
||||
});
|
||||
|
||||
it("should not fail on an empty string", function() {
|
||||
expect(removeLeadingSlash.bind(null, "")).to.not.throw();
|
||||
expect(removeLeadingSlash("")).to.equal("");
|
||||
});
|
||||
});
|
||||
|
||||
describe("removeTrailingSlash()", function() {
|
||||
const removeTrailingSlash = dirHelpers.removeTrailingSlash;
|
||||
|
||||
it("should remove last slash in a string", function() {
|
||||
expect(removeTrailingSlash("/")).to.equal("");
|
||||
expect(removeTrailingSlash("foo.txt/")).to.equal("foo.txt");
|
||||
expect(removeTrailingSlash("foo/file.txt/")).to.equal("foo/file.txt");
|
||||
});
|
||||
|
||||
it("should only remove one slash", function() {
|
||||
expect(removeTrailingSlash("///")).to.equal("//");
|
||||
expect(removeTrailingSlash("foo//")).to.equal("foo/");
|
||||
});
|
||||
|
||||
it("should do nothing for a string that doesn't end with a slash", function() {
|
||||
expect(removeTrailingSlash("foo.txt")).to.equal("foo.txt");
|
||||
expect(removeTrailingSlash("foo/test.txt")).to.equal("foo/test.txt");
|
||||
});
|
||||
|
||||
it("should not fail on an empty string", function() {
|
||||
expect(removeTrailingSlash.bind(null, "")).to.not.throw();
|
||||
expect(removeTrailingSlash("")).to.equal("");
|
||||
});
|
||||
});
|
||||
|
||||
describe("isValidFilename()", function() {
|
||||
const isValidFilename = dirHelpers.isValidFilename;
|
||||
|
||||
it("should return true for valid filenames", function() {
|
||||
expect(isValidFilename("test.txt")).to.equal(true);
|
||||
expect(isValidFilename("123.script")).to.equal(true);
|
||||
expect(isValidFilename("foo123.b")).to.equal(true);
|
||||
expect(isValidFilename("my_script.script")).to.equal(true);
|
||||
expect(isValidFilename("my-script.script")).to.equal(true);
|
||||
expect(isValidFilename("_foo.lit")).to.equal(true);
|
||||
expect(isValidFilename("mult.periods.script")).to.equal(true);
|
||||
expect(isValidFilename("mult.per-iods.again.script")).to.equal(true);
|
||||
expect(isValidFilename("BruteSSH.exe-50%-INC")).to.equal(true);
|
||||
expect(isValidFilename("DeepscanV1.exe-1.01%-INC")).to.equal(true);
|
||||
expect(isValidFilename("DeepscanV2.exe-1.00%-INC")).to.equal(true);
|
||||
expect(isValidFilename("AutoLink.exe-1.%-INC")).to.equal(true);
|
||||
});
|
||||
|
||||
it("should return false for invalid filenames", function() {
|
||||
expect(isValidFilename("foo")).to.equal(false);
|
||||
expect(isValidFilename("my script.script")).to.equal(false);
|
||||
expect(isValidFilename("a^.txt")).to.equal(false);
|
||||
expect(isValidFilename("b#.lit")).to.equal(false);
|
||||
expect(isValidFilename("lib().js")).to.equal(false);
|
||||
expect(isValidFilename("foo.script_")).to.equal(false);
|
||||
expect(isValidFilename("foo._script")).to.equal(false);
|
||||
expect(isValidFilename("foo.hyphened-ext")).to.equal(false);
|
||||
expect(isValidFilename("")).to.equal(false);
|
||||
expect(isValidFilename("AutoLink-1.%-INC.exe")).to.equal(false);
|
||||
expect(isValidFilename("AutoLink.exe-1.%-INC.exe")).to.equal(false);
|
||||
expect(isValidFilename("foo%.exe")).to.equal(false);
|
||||
expect(isValidFilename("-1.00%-INC")).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isValidDirectoryName()", function() {
|
||||
const isValidDirectoryName = dirHelpers.isValidDirectoryName;
|
||||
|
||||
it("should return true for valid directory names", function() {
|
||||
expect(isValidDirectoryName("a")).to.equal(true);
|
||||
expect(isValidDirectoryName("foo")).to.equal(true);
|
||||
expect(isValidDirectoryName("foo-dir")).to.equal(true);
|
||||
expect(isValidDirectoryName("foo_dir")).to.equal(true);
|
||||
expect(isValidDirectoryName(".a")).to.equal(true);
|
||||
expect(isValidDirectoryName("1")).to.equal(true);
|
||||
expect(isValidDirectoryName("a1")).to.equal(true);
|
||||
expect(isValidDirectoryName(".a1")).to.equal(true);
|
||||
expect(isValidDirectoryName("._foo")).to.equal(true);
|
||||
expect(isValidDirectoryName("_foo")).to.equal(true);
|
||||
});
|
||||
|
||||
it("should return false for invalid directory names", function() {
|
||||
expect(isValidDirectoryName("")).to.equal(false);
|
||||
expect(isValidDirectoryName("foo.dir")).to.equal(false);
|
||||
expect(isValidDirectoryName("1.")).to.equal(false);
|
||||
expect(isValidDirectoryName("foo.")).to.equal(false);
|
||||
expect(isValidDirectoryName("dir#")).to.equal(false);
|
||||
expect(isValidDirectoryName("dir!")).to.equal(false);
|
||||
expect(isValidDirectoryName("dir*")).to.equal(false);
|
||||
expect(isValidDirectoryName(".")).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isValidDirectoryPath()", function() {
|
||||
const isValidDirectoryPath = dirHelpers.isValidDirectoryPath;
|
||||
|
||||
it("should return false for empty strings", function() {
|
||||
expect(isValidDirectoryPath("")).to.equal(false);
|
||||
});
|
||||
|
||||
it("should return true only for the forward slash if the string has length 1", function() {
|
||||
expect(isValidDirectoryPath("/")).to.equal(true);
|
||||
expect(isValidDirectoryPath(" ")).to.equal(false);
|
||||
expect(isValidDirectoryPath(".")).to.equal(false);
|
||||
expect(isValidDirectoryPath("a")).to.equal(false);
|
||||
});
|
||||
|
||||
it("should return true for valid directory paths", function() {
|
||||
expect(isValidDirectoryPath("/a")).to.equal(true);
|
||||
expect(isValidDirectoryPath("/dir/a")).to.equal(true);
|
||||
expect(isValidDirectoryPath("/dir/foo")).to.equal(true);
|
||||
expect(isValidDirectoryPath("/.dir/foo-dir")).to.equal(true);
|
||||
expect(isValidDirectoryPath("/.dir/foo_dir")).to.equal(true);
|
||||
expect(isValidDirectoryPath("/.dir/.a")).to.equal(true);
|
||||
expect(isValidDirectoryPath("/dir1/1")).to.equal(true);
|
||||
expect(isValidDirectoryPath("/dir1/a1")).to.equal(true);
|
||||
expect(isValidDirectoryPath("/dir1/.a1")).to.equal(true);
|
||||
expect(isValidDirectoryPath("/dir_/._foo")).to.equal(true);
|
||||
expect(isValidDirectoryPath("/dir-/_foo")).to.equal(true);
|
||||
});
|
||||
|
||||
it("should return false if the path does not have a leading slash", function() {
|
||||
expect(isValidDirectoryPath("a")).to.equal(false);
|
||||
expect(isValidDirectoryPath("dir/a")).to.equal(false);
|
||||
expect(isValidDirectoryPath("dir/foo")).to.equal(false);
|
||||
expect(isValidDirectoryPath(".dir/foo-dir")).to.equal(false);
|
||||
expect(isValidDirectoryPath(".dir/foo_dir")).to.equal(false);
|
||||
expect(isValidDirectoryPath(".dir/.a")).to.equal(false);
|
||||
expect(isValidDirectoryPath("dir1/1")).to.equal(false);
|
||||
expect(isValidDirectoryPath("dir1/a1")).to.equal(false);
|
||||
expect(isValidDirectoryPath("dir1/.a1")).to.equal(false);
|
||||
expect(isValidDirectoryPath("dir_/._foo")).to.equal(false);
|
||||
expect(isValidDirectoryPath("dir-/_foo")).to.equal(false);
|
||||
});
|
||||
|
||||
it("should accept dot notation", function() {
|
||||
expect(isValidDirectoryPath("/dir/./a")).to.equal(true);
|
||||
expect(isValidDirectoryPath("/dir/../foo")).to.equal(true);
|
||||
expect(isValidDirectoryPath("/.dir/./foo-dir")).to.equal(true);
|
||||
expect(isValidDirectoryPath("/.dir/../foo_dir")).to.equal(true);
|
||||
expect(isValidDirectoryPath("/.dir/./.a")).to.equal(true);
|
||||
expect(isValidDirectoryPath("/dir1/1/.")).to.equal(true);
|
||||
expect(isValidDirectoryPath("/dir1/a1/..")).to.equal(true);
|
||||
expect(isValidDirectoryPath("/dir1/.a1/..")).to.equal(true);
|
||||
expect(isValidDirectoryPath("/dir_/._foo/.")).to.equal(true);
|
||||
expect(isValidDirectoryPath("/./dir-/_foo")).to.equal(true);
|
||||
expect(isValidDirectoryPath("/../dir-/_foo")).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isValidFilePath()", function() {
|
||||
const isValidFilePath = dirHelpers.isValidFilePath;
|
||||
|
||||
it("should return false for strings that are too short", function() {
|
||||
expect(isValidFilePath("/a")).to.equal(false);
|
||||
expect(isValidFilePath("a.")).to.equal(false);
|
||||
expect(isValidFilePath(".a")).to.equal(false);
|
||||
expect(isValidFilePath("/.")).to.equal(false);
|
||||
});
|
||||
|
||||
it("should return true for arguments that are just filenames", function() {
|
||||
expect(isValidFilePath("test.txt")).to.equal(true);
|
||||
expect(isValidFilePath("123.script")).to.equal(true);
|
||||
expect(isValidFilePath("foo123.b")).to.equal(true);
|
||||
expect(isValidFilePath("my_script.script")).to.equal(true);
|
||||
expect(isValidFilePath("my-script.script")).to.equal(true);
|
||||
expect(isValidFilePath("_foo.lit")).to.equal(true);
|
||||
expect(isValidFilePath("mult.periods.script")).to.equal(true);
|
||||
expect(isValidFilePath("mult.per-iods.again.script")).to.equal(true);
|
||||
});
|
||||
|
||||
it("should return true for valid filepaths", function() {
|
||||
expect(isValidFilePath("/foo/test.txt")).to.equal(true);
|
||||
expect(isValidFilePath("/../123.script")).to.equal(true);
|
||||
expect(isValidFilePath("/./foo123.b")).to.equal(true);
|
||||
expect(isValidFilePath("/dir/my_script.script")).to.equal(true);
|
||||
expect(isValidFilePath("/dir1/dir2/dir3/my-script.script")).to.equal(true);
|
||||
expect(isValidFilePath("/dir1/dir2/././../_foo.lit")).to.equal(true);
|
||||
expect(isValidFilePath("/.dir1/./../.dir2/mult.periods.script")).to.equal(true);
|
||||
expect(isValidFilePath("/_dir/../dir2/mult.per-iods.again.script")).to.equal(true);
|
||||
});
|
||||
|
||||
it("should return false for strings that end with a slash", function() {
|
||||
expect(isValidFilePath("/foo/")).to.equal(false);
|
||||
expect(isValidFilePath("foo.txt/")).to.equal(false);
|
||||
expect(isValidFilePath("/")).to.equal(false);
|
||||
expect(isValidFilePath("/_dir/")).to.equal(false);
|
||||
});
|
||||
|
||||
it("should return false for invalid arguments", function() {
|
||||
expect(isValidFilePath(null)).to.equal(false);
|
||||
expect(isValidFilePath()).to.equal(false);
|
||||
expect(isValidFilePath(5)).to.equal(false);
|
||||
expect(isValidFilePath({})).to.equal(false);
|
||||
})
|
||||
});
|
||||
|
||||
describe("getFirstParentDirectory()", function() {
|
||||
const getFirstParentDirectory = dirHelpers.getFirstParentDirectory;
|
||||
|
||||
it("should return the first parent directory in a filepath", function() {
|
||||
expect(getFirstParentDirectory("/dir1/foo.txt")).to.equal("dir1/");
|
||||
expect(getFirstParentDirectory("/dir1/dir2/dir3/dir4/foo.txt")).to.equal("dir1/");
|
||||
expect(getFirstParentDirectory("/_dir1/dir2/foo.js")).to.equal("_dir1/");
|
||||
});
|
||||
|
||||
it("should return '/' if there is no first parent directory", function() {
|
||||
expect(getFirstParentDirectory("")).to.equal("/");
|
||||
expect(getFirstParentDirectory(" ")).to.equal("/");
|
||||
expect(getFirstParentDirectory("/")).to.equal("/");
|
||||
expect(getFirstParentDirectory("//")).to.equal("/");
|
||||
expect(getFirstParentDirectory("foo.script")).to.equal("/");
|
||||
expect(getFirstParentDirectory("/foo.txt")).to.equal("/");
|
||||
});
|
||||
});
|
||||
|
||||
describe("getAllParentDirectories()", function() {
|
||||
const getAllParentDirectories = dirHelpers.getAllParentDirectories;
|
||||
|
||||
it("should return all parent directories in a filepath", function() {
|
||||
expect(getAllParentDirectories("/")).to.equal("/");
|
||||
expect(getAllParentDirectories("/home/var/foo.txt")).to.equal("/home/var/");
|
||||
expect(getAllParentDirectories("/home/var/")).to.equal("/home/var/");
|
||||
expect(getAllParentDirectories("/home/var/test/")).to.equal("/home/var/test/");
|
||||
});
|
||||
|
||||
it("should return an empty string if there are no parent directories", function() {
|
||||
expect(getAllParentDirectories("foo.txt")).to.equal("");
|
||||
});
|
||||
});
|
||||
|
||||
describe("isInRootDirectory()", function() {
|
||||
const isInRootDirectory = dirHelpers.isInRootDirectory;
|
||||
|
||||
it("should return true for filepaths that refer to a file in the root directory", function() {
|
||||
expect(isInRootDirectory("a.b")).to.equal(true);
|
||||
expect(isInRootDirectory("foo.txt")).to.equal(true);
|
||||
expect(isInRootDirectory("/foo.txt")).to.equal(true);
|
||||
});
|
||||
|
||||
it("should return false for filepaths that refer to a file that's NOT in the root directory", function() {
|
||||
expect(isInRootDirectory("/dir/foo.txt")).to.equal(false);
|
||||
expect(isInRootDirectory("dir/foo.txt")).to.equal(false);
|
||||
expect(isInRootDirectory("/./foo.js")).to.equal(false);
|
||||
expect(isInRootDirectory("../foo.js")).to.equal(false);
|
||||
expect(isInRootDirectory("/dir1/dir2/dir3/foo.txt")).to.equal(false);
|
||||
});
|
||||
|
||||
it("should return false for invalid inputs (inputs that aren't filepaths)", function() {
|
||||
expect(isInRootDirectory(null)).to.equal(false);
|
||||
expect(isInRootDirectory(undefined)).to.equal(false);
|
||||
expect(isInRootDirectory("")).to.equal(false);
|
||||
expect(isInRootDirectory(" ")).to.equal(false);
|
||||
expect(isInRootDirectory("a")).to.equal(false);
|
||||
expect(isInRootDirectory("/dir")).to.equal(false);
|
||||
expect(isInRootDirectory("/dir/")).to.equal(false);
|
||||
expect(isInRootDirectory("/dir/foo")).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("evaluateDirectoryPath()", function() {
|
||||
//const evaluateDirectoryPath = dirHelpers.evaluateDirectoryPath;
|
||||
|
||||
// TODO
|
||||
});
|
||||
|
||||
describe("evaluateFilePath()", function() {
|
||||
//const evaluateFilePath = dirHelpers.evaluateFilePath;
|
||||
|
||||
// TODO
|
||||
})
|
||||
});
|
||||
@@ -1,24 +0,0 @@
|
||||
<html>
|
||||
<!-- NOT CURRENTLY USED. Used to run mocha in browser -->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Mocha Tests</title>
|
||||
<link href="https://unpkg.com/mocha@6.1.4/mocha.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="mocha"></div>
|
||||
|
||||
<script defer src="https://unpkg.com/chai/chai.js"></script>
|
||||
<script defer src="https://unpkg.com/mocha/mocha.js"></script>
|
||||
|
||||
<script type="module" class="mocha-init">
|
||||
mocha.setup('bdd');
|
||||
mocha.checkLeaks();
|
||||
</script>
|
||||
<script type="module" src="test.bundle.js"></script>
|
||||
<script class="mocha-exec" type="module">
|
||||
console.log("Running Tests");
|
||||
mocha.run();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,5 +0,0 @@
|
||||
export * from "./Netscript/DynamicRamCalculationTests";
|
||||
export * from "./Netscript/StaticRamCalculationTests";
|
||||
export * from "./StockMarketTests";
|
||||
export * from "./StringHelperFunctionsTests";
|
||||
export * from "./Terminal/DirectoryTests";
|
||||
@@ -48,6 +48,20 @@ function ScriptLogPopup(props: IProps): React.ReactElement {
|
||||
removeElementById(props.id);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
function closeHandler(event: KeyboardEvent) {
|
||||
if(event.keyCode === 27) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', closeHandler);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('keydown', closeHandler);
|
||||
}
|
||||
}, []);
|
||||
|
||||
function kill(): void {
|
||||
killWorkerScript(props.script, props.script.server, true);
|
||||
close();
|
||||
@@ -55,12 +69,10 @@ function ScriptLogPopup(props: IProps): React.ReactElement {
|
||||
|
||||
function drag(event: React.MouseEvent<HTMLElement, MouseEvent>): void {
|
||||
event.preventDefault();
|
||||
//console.log(props.container.clientWidth);
|
||||
//console.log(props.container.clientHeight);
|
||||
let x = event.clientX;
|
||||
let y = event.clientY;
|
||||
let left = props.container.offsetLeft+props.container.clientWidth/2;
|
||||
let top = props.container.offsetTop+props.container.clientHeight/2;
|
||||
let top = props.container.offsetTop+props.container.clientWidth/5;
|
||||
function mouseMove(event: MouseEvent): void {
|
||||
left+=event.clientX-x;
|
||||
top+=event.clientY-y;
|
||||
@@ -95,7 +107,8 @@ function ScriptLogPopup(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
export function logBoxCreate(script: RunningScript): void {
|
||||
const id = script.filename+script.args.map((x: any): string => `${x}`).join('');
|
||||
const id = script.server+"-"+script.filename+script.args.map((x: any): string => `${x}`).join('-');
|
||||
if(document.getElementById(id) !== null) return;
|
||||
const container = createElement("div", {
|
||||
class: "log-box-container",
|
||||
id: id,
|
||||
|
||||
Reference in New Issue
Block a user