Compare commits

...

66 Commits

Author SHA1 Message Date
Olivier Gagnon 42704d8695 v0.52.9 2021-08-27 15:26:12 -04:00
Olivier Gagnon e75197dee3 build 2021-08-27 14:19:36 -04:00
Olivier Gagnon 9e92df47a5 Added file diagnostic. 2021-08-27 14:17:25 -04:00
Olivier Gagnon c110c22efb My corp infinity safeguard from 2 patch ago wasn't actually preventing it, just logging, now it returns to avoid it. 2021-08-27 11:18:06 -04:00
Olivier Gagnon c9ab7908a7 another blocker against mku equal 0 and added tprintf 2021-08-27 11:05:36 -04:00
Olivier Gagnon 3ab306f9d7 fix the errors about node setTimeout instead of window 2021-08-27 01:11:11 -04:00
hydroflame f08aa8924c Merge pull request #1102 from threehams/test-runner
Switch out test runner for jest
2021-08-27 00:53:45 -04:00
Olivier Gagnon c4914fa54f build community prs 2021-08-27 00:45:11 -04:00
hydroflame fa5e2f4964 Merge pull request #1079 from threehams/infil-instakill
Instakill player when automating infiltration
2021-08-26 21:42:08 -04:00
hydroflame 77eda1fd75 Merge pull request #1098 from brubsby/patch-1
add bladeburner_analysis_mult to getPlayer()
2021-08-26 21:42:01 -04:00
Olivier Gagnon c987c91a11 add corp safeguard 2021-08-26 21:39:51 -04:00
David Edmondson feaa74ed34 Only compile down imports during tests 2021-08-26 17:02:02 -07:00
David Edmondson 701fba7ec7 Drop cross-env 2021-08-26 16:45:39 -07:00
David Edmondson 51bd626e88 Remove unneeded stuff, .vscode on gitignore 2021-08-26 16:44:37 -07:00
David Edmondson ab4863e7df Swap out mocha/chai for jest 2021-08-26 16:43:11 -07:00
David Edmondson 1a8bcf66cc Fix existing tests, update to jest 2021-08-26 16:43:03 -07:00
David Edmondson 7bfceb1690 Replace old-style import with type 2021-08-26 16:42:57 -07:00
David Edmondson 27e22814a9 Remove missing + unused variable 2021-08-26 16:42:47 -07:00
Olivier Gagnon ceb4e304fd Hotfix corp mku getting set to zero and causing infinity 2021-08-26 15:22:06 -04:00
Olivier Gagnon e2d74f9432 fix beautify 2021-08-25 16:14:47 -04:00
Olivier Gagnon 79345a49b4 Bladeburner automation status always displays the commands, even when disabled 2021-08-25 11:50:33 -04:00
Olivier Gagnon 7066a793a1 build fix 2021-08-24 21:40:50 -04:00
hydroflame 2a5cf62168 Merge pull request #1097 from Snarling/patch-2
Fix joining blade via ns
2021-08-24 21:39:29 -04:00
brubsby 6495be5705 add bladeburner_analysis_mult to getPlayer() 2021-08-24 20:02:39 -05:00
Snarling 0d6d05db49 Fix joining blade via ns
Pass Player as an argument in Bladeburner constructor call for ns.bladeburner.joinBladeburnerDivision()
2021-08-24 20:08:29 -04:00
Olivier Gagnon 5d59620dce click to copy every bladeburner action 2021-08-23 11:42:14 -04:00
Olivier Gagnon 60d95a90d0 Fix script not being saved on their individual computers. 2021-08-23 09:33:49 -04:00
Olivier Gagnon 51debc60da build omuretsu fix 2021-08-23 09:18:43 -04:00
Snarling faf625b34d Update Root.tsx
Went back to tracking lastServer as a hostname, since server IPs are not static.
2021-08-23 04:04:52 -07:00
Snarling 1a8b194341 Update Root.tsx
Removed unnecessary conversions between server and ip
2021-08-23 04:04:52 -07:00
Snarling 386f8a11c5 Change lastServer to reference the server ip
Should fix issue with newly saved scripts failing to run
2021-08-23 04:04:52 -07:00
hydroflame 4278191b0e Merge pull request #1090 from danielyxie/dev
v0.52.8
2021-08-23 02:09:55 -04:00
Olivier Gagnon 6d2b8b4f6f v0.52.8 2021-08-23 02:09:49 -04:00
Olivier Gagnon b148b2f0b5 logbox close on escape now 2021-08-23 01:15:20 -04:00
hydroflame 4a9bac99d2 Merge pull request #1083 from danielyxie/dev
Fix monaco jumping to end of file.
2021-08-22 23:57:16 -04:00
Olivier Gagnon 0b3c114cd0 Fix monaco jumping to end of file. 2021-08-22 23:57:00 -04:00
hydroflame 49cc75a575 Merge pull request #1082 from danielyxie/dev
trying to fix the jumping bug
2021-08-22 23:47:44 -04:00
Olivier Gagnon e0d631f8b3 trying to fix the jumping bug 2021-08-22 23:46:30 -04:00
hydroflame 8289c9fc75 Merge pull request #1080 from danielyxie/dev
Fixed Script Editor not loading the same file after manually clicking it
2021-08-22 01:31:00 -04:00
Olivier Gagnon d66e36b637 Fixed Script Editor not loading the same file after manually clicking it 2021-08-22 01:30:28 -04:00
David Edmondson 6cd7465b82 Instakill player when automating infiltration 2021-08-21 15:00:00 -07:00
hydroflame c7125e2e46 Merge pull request #1077 from danielyxie/dev
Fix a few other bugs
2021-08-21 14:01:05 -04:00
Olivier Gagnon a564957092 v0.52.7 2021-08-21 14:00:28 -04:00
Olivier Gagnon 4b8e63f342 Fix a few other bugs 2021-08-21 11:30:31 -04:00
hydroflame 480d47eece Merge pull request #1076 from danielyxie/dev
Fix log box dragging.
2021-08-21 02:39:17 -04:00
Olivier Gagnon 4de20f8cce Made logbox drag a little smoother. 2021-08-21 02:31:37 -04:00
Olivier Gagnon 4b38d296a8 Fix corp industry wrong initial value. 2021-08-21 02:10:58 -04:00
hydroflame 9ac75d5bf5 Merge pull request #1075 from danielyxie/dev
Fix Corp research popup box appearing behind one another.
2021-08-21 02:07:10 -04:00
Olivier Gagnon 6561413137 Fix Corp research popup box appearing behind one another. 2021-08-21 02:06:48 -04:00
hydroflame 1fb5105d0a Merge pull request #1074 from danielyxie/dev
hotfix broken editor shortcuts
2021-08-21 01:55:05 -04:00
Olivier Gagnon b67c03ff8a hotfix broken editor shortcuts 2021-08-21 01:54:39 -04:00
hydroflame 7db3716256 Merge pull request #1072 from danielyxie/dev
hotfix the tutorial
2021-08-21 00:58:58 -04:00
Olivier Gagnon ee5a70901b hotfix logbox width 2021-08-21 00:58:24 -04:00
Olivier Gagnon 63b2c77907 hotfix the tutorial 2021-08-21 00:51:07 -04:00
hydroflame aa3ad3164c Merge pull request #1068 from danielyxie/dev
v0.52.6
2021-08-21 00:32:04 -04:00
hydroflame fea25249a8 Merge pull request #1062 from danielyxie/dev
v0.52.5
2021-08-19 16:38:26 -04:00
hydroflame 3826de72ef Merge pull request #1061 from danielyxie/dev
hotfix some blade netscript functions not working
2021-08-19 11:04:24 -04:00
hydroflame 5098ef6232 Merge pull request #1057 from danielyxie/dev
v0.52.4 - Bladeburner in React
2021-08-19 01:46:16 -04:00
hydroflame 27ee65f524 Merge pull request #1051 from danielyxie/dev
hotfix 0 territory being softlocked.
2021-08-17 17:47:58 -04:00
hydroflame 1d0f193c34 Merge pull request #1050 from danielyxie/dev
hotfix blocked in Gang
2021-08-17 17:14:11 -04:00
hydroflame 08908c87ea Merge pull request #1048 from danielyxie/dev
Hotfix weird bladeburner ui bug
2021-08-15 17:14:38 -04:00
hydroflame 3957a517db Merge pull request #1047 from danielyxie/dev
v0.52.3 - 2021-07-15 Gangs were OP (hydroflame)
2021-08-15 16:26:52 -04:00
hydroflame 21daab32c1 Merge pull request #1044 from danielyxie/dev
v0.52.2
2021-08-15 02:15:03 -04:00
hydroflame 5e2ed7a79e Merge pull request #1042 from danielyxie/dev
hotfix revert tutorial instructing the player to make a script on n00…
2021-08-11 01:05:46 -04:00
hydroflame d9e60ea124 Merge pull request #1039 from danielyxie/dev
rebuild with the version inside the game correctly udpated
2021-08-10 21:10:00 -04:00
hydroflame 2750eb293a Merge pull request #1038 from danielyxie/dev
v0.52.1
2021-08-10 21:04:05 -04:00
74 changed files with 13574 additions and 3360 deletions
-3
View File
@@ -1,3 +0,0 @@
{
"presets": ["@babel/preset-react"]
}
+4
View File
@@ -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
+9
View File
@@ -0,0 +1,9 @@
const TEST = process.env.NODE_ENV === "test";
module.exports = {
"presets": [
"@babel/preset-react",
TEST && "@babel/preset-env",
TEST && "@babel/preset-typescript",
].filter(Boolean),
}
+7 -1
View File
@@ -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);
+2 -2
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -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
+6 -1
View File
@@ -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);
+15 -15
View File
File diff suppressed because one or more lines are too long
+79
View File
@@ -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
View File
@@ -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
View File
@@ -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>
+9
View File
@@ -0,0 +1,9 @@
module.exports = {
setupFiles: ["./jest.setup.js"],
moduleFileExtensions: ["ts", "tsx", "js", "jsx"],
transform: {
"^.+\\.(js|jsx|ts|tsx)$": "babel-jest",
},
// testMatch: ["**/?(*.)+(test).[jt]s?(x)"],
testEnvironment: "jsdom",
};
+2
View File
@@ -0,0 +1,2 @@
import "regenerator-runtime/runtime";
global.$ = require("jquery");
+12233 -2441
View File
File diff suppressed because it is too large Load Diff
+9 -8
View File
@@ -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"
} }
+17
View File
@@ -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:
+8 -9
View File
@@ -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];
} }
} }
+1
View File
@@ -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;
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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>
</>); </>);
} }
+8 -6
View File
@@ -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>
</>); </>);
} }
+1
View File
@@ -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;
+1 -11
View File
@@ -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 {
+7 -27
View File
@@ -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);
*/
+26 -4
View File
@@ -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>
</>); </>);
} }
+6 -4
View File
@@ -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 />
+14
View File
@@ -0,0 +1,14 @@
import React from "react";
import { formatNumber } from "../../../utils/StringHelperFunctions";
interface IProps {
chance: number[];
}
export function SuccessChance(props: IProps): React.ReactElement {
if(props.chance[0] === props.chance[1]) {
return (<>{formatNumber(props.chance[0]*100, 1)}%</>);
}
return (<>{formatNumber(props.chance[0]*100, 1)}% ~ {formatNumber(props.chance[1]*100, 1)}%</>);
}
+1 -1
View File
@@ -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 {
+1 -1
View File
@@ -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
View File
@@ -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
`, `,
/*
*/
} }
+21 -8
View File
@@ -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 ` +
+5
View File
@@ -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
View File
@@ -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);
} }
} }
+68
View File
@@ -0,0 +1,68 @@
import React from "react";
import { AllServers } from "../Server/AllServers";
import { Script } from "../Script/Script";
import { TextFile } from "../TextFile";
import { Accordion } from "../ui/React/Accordion";
import { numeralWrapper } from "../ui/numeralFormat";
interface IServerProps {
ip: string;
}
export function ServerAccordion(props: IServerProps): React.ReactElement {
const server = AllServers[props.ip];
let totalSize = 0;
for(const f of server.scripts) {
totalSize += f.code.length;
}
for(const f of server.textFiles) {
totalSize += f.text.length;
}
if(totalSize === 0) {
return <></>
}
interface File {
name: string;
size: number;
}
const files: File[] = [];
for(const f of server.scripts) {
files.push({name: f.filename, size: f.code.length});
}
for(const f of server.textFiles) {
files.push({name: f.fn, size: f.text.length});
}
files.sort((a: File, b: File): number => b.size-a.size);
return <Accordion
headerContent={<>{server.hostname} ({numeralWrapper.formatBigNumber(totalSize)}b)</>}
panelContent={<ul>
{files.map((file: File) => <li key={file.name}>{file.name}: {numeralWrapper.formatBigNumber(file.size)}b</li>) }
</ul>}
/>
}
interface IProps {}
export function FileDiagnosticPopup(props: IProps): React.ReactElement {
const ips: string[] = [];
for(const ip of Object.keys(AllServers)) {
ips.push(ip);
}
return (<>
<p>
Welcome to the file diagnostic! If your save file is really big it's
likely because you have too many text/scripts. This tool can help you
narrow down where they are.
</p>
{ips.map((ip: string) => <ServerAccordion key={ip} ip={ip} />)}
</>);
}
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>)
} }
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>)
} }
+1 -1
View File
@@ -86,7 +86,7 @@ export function Cyberpunk2077Game(props: IMinigameProps): React.ReactElement {
return <span key={`${x}${y}`} style={{fontSize: fontSize, color: 'blue'}}>{cell}&nbsp;</span> return <span key={`${x}${y}`} style={{fontSize: fontSize, color: 'blue'}}>{cell}&nbsp;</span>
return <span key={`${x}${y}`} style={{fontSize: fontSize}}>{cell}&nbsp;</span> return <span key={`${x}${y}`} style={{fontSize: fontSize}}>{cell}&nbsp;</span>
})}</pre><br /></div>)} })}</pre><br /></div>)}
<KeyHandler onKeyDown={press} /> <KeyHandler onKeyDown={press} onFailure={props.onFailure} />
</Grid> </Grid>
</Grid>) </Grid>)
} }
+5 -2
View File
@@ -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";
+4 -1
View File
@@ -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;
} }
+7 -1
View File
@@ -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);
} }
+1 -1
View File
@@ -94,7 +94,7 @@ export function MinesweeperGame(props: IMinigameProps): React.ReactElement {
return <span key={x}>[&nbsp;]&nbsp;</span> return <span key={x}>[&nbsp;]&nbsp;</span>
} }
})}</pre><br /></div>)} })}</pre><br /></div>)}
<KeyHandler onKeyDown={press} /> <KeyHandler onKeyDown={press} onFailure={props.onFailure} />
</Grid> </Grid>
</Grid>) </Grid>)
} }
+3 -3
View File
@@ -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>)
} }
+1 -1
View File
@@ -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>)
} }
+3
View File
@@ -0,0 +1,3 @@
export declare function iTutorialNextStep(): void;
export declare const ITutorial: {isRunning: boolean, currStep: number};
export declare const iTutorialSteps: {[key: string]: number};
+10 -2
View File
@@ -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");
-1
View File
@@ -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);
-13
View File
@@ -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();
+2 -2
View File
@@ -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;
+96 -48
View File
@@ -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}
-1
View File
@@ -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
View File
@@ -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
View File
@@ -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>
+2 -1
View File
@@ -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=">>"
/>
</>);
}
+3 -1
View File
@@ -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>
) )
+1 -1
View File
@@ -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 {
+1 -1
View File
@@ -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 {
@@ -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);
@@ -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
View File
@@ -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
+33
View File
@@ -0,0 +1,33 @@
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
describe("StringHelperFunctions Tests", function () {
it("transforms strings", () => {
expect(convertTimeMsToTimeElapsedString(1000)).toEqual("1 seconds");
expect(convertTimeMsToTimeElapsedString(5 * 60 * 1000 + 34 * 1000)).toEqual(
"5 minutes 34 seconds",
);
expect(
convertTimeMsToTimeElapsedString(
2 * 60 * 60 * 24 * 1000 + 5 * 60 * 1000 + 34 * 1000,
),
).toEqual("2 days 5 minutes 34 seconds");
expect(
convertTimeMsToTimeElapsedString(
2 * 60 * 60 * 24 * 1000 + 5 * 60 * 1000 + 34 * 1000,
true,
),
).toEqual("2 days 5 minutes 34.000 seconds");
expect(
convertTimeMsToTimeElapsedString(
2 * 60 * 60 * 24 * 1000 + 5 * 60 * 1000 + 34 * 1000 + 123,
true,
),
).toEqual("2 days 5 minutes 34.123 seconds");
expect(
convertTimeMsToTimeElapsedString(
2 * 60 * 60 * 24 * 1000 + 5 * 60 * 1000 + 34 * 1000 + 123.888,
true,
),
).toEqual("2 days 5 minutes 34.123 seconds");
})
});
-11
View File
@@ -1,11 +0,0 @@
import { expect } from "chai";
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
describe("StringHelperFunctions Tests", function() {
expect(convertTimeMsToTimeElapsedString(1000)).to.equal("1 seconds");
expect(convertTimeMsToTimeElapsedString(5*60*1000+34*1000)).to.equal("5 minutes 34 seconds");
expect(convertTimeMsToTimeElapsedString(2*60*60*24*1000+5*60*1000+34*1000)).to.equal("2 days 5 minutes 34 seconds");
expect(convertTimeMsToTimeElapsedString(2*60*60*24*1000+5*60*1000+34*1000, true)).to.equal("2 days 5 minutes 34.000 seconds");
expect(convertTimeMsToTimeElapsedString(2*60*60*24*1000+5*60*1000+34*1000+123, true)).to.equal("2 days 5 minutes 34.123 seconds");
expect(convertTimeMsToTimeElapsedString(2*60*60*24*1000+5*60*1000+34*1000+123.888, true)).to.equal("2 days 5 minutes 34.123 seconds");
})
+295
View File
@@ -0,0 +1,295 @@
import * as dirHelpers from "../../src/Terminal/DirectoryHelpers";
describe("Terminal Directory Tests", function() {
describe("removeLeadingSlash()", function() {
const removeLeadingSlash = dirHelpers.removeLeadingSlash;
it("should remove first slash in a string", function() {
expect(removeLeadingSlash("/")).toEqual("");
expect(removeLeadingSlash("/foo.txt")).toEqual("foo.txt");
expect(removeLeadingSlash("/foo/file.txt")).toEqual("foo/file.txt");
});
it("should only remove one slash", function() {
expect(removeLeadingSlash("///")).toEqual("//");
expect(removeLeadingSlash("//foo")).toEqual("/foo");
});
it("should do nothing for a string that doesn't start with a slash", function() {
expect(removeLeadingSlash("foo.txt")).toEqual("foo.txt");
expect(removeLeadingSlash("foo/test.txt")).toEqual("foo/test.txt");
});
it("should not fail on an empty string", function() {
expect(removeLeadingSlash.bind(null, "")).not.toThrow();
expect(removeLeadingSlash("")).toEqual("");
});
});
describe("removeTrailingSlash()", function() {
const removeTrailingSlash = dirHelpers.removeTrailingSlash;
it("should remove last slash in a string", function() {
expect(removeTrailingSlash("/")).toEqual("");
expect(removeTrailingSlash("foo.txt/")).toEqual("foo.txt");
expect(removeTrailingSlash("foo/file.txt/")).toEqual("foo/file.txt");
});
it("should only remove one slash", function() {
expect(removeTrailingSlash("///")).toEqual("//");
expect(removeTrailingSlash("foo//")).toEqual("foo/");
});
it("should do nothing for a string that doesn't end with a slash", function() {
expect(removeTrailingSlash("foo.txt")).toEqual("foo.txt");
expect(removeTrailingSlash("foo/test.txt")).toEqual("foo/test.txt");
});
it("should not fail on an empty string", function() {
expect(removeTrailingSlash.bind(null, "")).not.toThrow();
expect(removeTrailingSlash("")).toEqual("");
});
});
describe("isValidFilename()", function() {
const isValidFilename = dirHelpers.isValidFilename;
it("should return true for valid filenames", function() {
expect(isValidFilename("test.txt")).toEqual(true);
expect(isValidFilename("123.script")).toEqual(true);
expect(isValidFilename("foo123.b")).toEqual(true);
expect(isValidFilename("my_script.script")).toEqual(true);
expect(isValidFilename("my-script.script")).toEqual(true);
expect(isValidFilename("_foo.lit")).toEqual(true);
expect(isValidFilename("mult.periods.script")).toEqual(true);
expect(isValidFilename("mult.per-iods.again.script")).toEqual(true);
expect(isValidFilename("BruteSSH.exe-50%-INC")).toEqual(true);
expect(isValidFilename("DeepscanV1.exe-1.01%-INC")).toEqual(true);
expect(isValidFilename("DeepscanV2.exe-1.00%-INC")).toEqual(true);
expect(isValidFilename("AutoLink.exe-1.%-INC")).toEqual(true);
});
it("should return false for invalid filenames", function() {
expect(isValidFilename("foo")).toEqual(false);
expect(isValidFilename("my script.script")).toEqual(false);
expect(isValidFilename("a^.txt")).toEqual(false);
expect(isValidFilename("b#.lit")).toEqual(false);
expect(isValidFilename("lib().js")).toEqual(false);
expect(isValidFilename("foo.script_")).toEqual(false);
expect(isValidFilename("foo._script")).toEqual(false);
expect(isValidFilename("foo.hyphened-ext")).toEqual(false);
expect(isValidFilename("")).toEqual(false);
expect(isValidFilename("AutoLink-1.%-INC.exe")).toEqual(false);
expect(isValidFilename("AutoLink.exe-1.%-INC.exe")).toEqual(false);
expect(isValidFilename("foo%.exe")).toEqual(false);
expect(isValidFilename("-1.00%-INC")).toEqual(false);
});
});
describe("isValidDirectoryName()", function() {
const isValidDirectoryName = dirHelpers.isValidDirectoryName;
it("should return true for valid directory names", function() {
expect(isValidDirectoryName("a")).toEqual(true);
expect(isValidDirectoryName("foo")).toEqual(true);
expect(isValidDirectoryName("foo-dir")).toEqual(true);
expect(isValidDirectoryName("foo_dir")).toEqual(true);
expect(isValidDirectoryName(".a")).toEqual(true);
expect(isValidDirectoryName("1")).toEqual(true);
expect(isValidDirectoryName("a1")).toEqual(true);
expect(isValidDirectoryName(".a1")).toEqual(true);
expect(isValidDirectoryName("._foo")).toEqual(true);
expect(isValidDirectoryName("_foo")).toEqual(true);
});
it("should return false for invalid directory names", function() {
expect(isValidDirectoryName("")).toEqual(false);
expect(isValidDirectoryName("foo.dir")).toEqual(false);
expect(isValidDirectoryName("1.")).toEqual(false);
expect(isValidDirectoryName("foo.")).toEqual(false);
expect(isValidDirectoryName("dir#")).toEqual(false);
expect(isValidDirectoryName("dir!")).toEqual(false);
expect(isValidDirectoryName("dir*")).toEqual(false);
expect(isValidDirectoryName(".")).toEqual(false);
});
});
describe("isValidDirectoryPath()", function() {
const isValidDirectoryPath = dirHelpers.isValidDirectoryPath;
it("should return false for empty strings", function() {
expect(isValidDirectoryPath("")).toEqual(false);
});
it("should return true only for the forward slash if the string has length 1", function() {
expect(isValidDirectoryPath("/")).toEqual(true);
expect(isValidDirectoryPath(" ")).toEqual(false);
expect(isValidDirectoryPath(".")).toEqual(false);
expect(isValidDirectoryPath("a")).toEqual(false);
});
it("should return true for valid directory paths", function() {
expect(isValidDirectoryPath("/a")).toEqual(true);
expect(isValidDirectoryPath("/dir/a")).toEqual(true);
expect(isValidDirectoryPath("/dir/foo")).toEqual(true);
expect(isValidDirectoryPath("/.dir/foo-dir")).toEqual(true);
expect(isValidDirectoryPath("/.dir/foo_dir")).toEqual(true);
expect(isValidDirectoryPath("/.dir/.a")).toEqual(true);
expect(isValidDirectoryPath("/dir1/1")).toEqual(true);
expect(isValidDirectoryPath("/dir1/a1")).toEqual(true);
expect(isValidDirectoryPath("/dir1/.a1")).toEqual(true);
expect(isValidDirectoryPath("/dir_/._foo")).toEqual(true);
expect(isValidDirectoryPath("/dir-/_foo")).toEqual(true);
});
it("should return false if the path does not have a leading slash", function() {
expect(isValidDirectoryPath("a")).toEqual(false);
expect(isValidDirectoryPath("dir/a")).toEqual(false);
expect(isValidDirectoryPath("dir/foo")).toEqual(false);
expect(isValidDirectoryPath(".dir/foo-dir")).toEqual(false);
expect(isValidDirectoryPath(".dir/foo_dir")).toEqual(false);
expect(isValidDirectoryPath(".dir/.a")).toEqual(false);
expect(isValidDirectoryPath("dir1/1")).toEqual(false);
expect(isValidDirectoryPath("dir1/a1")).toEqual(false);
expect(isValidDirectoryPath("dir1/.a1")).toEqual(false);
expect(isValidDirectoryPath("dir_/._foo")).toEqual(false);
expect(isValidDirectoryPath("dir-/_foo")).toEqual(false);
});
it("should accept dot notation", function() {
expect(isValidDirectoryPath("/dir/./a")).toEqual(true);
expect(isValidDirectoryPath("/dir/../foo")).toEqual(true);
expect(isValidDirectoryPath("/.dir/./foo-dir")).toEqual(true);
expect(isValidDirectoryPath("/.dir/../foo_dir")).toEqual(true);
expect(isValidDirectoryPath("/.dir/./.a")).toEqual(true);
expect(isValidDirectoryPath("/dir1/1/.")).toEqual(true);
expect(isValidDirectoryPath("/dir1/a1/..")).toEqual(true);
expect(isValidDirectoryPath("/dir1/.a1/..")).toEqual(true);
expect(isValidDirectoryPath("/dir_/._foo/.")).toEqual(true);
expect(isValidDirectoryPath("/./dir-/_foo")).toEqual(true);
expect(isValidDirectoryPath("/../dir-/_foo")).toEqual(true);
});
});
describe("isValidFilePath()", function() {
const isValidFilePath = dirHelpers.isValidFilePath;
it("should return false for strings that are too short", function() {
expect(isValidFilePath("/a")).toEqual(false);
expect(isValidFilePath("a.")).toEqual(false);
expect(isValidFilePath(".a")).toEqual(false);
expect(isValidFilePath("/.")).toEqual(false);
});
it("should return true for arguments that are just filenames", function() {
expect(isValidFilePath("test.txt")).toEqual(true);
expect(isValidFilePath("123.script")).toEqual(true);
expect(isValidFilePath("foo123.b")).toEqual(true);
expect(isValidFilePath("my_script.script")).toEqual(true);
expect(isValidFilePath("my-script.script")).toEqual(true);
expect(isValidFilePath("_foo.lit")).toEqual(true);
expect(isValidFilePath("mult.periods.script")).toEqual(true);
expect(isValidFilePath("mult.per-iods.again.script")).toEqual(true);
});
it("should return true for valid filepaths", function() {
expect(isValidFilePath("/foo/test.txt")).toEqual(true);
expect(isValidFilePath("/../123.script")).toEqual(true);
expect(isValidFilePath("/./foo123.b")).toEqual(true);
expect(isValidFilePath("/dir/my_script.script")).toEqual(true);
expect(isValidFilePath("/dir1/dir2/dir3/my-script.script")).toEqual(true);
expect(isValidFilePath("/dir1/dir2/././../_foo.lit")).toEqual(true);
expect(isValidFilePath("/.dir1/./../.dir2/mult.periods.script")).toEqual(true);
expect(isValidFilePath("/_dir/../dir2/mult.per-iods.again.script")).toEqual(true);
});
it("should return false for strings that end with a slash", function() {
expect(isValidFilePath("/foo/")).toEqual(false);
expect(isValidFilePath("foo.txt/")).toEqual(false);
expect(isValidFilePath("/")).toEqual(false);
expect(isValidFilePath("/_dir/")).toEqual(false);
});
it("should return false for invalid arguments", function() {
expect(isValidFilePath(null)).toEqual(false);
expect(isValidFilePath()).toEqual(false);
expect(isValidFilePath(5)).toEqual(false);
expect(isValidFilePath({})).toEqual(false);
})
});
describe("getFirstParentDirectory()", function() {
const getFirstParentDirectory = dirHelpers.getFirstParentDirectory;
it("should return the first parent directory in a filepath", function() {
expect(getFirstParentDirectory("/dir1/foo.txt")).toEqual("dir1/");
expect(getFirstParentDirectory("/dir1/dir2/dir3/dir4/foo.txt")).toEqual("dir1/");
expect(getFirstParentDirectory("/_dir1/dir2/foo.js")).toEqual("_dir1/");
});
it("should return '/' if there is no first parent directory", function() {
expect(getFirstParentDirectory("")).toEqual("/");
expect(getFirstParentDirectory(" ")).toEqual("/");
expect(getFirstParentDirectory("/")).toEqual("/");
expect(getFirstParentDirectory("//")).toEqual("/");
expect(getFirstParentDirectory("foo.script")).toEqual("/");
expect(getFirstParentDirectory("/foo.txt")).toEqual("/");
});
});
describe("getAllParentDirectories()", function() {
const getAllParentDirectories = dirHelpers.getAllParentDirectories;
it("should return all parent directories in a filepath", function() {
expect(getAllParentDirectories("/")).toEqual("/");
expect(getAllParentDirectories("/home/var/foo.txt")).toEqual("/home/var/");
expect(getAllParentDirectories("/home/var/")).toEqual("/home/var/");
expect(getAllParentDirectories("/home/var/test/")).toEqual("/home/var/test/");
});
it("should return an empty string if there are no parent directories", function() {
expect(getAllParentDirectories("foo.txt")).toEqual("");
});
});
describe("isInRootDirectory()", function() {
const isInRootDirectory = dirHelpers.isInRootDirectory;
it("should return true for filepaths that refer to a file in the root directory", function() {
expect(isInRootDirectory("a.b")).toEqual(true);
expect(isInRootDirectory("foo.txt")).toEqual(true);
expect(isInRootDirectory("/foo.txt")).toEqual(true);
});
it("should return false for filepaths that refer to a file that's NOT in the root directory", function() {
expect(isInRootDirectory("/dir/foo.txt")).toEqual(false);
expect(isInRootDirectory("dir/foo.txt")).toEqual(false);
expect(isInRootDirectory("/./foo.js")).toEqual(false);
expect(isInRootDirectory("../foo.js")).toEqual(false);
expect(isInRootDirectory("/dir1/dir2/dir3/foo.txt")).toEqual(false);
});
it("should return false for invalid inputs (inputs that aren't filepaths)", function() {
expect(isInRootDirectory(null)).toEqual(false);
expect(isInRootDirectory(undefined)).toEqual(false);
expect(isInRootDirectory("")).toEqual(false);
expect(isInRootDirectory(" ")).toEqual(false);
expect(isInRootDirectory("a")).toEqual(false);
expect(isInRootDirectory("/dir")).toEqual(false);
expect(isInRootDirectory("/dir/")).toEqual(false);
expect(isInRootDirectory("/dir/foo")).toEqual(false);
});
});
describe("evaluateDirectoryPath()", function() {
//const evaluateDirectoryPath = dirHelpers.evaluateDirectoryPath;
// TODO
});
describe("evaluateFilePath()", function() {
//const evaluateFilePath = dirHelpers.evaluateFilePath;
// TODO
})
});
-299
View File
@@ -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
})
});
-24
View File
@@ -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>
-5
View File
@@ -1,5 +0,0 @@
export * from "./Netscript/DynamicRamCalculationTests";
export * from "./Netscript/StaticRamCalculationTests";
export * from "./StockMarketTests";
export * from "./StringHelperFunctionsTests";
export * from "./Terminal/DirectoryTests";
+17 -4
View File
@@ -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,