mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-16 22:38:34 +02:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59cf1d5baf | ||
|
|
916ef06913 | ||
|
|
91ee65a101 | ||
|
|
042f926700 | ||
|
|
c0432359c3 | ||
|
|
fbf5545708 | ||
|
|
6ae7b0136c | ||
|
|
200ccd3ad0 | ||
|
|
31f97f74fd | ||
|
|
4cabd2e4ed | ||
|
|
8be7fa9157 | ||
|
|
8ddf7dfbd4 | ||
|
|
571ddb109a | ||
|
|
b2772bbfc1 | ||
|
|
b82d7c12af | ||
|
|
4476d6b258 |
@@ -7,16 +7,28 @@
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
|
||||
.bitnode {
|
||||
color: #00f;
|
||||
}
|
||||
&.level-0 {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.bitnode-destroyed {
|
||||
color: #f00;
|
||||
}
|
||||
&.level-1 {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
.bitnode:hover,
|
||||
.bitnode-destroyed:hover {
|
||||
color: #fff;
|
||||
&.level-2 {
|
||||
color: #48D1CC;
|
||||
}
|
||||
|
||||
&.level-3 {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
&.unimplemented {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
20
dist/engine.bundle.js
vendored
20
dist/engine.bundle.js
vendored
File diff suppressed because one or more lines are too long
2
dist/engineStyle.bundle.js
vendored
2
dist/engineStyle.bundle.js
vendored
@@ -1,2 +1,2 @@
|
||||
!function(n){function t(t){for(var e,i,f=t[0],c=t[1],l=t[2],p=0,s=[];p<f.length;p++)i=f[p],u[i]&&s.push(u[i][0]),u[i]=0;for(e in c)Object.prototype.hasOwnProperty.call(c,e)&&(n[e]=c[e]);for(a&&a(t);s.length;)s.shift()();return r.push.apply(r,l||[]),o()}function o(){for(var n,t=0;t<r.length;t++){for(var o=r[t],e=!0,f=1;f<o.length;f++){var c=o[f];0!==u[c]&&(e=!1)}e&&(r.splice(t--,1),n=i(i.s=o[0]))}return n}var e={},u={1:0},r=[];function i(t){if(e[t])return e[t].exports;var o=e[t]={i:t,l:!1,exports:{}};return n[t].call(o.exports,o,o.exports,i),o.l=!0,o.exports}i.m=n,i.c=e,i.d=function(n,t,o){i.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:o})},i.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},i.t=function(n,t){if(1&t&&(n=i(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var o=Object.create(null);if(i.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var e in n)i.d(o,e,function(t){return n[t]}.bind(null,e));return o},i.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return i.d(t,"a",t),t},i.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},i.p="";var f=window.webpackJsonp=window.webpackJsonp||[],c=f.push.bind(f);f.push=t,f=f.slice();for(var l=0;l<f.length;l++)t(f[l]);var a=c;r.push([361,0]),o()}({304:function(n,t,o){},306:function(n,t,o){},308:function(n,t,o){},310:function(n,t,o){},312:function(n,t,o){},314:function(n,t,o){},316:function(n,t,o){},318:function(n,t,o){},320:function(n,t,o){},322:function(n,t,o){},324:function(n,t,o){},326:function(n,t,o){},328:function(n,t,o){},330:function(n,t,o){},332:function(n,t,o){},334:function(n,t,o){},336:function(n,t,o){},338:function(n,t,o){},340:function(n,t,o){},342:function(n,t,o){},344:function(n,t,o){},346:function(n,t,o){},348:function(n,t,o){},350:function(n,t,o){},352:function(n,t,o){},354:function(n,t,o){},356:function(n,t,o){},358:function(n,t,o){},361:function(n,t,o){"use strict";o.r(t);o(360),o(358),o(356),o(354),o(352),o(350),o(348),o(346),o(344),o(342),o(340),o(338),o(336),o(334),o(332),o(330),o(328),o(326),o(324),o(322),o(320),o(318),o(316),o(314),o(312),o(310),o(308),o(306),o(304)}});
|
||||
!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([362,0]),o()}({305:function(n,t,o){},307:function(n,t,o){},309:function(n,t,o){},311:function(n,t,o){},313:function(n,t,o){},315:function(n,t,o){},317:function(n,t,o){},319:function(n,t,o){},321:function(n,t,o){},323:function(n,t,o){},325:function(n,t,o){},327:function(n,t,o){},329:function(n,t,o){},331:function(n,t,o){},333:function(n,t,o){},335:function(n,t,o){},337:function(n,t,o){},339:function(n,t,o){},341:function(n,t,o){},343:function(n,t,o){},345:function(n,t,o){},347:function(n,t,o){},349:function(n,t,o){},351:function(n,t,o){},353:function(n,t,o){},355:function(n,t,o){},357:function(n,t,o){},359:function(n,t,o){},362:function(n,t,o){"use strict";o.r(t);o(361),o(359),o(357),o(355),o(353),o(351),o(349),o(347),o(345),o(343),o(341),o(339),o(337),o(335),o(333),o(331),o(329),o(327),o(325),o(323),o(321),o(319),o(317),o(315),o(313),o(311),o(309),o(307),o(305)}});
|
||||
//# sourceMappingURL=engineStyle.bundle.js.map
|
||||
20
dist/engineStyle.css
vendored
20
dist/engineStyle.css
vendored
@@ -1279,14 +1279,22 @@ button {
|
||||
#red-pill-container {
|
||||
position: fixed; }
|
||||
|
||||
.bitnode {
|
||||
color: #00f; }
|
||||
.bitnode.level-0 {
|
||||
color: red; }
|
||||
|
||||
.bitnode-destroyed {
|
||||
color: #f00; }
|
||||
.bitnode.level-1 {
|
||||
color: yellow; }
|
||||
|
||||
.bitnode:hover,
|
||||
.bitnode-destroyed:hover {
|
||||
.bitnode.level-2 {
|
||||
color: #48D1CC; }
|
||||
|
||||
.bitnode.level-3 {
|
||||
color: blue; }
|
||||
|
||||
.bitnode.unimplemented {
|
||||
color: gray; }
|
||||
|
||||
.bitnode:hover {
|
||||
color: #fff; }
|
||||
|
||||
/* COLORS */
|
||||
|
||||
24
dist/vendor.bundle.js
vendored
24
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
@@ -204,8 +204,8 @@ The list contains the name of (i.e. the value returned by
|
||||
| | | the string, the result should be an array with only an empty string. |
|
||||
| | | |
|
||||
| | | Examples: |
|
||||
| | | ()())() -> ["()()()", "(())()"] |
|
||||
| | | (a)())() -> ["(a)()()", "(a())()"] |
|
||||
| | | ()())() -> [()()(), (())()] |
|
||||
| | | (a)())() -> [(a)()(), (a())()] |
|
||||
| | | )( -> [""] |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Find All Valid Math Expressions | | You are given a string which contains only digits between 0 and 9 as well as a target |
|
||||
@@ -214,10 +214,12 @@ The list contains the name of (i.e. the value returned by
|
||||
| | | |
|
||||
| | | The answer should be provided as an array of strings containing the valid expressions. |
|
||||
| | | |
|
||||
| | | NOTE: Numbers in an expression cannot have leading 0's |
|
||||
| | | |
|
||||
| | | Examples: |
|
||||
| | | Input: digits = "123", target = 6 |
|
||||
| | | Output: ["1+2+3", "1*2*3"] |
|
||||
| | | Output: [1+2+3, 1*2*3] |
|
||||
| | | |
|
||||
| | | Input: digits = "105", target = 5 |
|
||||
| | | Output: ["1*0+5", "10-5"] |
|
||||
| | | Output: [1*0+5, 10-5] |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
|
||||
@@ -312,9 +312,12 @@ kill
|
||||
^^^^
|
||||
|
||||
$ kill [script name] [args...]
|
||||
$ kill [pid]
|
||||
|
||||
Kill the script specified by the script name and arguments. Each argument must
|
||||
be separated by a space. Remember that a running script is uniquely identified
|
||||
Kill the script specified by the script filename and arguments OR by its PID.
|
||||
|
||||
If you are killing the script using its filename and arguments, then each argument
|
||||
must be separated by a space. Remember that a running script is uniquely identified
|
||||
by both its name and the arguments that are used to start it. So, if a script
|
||||
was ran with the following arguments::
|
||||
|
||||
@@ -324,8 +327,7 @@ Then to kill this script the same arguments would have to be used::
|
||||
|
||||
$ kill foo.script 50e3 sigma-cosmetics
|
||||
|
||||
Note that after issuing the 'kill' command for a script, it may take a few seconds for
|
||||
the script to actually stop running.
|
||||
If you are killing the script using its PID, then the PID argument must be numeric.
|
||||
|
||||
killall
|
||||
^^^^^^^
|
||||
@@ -403,7 +405,7 @@ to convert from script to text file, or vice versa.
|
||||
This function can also be used to rename files.
|
||||
|
||||
.. note:: Unlike the Linux :code:`mv` command, the *destination* argument must be the
|
||||
full filepath. It cannot be a directory.
|
||||
full filepath. It cannot be a directory.
|
||||
|
||||
Examples::
|
||||
|
||||
@@ -511,6 +513,8 @@ sudov
|
||||
|
||||
Prints whether or not you have root access to the current server.
|
||||
|
||||
.. _tail_terminal_command:
|
||||
|
||||
tail
|
||||
^^^^
|
||||
|
||||
|
||||
@@ -3,6 +3,39 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
v0.47.2 - 7/15/2019
|
||||
-------------------
|
||||
|
||||
**Netscript Changes**
|
||||
|
||||
* Added tail() Netscript function
|
||||
* hacknet.getNodeStats() function now returns an additional property for Hacknet Servers: hashCapacity
|
||||
* When writing to a file, the write() function now casts the data being written to a string (using String())
|
||||
* BitNode-selection page now shows what Source-File level you have for each BitNode
|
||||
* Overloaded kill() function so that you can kill a script by its PID
|
||||
* spawn() now only takes 10 seconds to run (decreased from 20 seconds)
|
||||
* run() and exec() now return the PID of the newly-executed scripts, rather than a boolean
|
||||
* (A PID is just a positive integer)
|
||||
* run(), exec(), and spawn() no longer need to be await-ed in NetscriptJS
|
||||
* Script parsing and RAM calculations now support ES9
|
||||
* installAugmentations() no longer has a return value since it causes all scripts to die
|
||||
* isBusy() now returns true if you are in a Hacking Mission
|
||||
* Bug fix: workForFaction() function now properly accounts for disabled logs
|
||||
* Bug fix: RAM should now be properly calculated when running a callback script with installAugmentations()
|
||||
* Bug fix: Fixed bug that caused scripts killed by exit()/spawn() to "clean up" twice
|
||||
|
||||
**Misc Changes**
|
||||
|
||||
* The 'kill' Terminal command can now kill a script by its PID
|
||||
* Added 'Solarized Dark' theme to CodeMirror editor
|
||||
* After Infiltration, you will now return to the company page rather than the city page
|
||||
* Bug fix: Stock Market UI should no longer crash for certain locale settings
|
||||
* Bug fix: You can now properly remove unfinished programs (the *.exe-N%-INC files)
|
||||
* Bug fix: Fixed an issue that allowed you to increase money on servers with a 'maxMoney' of 0 (like CSEC)
|
||||
* Bug fix: Scripts no longer persist if they were started with syntax/import errors
|
||||
* Bug fix: 'hack' and 'analyze' Terminal commands are now blocking
|
||||
* Bug fix: Exp earned by duplicate sleeves at universities/gyms now takes hash upgrades into account
|
||||
|
||||
v0.47.1 - 6/27/2019
|
||||
-------------------
|
||||
* Stock Market changes:
|
||||
|
||||
@@ -6,7 +6,7 @@ clear() Netscript Function
|
||||
:param string/number port/fn: Port or text file to clear
|
||||
:RAM cost: 1 GB
|
||||
|
||||
This function is used to clear data in a `Netscript Ports <http://bitburner.wikia.com/wiki/Netscript_Ports>`_ or a text file.
|
||||
This function is used to clear data in a :ref:`Netscript Port <netscript_ports>` or a text file.
|
||||
|
||||
If the *port/fn* argument is a number between 1 and 20, then it specifies a port and will clear it (deleting all data from the underlying queue).
|
||||
|
||||
|
||||
@@ -14,10 +14,15 @@ exec() Netscript Function
|
||||
Run a script as a separate process on a specified server. This is similar to the *run* function except
|
||||
that it can be used to run a script on any server, instead of just the current server.
|
||||
|
||||
Returns true if the script is successfully started, and false otherwise.
|
||||
If the script was successfully started, then this functions returns the PID
|
||||
of that script. Otherwise, it returns 0.
|
||||
|
||||
Running this function with a *numThreads* argument of 0 will return false without running the script.
|
||||
However, running this function with a negative *numThreads* argument will cause a runtime error.
|
||||
.. note:: PID stands for Process ID. The PID is a unique identifier for each script.
|
||||
The PID will always be a positive integer.
|
||||
|
||||
.. warning:: Running this function with a *numThreads* argument of 0 will return 0 without
|
||||
running the script. However, running this function with a negative *numThreads*
|
||||
argument will cause a runtime error.
|
||||
|
||||
The simplest way to use the *exec* command is to call it with just the script name and the target server.
|
||||
The following example will try to run *generic-hack.script* on the *foodnstuff* server::
|
||||
|
||||
@@ -11,3 +11,6 @@ getGrowTime() Netscript Function
|
||||
|
||||
The function takes in an optional *hackLvl* parameter that can be specified
|
||||
to see what the grow time would be at different hacking levels.
|
||||
|
||||
.. note:: For Hacknet Servers (the upgraded version of a Hacknet Node), this function will
|
||||
return :code:`Infinity`.
|
||||
|
||||
@@ -11,3 +11,6 @@ getHackTime() Netscript Function
|
||||
|
||||
The function takes in an optional *hackLvl* parameter that can be specified
|
||||
to see what the hack time would be at different hacking levels.
|
||||
|
||||
.. note:: For Hacknet Servers (the upgraded version of a Hacknet Node), this function will
|
||||
return :code:`Infinity`.
|
||||
|
||||
@@ -11,3 +11,6 @@ getWeakenTime() Netscript Function
|
||||
|
||||
The function takes in an optional *hackLvl* parameter that can be specified
|
||||
to see what the weaken time would be at different hacking levels.
|
||||
|
||||
.. note:: For Hacknet Servers (the upgraded version of a Hacknet Node), this function will
|
||||
return :code:`Infinity`.
|
||||
|
||||
@@ -4,7 +4,7 @@ growthAnalyze() Netscript Function
|
||||
.. js:function:: growthAnalyze(hostname/ip, growthAmount)
|
||||
|
||||
:param string hostname/ip: IP or hostname of server to analyze
|
||||
:param number growthAmount: Multiplicative factor by which the server is grown. Decimal form.
|
||||
:param number growthAmount: Multiplicative factor by which the server is grown. Decimal form. Must be >= 1.
|
||||
:returns: The amount of grow() calls needed to grow the specified server by the specified amount
|
||||
:RAM cost: 1 GB
|
||||
|
||||
|
||||
@@ -27,3 +27,22 @@ kill() Netscript Function
|
||||
The following will try to kill a script named *foo.script* on the current server that was ran with the arguments 1 and "foodnstuff"::
|
||||
|
||||
kill("foo.script", getHostname(), 1, "foodnstuff");
|
||||
|
||||
.. js:function:: kill(scriptPid)
|
||||
|
||||
:param number scriptPid: PID of the script to kill
|
||||
:RAM cost: 0.5 GB
|
||||
|
||||
Kills the script with the specified PID. Killing a script by its PID will typically
|
||||
have better performance, especially if you have many scripts running.
|
||||
|
||||
If this function successfully kills the specified script, then it will return true.
|
||||
Otherwise, it will return false.
|
||||
|
||||
*Examples:*
|
||||
|
||||
The following example will try to kill the script with the PID 10::
|
||||
|
||||
if (kill(10)) {
|
||||
print("Killed script with PID 10!");
|
||||
}
|
||||
|
||||
@@ -13,10 +13,15 @@ run() Netscript Function
|
||||
Run a script as a separate process. This function can only be used to run scripts located on the current server (the server
|
||||
running the script that calls this function).
|
||||
|
||||
Returns true if the script is successfully started, and false otherwise.
|
||||
If the script was successfully started, then this functions returns the PID
|
||||
of that script. Otherwise, it returns 0.
|
||||
|
||||
Running this function with a *numThreads* argument of 0 will return false without running the script.
|
||||
However, running this function with a negative *numThreads* argument will cause a runtime error.
|
||||
.. note:: PID stands for Process ID. The PID is a unique identifier for each script.
|
||||
The PID will always be a positive integer.
|
||||
|
||||
.. warning:: Running this function with a *numThreads* argument of 0 will return 0 without
|
||||
running the script. However, running this function with a negative *numThreads*
|
||||
argument will cause a runtime error.
|
||||
|
||||
The simplest way to use the *run* command is to call it with just the script name. The following example will run
|
||||
'foo.script' single-threaded with no arguments::
|
||||
|
||||
29
doc/source/netscript/basicfunctions/tail.rst
Normal file
29
doc/source/netscript/basicfunctions/tail.rst
Normal file
@@ -0,0 +1,29 @@
|
||||
tail() Netscript Function
|
||||
==================================
|
||||
|
||||
.. js:function:: tail([fn], [hostname/ip=current ip], [...args])
|
||||
|
||||
:param string fn: Optional. Filename of script to get logs from.
|
||||
:param string ip: Optional. IP or hostname of the server that the script is on
|
||||
:param args...: Arguments to identify which scripts to get logs for
|
||||
:RAM cost: 0 GB
|
||||
|
||||
Opens a script's logs. This is functionally the same as the
|
||||
:ref:`tail_terminal_command` Terminal command.
|
||||
|
||||
If the function is called with no arguments, it will open the current script's logs.
|
||||
|
||||
Otherwise, the `fn`, `hostname/ip,` and `args...` arguments can be used to get the logs
|
||||
from another script. Remember that scripts are uniquely identified by both
|
||||
their names and arguments.
|
||||
|
||||
Examples::
|
||||
|
||||
// Open logs from foo.script on the current server that was run with no args
|
||||
tail("foo.script");
|
||||
|
||||
// Open logs from foo.script on the foodnstuff server that was run with no args
|
||||
tail("foo.script", "foodnstuff");
|
||||
|
||||
// Open logs from foo.script on the foodnstuff server that was run with the arguments [1, "test"]
|
||||
tail("foo.script", "foodnstuff", 1, "test");
|
||||
@@ -15,6 +15,7 @@ getNodeStats() Netscript Function
|
||||
ram: Node's RAM,
|
||||
cores: Node's number of cores,
|
||||
cache: Cache level. Only applicable for Hacknet Servers
|
||||
hashCapacity: Hash Capacity provided by this Node. Only applicable for Hacknet Servers
|
||||
production: Node's production per second
|
||||
timeOnline: Number of seconds since Node has been purchased,
|
||||
totalProduction: Total amount that the Node has produced
|
||||
|
||||
@@ -24,6 +24,7 @@ This includes information such as function signatures, what they do, and their r
|
||||
enableLog() <basicfunctions/enableLog>
|
||||
isLogEnabled() <basicfunctions/isLogEnabled>
|
||||
getScriptLogs() <basicfunctions/getScriptLogs>
|
||||
tail() <basicfunctions/tail>
|
||||
scan() <basicfunctions/scan>
|
||||
nuke() <basicfunctions/nuke>
|
||||
brutessh() <basicfunctions/brutessh>
|
||||
|
||||
@@ -16,7 +16,7 @@ there is plenty of documentation on Javascript available on the web.
|
||||
|
||||
Browser compatibility
|
||||
---------------------
|
||||
As of the time of writing this, Mozilla Firefox and a few other browsers do not support `dynamic import <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import>`_ functionality and therefore cannot run NetscriptJS scripts. These browsers will thus only be capable of using Netscript 1.0.
|
||||
As of the time of writing this, a few browsers do not support `dynamic import <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import>`_ functionality and therefore cannot run NetscriptJS scripts. These browsers will thus only be capable of using Netscript 1.0.
|
||||
|
||||
How to use NetscriptJS
|
||||
----------------------
|
||||
@@ -51,8 +51,6 @@ Here is a summary of all rules you need to follow when writing Netscript JS code
|
||||
* grow
|
||||
* weaken
|
||||
* sleep
|
||||
* run
|
||||
* exec
|
||||
* prompt
|
||||
* wget
|
||||
|
||||
|
||||
@@ -57,6 +57,10 @@ And the data in port 1 will look like::
|
||||
|
||||
[3, 4, 5, 6, 7, 8, 9]
|
||||
|
||||
.. warning:: In :ref:`netscriptjs`, do not trying writing base
|
||||
`Promises <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_
|
||||
to a port.
|
||||
|
||||
**Port Handles**
|
||||
|
||||
WARNING: Port Handles only work in :ref:`netscriptjs`. They do not work in :ref:`netscript1`
|
||||
@@ -213,16 +217,6 @@ to specify a namespace for the import::
|
||||
keyword should **NOT** be used in :ref:`netscript1`, as this will break the script.
|
||||
It can, however, be used in :ref:`netscriptjs` (but it's not required).
|
||||
|
||||
Importing in NetscriptJS
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
There is a minor annoyance when using the `import` feature in :ref:`netscriptjs`.
|
||||
If you make a change in a NetscriptJS script, then you have to manually "refresh" all other
|
||||
scripts that import from that script.
|
||||
|
||||
The easiest way to do this is to simply save and then refresh the game. Alternatively,
|
||||
you can open up all the scripts that need to be "refreshed" in the script editor
|
||||
and then simply re-save them.
|
||||
|
||||
Standard, Built-In JavaScript Objects
|
||||
-------------------------------------
|
||||
Standard built-in JavaScript objects such as
|
||||
|
||||
@@ -63,17 +63,17 @@ Examples
|
||||
**Basic example usage**::
|
||||
|
||||
for (var i = 0; i < sleeve.getNumSleeves(); i++) {
|
||||
sleeve.shockRecovery(i);
|
||||
sleeve.setToShockRecovery(i);
|
||||
}
|
||||
|
||||
sleep(10 * 60 * 60); // wait 10h
|
||||
|
||||
for (var i = 0; i < sleeve.getNumSleeves(); i++) {
|
||||
sleeve.setToSynchronize(i);
|
||||
}
|
||||
|
||||
sleep(10*60*60); // wait 10h
|
||||
|
||||
for (var i = 0; i < sleeve.getNumSleeves(); i++) {
|
||||
sleeve.synchronize(i);
|
||||
}
|
||||
|
||||
sleep(10*60*60); // wait 10h
|
||||
|
||||
for (var i = 0; i < sleeve.getNumSleeves(); i++) {
|
||||
sleeve.commitCrime(i, 'shoplift');
|
||||
sleeve.setToCommitCrime(i, 'shoplift');
|
||||
}
|
||||
|
||||
@@ -11,4 +11,7 @@ installAugmentations() Netscript Function
|
||||
|
||||
This function will automatically install your Augmentations, resetting the game as usual.
|
||||
|
||||
It will return true if successful, and false otherwise.
|
||||
This function will return false if it was not able to install Augmentations.
|
||||
|
||||
If this function successfully installs Augmentations, then it has no return value because
|
||||
all scripts are immediately terminated.
|
||||
|
||||
@@ -5,5 +5,12 @@ isBusy() Netscript Function
|
||||
|
||||
If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to run this function.
|
||||
|
||||
Returns a boolean indicating whether or not the player is currently performing an 'action'. These actions include
|
||||
working for a company/faction, studying at a univeristy, working out at a gym, creating a program, or committing a crime.
|
||||
Returns a boolean indicating whether or not the player is currently performing an 'action'.
|
||||
These actions include:
|
||||
|
||||
* Working for a company/faction
|
||||
* Studying at a univeristy
|
||||
* Working out at a gym
|
||||
* Creating a program
|
||||
* Committing a crime
|
||||
* Carrying out a Hacking Mission
|
||||
|
||||
29
package-lock.json
generated
29
package-lock.json
generated
@@ -689,24 +689,9 @@
|
||||
}
|
||||
},
|
||||
"acorn": {
|
||||
"version": "5.7.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
|
||||
"integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw=="
|
||||
},
|
||||
"acorn-dynamic-import": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz",
|
||||
"integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=",
|
||||
"requires": {
|
||||
"acorn": "4.0.13"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "4.0.13",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz",
|
||||
"integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c="
|
||||
}
|
||||
}
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.2.0.tgz",
|
||||
"integrity": "sha512-8oe72N3WPMjA+2zVG71Ia0nXZ8DpQH+QyyHO+p06jT8eg8FGG3FbcUIi8KziHlAfheJQZeoqbvq1mQSQHXKYLw=="
|
||||
},
|
||||
"acorn-globals": {
|
||||
"version": "4.3.2",
|
||||
@@ -715,7 +700,7 @@
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"acorn": "6.1.1",
|
||||
"acorn-walk": "6.1.1"
|
||||
"acorn-walk": "6.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
@@ -744,9 +729,9 @@
|
||||
}
|
||||
},
|
||||
"acorn-walk": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz",
|
||||
"integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw=="
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz",
|
||||
"integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA=="
|
||||
},
|
||||
"ajv": {
|
||||
"version": "5.5.2",
|
||||
|
||||
@@ -9,9 +9,8 @@
|
||||
"@types/numeral": "0.0.25",
|
||||
"@types/react": "^16.8.6",
|
||||
"@types/react-dom": "^16.8.2",
|
||||
"acorn": "^5.7.3",
|
||||
"acorn-dynamic-import": "^2.0.0",
|
||||
"acorn-walk": "^6.1.1",
|
||||
"acorn": "^6.2.0",
|
||||
"acorn-walk": "^6.2.0",
|
||||
"ajv": "^5.1.5",
|
||||
"ajv-keywords": "^2.0.0",
|
||||
"async": "^2.6.1",
|
||||
|
||||
@@ -8,7 +8,7 @@ import { AugmentationsRoot } from "./ui/Root";
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { Factions, factionExists } from "../Faction/Factions";
|
||||
import { addWorkerScript } from "../NetscriptWorker";
|
||||
import { startWorkerScript } from "../NetscriptWorker";
|
||||
import { Player } from "../Player";
|
||||
import { prestigeAugmentation } from "../Prestige";
|
||||
import { saveObject } from "../SaveObject";
|
||||
@@ -2078,18 +2078,17 @@ function installAugmentations(cbScript=null) {
|
||||
//Run a script after prestiging
|
||||
if (cbScript && isString(cbScript)) {
|
||||
var home = Player.getHomeComputer();
|
||||
for (var i = 0; i < home.scripts.length; ++i) {
|
||||
if (home.scripts[i].filename === cbScript) {
|
||||
var script = home.scripts[i];
|
||||
var ramUsage = script.ramUsage;
|
||||
var ramAvailable = home.maxRam - home.ramUsed;
|
||||
for (const script of home.scripts) {
|
||||
if (script.filename === cbScript) {
|
||||
const ramUsage = script.ramUsage;
|
||||
const ramAvailable = home.maxRam - home.ramUsed;
|
||||
if (ramUsage > ramAvailable) {
|
||||
return; //Not enough RAM
|
||||
return; // Not enough RAM
|
||||
}
|
||||
var runningScriptObj = new RunningScript(script, []); //No args
|
||||
runningScriptObj.threads = 1; //Only 1 thread
|
||||
home.runScript(runningScriptObj, Player.hacknet_node_money_mult);
|
||||
addWorkerScript(runningScriptObj, home);
|
||||
const runningScriptObj = new RunningScript(script, []); // No args
|
||||
runningScriptObj.threads = 1; // Only 1 thread
|
||||
|
||||
startWorkerScript(runningScriptObj, home);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { IMap } from "./types";
|
||||
|
||||
export let CONSTANTS: IMap<any> = {
|
||||
Version: "0.47.1",
|
||||
Version: "v0.47.2",
|
||||
|
||||
/** Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
|
||||
* and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
|
||||
@@ -197,6 +197,13 @@ export let CONSTANTS: IMap<any> = {
|
||||
ClassLeadershipBaseCost: 320,
|
||||
ClassGymBaseCost: 120,
|
||||
|
||||
ClassStudyComputerScienceBaseExp: 0.5,
|
||||
ClassDataStructuresBaseExp: 1,
|
||||
ClassNetworksBaseExp: 2,
|
||||
ClassAlgorithmsBaseExp: 4,
|
||||
ClassManagementBaseExp: 2,
|
||||
ClassLeadershipBaseExp: 4,
|
||||
|
||||
CrimeShoplift: "shoplift",
|
||||
CrimeRobStore: "rob a store",
|
||||
CrimeMug: "mug someone",
|
||||
@@ -221,18 +228,35 @@ export let CONSTANTS: IMap<any> = {
|
||||
|
||||
LatestUpdate:
|
||||
`
|
||||
v0.47.1
|
||||
* Stock Market changes:
|
||||
** Transactions no longer influence stock prices (but they still influence forecast)
|
||||
** Changed the way stocks behave, particularly with regard to how the stock forecast occasionally "flips"
|
||||
** Hacking & growing a server can potentially affect the way the corresponding stock's forecast changes
|
||||
** Working for a company positively affects the way the corresponding stock's forecast changes
|
||||
v0.47.2
|
||||
-------
|
||||
|
||||
* Scripts now start/stop instantly
|
||||
* Improved performance when starting up many copies of a new NetscriptJS script (by Ornedan)
|
||||
* Improved performance when killing scripts
|
||||
* Dialog boxes can now be closed with the ESC key (by jaguilar)
|
||||
* NetscriptJS scripts should now be "re-compiled" if their dependencies change (by jaguilar)
|
||||
* write() function should now properly cause NetscriptJS scripts to "re-compile" (by jaguilar)
|
||||
Netscript Changes
|
||||
* Added tail() Netscript function
|
||||
* hacknet.getNodeStats() function now returns an additional property for Hacknet Servers: hashCapacity
|
||||
* When writing to a file, the write() function now casts the data being written to a string (using String())
|
||||
* BitNode-selection page now shows what Source-File level you have for each BitNode
|
||||
* Overloaded kill() function so that you can kill a script by its PID
|
||||
* spawn() now only takes 10 seconds to run (decreased from 20 seconds)
|
||||
* run() and exec() now return the PID of the newly-executed scripts, rather than a boolean
|
||||
** (A PID is just a positive integer)
|
||||
* run(), exec(), and spawn() no longer need to be await-ed in NetscriptJS
|
||||
* Script parsing and RAM calculations now support ES9
|
||||
* installAugmentations() no longer has a return value since it causes all scripts to die
|
||||
* isBusy() now returns true if you are in a Hacking Mission
|
||||
* Bug fix: workForFaction() function now properly accounts for disabled logs
|
||||
* Bug fix: RAM should now be properly calculated when running a callback script with installAugmentations()
|
||||
* Bug fix: Fixed bug that caused scripts killed by exit()/spawn() to "clean up" twice
|
||||
|
||||
Misc Changes
|
||||
* The 'kill' Terminal command can now kill a script by its PID
|
||||
* Added 'Solarized Dark' theme to CodeMirror editor
|
||||
* After Infiltration, you will now return to the company page rather than the city page
|
||||
* Bug fix: Stock Market UI should no longer crash for certain locale settings
|
||||
* Bug fix: You can now properly remove unfinished programs (the *.exe-N%-INC files)
|
||||
* Bug fix: Fixed an issue that allowed you to increase money on servers with a 'maxMoney' of 0 (like CSEC)
|
||||
* Bug fix: Scripts no longer persist if they were started with syntax/import errors
|
||||
* Bug fix: 'hack' and 'analyze' Terminal commands are now blocking
|
||||
* Bug fix: Exp earned by duplicate sleeves at universities/gyms now takes hash upgrades into account
|
||||
`
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { FconfSettings } from "./FconfSettings";
|
||||
|
||||
import { parse, Node } from "../../utils/acorn";
|
||||
import { parse, Node } from "acorn";
|
||||
import { dialogBoxCreate } from "../../utils/DialogBox";
|
||||
|
||||
var FconfComments = {
|
||||
|
||||
@@ -18,14 +18,17 @@ export const HashUpgradesMetadata: IConstructorParams[] = [
|
||||
{
|
||||
costPerLevel: 50,
|
||||
desc: "Use hashes to decrease the minimum security of a single server by 2%. " +
|
||||
"Note that a server's minimum security cannot go below 1.",
|
||||
"Note that a server's minimum security cannot go below 1. This effect persists " +
|
||||
"until you install Augmentations (since servers are reset at that time).",
|
||||
hasTargetServer: true,
|
||||
name: "Reduce Minimum Security",
|
||||
value: 0.98,
|
||||
},
|
||||
{
|
||||
costPerLevel: 50,
|
||||
desc: "Use hashes to increase the maximum amount of money on a single server by 2%",
|
||||
desc: "Use hashes to increase the maximum amount of money on a single server by 2%. " +
|
||||
"This effect persists until you install Augmentations (since servers " +
|
||||
"are reset at that time).",
|
||||
hasTargetServer: true,
|
||||
name: "Increase Maximum Money",
|
||||
value: 1.02,
|
||||
|
||||
@@ -129,7 +129,7 @@ function endInfiltration(inst, success) {
|
||||
clearEventListeners("infiltration-bribe");
|
||||
clearEventListeners("infiltration-escape");
|
||||
|
||||
Engine.loadLocationContent();
|
||||
Engine.loadLocationContent(false);
|
||||
}
|
||||
|
||||
function nextInfiltrationLevel(inst) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as acorn from "../utils/acorn";
|
||||
import * as acorn from "acorn";
|
||||
/**
|
||||
* @license
|
||||
* JavaScript Interpreter
|
||||
|
||||
@@ -76,37 +76,46 @@ export class UniversityLocation extends React.Component<IProps, any> {
|
||||
const managementCost = CONSTANTS.ClassManagementBaseCost * costMult;
|
||||
const leadershipCost = CONSTANTS.ClassLeadershipBaseCost * costMult;
|
||||
|
||||
const earnHackingExpTooltip = `Gain hacking experience!`
|
||||
const earnCharismaExpTooltip = `Gain charisma experience!`;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<StdButton
|
||||
onClick={this.study}
|
||||
style={this.btnStyle}
|
||||
text={`Study Computer Science (free)`}
|
||||
tooltip={earnHackingExpTooltip}
|
||||
/>
|
||||
<StdButton
|
||||
onClick={this.dataStructures}
|
||||
style={this.btnStyle}
|
||||
text={`Take Data Structures course (${numeralWrapper.formatMoney(dataStructuresCost)} / sec)`}
|
||||
tooltip={earnHackingExpTooltip}
|
||||
/>
|
||||
<StdButton
|
||||
onClick={this.networks}
|
||||
style={this.btnStyle}
|
||||
text={`Take Networks course (${numeralWrapper.formatMoney(networksCost)} / sec)`}
|
||||
tooltip={earnHackingExpTooltip}
|
||||
/>
|
||||
<StdButton
|
||||
onClick={this.algorithms}
|
||||
style={this.btnStyle}
|
||||
text={`Take Algorithms course (${numeralWrapper.formatMoney(algorithmsCost)} / sec)`}
|
||||
tooltip={earnHackingExpTooltip}
|
||||
/>
|
||||
<StdButton
|
||||
onClick={this.management}
|
||||
style={this.btnStyle}
|
||||
text={`Take Management course (${numeralWrapper.formatMoney(managementCost)} / sec)`}
|
||||
tooltip={earnCharismaExpTooltip}
|
||||
/>
|
||||
<StdButton
|
||||
onClick={this.leadership}
|
||||
style={this.btnStyle}
|
||||
text={`Take Leadership course (${numeralWrapper.formatMoney(leadershipCost)} / sec)`}
|
||||
tooltip={earnCharismaExpTooltip}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -89,7 +89,7 @@ function removeWorkerScript(workerScript: WorkerScript, rerenderUi: boolean=true
|
||||
// Recalculate ram used on that server
|
||||
server.ramUsed = roundToTwo(server.ramUsed - workerScript.ramUsage);
|
||||
if (server.ramUsed < 0) {
|
||||
console.warn(`Server RAM usage went negative (if it's due to floating pt imprecision, it's okay): ${server.ramUsed}`);
|
||||
console.warn(`Server (${server.hostname}) RAM usage went negative (if it's due to floating pt imprecision, it's okay): ${server.ramUsed}`);
|
||||
server.ramUsed = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { setTimeoutRef } from "./utils/SetTimeoutRef";
|
||||
import { parse, Node } from "../utils/acorn";
|
||||
|
||||
import { isValidIPAddress } from "../utils/helpers/isValidIPAddress";
|
||||
import { isString } from "../utils/helpers/isString";
|
||||
|
||||
@@ -145,9 +145,10 @@ import { setTimeoutRef } from "./utils/SetTimeoutRef";
|
||||
import { is2DArray } from "./utils/helpers/is2DArray";
|
||||
|
||||
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||
import { isPowerOfTwo } from "../utils/helpers/isPowerOfTwo";
|
||||
import { arrayToString } from "../utils/helpers/arrayToString";
|
||||
import { formatNumber, isHTML } from "../utils/StringHelperFunctions";
|
||||
import { logBoxCreate } from "../utils/LogBox";
|
||||
import { arrayToString } from "../utils/helpers/arrayToString";
|
||||
import { isPowerOfTwo } from "../utils/helpers/isPowerOfTwo";
|
||||
import { isString } from "../utils/helpers/isString";
|
||||
|
||||
import { createElement } from "../utils/uiHelpers/createElement";
|
||||
@@ -251,7 +252,7 @@ function NetscriptFunctions(workerScript) {
|
||||
/**
|
||||
* Gets the Server for a specific hostname/ip, throwing an error
|
||||
* if the server doesn't exist.
|
||||
* @param {string} Hostname or IP of the server
|
||||
* @param {string} ip - Hostname or IP of the server
|
||||
* @param {string} callingFnName - Name of calling function. For logging purposes
|
||||
* @returns {Server} The specified Server
|
||||
*/
|
||||
@@ -264,6 +265,59 @@ function NetscriptFunctions(workerScript) {
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for and returns the RunningScript object for the specified script.
|
||||
* If the 'fn' argument is not specified, this returns the current RunningScript.
|
||||
* @param {string} fn - Filename of script
|
||||
* @param {string} ip - Hostname/ip of the server on which the script resides
|
||||
* @param {any[]} scriptArgs - Running script's arguments
|
||||
* @returns {RunningScript}
|
||||
* Running script identified by the parameters, or null if no such script
|
||||
* exists, or the current running script if the first argument 'fn'
|
||||
* is not specified.
|
||||
*/
|
||||
const getRunningScript = function(fn, ip, callingFnName, scriptArgs) {
|
||||
// Sanitize arguments
|
||||
if (typeof callingFnName !== "string" || callingFnName === "") {
|
||||
callingFnName = "getRunningScript";
|
||||
}
|
||||
|
||||
if (!Array.isArray(scriptArgs)) {
|
||||
throw makeRuntimeRejectMsg(
|
||||
workerScript,
|
||||
`Invalid scriptArgs argument passed into getRunningScript() from ${callingFnName}(). ` +
|
||||
`This is probably a bug. Please report to game developer`
|
||||
);
|
||||
}
|
||||
|
||||
if (fn != null && typeof fn === "string") {
|
||||
// Get Logs of another script
|
||||
if (ip == null) { ip = workerScript.serverIp; }
|
||||
const server = safeGetServer(ip, callingFnName);
|
||||
|
||||
return findRunningScript(fn, scriptArgs, server);
|
||||
}
|
||||
|
||||
// If no arguments are specified, return the current RunningScript
|
||||
return workerScript.scriptRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for getting the error log message when the user specifies
|
||||
* a nonexistent running script
|
||||
* @param {string} fn - Filename of script
|
||||
* @param {string} ip - Hostname/ip of the server on which the script resides
|
||||
* @param {any[]} scriptArgs - Running script's arguments
|
||||
* @returns {string} Error message to print to logs
|
||||
*/
|
||||
const getCannotFindRunningScriptErrorMessage = function(fn, ip, scriptArgs) {
|
||||
if (!Array.isArray(scriptArgs)) {
|
||||
scriptArgs = [];
|
||||
}
|
||||
|
||||
return `Cannot find running script ${fn} on server ${ip} with args: ${arrayToString(scriptArgs)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the player has TIX API access. Throws an error if the player does not
|
||||
*/
|
||||
@@ -351,7 +405,7 @@ function NetscriptFunctions(workerScript) {
|
||||
const node = getHacknetNode(i);
|
||||
const hasUpgraded = hasHacknetServers();
|
||||
const res = {
|
||||
name: node.name,
|
||||
name: hasUpgraded ? node.hostname : node.name,
|
||||
level: node.level,
|
||||
ram: hasUpgraded ? node.maxRam : node.ram,
|
||||
cores: node.cores,
|
||||
@@ -362,6 +416,7 @@ function NetscriptFunctions(workerScript) {
|
||||
|
||||
if (hasUpgraded) {
|
||||
res.cache = node.cache;
|
||||
res.hashCapacity = node.hashCapacity;
|
||||
}
|
||||
|
||||
return res;
|
||||
@@ -556,8 +611,8 @@ function NetscriptFunctions(workerScript) {
|
||||
if (time === undefined) {
|
||||
throw makeRuntimeRejectMsg(workerScript, "sleep() call has incorrect number of arguments. Takes 1 argument");
|
||||
}
|
||||
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.sleep == null) {
|
||||
workerScript.scriptRef.log("Sleeping for " + time + " milliseconds");
|
||||
if (workerScript.shouldLog("sleep")) {
|
||||
workerScript.log(`Sleeping for ${time} milliseconds`);
|
||||
}
|
||||
return netscriptDelay(time, workerScript).then(function() {
|
||||
return Promise.resolve(true);
|
||||
@@ -597,10 +652,10 @@ function NetscriptFunctions(workerScript) {
|
||||
if (growthPercentage == 1) {
|
||||
expGain = 0;
|
||||
}
|
||||
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.grow == null) {
|
||||
workerScript.scriptRef.log("Available money on " + server.hostname + " grown by " +
|
||||
formatNumber((moneyAfter/moneyBefore)*100 - 100, 6) + "%. Gained " +
|
||||
formatNumber(expGain, 4) + " hacking exp (t=" + threads +")");
|
||||
if (workerScript.shouldLog("grow")) {
|
||||
workerScript.log("Available money on " + server.hostname + " grown by " +
|
||||
formatNumber((moneyAfter/moneyBefore)*100 - 100, 6) + "%. Gained " +
|
||||
formatNumber(expGain, 4) + " hacking exp (t=" + threads +")");
|
||||
}
|
||||
workerScript.scriptRef.onlineExpGained += expGain;
|
||||
Player.gainHackingExp(expGain);
|
||||
@@ -615,8 +670,8 @@ function NetscriptFunctions(workerScript) {
|
||||
|
||||
// Check argument validity
|
||||
const server = safeGetServer(ip, 'growthAnalyze');
|
||||
if (isNaN(growth)) {
|
||||
throw makeRuntimeRejectMsg(workerScript, `Invalid growth argument passed into growthAnalyze: ${growth}. Must be numeric`);
|
||||
if (typeof growth !== "number" || isNaN(growth) || growth < 1) {
|
||||
throw makeRuntimeRejectMsg(workerScript, `Invalid growth argument passed into growthAnalyze: ${growth}. Must be numeric and >= 1`);
|
||||
}
|
||||
|
||||
return numCycleForGrowth(server, Number(growth), Player);
|
||||
@@ -699,29 +754,23 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
return workerScript.disableLogs[fn] ? false : true;
|
||||
},
|
||||
getScriptLogs: function(fn, ip) {
|
||||
if (fn != null && typeof fn === 'string') {
|
||||
// Get Logs of another script
|
||||
if (ip == null) { ip = workerScript.serverIp; }
|
||||
const server = getServer(ip);
|
||||
if (server == null) {
|
||||
workerScript.log(`getScriptLogs() failed. Invalid IP or hostname passed in: ${ip}`);
|
||||
throw makeRuntimeRejectMsg(workerScript, `getScriptLogs() failed. Invalid IP or hostname passed in: ${ip}`);
|
||||
}
|
||||
|
||||
let argsForTarget = [];
|
||||
for (let i = 2; i < arguments.length; ++i) {
|
||||
argsForTarget.push(arguments[i]);
|
||||
}
|
||||
const runningScriptObj = findRunningScript(fn, argsForTarget, server);
|
||||
if (runningScriptObj == null) {
|
||||
workerScript.scriptRef.log(`getScriptLogs() failed. No such script ${fn} on ${server.hostname} with args: ${arrayToString(argsForTarget)}`);
|
||||
return "";
|
||||
}
|
||||
return runningScriptObj.logs.slice();
|
||||
getScriptLogs: function(fn, ip, ...scriptArgs) {
|
||||
const runningScriptObj = getRunningScript(fn, ip, "getScriptLogs", scriptArgs);
|
||||
if (runningScriptObj == null) {
|
||||
workerScript.log(`getScriptLogs() failed. ${getCannotFindRunningScriptErrorMessage(fn, ip, scriptArgs)}`);
|
||||
return "";
|
||||
}
|
||||
|
||||
return workerScript.scriptRef.logs.slice();
|
||||
return runningScriptObj.logs.slice();
|
||||
},
|
||||
tail: function(fn, ip, ...scriptArgs) {
|
||||
const runningScriptObj = getRunningScript(fn, ip, "tail", scriptArgs);
|
||||
if (runningScriptObj == null) {
|
||||
workerScript.log(`tail() failed. ${getCannotFindRunningScriptErrorMessage(fn, ip, scriptArgs)} `);
|
||||
return;
|
||||
}
|
||||
|
||||
logBoxCreate(runningScriptObj);
|
||||
},
|
||||
nuke: function(ip){
|
||||
updateDynamicRam("nuke", getRamCost("nuke"));
|
||||
@@ -924,6 +973,8 @@ function NetscriptFunctions(workerScript) {
|
||||
if (scriptname == null || threads == null) {
|
||||
throw makeRuntimeRejectMsg(workerScript, "Invalid scriptname or numThreads argument passed to spawn()");
|
||||
}
|
||||
|
||||
const spawnDelay = 10;
|
||||
setTimeoutRef(() => {
|
||||
if (scriptname === undefined) {
|
||||
throw makeRuntimeRejectMsg(workerScript, "spawn() call has incorrect number of arguments. Usage: spawn(scriptname, numThreads, [arg1], [arg2]...)");
|
||||
@@ -941,40 +992,57 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
|
||||
return runScriptFromScript(scriptServer, scriptname, argsForNewScript, workerScript, threads);
|
||||
}, 20e3);
|
||||
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.spawn == null) {
|
||||
workerScript.scriptRef.log("spawn() will execute " + scriptname + " in 20 seconds");
|
||||
}, spawnDelay * 1e3);
|
||||
|
||||
if (workerScript.shouldLog("spawn")) {
|
||||
workerScript.log(`spawn() will execute ${scriptname} in ${spawnDelay} seconds`);
|
||||
}
|
||||
|
||||
workerScript.running = false; // Prevent workerScript from "finishing execution naturally"
|
||||
if (killWorkerScript(workerScript)) {
|
||||
workerScript.log("Exiting...");
|
||||
}
|
||||
NetscriptFunctions(workerScript).exit();
|
||||
},
|
||||
kill: function(filename, ip) {
|
||||
kill: function(filename, ip, ...scriptArgs) {
|
||||
updateDynamicRam("kill", getRamCost("kill"));
|
||||
if (filename === undefined || ip === undefined) {
|
||||
throw makeRuntimeRejectMsg(workerScript, "kill() call has incorrect number of arguments. Usage: kill(scriptname, server, [arg1], [arg2]...)");
|
||||
|
||||
let res;
|
||||
const killByPid = (typeof filename === "number");
|
||||
if (killByPid) {
|
||||
// Kill by pid
|
||||
res = killWorkerScript(filename);
|
||||
} else {
|
||||
// Kill by filename/ip
|
||||
if (filename === undefined || ip === undefined) {
|
||||
throw makeRuntimeRejectMsg(workerScript, "kill() call has incorrect number of arguments. Usage: kill(scriptname, server, [arg1], [arg2]...)");
|
||||
}
|
||||
|
||||
const server = safeGetServer(ip);
|
||||
const runningScriptObj = getRunningScript(filename, ip, "kill", scriptArgs);
|
||||
if (runningScriptObj == null) {
|
||||
workerScript.log(`kill() failed. ${getCannotFindRunningScriptErrorMessage(filename, ip, scriptArgs)}`)
|
||||
return false;
|
||||
}
|
||||
|
||||
res = killWorkerScript(runningScriptObj, server.ip);
|
||||
}
|
||||
var server = getServer(ip);
|
||||
if (server == null) {
|
||||
workerScript.scriptRef.log("kill() failed. Invalid IP or hostname passed in: " + ip);
|
||||
throw makeRuntimeRejectMsg(workerScript, "kill() failed. Invalid IP or hostname passed in: " + ip);
|
||||
}
|
||||
var argsForKillTarget = [];
|
||||
for (var i = 2; i < arguments.length; ++i) {
|
||||
argsForKillTarget.push(arguments[i]);
|
||||
}
|
||||
var runningScriptObj = findRunningScript(filename, argsForKillTarget, server);
|
||||
if (runningScriptObj == null) {
|
||||
workerScript.scriptRef.log("kill() failed. No such script "+ filename + " on " + server.hostname + " with args: " + arrayToString(argsForKillTarget));
|
||||
return false;
|
||||
}
|
||||
var res = killWorkerScript(runningScriptObj, server.ip);
|
||||
|
||||
if (res) {
|
||||
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.kill == null) {
|
||||
workerScript.scriptRef.log("Killing " + filename + " on " + server.hostname + " with args: " + arrayToString(argsForKillTarget) + ". May take up to a few minutes for the scripts to die...");
|
||||
if (workerScript.shouldLog("kill")) {
|
||||
if (killByPid) {
|
||||
workerScript.log(`Killing script with PID ${filename}`);
|
||||
} else {
|
||||
workerScript.log(`Killing ${filename} on ${ip} with args: ${arrayToString(scriptArgs)}.`);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.kill == null) {
|
||||
workerScript.scriptRef.log("kill() failed. No such script "+ filename + " on " + server.hostname + " with args: " + arrayToString(argsForKillTarget));
|
||||
if (workerScript.shouldLog("kill")) {
|
||||
if (killByPid) {
|
||||
workerScript.log(`kill() failed. No such script with PID ${filename}`);
|
||||
} else {
|
||||
workerScript.log(`kill() failed. No such script ${filename} on ${ip} with args: ${arrayToString(scriptArgs)}`);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1001,14 +1069,11 @@ function NetscriptFunctions(workerScript) {
|
||||
return scriptsRunning;
|
||||
},
|
||||
exit : function() {
|
||||
var server = getServer(workerScript.serverIp);
|
||||
if (server == null) {
|
||||
throw makeRuntimeRejectMsg(workerScript, "Error getting Server for this script in exit(). This is a bug please contact game dev");
|
||||
}
|
||||
if (killWorkerScript(workerScript.scriptRef, server.ip)) {
|
||||
workerScript.scriptRef.log("Exiting...");
|
||||
workerScript.running = false; // Prevent workerScript from "finishing execution naturally"
|
||||
if (killWorkerScript(workerScript)) {
|
||||
workerScript.log("Exiting...");
|
||||
} else {
|
||||
workerScript.scriptRef.log("Exit failed(). This is a bug please contact game developer");
|
||||
workerScript.log("Exit failed(). This is a bug please contact game developer");
|
||||
}
|
||||
},
|
||||
scp: function(scriptname, ip1, ip2) {
|
||||
@@ -1945,6 +2010,13 @@ function NetscriptFunctions(workerScript) {
|
||||
throw makeRuntimeRejectMsg(workerScript, `write() failed due to invalid filepath: ${fn}`);
|
||||
}
|
||||
|
||||
// Coerce 'data' to be a string
|
||||
try {
|
||||
data = String(data);
|
||||
} catch (e) {
|
||||
throw makeRuntimeRejectMsg(workerScript, `write() failed because of invalid data (${e}). Data being written must be convertible to a string`);
|
||||
}
|
||||
|
||||
const server = workerScript.getServer();
|
||||
if (server == null) {
|
||||
throw makeRuntimeRejectMsg(workerScript, "Error getting Server for this script in write(). This is a bug please contact game dev");
|
||||
@@ -2680,7 +2752,7 @@ function NetscriptFunctions(workerScript) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
return Player.isWorking;
|
||||
return Player.isWorking || inMission;
|
||||
},
|
||||
stopAction: function() {
|
||||
updateDynamicRam("stopAction", getRamCost("stopAction"));
|
||||
@@ -2999,8 +3071,8 @@ function NetscriptFunctions(workerScript) {
|
||||
|
||||
if (Player.isWorking) {
|
||||
var txt = Player.singularityStopWork();
|
||||
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.workForFaction == null) {
|
||||
workerScript.scriptRef.log(txt);
|
||||
if (workerScript.shouldLog("workForFaction")) {
|
||||
workerScript.log(txt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3031,34 +3103,40 @@ function NetscriptFunctions(workerScript) {
|
||||
case "hacking contracts":
|
||||
case "hackingcontracts":
|
||||
if (!hackAvailable.includes(fac.name)) {
|
||||
workerScript.scriptRef.log("ERROR: Cannot carry out hacking contracts for " + fac.name + ". workForFaction() failed");
|
||||
workerScript.log("ERROR: Cannot carry out hacking contracts for " + fac.name + ". workForFaction() failed");
|
||||
return false;
|
||||
}
|
||||
Player.startFactionHackWork(fac);
|
||||
workerScript.scriptRef.log("Started carrying out hacking contracts for " + fac.name);
|
||||
if (workerScript.shouldLog("workForFaction")) {
|
||||
workerScript.log("Started carrying out hacking contracts for " + fac.name);
|
||||
}
|
||||
return true;
|
||||
case "field":
|
||||
case "fieldwork":
|
||||
case "field work":
|
||||
if (!fdWkAvailable.includes(fac.name)) {
|
||||
workerScript.scriptRef.log("ERROR: Cannot carry out field missions for " + fac.name + ". workForFaction() failed");
|
||||
workerScript.log("ERROR: Cannot carry out field missions for " + fac.name + ". workForFaction() failed");
|
||||
return false;
|
||||
}
|
||||
Player.startFactionFieldWork(fac);
|
||||
workerScript.scriptRef.log("Started carrying out field missions for " + fac.name);
|
||||
if (workerScript.shouldLog("workForFaction")) {
|
||||
workerScript.log("Started carrying out field missions for " + fac.name);
|
||||
}
|
||||
return true;
|
||||
case "security":
|
||||
case "securitywork":
|
||||
case "security work":
|
||||
if (!scWkAvailable.includes(fac.name)) {
|
||||
workerScript.scriptRef.log("ERROR: Cannot serve as security detail for " + fac.name + ". workForFaction() failed");
|
||||
workerScript.log("ERROR: Cannot serve as security detail for " + fac.name + ". workForFaction() failed");
|
||||
return false;
|
||||
}
|
||||
Player.startFactionSecurityWork(fac);
|
||||
workerScript.scriptRef.log("Started serving as security details for " + fac.name);
|
||||
if (workerScript.shouldLog("workForFaction")) {
|
||||
workerScript.log("Started serving as security details for " + fac.name);
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
workerScript.scriptRef.log("ERROR: Invalid work type passed into workForFaction(): " + type);
|
||||
workerScript.log("ERROR: Invalid work type passed into workForFaction(): " + type);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
@@ -3419,9 +3497,13 @@ function NetscriptFunctions(workerScript) {
|
||||
return false;
|
||||
}
|
||||
Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain);
|
||||
workerScript.scriptRef.log("Installing Augmentations. This will cause this script to be killed");
|
||||
installAugmentations(cbScript);
|
||||
return true;
|
||||
workerScript.log("Installing Augmentations. This will cause this script to be killed");
|
||||
setTimeoutRef(() => {
|
||||
installAugmentations(cbScript);
|
||||
}, 0);
|
||||
|
||||
workerScript.running = false; // Prevent workerScript from "finishing execution naturally"
|
||||
killWorkerScript(workerScript);
|
||||
},
|
||||
|
||||
// Gang API
|
||||
|
||||
@@ -30,14 +30,14 @@ import { setTimeoutRef } from "./utils/SetTimeoutRef";
|
||||
|
||||
import { generate } from "escodegen";
|
||||
|
||||
import { parse, Node } from "../utils/acorn";
|
||||
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||
import { compareArrays } from "../utils/helpers/compareArrays";
|
||||
import { arrayToString } from "../utils/helpers/arrayToString";
|
||||
import { roundToTwo } from "../utils/helpers/roundToTwo";
|
||||
import { isString } from "../utils/StringHelperFunctions";
|
||||
|
||||
const walk = require("acorn/dist/walk");
|
||||
import { parse, Node } from "acorn";
|
||||
const walk = require("acorn-walk");
|
||||
|
||||
// Netscript Ports are instantiated here
|
||||
export const NetscriptPorts = [];
|
||||
@@ -48,6 +48,7 @@ for (var i = 0; i < CONSTANTS.NumNetscriptPorts; ++i) {
|
||||
export function prestigeWorkerScripts() {
|
||||
for (const ws of workerScripts.values()) {
|
||||
ws.env.stopFlag = true;
|
||||
killWorkerScript(ws);
|
||||
}
|
||||
|
||||
WorkerScriptStartStopEventEmitter.emitEvent();
|
||||
@@ -149,6 +150,7 @@ function startNetscript1Script(workerScript) {
|
||||
dialogBoxCreate("Error processing Imports in " + workerScript.name + ":<br>" + e);
|
||||
workerScript.env.stopFlag = true;
|
||||
workerScript.running = false;
|
||||
killWorkerScript(workerScript);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -160,7 +162,7 @@ function startNetscript1Script(workerScript) {
|
||||
if (typeof entry === "function") {
|
||||
//Async functions need to be wrapped. See JS-Interpreter documentation
|
||||
if (name === "hack" || name === "grow" || name === "weaken" || name === "sleep" ||
|
||||
name === "prompt" || name === "run" || name === "exec") {
|
||||
name === "prompt") {
|
||||
let tempWrapper = function() {
|
||||
let fnArgs = [];
|
||||
|
||||
@@ -183,7 +185,8 @@ function startNetscript1Script(workerScript) {
|
||||
}
|
||||
int.setProperty(scope, name, int.createAsyncFunction(tempWrapper));
|
||||
} else if (name === "sprintf" || name === "vsprintf" || name === "scp" ||
|
||||
name == "write" || name === "read" || name === "tryWrite") {
|
||||
name == "write" || name === "read" || name === "tryWrite" ||
|
||||
name === "run" || name === "exec") {
|
||||
let tempWrapper = function() {
|
||||
let fnArgs = [];
|
||||
|
||||
@@ -232,6 +235,7 @@ function startNetscript1Script(workerScript) {
|
||||
dialogBoxCreate("Syntax ERROR in " + workerScript.name + ":<br>" + e);
|
||||
workerScript.env.stopFlag = true;
|
||||
workerScript.running = false;
|
||||
killWorkerScript(workerScript);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -283,7 +287,7 @@ function startNetscript1Script(workerScript) {
|
||||
*/
|
||||
function processNetscript1Imports(code, workerScript) {
|
||||
//allowReserved prevents 'import' from throwing error in ES5
|
||||
const ast = parse(code, { ecmaVersion: 6, allowReserved: true, sourceType: "module" });
|
||||
const ast = parse(code, { ecmaVersion: 9, allowReserved: true, sourceType: "module" });
|
||||
|
||||
var server = workerScript.getServer();
|
||||
if (server == null) {
|
||||
@@ -314,7 +318,7 @@ function processNetscript1Imports(code, workerScript) {
|
||||
if (script == null) {
|
||||
throw new Error("'Import' failed due to invalid script: " + scriptName);
|
||||
}
|
||||
let scriptAst = parse(script.code, {ecmaVersion:5, allowReserved:true, sourceType:"module"});
|
||||
let scriptAst = parse(script.code, { ecmaVersion:9, allowReserved:true, sourceType:"module" });
|
||||
|
||||
if (node.specifiers.length === 1 && node.specifiers[0].type === "ImportNamespaceSpecifier") {
|
||||
// import * as namespace from script
|
||||
@@ -445,14 +449,35 @@ function generateNextPid() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a script
|
||||
*
|
||||
* Given a RunningScript object, constructs a corresponding WorkerScript,
|
||||
* Used to start a RunningScript (by creating and starting its
|
||||
* corresponding WorkerScript), and add the RunningScript to the server on which
|
||||
* it is active
|
||||
* @param {RunningScript} runningScriptObj - Script that's being run
|
||||
* @param {Server} server - Server on which the script is to be run
|
||||
* @returns {number} pid of started script
|
||||
*/
|
||||
export function startWorkerScript(runningScript, server) {
|
||||
if (createAndAddWorkerScript(runningScript, server)) {
|
||||
// Push onto runningScripts.
|
||||
// This has to come after createAndAddWorkerScript() because that fn updates RAM usage
|
||||
server.runScript(runningScript, Player.hacknet_node_money_mult);
|
||||
|
||||
// Once the WorkerScript is constructed in createAndAddWorkerScript(), the RunningScript
|
||||
// object should have a PID assigned to it, so we return that
|
||||
return runningScript.pid;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a RunningScript object, constructs its corresponding WorkerScript,
|
||||
* adds it to the global 'workerScripts' pool, and begins executing it.
|
||||
* @param {RunningScript} runningScriptObj - Script that's being run
|
||||
* @param {Server} server - Server on which the script is to be run
|
||||
* returns {boolean} indicating whether or not the workerScript was successfully added
|
||||
*/
|
||||
export function addWorkerScript(runningScriptObj, server) {
|
||||
export function createAndAddWorkerScript(runningScriptObj, server) {
|
||||
const filename = runningScriptObj.filename;
|
||||
|
||||
// Update server's ram usage
|
||||
@@ -471,7 +496,7 @@ export function addWorkerScript(runningScriptObj, server) {
|
||||
`the game and the script's RAM usage increased (either because of an update to the game or ` +
|
||||
`your changes to the script.)`
|
||||
);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
server.ramUsed = roundToTwo(server.ramUsed + ramUsage);
|
||||
|
||||
@@ -489,31 +514,35 @@ export function addWorkerScript(runningScriptObj, server) {
|
||||
const s = new WorkerScript(runningScriptObj, pid, NetscriptFunctions);
|
||||
s.ramUsage = ramUsage;
|
||||
|
||||
// Add the WorkerScript to the global pool
|
||||
workerScripts.set(pid, s);
|
||||
WorkerScriptStartStopEventEmitter.emitEvent();
|
||||
|
||||
// Start the script's execution
|
||||
let p = null; // Script's resulting promise
|
||||
if (s.name.endsWith(".js") || s.name.endsWith(".ns")) {
|
||||
p = startNetscript2Script(s);
|
||||
} else {
|
||||
p = startNetscript1Script(s);
|
||||
if (!(p instanceof Promise)) { return; }
|
||||
if (!(p instanceof Promise)) { return false; }
|
||||
}
|
||||
|
||||
// Once the code finishes (either resolved or rejected, doesnt matter), set its
|
||||
// running status to false
|
||||
p.then(function(w) {
|
||||
// If the WorkerScript is no longer "running", then this means its execution was
|
||||
// already stopped somewhere else (maybe by something like exit()). This prevents
|
||||
// the script from being cleaned up twice
|
||||
if (!w.running) { return; }
|
||||
|
||||
console.log("Stopping script " + w.name + " because it finished running naturally");
|
||||
killWorkerScript(s);
|
||||
w.scriptRef.log("Script finished running");
|
||||
w.log("Script finished running");
|
||||
}).catch(function(w) {
|
||||
if (w instanceof Error) {
|
||||
dialogBoxCreate("Script runtime unknown error. This is a bug please contact game developer");
|
||||
console.error("Evaluating workerscript returns an Error. THIS SHOULDN'T HAPPEN: " + w.toString());
|
||||
return;
|
||||
} else if (w.constructor === Array && w.length === 2 && w[0] === "RETURNSTATEMENT") {
|
||||
// Script ends with a return statement
|
||||
console.log("Script returning with value: " + w[1]);
|
||||
// TODO maybe do something with this in the future
|
||||
return;
|
||||
} else if (w instanceof WorkerScript) {
|
||||
if (isScriptErrorMessage(w.errorMessage)) {
|
||||
var errorTextArray = w.errorMessage.split("|");
|
||||
@@ -529,9 +558,9 @@ export function addWorkerScript(runningScriptObj, server) {
|
||||
dialogBoxCreate("Script runtime error: <br>Server Ip: " + serverIp +
|
||||
"<br>Script name: " + scriptName +
|
||||
"<br>Args:" + arrayToString(w.args) + "<br>" + errorMsg);
|
||||
w.scriptRef.log("Script crashed with runtime error");
|
||||
w.log("Script crashed with runtime error");
|
||||
} else {
|
||||
w.scriptRef.log("Script killed");
|
||||
w.log("Script killed");
|
||||
return; // Already killed, so stop here
|
||||
}
|
||||
w.running = false;
|
||||
@@ -548,10 +577,7 @@ export function addWorkerScript(runningScriptObj, server) {
|
||||
killWorkerScript(s);
|
||||
});
|
||||
|
||||
// Add the WorkerScript to the global pool
|
||||
workerScripts.set(pid, s);
|
||||
WorkerScriptStartStopEventEmitter.emitEvent();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -589,7 +615,7 @@ export function loadAllRunningScripts() {
|
||||
server.runningScripts.length = 0;
|
||||
} else {
|
||||
for (let j = 0; j < server.runningScripts.length; ++j) {
|
||||
addWorkerScript(server.runningScripts[j], server);
|
||||
createAndAddWorkerScript(server.runningScripts[j], server);
|
||||
|
||||
// Offline production
|
||||
total += scriptCalculateOfflineProduction(server.runningScripts[j]);
|
||||
@@ -605,54 +631,62 @@ export function loadAllRunningScripts() {
|
||||
* Run a script from inside another script (run(), exec(), spawn(), etc.)
|
||||
*/
|
||||
export function runScriptFromScript(server, scriptname, args, workerScript, threads=1) {
|
||||
//Check if the script is already running
|
||||
let runningScriptObj = findRunningScript(scriptname, args, server);
|
||||
if (runningScriptObj != null) {
|
||||
workerScript.scriptRef.log(scriptname + " is already running on " + server.hostname);
|
||||
return Promise.resolve(false);
|
||||
// Sanitize arguments
|
||||
if (!(workerScript instanceof WorkerScript)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
//'null/undefined' arguments are not allowed
|
||||
if (typeof scriptname !== "string" || !Array.isArray(args)) {
|
||||
workerScript.log(`ERROR: runScriptFromScript() failed due to invalid arguments`);
|
||||
console.error(`runScriptFromScript() failed due to invalid arguments`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check if the script is already running
|
||||
let runningScriptObj = server.getRunningScript(scriptname, args);
|
||||
if (runningScriptObj != null) {
|
||||
workerScript.log(`${scriptname} is already running on ${server.hostname}`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 'null/undefined' arguments are not allowed
|
||||
for (let i = 0; i < args.length; ++i) {
|
||||
if (args[i] == null) {
|
||||
workerScript.scriptRef.log("ERROR: Cannot execute a script with null/undefined as an argument");
|
||||
return Promise.resolve(false);
|
||||
workerScript.log("ERROR: Cannot execute a script with null/undefined as an argument");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//Check if the script exists and if it does run it
|
||||
// Check if the script exists and if it does run it
|
||||
for (let i = 0; i < server.scripts.length; ++i) {
|
||||
if (server.scripts[i].filename == scriptname) {
|
||||
//Check for admin rights and that there is enough RAM availble to run
|
||||
var script = server.scripts[i];
|
||||
var ramUsage = script.ramUsage;
|
||||
threads = Math.round(Number(threads)); //Convert to number and round
|
||||
if (threads === 0) { return Promise.resolve(false); }
|
||||
if (server.scripts[i].filename === scriptname) {
|
||||
// Check for admin rights and that there is enough RAM availble to run
|
||||
const script = server.scripts[i];
|
||||
let ramUsage = script.ramUsage;
|
||||
threads = Math.round(Number(threads));
|
||||
if (threads === 0) { return 0; }
|
||||
ramUsage = ramUsage * threads;
|
||||
var ramAvailable = server.maxRam - server.ramUsed;
|
||||
const ramAvailable = server.maxRam - server.ramUsed;
|
||||
|
||||
if (server.hasAdminRights == false) {
|
||||
workerScript.scriptRef.log("Cannot run script " + scriptname + " on " + server.hostname + " because you do not have root access!");
|
||||
return Promise.resolve(false);
|
||||
workerScript.log(`Cannot run script ${scriptname} on ${server.hostname} because you do not have root access!`);
|
||||
return 0;
|
||||
} else if (ramUsage > ramAvailable){
|
||||
workerScript.scriptRef.log("Cannot run script " + scriptname + "(t=" + threads + ") on " + server.hostname + " because there is not enough available RAM!");
|
||||
return Promise.resolve(false);
|
||||
workerScript.log(`Cannot run script ${scriptname} (t=${threads}) on ${server.hostname} because there is not enough available RAM!`);
|
||||
return 0;
|
||||
} else {
|
||||
//Able to run script
|
||||
// Able to run script
|
||||
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.exec == null && workerScript.disableLogs.run == null && workerScript.disableLogs.spawn == null) {
|
||||
workerScript.scriptRef.log(`Running script: ${scriptname} on ${server.hostname} with ${threads} threads and args: ${arrayToString(args)}. May take a few seconds to start up...`);
|
||||
workerScript.log(`Running script: ${scriptname} on ${server.hostname} with ${threads} threads and args: ${arrayToString(args)}.`);
|
||||
}
|
||||
let runningScriptObj = new RunningScript(script, args);
|
||||
runningScriptObj.threads = threads;
|
||||
addWorkerScript(runningScriptObj, server);
|
||||
|
||||
// Push onto runningScripts.
|
||||
// This has to come after addWorkerScript() because that fn updates RAM usage
|
||||
server.runScript(runningScriptObj, Player.hacknet_node_money_mult);
|
||||
return Promise.resolve(true);
|
||||
return startWorkerScript(runningScriptObj, server);
|
||||
}
|
||||
}
|
||||
}
|
||||
workerScript.scriptRef.log("Could not find script " + scriptname + " on " + server.hostname);
|
||||
return Promise.resolve(false);
|
||||
|
||||
workerScript.log(`Could not find script ${scriptname} on ${server.hostname}`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ export interface IPlayer {
|
||||
corporation: any;
|
||||
currentServer: string;
|
||||
factions: string[];
|
||||
firstProgramAvailable: boolean;
|
||||
firstTimeTraveled: boolean;
|
||||
hacknetNodes: (HacknetNode | string)[]; // HacknetNode object or IP of Hacknet Server
|
||||
has4SData: boolean;
|
||||
|
||||
@@ -1211,42 +1211,33 @@ export function startClass(costMult, expMult, className) {
|
||||
|
||||
var gameCPS = 1000 / Engine._idleSpeed;
|
||||
|
||||
//Base exp gains per second
|
||||
var baseStudyComputerScienceExp = 0.5;
|
||||
var baseDataStructuresExp = 1;
|
||||
var baseNetworksExp = 2;
|
||||
var baseAlgorithmsExp = 4;
|
||||
var baseManagementExp = 2;
|
||||
var baseLeadershipExp = 4;
|
||||
var baseGymExp = 1;
|
||||
|
||||
//Find cost and exp gain per game cycle
|
||||
var cost = 0;
|
||||
var hackExp = 0, strExp = 0, defExp = 0, dexExp = 0, agiExp = 0, chaExp = 0;
|
||||
const hashManager = this.hashManager;
|
||||
switch (className) {
|
||||
case CONSTANTS.ClassStudyComputerScience:
|
||||
hackExp = baseStudyComputerScienceExp * expMult / gameCPS * hashManager.getStudyMult();
|
||||
hackExp = CONSTANTS.ClassStudyComputerScienceBaseExp * expMult / gameCPS * hashManager.getStudyMult();
|
||||
break;
|
||||
case CONSTANTS.ClassDataStructures:
|
||||
cost = CONSTANTS.ClassDataStructuresBaseCost * costMult / gameCPS;
|
||||
hackExp = baseDataStructuresExp * expMult / gameCPS * hashManager.getStudyMult();
|
||||
hackExp = CONSTANTS.ClassDataStructuresBaseExp * expMult / gameCPS * hashManager.getStudyMult();
|
||||
break;
|
||||
case CONSTANTS.ClassNetworks:
|
||||
cost = CONSTANTS.ClassNetworksBaseCost * costMult / gameCPS;
|
||||
hackExp = baseNetworksExp * expMult / gameCPS * hashManager.getStudyMult();
|
||||
hackExp = CONSTANTS.ClassNetworksBaseExp * expMult / gameCPS * hashManager.getStudyMult();
|
||||
break;
|
||||
case CONSTANTS.ClassAlgorithms:
|
||||
cost = CONSTANTS.ClassAlgorithmsBaseCost * costMult / gameCPS;
|
||||
hackExp = baseAlgorithmsExp * expMult / gameCPS * hashManager.getStudyMult();
|
||||
hackExp = CONSTANTS.ClassAlgorithmsBaseExp * expMult / gameCPS * hashManager.getStudyMult();
|
||||
break;
|
||||
case CONSTANTS.ClassManagement:
|
||||
cost = CONSTANTS.ClassManagementBaseCost * costMult / gameCPS;
|
||||
chaExp = baseManagementExp * expMult / gameCPS * hashManager.getStudyMult();
|
||||
chaExp = CONSTANTS.ClassManagementBaseExp * expMult / gameCPS * hashManager.getStudyMult();
|
||||
break;
|
||||
case CONSTANTS.ClassLeadership:
|
||||
cost = CONSTANTS.ClassLeadershipBaseCost * costMult / gameCPS;
|
||||
chaExp = baseLeadershipExp * expMult / gameCPS * hashManager.getStudyMult();
|
||||
chaExp = CONSTANTS.ClassLeadershipBaseExp * expMult / gameCPS * hashManager.getStudyMult();
|
||||
break;
|
||||
case CONSTANTS.ClassGymStrength:
|
||||
cost = CONSTANTS.ClassGymBaseCost * costMult / gameCPS;
|
||||
|
||||
@@ -9,9 +9,11 @@
|
||||
import { SleeveTaskType } from "./SleeveTaskTypesEnum";
|
||||
|
||||
import { IPlayer } from "../IPlayer";
|
||||
import { Person,
|
||||
ITaskTracker,
|
||||
createTaskTracker } from "../Person";
|
||||
import {
|
||||
Person,
|
||||
ITaskTracker,
|
||||
createTaskTracker
|
||||
} from "../Person";
|
||||
|
||||
import { Augmentation } from "../../Augmentation/Augmentation";
|
||||
|
||||
@@ -44,6 +46,11 @@ export class Sleeve extends Person {
|
||||
return Generic_fromJSON(Sleeve, value.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the name of the class that the player is currently taking
|
||||
*/
|
||||
className: string = "";
|
||||
|
||||
/**
|
||||
* Stores the type of crime the sleeve is currently attempting
|
||||
* Must match the name of a Crime object
|
||||
@@ -493,6 +500,7 @@ export class Sleeve extends Person {
|
||||
break;
|
||||
case SleeveTaskType.Class:
|
||||
case SleeveTaskType.Gym:
|
||||
this.updateTaskGainRates(p);
|
||||
retValue = this.gainExperience(p, this.gainRatesForTask, cyclesUsed);
|
||||
this.gainMoney(p, this.gainRatesForTask, cyclesUsed);
|
||||
break;
|
||||
@@ -560,6 +568,7 @@ export class Sleeve extends Person {
|
||||
this.crimeType = "";
|
||||
this.currentTaskLocation = "";
|
||||
this.gymStatType = "";
|
||||
this.className = "";
|
||||
}
|
||||
|
||||
shockRecovery(p: IPlayer): boolean {
|
||||
@@ -597,67 +606,50 @@ export class Sleeve extends Person {
|
||||
// Set exp/money multipliers based on which university.
|
||||
// Also check that the sleeve is in the right city
|
||||
let costMult: number = 1;
|
||||
let expMult: number = 1;
|
||||
switch (universityName.toLowerCase()) {
|
||||
case LocationName.AevumSummitUniversity.toLowerCase():
|
||||
if (this.city !== CityName.Aevum) { return false; }
|
||||
this.currentTaskLocation = LocationName.AevumSummitUniversity;
|
||||
costMult = 4;
|
||||
expMult = 3;
|
||||
break;
|
||||
case LocationName.Sector12RothmanUniversity.toLowerCase():
|
||||
if (this.city !== CityName.Sector12) { return false; }
|
||||
this.currentTaskLocation = LocationName.Sector12RothmanUniversity;
|
||||
costMult = 3;
|
||||
expMult = 2;
|
||||
break;
|
||||
case LocationName.VolhavenZBInstituteOfTechnology.toLowerCase():
|
||||
if (this.city !== CityName.Volhaven) { return false; }
|
||||
this.currentTaskLocation = LocationName.VolhavenZBInstituteOfTechnology;
|
||||
costMult = 5;
|
||||
expMult = 4;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set experience/money gains based on class
|
||||
// TODO Refactor University Courses into its own class or something
|
||||
const baseStudyComputerScienceExp: number = 0.5;
|
||||
const baseDataStructuresExp: number = 1;
|
||||
const baseNetworksExp: number = 2;
|
||||
const baseAlgorithmsExp: number = 4;
|
||||
const baseManagementExp: number = 2;
|
||||
const baseLeadershipExp: number = 4;
|
||||
|
||||
switch (className.toLowerCase()) {
|
||||
case "study computer science":
|
||||
this.gainRatesForTask.hack = (baseStudyComputerScienceExp * expMult * this.hacking_exp_mult);
|
||||
break;
|
||||
case "data structures":
|
||||
this.gainRatesForTask.hack = (baseDataStructuresExp * expMult * this.hacking_exp_mult);
|
||||
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassDataStructuresBaseCost * costMult);
|
||||
break;
|
||||
case "networks":
|
||||
this.gainRatesForTask.hack = (baseNetworksExp * expMult * this.hacking_exp_mult);
|
||||
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassNetworksBaseCost * costMult);
|
||||
break;
|
||||
case "algorithms":
|
||||
this.gainRatesForTask.hack = (baseAlgorithmsExp * expMult * this.hacking_exp_mult);
|
||||
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassAlgorithmsBaseCost * costMult);
|
||||
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassAlgorithmsBaseCost * costMult);
|
||||
break;
|
||||
case "management":
|
||||
this.gainRatesForTask.cha = (baseManagementExp * expMult * this.charisma_exp_mult);
|
||||
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassManagementBaseCost * costMult);
|
||||
break;
|
||||
case "leadership":
|
||||
this.gainRatesForTask.cha = (baseLeadershipExp * expMult * this.charisma_exp_mult);
|
||||
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassLeadershipBaseCost * costMult);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
this.className = className;
|
||||
this.currentTask = SleeveTaskType.Class;
|
||||
return true;
|
||||
}
|
||||
@@ -672,6 +664,112 @@ export class Sleeve extends Person {
|
||||
return true;
|
||||
}
|
||||
|
||||
tryBuyAugmentation(p: IPlayer, aug: Augmentation): boolean {
|
||||
if (!p.canAfford(aug.startingCost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p.loseMoney(aug.startingCost);
|
||||
this.installAugmentation(aug);
|
||||
return true;
|
||||
}
|
||||
|
||||
updateTaskGainRates(p: IPlayer): void {
|
||||
if (this.currentTask === SleeveTaskType.Class) {
|
||||
let expMult: number = 1;
|
||||
switch (this.currentTaskLocation.toLowerCase()) {
|
||||
case LocationName.AevumSummitUniversity.toLowerCase():
|
||||
expMult = 3;
|
||||
break;
|
||||
case LocationName.Sector12RothmanUniversity.toLowerCase():
|
||||
expMult = 2;
|
||||
break;
|
||||
case LocationName.VolhavenZBInstituteOfTechnology.toLowerCase():
|
||||
expMult = 4;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
const totalExpMult = expMult * p.hashManager.getStudyMult();
|
||||
switch (this.className.toLowerCase()) {
|
||||
case "study computer science":
|
||||
this.gainRatesForTask.hack = (CONSTANTS.ClassStudyComputerScienceBaseExp * totalExpMult * this.hacking_exp_mult);
|
||||
break;
|
||||
case "data structures":
|
||||
this.gainRatesForTask.hack = (CONSTANTS.ClassDataStructuresBaseExp * totalExpMult * this.hacking_exp_mult);
|
||||
break;
|
||||
case "networks":
|
||||
this.gainRatesForTask.hack = (CONSTANTS.ClassNetworksBaseExp * totalExpMult * this.hacking_exp_mult);
|
||||
break;
|
||||
case "algorithms":
|
||||
this.gainRatesForTask.hack = (CONSTANTS.ClassAlgorithmsBaseExp * totalExpMult * this.hacking_exp_mult);
|
||||
break;
|
||||
case "management":
|
||||
this.gainRatesForTask.cha = (CONSTANTS.ClassManagementBaseExp * totalExpMult * this.charisma_exp_mult);
|
||||
break;
|
||||
case "leadership":
|
||||
this.gainRatesForTask.cha = (CONSTANTS.ClassLeadershipBaseExp * totalExpMult * this.charisma_exp_mult);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.currentTask === SleeveTaskType.Gym) {
|
||||
// Get gym exp multiplier
|
||||
let expMult: number = 1;
|
||||
switch (this.currentTaskLocation.toLowerCase()) {
|
||||
case LocationName.AevumCrushFitnessGym.toLowerCase():
|
||||
expMult = 2;
|
||||
break;
|
||||
case LocationName.AevumSnapFitnessGym.toLowerCase():
|
||||
expMult = 5;
|
||||
break;
|
||||
case LocationName.Sector12IronGym.toLowerCase():
|
||||
expMult = 1;
|
||||
break;
|
||||
case LocationName.Sector12PowerhouseGym.toLowerCase():
|
||||
expMult = 10;
|
||||
break;
|
||||
case LocationName.VolhavenMilleniumFitnessGym:
|
||||
expMult = 4;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// Set stat gain rate
|
||||
const baseGymExp: number = 1;
|
||||
const totalExpMultiplier = p.hashManager.getTrainingMult() * expMult;
|
||||
const sanitizedStat: string = this.gymStatType.toLowerCase();
|
||||
if (sanitizedStat.includes("str")) {
|
||||
this.gainRatesForTask.str = (baseGymExp * totalExpMultiplier * this.strength_exp_mult);
|
||||
} else if (sanitizedStat.includes("def")) {
|
||||
this.gainRatesForTask.def = (baseGymExp * totalExpMultiplier * this.defense_exp_mult);
|
||||
} else if (sanitizedStat.includes("dex")) {
|
||||
this.gainRatesForTask.dex = (baseGymExp * totalExpMultiplier * this.dexterity_exp_mult);
|
||||
} else if (sanitizedStat.includes("agi")) {
|
||||
this.gainRatesForTask.agi = (baseGymExp * totalExpMultiplier * this.agility_exp_mult);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
console.warn(`Sleeve.updateTaskGainRates() called for unexpected task type ${this.currentTask}`);
|
||||
}
|
||||
|
||||
upgradeMemory(n: number): void {
|
||||
if (n < 0) {
|
||||
console.warn(`Sleeve.upgradeMemory() called with negative value: ${n}`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.memory = Math.min(100, Math.round(this.memory + n));
|
||||
}
|
||||
|
||||
/**
|
||||
* Start work for one of the player's companies
|
||||
* Returns boolean indicating success
|
||||
@@ -698,27 +796,27 @@ export class Sleeve extends Person {
|
||||
this.gainRatesForTask.hack = companyPosition.hackingExpGain *
|
||||
company.expMultiplier *
|
||||
this.hacking_exp_mult *
|
||||
BitNodeMultipliers.FactionWorkExpGain;
|
||||
BitNodeMultipliers.CompanyWorkExpGain;
|
||||
this.gainRatesForTask.str = companyPosition.strengthExpGain *
|
||||
company.expMultiplier *
|
||||
this.strength_exp_mult *
|
||||
BitNodeMultipliers.FactionWorkExpGain;
|
||||
BitNodeMultipliers.CompanyWorkExpGain;
|
||||
this.gainRatesForTask.def = companyPosition.defenseExpGain *
|
||||
company.expMultiplier *
|
||||
this.defense_exp_mult *
|
||||
BitNodeMultipliers.FactionWorkExpGain;
|
||||
BitNodeMultipliers.CompanyWorkExpGain;
|
||||
this.gainRatesForTask.dex = companyPosition.dexterityExpGain *
|
||||
company.expMultiplier *
|
||||
this.dexterity_exp_mult *
|
||||
BitNodeMultipliers.FactionWorkExpGain;
|
||||
BitNodeMultipliers.CompanyWorkExpGain;
|
||||
this.gainRatesForTask.agi = companyPosition.agilityExpGain *
|
||||
company.expMultiplier *
|
||||
this.agility_exp_mult *
|
||||
BitNodeMultipliers.FactionWorkExpGain;
|
||||
BitNodeMultipliers.CompanyWorkExpGain;
|
||||
this.gainRatesForTask.cha = companyPosition.charismaExpGain *
|
||||
company.expMultiplier *
|
||||
this.charisma_exp_mult *
|
||||
BitNodeMultipliers.FactionWorkExpGain;
|
||||
BitNodeMultipliers.CompanyWorkExpGain;
|
||||
|
||||
this.currentTaskLocation = companyName;
|
||||
this.currentTask = SleeveTaskType.Company;
|
||||
@@ -792,60 +890,48 @@ export class Sleeve extends Person {
|
||||
// Set exp/money multipliers based on which university.
|
||||
// Also check that the sleeve is in the right city
|
||||
let costMult: number = 1;
|
||||
let expMult: number = 1;
|
||||
switch (gymName.toLowerCase()) {
|
||||
case LocationName.AevumCrushFitnessGym.toLowerCase():
|
||||
if (this.city != CityName.Aevum) { return false; }
|
||||
this.currentTaskLocation = LocationName.AevumCrushFitnessGym;
|
||||
costMult = 3;
|
||||
expMult = 2;
|
||||
break;
|
||||
case LocationName.AevumSnapFitnessGym.toLowerCase():
|
||||
if (this.city != CityName.Aevum) { return false; }
|
||||
this.currentTaskLocation = LocationName.AevumSnapFitnessGym;
|
||||
costMult = 10;
|
||||
expMult = 5;
|
||||
break;
|
||||
case LocationName.Sector12IronGym.toLowerCase():
|
||||
if (this.city != CityName.Sector12) { return false; }
|
||||
this.currentTaskLocation = LocationName.Sector12IronGym;
|
||||
costMult = 1;
|
||||
expMult = 1;
|
||||
break;
|
||||
case LocationName.Sector12PowerhouseGym.toLowerCase():
|
||||
if (this.city != CityName.Sector12) { return false; }
|
||||
this.currentTaskLocation = LocationName.Sector12PowerhouseGym;
|
||||
costMult = 20;
|
||||
expMult = 10;
|
||||
break;
|
||||
case LocationName.VolhavenMilleniumFitnessGym:
|
||||
if (this.city != CityName.Volhaven) { return false; }
|
||||
this.currentTaskLocation = LocationName.VolhavenMilleniumFitnessGym;
|
||||
costMult = 7;
|
||||
expMult = 4;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set experience/money gains based on class
|
||||
// TODO Refactor University Courses into its own class or something
|
||||
const baseGymExp: number = 1;
|
||||
const sanitizedStat: string = stat.toLowerCase();
|
||||
|
||||
// Set cost
|
||||
this.gainRatesForTask.money = -1 * (CONSTANTS.ClassGymBaseCost * costMult);
|
||||
|
||||
// Set stat gain rate
|
||||
if (sanitizedStat.includes("str")) {
|
||||
this.gainRatesForTask.str = (baseGymExp * expMult);
|
||||
} else if (sanitizedStat.includes("def")) {
|
||||
this.gainRatesForTask.def = (baseGymExp * expMult);
|
||||
} else if (sanitizedStat.includes("dex")) {
|
||||
this.gainRatesForTask.dex = (baseGymExp * expMult);
|
||||
} else if (sanitizedStat.includes("agi")) {
|
||||
this.gainRatesForTask.agi = (baseGymExp * expMult);
|
||||
} else {
|
||||
// Validate "stat" argument
|
||||
if (!sanitizedStat.includes("str")
|
||||
&& !sanitizedStat.includes("def")
|
||||
&& !sanitizedStat.includes("dex")
|
||||
&& !sanitizedStat.includes("agi")) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -855,25 +941,6 @@ export class Sleeve extends Person {
|
||||
return true;
|
||||
}
|
||||
|
||||
tryBuyAugmentation(p: IPlayer, aug: Augmentation): boolean {
|
||||
if (!p.canAfford(aug.startingCost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p.loseMoney(aug.startingCost);
|
||||
this.installAugmentation(aug);
|
||||
return true;
|
||||
}
|
||||
|
||||
upgradeMemory(n: number): void {
|
||||
if (n < 0) {
|
||||
console.warn(`Sleeve.upgradeMemory() called with negative value: ${n}`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.memory = Math.min(100, Math.round(this.memory + n));
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the current object to a JSON save state.
|
||||
*/
|
||||
|
||||
@@ -566,6 +566,11 @@ function updateSleeveTaskSelector(sleeve: Sleeve, elems: ISleeveUIElems, allSlee
|
||||
// First selector has class type
|
||||
for (let i = 0; i < universitySelectorOptions.length; ++i) {
|
||||
elems.taskDetailsSelector!.add(createOptionElement(universitySelectorOptions[i]));
|
||||
|
||||
// Set initial value
|
||||
if (sleeve.className === universitySelectorOptions[i]) {
|
||||
elems.taskDetailsSelector!.selectedIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Second selector has which university
|
||||
|
||||
@@ -41,7 +41,6 @@ function getNumAvailableCreateProgram() {
|
||||
|
||||
if (Player.firstProgramAvailable === false && count > 0) {
|
||||
Player.firstProgramAvailable = true;
|
||||
document.getElementById("create-program-tab").style.display = "list-item";
|
||||
document.getElementById("hacking-menu-header").click();
|
||||
document.getElementById("hacking-menu-header").click();
|
||||
}
|
||||
|
||||
109
src/RedPill.js
109
src/RedPill.js
@@ -5,8 +5,9 @@ import { BitNodes } from "./BitNode/BitNode";
|
||||
import { Engine } from "./engine";
|
||||
import { Player } from "./Player";
|
||||
import { prestigeSourceFile } from "./Prestige";
|
||||
import { SourceFiles } from "./SourceFile/SourceFiles";
|
||||
import { PlayerOwnedSourceFile } from "./SourceFile/PlayerOwnedSourceFile";
|
||||
import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
|
||||
import { SourceFiles } from "./SourceFile/SourceFiles";
|
||||
import { Terminal } from "./Terminal";
|
||||
import { setTimeoutRef } from "./utils/SetTimeoutRef";
|
||||
|
||||
@@ -138,15 +139,25 @@ function giveSourceFile(bitNodeNumber) {
|
||||
}
|
||||
}
|
||||
|
||||
// Keeps track of what Source-Files the player will have AFTER the current bitnode
|
||||
// is destroyed. Updated every time loadBitVerse() is called
|
||||
let nextSourceFileFlags = [];
|
||||
|
||||
function loadBitVerse(destroyedBitNodeNum, flume=false) {
|
||||
// Clear the screen
|
||||
var container = document.getElementById("red-pill-content");
|
||||
const container = document.getElementById("red-pill-content");
|
||||
removeChildrenFromElement(container);
|
||||
|
||||
// Update NextSourceFileFlags
|
||||
nextSourceFileFlags = SourceFileFlags.slice();
|
||||
if (!flume) {
|
||||
++nextSourceFileFlags[destroyedBitNodeNum];
|
||||
}
|
||||
|
||||
// Create the Bit Verse
|
||||
var bitVerseImage = document.createElement("pre");
|
||||
var bitNodes = [];
|
||||
for (var i = 1; i <= 12; ++i) {
|
||||
const bitVerseImage = document.createElement("pre");
|
||||
const bitNodes = [];
|
||||
for (let i = 1; i <= 12; ++i) {
|
||||
bitNodes.push(createBitNode(i));
|
||||
}
|
||||
|
||||
@@ -177,53 +188,29 @@ function loadBitVerse(destroyedBitNodeNum, flume=false) {
|
||||
" | | | | | | | | <br>" +
|
||||
" \\JUMP3R|JUMP|3R| |R3|PMUJ|R3PMUJ/ <br><br><br><br>";
|
||||
|
||||
|
||||
/*
|
||||
" O <br>" +
|
||||
" | O O | O O | <br>" +
|
||||
" O | | / __| \ | | O <br>" +
|
||||
" O | O | | O / | O | | O | O <br>" +
|
||||
" | | | | |_/ |/ | \_ \_| | | | | <br>" +
|
||||
" O | | | O | | O__/ | / \__ | | O | | | O <br>" +
|
||||
" | | | | | | | / /| O / \| | | | | | | <br>" +
|
||||
"O | | | \| | O / _/ | / O | |/ | | | O<br>" +
|
||||
"| | | |O / | | O / | O O | | \ O| | | |<br>" +
|
||||
"| | |/ \/ / __| | |/ \ | \ | |__ \ \/ \| | |<br>" +
|
||||
" \| O | |_/ |\| \ O \__| \_| | O |/ <br>" +
|
||||
" | | |_/ | | \| / | \_| | | <br>" +
|
||||
" \| / \| | / / \ |/ <br>" +
|
||||
" | O | | / | O | <br>" +
|
||||
" O | | | | | | | O <br>" +
|
||||
" | | | / / \ \ | | | <br>" +
|
||||
" \| | / O / \ O \ | |/ <br>" +
|
||||
" \ | / / | | \ \ | / <br>" +
|
||||
" \ \JUMP O3R | | | | | | R3O PMUJ/ / <br>" +
|
||||
" \|| | | | | | | | | ||/ <br>" +
|
||||
" \| \_ | | | | | | _/ |/ <br>" +
|
||||
" \ \| / \ / \ |/ / <br>" +
|
||||
" O |/ O | | O \| O <br>" +
|
||||
" | | | | | | | | <br>" +
|
||||
" \JUMP3R|JUMP|3R| |R3|PMUJ|R3PMUJ/ <br>";
|
||||
*/
|
||||
|
||||
container.appendChild(bitVerseImage);
|
||||
|
||||
// Bit node event listeners
|
||||
for (var i = 1; i <= 12; ++i) {
|
||||
// BitNode event listeners
|
||||
for (let i = 1; i <= 12; ++i) {
|
||||
(function(i) {
|
||||
var elemId = "bitnode-" + i.toString();
|
||||
var elem = clearEventListeners(elemId);
|
||||
if (elem == null) {return;}
|
||||
const elemId = "bitnode-" + i.toString();
|
||||
const elem = clearEventListeners(elemId);
|
||||
if (elem == null) { return; }
|
||||
if (i >= 1 && i <= 12) {
|
||||
elem.addEventListener("click", function() {
|
||||
var bitNodeKey = "BitNode" + i;
|
||||
var bitNode = BitNodes[bitNodeKey];
|
||||
const bitNodeKey = "BitNode" + i;
|
||||
const bitNode = BitNodes[bitNodeKey];
|
||||
if (bitNode == null) {
|
||||
console.log("ERROR: Could not find BitNode object for number: " + i);
|
||||
console.error(`Could not find BitNode object for number: ${i}`);
|
||||
return;
|
||||
}
|
||||
yesNoBoxCreate("BitNode-" + i + ": " + bitNode.name + "<br><br>" + bitNode.info);
|
||||
createBitNodeYesNoEventListeners(i, destroyedBitNodeNum, flume);
|
||||
|
||||
const maxSourceFileLevel = i === 12 ? "∞" : "3";
|
||||
const popupBoxText = `BitNode-${i}: ${bitNode.name}<br>` +
|
||||
`Source-File Level: ${nextSourceFileFlags[i]} / ${maxSourceFileLevel}<br><br>` +
|
||||
`${bitNode.info}`;
|
||||
yesNoBoxCreate(popupBoxText);
|
||||
createBitNodeYesNoEventListener(i, destroyedBitNodeNum, flume);
|
||||
});
|
||||
} else {
|
||||
elem.addEventListener("click", function() {
|
||||
@@ -284,18 +271,28 @@ function loadBitVerse(destroyedBitNodeNum, flume=false) {
|
||||
|
||||
// Returns string with DOM element for Bit Node
|
||||
function createBitNode(n) {
|
||||
var bitNodeStr = "BitNode" + n.toString();
|
||||
var bitNode = BitNodes[bitNodeStr];
|
||||
if (bitNode == null) {return "O";}
|
||||
return "<a class='bitnode tooltip' id='bitnode-" + bitNode.number.toString() + "'><strong>O</strong>" +
|
||||
"<span class='tooltiptext'>" +
|
||||
"<strong>BitNode-" + bitNode.number.toString() + "<br>" + bitNode.name+ "</strong><br>" +
|
||||
bitNode.desc + "<br>" +
|
||||
"</span></a>";
|
||||
const bitNodeStr = "BitNode" + n.toString();
|
||||
const bitNode = BitNodes[bitNodeStr];
|
||||
if (bitNode == null) { return "O"; }
|
||||
|
||||
const level = nextSourceFileFlags[n];
|
||||
let cssClass;
|
||||
if (n === 12 && level >= 2) {
|
||||
// Repeating BitNode
|
||||
cssClass = "level-2";
|
||||
} else {
|
||||
cssClass = `level-${level}`;
|
||||
}
|
||||
|
||||
return `<a class='bitnode ${cssClass} tooltip' id='bitnode-${bitNode.number}'><strong>O</strong>` +
|
||||
"<span class='tooltiptext'>" +
|
||||
`<strong>BitNode-${bitNode.number.toString()}<br>${bitNode.name}</strong><br>` +
|
||||
`${bitNode.desc}<br>` +
|
||||
"</span></a>";
|
||||
}
|
||||
|
||||
function createBitNodeYesNoEventListeners(newBitNode, destroyedBitNode, flume=false) {
|
||||
var yesBtn = yesNoBoxGetYesButton();
|
||||
function createBitNodeYesNoEventListener(newBitNode, destroyedBitNode, flume=false) {
|
||||
const yesBtn = yesNoBoxGetYesButton();
|
||||
yesBtn.innerHTML = "Enter BitNode-" + newBitNode;
|
||||
yesBtn.addEventListener("click", function() {
|
||||
if (!flume) {
|
||||
@@ -311,7 +308,7 @@ function createBitNodeYesNoEventListeners(newBitNode, destroyedBitNode, flume=fa
|
||||
|
||||
// Set new Bit Node
|
||||
Player.bitNodeN = newBitNode;
|
||||
console.log("Entering Bit Node " + Player.bitNodeN);
|
||||
console.log(`Entering Bit Node ${Player.bitNodeN}`);
|
||||
|
||||
// Reenable terminal
|
||||
$("#hack-progress-bar").attr('id', "old-hack-progress-bar");
|
||||
@@ -324,7 +321,7 @@ function createBitNodeYesNoEventListeners(newBitNode, destroyedBitNode, flume=fa
|
||||
prestigeSourceFile();
|
||||
yesNoBoxClose();
|
||||
});
|
||||
var noBtn = yesNoBoxGetNoButton();
|
||||
const noBtn = yesNoBoxGetNoButton();
|
||||
noBtn.innerHTML = "Back";
|
||||
noBtn.addEventListener("click", function() {
|
||||
yesNoBoxClose();
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
* the way
|
||||
*/
|
||||
import * as walk from "acorn-walk";
|
||||
import { parse, Node } from "acorn";
|
||||
|
||||
import { RamCalculationErrorCode } from "./RamCalculationErrorCodes";
|
||||
|
||||
import { RamCosts, RamCostConstants } from "../Netscript/RamCostGenerator";
|
||||
import { parse, Node } from "../../utils/acorn";
|
||||
|
||||
// These special strings are used to reference the presence of a given logical
|
||||
// construct within a user script.
|
||||
@@ -215,7 +215,7 @@ async function parseOnlyRamCalculate(otherScripts, code, workerScript) {
|
||||
* that need to be parsed (i.e. are 'import'ed scripts).
|
||||
*/
|
||||
function parseOnlyCalculateDeps(code, currentModule) {
|
||||
const ast = parse(code, {sourceType:"module", ecmaVersion: 8});
|
||||
const ast = parse(code, {sourceType:"module", ecmaVersion: 9});
|
||||
|
||||
// Everything from the global scope goes in ".". Everything else goes in ".function", where only
|
||||
// the outermost layer of functions counts.
|
||||
|
||||
@@ -62,7 +62,7 @@ let NetscriptFunctions =
|
||||
"hack|hackAnalyzeThreads|hackAnalyzePercent|hackChance|" +
|
||||
"sleep|grow|weaken|growthAnalyze|print|tprint|scan|nuke|brutessh|" +
|
||||
"ftpcrack|" +
|
||||
"clearLog|disableLog|enableLog|isLogEnabled|getScriptLogs|" +
|
||||
"clearLog|disableLog|enableLog|isLogEnabled|getScriptLogs|tail|" +
|
||||
"relaysmtp|httpworm|sqlinject|run|exec|spawn|kill|killall|exit|" +
|
||||
"scp|ls|ps|hasRootAccess|" +
|
||||
"getIp|getHackingMultipliers|getBitNodeMultipliers|getStats|isBusy|" +
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
if (!options.indent) // JSHint error.character actually is a column index, this fixes underlining on lines using tabs for indentation
|
||||
options.indent = 1; // JSHint default value is 4
|
||||
|
||||
options.esversion = 6;
|
||||
options.esversion = 9;
|
||||
|
||||
JSHINT(sanitizedText, options, options.globals);
|
||||
var errors = JSHINT.data().errors, result = [];
|
||||
|
||||
@@ -58,6 +58,7 @@ CodeMirror.defineMode("netscript", function(config, parserConfig) {
|
||||
"enableLog": atom,
|
||||
"isLogEnabled": atom,
|
||||
"getScriptLogs": atom,
|
||||
"tail": atom,
|
||||
"relaysmtp": atom,
|
||||
"httpworm": atom,
|
||||
"sqlinject": atom,
|
||||
|
||||
@@ -12,6 +12,7 @@ import { IReturnStatus } from "../types";
|
||||
import { isScriptFilename } from "../Script/ScriptHelpersTS";
|
||||
|
||||
import { createRandomIp } from "../../utils/IPAddress";
|
||||
import { compareArrays } from "../../utils/helpers/compareArrays";
|
||||
|
||||
interface IConstructorParams {
|
||||
adminRights?: boolean;
|
||||
@@ -113,8 +114,27 @@ export class BaseServer {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Given the name of the script, returns the corresponding
|
||||
// script object on the server (if it exists)
|
||||
/**
|
||||
* Find an actively running script on this server
|
||||
* @param scriptName - Filename of script to search for
|
||||
* @param scriptArgs - Arguments that script is being run with
|
||||
* @returns RunningScript for the specified active script
|
||||
* Returns null if no such script can be found
|
||||
*/
|
||||
getRunningScript(scriptName: string, scriptArgs: any[]): RunningScript | null {
|
||||
for (let rs of this.runningScripts) {
|
||||
if (rs.filename === scriptName && compareArrays(rs.args, scriptArgs)) {
|
||||
return rs;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the name of the script, returns the corresponding
|
||||
* Script object on the server (if it exists)
|
||||
*/
|
||||
getScript(scriptName: string): Script | null {
|
||||
for (let i = 0; i < this.scripts.length; i++) {
|
||||
if (this.scripts[i].filename === scriptName) {
|
||||
@@ -129,7 +149,6 @@ export class BaseServer {
|
||||
* Returns boolean indicating whether the given script is running on this server
|
||||
*/
|
||||
isRunning(fn: string): boolean {
|
||||
// Check that the script isnt currently running
|
||||
for (const runningScriptObj of this.runningScripts) {
|
||||
if (runningScriptObj.filename === fn) {
|
||||
return true;
|
||||
@@ -157,7 +176,7 @@ export class BaseServer {
|
||||
* @returns {IReturnStatus} Return status object indicating whether or not file was deleted
|
||||
*/
|
||||
removeFile(fn: string): IReturnStatus {
|
||||
if (fn.endsWith(".exe")) {
|
||||
if (fn.endsWith(".exe") || fn.match(/^.+\.exe-\d+(?:\.\d*)?%-INC$/) != null) {
|
||||
for (let i = 0; i < this.programs.length; ++i) {
|
||||
if (this.programs[i] === fn) {
|
||||
this.programs.splice(i, 1);
|
||||
|
||||
@@ -11,6 +11,7 @@ import { HacknetServer } from "../Hacknet/HacknetServer";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { Programs } from "../Programs/Programs";
|
||||
|
||||
import { isValidNumber } from "../utils/helpers/isValidNumber";
|
||||
import { isValidIPAddress } from "../../utils/helpers/isValidIPAddress";
|
||||
|
||||
/**
|
||||
@@ -35,11 +36,17 @@ export function safetlyCreateUniqueServer(params: IConstructorParams): Server {
|
||||
return new Server(params);
|
||||
}
|
||||
|
||||
// Returns the number of cycles needed to grow the specified server by the
|
||||
// specified amount. 'growth' parameter is in decimal form, not percentage
|
||||
/**
|
||||
* Returns the number of "growth cycles" needed to grow the specified server by the
|
||||
* specified amount.
|
||||
* @param server - Server being grown
|
||||
* @param growth - How much the server is being grown by, in DECIMAL form (e.g. 1.5 rather than 50)
|
||||
* @param p - Reference to Player object
|
||||
* @returns Number of "growth cycles" needed
|
||||
*/
|
||||
export function numCycleForGrowth(server: Server, growth: number, p: IPlayer) {
|
||||
let ajdGrowthRate = 1 + (CONSTANTS.ServerBaseGrowthRate - 1) / server.hackDifficulty;
|
||||
if(ajdGrowthRate > CONSTANTS.ServerMaxGrowthRate) {
|
||||
if (ajdGrowthRate > CONSTANTS.ServerMaxGrowthRate) {
|
||||
ajdGrowthRate = CONSTANTS.ServerMaxGrowthRate;
|
||||
}
|
||||
|
||||
@@ -75,12 +82,12 @@ export function processSingleServerGrowth(server: Server, numCycles: number, p:
|
||||
server.moneyAvailable *= serverGrowth;
|
||||
|
||||
// in case of data corruption
|
||||
if (server.moneyMax && isNaN(server.moneyAvailable)) {
|
||||
if (isValidNumber(server.moneyMax) && isNaN(server.moneyAvailable)) {
|
||||
server.moneyAvailable = server.moneyMax;
|
||||
}
|
||||
|
||||
// cap at max
|
||||
if (server.moneyMax && server.moneyAvailable > server.moneyMax) {
|
||||
if (isValidNumber(server.moneyMax) && server.moneyAvailable > server.moneyMax) {
|
||||
server.moneyAvailable = server.moneyMax;
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ export enum CodeMirrorThemeSetting {
|
||||
Seti = "seti",
|
||||
Shadowfox = "shadowfox",
|
||||
Solarized = "solarized",
|
||||
SolarizedDark = "solarized dark",
|
||||
ssms = "ssms",
|
||||
The_matrix = "the-matrix",
|
||||
Tomorrow_night_bright = "tomorrow-night-bright",
|
||||
|
||||
@@ -20,6 +20,7 @@ import { CONSTANTS } from "../Constants";
|
||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
import { Player } from "../Player";
|
||||
import { IMap } from "../types";
|
||||
import { EventEmitter } from "../utils/EventEmitter";
|
||||
|
||||
import { Page, routing } from ".././ui/navigationTracking";
|
||||
import { numeralWrapper } from ".././ui/numeralFormat";
|
||||
@@ -290,11 +291,15 @@ function initStockMarketFnForReact() {
|
||||
initSymbolToStockMap();
|
||||
}
|
||||
|
||||
const eventEmitterForUiReset = new EventEmitter();
|
||||
|
||||
export function displayStockMarketContent() {
|
||||
if (!routing.isOn(Page.StockMarket)) {
|
||||
return;
|
||||
}
|
||||
|
||||
eventEmitterForUiReset.emitEvent();
|
||||
|
||||
if (stockMarketContainer instanceof HTMLElement) {
|
||||
const castedStockMarket = StockMarket as IStockMarket;
|
||||
ReactDOM.render(
|
||||
@@ -302,6 +307,7 @@ export function displayStockMarketContent() {
|
||||
buyStockLong={buyStock}
|
||||
buyStockShort={shortStock}
|
||||
cancelOrder={cancelOrder}
|
||||
eventEmitterForReset={eventEmitterForUiReset}
|
||||
initStockMarket={initStockMarketFnForReact}
|
||||
p={Player}
|
||||
placeOrder={placeOrder}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { OrderTypes } from "../data/OrderTypes";
|
||||
import { PositionTypes } from "../data/PositionTypes";
|
||||
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { EventEmitter } from "../../utils/EventEmitter";
|
||||
|
||||
type txFn = (stock: Stock, shares: number) => boolean;
|
||||
export type placeOrderFn = (stock: Stock, shares: number, price: number, ordType: OrderTypes, posType: PositionTypes) => boolean;
|
||||
@@ -20,6 +21,7 @@ type IProps = {
|
||||
buyStockLong: txFn;
|
||||
buyStockShort: txFn;
|
||||
cancelOrder: (params: object) => void;
|
||||
eventEmitterForReset?: EventEmitter;
|
||||
initStockMarket: () => void;
|
||||
p: IPlayer;
|
||||
placeOrder: placeOrderFn;
|
||||
@@ -65,6 +67,7 @@ export class StockMarketRoot extends React.Component<IProps, IState> {
|
||||
buyStockLong={this.props.buyStockLong}
|
||||
buyStockShort={this.props.buyStockShort}
|
||||
cancelOrder={this.props.cancelOrder}
|
||||
eventEmitterForReset={this.props.eventEmitterForReset}
|
||||
p={this.props.p}
|
||||
placeOrder={this.props.placeOrder}
|
||||
sellStockLong={this.props.sellStockLong}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Stock } from "../Stock";
|
||||
import { TickerHeaderFormatData } from "../data/TickerHeaderFormatData";
|
||||
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
type IProps = {
|
||||
@@ -16,12 +17,22 @@ type IProps = {
|
||||
stock: Stock;
|
||||
}
|
||||
|
||||
const localesWithLongPriceFormat = [
|
||||
"cs",
|
||||
"lv",
|
||||
"pl",
|
||||
"ru",
|
||||
];
|
||||
|
||||
export function StockTickerHeaderText(props: IProps): React.ReactElement {
|
||||
const stock = props.stock;
|
||||
|
||||
const stockPriceFormat = numeralWrapper.formatMoney(stock.price);
|
||||
const spacesAllottedForStockPrice = localesWithLongPriceFormat.includes(Settings.Locale) ? 15 : 12;
|
||||
const spacesAfterStockName = " ".repeat(1 + TickerHeaderFormatData.longestName - stock.name.length + (TickerHeaderFormatData.longestSymbol - stock.symbol.length));
|
||||
const spacesBeforePrice = " ".repeat(spacesAllottedForStockPrice - stockPriceFormat.length);
|
||||
|
||||
let hdrText = `${stock.name}${" ".repeat(1 + TickerHeaderFormatData.longestName - stock.name.length + (TickerHeaderFormatData.longestSymbol - stock.symbol.length))}${stock.symbol} -${" ".repeat(10 - stockPriceFormat.length)}${stockPriceFormat}`;
|
||||
let hdrText = `${stock.name}${spacesAfterStockName}${stock.symbol} -${spacesBeforePrice}${stockPriceFormat}`;
|
||||
if (props.p.has4SData) {
|
||||
hdrText += ` - Volatility: ${numeralWrapper.format(stock.mv, '0,0.00')}% - Price Forecast: `;
|
||||
let plusOrMinus = stock.b; // True for "+", false for "-"
|
||||
|
||||
@@ -14,6 +14,9 @@ import { OrderTypes } from "../data/OrderTypes";
|
||||
import { PositionTypes } from "../data/PositionTypes";
|
||||
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { EventEmitter } from "../../utils/EventEmitter";
|
||||
|
||||
import { ErrorBoundary } from "../../ui/React/ErrorBoundary";
|
||||
|
||||
export type txFn = (stock: Stock, shares: number) => boolean;
|
||||
export type placeOrderFn = (stock: Stock, shares: number, price: number, ordType: OrderTypes, posType: PositionTypes) => boolean;
|
||||
@@ -22,6 +25,7 @@ type IProps = {
|
||||
buyStockLong: txFn;
|
||||
buyStockShort: txFn;
|
||||
cancelOrder: (params: object) => void;
|
||||
eventEmitterForReset?: EventEmitter;
|
||||
p: IPlayer;
|
||||
placeOrder: placeOrderFn;
|
||||
sellStockLong: txFn;
|
||||
@@ -169,8 +173,13 @@ export class StockTickers extends React.Component<IProps, IState> {
|
||||
}
|
||||
}
|
||||
|
||||
const errorBoundaryProps = {
|
||||
eventEmitterForReset: this.props.eventEmitterForReset,
|
||||
id: "StockTickersErrorBoundary",
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ErrorBoundary {...errorBoundaryProps}>
|
||||
<StockTickersConfig
|
||||
changeDisplayMode={this.changeDisplayMode}
|
||||
changeWatchlistFilter={this.changeWatchlistFilter}
|
||||
@@ -182,7 +191,7 @@ export class StockTickers extends React.Component<IProps, IState> {
|
||||
<ul id="stock-market-list" ref={this.listRef}>
|
||||
{tickers}
|
||||
</ul>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ import {
|
||||
import { showLiterature } from "./Literature";
|
||||
import { Message } from "./Message/Message";
|
||||
import { showMessage } from "./Message/MessageHelpers";
|
||||
import { addWorkerScript } from "./NetscriptWorker";
|
||||
import { startWorkerScript } from "./NetscriptWorker";
|
||||
import { killWorkerScript } from "./Netscript/killWorkerScript";
|
||||
import { WorkerScriptStartStopEventEmitter } from "./Netscript/WorkerScriptStartStopEventEmitter";
|
||||
import { Player } from "./Player";
|
||||
@@ -707,6 +707,11 @@ let Terminal = {
|
||||
},
|
||||
|
||||
executeCommand : function(command) {
|
||||
if (Terminal.hackFlag || Terminal.analyzeFlag) {
|
||||
postError(`Cannot execute command (${command}) while an action is in progress`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Process any aliases
|
||||
command = substituteAliases(command);
|
||||
|
||||
@@ -1150,7 +1155,7 @@ let Terminal = {
|
||||
killWorkerScript(s.runningScripts[i], s.ip, false);
|
||||
}
|
||||
WorkerScriptStartStopEventEmitter.emitEvent();
|
||||
post("Killing all running scripts. May take up to a few minutes for the scripts to die...");
|
||||
post("Killing all running scripts");
|
||||
break;
|
||||
}
|
||||
case "ls": {
|
||||
@@ -1573,19 +1578,32 @@ let Terminal = {
|
||||
return;
|
||||
}
|
||||
|
||||
// Kill by PID
|
||||
if (typeof commandArray[1] === "number") {
|
||||
const pid = commandArray[1];
|
||||
const res = killWorkerScript(pid);
|
||||
if (res) {
|
||||
post(`Killing script with PID ${pid}`);
|
||||
} else {
|
||||
post(`Failed to kill script with PID ${pid}. No such script exists`);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const s = Player.getCurrentServer();
|
||||
const scriptName = Terminal.getFilepath(commandArray[1]);
|
||||
const args = [];
|
||||
for (let i = 2; i < commandArray.length; ++i) {
|
||||
args.push(commandArray[i]);
|
||||
}
|
||||
const runningScript = findRunningScript(scriptName, args, s);
|
||||
const runningScript = s.getRunningScript(scriptName, args);
|
||||
if (runningScript == null) {
|
||||
postError("No such script is running. Nothing to kill");
|
||||
return;
|
||||
}
|
||||
killWorkerScript(runningScript, s.ip);
|
||||
post(`Killing ${scriptName}. May take up to a few seconds for the scripts to die...`);
|
||||
post(`Killing ${scriptName}`);
|
||||
} catch(e) {
|
||||
Terminal.postThrownError(e);
|
||||
}
|
||||
@@ -2290,22 +2308,19 @@ let Terminal = {
|
||||
return;
|
||||
} else {
|
||||
// Able to run script
|
||||
post("Running script with " + numThreads + " thread(s) and args: " + arrayToString(args) + ".");
|
||||
post("May take a few seconds to start up the process...");
|
||||
var runningScriptObj = new RunningScript(script, args);
|
||||
runningScriptObj.threads = numThreads;
|
||||
|
||||
addWorkerScript(runningScriptObj, server);
|
||||
|
||||
// This has to come after addWorkerScript() because that fn
|
||||
// updates the RAM usage. This kinda sucks, address if possible
|
||||
server.runScript(runningScriptObj, Player.hacknet_node_money_mult);
|
||||
return;
|
||||
if (startWorkerScript(runningScriptObj, server)) {
|
||||
post("Running script with " + numThreads + " thread(s) and args: " + arrayToString(args) + ".");
|
||||
} else {
|
||||
postError(`Failed to start script`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
post("ERROR: No such script");
|
||||
},
|
||||
|
||||
|
||||
@@ -36,9 +36,9 @@ export function removeTrailingSlash(s: string): string {
|
||||
* not the entire filepath
|
||||
*/
|
||||
export function isValidFilename(filename: string): boolean {
|
||||
// Allows alphanumerics, hyphens, underscores.
|
||||
// Allows alphanumerics, hyphens, underscores, and percentage signs
|
||||
// Must have a file extension
|
||||
const regex = /^[.a-zA-Z0-9_-]+[.][.a-zA-Z0-9]+$/;
|
||||
const regex = /^[.a-zA-Z0-9_-]+[.][a-zA-Z0-9]+(?:-\d+(?:\.\d*)?%-INC)?$/;
|
||||
|
||||
// match() returns null if no match is found
|
||||
return filename.match(regex) != null;
|
||||
@@ -49,7 +49,7 @@ export function isValidFilename(filename: string): boolean {
|
||||
* not an entire path
|
||||
*/
|
||||
export function isValidDirectoryName(name: string): boolean {
|
||||
// Allows alphanumerics, hyphens and underscores.
|
||||
// Allows alphanumerics, hyphens, underscores, and percentage signs.
|
||||
// Name can begin with a single period, but otherwise cannot have any
|
||||
const regex = /^.?[a-zA-Z0-9_-]+$/;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ export const TerminalHelpText: string =
|
||||
"home Connect to home computer<br>" +
|
||||
"hostname Displays the hostname of the machine<br>" +
|
||||
"ifconfig Displays the IP address of the machine<br>" +
|
||||
"kill [script] [args...] Stops the specified script on the current server <br>" +
|
||||
"kill [script/pid] [args...] Stops the specified script on the current server <br>" +
|
||||
"killall Stops all running scripts on the current machine<br>" +
|
||||
"ls [dir] [| grep pattern] Displays all files on the machine<br>" +
|
||||
"lscpu Displays the number of CPU cores on the machine<br>" +
|
||||
@@ -128,15 +128,16 @@ export const HelpTexts: IMap<string> = {
|
||||
ifconfig: "ipconfig<br>" +
|
||||
"Prints the IP address of the current server",
|
||||
kill: "kill [script name] [args...]<br>" +
|
||||
"Kill the script specified by the script name and arguments. Each argument must be separated by " +
|
||||
"a space. Remember that a running script is uniquely identified by " +
|
||||
"both its name and the arguments that are used to start it. So, if a script was ran with the following arguments:<br><br>" +
|
||||
"kill [pid]<br>" +
|
||||
"Kill the script specified by the script name and arguments OR by its PID.<br><br>" +
|
||||
"If you are killing the script using its filename and arguments, then each " +
|
||||
"argument must be separated by a space. Remember that a running script is " +
|
||||
"uniquely identified by both its name and the arguments that are used to start " +
|
||||
"it. So, if a script was ran with the following arguments:<br><br>" +
|
||||
"run foo.script 1 sigma-cosmetics<br><br>" +
|
||||
"Then to kill this script the same arguments would have to be used:<br><br>" +
|
||||
"kill foo.script 1 sigma-cosmetics<br><br>" +
|
||||
"Note that after issuing the 'kill' command for a script, it may take a while for the script to actually stop running. " +
|
||||
"This will happen if the script is in the middle of a command such as grow() or weaken() that takes time to execute. " +
|
||||
"The script will not be stopped/killed until after that time has elapsed.",
|
||||
"If you are killing the script using its PID, then the PID argument must be numeric",
|
||||
killall: "killall<br>" +
|
||||
"Kills all scripts on the current server. " +
|
||||
"Note that after the 'kill' command is issued for a script, it may take a while for the script to actually stop running. " +
|
||||
@@ -168,7 +169,7 @@ export const HelpTexts: IMap<string> = {
|
||||
"Move the source file to the specified destination. This can also be used to rename files. " +
|
||||
"This command only works for scripts and text files (.txt). This command CANNOT be used to " +
|
||||
"convert to different file types<br><br>" +
|
||||
"Note that, unlike the Linux 'mv' command, the destination argument must be the " +
|
||||
"Note that, unlike the Linux 'mv' command, the destination argument must be the " +
|
||||
"full filepath. " +
|
||||
"Examples: <br><br>" +
|
||||
"mv hacking-controller.script scripts/hacking-controller.script<br>" +
|
||||
|
||||
@@ -748,8 +748,8 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
"the result should be an array with only an empty string.\n\n",
|
||||
"IMPORTANT: The string may contain letters, not just parentheses.",
|
||||
`Examples:\n`,
|
||||
`"()())()" -> ["()()()", "(())()"]\n`,
|
||||
`"(a)())()" -> ["(a)()()", "(a())()"]\n`,
|
||||
`"()())()" -> [()()(), (())()]\n`,
|
||||
`"(a)())()" -> [(a)()(), (a())()]\n`,
|
||||
`")( -> [""]`].join(" ");
|
||||
},
|
||||
difficulty: 10,
|
||||
@@ -841,11 +841,13 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
"The data provided by this problem is an array with two elements. The first element",
|
||||
"is the string of digits, while the second element is the target number:\n\n",
|
||||
`["${digits}", ${target}]\n\n`,
|
||||
"NOTE: Numbers in the expression cannot have leading 0's. In other words,",
|
||||
`"1+01" is not a valid expression`,
|
||||
"Examples:\n\n",
|
||||
`Input: digits = "123", target = 6\n`,
|
||||
`Output: ["1+2+3", "1*2*3"]\n\n`,
|
||||
`Output: [1+2+3, 1*2*3]\n\n`,
|
||||
`Input: digits = "105", target = 5\n`,
|
||||
`Output: ["1*0+5", "10-5"]`].join(" ");
|
||||
`Output: [1*0+5, 10-5]`].join(" ");
|
||||
},
|
||||
difficulty: 10,
|
||||
gen: () => {
|
||||
|
||||
@@ -25,7 +25,6 @@ export function WorkerScriptAccordion(props: IProps): React.ReactElement {
|
||||
const workerScript = props.workerScript;
|
||||
const scriptRef = workerScript.scriptRef;
|
||||
|
||||
|
||||
const logClickHandler = logBoxCreate.bind(null, scriptRef);
|
||||
const killScript = killWorkerScript.bind(null, scriptRef, scriptRef.server);
|
||||
|
||||
|
||||
@@ -66,6 +66,8 @@ export function initializeMainMenuHeaders(p: IPlayer, dev: boolean=false): boole
|
||||
const createProgram: HTMLElement = safeGetElement("create-program-tab");
|
||||
const createProgramNot: HTMLElement = safeGetElement("create-program-notification");
|
||||
|
||||
createProgram.style.display = p.firstProgramAvailable ? "list-item" : "none";
|
||||
|
||||
this.classList.toggle("opened");
|
||||
|
||||
const elems: HTMLElement[] = [terminal, createScript, activeScripts, createProgram];
|
||||
@@ -87,7 +89,7 @@ export function initializeMainMenuHeaders(p: IPlayer, dev: boolean=false): boole
|
||||
const sleeves: HTMLElement = safeGetElement("sleeves-tab");
|
||||
|
||||
sleeves.style.display = p.sleeves.length > 0 ? "list-item" : "none";
|
||||
|
||||
|
||||
this.classList.toggle("opened");
|
||||
|
||||
const elems: HTMLElement[] = [stats, factions, augmentations, hacknetnodes, sleeves];
|
||||
|
||||
103
src/ui/React/ErrorBoundary.tsx
Normal file
103
src/ui/React/ErrorBoundary.tsx
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* React Component for a simple Error Boundary. The fallback UI for
|
||||
* this error boundary is simply a bordered text box
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { EventEmitter } from "../../utils/EventEmitter";
|
||||
|
||||
type IProps = {
|
||||
eventEmitterForReset?: EventEmitter;
|
||||
id?: string;
|
||||
};
|
||||
|
||||
type IState = {
|
||||
errorInfo: string;
|
||||
hasError: boolean;
|
||||
}
|
||||
|
||||
type IErrorInfo = {
|
||||
componentStack: string;
|
||||
}
|
||||
|
||||
// TODO: Move this out to a css file
|
||||
const styleMarkup = {
|
||||
border: "1px solid red",
|
||||
display: "inline-block",
|
||||
margin: "4px",
|
||||
padding: "4px",
|
||||
}
|
||||
|
||||
export class ErrorBoundary extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
errorInfo: "",
|
||||
hasError: false,
|
||||
}
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error: Error) {
|
||||
return {
|
||||
errorInfo: error.message,
|
||||
hasError: true,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, info: IErrorInfo) {
|
||||
console.error(`Caught error in React ErrorBoundary. Component stack:`);
|
||||
console.error(info.componentStack);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const cb = () => {
|
||||
this.setState({
|
||||
hasError: false,
|
||||
});
|
||||
}
|
||||
|
||||
if (this.hasEventEmitter()) {
|
||||
this.props.eventEmitterForReset!.addSubscriber({
|
||||
cb: cb,
|
||||
id: this.props.id!,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.hasEventEmitter()) {
|
||||
this.props.eventEmitterForReset!.removeSubscriber(this.props.id!);
|
||||
}
|
||||
}
|
||||
|
||||
hasEventEmitter(): boolean {
|
||||
return this.props.eventEmitterForReset instanceof EventEmitter
|
||||
&& typeof this.props.id === "string";
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return (
|
||||
<div style={styleMarkup}>
|
||||
<p>
|
||||
{
|
||||
`Error rendering UI. This is (probably) a bug. Please report to game developer.`
|
||||
}
|
||||
</p>
|
||||
<p>
|
||||
{
|
||||
`In the meantime, try refreshing the game WITHOUT saving.`
|
||||
}
|
||||
</p>
|
||||
<p>
|
||||
{
|
||||
`Error info: ${this.state.errorInfo}`
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
7
src/utils/helpers/isValidNumber.ts
Normal file
7
src/utils/helpers/isValidNumber.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Checks that a variable is a valid number. A valid number
|
||||
* must be a "number" type and cannot be NaN
|
||||
*/
|
||||
export function isValidNumber(n: number): boolean {
|
||||
return (typeof n === "number") && !isNaN(n);
|
||||
}
|
||||
@@ -67,6 +67,10 @@ describe("Terminal Directory Tests", function() {
|
||||
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() {
|
||||
@@ -79,6 +83,10 @@ describe("Terminal Directory Tests", function() {
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
3631
utils/acorn.js
3631
utils/acorn.js
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user