mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-05-06 07:37:56 +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 |
@@ -1,3 +1,4 @@
|
|||||||
|
.vscode
|
||||||
Changelog.txt
|
Changelog.txt
|
||||||
Netburner.txt
|
Netburner.txt
|
||||||
/doc/build
|
/doc/build
|
||||||
@@ -6,3 +7,6 @@ Netburner.txt
|
|||||||
/test/*.map
|
/test/*.map
|
||||||
/test/*.bundle.*
|
/test/*.bundle.*
|
||||||
/test/*.css
|
/test/*.css
|
||||||
|
|
||||||
|
# editor files
|
||||||
|
.vscode
|
||||||
|
|||||||
@@ -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),
|
||||||
|
}
|
||||||
+7
-1
@@ -99,7 +99,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.log-box-log-container {
|
.log-box-log-container {
|
||||||
max-width: 400px;
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +112,13 @@
|
|||||||
background-color: #000;
|
background-color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.log-box-button:hover,
|
||||||
|
.log-box-button:focus {
|
||||||
|
color: var(--my-font-color);
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.dialog-box-content {
|
.dialog-box-content {
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
background-color: var(--my-background-color);
|
background-color: var(--my-background-color);
|
||||||
|
|||||||
Vendored
+2
-2
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -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
|
//# sourceMappingURL=engineStyle.bundle.js.map
|
||||||
Vendored
+6
-1
@@ -2433,7 +2433,6 @@ input[type="checkbox"] {
|
|||||||
justify-content: space-between; }
|
justify-content: space-between; }
|
||||||
|
|
||||||
.log-box-log-container {
|
.log-box-log-container {
|
||||||
max-width: 400px;
|
|
||||||
overflow-y: auto; }
|
overflow-y: auto; }
|
||||||
|
|
||||||
.log-box-button {
|
.log-box-button {
|
||||||
@@ -2445,6 +2444,12 @@ input[type="checkbox"] {
|
|||||||
border: 1px solid #fff;
|
border: 1px solid #fff;
|
||||||
background-color: #000; }
|
background-color: #000; }
|
||||||
|
|
||||||
|
.log-box-button:hover,
|
||||||
|
.log-box-button:focus {
|
||||||
|
color: var(--my-font-color);
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer; }
|
||||||
|
|
||||||
.dialog-box-content {
|
.dialog-box-content {
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
background-color: var(--my-background-color);
|
background-color: var(--my-background-color);
|
||||||
|
|||||||
Vendored
+15
-15
File diff suppressed because one or more lines are too long
@@ -3,6 +3,85 @@
|
|||||||
Changelog
|
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)
|
v0.52.6 - 2021-07-21 Logboxes and VS-code (hydroflame)
|
||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -66,7 +66,7 @@ documentation_title = '{0} Documentation'.format(project)
|
|||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = '0.52'
|
version = '0.52'
|
||||||
# The full version, including alpha/beta/rc tags.
|
# 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
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
|
|||||||
@@ -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
|
:RAM cost: 4 GB
|
||||||
:param string type: Type of action. See :ref:`bladeburner_action_types`
|
:param string type: Type of action. See :ref:`bladeburner_action_types`
|
||||||
:param string name: Name of action. Must be an exact match
|
: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:
|
Example:
|
||||||
|
|
||||||
.. code-block:: javascript
|
.. 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>
|
sleep() <basicfunctions/sleep>
|
||||||
print() <basicfunctions/print>
|
print() <basicfunctions/print>
|
||||||
tprint() <basicfunctions/tprint>
|
tprint() <basicfunctions/tprint>
|
||||||
|
tprintf() <basicfunctions/tprint>
|
||||||
clearLog() <basicfunctions/clearLog>
|
clearLog() <basicfunctions/clearLog>
|
||||||
disableLog() <basicfunctions/disableLog>
|
disableLog() <basicfunctions/disableLog>
|
||||||
enableLog() <basicfunctions/enableLog>
|
enableLog() <basicfunctions/enableLog>
|
||||||
|
|||||||
+12
-2
@@ -581,10 +581,12 @@
|
|||||||
Copy Save data to Clipboard
|
Copy Save data to Clipboard
|
||||||
</button>
|
</button>
|
||||||
<button id="debug-delete-scripts-link" class="a-link-button tooltip">
|
<button id="debug-delete-scripts-link" class="a-link-button tooltip">
|
||||||
Delete all active scripts
|
Force kill all active scripts
|
||||||
<span class="tooltiptextleft">
|
<span class="tooltiptextleft">
|
||||||
Forcefully kill all active running scripts, in case there is a bug or some unexpected issue with the game. After
|
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>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="debug-soft-reset" class="a-link-button tooltip">
|
<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.
|
Perform a soft reset. Resets everything as if you had just purchased an Augmentation.
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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",
|
||||||
|
};
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
import "regenerator-runtime/runtime";
|
||||||
|
global.$ = require("jquery");
|
||||||
Generated
+12233
-2441
File diff suppressed because it is too large
Load Diff
+9
-8
@@ -50,17 +50,18 @@
|
|||||||
"description": "A cyberpunk-themed incremental game",
|
"description": "A cyberpunk-themed incremental game",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.3.4",
|
"@babel/core": "^7.3.4",
|
||||||
|
"@babel/preset-env": "^7.15.0",
|
||||||
"@babel/preset-react": "^7.0.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/lodash": "^4.14.168",
|
||||||
"@types/mocha": "^5.2.7",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^4.22.0",
|
"@typescript-eslint/eslint-plugin": "^4.22.0",
|
||||||
"@typescript-eslint/parser": "^4.22.0",
|
"@typescript-eslint/parser": "^4.22.0",
|
||||||
|
"babel-jest": "^27.0.6",
|
||||||
"babel-loader": "^8.0.5",
|
"babel-loader": "^8.0.5",
|
||||||
"beautify-lint": "^1.0.3",
|
"beautify-lint": "^1.0.3",
|
||||||
"benchmark": "^2.1.1",
|
"benchmark": "^2.1.1",
|
||||||
"bundle-loader": "~0.5.0",
|
"bundle-loader": "~0.5.0",
|
||||||
"chai": "^4.2.0",
|
|
||||||
"css-loader": "^0.28.11",
|
"css-loader": "^0.28.11",
|
||||||
"es6-promise-polyfill": "^1.1.1",
|
"es6-promise-polyfill": "^1.1.1",
|
||||||
"eslint": "^7.24.0",
|
"eslint": "^7.24.0",
|
||||||
@@ -69,6 +70,7 @@
|
|||||||
"html-webpack-plugin": "^3.2.0",
|
"html-webpack-plugin": "^3.2.0",
|
||||||
"i18n-webpack-plugin": "^1.0.0",
|
"i18n-webpack-plugin": "^1.0.0",
|
||||||
"istanbul": "^0.4.5",
|
"istanbul": "^0.4.5",
|
||||||
|
"jest": "^27.0.6",
|
||||||
"js-beautify": "^1.5.10",
|
"js-beautify": "^1.5.10",
|
||||||
"jsdom": "^15.0.0",
|
"jsdom": "^15.0.0",
|
||||||
"jsdom-global": "^3.0.2",
|
"jsdom-global": "^3.0.2",
|
||||||
@@ -78,10 +80,9 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mini-css-extract-plugin": "^0.4.1",
|
"mini-css-extract-plugin": "^0.4.1",
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"mocha": "^6.1.4",
|
|
||||||
"mochapack": "^1.1.1",
|
|
||||||
"null-loader": "^1.0.0",
|
"null-loader": "^1.0.0",
|
||||||
"raw-loader": "~0.5.0",
|
"raw-loader": "~0.5.0",
|
||||||
|
"regenerator-runtime": "^0.13.9",
|
||||||
"sass-loader": "^7.0.3",
|
"sass-loader": "^7.0.3",
|
||||||
"script-loader": "~0.7.0",
|
"script-loader": "~0.7.0",
|
||||||
"should": "^11.1.1",
|
"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: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/*",
|
"lint:style": "stylelint --fix ./css/*",
|
||||||
"preinstall": "node ./scripts/engines-check.js",
|
"preinstall": "node ./scripts/engines-check.js",
|
||||||
"test": "mochapack --webpack-config webpack.config-test.js -r jsdom-global/register ./test/index.js",
|
"test": "jest",
|
||||||
"test:container": "mochapack --webpack-config webpack.config-test.js --slow 2000 --timeout 10000 -r jsdom-global/register ./test/index.js",
|
"test:watch": "jest --watch",
|
||||||
"watch": "webpack --watch --mode production",
|
"watch": "webpack --watch --mode production",
|
||||||
"watch:dev": "webpack --watch --mode development"
|
"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;
|
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
|
* @inst - Bladeburner Object
|
||||||
* @params - options:
|
* @params - options:
|
||||||
|
|||||||
@@ -606,13 +606,11 @@ export class Bladeburner implements IBladeburner {
|
|||||||
const flag = args[1];
|
const flag = args[1];
|
||||||
if (flag.toLowerCase() === "status") {
|
if (flag.toLowerCase() === "status") {
|
||||||
this.postToConsole("Automation: " + (this.automateEnabled ? "enabled" : "disabled"));
|
this.postToConsole("Automation: " + (this.automateEnabled ? "enabled" : "disabled"));
|
||||||
if (this.automateEnabled) {
|
|
||||||
this.postToConsole("When your stamina drops to " + formatNumber(this.automateThreshLow, 0) +
|
this.postToConsole("When your stamina drops to " + formatNumber(this.automateThreshLow, 0) +
|
||||||
", you will automatically switch to " + this.automateActionLow.name +
|
", you will automatically switch to " + this.automateActionLow.name +
|
||||||
". When your stamina recovers to " +
|
". When your stamina recovers to " +
|
||||||
formatNumber(this.automateThreshHigh, 0) + ", you will automatically " +
|
formatNumber(this.automateThreshHigh, 0) + ", you will automatically " +
|
||||||
"switch to " + this.automateActionHigh.name + ".");
|
"switch to " + this.automateActionHigh.name + ".");
|
||||||
}
|
|
||||||
|
|
||||||
} else if (flag.toLowerCase().includes("en")) {
|
} else if (flag.toLowerCase().includes("en")) {
|
||||||
if (!(this.automateActionLow instanceof ActionIdentifier) ||
|
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 errorLogText = `Invalid action: type='${type}' name='${name}'`
|
||||||
const actionId = this.getActionIdFromTypeAndName(type, name);
|
const actionId = this.getActionIdFromTypeAndName(type, name);
|
||||||
if (actionId == null) {
|
if (actionId == null) {
|
||||||
workerScript.log("bladeburner.getActionEstimatedSuccessChance", errorLogText);
|
workerScript.log("bladeburner.getActionEstimatedSuccessChance", errorLogText);
|
||||||
return -1;
|
return [-1, -1];
|
||||||
}
|
}
|
||||||
|
|
||||||
const actionObj = this.getActionObject(actionId);
|
const actionObj = this.getActionObject(actionId);
|
||||||
if (actionObj == null) {
|
if (actionObj == null) {
|
||||||
workerScript.log("bladeburner.getActionEstimatedSuccessChance", errorLogText);
|
workerScript.log("bladeburner.getActionEstimatedSuccessChance", errorLogText);
|
||||||
return -1;
|
return [-1, -1];
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (actionId.type) {
|
switch (actionId.type) {
|
||||||
@@ -1807,16 +1805,17 @@ export class Bladeburner implements IBladeburner {
|
|||||||
case ActionTypes["Operation"]:
|
case ActionTypes["Operation"]:
|
||||||
case ActionTypes["BlackOp"]:
|
case ActionTypes["BlackOp"]:
|
||||||
case ActionTypes["BlackOperation"]:
|
case ActionTypes["BlackOperation"]:
|
||||||
return actionObj.getSuccessChance(this, {est:true});
|
return actionObj.getEstSuccessChance(this);
|
||||||
case ActionTypes["Training"]:
|
case ActionTypes["Training"]:
|
||||||
case ActionTypes["Field Analysis"]:
|
case ActionTypes["Field Analysis"]:
|
||||||
case ActionTypes["FieldAnalysis"]:
|
case ActionTypes["FieldAnalysis"]:
|
||||||
return 1;
|
return [1, 1];
|
||||||
case ActionTypes["Recruitment"]:
|
case ActionTypes["Recruitment"]:
|
||||||
return this.getRecruitmentSuccessChance(player);
|
const recChance = this.getRecruitmentSuccessChance(player);
|
||||||
|
return [recChance, recChance];
|
||||||
default:
|
default:
|
||||||
workerScript.log("bladeburner.getActionEstimatedSuccessChance", errorLogText);
|
workerScript.log("bladeburner.getActionEstimatedSuccessChance", errorLogText);
|
||||||
return -1;
|
return [-1, -1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ export interface IAction {
|
|||||||
getActionTypeSkillSuccessBonus(inst: IBladeburner): number;
|
getActionTypeSkillSuccessBonus(inst: IBladeburner): number;
|
||||||
getChaosCompetencePenalty(inst: IBladeburner, params: ISuccessChanceParams): number;
|
getChaosCompetencePenalty(inst: IBladeburner, params: ISuccessChanceParams): number;
|
||||||
getChaosDifficultyBonus(inst: IBladeburner): number;
|
getChaosDifficultyBonus(inst: IBladeburner): number;
|
||||||
|
getEstSuccessChance(inst: IBladeburner): number[];
|
||||||
getSuccessChance(inst: IBladeburner, params: ISuccessChanceParams): number;
|
getSuccessChance(inst: IBladeburner, params: ISuccessChanceParams): number;
|
||||||
getSuccessesNeededForNextLevel(baseSuccessesPerLevel: number): number;
|
getSuccessesNeededForNextLevel(baseSuccessesPerLevel: number): number;
|
||||||
setMaxLevel(baseSuccessesPerLevel: number): void;
|
setMaxLevel(baseSuccessesPerLevel: number): void;
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export interface IBladeburner {
|
|||||||
getSkillNamesNetscriptFn(): string[];
|
getSkillNamesNetscriptFn(): string[];
|
||||||
startActionNetscriptFn(player: IPlayer, type: string, name: string, workerScript: WorkerScript): boolean;
|
startActionNetscriptFn(player: IPlayer, type: string, name: string, workerScript: WorkerScript): boolean;
|
||||||
getActionTimeNetscriptFn(player: IPlayer, type: string, name: string, workerScript: WorkerScript): number;
|
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;
|
getActionCountRemainingNetscriptFn(type: string, name: string, workerScript: WorkerScript): number;
|
||||||
getSkillLevelNetscriptFn(skillName: string, workerScript: WorkerScript): number;
|
getSkillLevelNetscriptFn(skillName: string, workerScript: WorkerScript): number;
|
||||||
getSkillUpgradeCostNetscriptFn(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 === 'BlackOps' && <BlackOpPage bladeburner={props.bladeburner} player={props.player} />}
|
||||||
{page === 'Skills' && <SkillPage bladeburner={props.bladeburner} />}
|
{page === 'Skills' && <SkillPage bladeburner={props.bladeburner} />}
|
||||||
</div>
|
</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 { TeamSizePopup } from "./TeamSizePopup";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import { SuccessChance } from "./SuccessChance";
|
||||||
|
import { CopyableText } from "../../ui/React/CopyableText";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
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 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 actionTime = props.action.getActionTime(props.bladeburner);
|
||||||
const hasReqdRank = props.bladeburner.rank >= props.action.reqdRank;
|
const hasReqdRank = props.bladeburner.rank >= props.action.reqdRank;
|
||||||
const computedActionTimeCurrent = Math.min(props.bladeburner.actionTimeCurrent+props.bladeburner.actionTimeOverflow, props.bladeburner.actionTimeToComplete);
|
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 (<>
|
return (<>
|
||||||
<h2 style={{display: 'inline-block'}}>
|
<h2 style={{display: 'inline-block'}}>
|
||||||
{isActive ?
|
{isActive ?
|
||||||
<>{props.action.name} (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
|
<><CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
|
||||||
<>{props.action.name}</>
|
<CopyableText value={props.action.name} />
|
||||||
}
|
}
|
||||||
</h2>
|
</h2>
|
||||||
{isActive ?
|
{isActive ?
|
||||||
@@ -78,10 +80,10 @@ export function BlackOpElem(props: IProps): React.ReactElement {
|
|||||||
Required Rank: {formatNumber(props.action.reqdRank, 0)}
|
Required Rank: {formatNumber(props.action.reqdRank, 0)}
|
||||||
</p>
|
</p>
|
||||||
<br />
|
<br />
|
||||||
<p style={{display:"inline-block"}}>
|
<pre style={{display:"inline-block"}}>
|
||||||
Estimated Success Chance: {formatNumber(estimatedSuccessChance*100, 1)}% {props.action.isStealth?stealthIcon:<></>}{props.action.isKill?killIcon:<></>}
|
Estimated Success Chance: <SuccessChance chance={estimatedSuccessChance} /> {props.action.isStealth?stealthIcon:<></>}{props.action.isKill?killIcon:<></>}
|
||||||
<br />
|
<br />
|
||||||
Time Required: {convertTimeMsToTimeElapsedString(actionTime*1000)}
|
Time Required: {convertTimeMsToTimeElapsedString(actionTime*1000)}
|
||||||
</p>
|
</pre>
|
||||||
</>);
|
</>);
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ import * as React from "react";
|
|||||||
import { BlackOpList } from "./BlackOpList";
|
import { BlackOpList } from "./BlackOpList";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import { CopyableText } from "../../ui/React/CopyableText";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
|
|||||||
@@ -26,18 +26,8 @@ export function Console(props: IProps): React.ReactElement {
|
|||||||
|
|
||||||
// TODO: Figure out how to actually make the scrolling work correctly.
|
// TODO: Figure out how to actually make the scrolling work correctly.
|
||||||
function scrollToBottom(): void {
|
function scrollToBottom(): void {
|
||||||
function isMaxed(): boolean {
|
if(!lastRef.current) return;
|
||||||
if(!lastRef.current) return false;
|
|
||||||
const oldTop = lastRef.current.scrollTop;
|
|
||||||
lastRef.current.scrollTop = lastRef.current.scrollHeight;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function rerender(): void {
|
function rerender(): void {
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import { stealthIcon, killIcon } from "../data/Icons";
|
|||||||
import { BladeburnerConstants } from "../data/Constants";
|
import { BladeburnerConstants } from "../data/Constants";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import { SuccessChance } from "./SuccessChance";
|
||||||
|
import { CopyableText } from "../../ui/React/CopyableText";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
@@ -19,7 +21,8 @@ interface IProps {
|
|||||||
export function ContractElem(props: IProps): React.ReactElement {
|
export function ContractElem(props: IProps): React.ReactElement {
|
||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
const isActive = props.bladeburner.action.type === ActionTypes["Contract"] && props.action.name === props.bladeburner.action.name;
|
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 computedActionTimeCurrent = Math.min(props.bladeburner.actionTimeCurrent+props.bladeburner.actionTimeOverflow, props.bladeburner.actionTimeToComplete);
|
||||||
const maxLevel = (props.action.level >= props.action.maxLevel);
|
const maxLevel = (props.action.level >= props.action.maxLevel);
|
||||||
const actionTime = props.action.getActionTime(props.bladeburner);
|
const actionTime = props.action.getActionTime(props.bladeburner);
|
||||||
@@ -52,8 +55,8 @@ export function ContractElem(props: IProps): React.ReactElement {
|
|||||||
return (<>
|
return (<>
|
||||||
<h2 style={{display: 'inline-block'}}>
|
<h2 style={{display: 'inline-block'}}>
|
||||||
{isActive ?
|
{isActive ?
|
||||||
<>{props.action.name} (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
|
<><CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
|
||||||
<>{props.action.name}</>
|
<CopyableText value={props.action.name} />
|
||||||
}
|
}
|
||||||
</h2>
|
</h2>
|
||||||
{isActive ?
|
{isActive ?
|
||||||
@@ -93,7 +96,7 @@ export function ContractElem(props: IProps): React.ReactElement {
|
|||||||
<pre style={{display: 'inline-block'}}>
|
<pre style={{display: 'inline-block'}}>
|
||||||
<span dangerouslySetInnerHTML={{__html: props.action.desc}} />
|
<span dangerouslySetInnerHTML={{__html: props.action.desc}} />
|
||||||
<br /><br />
|
<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 />
|
Time Required: {convertTimeMsToTimeElapsedString(actionTime*1000)}<br />
|
||||||
Contracts remaining: {Math.floor(props.action.count)}<br />
|
Contracts remaining: {Math.floor(props.action.count)}<br />
|
||||||
Successes: {props.action.successes}<br />
|
Successes: {props.action.successes}<br />
|
||||||
@@ -114,26 +117,3 @@ Failures: {props.action.failures}
|
|||||||
onChange={onAutolevel}/>
|
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 React, { useState } from "react";
|
||||||
import { ActionTypes } from "../data/ActionTypes";
|
import { ActionTypes } from "../data/ActionTypes";
|
||||||
import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
|
import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
|
||||||
import { formatNumber } from "../../../utils/StringHelperFunctions";
|
import {
|
||||||
|
formatNumber,
|
||||||
|
convertTimeMsToTimeElapsedString,
|
||||||
|
} from "../../../utils/StringHelperFunctions";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import { CopyableText } from "../../ui/React/CopyableText";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
@@ -15,6 +19,20 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
|
|||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
const isActive = props.action.name === props.bladeburner.action.name;
|
const isActive = props.action.name === props.bladeburner.action.name;
|
||||||
const computedActionTimeCurrent = Math.min(props.bladeburner.actionTimeCurrent+props.bladeburner.actionTimeOverflow, props.bladeburner.actionTimeToComplete);
|
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 {
|
function onStart(): void {
|
||||||
props.bladeburner.action.type = ActionTypes[(props.action.name as string)];
|
props.bladeburner.action.type = ActionTypes[(props.action.name as string)];
|
||||||
@@ -26,8 +44,8 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
|
|||||||
return (<>
|
return (<>
|
||||||
<h2 style={{display: 'inline-block'}}>
|
<h2 style={{display: 'inline-block'}}>
|
||||||
{isActive ?
|
{isActive ?
|
||||||
<>{props.action.name} (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
|
<><CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
|
||||||
<>{props.action.name}</>
|
<CopyableText value={props.action.name} />
|
||||||
}
|
}
|
||||||
</h2>
|
</h2>
|
||||||
{isActive ?
|
{isActive ?
|
||||||
@@ -42,6 +60,10 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
|
|||||||
</>}
|
</>}
|
||||||
<br />
|
<br />
|
||||||
<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 { TeamSizePopup } from "./TeamSizePopup";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import { SuccessChance } from "./SuccessChance";
|
||||||
|
import { CopyableText } from "../../ui/React/CopyableText";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
@@ -21,7 +23,7 @@ interface IProps {
|
|||||||
export function OperationElem(props: IProps): React.ReactElement {
|
export function OperationElem(props: IProps): React.ReactElement {
|
||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
const isActive = props.bladeburner.action.type === ActionTypes["Operation"] && props.action.name === props.bladeburner.action.name;
|
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 computedActionTimeCurrent = Math.min(props.bladeburner.actionTimeCurrent+props.bladeburner.actionTimeOverflow,props.bladeburner.actionTimeToComplete);
|
||||||
const maxLevel = (props.action.level >= props.action.maxLevel);
|
const maxLevel = (props.action.level >= props.action.maxLevel);
|
||||||
const actionTime = props.action.getActionTime(props.bladeburner);
|
const actionTime = props.action.getActionTime(props.bladeburner);
|
||||||
@@ -63,8 +65,8 @@ export function OperationElem(props: IProps): React.ReactElement {
|
|||||||
return (<>
|
return (<>
|
||||||
<h2 style={{display: 'inline-block'}}>
|
<h2 style={{display: 'inline-block'}}>
|
||||||
{isActive ?
|
{isActive ?
|
||||||
<>{props.action.name} (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
|
<><CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
|
||||||
<>{props.action.name}</>
|
<CopyableText value={props.action.name} />
|
||||||
}
|
}
|
||||||
</h2>
|
</h2>
|
||||||
{isActive ?
|
{isActive ?
|
||||||
@@ -110,7 +112,7 @@ export function OperationElem(props: IProps): React.ReactElement {
|
|||||||
<pre style={{display:"inline-block"}}>
|
<pre style={{display:"inline-block"}}>
|
||||||
<span dangerouslySetInnerHTML={{__html: props.action.desc}} />
|
<span dangerouslySetInnerHTML={{__html: props.action.desc}} />
|
||||||
<br /><br />
|
<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 />
|
Time Required: {convertTimeMsToTimeElapsedString(actionTime*1000)}<br />
|
||||||
Operations remaining: {Math.floor(props.action.count)}<br />
|
Operations remaining: {Math.floor(props.action.count)}<br />
|
||||||
Successes: {props.action.successes}<br />
|
Successes: {props.action.successes}<br />
|
||||||
|
|||||||
@@ -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 {
|
componentDidMount(): void {
|
||||||
this.interval = setInterval(this.step, 50);
|
this.interval = window.setInterval(this.step, 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
step(): void {
|
step(): void {
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export class SlotMachine extends Game<IProps, IState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
this.interval = setInterval(this.step, 50);
|
this.interval = window.setInterval(this.step, 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
step(): void {
|
step(): void {
|
||||||
|
|||||||
+27
-13
@@ -6,7 +6,7 @@
|
|||||||
import { IMap } from "./types";
|
import { IMap } from "./types";
|
||||||
|
|
||||||
export const CONSTANTS: IMap<any> = {
|
export const CONSTANTS: IMap<any> = {
|
||||||
Version: "0.52.6",
|
Version: "0.52.9",
|
||||||
|
|
||||||
// Speed (in ms) at which the main loop is updated
|
// Speed (in ms) at which the main loop is updated
|
||||||
_idleSpeed: 200,
|
_idleSpeed: 200,
|
||||||
@@ -228,26 +228,40 @@ export const CONSTANTS: IMap<any> = {
|
|||||||
TotalNumBitNodes: 24,
|
TotalNumBitNodes: 24,
|
||||||
|
|
||||||
LatestUpdate: `
|
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
|
* Now less laggy, has pagination.
|
||||||
vs-code). The options are a bit lackluster but more will be added as
|
|
||||||
feedback comes.
|
|
||||||
|
|
||||||
** Log boxes **
|
** File diagnostic **
|
||||||
|
|
||||||
* Multiple log boxes can be opened at once. They can be moved around the
|
* Added a popup found under options that shows the files you own and how
|
||||||
screen. (but the movement behavior is a bit weird.)
|
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. **
|
** Misc. **
|
||||||
|
|
||||||
* Job promotion now correctly updates the UI.
|
* Infiltration kills you if you try to automate it. (@threehams)
|
||||||
* Milestones now call the faction CyberSec instead of CSEC
|
* Fix beautify button not working
|
||||||
* Can no longer create file that break the filesystem.
|
* Added bladeburner_analysis_mult to getPlayer() (@brusby)
|
||||||
* Remove dollar sign in blade contract UI element
|
* Fixed joining bladeburner via netscript functions. (@omuretsu)
|
||||||
|
* All bladeburner actions are click-to-copy
|
||||||
* nerf noodle bar
|
* nerf noodle bar
|
||||||
`,
|
`,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
@@ -119,7 +119,7 @@ function Industry(params={}) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.name = params.name ? params.name : 0;
|
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.sciResearch = new Material({name: "Scientific Research"});
|
||||||
this.researched = {}; // Object of acquired Research. Keys = research name
|
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 mgmtProd = office.employeeProd[EmployeePositions.Management];
|
||||||
const opProd = office.employeeProd[EmployeePositions.Operations];
|
const opProd = office.employeeProd[EmployeePositions.Operations];
|
||||||
const total = engrProd + mgmtProd + opProd;
|
const total = engrProd + mgmtProd + opProd;
|
||||||
|
|
||||||
if (total <= 0) { break; }
|
if (total <= 0) { break; }
|
||||||
|
|
||||||
// Management is a multiplier for the production from Engineers
|
// 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) {
|
} else if (product.marketTa1) {
|
||||||
sCost = product.pCost + markupLimit;
|
sCost = product.pCost + markupLimit;
|
||||||
} else if (isString(product.sCost)) {
|
} 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 = product.sCost.replace(/MP/g, product.pCost + product.rat / product.mku);
|
||||||
sCost = eval(sCost);
|
sCost = eval(sCost);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
sCost = product.sCost;
|
sCost = product.sCost;
|
||||||
}
|
}
|
||||||
@@ -1397,11 +1401,12 @@ Industry.prototype.createResearchBox = function() {
|
|||||||
researchTree.research(allResearch[i]);
|
researchTree.research(allResearch[i]);
|
||||||
this.researched[allResearch[i]] = true;
|
this.researched[allResearch[i]] = true;
|
||||||
|
|
||||||
|
const researchBox = this.createResearchBox();
|
||||||
dialogBoxCreate(`Researched ${allResearch[i]}. It may take a market cycle ` +
|
dialogBoxCreate(`Researched ${allResearch[i]}. It may take a market cycle ` +
|
||||||
`(~${SecsPerMarketCycle} seconds) before the effects of ` +
|
`(~${SecsPerMarketCycle} seconds) before the effects of ` +
|
||||||
`the Research apply.`);
|
`the Research apply.`);
|
||||||
|
|
||||||
return this.createResearchBox();
|
return researchBox;
|
||||||
} else {
|
} else {
|
||||||
dialogBoxCreate(`You do not have enough Scientific Research for ${research.name}`);
|
dialogBoxCreate(`You do not have enough Scientific Research for ${research.name}`);
|
||||||
}
|
}
|
||||||
@@ -1941,6 +1946,14 @@ function Corporation(params={}) {
|
|||||||
this.state = new CorporationState();
|
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() {
|
Corporation.prototype.getState = function() {
|
||||||
return this.state.getState();
|
return this.state.getState();
|
||||||
}
|
}
|
||||||
@@ -1980,7 +1993,7 @@ Corporation.prototype.process = function() {
|
|||||||
});
|
});
|
||||||
var profit = this.revenue.minus(this.expenses);
|
var profit = this.revenue.minus(this.expenses);
|
||||||
const cycleProfit = profit.times(marketCycles * SecsPerMarketCycle);
|
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. " +
|
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>" +
|
"This is a bug. Please report to game developer.<br><br>" +
|
||||||
"(Your funds have been set to $150b for the inconvenience)");
|
"(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));
|
const profit = this.numShares * dividendsPerShare * (1 - (this.dividendTaxPercentage / 100));
|
||||||
Player.gainMoney(profit);
|
Player.gainMoney(profit);
|
||||||
Player.recordMoneySource(profit, "corporation");
|
Player.recordMoneySource(profit, "corporation");
|
||||||
this.funds = this.funds.plus(retainedEarnings);
|
this.addFunds(retainedEarnings);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.funds = this.funds.plus(cycleProfit);
|
this.addFunds(cycleProfit);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateSharePrice();
|
this.updateSharePrice();
|
||||||
@@ -2069,7 +2082,7 @@ Corporation.prototype.getInvestment = function() {
|
|||||||
noBtn.innerHML = "Reject";
|
noBtn.innerHML = "Reject";
|
||||||
yesBtn.addEventListener("click", () => {
|
yesBtn.addEventListener("click", () => {
|
||||||
++this.fundingRound;
|
++this.fundingRound;
|
||||||
this.funds = this.funds.plus(funding);
|
this.addFunds(funding);
|
||||||
this.numShares -= investShares;
|
this.numShares -= investShares;
|
||||||
this.rerender();
|
this.rerender();
|
||||||
return yesNoBoxClose();
|
return yesNoBoxClose();
|
||||||
@@ -2123,7 +2136,7 @@ Corporation.prototype.goPublic = function() {
|
|||||||
this.sharePrice = initialSharePrice;
|
this.sharePrice = initialSharePrice;
|
||||||
this.issuedShares = numShares;
|
this.issuedShares = numShares;
|
||||||
this.numShares -= numShares;
|
this.numShares -= numShares;
|
||||||
this.funds = this.funds.plus(numShares * initialSharePrice);
|
this.addFunds(numShares * initialSharePrice);
|
||||||
this.rerender();
|
this.rerender();
|
||||||
removeElementById(goPublicPopupId);
|
removeElementById(goPublicPopupId);
|
||||||
dialogBoxCreate(`You took your ${this.name} public and earned ` +
|
dialogBoxCreate(`You took your ${this.name} public and earned ` +
|
||||||
|
|||||||
@@ -187,6 +187,11 @@ export class Product {
|
|||||||
this.calculateRating(industry);
|
this.calculateRating(industry);
|
||||||
const advMult = 1 + (Math.pow(this.advCost, 0.1) / 100);
|
const advMult = 1 + (Math.pow(this.advCost, 0.1) / 100);
|
||||||
this.mku = 100 / (advMult * Math.pow((this.qlt + 0.001), 0.65) * (busRatio + mgmtRatio));
|
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.dmd = industry.awareness === 0 ? 20 : Math.min(100, advMult * (100 * (industry.popularity / industry.awareness)));
|
||||||
this.cmp = getRandomInt(0, 70);
|
this.cmp = getRandomInt(0, 70);
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -502,7 +502,7 @@ class DevMenuComponent extends Component {
|
|||||||
modifyBladeburnerRank(modify) {
|
modifyBladeburnerRank(modify) {
|
||||||
return function(rank) {
|
return function(rank) {
|
||||||
if (Player.bladeburner) {
|
if (Player.bladeburner) {
|
||||||
Player.bladeburner.changeRank(rank*modify);
|
Player.bladeburner.changeRank(Player, rank*modify);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -514,7 +514,7 @@ class DevMenuComponent extends Component {
|
|||||||
|
|
||||||
addTonsBladeburnerRank() {
|
addTonsBladeburnerRank() {
|
||||||
if (Player.bladeburner) {
|
if (Player.bladeburner) {
|
||||||
Player.bladeburner.changeRank(tonsP);
|
Player.bladeburner.changeRank(Player, tonsP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
selectedServer: "foodnstuff",
|
selectedServer: "ecorp",
|
||||||
}
|
}
|
||||||
|
|
||||||
this.changeTargetServer = this.changeTargetServer.bind(this);
|
this.changeTargetServer = this.changeTargetServer.bind(this);
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export function BackwardGame(props: IMinigameProps): React.ReactElement {
|
|||||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<h1 className={"noselect"}>Type it backward</h1>
|
<h1 className={"noselect"}>Type it backward</h1>
|
||||||
<KeyHandler onKeyDown={press} />
|
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={6}>
|
<Grid item xs={6}>
|
||||||
<p style={{transform: 'scaleX(-1)'}}>{answer}</p>
|
<p style={{transform: 'scaleX(-1)'}}>{answer}</p>
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export function BracketGame(props: IMinigameProps): React.ReactElement {
|
|||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<h1 className={"noselect"}>Close the brackets</h1>
|
<h1 className={"noselect"}>Close the brackets</h1>
|
||||||
<p style={{fontSize: '5em'}}>{`${left}${right}`}<BlinkingCursor /></p>
|
<p style={{fontSize: '5em'}}>{`${left}${right}`}<BlinkingCursor /></p>
|
||||||
<KeyHandler onKeyDown={press} />
|
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>)
|
</Grid>)
|
||||||
}
|
}
|
||||||
@@ -51,7 +51,7 @@ export function BribeGame(props: IMinigameProps): React.ReactElement {
|
|||||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<h1>Say something nice about the guard.</h1>
|
<h1>Say something nice about the guard.</h1>
|
||||||
<KeyHandler onKeyDown={press} />
|
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={6}>
|
<Grid item xs={6}>
|
||||||
<h2 style={{fontSize: "2em"}}>↑</h2>
|
<h2 style={{fontSize: "2em"}}>↑</h2>
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export function CheatCodeGame(props: IMinigameProps): React.ReactElement {
|
|||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<h1 className={"noselect"}>Enter the Code!</h1>
|
<h1 className={"noselect"}>Enter the Code!</h1>
|
||||||
<p style={{fontSize: '5em'}}>{code[index]}</p>
|
<p style={{fontSize: '5em'}}>{code[index]}</p>
|
||||||
<KeyHandler onKeyDown={press} />
|
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||||
</Grid>
|
</Grid>
|
||||||
</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, color: 'blue'}}>{cell} </span>
|
||||||
return <span key={`${x}${y}`} style={{fontSize: fontSize}}>{cell} </span>
|
return <span key={`${x}${y}`} style={{fontSize: fontSize}}>{cell} </span>
|
||||||
})}</pre><br /></div>)}
|
})}</pre><br /></div>)}
|
||||||
<KeyHandler onKeyDown={press} />
|
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||||
</Grid>
|
</Grid>
|
||||||
</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);
|
setStage(Stage.Countdown);
|
||||||
pushResult(false);
|
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");
|
const menu = document.getElementById("mainmenu-container");
|
||||||
if(menu === null) throw new Error("mainmenu-container not found");
|
if(menu === null) throw new Error("mainmenu-container not found");
|
||||||
menu.style.visibility = "visible";
|
menu.style.visibility = "visible";
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
export interface IMinigameProps {
|
export interface IMinigameProps {
|
||||||
onSuccess: () => void;
|
onSuccess: () => void;
|
||||||
onFailure: () => void;
|
onFailure: (options?: {
|
||||||
|
/** Failed due to using untrusted events (automation) */
|
||||||
|
automated: boolean;
|
||||||
|
}) => void;
|
||||||
difficulty: number;
|
difficulty: number;
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ import React, { useEffect } from 'react';
|
|||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
onKeyDown: (event: React.KeyboardEvent<HTMLElement>) => void;
|
onKeyDown: (event: React.KeyboardEvent<HTMLElement>) => void;
|
||||||
|
onFailure: (options?: { automated: boolean }) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function KeyHandler(props: IProps): React.ReactElement {
|
export function KeyHandler(props: IProps): React.ReactElement {
|
||||||
@@ -9,7 +10,12 @@ export function KeyHandler(props: IProps): React.ReactElement {
|
|||||||
useEffect(() => elem.focus());
|
useEffect(() => elem.focus());
|
||||||
|
|
||||||
function onKeyDown(event: React.KeyboardEvent<HTMLElement>): void {
|
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);
|
props.onKeyDown(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ export function MinesweeperGame(props: IMinigameProps): React.ReactElement {
|
|||||||
return <span key={x}>[ ] </span>
|
return <span key={x}>[ ] </span>
|
||||||
}
|
}
|
||||||
})}</pre><br /></div>)}
|
})}</pre><br /></div>)}
|
||||||
<KeyHandler onKeyDown={press} />
|
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>)
|
</Grid>)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ export function SlashGame(props: IMinigameProps): React.ReactElement {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let id2 = -1;
|
let id2 = -1;
|
||||||
const id = setTimeout(() => {
|
const id = window.setTimeout(() => {
|
||||||
setGuarding(false);
|
setGuarding(false);
|
||||||
id2 = setTimeout(()=>setGuarding(true), difficulty.window)
|
id2 = window.setTimeout(()=>setGuarding(true), difficulty.window)
|
||||||
}, Math.random()*3250+1500);
|
}, Math.random()*3250+1500);
|
||||||
return () => {
|
return () => {
|
||||||
clearInterval(id);
|
clearInterval(id);
|
||||||
@@ -54,7 +54,7 @@ export function SlashGame(props: IMinigameProps): React.ReactElement {
|
|||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<h1 className={"noselect"}>Slash when his guard is down!</h1>
|
<h1 className={"noselect"}>Slash when his guard is down!</h1>
|
||||||
<p style={{fontSize: '5em'}}>{guarding ? "!Guarding!" : "!ATTACKING!"}</p>
|
<p style={{fontSize: '5em'}}>{guarding ? "!Guarding!" : "!ATTACKING!"}</p>
|
||||||
<KeyHandler onKeyDown={press} />
|
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>)
|
</Grid>)
|
||||||
}
|
}
|
||||||
@@ -111,7 +111,7 @@ export function WireCuttingGame(props: IMinigameProps): React.ReactElement {
|
|||||||
})}
|
})}
|
||||||
</pre>
|
</pre>
|
||||||
</div>)}
|
</div>)}
|
||||||
<KeyHandler onKeyDown={press} />
|
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>)
|
</Grid>)
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+3
@@ -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)}`);
|
post(`${workerScript.scriptRef.filename}: ${argsToString(arguments)}`);
|
||||||
},
|
},
|
||||||
|
tprintf: function(format, ...args) {
|
||||||
|
post(vsprintf(format, args));
|
||||||
|
},
|
||||||
clearLog: function() {
|
clearLog: function() {
|
||||||
workerScript.scriptRef.clearLog();
|
workerScript.scriptRef.clearLog();
|
||||||
},
|
},
|
||||||
@@ -2149,11 +2152,15 @@ function NetscriptFunctions(workerScript) {
|
|||||||
}
|
}
|
||||||
return port.write(data);
|
return port.write(data);
|
||||||
} else if (isString(port)) { // Write to script or text file
|
} else if (isString(port)) { // Write to script or text file
|
||||||
const fn = removeLeadingSlash(port);
|
let fn = port;
|
||||||
if (!isValidFilePath(fn)) {
|
if (!isValidFilePath(fn)) {
|
||||||
throw makeRuntimeErrorMsg("write", `Invalid filepath: ${fn}`);
|
throw makeRuntimeErrorMsg("write", `Invalid filepath: ${fn}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(fn.lastIndexOf("/") === 0) {
|
||||||
|
fn = removeLeadingSlash(fn);
|
||||||
|
}
|
||||||
|
|
||||||
// Coerce 'data' to be a string
|
// Coerce 'data' to be a string
|
||||||
try {
|
try {
|
||||||
data = String(data);
|
data = String(data);
|
||||||
@@ -3025,6 +3032,7 @@ function NetscriptFunctions(workerScript) {
|
|||||||
has4SDataTixApi: Player.has4SDataTixApi,
|
has4SDataTixApi: Player.has4SDataTixApi,
|
||||||
bladeburner_max_stamina_mult: Player.bladeburner_max_stamina_mult,
|
bladeburner_max_stamina_mult: Player.bladeburner_max_stamina_mult,
|
||||||
bladeburner_stamina_gain_mult: Player.bladeburner_stamina_gain_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,
|
bladeburner_success_chance_mult: Player.bladeburner_success_chance_mult,
|
||||||
bitNodeN: Player.bitNodeN,
|
bitNodeN: Player.bitNodeN,
|
||||||
totalPlaytime: Player.totalPlaytime,
|
totalPlaytime: Player.totalPlaytime,
|
||||||
@@ -4061,7 +4069,7 @@ function NetscriptFunctions(workerScript) {
|
|||||||
return true; // Already member
|
return true; // Already member
|
||||||
} else if (Player.strength >= 100 && Player.defense >= 100 &&
|
} else if (Player.strength >= 100 && Player.defense >= 100 &&
|
||||||
Player.dexterity >= 100 && Player.agility >= 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");
|
workerScript.log("joinBladeburnerDivision", "You have been accepted into the Bladeburner division");
|
||||||
|
|
||||||
const worldHeader = document.getElementById("world-menu-header");
|
const worldHeader = document.getElementById("world-menu-header");
|
||||||
|
|||||||
@@ -82,7 +82,6 @@ BitburnerSaveObject.prototype.getSaveString = function() {
|
|||||||
this.GlobalAliasesSave = JSON.stringify(GlobalAliases);
|
this.GlobalAliasesSave = JSON.stringify(GlobalAliases);
|
||||||
this.MessagesSave = JSON.stringify(Messages);
|
this.MessagesSave = JSON.stringify(Messages);
|
||||||
this.StockMarketSave = JSON.stringify(StockMarket);
|
this.StockMarketSave = JSON.stringify(StockMarket);
|
||||||
console.log(JSON.stringify(Settings));
|
|
||||||
this.SettingsSave = JSON.stringify(Settings);
|
this.SettingsSave = JSON.stringify(Settings);
|
||||||
this.FconfSettingsSave = JSON.stringify(FconfSettings);
|
this.FconfSettingsSave = JSON.stringify(FconfSettings);
|
||||||
this.VersionSave = JSON.stringify(CONSTANTS.Version);
|
this.VersionSave = JSON.stringify(CONSTANTS.Version);
|
||||||
|
|||||||
@@ -28,19 +28,6 @@ import { dialogBoxCreate } from "../../utils/DialogBox";
|
|||||||
import { compareArrays } from "../../utils/helpers/compareArrays";
|
import { compareArrays } from "../../utils/helpers/compareArrays";
|
||||||
import { createElement } from "../../utils/uiHelpers/createElement";
|
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) {
|
export function scriptCalculateOfflineProduction(runningScriptObj) {
|
||||||
//The Player object stores the last update time from when we were online
|
//The Player object stores the last update time from when we were online
|
||||||
const thisUpdate = new Date().getTime();
|
const thisUpdate = new Date().getTime();
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ export class PositionTracker {
|
|||||||
const position = this.positions.get(filename);
|
const position = this.positions.get(filename);
|
||||||
if (!position) {
|
if (!position) {
|
||||||
return {
|
return {
|
||||||
row: 0,
|
row: -1,
|
||||||
column: 0,
|
column: -1,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return position;
|
return position;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef } from 'react';
|
|||||||
import { StdButton } from "../../ui/React/StdButton";
|
import { StdButton } from "../../ui/React/StdButton";
|
||||||
import Editor from "@monaco-editor/react";
|
import Editor from "@monaco-editor/react";
|
||||||
import * as monaco from "monaco-editor";
|
import * as monaco from "monaco-editor";
|
||||||
import IStandaloneCodeEditor = monaco.editor.IStandaloneCodeEditor;
|
type IStandaloneCodeEditor = monaco.editor.IStandaloneCodeEditor;
|
||||||
import { createPopup } from "../../ui/React/createPopup";
|
import { createPopup } from "../../ui/React/createPopup";
|
||||||
import { OptionsPopup } from "./OptionsPopup";
|
import { OptionsPopup } from "./OptionsPopup";
|
||||||
import { Options } from "./Options";
|
import { Options } from "./Options";
|
||||||
@@ -23,6 +23,12 @@ import { libSource } from "../NetscriptDefinitions";
|
|||||||
import { NetscriptFunctions } from "../../NetscriptFunctions";
|
import { NetscriptFunctions } from "../../NetscriptFunctions";
|
||||||
import { WorkerScript } from "../../Netscript/WorkerScript";
|
import { WorkerScript } from "../../Netscript/WorkerScript";
|
||||||
import { Settings } from "../../Settings/Settings";
|
import { Settings } from "../../Settings/Settings";
|
||||||
|
import { GetServerByHostname } from "../../Server/ServerHelpers";
|
||||||
|
import {
|
||||||
|
iTutorialNextStep,
|
||||||
|
ITutorial,
|
||||||
|
iTutorialSteps,
|
||||||
|
} from "../../InteractiveTutorial";
|
||||||
|
|
||||||
let symbols: string[] = [];
|
let symbols: string[] = [];
|
||||||
(function() {
|
(function() {
|
||||||
@@ -50,7 +56,12 @@ interface IProps {
|
|||||||
code: string;
|
code: string;
|
||||||
player: IPlayer;
|
player: IPlayer;
|
||||||
engine: IEngine;
|
engine: IEngine;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
// How to load function definition in monaco
|
// How to load function definition in monaco
|
||||||
// https://github.com/Microsoft/monaco-editor/issues/1415
|
// 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://microsoft.github.io/monaco-editor/playground.html#extending-language-services-custom-languages
|
||||||
// https://github.com/threehams/typescript-error-guide/blob/master/stories/components/Editor.tsx#L11-L39
|
// https://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 {
|
export function Root(props: IProps): React.ReactElement {
|
||||||
const editorRef = useRef<IStandaloneCodeEditor | null>(null);
|
const editorRef = useRef<IStandaloneCodeEditor | null>(null);
|
||||||
const [filename, setFilename] = useState(props.filename);
|
const [filename, setFilename] = useState(props.filename ? props.filename : lastFilename);
|
||||||
const [code, setCode] = useState<string>(props.code);
|
const [code, setCode] = useState<string>(props.code ? props.code : lastCode);
|
||||||
const [ram, setRAM] = useState('');
|
const [ram, setRAM] = useState('RAM: ???');
|
||||||
const [options, setOptions] = useState<Options>({
|
const [options, setOptions] = useState<Options>({
|
||||||
theme: Settings.MonacoTheme,
|
theme: Settings.MonacoTheme,
|
||||||
insertSpaces: Settings.MonacoInsertSpaces,
|
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 {
|
function save(): void {
|
||||||
if(editorRef.current !== null) {
|
if(editorRef.current !== null) {
|
||||||
const position = editorRef.current.getPosition();
|
const position = editorRef.current.getPosition();
|
||||||
@@ -79,37 +104,38 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
lastPosition = null;
|
||||||
|
|
||||||
// TODO(hydroflame): re-enable the tutorial.
|
// TODO(hydroflame): re-enable the tutorial.
|
||||||
// if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
|
if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
|
||||||
// //Make sure filename + code properly follow tutorial
|
//Make sure filename + code properly follow tutorial
|
||||||
// if (filename !== "n00dles.script") {
|
if (filename !== "n00dles.script") {
|
||||||
// dialogBoxCreate("Leave the script name as 'n00dles'!");
|
dialogBoxCreate("Leave the script name as 'n00dles'!");
|
||||||
// return;
|
return;
|
||||||
// }
|
}
|
||||||
// code = code.replace(/\s/g, "");
|
if (code.replace(/\s/g, "").indexOf("while(true){hack('n00dles');}") == -1) {
|
||||||
// if (code.indexOf("while(true){hack('n00dles');}") == -1) {
|
dialogBoxCreate("Please copy and paste the code from the tutorial!");
|
||||||
// dialogBoxCreate("Please copy and paste the code from the tutorial!");
|
return;
|
||||||
// return;
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// //Save the script
|
//Save the script
|
||||||
// let s = Player.getCurrentServer();
|
const server = props.player.getCurrentServer();
|
||||||
// for (var i = 0; i < s.scripts.length; i++) {
|
if(server === null) throw new Error('Server should not be null but it is.');
|
||||||
// if (filename == s.scripts[i].filename) {
|
for (let i = 0; i < server.scripts.length; i++) {
|
||||||
// s.scripts[i].saveScript(getCurrentEditor().getCode(), Player.currentServer, Player.getCurrentServer().scripts);
|
if (filename == server.scripts[i].filename) {
|
||||||
// Engine.loadTerminalContent();
|
server.scripts[i].saveScript(code, props.player.currentServer, server.scripts);
|
||||||
// return iTutorialNextStep();
|
props.engine.loadTerminalContent();
|
||||||
// }
|
return iTutorialNextStep();
|
||||||
// }
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// // If the current script does NOT exist, create a new one
|
// If the current script does NOT exist, create a new one
|
||||||
// let script = new Script();
|
const script = new Script();
|
||||||
// script.saveScript(getCurrentEditor().getCode(), Player.currentServer, Player.getCurrentServer().scripts);
|
script.saveScript(code, props.player.currentServer, server.scripts);
|
||||||
// s.scripts.push(script);
|
server.scripts.push(script);
|
||||||
|
|
||||||
// return iTutorialNextStep();
|
return iTutorialNextStep();
|
||||||
// }
|
}
|
||||||
|
|
||||||
if (filename == "") {
|
if (filename == "") {
|
||||||
dialogBoxCreate("You must specify a filename!");
|
dialogBoxCreate("You must specify a filename!");
|
||||||
@@ -121,7 +147,8 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
return;
|
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") {
|
if (filename === ".fconf") {
|
||||||
try {
|
try {
|
||||||
parseFconfSettings(code);
|
parseFconfSettings(code);
|
||||||
@@ -131,9 +158,9 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
} else if (isScriptFilename(filename)) {
|
} else if (isScriptFilename(filename)) {
|
||||||
//If the current script already exists on the server, overwrite it
|
//If the current script already exists on the server, overwrite it
|
||||||
for (let i = 0; i < s.scripts.length; i++) {
|
for (let i = 0; i < server.scripts.length; i++) {
|
||||||
if (filename == s.scripts[i].filename) {
|
if (filename == server.scripts[i].filename) {
|
||||||
s.scripts[i].saveScript(code, props.player.currentServer, props.player.getCurrentServer().scripts);
|
server.scripts[i].saveScript(code, props.player.currentServer, server.scripts);
|
||||||
props.engine.loadTerminalContent();
|
props.engine.loadTerminalContent();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -141,20 +168,20 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
|
|
||||||
//If the current script does NOT exist, create a new one
|
//If the current script does NOT exist, create a new one
|
||||||
const script = new Script();
|
const script = new Script();
|
||||||
script.saveScript(code, props.player.currentServer, props.player.getCurrentServer().scripts);
|
script.saveScript(code, props.player.currentServer, server.scripts);
|
||||||
s.scripts.push(script);
|
server.scripts.push(script);
|
||||||
} else if (filename.endsWith(".txt")) {
|
} else if (filename.endsWith(".txt")) {
|
||||||
for (let i = 0; i < s.textFiles.length; ++i) {
|
for (let i = 0; i < server.textFiles.length; ++i) {
|
||||||
if (s.textFiles[i].fn === filename) {
|
if (server.textFiles[i].fn === filename) {
|
||||||
s.textFiles[i].write(code);
|
server.textFiles[i].write(code);
|
||||||
props.engine.loadTerminalContent();
|
props.engine.loadTerminalContent();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const textFile = new TextFile(filename, code);
|
const textFile = new TextFile(filename, code);
|
||||||
s.textFiles.push(textFile);
|
server.textFiles.push(textFile);
|
||||||
} else {
|
} 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)")
|
" or text file (.txt)")
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -162,14 +189,17 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function beautify(): void {
|
function beautify(): void {
|
||||||
setCode(code => beautifyCode(code, {
|
if (editorRef.current === null) return;
|
||||||
|
const pretty = beautifyCode(code, {
|
||||||
indent_with_tabs: !options.insertSpaces,
|
indent_with_tabs: !options.insertSpaces,
|
||||||
indent_size: 4,
|
indent_size: 4,
|
||||||
brace_style: "preserve-inline",
|
brace_style: "preserve-inline",
|
||||||
}));
|
});
|
||||||
|
editorRef.current.setValue(pretty);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFilenameChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
function onFilenameChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
|
lastFilename = filename;
|
||||||
setFilename(event.target.value);
|
setFilename(event.target.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,12 +217,16 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
setOptions(options);
|
setOptions(options);
|
||||||
Settings.MonacoTheme = options.theme;
|
Settings.MonacoTheme = options.theme;
|
||||||
Settings.MonacoInsertSpaces = options.insertSpaces;
|
Settings.MonacoInsertSpaces = options.insertSpaces;
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCode(newCode?: string): void {
|
function updateCode(newCode?: string): void {
|
||||||
if(newCode === undefined) return;
|
if(newCode === undefined) return;
|
||||||
|
lastCode = newCode;
|
||||||
|
if(editorRef.current !== null) {
|
||||||
|
lastPosition = editorRef.current.getPosition();
|
||||||
|
}
|
||||||
setCode(newCode);
|
setCode(newCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,11 +260,27 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
return () => clearInterval(id);
|
return () => clearInterval(id);
|
||||||
}, [code]);
|
}, [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 {
|
function onMount(editor: IStandaloneCodeEditor): void {
|
||||||
editorRef.current = editor;
|
editorRef.current = editor;
|
||||||
if(editorRef.current === null) return;
|
if(editorRef.current === null) return;
|
||||||
const position = CursorPositions.getCursor(filename);
|
const position = CursorPositions.getCursor(filename);
|
||||||
|
if(position.row !== -1)
|
||||||
editorRef.current.setPosition({lineNumber: position.row, column: position.column});
|
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();
|
editorRef.current.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,13 +297,12 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
return { suggestions: suggestions };
|
return { suggestions: suggestions };
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
monaco.languages.typescript.javascriptDefaults.addExtraLib(libSource, 'netscript.d.ts');
|
monaco.languages.typescript.javascriptDefaults.addExtraLib(libSource, 'netscript.d.ts');
|
||||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(libSource, 'netscript.d.ts');
|
monaco.languages.typescript.typescriptDefaults.addExtraLib(libSource, 'netscript.d.ts');
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(options);
|
|
||||||
return (<div id="script-editor-wrapper">
|
return (<div id="script-editor-wrapper">
|
||||||
<div id="script-editor-filename-wrapper">
|
<div id="script-editor-filename-wrapper">
|
||||||
<p id="script-editor-filename-tag" className="noselect"> <strong style={{backgroundColor:'#555'}}>Script name: </strong></p>
|
<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%"
|
height="80%"
|
||||||
defaultLanguage="javascript"
|
defaultLanguage="javascript"
|
||||||
defaultValue={code}
|
defaultValue={code}
|
||||||
value={code}
|
|
||||||
onChange={updateCode}
|
onChange={updateCode}
|
||||||
theme={options.theme}
|
theme={options.theme}
|
||||||
options={options}
|
options={options}
|
||||||
|
|||||||
@@ -145,7 +145,6 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
|
|||||||
Object.assign(Settings, defaultSettings);
|
Object.assign(Settings, defaultSettings);
|
||||||
},
|
},
|
||||||
load(saveString: string) {
|
load(saveString: string) {
|
||||||
console.log(saveString);
|
|
||||||
Object.assign(Settings, JSON.parse(saveString));
|
Object.assign(Settings, JSON.parse(saveString));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
+11
-4
@@ -57,6 +57,7 @@ import {
|
|||||||
loadAllRunningScripts,
|
loadAllRunningScripts,
|
||||||
updateOnlineScriptTimes,
|
updateOnlineScriptTimes,
|
||||||
} from "./NetscriptWorker";
|
} from "./NetscriptWorker";
|
||||||
|
import { GetServerByHostname } from "./Server/ServerHelpers";
|
||||||
import { Player } from "./Player";
|
import { Player } from "./Player";
|
||||||
import { prestigeAugmentation } from "./Prestige";
|
import { prestigeAugmentation } from "./Prestige";
|
||||||
import {
|
import {
|
||||||
@@ -101,6 +102,9 @@ import { ActiveScriptsRoot } from "./ui/ActiveScripts/Root";
|
|||||||
import { initializeMainMenuHeaders } from "./ui/MainMenu/Headers";
|
import { initializeMainMenuHeaders } from "./ui/MainMenu/Headers";
|
||||||
import { initializeMainMenuLinks, MainMenuLinks } from "./ui/MainMenu/Links";
|
import { initializeMainMenuLinks, MainMenuLinks } from "./ui/MainMenu/Links";
|
||||||
|
|
||||||
|
import { FileDiagnosticPopup } from "./Diagnostic/FileDiagnosticPopup";
|
||||||
|
import { createPopup } from "./ui/React/createPopup";
|
||||||
|
|
||||||
import { dialogBoxCreate } from "../utils/DialogBox";
|
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||||
import { gameOptionsBoxClose, gameOptionsBoxOpen } from "../utils/GameOptions";
|
import { gameOptionsBoxClose, gameOptionsBoxOpen } from "../utils/GameOptions";
|
||||||
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||||
@@ -251,9 +255,6 @@ const Engine = {
|
|||||||
Engine.Display.scriptEditorContent.style.display = "block";
|
Engine.Display.scriptEditorContent.style.display = "block";
|
||||||
routing.navigateTo(Page.ScriptEditor);
|
routing.navigateTo(Page.ScriptEditor);
|
||||||
|
|
||||||
|
|
||||||
const monaco = document.getElementById('monaco-editor');
|
|
||||||
//https://www.npmjs.com/package/@monaco-editor/react#development-playground
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<ScriptEditorRoot filename={filename} code={code} player={Player} engine={this} />,
|
<ScriptEditorRoot filename={filename} code={code} player={Player} engine={this} />,
|
||||||
Engine.Display.scriptEditorContent,
|
Engine.Display.scriptEditorContent,
|
||||||
@@ -1525,6 +1526,12 @@ const Engine = {
|
|||||||
gameOptionsBoxClose();
|
gameOptionsBoxClose();
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// DEBUG File diagnostic
|
||||||
|
document.getElementById("debug-files").addEventListener("click", function() {
|
||||||
|
createPopup("debug-files-diagnostic-popup", FileDiagnosticPopup, {});
|
||||||
|
return false;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
start: function() {
|
start: function() {
|
||||||
@@ -1573,4 +1580,4 @@ window.onload = function() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export {Engine, indexedDb};
|
export {Engine};
|
||||||
|
|||||||
+12
-2
@@ -594,10 +594,12 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
|
|||||||
Copy Save data to Clipboard
|
Copy Save data to Clipboard
|
||||||
</button>
|
</button>
|
||||||
<button id="debug-delete-scripts-link" class="a-link-button tooltip">
|
<button id="debug-delete-scripts-link" class="a-link-button tooltip">
|
||||||
Delete all active scripts
|
Force kill all active scripts
|
||||||
<span class="tooltiptextleft">
|
<span class="tooltiptextleft">
|
||||||
Forcefully kill all active running scripts, in case there is a bug or some unexpected issue with the game. After
|
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>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="debug-soft-reset" class="a-link-button tooltip">
|
<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.
|
Perform a soft reset. Resets everything as if you had just purchased an Augmentation.
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import * as React from "react";
|
|||||||
|
|
||||||
import { WorkerScriptAccordion } from "./WorkerScriptAccordion";
|
import { WorkerScriptAccordion } from "./WorkerScriptAccordion";
|
||||||
import { Accordion } from "../React/Accordion";
|
import { Accordion } from "../React/Accordion";
|
||||||
|
import { ServerAccordionContent } from "./ServerAccordionContent";
|
||||||
|
|
||||||
import { BaseServer } from "../../Server/BaseServer";
|
import { BaseServer } from "../../Server/BaseServer";
|
||||||
import { WorkerScript } from "../../Netscript/WorkerScript";
|
import { WorkerScript } from "../../Netscript/WorkerScript";
|
||||||
@@ -42,7 +43,7 @@ export function ServerAccordion(props: IProps): React.ReactElement {
|
|||||||
<pre>{headerTxt}</pre>
|
<pre>{headerTxt}</pre>
|
||||||
}
|
}
|
||||||
panelContent={
|
panelContent={
|
||||||
<ul>{scripts}</ul>
|
<ServerAccordionContent workerScripts={props.workerScripts} />
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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;
|
className = this.props.panelClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!this.props.opened) return (<></>);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className} style={{display: this.props.opened ? "block" : "none"}}>
|
<div className={className} style={{display: "block"}}>
|
||||||
{this.props.panelContent}
|
{this.props.panelContent}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export class AutoupdatingParagraph extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
const time = this.props.intervalTime ? this.props.intervalTime : 1000;
|
const time = this.props.intervalTime ? this.props.intervalTime : 1000;
|
||||||
this.interval = setInterval(() => this.tick(), time);
|
this.interval = window.setInterval(() => this.tick(), time);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount(): void {
|
componentWillUnmount(): void {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export class AutoupdatingStdButton extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
const time = this.props.intervalTime ? this.props.intervalTime : 1000;
|
const time = this.props.intervalTime ? this.props.intervalTime : 1000;
|
||||||
this.interval = setInterval(() => this.tick(), time);
|
this.interval = window.setInterval(() => this.tick(), time);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount(): void {
|
componentWillUnmount(): void {
|
||||||
|
|||||||
+21
-26
@@ -5,12 +5,6 @@ import { RunningScript } from "../../src/Script/RunningScript";
|
|||||||
import { Script } from "../../src/Script/Script";
|
import { Script } from "../../src/Script/Script";
|
||||||
import { SourceFileFlags } from "../../src/SourceFile/SourceFileFlags";
|
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;
|
const ScriptBaseCost = RamCostConstants.ScriptBaseRamCost;
|
||||||
|
|
||||||
describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
|
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
|
// Tests numeric equality, allowing for floating point imprecision
|
||||||
function testEquality(val, expected) {
|
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
|
// Runs a Netscript function and properly catches it if it returns promise
|
||||||
function runPotentiallyAsyncFunction(fn) {
|
function runPotentiallyAsyncFunction(fn) {
|
||||||
let res = fn();
|
const res = fn();
|
||||||
if (res instanceof Promise) {
|
if (res instanceof Promise) {
|
||||||
res.catch(() => undefined);
|
res.catch(() => undefined);
|
||||||
}
|
}
|
||||||
@@ -47,9 +42,9 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
|
|||||||
* including the namespace(s). e.g. ["gang", "getMemberNames"]
|
* including the namespace(s). e.g. ["gang", "getMemberNames"]
|
||||||
*/
|
*/
|
||||||
async function testNonzeroDynamicRamCost(fnDesc) {
|
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);
|
const expected = getRamCost(...fnDesc);
|
||||||
expect(expected).to.be.above(0);
|
expect(expected).toBeGreaterThan(0);
|
||||||
|
|
||||||
const code = `${fnDesc.join(".")}();`
|
const code = `${fnDesc.join(".")}();`
|
||||||
|
|
||||||
@@ -72,7 +67,7 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
|
|||||||
let curr = scope[fnDesc[0]];
|
let curr = scope[fnDesc[0]];
|
||||||
for (let i = 1; i < fnDesc.length; ++i) {
|
for (let i = 1; i < fnDesc.length; ++i) {
|
||||||
if (curr == null) {
|
if (curr == null) {
|
||||||
expect.fail(`Invalid function specified: [${fnDesc}]`);
|
throw new Error(`Invalid function specified: [${fnDesc}]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof curr === "function") {
|
if (typeof curr === "function") {
|
||||||
@@ -92,13 +87,13 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
|
|||||||
runPotentiallyAsyncFunction(curr);
|
runPotentiallyAsyncFunction(curr);
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
} else {
|
} else {
|
||||||
expect.fail(`Invalid function specified: [${fndesc}]`);
|
throw new Error(`Invalid function specified: [${fnDesc}]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const fnName = fnDesc[fnDesc.length - 1];
|
const fnName = fnDesc[fnDesc.length - 1];
|
||||||
testEquality(workerScript.dynamicRamUsage - ScriptBaseCost, expected);
|
testEquality(workerScript.dynamicRamUsage - ScriptBaseCost, expected);
|
||||||
testEquality(workerScript.dynamicRamUsage, runningScript.ramUsage);
|
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"]
|
* including the namespace(s). e.g. ["gang", "getMemberNames"]
|
||||||
*/
|
*/
|
||||||
async function testZeroDynamicRamCost(fnDesc) {
|
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);
|
const expected = getRamCost(...fnDesc);
|
||||||
expect(expected).to.equal(0);
|
expect(expected).toEqual(0);
|
||||||
|
|
||||||
const code = `${fnDesc.join(".")}();`
|
const code = `${fnDesc.join(".")}();`
|
||||||
|
|
||||||
@@ -135,7 +130,7 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
|
|||||||
let curr = scope[fnDesc[0]];
|
let curr = scope[fnDesc[0]];
|
||||||
for (let i = 1; i < fnDesc.length; ++i) {
|
for (let i = 1; i < fnDesc.length; ++i) {
|
||||||
if (curr == null) {
|
if (curr == null) {
|
||||||
expect.fail(`Invalid function specified: [${fnDesc}]`);
|
throw new Error(`Invalid function specified: [${fnDesc}]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof curr === "function") {
|
if (typeof curr === "function") {
|
||||||
@@ -155,20 +150,20 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
|
|||||||
runPotentiallyAsyncFunction(curr);
|
runPotentiallyAsyncFunction(curr);
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
} else {
|
} else {
|
||||||
expect.fail(`Invalid function specified: [${fndesc}]`);
|
throw new Error(`Invalid function specified: [${fndesc}]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
testEquality(workerScript.dynamicRamUsage, ScriptBaseCost);
|
testEquality(workerScript.dynamicRamUsage, ScriptBaseCost);
|
||||||
testEquality(workerScript.dynamicRamUsage, runningScript.ramUsage);
|
testEquality(workerScript.dynamicRamUsage, runningScript.ramUsage);
|
||||||
}
|
}
|
||||||
|
|
||||||
before(function() {
|
beforeEach(function() {
|
||||||
for (let i = 0; i < SourceFileFlags.length; ++i) {
|
for (let i = 0; i < SourceFileFlags.length; ++i) {
|
||||||
SourceFileFlags[i] = 3;
|
SourceFileFlags[i] = 3;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Basic Functions", async function() {
|
describe("Basic Functions", function() {
|
||||||
it("hack()", async function() {
|
it("hack()", async function() {
|
||||||
const f = ["hack"];
|
const f = ["hack"];
|
||||||
await testNonzeroDynamicRamCost(f);
|
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() {
|
it("getBitNodeMultipliers()", async function() {
|
||||||
const f = ["getBitNodeMultipliers"];
|
const f = ["getBitNodeMultipliers"];
|
||||||
await testNonzeroDynamicRamCost(f);
|
await testNonzeroDynamicRamCost(f);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("TIX API", async function() {
|
describe("TIX API", function() {
|
||||||
it("getStockSymbols()", async function() {
|
it("getStockSymbols()", async function() {
|
||||||
const f = ["getStockSymbols"];
|
const f = ["getStockSymbols"];
|
||||||
await testNonzeroDynamicRamCost(f);
|
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() {
|
it("universityCourse()", async function() {
|
||||||
const f = ["universityCourse"];
|
const f = ["universityCourse"];
|
||||||
await testNonzeroDynamicRamCost(f);
|
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() {
|
it("getContractNames()", async function() {
|
||||||
const f = ["bladeburner", "getContractNames"];
|
const f = ["bladeburner", "getContractNames"];
|
||||||
await testNonzeroDynamicRamCost(f);
|
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() {
|
it("getMemberNames()", async function() {
|
||||||
const f = ["gang", "getMemberNames"];
|
const f = ["gang", "getMemberNames"];
|
||||||
await testNonzeroDynamicRamCost(f);
|
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() {
|
it("attempt()", async function() {
|
||||||
const f = ["codingcontract", "attempt"];
|
const f = ["codingcontract", "attempt"];
|
||||||
await testNonzeroDynamicRamCost(f);
|
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() {
|
it("getNumSleeves()", async function() {
|
||||||
const f = ["sleeve", "getNumSleeves"];
|
const f = ["sleeve", "getNumSleeves"];
|
||||||
await testNonzeroDynamicRamCost(f);
|
await testNonzeroDynamicRamCost(f);
|
||||||
+16
-18
@@ -1,8 +1,5 @@
|
|||||||
import { getRamCost, RamCostConstants } from "../../src/Netscript/RamCostGenerator";
|
import { getRamCost, RamCostConstants } from "../../src/Netscript/RamCostGenerator";
|
||||||
import { calculateRamUsage } from "../../src/Script/RamCalculations"
|
import { calculateRamUsage } from "../../src/Script/RamCalculations"
|
||||||
import { expect } from "chai";
|
|
||||||
|
|
||||||
console.log("Beginning Netscript Static RAM Calculation/Generation Tests");
|
|
||||||
|
|
||||||
const ScriptBaseCost = RamCostConstants.ScriptBaseRamCost;
|
const ScriptBaseCost = RamCostConstants.ScriptBaseRamCost;
|
||||||
const HacknetNamespaceCost = RamCostConstants.ScriptHacknetNodesRamCost;
|
const HacknetNamespaceCost = RamCostConstants.ScriptHacknetNodesRamCost;
|
||||||
@@ -10,7 +7,8 @@ const HacknetNamespaceCost = RamCostConstants.ScriptHacknetNodesRamCost;
|
|||||||
describe("Netscript Static RAM Calculation/Generation Tests", function() {
|
describe("Netscript Static RAM Calculation/Generation Tests", function() {
|
||||||
// Tests numeric equality, allowing for floating point imprecision
|
// Tests numeric equality, allowing for floating point imprecision
|
||||||
function testEquality(val, expected) {
|
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) {
|
async function expectNonZeroRamCost(fnDesc) {
|
||||||
if (!Array.isArray(fnDesc)) { expect.fail("Non-array passed to expectNonZeroRamCost()"); }
|
if (!Array.isArray(fnDesc)) { expect.fail("Non-array passed to expectNonZeroRamCost()"); }
|
||||||
const expected = getRamCost(...fnDesc);
|
const expected = getRamCost(...fnDesc);
|
||||||
expect(expected).to.be.above(0);
|
expect(expected).toBeGreaterThan(0);
|
||||||
|
|
||||||
const code = fnDesc.join(".") + "(); ";
|
const code = fnDesc.join(".") + "(); ";
|
||||||
|
|
||||||
@@ -33,7 +31,7 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
|
|||||||
|
|
||||||
const multipleCallsCode = code.repeat(3);
|
const multipleCallsCode = code.repeat(3);
|
||||||
const multipleCallsCalculated = await calculateRamUsage(multipleCallsCode, []);
|
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) {
|
async function expectZeroRamCost(fnDesc) {
|
||||||
if (!Array.isArray(fnDesc)) { expect.fail("Non-array passed to expectZeroRamCost()"); }
|
if (!Array.isArray(fnDesc)) { expect.fail("Non-array passed to expectZeroRamCost()"); }
|
||||||
const expected = getRamCost(...fnDesc);
|
const expected = getRamCost(...fnDesc);
|
||||||
expect(expected).to.equal(0);
|
expect(expected).toEqual(0);
|
||||||
|
|
||||||
const code = fnDesc.join(".") + "(); ";
|
const code = fnDesc.join(".") + "(); ";
|
||||||
|
|
||||||
@@ -55,10 +53,10 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
|
|||||||
testEquality(calculated, ScriptBaseCost);
|
testEquality(calculated, ScriptBaseCost);
|
||||||
|
|
||||||
const multipleCallsCalculated = await calculateRamUsage(code, []);
|
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() {
|
it("hack()", async function() {
|
||||||
const f = ["hack"];
|
const f = ["hack"];
|
||||||
await expectNonZeroRamCost(f);
|
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() {
|
it("getBitNodeMultipliers()", async function() {
|
||||||
const f = ["getBitNodeMultipliers"];
|
const f = ["getBitNodeMultipliers"];
|
||||||
await expectNonZeroRamCost(f);
|
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
|
// The Hacknet Node API RAM cost is a bit different because
|
||||||
// it's just a one-time cost to access the 'hacknet' namespace.
|
// it's just a one-time cost to access the 'hacknet' namespace.
|
||||||
// Otherwise, all functions cost 0 RAM
|
// 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() {
|
it("should have zero RAM cost for all functions", function() {
|
||||||
for (const fn of apiFunctions) {
|
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() {
|
it("getStockSymbols()", async function() {
|
||||||
const f = ["getStockSymbols"];
|
const f = ["getStockSymbols"];
|
||||||
await expectNonZeroRamCost(f);
|
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() {
|
it("universityCourse()", async function() {
|
||||||
const f = ["universityCourse"];
|
const f = ["universityCourse"];
|
||||||
await expectNonZeroRamCost(f);
|
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() {
|
it("getContractNames()", async function() {
|
||||||
const f = ["bladeburner", "getContractNames"];
|
const f = ["bladeburner", "getContractNames"];
|
||||||
await expectNonZeroRamCost(f);
|
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() {
|
it("getMemberNames()", async function() {
|
||||||
const f = ["gang", "getMemberNames"];
|
const f = ["gang", "getMemberNames"];
|
||||||
await expectNonZeroRamCost(f);
|
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() {
|
it("attempt()", async function() {
|
||||||
const f = ["codingcontract", "attempt"];
|
const f = ["codingcontract", "attempt"];
|
||||||
await expectNonZeroRamCost(f);
|
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() {
|
it("getNumSleeves()", async function() {
|
||||||
const f = ["sleeve", "getNumSleeves"];
|
const f = ["sleeve", "getNumSleeves"];
|
||||||
await expectNonZeroRamCost(f);
|
await expectNonZeroRamCost(f);
|
||||||
+7
-2
@@ -1,5 +1,10 @@
|
|||||||
# Unit Tests
|
# Unit Tests
|
||||||
This directory contains unit tests for Bitburner.
|
This directory contains unit tests for Bitburner.
|
||||||
|
|
||||||
Unit tests use Mocha/Chai and are run using mochapack (a mocha-webpack fork).
|
Unit tests use jest.
|
||||||
Run the test command with `npm run test`
|
|
||||||
|
## 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
@@ -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");
|
|
||||||
})
|
|
||||||
@@ -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";
|
|
||||||
+17
-4
@@ -48,6 +48,20 @@ function ScriptLogPopup(props: IProps): React.ReactElement {
|
|||||||
removeElementById(props.id);
|
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 {
|
function kill(): void {
|
||||||
killWorkerScript(props.script, props.script.server, true);
|
killWorkerScript(props.script, props.script.server, true);
|
||||||
close();
|
close();
|
||||||
@@ -55,12 +69,10 @@ function ScriptLogPopup(props: IProps): React.ReactElement {
|
|||||||
|
|
||||||
function drag(event: React.MouseEvent<HTMLElement, MouseEvent>): void {
|
function drag(event: React.MouseEvent<HTMLElement, MouseEvent>): void {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
//console.log(props.container.clientWidth);
|
|
||||||
//console.log(props.container.clientHeight);
|
|
||||||
let x = event.clientX;
|
let x = event.clientX;
|
||||||
let y = event.clientY;
|
let y = event.clientY;
|
||||||
let left = props.container.offsetLeft+props.container.clientWidth/2;
|
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 {
|
function mouseMove(event: MouseEvent): void {
|
||||||
left+=event.clientX-x;
|
left+=event.clientX-x;
|
||||||
top+=event.clientY-y;
|
top+=event.clientY-y;
|
||||||
@@ -95,7 +107,8 @@ function ScriptLogPopup(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function logBoxCreate(script: RunningScript): void {
|
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", {
|
const container = createElement("div", {
|
||||||
class: "log-box-container",
|
class: "log-box-container",
|
||||||
id: id,
|
id: id,
|
||||||
|
|||||||
Reference in New Issue
Block a user